Skip to content

Commit 2e35a51

Browse files
pradeep.rauthanpradeep.rauthan
authored andcommitted
Merge branch '2.4-develop' of https://github.com/magento-commerce/magento2ce into MC-42623
2 parents e438e74 + 1807f70 commit 2e35a51

File tree

16 files changed

+225
-58
lines changed

16 files changed

+225
-58
lines changed

app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/DefaultStock.php

Lines changed: 20 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
use Magento\Catalog\Model\ResourceModel\Product\Indexer\AbstractIndexer;
1010
use Magento\CatalogInventory\Model\Stock;
11+
use Magento\Framework\App\ObjectManager;
1112
use Magento\Framework\DB\Adapter\AdapterInterface;
1213
use Magento\CatalogInventory\Api\StockConfigurationInterface;
1314
use Magento\CatalogInventory\Model\Indexer\Stock\Action\Full;
@@ -26,22 +27,16 @@
2627
class DefaultStock extends AbstractIndexer implements StockInterface
2728
{
2829
/**
29-
* Current Product Type Id
30-
*
3130
* @var string
3231
*/
3332
protected $_typeId;
3433

3534
/**
36-
* Product Type is composite flag
37-
*
3835
* @var bool
3936
*/
4037
protected $_isComposite = false;
4138

4239
/**
43-
* Core store config
44-
*
4540
* @var \Magento\Framework\App\Config\ScopeConfigInterface
4641
*/
4742
protected $_scopeConfig;
@@ -58,12 +53,15 @@ class DefaultStock extends AbstractIndexer implements StockInterface
5853
protected $stockConfiguration;
5954

6055
/**
61-
* Param for switching logic which depends on action type (full reindex or partial)
62-
*
6356
* @var string
6457
*/
6558
private $actionType;
6659

60+
/**
61+
* @var GetStatusExpression
62+
*/
63+
private $getStatusExpression;
64+
6765
/**
6866
* Class constructor
6967
*
@@ -72,16 +70,21 @@ class DefaultStock extends AbstractIndexer implements StockInterface
7270
* @param \Magento\Eav\Model\Config $eavConfig
7371
* @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
7472
* @param string $connectionName
73+
* @param \Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock\GetStatusExpression|null $getStatusExpression
7574
*/
7675
public function __construct(
7776
\Magento\Framework\Model\ResourceModel\Db\Context $context,
7877
\Magento\Framework\Indexer\Table\StrategyInterface $tableStrategy,
7978
\Magento\Eav\Model\Config $eavConfig,
8079
\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
81-
$connectionName = null
80+
$connectionName = null,
81+
\Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock\GetStatusExpression $getStatusExpression = null
8282
) {
8383
$this->_scopeConfig = $scopeConfig;
8484
parent::__construct($context, $tableStrategy, $eavConfig, $connectionName);
85+
$this->getStatusExpression = $getStatusExpression ?: ObjectManager::getInstance()->get(
86+
\Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock\GetStatusExpression::class
87+
);
8588
}
8689

8790
/**
@@ -251,6 +254,10 @@ protected function _getStockStatusSelect($entityIds = null, $usePrimaryTable = f
251254
. ' AND mcpei.attribute_id = ' . $this->_getAttribute('status')->getId()
252255
. ' AND mcpei.value = ' . ProductStatus::STATUS_ENABLED,
253256
[]
257+
)->joinLeft(
258+
['css' => 'cataloginventory_stock_status'],
259+
'css.product_id = e.entity_id',
260+
[]
254261
)->columns(
255262
['qty' => $qtyExpr]
256263
)->where(
@@ -292,14 +299,14 @@ protected function _prepareIndexTable($entityIds = null)
292299
*/
293300
protected function _updateIndex($entityIds)
294301
{
295-
$this->deleteOldRecords($entityIds);
296302
$connection = $this->getConnection();
297303
$select = $this->_getStockStatusSelect($entityIds, true);
298304
$select = $this->getQueryProcessorComposite()->processQuery($select, $entityIds, true);
299305
$query = $connection->query($select);
300306

301307
$i = 0;
302308
$data = [];
309+
$savedEntityIds = [];
303310
while ($row = $query->fetch(\PDO::FETCH_ASSOC)) {
304311
$i++;
305312
$data[] = [
@@ -309,6 +316,7 @@ protected function _updateIndex($entityIds)
309316
'qty' => (double)$row['qty'],
310317
'stock_status' => (int)$row['status'],
311318
];
319+
$savedEntityIds[] = (int)$row['entity_id'];
312320
if ($i % 1000 == 0) {
313321
$this->_updateIndexTable($data);
314322
$data = [];
@@ -317,6 +325,7 @@ protected function _updateIndex($entityIds)
317325

318326
$this->_updateIndexTable($data);
319327

328+
$this->deleteOldRecords(array_diff($entityIds, $savedEntityIds));
320329
return $this;
321330
}
322331

@@ -376,21 +385,7 @@ public function getIdxTable($table = null)
376385
*/
377386
protected function getStatusExpression(AdapterInterface $connection, $isAggregate = false)
378387
{
379-
$isInStockExpression = $isAggregate ? 'MAX(cisi.is_in_stock)' : 'cisi.is_in_stock';
380-
if ($this->_isManageStock()) {
381-
$statusExpr = $connection->getCheckSql(
382-
'cisi.use_config_manage_stock = 0 AND cisi.manage_stock = 0',
383-
1,
384-
$isInStockExpression
385-
);
386-
} else {
387-
$statusExpr = $connection->getCheckSql(
388-
'cisi.use_config_manage_stock = 0 AND cisi.manage_stock = 1',
389-
$isInStockExpression,
390-
1
391-
);
392-
}
393-
return $statusExpr;
388+
return $this->getStatusExpression->execute($this->getTypeId(), $connection, $isAggregate);
394389
}
395390

396391
/**
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
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\CatalogInventory\Model\ResourceModel\Indexer\Stock;
9+
10+
use InvalidArgumentException;
11+
use Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock\StatusExpression\ExpressionInterface;
12+
use Magento\Framework\DB\Adapter\AdapterInterface;
13+
use Zend_Db_Expr;
14+
15+
class GetStatusExpression
16+
{
17+
/**
18+
* @var array
19+
*/
20+
private $statusExpressions;
21+
22+
/**
23+
* @param array $statusExpressions
24+
*/
25+
public function __construct(array $statusExpressions = [])
26+
{
27+
foreach ($statusExpressions as $expression) {
28+
if (!($expression instanceof ExpressionInterface)) {
29+
throw new InvalidArgumentException(
30+
'Expressions must implement '
31+
.'\Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock\StatusExpression\ExpressionInterface'
32+
.' interface'
33+
);
34+
}
35+
}
36+
$this->statusExpressions = $statusExpressions;
37+
}
38+
39+
/**
40+
* Returns stock status expression for MySQL query.
41+
*
42+
* @param string $productType
43+
* @param AdapterInterface $connection
44+
* @param bool $isAggregate
45+
* @return Zend_Db_Expr|null
46+
*/
47+
public function execute(string $productType, AdapterInterface $connection, bool $isAggregate): ?Zend_Db_Expr
48+
{
49+
$expression = $this->statusExpressions[$productType] ?? $this->statusExpressions['default'];
50+
return $expression->getExpression($connection, $isAggregate);
51+
}
52+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
namespace Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock\StatusExpression;
8+
9+
use Magento\CatalogInventory\Model\Configuration;
10+
use Magento\Framework\App\Config\ScopeConfigInterface;
11+
use Magento\Framework\DB\Adapter\AdapterInterface;
12+
use Magento\Store\Model\ScopeInterface;
13+
use Zend_Db_Expr;
14+
15+
class DefaultExpression implements ExpressionInterface
16+
{
17+
/**
18+
* @var ScopeConfigInterface
19+
*/
20+
private $scopeConfig;
21+
22+
/**
23+
* @param ScopeConfigInterface $scopeConfig
24+
*/
25+
public function __construct(ScopeConfigInterface $scopeConfig)
26+
{
27+
$this->scopeConfig = $scopeConfig;
28+
}
29+
30+
/**
31+
* Returns status expressions for MySQL query
32+
*
33+
* @ingeritdoc
34+
* @param AdapterInterface $connection
35+
* @param bool $isAggregate
36+
* @return Zend_Db_Expr
37+
*/
38+
public function getExpression(AdapterInterface $connection, bool $isAggregate): Zend_Db_Expr
39+
{
40+
$isManageStock = $this->scopeConfig->isSetFlag(
41+
Configuration::XML_PATH_MANAGE_STOCK,
42+
ScopeInterface::SCOPE_STORE
43+
);
44+
45+
$isInStockExpression = $isAggregate ? 'MAX(cisi.is_in_stock)' : 'cisi.is_in_stock';
46+
if ($isManageStock) {
47+
$statusExpr = $connection->getCheckSql(
48+
'cisi.use_config_manage_stock = 0 AND cisi.manage_stock = 0',
49+
1,
50+
$isInStockExpression
51+
);
52+
} else {
53+
$statusExpr = $connection->getCheckSql(
54+
'cisi.use_config_manage_stock = 0 AND cisi.manage_stock = 1',
55+
$isInStockExpression,
56+
1
57+
);
58+
}
59+
return $statusExpr;
60+
}
61+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
namespace Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock\StatusExpression;
8+
9+
use Magento\Framework\DB\Adapter\AdapterInterface;
10+
use Zend_Db_Expr;
11+
12+
/**
13+
* Interface for composite status expressions for MySQL query.
14+
*/
15+
interface ExpressionInterface
16+
{
17+
/**
18+
* Returns status expressions for MySQL query
19+
*
20+
* @param AdapterInterface $connection
21+
* @param bool $isAggregate
22+
* @return Zend_Db_Expr
23+
*/
24+
public function getExpression(AdapterInterface $connection, bool $isAggregate): Zend_Db_Expr;
25+
}

app/code/Magento/CatalogInventory/etc/di.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,4 +133,11 @@
133133
<type name="Magento\Catalog\Controller\Adminhtml\Product\Action\Attribute\Save">
134134
<plugin name="massAction" type="Magento\CatalogInventory\Plugin\MassUpdateProductAttribute" />
135135
</type>
136+
<type name="Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock\GetStatusExpression">
137+
<arguments>
138+
<argument name="statusExpressions" xsi:type="array">
139+
<item name="default" xsi:type="object">Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock\StatusExpression\DefaultExpression</item>
140+
</argument>
141+
</arguments>
142+
</type>
136143
</config>

dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryProductsVariantsTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
class CategoryProductsVariantsTest extends GraphQlAbstract
1919
{
2020
/**
21+
* @magentoApiDataFixture Magento/Catalog/_files/reindex_catalog_inventory_stock.php
2122
* @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
2223
* @throws \Magento\Framework\Exception\NoSuchEntityException
2324
*/

dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1616,11 +1616,16 @@ public function testFilterProductsBySingleCategoryId(string $fieldName, string $
16161616
/**
16171617
* Sorting the search results by relevance (DESC => most relevant)
16181618
*
1619+
* Sorting by relevance may return different results depending on the ES.
1620+
* To check that sorting works, we compare results with ASC and DESC relevance sorting
1621+
*
16191622
* Search for products for a fuzzy match and checks if all matching results returned including
16201623
* results based on matching keywords from description
16211624
*
16221625
* @magentoApiDataFixture Magento/Catalog/_files/products_for_relevance_sorting.php
16231626
* @return void
1627+
*
1628+
* @throws \Exception
16241629
*/
16251630
public function testSearchAndSortByRelevance()
16261631
{
@@ -1630,7 +1635,7 @@ public function testSearchAndSortByRelevance()
16301635
{
16311636
products(
16321637
search:"{$search_term}"
1633-
sort:{relevance:DESC}
1638+
sort:{relevance:%s}
16341639
pageSize: 5
16351640
currentPage: 1
16361641
)
@@ -1671,16 +1676,16 @@ public function testSearchAndSortByRelevance()
16711676
16721677
}
16731678
QUERY;
1674-
$response = $this->graphQlQuery($query);
1675-
$this->assertEquals(3, $response['products']['total_count']);
1676-
$this->assertNotEmpty($response['products']['filters'], 'Filters should have the Category layer');
1677-
$this->assertEquals('Colorful Category', $response['products']['filters'][0]['filter_items'][0]['label']);
1678-
$this->assertCount(2, $response['products']['aggregations']);
1679-
$productsInResponse = ['Blue briefs', 'Navy Blue Striped Shoes', 'Grey shorts'];
1680-
$count = count($response['products']['items']);
1681-
for ($i = 0; $i < $count; $i++) {
1682-
$this->assertEquals($productsInResponse[$i], $response['products']['items'][$i]['name']);
1683-
}
1679+
$responseDesc = $this->graphQlQuery(sprintf($query, 'DESC'));
1680+
$responseAsc = $this->graphQlQuery(sprintf($query, 'ASC'));
1681+
$this->assertEquals(3, $responseDesc['products']['total_count']);
1682+
$this->assertNotEmpty($responseDesc['products']['filters'], 'Filters should have the Category layer');
1683+
$this->assertEquals('Colorful Category', $responseDesc['products']['filters'][0]['filter_items'][0]['label']);
1684+
$this->assertCount(2, $responseDesc['products']['aggregations']);
1685+
$expectedProductsInResponse = ['Blue briefs', 'Navy Blue Striped Shoes', 'Grey shorts'];
1686+
$namesDesc = array_column($responseDesc['products']['items'], 'name');
1687+
$this->assertEqualsCanonicalizing($expectedProductsInResponse, $namesDesc);
1688+
$this->assertEquals($namesDesc, array_reverse(array_column($responseAsc['products']['items'], 'name')));
16841689
}
16851690

16861691
/**

dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/ReorderTest.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ protected function setUp(): void
5656
} catch (NoSuchEntityException $e) {
5757
}
5858
}
59-
6059
/**
6160
* @magentoApiDataFixture Magento/Sales/_files/customer_order_item_with_product_and_custom_options.php
6261
*/
@@ -188,15 +187,17 @@ public function testReorderWithLowStock()
188187
}
189188

190189
/**
191-
* Assert that simple product is not available.
190+
* Assert that simple product is not available
192191
*/
193192
private function assertProductNotAvailable()
194193
{
195194
$response = $this->makeReorderForDefaultCustomer();
196195
$expectedResponse = [
197196
'userInputErrors' => [
198197
[
199-
'path' => ['orderNumber'],
198+
'path' => [
199+
'orderNumber'
200+
],
200201
'code' => 'NOT_SALABLE',
201202
],
202203
],

dev/tests/integration/testsuite/Magento/Catalog/_files/products_list_rollback.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
declare(strict_types=1);
77

88
/** @var \Magento\Framework\Registry $registry */
9+
10+
use Magento\Framework\Indexer\IndexerRegistry;
11+
912
$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class);
1013

1114
$registry->unregister('isSecureArea');
@@ -37,6 +40,9 @@
3740
//Product already removed
3841
}
3942

43+
\Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(IndexerRegistry::class)
44+
->get(Magento\CatalogInventory\Model\Indexer\Stock\Processor::INDEXER_ID)
45+
->reindexAll();
4046

4147
$registry->unregister('isSecureArea');
4248
$registry->register('isSecureArea', false);

0 commit comments

Comments
 (0)