Skip to content

Commit d16e29c

Browse files
committed
MAGETWO-89220: Cart rule using subselection condition incorrectly gives discount
1 parent 323994f commit d16e29c

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
@@ -1400,7 +1400,7 @@ public function getAllVisibleItems()
14001400
{
14011401
$items = [];
14021402
foreach ($this->getItemsCollection() as $item) {
1403-
if (!$item->isDeleted() && !$item->getParentItemId()) {
1403+
if (!$item->isDeleted() && !$item->getParentItemId() && !$item->getParentItem()) {
14041404
$items[] = $item;
14051405
}
14061406
}

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
/**
@@ -94,4 +102,71 @@ public function validateProductConditionDataProvider()
94102
]
95103
];
96104
}
105+
106+
/**
107+
* Ensure that SalesRules filtering on quote items quantity validates configurable product correctly
108+
*
109+
* 1. Load a quote with a configured product and a sales rule set to filter items with quantity 2.
110+
* 2. Attempt to validate the sales rule against the quote and assert the output is negative.
111+
*
112+
* @magentoDataFixture Magento/ConfigurableProduct/_files/quote_with_configurable_product.php
113+
* @magentoDataFixture Magento/SalesRule/_files/cart_rule_10_percent_off.php
114+
*/
115+
public function testValidateQtySalesRuleWithConfigurable()
116+
{
117+
// Load the quote that contains a child of a configurable product with quantity 1.
118+
$quote = $this->getQuote('test_cart_with_configurable');
119+
120+
// Load the SalesRule looking for products with quantity 2.
121+
$rule = $this->getSalesRule('10% Off on orders with two items');
122+
123+
$this->assertFalse(
124+
$rule->validate($quote->getBillingAddress())
125+
);
126+
}
127+
128+
/**
129+
* Gets quote by reserved order id.
130+
*
131+
* @param string $reservedOrderId
132+
* @return CartInterface
133+
*/
134+
private function getQuote($reservedOrderId)
135+
{
136+
/** @var SearchCriteriaBuilder $searchCriteriaBuilder */
137+
$searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class);
138+
$searchCriteria = $searchCriteriaBuilder->addFilter('reserved_order_id', $reservedOrderId)
139+
->create();
140+
141+
/** @var CartRepositoryInterface $quoteRepository */
142+
$quoteRepository = $this->objectManager->get(CartRepositoryInterface::class);
143+
$items = $quoteRepository->getList($searchCriteria)->getItems();
144+
return array_pop($items);
145+
}
146+
147+
/**
148+
* Gets rule by name.
149+
*
150+
* @param string $name
151+
* @return \Magento\SalesRule\Model\Rule
152+
* @throws \Magento\Framework\Exception\InputException
153+
* @throws \Magento\Framework\Exception\NoSuchEntityException
154+
*/
155+
private function getSalesRule(string $name): \Magento\SalesRule\Model\Rule
156+
{
157+
/** @var SearchCriteriaBuilder $searchCriteriaBuilder */
158+
$searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class);
159+
$searchCriteria = $searchCriteriaBuilder->addFilter('name', $name)
160+
->create();
161+
162+
/** @var CartRepositoryInterface $quoteRepository */
163+
$ruleRepository = $this->objectManager->get(RuleRepositoryInterface::class);
164+
$items = $ruleRepository->getList($searchCriteria)->getItems();
165+
166+
$rule = array_pop($items);
167+
/** @var \Magento\SalesRule\Model\Converter\ToModel $converter */
168+
$converter = $this->objectManager->get(\Magento\SalesRule\Model\Converter\ToModel::class);
169+
170+
return $converter->toModel($rule);
171+
}
97172
}
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)