Skip to content

Commit 724d2ab

Browse files
Merge branch '2.4-develop' into GL_PR_Arrows_August_07_2023
2 parents b5dd74c + 14c1547 commit 724d2ab

File tree

66 files changed

+2597
-447
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+2597
-447
lines changed

app/code/Magento/Backend/Block/Widget/Grid/Extended.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,6 +1022,7 @@ public function getCsvFile()
10221022
$stream = $this->_directory->openFile($file, 'w+');
10231023

10241024
$stream->lock();
1025+
$stream->write(pack('CCC', 0xef, 0xbb, 0xbf));
10251026
$stream->writeCsv($this->_getExportHeaders());
10261027
$this->_exportIterateCollection('_exportCsvItem', [$stream]);
10271028

app/code/Magento/Bundle/Model/ResourceModel/Indexer/Price.php

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,18 @@
66
namespace Magento\Bundle\Model\ResourceModel\Indexer;
77

88
use Magento\Catalog\Api\Data\ProductInterface;
9-
use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\BasePriceModifier;
10-
use Magento\Framework\DB\Select;
11-
use Magento\Framework\Indexer\DimensionalIndexerInterface;
12-
use Magento\Framework\EntityManager\MetadataPool;
139
use Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer;
14-
use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\IndexTableStructureFactory;
10+
use Magento\Catalog\Model\Product\Attribute\Source\Status;
11+
use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\BasePriceModifier;
1512
use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\IndexTableStructure;
13+
use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\IndexTableStructureFactory;
1614
use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\Query\JoinAttributeProcessor;
15+
use Magento\CatalogInventory\Model\Stock;
1716
use Magento\Customer\Model\Indexer\CustomerGroupDimensionProvider;
17+
use Magento\Framework\DB\Select;
18+
use Magento\Framework\EntityManager\MetadataPool;
19+
use Magento\Framework\Indexer\DimensionalIndexerInterface;
1820
use Magento\Store\Model\Indexer\WebsiteDimensionProvider;
19-
use Magento\Catalog\Model\Product\Attribute\Source\Status;
20-
use Magento\CatalogInventory\Model\Stock;
2121

2222
/**
2323
* Bundle products Price indexer resource model
@@ -671,10 +671,9 @@ private function calculateFixedBundleSelectionPrice()
671671
* @return void
672672
* @throws \Exception
673673
*/
674-
private function calculateDynamicBundleSelectionPrice($dimensions)
674+
private function calculateDynamicBundleSelectionPrice(array $dimensions): void
675675
{
676676
$connection = $this->getConnection();
677-
678677
$price = 'idx.min_price * bs.selection_qty';
679678
$specialExpr = $connection->getCheckSql(
680679
'i.special_price > 0 AND i.special_price < 100',
@@ -716,8 +715,32 @@ private function calculateDynamicBundleSelectionPrice($dimensions)
716715
[]
717716
);
718717
$select->where('si.stock_status = ?', Stock::STOCK_IN_STOCK);
718+
$query = str_replace('AS `idx`', 'AS `idx` USE INDEX (PRIMARY)', (string) $select);
719+
$insertColumns = [
720+
'entity_id',
721+
'customer_group_id',
722+
'website_id',
723+
'option_id',
724+
'selection_id',
725+
'group_type',
726+
'is_required',
727+
'price',
728+
'tier_price'
729+
];
730+
$insertColumns = array_map(function ($item) use ($connection) {
731+
return $connection->quoteIdentifier($item);
732+
}, $insertColumns);
733+
$updateValues = [];
734+
foreach ($insertColumns as $column) {
735+
$updateValues[] = sprintf("%s = VALUES(%s)", $column, $column);
736+
}
719737

720-
$this->tableMaintainer->insertFromSelect($select, $this->getBundleSelectionTable(), []);
738+
$connection->query(sprintf(
739+
"INSERT INTO `" . $this->getBundleSelectionTable() . "` (%s) %s ON DUPLICATE KEY UPDATE %s",
740+
implode(",", $insertColumns),
741+
$query,
742+
implode(",", $updateValues)
743+
));
721744
}
722745

