Magento – Apply custom discount to admin order

discountpriceshopping-cart-price-rules

I have a client that would like to do the following when creating an Admin Sales Order (without creating a Price Rule);

Give £10 discount when ordering 4 of Product A (Unit Price: £10).

At present, it seems that the only way this can be done is by checking the Custom Price and setting the Unit Price of all 4 items to £7.50 to give the discount of £10 making a total of £30.

The ideal would be to do something like this;

Add 2 of Product A at £10
Add 2 of Product A at £5

So that the total would be £30 instead of £40. Or to have a field when creating the admin order that can simply add a fixed amount discount to the order.

The client gives different discounts and they don't want to have to keep creating a price rule every time that they wish to create an admin order with a discount.

Has anyone comes across an extension that can do this before? I've done endless searching online and have asked at some of the extension developers like Amasty, Webshopapps and Mageworx etc but they're all pretty certain that this isn't possible. But I figured I would try here too as I hate telling a client that something just can't be done within the Magento framework.

Best Answer

Steps To Apply Custom Discount while placing order from admin:

Please follow the below steps:

  1. Create sales.xml file in your custom module inside etc directory

    <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Sales:etc/sales.xsd">
        <section name="quote">
          <group name="totals">
            <item name="customdiscount" instance="[VendorName]\[Namespace]\Model\Quote\Address\Total\Discount" sort_order="500"/>
          </group>
       </section>
    </config>
    
  2. Create discount.php in [VendorName]/[Namespace]/Model/Quote/Address/Total

    namespace [VendorName]\[Namespace]\Model\Quote\Address\Total;
    
    use Magento\Catalog\Model\ProductRepository;
    
    class Discount extends \Magento\Quote\Model\Quote\Address\Total\AbstractTotal
       {
          /** @var $_productRepository */ 
          protected $_productRepository;
    
        /**
          * Discount constructor.
          * @param ProductRepository $productRepository
          */
       public function __construct(
           ProductRepository $productRepository
       ) {
       $this->setCode('customdiscount');
       $this->_productRepository = $productRepository;
       }
    
     /**
       * Collect address discount amount
       * @param \Magento\Quote\Model\Quote $quote
       * @param \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment
       * @param \Magento\Quote\Model\Quote\Address\Total $total
       * @return $this
       */
    public function collect(
    \Magento\Quote\Model\Quote $quote,
    \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment,
    \Magento\Quote\Model\Quote\Address\Total $total
    ) {
    $data = $quote->getItems();
    if (!count($data)) {
        return $this;
    }
    parent::collect($quote, $shippingAssignment, $total);
    
    /** Custom Discount is saved in line item level (i.e in quote_item) */
        foreach ($data as $item) {
             /** Add your Logic Here */
             if ($item['product_id'] == 'A' && $item['qty'] ) {
              $item->setDiscountAmount([setyourcustomdiscount]);
             $item->setBaseDiscountAmount([setyourcustomdiscount]);
             }
    
      }   
            /** Saving the values in quote_address table */
            $total->setDiscountDescription('Custom Discount');
            $total->setDiscountAmount([setTotalDiscountAmount]);
            $total->setBaseDiscountAmount([setTotalDiscountAmount]);
    
    }
     Note : 1. setTotalDiscountAmount is the sum of all discount amount. 
            2. setyourcustomdiscount is the discount per item in cart id applicable. 
            3. sales.xml is used to add the custom discount. This is common for both frontend and admin.i.e this logic will work in frontend as well.