Skip to content

Commit 9e76ef8

Browse files
committed
ACP2E-3377: Cart rules "Fixed amount discount for whole cart" action applies discounts incorrectly when adding bundle products
1 parent 577d80d commit 9e76ef8

File tree

2 files changed

+126
-57
lines changed

2 files changed

+126
-57
lines changed

app/code/Magento/Quote/Model/Quote/Item/AbstractItem.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@
2626
* @method float getDiscountPercent()
2727
* @method \Magento\Quote\Model\Quote\Item\AbstractItem setDiscountPercent()
2828
* @method float getOriginalDiscountAmount()
29-
* @method \Magento\Quote\Model\Quote\Item\AbstractItem setOriginalDiscountAmount()
29+
* @method \Magento\Quote\Model\Quote\Item\AbstractItem setOriginalDiscountAmount(float $amount)
3030
* @method float getBaseOriginalDiscountAmount()
31-
* @method \Magento\Quote\Model\Quote\Item\AbstractItem setBaseOriginalDiscountAmount()
31+
* @method \Magento\Quote\Model\Quote\Item\AbstractItem setBaseOriginalDiscountAmount(float $amount)
3232
* @method float getDiscountCalculationPrice()
33-
* @method \Magento\Quote\Model\Quote\Item\AbstractItem setDiscountCalculationPrice()
33+
* @method \Magento\Quote\Model\Quote\Item\AbstractItem setDiscountCalculationPrice(float $amount)
3434
* @method float getBaseDiscountCalculationPrice()
3535
* @method \Magento\Quote\Model\Quote\Item\AbstractItem setBaseDiscountCalculationPrice($price)
3636
* @method int[] getAppliedRuleIds()

app/code/Magento/SalesRule/Model/Quote/Discount.php