723746
/**

app/code/Magento/Bundle/Test/Unit/Model/ResourceModel/Indexer/PriceTest.php

Lines changed: 132 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\Query\JoinAttributeProcessor;
1515
use Magento\Framework\App\ResourceConnection;
1616
use Magento\Framework\DB\Adapter\AdapterInterface;
17+
use Magento\Framework\EntityManager\EntityMetadataInterface;
1718
use Magento\Framework\EntityManager\MetadataPool;
1819
use Magento\Framework\Event\ManagerInterface;
1920
use Magento\Framework\Module\Manager;
@@ -22,6 +23,8 @@
2223

2324
/**
2425
* Class to test Bundle products Price indexer resource model
26+
*
27+
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
2528
*/
2629
class PriceTest extends TestCase
2730
{
@@ -45,6 +48,11 @@ class PriceTest extends TestCase
4548
*/
4649
private $priceModel;
4750

51+
/**
52+
* @var MetadataPool
53+
*/
54+
private $metadataPool;
55+
4856
/**
4957
* @inheritdoc
5058
*/
@@ -64,7 +72,7 @@ protected function setUp(): void
6472
/** @var TableMaintainer|MockObject $tableMaintainer */
6573
$tableMaintainer = $this->createMock(TableMaintainer::class);
6674
/** @var MetadataPool|MockObject $metadataPool */
67-
$metadataPool = $this->createMock(MetadataPool::class);
75+
$this->metadataPool = $this->createMock(MetadataPool::class);
6876
/** @var BasePriceModifier|MockObject $basePriceModifier */
6977
$basePriceModifier = $this->createMock(BasePriceModifier::class);
7078
/** @var JoinAttributeProcessor|MockObject $joinAttributeProcessor */
@@ -78,7 +86,7 @@ protected function setUp(): void
7886
$this->priceModel = new Price(
7987
$indexTableStructureFactory,
8088
$tableMaintainer,
81-
$metadataPool,
89+
$this->metadataPool,
8290
$this->resourceMock,
8391
$basePriceModifier,
8492
$joinAttributeProcessor,
@@ -89,6 +97,124 @@ protected function setUp(): void
8997
);
9098
}
9199

