4

This is the core issue in all Magento 2.2.x. When we add two cart price rule with action " Percent of product price discount". It makes little calculation issue in total discount.

For example, I have created two cart price rules with the same priority:

Rule 1:

Rule Name: 10% Fix Discount
Coupon: No Coupon
Conditions: Apply for all product
Action: 
    Apply: Percent of product price discount
    Discount Amount: 10

Rule 2:

Rule Name: 5% Fix Discount
Coupon: No Coupon
Conditions: Apply for all product
Action: 
    Apply: Percent of product price discount
    Discount Amount: 5

So the calculation should be like this

For ex. Product Price = $45

First Rule(10% of product price) Discount: (45*10) / 100 = 4.5
Second Rule(5% of product price) Discount: (45*5) / 100 = 2.25

Total Discount = 6.75

But it's shows $6.53 in total discount.

I have raised the issue on GitHub and magento team have also approved the issue. But it takes some time to resolve this issue. https://github.com/magento/magento2/issues/12837

Anyone have idea or any patch to resolve this issue?

  • keep it up... :) – Ronak Chauhan Dec 22 '17 at 5:55
  • Easier to do math than change code to me. So, for rule 2, since rule 1 is already applied, 40.5 X = 2.25 and X = 0.055556 - But to keep it clean for larger numbers, in the Magento backend rule percentage enter "5.5555". The amount doesn't matter as the calculations will still reflect what 5% would have been of the original price. – dawhoo Jan 11 '18 at 20:56
  • @PrincePatel have you found any working solution for this? Even I'm trying to do the same thing in my Magento store. Please post here if you found any solution. Thanks in advance. – Yudi Dec 12 '18 at 5:04
1
+25

Obviously it applies the second rule on the discounted price of rule one. I think this is in app/code/Magento/SalesRule/Model/Quote/Discount.php:

public function collect(...) {

    // ...

    foreach ($items as $item) {

        // ...

        if ($item->getHasChildren() && $item->isChildrenCalculated()) {
            $this->calculator->process($item);
            $this->distributeDiscount($item);
            foreach ($item->getChildren() as $child) {
                $eventArgs['item'] = $child;
                $this->eventManager->dispatch('sales_quote_address_discount_item', $eventArgs);
                $this->aggregateItemDiscount($child, $total);
            }
        } else {
            $this->calculator->process($item);
            $this->aggregateItemDiscount($item, $total);
        }
    }

    // ...
}

/**
 * Aggregate item discount information to total data and related properties
 *
 * @param \Magento\Quote\Model\Quote\Item\AbstractItem $item
 * @param \Magento\Quote\Model\Quote\Address\Total $total
 * @return $this
 */
protected function aggregateItemDiscount(
    \Magento\Quote\Model\Quote\Item\AbstractItem $item,
    \Magento\Quote\Model\Quote\Address\Total $total
) {
    $total->addTotalAmount($this->getCode(), -$item->getDiscountAmount());
    $total->addBaseTotalAmount($this->getCode(), -$item->getBaseDiscountAmount());
    return $this;
}

Check the foreach ($item->getChildren() as $child) { loop. I guess you have to change that behaviour.

Change that to:

// ...

/** @var \Magento\Quote\Model\Quote\Item $item */
foreach ($items as $item) {
    // ...
    $this->eventManager->dispatch('sales_quote_address_discount_item', $eventArgs);

    $this->aggregateItemDiscount($item, $total);
}

// ...

/**
 * Aggregate item discount information to total data and related properties
 *
 * @param \Magento\Quote\Model\Quote\Item\AbstractItem $item
 * @param \Magento\Quote\Model\Quote\Address\Total $total
 * @return $this
 */
protected function aggregateItemDiscount(
    \Magento\Quote\Model\Quote\Item\AbstractItem $item,
    \Magento\Quote\Model\Quote\Address\Total $total
) {
    if ($item->getHasChildren() && $item->isChildrenCalculated()) {
        $this->calculator->process($item);
        $this->distributeDiscount($item);
        foreach ($item->getChildren() as $child) {
            $eventArgs['item'] = $child;
            $this->eventManager->dispatch('sales_quote_address_discount_item', $eventArgs);
            $discountAmount -= -$child->getDiscountAmount();
            $baseDiscountAmount -= -$child->getBaseDiscountAmount();
        }
    } else {
        $this->calculator->process($item);
        $discountAmount = -$item->getDiscountAmount();
        $baseDiscountAmount = -$item->getBaseDiscountAmount();
    }

    $total->addTotalAmount($this->getCode(), $discountAmount);
    $total->addBaseTotalAmount($this->getCode(), $baseDiscountAmount);
    return $this;
}

I haven't tested this so try out what happens. There might be other places where you have to do this.

  • Btw. looking at it again I'm not sure if this is not doing what I think it does (I'm at work and can't look into this deeply): you will have to find the place where the discounts are applied one after another and on the discounted price. There you will have to change the logic so that the discount is applied to the base price instead of the discounted price. – steros Jan 10 '18 at 10:13
  • Thanks to efforts +1 for an explanation. Yes the second rule apply on the discounted price of rule one. But it must be applied the second rule on base price of the product. I will check this file. – Prince Patel Jan 10 '18 at 13:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.