Skip to content

Commit 1fb2288

Browse files
committed
ACP2E-3666: [Mainline] Cart Price rule is not respecting Multishipping
- Initial commit with tests
1 parent 982b1c4 commit 1fb2288

File tree

2 files changed

+93
-50
lines changed

2 files changed

+93
-50
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
}
162+
157163
$attr = $this->getAttribute();
158164
$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-
}
165+
166+
foreach ($model->getAllItems() as $item) {
167+
$subSelectConditionsFlag = $this->validateSubSelectConditions($item);
168+
if ($subSelectConditionsFlag) {
169+
$total = $this->getBaseRowTotalForChildrenProduct($item, $attr, $total);
173170
}
174-
if ($attr !== TotalsItemInterface::KEY_BASE_ROW_TOTAL) {
175-
$childrenAttrTotal *= $item->getQty();
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: 5 additions & 17 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

@@ -10,7 +10,6 @@
1010
use Magento\Catalog\Model\Product;
1111
use Magento\Framework\DataObject;
1212
use Magento\Framework\Model\AbstractModel;
13-
use Magento\Quote\Model\Quote;
1413
use Magento\Quote\Model\Quote\Item;
1514
use Magento\Quote\Model\Quote\Item\AbstractItem;
1615
use Magento\Rule\Model\Condition\Context;
@@ -37,9 +36,6 @@ class SubselectTest extends TestCase
3736
/** @var Product|MockObject */
3837
private $productMock;
3938

40-
/** @var Quote|MockObject */
41-
private $quoteMock;
42-
4339
/** @var Item|MockObject */
4440
private $quoteItemMock;
4541

@@ -55,17 +51,13 @@ protected function setUp(): void
5551
->getMock();
5652
$this->abstractModel = $this->getMockBuilder(AbstractModel::class)
5753
->disableOriginalConstructor()
58-
->addMethods(['getQuote', 'getProduct'])
54+
->addMethods(['getAllItems', 'getProduct'])
5955
->getMockForAbstractClass();
6056
$this->productMock = $this->getMockBuilder(Product::class)
6157
->onlyMethods(['getData', 'getResource', 'hasData'])
6258
->addMethods(['getOperatorForValidate', 'getValueParsed'])
6359
->disableOriginalConstructor()
6460
->getMock();
65-
$this->quoteMock = $this->getMockBuilder(Quote::class)
66-
->disableOriginalConstructor()
67-
->onlyMethods(['getAllVisibleItems'])
68-
->getMock();
6961
$this->quoteItemMock = $this->getMockBuilder(Item::class)
7062
->addMethods(['getHasChildren', 'getProductId'])
7163
->onlyMethods(
@@ -74,19 +66,15 @@ protected function setUp(): void
7466
'getProduct',
7567
'getProductType',
7668
'getChildren',
77-
'getQuote',
7869
'getAddress',
7970
'getOptionByCode'
8071
]
8172
)
8273
->disableOriginalConstructor()
8374
->getMock();
84-
$this->quoteMock->expects($this->any())
85-
->method('getAllVisibleItems')
86-
->willReturn([$this->quoteItemMock]);
8775
$this->abstractModel->expects($this->any())
88-
->method('getQuote')
89-
->willReturn($this->quoteMock);
76+
->method('getAllItems')
77+
->willReturn([$this->quoteItemMock]);
9078
$this->abstractModel->expects($this->any())
9179
->method('getProduct')
9280
->willReturn($this->productMock);

0 commit comments

Comments
 (0)