100+
/**
101+
* @throws \ReflectionException
102+
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
103+
*/
104+
public function testCalculateDynamicBundleSelectionPrice(): void
105+
{
106+
$entity = 'entity_id';
107+
$price = 'idx.min_price * bs.selection_qty';
108+
//@codingStandardsIgnoreStart
109+
$selectQuery = "SELECT `i`.`entity_id`,
110+
`i`.`customer_group_id`,
111+
`i`.`website_id`,
112+
`bo`.`option_id`,
113+
`bs`.`selection_id`,
114+
IF(bo.type = 'select' OR bo.type = 'radio', 0, 1) AS `group_type`,
115+
`bo`.`required` AS `is_required`,
116+
LEAST(IF(i.special_price > 0 AND i.special_price < 100,
117+
ROUND(idx.min_price * bs.selection_qty * (i.special_price / 100), 4), idx.min_price * bs.selection_qty),
118+
IFNULL((IF(i.tier_percent IS NOT NULL,
119+
ROUND((1 - i.tier_percent / 100) * idx.min_price * bs.selection_qty, 4), NULL)), idx.min_price *
120+
bs.selection_qty)) AS `price`,
121+
IF(i.tier_percent IS NOT NULL, ROUND((1 - i.tier_percent / 100) * idx.min_price * bs.selection_qty, 4),
122+
NULL) AS `tier_price`
123+
FROM `catalog_product_index_price_bundle_temp` AS `i`
124+
INNER JOIN `catalog_product_entity` AS `parent_product` ON parent_product.entity_id = i.entity_id AND
125+
(parent_product.created_in <= 1 AND parent_product.updated_in > 1)
126+
INNER JOIN `catalog_product_bundle_option` AS `bo` ON bo.parent_id = parent_product.row_id
127+
INNER JOIN `catalog_product_bundle_selection` AS `bs` ON bs.option_id = bo.option_id
128+
INNER JOIN `catalog_product_index_price_replica` AS `idx`
129+
ON bs.product_id = idx.entity_id AND i.customer_group_id = idx.customer_group_id AND
130+
i.website_id = idx.website_id
131+
INNER JOIN `cataloginventory_stock_status` AS `si` ON si.product_id = bs.product_id
132+
WHERE (i.price_type = 0)
133+
AND (si.stock_status = 1)
134+
ON DUPLICATE KEY UPDATE `entity_id` = VALUES(`entity_id`),
135+
`customer_group_id` = VALUES(`customer_group_id`),
136+
`website_id` = VALUES(`website_id`),
137+
`option_id` = VALUES(`option_id`),
138+
`selection_id` = VALUES(`selection_id`),
139+
`group_type` = VALUES(`group_type`),
140+
`is_required` = VALUES(`is_required`),
141+
`price` = VALUES(`price`),
142+
`tier_price` = VALUES(`tier_price`)";
143+
$processedQuery = "INSERT INTO `catalog_product_index_price_bundle_sel_temp` (,,,,,,,,) SELECT `i`.`entity_id`,
144+
`i`.`customer_group_id`,
145+
`i`.`website_id`,
146+
`bo`.`option_id`,
147+
`bs`.`selection_id`,
148+
IF(bo.type = 'select' OR bo.type = 'radio', 0, 1) AS `group_type`,
149+
`bo`.`required` AS `is_required`,
150+
LEAST(IF(i.special_price > 0 AND i.special_price < 100,
151+
ROUND(idx.min_price * bs.selection_qty * (i.special_price / 100), 4), idx.min_price * bs.selection_qty),
152+
IFNULL((IF(i.tier_percent IS NOT NULL,
153+
ROUND((1 - i.tier_percent / 100) * idx.min_price * bs.selection_qty, 4), NULL)), idx.min_price *
154+
bs.selection_qty)) AS `price`,
155+
IF(i.tier_percent IS NOT NULL, ROUND((1 - i.tier_percent / 100) * idx.min_price * bs.selection_qty, 4),
156+
NULL) AS `tier_price`
157+
FROM `catalog_product_index_price_bundle_temp` AS `i`
158+
INNER JOIN `catalog_product_entity` AS `parent_product` ON parent_product.entity_id = i.entity_id AND
159+
(parent_product.created_in <= 1 AND parent_product.updated_in > 1)
160+
INNER JOIN `catalog_product_bundle_option` AS `bo` ON bo.parent_id = parent_product.row_id
161+
INNER JOIN `catalog_product_bundle_selection` AS `bs` ON bs.option_id = bo.option_id
162+
INNER JOIN `catalog_product_index_price_replica` AS `idx` USE INDEX (PRIMARY)
163+
ON bs.product_id = idx.entity_id AND i.customer_group_id = idx.customer_group_id AND
164+
i.website_id = idx.website_id
165+
INNER JOIN `cataloginventory_stock_status` AS `si` ON si.product_id = bs.product_id
166+
WHERE (i.price_type = 0)
167+
AND (si.stock_status = 1)
168+
ON DUPLICATE KEY UPDATE `entity_id` = VALUES(`entity_id`),
169+
`customer_group_id` = VALUES(`customer_group_id`),
170+
`website_id` = VALUES(`website_id`),
171+
`option_id` = VALUES(`option_id`),
172+
`selection_id` = VALUES(`selection_id`),
173+
`group_type` = VALUES(`group_type`),
174+
`is_required` = VALUES(`is_required`),
175+
`price` = VALUES(`price`),
176+
`tier_price` = VALUES(`tier_price`) ON DUPLICATE KEY UPDATE = VALUES(), = VALUES(), = VALUES(), = VALUES(), = VALUES(), = VALUES(), = VALUES(), = VALUES(), = VALUES()";
177+
//@codingStandardsIgnoreEnd
178+
$this->connectionMock->expects($this->exactly(3))
179+
->method('getCheckSql')
180+
->withConsecutive(
181+
[
182+
'i.special_price > 0 AND i.special_price < 100',
183+
'ROUND(' . $price . ' * (i.special_price / 100), 4)',
184+
$price
185+
],
186+
[
187+
'i.tier_percent IS NOT NULL',
188+
'ROUND((1 - i.tier_percent / 100) * ' . $price . ', 4)',
189+
'NULL'
190+
],
191+
["bo.type = 'select' OR bo.type = 'radio'", '0', '1']
192+
);
193+
194+
$select = $this->createMock(\Magento\Framework\DB\Select::class);
195+
$select->expects($this->once())->method('from')->willReturn($select);
196+
$select->expects($this->exactly(5))->method('join')->willReturn($select);
197+
$select->expects($this->exactly(2))->method('where')->willReturn($select);
198+
$select->expects($this->once())->method('columns')->willReturn($select);
199+
$select->expects($this->any())->method('__toString')->willReturn($selectQuery);
200+
201+
$this->connectionMock->expects($this->once())->method('getIfNullSql');
202+
$this->connectionMock->expects($this->once())->method('getLeastSql');
203+
$this->connectionMock->expects($this->any())
204+
->method('select')
205+
->willReturn($select);
206+
$this->connectionMock->expects($this->exactly(9))->method('quoteIdentifier');
207+
$this->connectionMock->expects($this->once())->method('query')->with($processedQuery);
208+
209+
$pool = $this->createMock(EntityMetadataInterface::class);
210+
$pool->expects($this->once())->method('getLinkField')->willReturn($entity);
211+
$this->metadataPool->expects($this->once())
212+
->method('getMetadata')
213+
->willReturn($pool);
214+
215+
$this->invokeMethodViaReflection('calculateDynamicBundleSelectionPrice', []);
216+
}
217+
92218
/**
93219
* Tests create Bundle Price temporary table
94220
*/
@@ -147,16 +273,18 @@ public function testGetBundleOptionTable(): void
147273
* Invoke private method via reflection
148274
*
149275
* @param string $methodName
276+
* @param array $args
150277
* @return string
278+
* @throws \ReflectionException
151279
*/
152-
private function invokeMethodViaReflection(string $methodName): string
280+
private function invokeMethodViaReflection(string $methodName, array $args = []): string
153281
{
154282
$method = new \ReflectionMethod(
155283
Price::class,
156284
$methodName
157285
);
158286
$method->setAccessible(true);
159287

160-
return (string)$method->invoke($this->priceModel);
288+
return (string)$method->invoke($this->priceModel, $args);
161289
}
162290
}

