Skip to content

Commit 1344d1e

Browse files
committed
ACP2E-330: When partial indexer is executed catalog rule prices get removed
1 parent 724953d commit 1344d1e

File tree

5 files changed

+253
-72
lines changed

5 files changed

+253
-72
lines changed

app/code/Magento/CatalogRuleConfigurable/Plugin/CatalogRule/Model/Rule/ConfigurableProductHandler.php

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,26 @@ public function __construct(
4242
}
4343

4444
/**
45+
* Match configurable child products if configurable product match the condition
46+
*
4547
* @param \Magento\CatalogRule\Model\Rule $rule
46-
* @param array $productIds
48+
* @param \Closure $proceed
4749
* @return array
4850
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
51+
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
4952
*/
50-
public function afterGetMatchingProductIds(\Magento\CatalogRule\Model\Rule $rule, array $productIds)
51-
{
53+
public function aroundGetMatchingProductIds(
54+
\Magento\CatalogRule\Model\Rule $rule,
55+
\Closure $proceed
56+
) {
57+
$productsFilter = $rule->getProductsFilter() ? (array) $rule->getProductsFilter() : [];
58+
if ($productsFilter) {
59+
$parentProductIds = $this->configurable->getParentIdsByChild($productsFilter);
60+
$rule->setProductsFilter(array_unique(array_merge($productsFilter, $parentProductIds)));
61+
}
62+
63+
$productIds = $proceed();
64+
5265
$configurableProductIds = $this->configurableProductsProvider->getIds(array_keys($productIds));
5366
foreach ($configurableProductIds as $productId) {
5467
if (!isset($this->childrenProducts[$productId])) {
@@ -58,11 +71,15 @@ public function afterGetMatchingProductIds(\Magento\CatalogRule\Model\Rule $rule
5871
$parentValidationResult = isset($productIds[$productId])
5972
? array_filter($productIds[$productId])
6073
: [];
74+
$processAllChildren = !$productsFilter || in_array($productId, $productsFilter);
6175
foreach ($subProductIds as $subProductId) {
62-
$childValidationResult = isset($productIds[$subProductId])
63-
? array_filter($productIds[$subProductId])
64-
: [];
65-
$productIds[$subProductId] = $parentValidationResult + $childValidationResult;
76+
if ($processAllChildren || in_array($subProductId, $productsFilter)) {
77+
$childValidationResult = isset($productIds[$subProductId])
78+
? array_filter($productIds[$subProductId])
79+
: [];
80+
$productIds[$subProductId] = $parentValidationResult + $childValidationResult;
81+
}
82+
6683
}
6784
unset($productIds[$productId]);
6885
}

dev/tests/integration/testsuite/Magento/CatalogRuleConfigurable/Model/Indexer/Product/ProductRuleIndexerTest.php

Lines changed: 86 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@
1717

1818
/**
1919
* @magentoDbIsolation disabled
20+
* @magentoAppIsolation enabled
2021
* @magentoAppArea adminhtml
22+
* @magentoDataFixture Magento/Catalog/_files/categories_no_products.php
2123
* @magentoDataFixture Magento/CatalogRuleConfigurable/_files/configurable_product_with_percent_rule.php
24+
* @magentoDataFixture Magento/CatalogRuleConfigurable/_files/configurable_product_with_categories_rule.php
2225
*/
2326
class ProductRuleIndexerTest extends TestCase
2427
{
@@ -49,51 +52,116 @@ protected function setUp(): void
4952
}
5053

5154
/**
55+
* @dataProvider productsDataProvider
56+
* @param string $reindexSku
57+
* @param array $expectedPrices
5258
* @return void
59+
* @throws \Magento\Framework\Exception\NoSuchEntityException
5360
*/
54-
public function testExecute(): void
61+
public function testExecute(string $reindexSku, array $expectedPrices): void
5562
{
56-
$product = $this->productRepository->get('configurable');
63+
$product = $this->productRepository->get($reindexSku);
5764
$this->productRuleIndexer->execute([$product->getId()]);
5865

59-
$product = $this->productRepository->get('simple_10');
60-
$price = $this->getCatalogRulePrice($product);
61-
$this->assertEquals(5, $price);
66+
$this->assertEquals($expectedPrices, $this->getCatalogRulePrices(array_keys($expectedPrices)));
6267
}
6368

6469
/**
70+
* @dataProvider productsDataProvider
71+
* @param string $reindexSku
72+
* @param array $expectedPrices
6573
* @return void
74+
* @throws \Magento\Framework\Exception\LocalizedException
75+
* @throws \Magento\Framework\Exception\NoSuchEntityException
6676
*/
67-
public function testExecuteRow(): void
77+
public function testExecuteRow(string $reindexSku, array $expectedPrices): void
6878
{
69-
$product = $this->productRepository->get('configurable');
79+
$product = $this->productRepository->get($reindexSku);
7080
$this->productRuleIndexer->executeRow($product->getId());
7181

72-
$product = $this->productRepository->get('simple_10');
73-
$price = $this->getCatalogRulePrice($product);
74-
$this->assertEquals(5, $price);
82+
$this->assertEquals($expectedPrices, $this->getCatalogRulePrices(array_keys($expectedPrices)));
7583
}
7684

7785
/**
86+
* @dataProvider productsDataProvider
87+
* @param string $reindexSku
88+
* @param array $expectedPrices
7889
* @return void
90+
* @throws \Magento\Framework\Exception\LocalizedException
91+
* @throws \Magento\Framework\Exception\NoSuchEntityException
7992
*/
80-
public function testExecuteList(): void
93+
public function testExecuteList(string $reindexSku, array $expectedPrices): void
8194
{
82-
$product = $this->productRepository->get('configurable');
95+
$product = $this->productRepository->get($reindexSku);
8396
$this->productRuleIndexer->executeList([$product->getId()]);
8497

85-
$product = $this->productRepository->get('simple_10');
86-
$price = $this->getCatalogRulePrice($product);
87-
$this->assertEquals(5, $price);
98+
$this->assertEquals($expectedPrices, $this->getCatalogRulePrices(array_keys($expectedPrices)));
8899
}
89100

101+
/**
102+
* @return void
103+
*/
90104
public function testExecuteFull(): void
91105
{
92106
$this->productRuleIndexer->executeFull();
93107

94-
$product = $this->productRepository->get('simple_10');
95-
$price = $this->getCatalogRulePrice($product);
96-
$this->assertEquals(5, $price);
108+
$expectedPrices = [
109+
'simple_10' => 5,
110+
'simple_20' => 10,
111+
'simple_30' => 15,
112+
'simple_40' => 20,
113+
];
114+
$this->assertEquals($expectedPrices, $this->getCatalogRulePrices(array_keys($expectedPrices)));
115+
}
116+
117+
/**
118+
* @return array
119+
*/
120+
public function productsDataProvider(): array
121+
{
122+
return [
123+
[
124+
'configurable',
125+
[
126+
'simple_10' => 5,
127+
'simple_20' => 10,
128+
]
129+
],
130+
[
131+
'simple_10',
132+
[
133+
'simple_10' => 5,
134+
]
135+
],
136+
[
137+
'12345',
138+
[
139+
'simple_30' => 15,
140+
'simple_40' => 20,
141+
]
142+
],
143+
[
144+
'simple_30',
145+
[
146+
'simple_30' => 15,
147+
]
148+
],
149+
];
150+
}
151+
152+
/**
153+
* @param array $skus
154+
* @return array
155+
* @throws \Magento\Framework\Exception\NoSuchEntityException
156+
*/
157+
private function getCatalogRulePrices(array $skus): array
158+
{
159+
$actualPrices = [];
160+
foreach ($skus as $sku) {
161+
$product = $this->productRepository->get($sku);
162+
$actualPrices[$sku] = $this->getCatalogRulePrice($product);
163+
}
164+
return $actualPrices;
97165
}
98166

99167
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
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\Catalog\Api\CategoryLinkManagementInterface;
9+
use Magento\Catalog\Api\ProductRepositoryInterface;
10+
use Magento\CatalogRule\Api\CatalogRuleRepositoryInterface;
11+
use Magento\CatalogRule\Model\Rule;
12+
use Magento\CatalogRule\Model\Rule\Condition\Combine;
13+
use Magento\CatalogRule\Model\Rule\Condition\Product;
14+
use Magento\Customer\Model\Group;
15+
use Magento\Store\Api\WebsiteRepositoryInterface;
16+
use Magento\TestFramework\Helper\Bootstrap;
17+
use Magento\TestFramework\Workaround\Override\Fixture\Resolver;
18+
19+
Resolver::getInstance()
20+
->requireDataFixture('Magento/ConfigurableProduct/_files/product_configurable_12345.php');
21+
22+
$objectManager = Bootstrap::getObjectManager();
23+
24+
$productRepository = $objectManager->get(ProductRepositoryInterface::class);
25+
$confProduct = $productRepository->get('12345');
26+
$childProduct = $productRepository->get('simple_30');
27+
$categoryLinkManagement = $objectManager->get(CategoryLinkManagementInterface::class);
28+
$categoryLinkManagement->assignProductToCategories($confProduct->getSku(), [3]);
29+
$categoryLinkManagement->assignProductToCategories($childProduct->getSku(), [3, 6]);
30+
31+
$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class);
32+
$website = $websiteRepository->get('base');
33+
34+
$rule = $objectManager->create(Rule::class);
35+
$rule->loadPost(
36+
[
37+
'name' => 'Categories rule for configurable product',
38+
'is_active' => '1',
39+
'stop_rules_processing' => 0,
40+
'website_ids' => [$website->getId()],
41+
'customer_group_ids' => Group::NOT_LOGGED_IN_ID,
42+
'discount_amount' => 50,
43+
'simple_action' => 'by_percent',
44+
'from_date' => '',
45+
'to_date' => '',
46+
'sort_order' => 0,
47+
'sub_is_enable' => 0,
48+
'sub_discount_amount' => 0,
49+
'conditions' => [
50+
'1' => [
51+
'type' => Combine::class,
52+
'aggregator' => 'all',
53+
'value' => '1',
54+
'new_child' => ''
55+
],
56+
'1--1' => [
57+
'type' => Product::class,
58+
'attribute' => 'category_ids',
59+
'operator' => '()',
60+
'value' => '3'
61+
],
62+
'1--2' => [
63+
'type' => Product::class,
64+
'attribute' => 'category_ids',
65+
'operator' => '!()',
66+
'value' => '6'
67+
],
68+
],
69+
]
70+
);
71+
$ruleRepository = $objectManager->get(CatalogRuleRepositoryInterface::class);
72+
$ruleRepository->save($rule);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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\CatalogRule\Api\CatalogRuleRepositoryInterface;
9+
use Magento\CatalogRule\Model\Indexer\IndexBuilder;
10+
use Magento\CatalogRule\Model\ResourceModel\Rule\Collection;
11+
use Magento\TestFramework\Helper\Bootstrap;
12+
use Magento\TestFramework\Workaround\Override\Fixture\Resolver;
13+
14+
Resolver::getInstance()
15+
->requireDataFixture('Magento/ConfigurableProduct/_files/product_configurable_12345_rollback.php');
16+
17+
$objectManager = Bootstrap::getObjectManager();
18+
19+
$ruleCollection = $objectManager->create(Collection::class)
20+
->addFieldToFilter('name', ['eq' => 'Categories rule for configurable product'])
21+
->setPageSize(1);
22+
$rule = $ruleCollection->getFirstItem();
23+
if ($rule->getId()) {
24+
$ruleRepository = $objectManager->create(CatalogRuleRepositoryInterface::class);
25+
$ruleRepository->delete($rule);
26+
}
27+
28+
$indexBuilder = $objectManager->get(IndexBuilder::class);
29+
$indexBuilder->reindexFull();

0 commit comments

Comments
 (0)