Skip to content

Commit 7573375

Browse files
Merge branch 'MC-31025' into 2.4-develop-com-pr6
2 parents 6fac1fe + 55337b9 commit 7573375

File tree

9 files changed

+608
-97
lines changed

9 files changed

+608
-97
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
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+
namespace Magento\TestFramework\Catalog\Model\Product\Price;
9+
10+
use Magento\Catalog\Model\Indexer\Product\Price\PriceTableResolver;
11+
use Magento\Catalog\Model\ResourceModel\Product as ProductResource;
12+
use Magento\Customer\Model\Indexer\CustomerGroupDimensionProvider;
13+
use Magento\Framework\Indexer\DimensionFactory;
14+
use Magento\Store\Model\Indexer\WebsiteDimensionProvider;
15+
16+
/**
17+
* Search and return price data from price index table.
18+
*/
19+
class GetPriceIndexDataByProductId
20+
{
21+
/**
22+
* @var ProductResource
23+
*/
24+
private $productResource;
25+
26+
/**
27+
* @var PriceTableResolver
28+
*/
29+
private $priceTableResolver;
30+
31+
/**
32+
* @var DimensionFactory
33+
*/
34+
private $dimensionFactory;
35+
36+
/**
37+
* @param ProductResource $productResource
38+
* @param PriceTableResolver $priceTableResolver
39+
* @param DimensionFactory $dimensionFactory
40+
*/
41+
public function __construct(
42+
ProductResource $productResource,
43+
PriceTableResolver $priceTableResolver,
44+
DimensionFactory $dimensionFactory
45+
) {
46+
$this->productResource = $productResource;
47+
$this->priceTableResolver = $priceTableResolver;
48+
$this->dimensionFactory = $dimensionFactory;
49+
}
50+
51+
/**
52+
* Returns price data by product id.
53+
*
54+
* @param int $productId
55+
* @param int $groupId
56+
* @param int $websiteId
57+
* @return array
58+
*/
59+
public function execute(int $productId, int $groupId, int $websiteId): array
60+
{
61+
$tableName = $this->priceTableResolver->resolve(
62+
'catalog_product_index_price',
63+
[
64+
$this->dimensionFactory->create(WebsiteDimensionProvider::DIMENSION_NAME, (string)$websiteId),
65+
$this->dimensionFactory->create(CustomerGroupDimensionProvider::DIMENSION_NAME, (string)$groupId),
66+
]
67+
);
68+
69+
$select = $this->productResource->getConnection()->select()
70+
->from($tableName)
71+
->where('entity_id = ?', $productId)
72+
->where('customer_group_id = ?', $groupId)
73+
->where('website_id = ?', $websiteId);
74+
75+
return $this->productResource->getConnection()->fetchAll($select);
76+
}
77+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
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+
namespace Magento\CatalogRuleConfigurable\Model\Product\Type\Configurable;
9+
10+
use Magento\Catalog\Api\Data\ProductInterface;
11+
use Magento\Catalog\Api\ProductRepositoryInterface;
12+
use Magento\Catalog\Model\Product\Type\AbstractType;
13+
use Magento\ConfigurableProduct\Model\Product\Type\Configurable\Price;
14+
use Magento\Customer\Model\Group;
15+
use Magento\Framework\ObjectManagerInterface;
16+
use Magento\Store\Api\WebsiteRepositoryInterface;
17+
use Magento\TestFramework\Catalog\Model\Product\Price\GetPriceIndexDataByProductId;
18+
use Magento\TestFramework\Helper\Bootstrap;
19+
use PHPUnit\Framework\TestCase;
20+
21+
/**
22+
* Provides tests for configurable product pricing with catalog rules.
23+
*
24+
* @magentoDbIsolation disabled
25+
* @magentoAppArea frontend
26+
*/
27+
class PriceTest extends TestCase
28+
{
29+
/**
30+
* @var ObjectManagerInterface
31+
*/
32+
private $objectManager;
33+
34+
/**
35+
* @var WebsiteRepositoryInterface
36+
*/
37+
private $websiteRepository;
38+
39+
/**
40+
* @var ProductRepositoryInterface
41+
*/
42+
private $productRepository;
43+
44+
/**
45+
* @var Price
46+
*/
47+
private $priceModel;
48+
49+
/**
50+
* @var GetPriceIndexDataByProductId
51+
*/
52+
private $getPriceIndexDataByProductId;
53+
54+
/**
55+
* @inheritdoc
56+
*/
57+
protected function setUp()
58+
{
59+
$this->objectManager = Bootstrap::getObjectManager();
60+
$this->priceModel = $this->objectManager->create(Price::class);
61+
$this->websiteRepository = $this->objectManager->get(WebsiteRepositoryInterface::class);
62+
$this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class);
63+
$this->productRepository->cleanCache();
64+
$this->getPriceIndexDataByProductId = $this->objectManager->get(GetPriceIndexDataByProductId::class);
65+
}
66+
67+
/**
68+
* @magentoDataFixture Magento/CatalogRuleConfigurable/_files/configurable_product_with_percent_rule.php
69+
* @return void
70+
*/
71+
public function testGetFinalPriceWithCustomOptionAndCatalogRule(): void
72+
{
73+
$indexPrices = [
74+
'simple_10' => [
75+
'price' => 10,
76+
'final_price' => 9,
77+
'min_price' => 9,
78+
'max_price' => 9,
79+
'tier_price' => null
80+
],
81+
'simple_20' => [
82+
'price' => 20,
83+
'final_price' => 15,
84+
'min_price' => 15,
85+
'max_price' => 15,
86+
'tier_price' => 15
87+
],
88+
'configurable' => [
89+
'price' => 0,
90+
'final_price' => 0,
91+
'min_price' => 9,
92+
'max_price' => 30,
93+
'tier_price' => 15
94+
],
95+
];
96+
$this->assertConfigurableProductPrice(20, 25, $indexPrices);
97+
}
98+
99+
/**
100+
* @magentoDataFixture Magento/CatalogRuleConfigurable/_files/configurable_product_with_percent_rules_for_children.php
101+
* @return void
102+
*/
103+
public function testGetFinalPriceWithCustomOptionAndCatalogRulesForChildren(): void
104+
{
105+
$indexPrices = [
106+
'simple_10' => [
107+
'price' => 10,
108+
'final_price' => 4.5,
109+
'min_price' => 4.5,
110+
'max_price' => 9,
111+
'tier_price' => null
112+
],
113+
'simple_20' => [
114+
'price' => 20,
115+
'final_price' => 8,
116+
'min_price' => 8,
117+
'max_price' => 15,
118+
'tier_price' => 15
119+
],
120+
'configurable' => [
121+
'price' => 0,
122+
'final_price' => 0,
123+
'min_price' => 4.5,
124+
'max_price' => 23,
125+
'tier_price' => 15
126+
],
127+
];
128+
$this->assertConfigurableProductPrice(19.5, 23, $indexPrices);
129+
}
130+
131+
/**
132+
* Asserts configurable product prices.
133+
*
134+
* @param float $priceWithFirstSimple
135+
* @param float $priceWithSecondSimple
136+
* @param array $indexPrices
137+
* @return void
138+
*/
139+
private function assertConfigurableProductPrice(
140+
float $priceWithFirstSimple,
141+
float $priceWithSecondSimple,
142+
array $indexPrices
143+
): void {
144+
foreach ($indexPrices as $sku => $prices) {
145+
$this->assertIndexTableData($sku, $prices);
146+
}
147+
$configurable = $this->productRepository->get('configurable');
148+
//Add tier price option
149+
$optionId = $configurable->getOptions()[0]->getId();
150+
$configurable->addCustomOption(AbstractType::OPTION_PREFIX . $optionId, 'text');
151+
$configurable->addCustomOption('option_ids', $optionId);
152+
//First simple rule price + Option price
153+
$this->assertFinalPrice($configurable, $priceWithFirstSimple);
154+
$configurable->addCustomOption('simple_product', 20, $this->productRepository->get('simple_20'));
155+
//Second simple rule price + Option price
156+
$this->assertFinalPrice($configurable, $priceWithSecondSimple);
157+
}
158+
159+
/**
160+
* Asserts product final price.
161+
*
162+
* @param ProductInterface $product
163+
* @param float $expectedPrice
164+
* @return void
165+
*/
166+
private function assertFinalPrice(ProductInterface $product, float $expectedPrice): void
167+
{
168+
$this->assertEquals(
169+
round($expectedPrice, 2),
170+
round($this->priceModel->getFinalPrice(1, $product), 2)
171+
);
172+
}
173+
174+
/**
175+
* Asserts price data in index table.
176+
*
177+
* @param string $sku
178+
* @param array $expectedPrices
179+
* @return void
180+
*/
181+
private function assertIndexTableData(string $sku, array $expectedPrices): void
182+
{
183+
$data = $this->getPriceIndexDataByProductId->execute(
184+
(int)$this->productRepository->get($sku)->getId(),
185+
Group::NOT_LOGGED_IN_ID,
186+
(int)$this->websiteRepository->get('base')->getId()
187+
);
188+
$data = reset($data);
189+
foreach ($expectedPrices as $column => $price) {
190+
$this->assertEquals($price, $data[$column]);
191+
}
192+
}
193+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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\Rule;
10+
use Magento\CatalogRule\Model\Rule\Condition\Combine;
11+
use Magento\CatalogRule\Model\Rule\Condition\Product;
12+
use Magento\CatalogRule\Model\RuleFactory;
13+
use Magento\Customer\Model\Group;
14+
use Magento\Framework\App\Area;
15+
use Magento\Store\Api\WebsiteRepositoryInterface;
16+
use Magento\Store\Model\StoreManagerInterface;
17+
use Magento\TestFramework\Helper\Bootstrap;
18+
19+
require __DIR__ . '/../../ConfigurableProduct/_files/configurable_product_with_custom_option_and_simple_tier_price.php';
20+
Bootstrap::getInstance()->loadArea(Area::AREA_ADMINHTML);
21+
22+
/** @var StoreManagerInterface $storeManager */
23+
$storeManager = $objectManager->get(StoreManagerInterface::class);
24+
/** @var WebsiteRepositoryInterface $websiteRepository */
25+
$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class);
26+
/** @var CatalogRuleRepositoryInterface $ruleRepository */
27+
$ruleRepository = $objectManager->get(CatalogRuleRepositoryInterface::class);
28+
/** @var Rule $rule */
29+
$rule = $objectManager->get(RuleFactory::class)->create();
30+
$rule->loadPost(
31+
[
32+
'name' => 'Percent rule for configurable product',
33+
'is_active' => '1',
34+
'stop_rules_processing' => 0,
35+
'website_ids' => [$websiteRepository->get('base')->getId()],
36+
'customer_group_ids' => Group::NOT_LOGGED_IN_ID,
37+
'discount_amount' => 50,
38+
'simple_action' => 'by_percent',
39+
'from_date' => '',
40+
'to_date' => '',
41+
'sort_order' => 0,
42+
'sub_is_enable' => 0,
43+
'sub_discount_amount' => 0,
44+
'conditions' => [
45+
'1' => ['type' => Combine::class, 'aggregator' => 'all', 'value' => '1', 'new_child' => ''],
46+
'1--1' => ['type' => Product::class, 'attribute' => 'sku', 'operator' => '==', 'value' => 'configurable'],
47+
],
48+
]
49+
);
50+
$ruleRepository->save($rule);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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\CollectionFactory;
11+
use Magento\CatalogRule\Model\Rule;
12+
use Magento\TestFramework\Helper\Bootstrap;
13+
14+
require __DIR__
15+
. '/../../ConfigurableProduct/_files/'
16+
. 'configurable_product_with_custom_option_and_simple_tier_price_rollback.php';
17+
18+
$objectManager = Bootstrap::getObjectManager();
19+
/** @var CatalogRuleRepositoryInterface $ruleRepository */
20+
$ruleRepository = $objectManager->create(CatalogRuleRepositoryInterface::class);
21+
/** @var IndexBuilder $indexBuilder */
22+
$indexBuilder = $objectManager->get(IndexBuilder::class);
23+
/** @var CollectionFactory $ruleCollectionFactory */
24+
$ruleCollectionFactory = $objectManager->get(CollectionFactory::class);
25+
$ruleCollection = $ruleCollectionFactory->create()
26+
->addFieldToFilter('name', ['eq' => 'Percent rule for configurable product'])
27+
->setPageSize(1);
28+
/** @var Rule $rule */
29+
$rule = $ruleCollection->getFirstItem();
30+
if ($rule->getId()) {
31+
$ruleRepository->delete($rule);
32+
}
33+
$indexBuilder->reindexFull();

0 commit comments

Comments
 (0)