Skip to content

Commit 31e0b81

Browse files
committed
MAGETWO-91668: Cart rule using subselection condition incorrectly gives discount
1 parent 0fdd197 commit 31e0b81

File tree

3 files changed

+140
-1
lines changed

3 files changed

+140
-1
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1399,7 +1399,7 @@ public function getAllVisibleItems()
13991399
{
14001400
$items = [];
14011401
foreach ($this->getItemsCollection() as $item) {
1402-
if (!$item->isDeleted() && !$item->getParentItemId()) {
1402+
if (!$item->isDeleted() && !$item->getParentItemId() && !$item->getParentItem()) {
14031403
$items[] = $item;
14041404
}
14051405
}

dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Condition/ProductTest.php

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@
66

77
namespace Magento\SalesRule\Model\Rule\Condition;
88

9+
use Magento\Quote\Api\CartRepositoryInterface;
10+
use Magento\Framework\Api\SearchCriteriaBuilder;
11+
use Magento\Quote\Api\Data\CartInterface;
12+
use Magento\SalesRule\Api\RuleRepositoryInterface;
13+
14+
/**
15+
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
16+
*/
917
class ProductTest extends \PHPUnit\Framework\TestCase
1018
{
1119
/**
@@ -98,4 +106,71 @@ public function validateProductConditionDataProvider()
98106
]
99107
];
100108
}
109+
110+
/**
111+
* Ensure that SalesRules filtering on quote items quantity validates configurable product correctly
112+
*
113+
* 1. Load a quote with a configured product and a sales rule set to filter items with quantity 2.
114+
* 2. Attempt to validate the sales rule against the quote and assert the output is negative.
115+
*
116+
* @magentoDataFixture Magento/ConfigurableProduct/_files/quote_with_configurable_product.php
117+
* @magentoDataFixture Magento/SalesRule/_files/cart_rule_10_percent_off.php
118+
*/
119+
public function testValidateQtySalesRuleWithConfigurable()
120+
{
121+
// Load the quote that contains a child of a configurable product with quantity 1.
122+
$quote = $this->getQuote('test_cart_with_configurable');
123+
124+
// Load the SalesRule looking for products with quantity 2.
125+
$rule = $this->getSalesRule('10% Off on orders with two items');
126+
127+
$this->assertFalse(
128+
$rule->validate($quote->getBillingAddress())
129+
);
130+
}
131+
132+
/**
133+
* Gets quote by reserved order id.
134+
*
135+
* @param string $reservedOrderId
136+
* @return CartInterface
137+
*/
138+
private function getQuote($reservedOrderId)
139+
{
140+
/** @var SearchCriteriaBuilder $searchCriteriaBuilder */
141+
$searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class);
142+
$searchCriteria = $searchCriteriaBuilder->addFilter('reserved_order_id', $reservedOrderId)
143+
->create();
144+
145+
/** @var CartRepositoryInterface $quoteRepository */
146+
$quoteRepository = $this->objectManager->get(CartRepositoryInterface::class);
147+
$items = $quoteRepository->getList($searchCriteria)->getItems();
148+
return array_pop($items);
149+
}
150+
151+
/**
152+
* Gets rule by name.
153+
*
154+
* @param string $name
155+
* @return \Magento\SalesRule\Model\Rule
156+
* @throws \Magento\Framework\Exception\InputException
157+
* @throws \Magento\Framework\Exception\NoSuchEntityException
158+
*/
159+
private function getSalesRule(string $name): \Magento\SalesRule\Model\Rule
160+
{
161+
/** @var SearchCriteriaBuilder $searchCriteriaBuilder */
162+
$searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class);
163+
$searchCriteria = $searchCriteriaBuilder->addFilter('name', $name)
164+
->create();
165+
166+
/** @var CartRepositoryInterface $quoteRepository */
167+
$ruleRepository = $this->objectManager->get(RuleRepositoryInterface::class);
168+
$items = $ruleRepository->getList($searchCriteria)->getItems();
169+
170+
$rule = array_pop($items);
171+
/** @var \Magento\SalesRule\Model\Converter\ToModel $converter */
172+
$converter = $this->objectManager->get(\Magento\SalesRule\Model\Converter\ToModel::class);
173+
174+
return $converter->toModel($rule);
175+
}
101176
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
use Magento\Customer\Model\GroupManagement;
9+
use Magento\SalesRule\Model\Rule;
10+
use Magento\Store\Model\StoreManagerInterface;
11+
use Magento\TestFramework\Helper\Bootstrap;
12+
13+
$objectManager = Bootstrap::getObjectManager();
14+
$websiteId = Bootstrap::getObjectManager()->get(StoreManagerInterface::class)
15+
->getWebsite()
16+
->getId();
17+
18+
/** @var Rule $salesRule */
19+
$salesRule = $objectManager->create(Rule::class);
20+
$salesRule->setData(
21+
[
22+
'name' => '10% Off on orders with two items',
23+
'is_active' => 1,
24+
'customer_group_ids' => [GroupManagement::NOT_LOGGED_IN_ID],
25+
'coupon_type' => Rule::COUPON_TYPE_NO_COUPON,
26+
'simple_action' => 'by_percent',
27+
'discount_amount' => 10,
28+
'discount_step' => 0,
29+
'stop_rules_processing' => 1,
30+
'website_ids' => [$websiteId]
31+
]
32+
);
33+
34+
$salesRule->getConditions()->loadArray([
35+
'type' => \Magento\SalesRule\Model\Rule\Condition\Combine::class,
36+
'attribute' => null,
37+
'operator' => null,
38+
'value' => '1',
39+
'is_value_processed' => null,
40+
'aggregator' => 'all',
41+
'conditions' =>
42+
[
43+
[
44+
'type' => \Magento\SalesRule\Model\Rule\Condition\Product\Subselect::class,
45+
'attribute' => 'qty',
46+
'operator' => '==',
47+
'value' => '2',
48+
'is_value_processed' => null,
49+
'aggregator' => 'all',
50+
'conditions' =>
51+
[
52+
[
53+
'type' => \Magento\SalesRule\Model\Rule\Condition\Product::class,
54+
'attribute' => 'attribute_set_id',
55+
'operator' => '==',
56+
'value' => '4',
57+
'is_value_processed' => false,
58+
],
59+
],
60+
],
61+
],
62+
]);
63+
64+
$salesRule->save();

0 commit comments

Comments
 (0)