Lines changed: 123 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
use Magento\Framework\App\ObjectManager;
99
use Magento\Framework\Event\ManagerInterface;
10+
use Magento\Framework\Exception\NoSuchEntityException;
1011
use Magento\Framework\Pricing\PriceCurrencyInterface;
1112
use Magento\Quote\Api\Data\AddressInterface;
1213
use Magento\Quote\Api\Data\ShippingAssignmentInterface;
@@ -128,71 +129,34 @@ public function _resetState(): void
128129
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
129130
* @SuppressWarnings(PHPMD.NPathComplexity)
130131
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
132+
* @throws \Zend_Db_Select_Exception|NoSuchEntityException
131133
*/
132134
public function collect(
133135
Quote $quote,
134136
ShippingAssignmentInterface $shippingAssignment,
135137
Total $total
136138
) {
137139
parent::collect($quote, $shippingAssignment, $total);
138-
$store = $this->storeManager->getStore($quote->getStoreId());
139-
/** @var Address $address */
140-
$address = $shippingAssignment->getShipping()->getAddress();
141-
if ($quote->currentPaymentWasSet()) {
142-
$address->setPaymentMethod($quote->getPayment()->getMethod());
143-
}
144-
$this->calculator->reset($address);
145-
$itemsAggregate = [];
146-
foreach ($shippingAssignment->getItems() as $item) {
147-
$itemId = $item->getId();
148-
$itemsAggregate[$itemId] = $item;
149-
}
150-
$items = [];
151-
foreach ($quote->getAllAddresses() as $quoteAddress) {
152-
foreach ($quoteAddress->getAllItems() as $item) {
153-
$items[] = $item;
154-
}
155-
}
140+
$this->addressDiscountAggregator = [];
141+
142+
$address = $this->getAddress($shippingAssignment, $quote);
143+
$itemsAggregate = $this->getShippingItems($shippingAssignment);
144+
$items = $this->extractQuoteItems($quote, $address);
145+
156146
if (!$items || !$itemsAggregate) {
157147
return $this;
158148
}
159-
$eventArgs = [
160-
'website_id' => $store->getWebsiteId(),
161-
'customer_group_id' => $quote->getCustomerGroupId(),
162-
'coupon_code' => $quote->getCouponCode(),
163-
];
164-
$address->setDiscountDescription([]);
165-
$address->getExtensionAttributes()->setDiscounts([]);
166-
$this->addressDiscountAggregator = [];
167-
$address->setCartFixedRules([]);
149+
$store = $this->storeManager->getStore($quote->getStoreId());
150+
168151
$quote->setCartFixedRules([]);
169-
foreach ($items as $item) {
170-
$item->setAppliedRuleIds(null);
171-
if ($item->getExtensionAttributes()) {
172-
$item->getExtensionAttributes()->setDiscounts(null);
173-
}
174-
$item->setDiscountAmount(0);
175-
$item->setBaseDiscountAmount(0);
176-
$item->setDiscountPercent(0);
177-
if ($item->getChildren() && $item->isChildrenCalculated()) {
178-
foreach ($item->getChildren() as $child) {
179-
$child->setDiscountAmount(0);
180-
$child->setBaseDiscountAmount(0);
181-
$child->setDiscountPercent(0);
182-
}
183-
}
184-
$item->getAddress()->setBaseDiscountAmount(0);
185-
}
186152
$this->calculator->initFromQuote($quote);
187153
$this->calculator->initTotals($items, $address);
188-
$items = $this->calculator->sortItemsByPriority($items, $address);
154+
189155
$itemsToApplyRules = $items;
190-
$rules = $this->calculator->getRules($address);
191-
$totalDiscount = [];
192-
$address->setBaseDiscountAmount(0);
156+
193157
/** @var Rule $rule */
194-
foreach ($rules as $rule) {
195-
/** @var Item $item */
158+
foreach ($this->calculator->getRules($address) as $rule) {
159+
$totalDiscount = 0;
196160
foreach ($itemsToApplyRules as $key => $item) {
197161
if ($item->getNoDiscount() || !$this->calculator->canApplyDiscount($item) || $item->getParentItem()) {
198162
continue;
@@ -212,18 +176,25 @@ public function collect(
212176
break;
213177
}
214178

215-
$eventArgs['item'] = $item;
216-
$this->eventManager->dispatch('sales_quote_address_discount_item', $eventArgs);
179+
$this->eventManager->dispatch(
180+
'sales_quote_address_discount_item',
181+
[
182+
'website_id' => $store->getWebsiteId(),
183+
'customer_group_id' => $quote->getCustomerGroupId(),
184+
'coupon_code' => $quote->getCouponCode(),
185+
'item' => $item
186+
]
187+
);
217188

218189
$this->calculator->process($item, $rule);
219190
$appliedRuleIds = $item->getAppliedRuleIds() ? explode(',', $item->getAppliedRuleIds()) : [];
220191
if ($rule->getStopRulesProcessing() && in_array($rule->getId(), $appliedRuleIds)) {
221192
unset($itemsToApplyRules[$key]);
222193
}
223194

224-
$totalDiscount[$item->getId()] = $item->getBaseDiscountAmount();
195+
$totalDiscount += $this->getAggregatedItemBaseDiscount($item);
225196
}
226-
$address->setBaseDiscountAmount(array_sum(array_values($totalDiscount)));
197+
$address->setBaseDiscountAmount($totalDiscount);
227198
}
228199
$this->calculator->initTotals($items, $address);
229200
foreach ($items as $item) {
@@ -271,6 +242,104 @@ protected function aggregateItemDiscount(
271242
return $this;
272243
}
273244

245+
/**
246+
* Get quote items
247+
*
248+
* @param Quote $quote
249+
* @param AddressInterface $address
250+
* @return Address\Item[]
251+
* @throws \Zend_Db_Select_Exception
252+
*/
253+
private function extractQuoteItems(Quote $quote, AddressInterface $address): array
254+
{
255+
$items = [];
256+
foreach ($quote->getAllAddresses() as $quoteAddress) {
257+
foreach ($quoteAddress->getAllItems() as $item) {
258+
$item->setAppliedRuleIds(null);
259+
if ($item->getExtensionAttributes()) {
260+
$item->getExtensionAttributes()->setDiscounts(null);
261+
}
262+
$item->setDiscountAmount(0);
263+
$item->setBaseDiscountAmount(0);
264+
$item->setDiscountPercent(0);
265+
if ($item->getChildren() && $item->isChildrenCalculated()) {
266+
foreach ($item->getChildren() as $child) {
267+
$child->setDiscountAmount(0);
268+
$child->setBaseDiscountAmount(0);
269+
$child->setDiscountPercent(0);
270+
}
271+
}
272+
$item->getAddress()->setBaseDiscountAmount(0);
273+
$items[] = $item;
274+
}
275+
}
276+
277+
if ($items) {
278+
$items = $this->calculator->sortItemsByPriority($items, $address);
279+
}
280+
281+
return $items;
282+
}
283+
284+
/**
285+
* Get shipping items
286+
*
287+
* @param ShippingAssignmentInterface $shippingAssignment
288+
* @return array
289+
*/
290+
private function getShippingItems(ShippingAssignmentInterface $shippingAssignment): array
291+
{
292+
$itemsAggregate = [];
293+
foreach ($shippingAssignment->getItems() as $item) {
294+
$itemId = $item->getId();
295+
$itemsAggregate[$itemId] = $item;
296+
}
297+
298+
return $itemsAggregate;
299+
}
300+
301+
/**
302+
* Prepare quote address
303+
*
304+
* @param ShippingAssignmentInterface $shippingAssignment
305+
* @param Quote $quote
306+
* @return AddressInterface
307+
*/
308+
private function getAddress(ShippingAssignmentInterface $shippingAssignment, Quote $quote): AddressInterface
309+
{
310+
$address = $shippingAssignment->getShipping()->getAddress();
311+
if ($quote->currentPaymentWasSet()) {
312+
$address->setPaymentMethod($quote->getPayment()->getMethod());
313+
}
314+
315+
$this->calculator->reset($address);
316+
$address->setDiscountDescription([]);
317+
$address->getExtensionAttributes()->setDiscounts([]);
318+
$address->setCartFixedRules([]);
319+
$address->setBaseDiscountAmount(0);
320+
return $address;
321+
}
322+
323+
/**
324+
* Calculate quote item base discount
325+
*
326+
* @param Item $item
327+
* @return float
328+
*/
329+
private function getAggregatedItemBaseDiscount(Item $item): float
330+
{
331+
$baseDiscount = 0;
332+
if ($item->getChildren() && $item->isChildrenCalculated()) {
333+
foreach ($item->getChildren() as $child) {
334+
$baseDiscount += $child->getBaseDiscountAmount();
335+
}
336+
} else {
337+
$baseDiscount = $item->getBaseDiscountAmount();
338+
}
339+
340+
return $baseDiscount;
341+
}
342+
274343
/**
275344
* Distribute discount at parent item to children items
276345
*

0 commit comments

Comments
 (0)