Skip to content

Commit 4b76a96

Browse files
Merge branch '2.4-develop' into ACPT-1599-2.4.7-beta2-develop
2 parents 9e3ac4f + bc1c7ba commit 4b76a96

File tree

402 files changed

+9817
-30865
lines changed

Some content is hidden

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

402 files changed

+9817
-30865
lines changed

app/code/Magento/AwsS3/Driver/AwsS3.php

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -893,16 +893,24 @@ public function fileClose($resource): bool
893893
*/
894894
public function fileOpen($path, $mode)
895895
{
896+
$_mode = str_replace(['b', '+'], '', strtolower($mode));
897+
if (!in_array($_mode, ['r', 'w', 'a'], true)) {
898+
throw new FileSystemException(new Phrase('Invalid file open mode "%1".', [$mode]));
899+
}
896900
$path = $this->normalizeRelativePath($path, true);
897901

898902
if (!isset($this->streams[$path])) {
899903
$this->streams[$path] = tmpfile();
900904
try {
901905
if ($this->adapter->fileExists($path)) {
902-
//phpcs:ignore Magento2.Functions.DiscouragedFunction
903-
fwrite($this->streams[$path], $this->adapter->read($path));
904-
//phpcs:ignore Magento2.Functions.DiscouragedFunction
905-
rewind($this->streams[$path]);
906+
if ($_mode !== 'w') {
907+
//phpcs:ignore Magento2.Functions.DiscouragedFunction
908+
fwrite($this->streams[$path], $this->adapter->read($path));
909+
//phpcs:ignore Magento2.Functions.DiscouragedFunction
910+
if ($_mode !== 'a') {
911+
rewind($this->streams[$path]);
912+
}
913+
}
906914
}
907915
} catch (FlysystemFilesystemException $e) {
908916
$this->logger->error($e->getMessage());

app/code/Magento/AwsS3/Test/Unit/Driver/AwsS3Test.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,4 +537,42 @@ public function testFileCloseShouldReturnFalseIfTheArgumentIsNotAResource(): voi
537537
$this->assertEquals(false, $this->driver->fileClose(null));
538538
$this->assertEquals(false, $this->driver->fileClose(false));
539539
}
540+
541+
/**
542+
* @dataProvider fileOpenModesDataProvider
543+
*/
544+
public function testFileOppenedMode($mode, $expected): void
545+
{
546+
$this->adapterMock->method('fileExists')->willReturn(true);
547+
if ($mode !== 'w') {
548+
$this->adapterMock->expects($this->once())->method('read')->willReturn('aaa');
549+
} else {
550+
$this->adapterMock->expects($this->never())->method('read');
551+
}
552+
$resource = $this->driver->fileOpen('test/path', $mode);
553+
$this->assertEquals($expected, ftell($resource));
554+
}
555+
556+
/**
557+
* Data provider for testFileOppenedMode
558+
*
559+
* @return array[]
560+
*/
561+
public function fileOpenModesDataProvider(): array
562+
{
563+
return [
564+
[
565+
"mode" => "a",
566+
"expected" => 3
567+
],
568+
[
569+
"mode" => "r",
570+
"expected" => 0
571+
],
572+
[
573+
"mode" => "w",
574+
"expected" => 0
575+
]
576+
];
577+
}
540578
}

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/Block/Adminhtml/Category/Checkboxes/Tree.php

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

77
/**
88
* Categories tree with checkboxes
9-
*
10-
* @author Magento Core Team <core@magentocommerce.com>
119
*/
1210
namespace Magento\Catalog\Block\Adminhtml\Category\Checkboxes;
1311

app/code/Magento/Catalog/Block/Adminhtml/Category/Helper/Image.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66

77
/**
88
* Category form image field helper
9-
*
10-
* @author Magento Core Team <core@magentocommerce.com>
119
*/
1210
namespace Magento\Catalog\Block\Adminhtml\Category\Helper;
1311

@@ -39,6 +37,8 @@ public function __construct(
3937
}
4038

4139
/**
40+
* Return the URL
41+
*
4242
* @return bool|string
4343
*/
4444
protected function _getUrl()

app/code/Magento/Catalog/Block/Adminhtml/Category/Helper/Pricestep.php

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

77
/**
88
* Adminhtml additional helper block for sort by
9-
*
10-
* @author Magento Core Team <core@magentocommerce.com>
119
*/
1210
namespace Magento\Catalog\Block\Adminhtml\Category\Helper;
1311

app/code/Magento/Catalog/Block/Adminhtml/Category/Helper/Sortby/Available.php

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

77
/**
88
* Adminhtml additional helper block for sort by
9-
*
10-
* @author Magento Core Team <core@magentocommerce.com>
119
*/
1210
namespace Magento\Catalog\Block\Adminhtml\Category\Helper\Sortby;
1311

app/code/Magento/Catalog/Block/Adminhtml/Category/Helper/Sortby/DefaultSortby.php

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

77
/**
88
* Adminhtml additional helper block for sort by
9-
*
10-
* @author Magento Core Team <core@magentocommerce.com>
119
*/
1210
namespace Magento\Catalog\Block\Adminhtml\Category\Helper\Sortby;
1311

0 commit comments

Comments
 (0)