The question:
I want to apply custom discount amount to cart programmatically. I have tried doing it in following 2 ways, none of which work:
METHOD 1:
I tried following the default way in which Magento2 applies sales-rule
discounts. I did it by creating following files in my module:
etcsales.xml
<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="discount" instance="VendornameModulenmaeModelCustomFolderNameQuoteDiscount" sort_order="400"/>
</group>
</section>
</config>
frontendlayoutcheckout_cart_index.xml
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<body>
<referenceBlock name="checkout.cart.totals">
<arguments>
<argument name="jsLayout" xsi:type="array">
<item name="components" xsi:type="array">
<item name="block-totals" xsi:type="array">
<item name="children" xsi:type="array">
<item name="before_grandtotal" xsi:type="array">
<item name="children" xsi:type="array">
<item name="discount" xsi:type="array">
<item name="config" xsi:type="array">
<item name="title" xsi:type="string" translate="true">My Custom Discount</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</argument>
</arguments>
</referenceBlock>
</body>
</page>
Model File
class Discount extends MagentoQuoteModelQuoteAddressTotalAbstractTotal
{
public function collect(
MagentoQuoteModelQuote $quote,
MagentoQuoteApiDataShippingAssignmentInterface $shippingAssignment,
MagentoQuoteModelQuoteAddressTotal $total
) {
parent::collect($quote, $shippingAssignment, $total);
$label = 'My Custom Discount';
$discountAmount = -10;
$total->setDiscountDescription($label);
$total->setDiscountAmount($discountAmount);
$total->setBaseDiscountAmount($discountAmount);
$total->setSubtotalWithDiscount($total->getSubtotal() + $discountAmount);
$total->setBaseSubtotalWithDiscount($total->getBaseSubtotal() + $discountAmount);
return $this;
}
//fetch function is also defined in this class. Since it is only used for display purposes I have not added its code in this question.
}
However, with this method, my custom discount gets applied but Magento’s default sales rules
like coupon code
, etc. stop working. They throw errors like Invalid Coupon Code
even when the coupon code is completely valid. If I revert my changes, the default coupon codes start working again.
METHOD 2:
Another method i tried is applying discount by observing the sales_quote_collect_totals_after
event. It is working fine, but i think method 1 might be the best way to achieve it.
Any ideas as to how can I do it correctly ?
The Solutions:
Below are the methods you can try. The first solution is probably the best. Try others if the first one doesn’t work. Senior developers aren’t just copying/pasting – they read the methods carefully & apply them wisely to each case.
Method 1
Thanks @R.S. . I found the solution by referring repo created by @R.S. here: https://github.com/magepal/stackexchange/tree/develop/104112 after some minor modifications.
sales.xml
<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="testdiscount" instance="MagePalTestDiscountModelQuoteDiscount" sort_order="500"/>
</group>
</section>
</config>
Model file
public function collect(
MagentoQuoteModelQuote $quote,
MagentoQuoteApiDataShippingAssignmentInterface $shippingAssignment,
MagentoQuoteModelQuoteAddressTotal $total
) {
//Fix for discount applied twice
$items = $shippingAssignment->getItems();
if (!count($items)) {
return $this;
}
parent::collect($quote, $shippingAssignment, $total);
//$address = $shippingAssignment->getShipping()->getAddress();
$label = 'My Custom Discount';
$discountAmount = -10;
$appliedCartDiscount = 0;
if($total->getDiscountDescription()) {
// If a discount exists in cart and another discount is applied, the add both discounts.
$appliedCartDiscount = $total->getDiscountAmount();
$discountAmount = $total->getDiscountAmount()+$discountAmount;
$label = $total->getDiscountDescription().', '.$label;
}
$total->setDiscountDescription($label);
$total->setDiscountAmount($discountAmount);
$total->setBaseDiscountAmount($discountAmount);
$total->setSubtotalWithDiscount($total->getSubtotal() + $discountAmount);
$total->setBaseSubtotalWithDiscount($total->getBaseSubtotal() + $discountAmount);
if(isset($appliedCartDiscount)) {
$total->addTotalAmount($this->getCode(), $discountAmount - $appliedCartDiscount);
$total->addBaseTotalAmount($this->getCode(), $discountAmount - $appliedCartDiscount);
} else {
$total->addTotalAmount($this->getCode(), $discountAmount);
$total->addBaseTotalAmount($this->getCode(), $discountAmount);
}
return $this;
}
checkout_cart_index.xml
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<body>
<referenceBlock name="checkout.cart.totals">
<arguments>
<argument name="jsLayout" xsi:type="array">
<item name="components" xsi:type="array">
<item name="block-totals" xsi:type="array">
<item name="children" xsi:type="array">
<item name="before_grandtotal" xsi:type="array">
<item name="children" xsi:type="array">
<item name="testdiscount" xsi:type="array">
<item name="config" xsi:type="array">
<item name="title" xsi:type="string" translate="true">My Discount</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</argument>
</arguments>
</referenceBlock>
</body>
</page>
checkout_index_index.xml
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<body>
<referenceBlock name="checkout.root">
<arguments>
<argument name="jsLayout" xsi:type="array">
<item name="components" xsi:type="array">
<item name="checkout" xsi:type="array">
<item name="children" xsi:type="array">
<item name="sidebar" xsi:type="array">
<item name="children" xsi:type="array">
<item name="summary" xsi:type="array">
<item name="children" xsi:type="array">
<item name="totals" xsi:type="array">
<item name="children" xsi:type="array">
<item name="testdiscount" xsi:type="array">
<item name="config" xsi:type="array">
<item name="title" xsi:type="string" translate="true">My Discount</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</argument>
</arguments>
</referenceBlock>
</body>
</page>
Fetch is not showing the custom discount description. I have opened its ticket here: https://github.com/magento/magento2/issues/3594
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0