Little-known discount issue in Magento and how to fix it
Hello, my name is Pavel, and the most of my work with Magento is connected with discounts. Sounds like a confession! I can say I know a lot about them, but our sophisticated customers never let me rest and ask more and more questions about this topic.
A couple of days ago I’ve encountered a problem with discounts in Magento, and today I wanted to explain this issue to you, also giving tips on fixing it fast.
How we found the issue
Since these two extensions are very reliable, and all of our updates are carefully tested, I became very concerned with the issue and started investigating right away.
I found out that it’s not a compatibility problem, as the issue was reproduced on a clean local Magento 220.127.116.11 installation with only two Amasty extensions.
Later it came out that the issue hides inside Magento logic, and in fact it can spoil any extension that works with discount.
The rundown of the issue
To show a discount, Magento uses a cycle, which runs through all the items in the cart and applies all the discount rules to each and every item.
Different items initiate rules for each product, while several pieces of the same product initiate the rules collection only once.
The issue was hidden in the “Maximum Qty Discount is Applied To” field. Here’s a simple example.
We want to give 50% off for all the items, plus a 10% coupon for two items as well. The customer bought 10 beers, $10 per beer, $100 total.
Let’s do some maths: 50% from $100 is $50, 10% for two items ($5+$5)*10%=$1. Please remember that a beer is not $10, but $5, because we have already reduced the price.
Now, we calculated the total discount, which is $51. Let’s see the number Magento calculates.
What is happening in reality
The first rule: 50% off for the whole cart
The second rule: 10% off the cart, and Maximum Qty Discount is Applied To = 2
Magento calculates discounts with the following formula:
$discountAmount = ($qty * $itemPrice - $item->getDiscountAmount() ) * $_rulePct;
In this case qty is the number of products, if it’s not restricted by “Maximum Qty Discount is Applied To” field, otherwise it’s “Maximum Qty Discount is Applied To” field value.
$item->getDiscountAmount() is the discount, which has been already applied to the product.
Here’s how Magento calculates it:
The first rule: ($10*10 – 0) * 50% = $50
The second rule: ($10*2 – 50$) * 10% = -$3
In this situation Magento tries to take the applied discount into account, but its algorithm has a serious issue when counting applied discounts for several pieces of a product, and we suddenly get a -$3 discount when applying the second rule.
And the total discount appears to be $47. Yes, you’re right, it’s even less than 50% off. Which is, of course, totally wrong.
But that’s not all. If we step down a bit, we’ll see a discount check, which is performed to ensure that the store won’t give a discount higher than the item price.
$discountAmount = min($itemDiscountAmount + $discountAmount, $itemPrice * $qty);
But we remember that $qty was restricted by “Maximum Qty Discount is Applied To”, and its value is 2 in this case.
Hence, we have the following when the second rule is applied.
$discountAmount = min( 50 + (-3), 10*2)
The answer is $20. Tada!
Where you can find this issue
Looks like this bug is at least five years old, the earliest version that I checked was ver 18.104.22.168 – Added Feb 19, 2010. It seems to be working fine in the previous versions.
Also this bug can be reproduced on Magento 2 version (as of 23rd of October, 2015).
The temporary fix of the issue
To fix this issue, make sure that all the rules with “Maximum Qty Discount is Applied To” field, different from zero, should be applied in the very beginning, use the Priority field for that.
If you have any questions about this topic, I’d be happy to discuss them in comments.