Skip to content

Commit b0abb22

Browse files
committed
Merge remote-tracking branch 'origin/ACP2E-3973' into PR_2025_08_04_chittima
2 parents f39080e + 0bdc27f commit b0abb22

File tree

4 files changed

+392
-42
lines changed

4 files changed

+392
-42
lines changed

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

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,6 @@ public function collect(
191191
$address->setBaseDiscountAmount(0);
192192
/** @var Rule $rule */
193193
foreach ($rules as $rule) {
194-
$ruleTotalDiscount = 0;
195194
/** @var Item $item */
196195
foreach ($itemsToApplyRules as $key => $item) {
197196
if ($item->getNoDiscount() || !$this->calculator->canApplyDiscount($item) || $item->getParentItem()) {
@@ -220,15 +219,27 @@ public function collect(
220219
if ($rule->getStopRulesProcessing() && in_array($rule->getId(), $appliedRuleIds)) {
221220
unset($itemsToApplyRules[$key]);
222221
}
223-
222+
}
223+
$baseDiscountAmount = 0;
224+
$discountAmount = 0;
225+
// $itemsAggregate are items specific to the current shipping address
226+
foreach ($itemsAggregate as $item) {
227+
if ($item->getParentItem()) {
228+
continue;
229+
}
224230
if ($item->getChildren() && $item->isChildrenCalculated()) {
225231
foreach ($item->getChildren() as $child) {
226-
$ruleTotalDiscount += $child->getBaseDiscountAmount();
232+
$baseDiscountAmount += $child->getBaseDiscountAmount();
233+
$discountAmount += $child->getDiscountAmount();
227234
}
228235
}
229-
$ruleTotalDiscount += $item->getBaseDiscountAmount();
236+
$baseDiscountAmount += $item->getBaseDiscountAmount();
237+
$discountAmount += $item->getDiscountAmount();
230238
}
231-
$address->setBaseDiscountAmount($ruleTotalDiscount);
239+
$address->setBaseDiscountAmount(-$baseDiscountAmount);
240+
$address->setDiscountAmount(-$discountAmount);
241+
$address->setBaseSubtotalWithDiscount($address->getBaseSubtotal() - $baseDiscountAmount);
242+
$address->setSubtotalWithDiscount($address->getSubtotal() - $discountAmount);
232243
}
233244
$this->calculator->initTotals($items, $address);
234245
foreach ($items as $item) {

app/code/Magento/SalesRule/Test/Fixture/Rule.php

Lines changed: 115 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?php
22
/**
3-
* Copyright © Magento, Inc. All rights reserved.
4-
* See COPYING.txt for license details.
3+
* Copyright 2022 Adobe
4+
* All Rights Reserved.
55
*/
66
declare(strict_types=1);
77

@@ -16,6 +16,71 @@
1616
use Magento\TestFramework\Fixture\Data\ProcessorInterface;
1717
use Magento\TestFramework\Fixture\RevertibleDataFixtureInterface;
1818

19+
/**
20+
* Cart price rule fixture
21+
*
22+
* Example 1: Using array list: (base_subtotal > 1000) AND (sku in (simple1,simple3))
23+
*
24+
* ```php
25+
* #[
26+
* DataFixture(
27+
* RuleFixture::class,
28+
* [
29+
* 'discount_amount' => 25,
30+
* 'conditions' => [
31+
* [
32+
* 'attribute' => 'base_subtotal',
33+
* 'operator' => '>',
34+
* 'value' => 1000
35+
* ],
36+
* ],
37+
* 'actions' => [
38+
* [
39+
* 'attribute' => 'sku',
40+
* 'operator' => '()',
41+
* 'value' => 'simple1,simple3'
42+
* ]
43+
* ]
44+
* ],
45+
* 'rule'
46+
* )
47+
* ]
48+
* ```
49+
*
50+
* Example 2: Using associative conditions: (category_ids in (1, 2) AND attribute_set_id=default) OR (sku=simple3)
51+
*
52+
* ```php
53+
* #[
54+
* DataFixture(
55+
* RuleFixture::class,
56+
* [
57+
* 'discount_amount' => 25,
58+
* 'actions' => [
59+
* 'aggregator' => 'any',
60+
* 'conditions' => [
61+
* [
62+
* [
63+
* 'attribute' => 'category_ids',
64+
* 'operator' => '()',
65+
* 'value' => '1,2',
66+
* ],
67+
* [
68+
* 'attribute' => 'attribute_set_id',
69+
* 'value' => 'default',
70+
* ],
71+
* ],
72+
* [
73+
* 'attribute' => 'sku',
74+
* 'value' => 'simple3'
75+
* ]
76+
* ],
77+
* ],
78+
* ],
79+
* 'rule'
80+
* )
81+
* ]
82+
* ```
83+
*/
1984
class Rule implements RevertibleDataFixtureInterface
2085
{
2186
private const DEFAULT_DATA = [
@@ -145,32 +210,17 @@ public function revert(DataObject $data): void
145210
private function prepareData(array $data): array
146211
{
147212
$data = array_merge($this->prepareDefaultData(), $data);
148-
$data['conditions'] = $data['conditions'] ?? [];
149-
$data['actions'] = $data['actions'] ?? [];
150-
151-
if ($data['conditions'] instanceof DataObject) {
152-
$data['conditions'] = $data['conditions']->toArray();
153-
} else {
154-
$conditions = $data['conditions'];
155-
$data['conditions'] = Conditions::DEFAULT_DATA;
156-
foreach ($conditions as $condition) {
157-
$data['conditions']['conditions'][] = $condition instanceof DataObject
158-
? $condition->toArray()
159-
: $condition;
160-
}
161-
}
162213

163-
if ($data['actions'] instanceof DataObject) {
164-
$data['actions'] = $data['actions']->toArray();
165-
} else {
166-
$conditions = $data['actions'];
167-
$data['actions'] = ProductConditions::DEFAULT_DATA;
168-
foreach ($conditions as $condition) {
169-
$data['actions']['conditions'][] = $condition instanceof DataObject
170-
? $condition->toArray()
171-
: $condition;
172-
}
173-
}
214+
$data['conditions'] = $this->prepareConditionsData(
215+
$data['conditions'] instanceof DataObject ? $data['conditions']->toArray() : $data['conditions'],
216+
Conditions::DEFAULT_DATA,
217+
AddressCondition::DEFAULT_DATA
218+
);
219+
$data['actions'] = $this->prepareConditionsData(
220+
$data['actions'] instanceof DataObject ? $data['actions']->toArray() : $data['actions'],
221+
ProductConditions::DEFAULT_DATA,
222+
ProductCondition::DEFAULT_DATA
223+
);
174224

175225
if (!empty($data['coupon_code'])) {
176226
$data['coupon_type'] = \Magento\SalesRule\Model\Rule::COUPON_TYPE_SPECIFIC;
@@ -179,6 +229,44 @@ private function prepareData(array $data): array
179229
return $this->dataProcessor->process($this, $data);
180230
}
181231

232+
/**
233+
* Prepare conditions data
234+
*
235+
* @param array $conditions
236+
* @param array $defaultConditionsData
237+
* @param array $defaultConditionData
238+
* @return array
239+
*/
240+
private function prepareConditionsData(
241+
array $conditions,
242+
array $defaultConditionsData,
243+
array $defaultConditionData
244+
): array {
245+
$conditionsArray = array_is_list($conditions)
246+
? ['conditions' => $conditions]
247+
: $conditions;
248+
$conditionsArray += $defaultConditionsData;
249+
$subConditions = $conditionsArray['conditions'];
250+
$conditionsArray['conditions'] = [];
251+
foreach ($subConditions as $condition) {
252+
$conditionArray = $condition instanceof DataObject
253+
? $condition->toArray()
254+
: $condition;
255+
// Condition is a combine
256+
if (array_is_list($conditionArray) || isset($conditionArray['conditions'])) {
257+
$conditionArray = $this->prepareConditionsData(
258+
$conditionArray,
259+
$defaultConditionsData,
260+
$defaultConditionData
261+
);
262+
} else {
263+
$conditionArray += $defaultConditionData;
264+
}
265+
$conditionsArray['conditions'][] = $conditionArray;
266+
}
267+
return $conditionsArray;
268+
}
269+
182270
/**
183271
* Prepares rule default data
184272
*

app/code/Magento/SalesRule/Test/Unit/Model/Quote/DiscountTest.php

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use Magento\Framework\Event\Manager;
1212
use Magento\Framework\Event\ManagerInterface;
1313
use Magento\Framework\Pricing\PriceCurrencyInterface;
14+
use Magento\Framework\TestFramework\Unit\Matcher\MethodInvokedAtIndex;
1415
use Magento\Quote\Api\Data\ShippingAssignmentInterface;
1516
use Magento\Quote\Api\Data\ShippingInterface;
1617
use Magento\Quote\Model\Quote;
@@ -426,7 +427,7 @@ public function testCollectAddressBaseDiscountAmountIncludingItemChildren(): voi
426427
)
427428
->disableOriginalConstructor()
428429
->getMock();
429-
$total->expects($this->any())->method('getBaseDiscountAmount')->willReturn(20.00);
430+
$total->expects($this->any())->method('getBaseDiscountAmount')->willReturn(-20.00);
430431

431432
$store = $this->createMock(Store::class);
432433
$this->storeManagerMock->expects($this->once())->method('getStore')->with($storeId)->willReturn($store);
@@ -463,8 +464,8 @@ public function testCollectAddressBaseDiscountAmountIncludingItemChildren(): voi
463464
->addMethods(['getBaseDiscountAmount'])
464465
->disableOriginalConstructor()
465466
->getMock();
466-
$item->expects($this->exactly(2))->method('getChildren')->willReturn([$child]);
467-
$item->expects($this->once())->method('isChildrenCalculated')->willReturn(true);
467+
$item->expects($this->any())->method('getChildren')->willReturn([$child]);
468+
$item->expects($this->any())->method('isChildrenCalculated')->willReturn(true);
468469
$index = 1;
469470
$child->expects($this->any())->method('getBaseDiscountAmount')->willReturnCallback(function () use (&$index) {
470471
$value = $index * 10;
@@ -479,14 +480,23 @@ public function testCollectAddressBaseDiscountAmountIncludingItemChildren(): voi
479480
->willReturnArgument(0);
480481

481482
$this->addressMock->expects($this->exactly(5))
483+
->method('setBaseDiscountAmount');
484+
485+
$this->addressMock->expects(new MethodInvokedAtIndex(0))
486+
->method('setBaseDiscountAmount')
487+
->with(0);
488+
$this->addressMock->expects(new MethodInvokedAtIndex(1))
489+
->method('setBaseDiscountAmount')
490+
->with(0);
491+
$this->addressMock->expects(new MethodInvokedAtIndex(2))
492+
->method('setBaseDiscountAmount')
493+
->with(-10);
494+
$this->addressMock->expects(new MethodInvokedAtIndex(3))
495+
->method('setBaseDiscountAmount')
496+
->with(-20);
497+
$this->addressMock->expects(new MethodInvokedAtIndex(4))
482498
->method('setBaseDiscountAmount')
483-
->with($this->logicalOr(
484-
$this->equalTo(0),
485-
$this->equalTo(10),
486-
$this->equalTo(20),
487-
$this->equalTo(20),
488-
$this->equalTo(20.00)
489-
));
499+
->with(-20);
490500

491501
$this->discount->collect($quote, $this->shippingAssignmentMock, $total);
492502
}

0 commit comments

Comments
 (0)