Skip to content

Commit fedc411

Browse files
committed
Merge branch 'ACP2E-3666' of https://github.com/adobe-commerce-tier-4/magento2ce into PR-02-28-2025
2 parents a7627fc + c6c396f commit fedc411

File tree

2 files changed

+121
-43
lines changed

2 files changed

+121
-43
lines changed

app/code/Magento/SalesRule/Model/Rule/Condition/Product/Subselect.php

Lines changed: 88 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,33 @@
11
<?php
22
/**
3-
* Copyright © Magento, Inc. All rights reserved.
4-
* See COPYING.txt for license details.
3+
* Copyright 2011 Adobe
4+
* All Rights Reserved.
55
*/
66
namespace Magento\SalesRule\Model\Rule\Condition\Product;
77

8+
use Magento\Catalog\Model\Product\Type;
9+
use Magento\Framework\Model\AbstractModel;
810
use Magento\Quote\Api\Data\TotalsItemInterface;
11+
use Magento\Rule\Model\Condition\Context;
12+
use Magento\SalesRule\Model\Rule\Condition\Product;
913

1014
/**
11-
* Subselect conditions for product.
15+
* SubSelect conditions for product.
1216
*/
13-
class Subselect extends \Magento\SalesRule\Model\Rule\Condition\Product\Combine
17+
class Subselect extends Combine
1418
{
1519
/**
16-
* @param \Magento\Rule\Model\Condition\Context $context
17-
* @param \Magento\SalesRule\Model\Rule\Condition\Product $ruleConditionProduct
20+
* @param Context $context
21+
* @param Product $ruleConditionProduct
1822
* @param array $data
1923
*/
2024
public function __construct(
21-
\Magento\Rule\Model\Condition\Context $context,
22-
\Magento\SalesRule\Model\Rule\Condition\Product $ruleConditionProduct,
25+
Context $context,
26+
Product $ruleConditionProduct,
2327
array $data = []
2428
) {
2529
parent::__construct($context, $ruleConditionProduct, $data);
26-
$this->setType(\Magento\SalesRule\Model\Rule\Condition\Product\Subselect::class)->setValue(null);
30+
$this->setType(Subselect::class)->setValue(null);
2731
}
2832

2933
/**
@@ -143,43 +147,94 @@ public function asHtml()
143147
}
144148

145149
/**
146-
* Validate
150+
* Validate subSelect conditions, base_row_total and attribute
147151
*
148-
* @param \Magento\Framework\Model\AbstractModel $model
152+
* @param AbstractModel $model
149153
* @return bool
150154
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
151155
*/
152-
public function validate(\Magento\Framework\Model\AbstractModel $model)
156+
public function validate(AbstractModel $model)
153157
{
158+
$subSelectConditionsFlag = true;
154159
if (!$this->getConditions()) {
155160
return false;
156161
}
157162
$attr = $this->getAttribute();
158163
$total = 0;
159-
foreach ($model->getQuote()->getAllVisibleItems() as $item) {
160-
$hasValidChild = false;
161-
$useChildrenTotal = ($item->getProductType() == \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE);
162-
$childrenAttrTotal = 0;
163-
$children = $item->getChildren();
164-
if (!empty($children)) {
165-
foreach ($children as $child) {
166-
if (parent::validate($child)) {
167-
$hasValidChild = true;
168-
if ($useChildrenTotal) {
169-
$childrenAttrTotal += $child->getData($attr);
170-
}
171-
}
172-
}
164+
$isMultiShipping = (bool) $model->getQuote()->getIsMultiShipping();
165+
$items = $isMultiShipping ? $model->getAllItems() : $model->getQuote()->getAllVisibleItems();
166+
foreach ($items as $item) {
167+
if ($isMultiShipping) {
168+
$subSelectConditionsFlag = $this->validateSubSelectConditions($item);
173169
}
174-
if ($attr !== TotalsItemInterface::KEY_BASE_ROW_TOTAL) {
175-
$childrenAttrTotal *= $item->getQty();
170+
$total = $this->getBaseRowTotalForChildrenProduct($item, $attr, $total);
171+
}
172+
return $subSelectConditionsFlag && $this->validateAttribute($total);
173+
}
174+
175+
/**
176+
* Check subSelect conditions to verify if they are met
177+
*
178+
* @param mixed $item
179+
* @return bool
180+
*/
181+
private function validateSubSelectConditions(mixed $item): bool
182+
{
183+
$subSelectConditionsFlag = true;
184+
$all = $this->getAggregator() === 'all';
185+
$true = (bool)$this->getValue();
186+
$conditions = $this->getConditions();
187+
if (!empty($conditions)) {
188+
foreach ($conditions as $cond) {
189+
if ($item instanceof AbstractModel) {
190+
$validated = $cond->validate($item);
191+
} else {
192+
$validated = $cond->validateByEntityId($item);
193+
}
194+
if ($all && $validated !== $true) {
195+
$subSelectConditionsFlag = false;
196+
break;
197+
} elseif (!$all && $validated === $true) {
198+
continue;
199+
}
176200
}
177-
if ($hasValidChild || parent::validate($item)) {
178-
$total += ($hasValidChild && $useChildrenTotal && $childrenAttrTotal > 0)
179-
? $childrenAttrTotal
180-
: $item->getData($attr);
201+
}
202+
return $subSelectConditionsFlag;
203+
}
204+
205+
/**
206+
* Get base row total for children product for bundle and configurable product
207+
*
208+
* @param mixed $item
209+
* @param mixed $attr
210+
* @param int $total
211+
* @return int|mixed
212+
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
213+
*/
214+
private function getBaseRowTotalForChildrenProduct(mixed $item, mixed $attr, int $total): mixed
215+
{
216+
$hasValidChild = false;
217+
$useChildrenTotal = ($item->getProductType() == Type::TYPE_BUNDLE);
218+
$childrenAttrTotal = 0;
219+
$children = $item->getChildren();
220+
if (!empty($children)) {
221+
foreach ($children as $child) {
222+
if (parent::validate($child)) {
223+
$hasValidChild = true;
224+
if ($useChildrenTotal) {
225+
$childrenAttrTotal += $child->getData($attr);
226+
}
227+
}
181228
}
182229
}
183-
return $this->validateAttribute($total);
230+
if ($attr !== TotalsItemInterface::KEY_BASE_ROW_TOTAL) {
231+
$childrenAttrTotal *= $item->getQty();
232+
}
233+
if ($hasValidChild || parent::validate($item)) {
234+
$total += ($hasValidChild && $useChildrenTotal && $childrenAttrTotal > 0)
235+
? $childrenAttrTotal
236+
: $item->getData($attr);
237+
}
238+
return $total;
184239
}
185240
}

app/code/Magento/SalesRule/Test/Unit/Model/Rule/Condition/Product/SubselectTest.php

Lines changed: 33 additions & 10 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 2021 Adobe
4+
* All Rights Reserved.
55
*/
66
declare(strict_types=1);
77

@@ -55,7 +55,7 @@ protected function setUp(): void
5555
->getMock();
5656
$this->abstractModel = $this->getMockBuilder(AbstractModel::class)
5757
->disableOriginalConstructor()
58-
->addMethods(['getQuote', 'getProduct'])
58+
->addMethods(['getQuote', 'getAllItems', 'getProduct'])
5959
->getMockForAbstractClass();
6060
$this->productMock = $this->getMockBuilder(Product::class)
6161
->onlyMethods(['getData', 'getResource', 'hasData'])
@@ -64,6 +64,7 @@ protected function setUp(): void
6464
->getMock();
6565
$this->quoteMock = $this->getMockBuilder(Quote::class)
6666
->disableOriginalConstructor()
67+
->addMethods(['getIsMultiShipping'])
6768
->onlyMethods(['getAllVisibleItems'])
6869
->getMock();
6970
$this->quoteItemMock = $this->getMockBuilder(Item::class)
@@ -87,6 +88,9 @@ protected function setUp(): void
8788
$this->abstractModel->expects($this->any())
8889
->method('getQuote')
8990
->willReturn($this->quoteMock);
91+
$this->abstractModel->expects($this->any())
92+
->method('getAllItems')
93+
->willReturn([$this->quoteItemMock]);
9094
$this->abstractModel->expects($this->any())
9195
->method('getProduct')
9296
->willReturn($this->productMock);
@@ -102,6 +106,7 @@ protected function setUp(): void
102106
*
103107
* @param array|null $attributeDetails
104108
* @param array $productDetails
109+
* @param bool $isMultiShipping
105110
* @param bool $expectedResult
106111
* @return void
107112
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
@@ -110,6 +115,7 @@ protected function setUp(): void
110115
public function testValidateForFixedBundleProduct(
111116
?array $attributeDetails,
112117
array $productDetails,
118+
bool $isMultiShipping,
113119
bool $expectedResult
114120
): void {
115121
$attributeResource = new DataObject();
@@ -142,6 +148,9 @@ public function testValidateForFixedBundleProduct(
142148
$this->ruleConditionMock->expects($this->any())->method('getOperatorForValidate')
143149
->willReturn($attributeDetails['attributeOperator']);
144150
}
151+
$this->quoteMock->expects($this->any())
152+
->method('getIsMultiShipping')
153+
->willReturn($isMultiShipping);
145154
$this->quoteItemMock->expects($this->any())
146155
->method('getProductType')
147156
->willReturn($productDetails['type']);
@@ -197,7 +206,7 @@ public function testValidateForFixedBundleProduct(
197206
public static function dataProviderForFixedBundleProduct(): array
198207
{
199208
return [
200-
'validate true for bundle product data with conditions' =>
209+
'validate true for bundle product data with conditions with multi shipping' =>
201210
[
202211
[
203212
'id' => 'attribute_set_id',
@@ -214,9 +223,10 @@ public static function dataProviderForFixedBundleProduct(): array
214223
'baseRowTotal' => 100,
215224
'valueParsed' => 100
216225
],
226+
true,
217227
true
218228
],
219-
'validate false for bundle product data with conditions' =>
229+
'validate false for bundle product data with conditions w/o multi shipping' =>
220230
[
221231
[
222232
'id' => 'attribute_set_id',
@@ -233,9 +243,10 @@ public static function dataProviderForFixedBundleProduct(): array
233243
'baseRowTotal' => 100,
234244
'valueParsed' => 50
235245
],
246+
false,
236247
false
237248
],
238-
'validate product data without conditions with bundle product' =>
249+
'validate product data without conditions with bundle product w/o multi shipping' =>
239250
[
240251
null,
241252
[
@@ -247,9 +258,11 @@ public static function dataProviderForFixedBundleProduct(): array
247258
'baseRowTotal' => 100,
248259
'valueParsed' => 100
249260
],
261+
false,
250262
false
251263
],
252-
'validate true for bundle product data with conditions for attribute base_row_total' =>
264+
'validate true for bundle product
265+
data with conditions for attribute base_row_total w/o multi shipping' =>
253266
[
254267
[
255268
'id' => 'attribute_set_id',
@@ -266,9 +279,10 @@ public static function dataProviderForFixedBundleProduct(): array
266279
'baseRowTotal' => 200,
267280
'valueParsed' => 200
268281
],
282+
false,
269283
false
270284
],
271-
'validate true for simple product data with conditions' =>
285+
'validate true for simple product data with conditions with multi shipping' =>
272286
[
273287
[
274288
'id' => 'attribute_set_id',
@@ -285,9 +299,10 @@ public static function dataProviderForFixedBundleProduct(): array
285299
'baseRowTotal' => 100,
286300
'valueParsed' => 100
287301
],
302+
true,
288303
true
289304
],
290-
'validate false for simple product data with conditions' =>
305+
'validate false for simple product data with conditions w/o multi shipping' =>
291306
[
292307
[
293308
'id' => 'attribute_set_id',
@@ -304,6 +319,7 @@ public static function dataProviderForFixedBundleProduct(): array
304319
'baseRowTotal' => 100,
305320
'valueParsed' => 50
306321
],
322+
false,
307323
false
308324
]
309325
];
@@ -314,6 +330,7 @@ public static function dataProviderForFixedBundleProduct(): array
314330
*
315331
* @param array|null $attributeDetails
316332
* @param array $productDetails
333+
* @param bool $isMultiShipping
317334
* @param bool $expectedResult
318335
* @return void
319336
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
@@ -322,6 +339,7 @@ public static function dataProviderForFixedBundleProduct(): array
322339
public function testValidateForBaseTotalInclTax(
323340
?array $attributeDetails,
324341
array $productDetails,
342+
bool $isMultiShipping,
325343
bool $expectedResult
326344
):void {
327345
$attributeResource = new DataObject();
@@ -355,6 +373,9 @@ public function testValidateForBaseTotalInclTax(
355373
->willReturn($attributeDetails['attributeOperator']);
356374
}
357375

376+
$this->quoteMock->expects($this->any())
377+
->method('getIsMultiShipping')
378+
->willReturn($isMultiShipping);
358379
/* @var AbstractItem|MockObject $quoteItemMock */
359380
$this->productMock->expects($this->any())
360381
->method('getResource')
@@ -380,7 +401,8 @@ public function testValidateForBaseTotalInclTax(
380401
public static function dataProviderForBaseTotalInclTax(): array
381402
{
382403
return [
383-
'validate true for product data with conditions for attribute base_row_total_incl_tax' =>
404+
'validate true for product data with conditions
405+
for attribute base_row_total_incl_tax w/o multi shipping' =>
384406
[
385407
[
386408
'id' => 'attribute_set_id',
@@ -397,6 +419,7 @@ public static function dataProviderForBaseTotalInclTax(): array
397419
'baseRowTotalInclTax' => 200,
398420
'valueParsed' => 200
399421
],
422+
false,
400423
false
401424
]
402425
];

0 commit comments

Comments
 (0)