Skip to content

Commit 8369afa

Browse files
authored
Merge pull request #2667 from magento-tango/MAGETWO-86125
MAGETWO-86125: Sorting on price of configurable products in catalog not working properly
2 parents 1ff3ca5 + eb9042c commit 8369afa

File tree

24 files changed

+798
-233
lines changed

24 files changed

+798
-233
lines changed

app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ protected function prepareFinalPriceDataForType($entityIds, $type)
307307
$query = $select->insertFromSelect($finalPriceTable->getTableName(), [], false);
308308
$this->getConnection()->query($query);
309309

310-
$this->applyDiscountPrices($finalPriceTable);
310+
$this->modifyPriceIndex($finalPriceTable);
311311

312312
return $this;
313313
}
@@ -512,12 +512,12 @@ protected function _prepareCustomOptionPriceTable()
512512
}
513513

514514
/**
515-
* Apply discount prices to final price index table.
515+
* Modify data in price index table.
516516
*
517517
* @param IndexTableStructure $finalPriceTable
518518
* @return void
519519
*/
520-
private function applyDiscountPrices(IndexTableStructure $finalPriceTable)
520+
private function modifyPriceIndex(IndexTableStructure $finalPriceTable)
521521
{
522522
foreach ($this->priceModifiers as $priceModifier) {
523523
$priceModifier->modifyPrice($finalPriceTable);
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
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\Indexer;
9+
10+
use Magento\CatalogInventory\Api\StockConfigurationInterface;
11+
use Magento\CatalogInventory\Model\ResourceModel\Stock\Item;
12+
use Magento\CatalogInventory\Model\Stock;
13+
use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\PriceModifierInterface;
14+
use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\IndexTableStructure;
15+
16+
/**
17+
* Class for filter product price index.
18+
*/
19+
class ProductPriceIndexFilter implements PriceModifierInterface
20+
{
21+
/**
22+
* @var StockConfigurationInterface
23+
*/
24+
private $stockConfiguration;
25+
26+
/**
27+
* @var Item
28+
*/
29+
private $stockItem;
30+
31+
/**
32+
* @param StockConfigurationInterface $stockConfiguration
33+
* @param Item $stockItem
34+
*/
35+
public function __construct(StockConfigurationInterface $stockConfiguration, Item $stockItem)
36+
{
37+
$this->stockConfiguration = $stockConfiguration;
38+
$this->stockItem = $stockItem;
39+
}
40+
41+
/**
42+
* Remove out of stock products data from price index.
43+
*
44+
* @param IndexTableStructure $priceTable
45+
* @param array $entityIds
46+
* @return void
47+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
48+
*/
49+
public function modifyPrice(IndexTableStructure $priceTable, array $entityIds = [])
50+
{
51+
if ($this->stockConfiguration->isShowOutOfStock()) {
52+
return;
53+
}
54+
55+
$connection = $this->stockItem->getConnection();
56+
$select = $connection->select();
57+
$select->from(
58+
['price_index' => $priceTable->getTableName()],
59+
[]
60+
);
61+
$select->joinInner(
62+
['stock_item' => $this->stockItem->getMainTable()],
63+
'stock_item.product_id = price_index.' . $priceTable->getEntityField()
64+
. ' AND stock_item.stock_id = ' . Stock::DEFAULT_STOCK_ID,
65+
[]
66+
);
67+
if ($this->stockConfiguration->getManageStock()) {
68+
$stockStatus = $connection->getCheckSql(
69+
'use_config_manage_stock = 0 AND manage_stock = 0',
70+
Stock::STOCK_IN_STOCK,
71+
'is_in_stock'
72+
);
73+
} else {
74+
$stockStatus = $connection->getCheckSql(
75+
'use_config_manage_stock = 0 AND manage_stock = 1',
76+
'is_in_stock',
77+
Stock::STOCK_IN_STOCK
78+
);
79+
}
80+
$select->where($stockStatus . ' = ?', Stock::STOCK_OUT_OF_STOCK);
81+
82+
$query = $select->deleteFromSelect('price_index');
83+
$connection->query($query);
84+
}
85+
}
Lines changed: 77 additions & 0 deletions
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+
namespace Magento\CatalogInventory\Model\Plugin;
7+
8+
use Magento\CatalogInventory\Model\ResourceModel\Stock\Item;
9+
use Magento\Catalog\Model\Indexer\Product\Price\Processor;
10+
use Magento\Framework\Model\AbstractModel;
11+
12+
/**
13+
* Update product price index after product stock status changed.
14+
*/
15+
class PriceIndexUpdater
16+
{
17+
/**
18+
* @var Processor
19+
*/
20+
private $priceIndexProcessor;
21+
22+
/**
23+
* @param Processor $priceIndexProcessor
24+
*/
25+
public function __construct(Processor $priceIndexProcessor)
26+
{
27+
$this->priceIndexProcessor = $priceIndexProcessor;
28+
}
29+
30+
/**
31+
* @param Item $subject
32+
* @param Item $result
33+
* @param AbstractModel $model
34+
* @return Item
35+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
36+
*/
37+
public function afterSave(Item $subject, Item $result, AbstractModel $model)
38+
{
39+
$fields = [
40+
'is_in_stock',
41+
'use_config_manage_stock',
42+
'manage_stock',
43+
];
44+
foreach ($fields as $field) {
45+
if ($model->dataHasChangedFor($field)) {
46+
$this->priceIndexProcessor->reindexRow($model->getProductId());
47+
break;
48+
}
49+
}
50+
51+
return $result;
52+
}
53+
54+
/**
55+
* @param Item $subject
56+
* @param $result
57+
* @param int $websiteId
58+
* @return void
59+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
60+
*/
61+
public function afterUpdateSetOutOfStock(Item $subject, $result, int $websiteId)
62+
{
63+
$this->priceIndexProcessor->markIndexerAsInvalid();
64+
}
65+
66+
/**
67+
* @param Item $subject
68+
* @param $result
69+
* @param int $websiteId
70+
* @return void
71+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
72+
*/
73+
public function afterUpdateSetInStock(Item $subject, $result, int $websiteId)
74+
{
75+
$this->priceIndexProcessor->markIndexerAsInvalid();
76+
}
77+
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,8 @@ protected function _initConfig()
206206
/**
207207
* Set items out of stock basing on their quantities and config settings
208208
*
209+
* @deprecated
210+
* @see \Magento\CatalogInventory\Model\ResourceModel\Stock\Item::updateSetOutOfStock
209211
* @param string|int $website
210212
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
211213
* @return void
@@ -241,6 +243,8 @@ public function updateSetOutOfStock($website = null)
241243
/**
242244
* Set items in stock basing on their quantities and config settings
243245
*
246+
* @deprecated
247+
* @see \Magento\CatalogInventory\Model\ResourceModel\Stock\Item::updateSetInStock
244248
* @param int|string $website
245249
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
246250
* @return void
@@ -274,6 +278,8 @@ public function updateSetInStock($website)
274278
/**
275279
* Update items low stock date basing on their quantities and config settings
276280
*
281+
* @deprecated
282+
* @see \Magento\CatalogInventory\Model\ResourceModel\Stock\Item::updateLowStockDate
277283
* @param int|string $website
278284
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
279285
* @return void

app/code/Magento/CatalogInventory/Model/ResourceModel/Stock/Item.php

Lines changed: 142 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,14 @@
66
namespace Magento\CatalogInventory\Model\ResourceModel\Stock;
77

88
use Magento\CatalogInventory\Api\Data\StockItemInterface;
9+
use Magento\CatalogInventory\Api\StockConfigurationInterface;
10+
use Magento\CatalogInventory\Model\Stock;
911
use Magento\CatalogInventory\Model\Indexer\Stock\Processor;
10-
use Magento\Framework\App\ResourceConnection as AppResource;
1112
use Magento\Framework\Model\AbstractModel;
12-
use Magento\Framework\Model\ResourceModel\Db\TransactionManagerInterface;
13+
use Magento\Framework\Model\ResourceModel\Db\Context;
14+
use Magento\Framework\DB\Select;
15+
use Magento\Framework\App\ObjectManager;
16+
use Magento\Framework\Stdlib\DateTime\DateTime;
1317

1418
/**
1519
* Stock item resource model
@@ -29,17 +33,36 @@ class Item extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
2933
protected $stockIndexerProcessor;
3034

3135
/**
32-
* @param \Magento\Framework\Model\ResourceModel\Db\Context $context
36+
* @var StockConfigurationInterface
37+
*/
38+
private $stockConfiguration;
39+
40+
/**
41+
* @var DateTime
42+
*/
43+
private $dateTime;
44+
45+
/**
46+
* @param Context $context
3347
* @param Processor $processor
3448
* @param string $connectionName
49+
* @param StockConfigurationInterface $stockConfiguration
50+
* @param DateTime $dateTime
3551
*/
3652
public function __construct(
37-
\Magento\Framework\Model\ResourceModel\Db\Context $context,
53+
Context $context,
3854
Processor $processor,
39-
$connectionName = null
55+
$connectionName = null,
56+
StockConfigurationInterface $stockConfiguration = null,
57+
DateTime $dateTime = null
4058
) {
4159
$this->stockIndexerProcessor = $processor;
4260
parent::__construct($context, $connectionName);
61+
62+
$this->stockConfiguration = $stockConfiguration ??
63+
ObjectManager::getInstance()->get(StockConfigurationInterface::class);
64+
$this->dateTime = $dateTime ??
65+
ObjectManager::getInstance()->get(DateTime::class);
4366
}
4467

4568
/**
@@ -139,4 +162,118 @@ public function setProcessIndexEvents($process = true)
139162
$this->processIndexEvents = $process;
140163
return $this;
141164
}
165+
166+
/**
167+
* Set items out of stock basing on their quantities and config settings
168+
*
169+
* @param int $websiteId
170+
* @return void
171+
*/
172+
public function updateSetOutOfStock(int $websiteId)
173+
{
174+
$connection = $this->getConnection();
175+
176+
$values = [
177+
'is_in_stock' => Stock::STOCK_OUT_OF_STOCK,
178+
'stock_status_changed_auto' => 1,
179+
];
180+
$select = $this->buildProductsSelectByConfigTypes();
181+
$where = [
182+
'website_id = ' . $websiteId,
183+
'is_in_stock = ' . Stock::STOCK_IN_STOCK,
184+
'(use_config_manage_stock = 1 AND 1 = ' . $this->stockConfiguration->getManageStock() . ')'
185+
. ' OR (use_config_manage_stock = 0 AND manage_stock = 1)',
186+
'(use_config_min_qty = 1 AND qty <= ' . $this->stockConfiguration->getMinQty() . ')'
187+
. ' OR (use_config_min_qty = 0 AND qty <= min_qty)',
188+
'product_id IN (' . $select->assemble() . ')',
189+
];
190+
$backordersWhere = '(use_config_backorders = 0 AND backorders = ' . Stock::BACKORDERS_NO . ')';
191+
if (Stock::BACKORDERS_NO == $this->stockConfiguration->getBackorders()) {
192+
$where[] = $backordersWhere . ' OR use_config_backorders = 1';
193+
} else {
194+
$where[] = $backordersWhere;
195+
}
196+
$connection->update($this->getMainTable(), $values, $where);
197+
198+
$this->stockIndexerProcessor->markIndexerAsInvalid();
199+
}
200+
201+
/**
202+
* Set items in stock basing on their quantities and config settings
203+
*
204+
* @param int $websiteId
205+
* @return void
206+
*/
207+
public function updateSetInStock(int $websiteId)
208+
{
209+
$connection = $this->getConnection();
210+
211+
$values = [
212+
'is_in_stock' => Stock::STOCK_IN_STOCK,
213+
];
214+
$select = $this->buildProductsSelectByConfigTypes();
215+
$where = [
216+
'website_id = ' . $websiteId,
217+
'stock_status_changed_auto = 1',
218+
'(use_config_min_qty = 1 AND qty > ' . $this->stockConfiguration->getMinQty() . ')'
219+
. ' OR (use_config_min_qty = 0 AND qty > min_qty)',
220+
'product_id IN (' . $select->assemble() . ')',
221+
];
222+
$manageStockWhere = '(use_config_manage_stock = 0 AND manage_stock = 1)';
223+
if ($this->stockConfiguration->getManageStock()) {
224+
$where[] = $manageStockWhere . ' OR use_config_manage_stock = 1';
225+
} else {
226+
$where[] = $manageStockWhere;
227+
}
228+
$connection->update($this->getMainTable(), $values, $where);
229+
230+
$this->stockIndexerProcessor->markIndexerAsInvalid();
231+
}
232+
233+
/**
234+
* Update items low stock date basing on their quantities and config settings
235+
*
236+
* @param int $websiteId
237+
* @return void
238+
*/
239+
public function updateLowStockDate(int $websiteId)
240+
{
241+
$connection = $this->getConnection();
242+
243+
$condition = $connection->quoteInto(
244+
'(use_config_notify_stock_qty = 1 AND qty < ?)',
245+
$this->stockConfiguration->getNotifyStockQty()
246+
) . ' OR (use_config_notify_stock_qty = 0 AND qty < notify_stock_qty)';
247+
$currentDbTime = $connection->quoteInto('?', $this->dateTime->gmtDate());
248+
$conditionalDate = $connection->getCheckSql($condition, $currentDbTime, 'NULL');
249+
$value = [
250+
'low_stock_date' => new \Zend_Db_Expr($conditionalDate),
251+
];
252+
$select = $this->buildProductsSelectByConfigTypes();
253+
$where = [
254+
'website_id = ' . $websiteId,
255+
'product_id IN (' . $select->assemble() . ')'
256+
];
257+
$manageStockWhere = '(use_config_manage_stock = 0 AND manage_stock = 1)';
258+
if ($this->stockConfiguration->getManageStock()) {
259+
$where[] = $manageStockWhere . ' OR use_config_manage_stock = 1';
260+
} else {
261+
$where[] = $manageStockWhere;
262+
}
263+
$connection->update($this->getMainTable(), $value, $where);
264+
}
265+
266+
/**
267+
* Build select for products with types from config
268+
*
269+
* @return Select
270+
*/
271+
private function buildProductsSelectByConfigTypes(): Select
272+
{
273+
$select = $this->getConnection()->select()
274+
->from($this->getTable('catalog_product_entity'), 'entity_id')
275+
->where('type_id IN (?)', array_keys($this->stockConfiguration->getIsQtyTypeIds(true)));
276+
277+
return $select;
278+
}
142279
}

0 commit comments

Comments
 (0)