app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -122,24 +122,6 @@ class Helper
122122
*/
123123
private $categoryLinkFactory;
124124

125-
/**
126-
* @var array
127-
*/
128-
private $productDataKeys = [
129-
'weight',
130-
'special_price',
131-
'cost',
132-
'country_of_manufacture',
133-
'description',
134-
'short_description',
135-
'meta_description',
136-
'meta_keyword',
137-
'meta_title',
138-
'page_layout',
139-
'custom_design',
140-
'gift_wrapping_price'
141-
];
142-
143125
/**
144126
* Constructor
145127
*
@@ -222,12 +204,6 @@ public function initializeFromData(Product $product, array $productData)
222204
$productData['product_has_weight'] = 0;
223205
}
224206

225-
foreach ($productData as $key => $value) {
226-
if (in_array($key, $this->productDataKeys) && $value === '') {
227-
$productData[$key] = null;
228-
}
229-
}
230-
231207
foreach (['category_ids', 'website_ids'] as $field) {
232208
if (!isset($productData[$field])) {
233209
$productData[$field] = [];

app/code/Magento/Catalog/Helper/Product/Compare.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,6 @@ public function getItemCollection()
298298

299299
/* update compare items count */
300300
$count = count($this->_itemCollection);
301-
$counts = $this->_catalogSession->getCatalogCompareItemsCountPerWebsite() ?: [];
302301
$counts[$this->_storeManager->getWebsite()->getId()] = $count;
303302
$this->_catalogSession->setCatalogCompareItemsCountPerWebsite($counts);
304303
$this->_catalogSession->setCatalogCompareItemsCount($count); //deprecated
@@ -331,7 +330,6 @@ public function calculate($logout = false)
331330
->setVisibility($this->_catalogProductVisibility->getVisibleInSiteIds());
332331

333332
$count = $collection->getSize();
334-
$counts = $this->_catalogSession->getCatalogCompareItemsCountPerWebsite() ?: [];
335333
$counts[$this->_storeManager->getWebsite()->getId()] = $count;
336334
$this->_catalogSession->setCatalogCompareItemsCountPerWebsite($counts);
337335
$this->_catalogSession->setCatalogCompareItemsCount($count); //deprecated
@@ -349,6 +347,7 @@ public function getItemCount()
349347
$counts = $this->_catalogSession->getCatalogCompareItemsCountPerWebsite() ?: [];
350348
if (!isset($counts[$this->_storeManager->getWebsite()->getId()])) {
351349
$this->calculate();
350+
$counts = $this->_catalogSession->getCatalogCompareItemsCountPerWebsite() ?: [];
352351
}
353352

354353
return $counts[$this->_storeManager->getWebsite()->getId()] ?? 0;

0 commit comments

Comments
 (0)