diff --git a/vendor/magento/module-catalog/Model/ResourceModel/GetProductTypeById.php b/vendor/magento/module-catalog/Model/ResourceModel/GetProductTypeById.php
new file mode 100644
index 000000000000..bd62fedb457a
--- /dev/null
+++ b/vendor/magento/module-catalog/Model/ResourceModel/GetProductTypeById.php
@@ -0,0 +1,52 @@
+resource = $resource;
+ }
+
+ /**
+ * Retrieve product type by its product ID
+ *
+ * @param int $productId
+ * @return string
+ */
+ public function execute(int $productId): string
+ {
+ $connection = $this->resource->getConnection();
+ $productTable = $this->resource->getTableName('catalog_product_entity');
+
+ $select = $connection->select()
+ ->from(
+ $productTable,
+ ProductInterface::TYPE_ID
+ )->where('entity_id = ?', $productId);
+
+ $result = $connection->fetchOne($select);
+ return $result ?: '';
+ }
+}
diff --git a/vendor/magento/module-catalog-inventory/Model/ResourceModel/Indexer/Stock/DefaultStock.php b/vendor/magento/module-catalog-inventory/Model/ResourceModel/Indexer/Stock/DefaultStock.php
index bfb84a9ffa9d..da22f0bee50e 100644
--- a/vendor/magento/module-catalog-inventory/Model/ResourceModel/Indexer/Stock/DefaultStock.php
+++ b/vendor/magento/module-catalog-inventory/Model/ResourceModel/Indexer/Stock/DefaultStock.php
@@ -8,6 +8,7 @@
use Magento\Catalog\Model\ResourceModel\Product\Indexer\AbstractIndexer;
use Magento\CatalogInventory\Model\Stock;
+use Magento\Framework\App\ObjectManager;
use Magento\Framework\DB\Adapter\AdapterInterface;
use Magento\CatalogInventory\Api\StockConfigurationInterface;
use Magento\CatalogInventory\Model\Indexer\Stock\Action\Full;
@@ -26,22 +27,16 @@
class DefaultStock extends AbstractIndexer implements StockInterface
{
/**
- * Current Product Type Id
- *
* @var string
*/
protected $_typeId;
/**
- * Product Type is composite flag
- *
* @var bool
*/
protected $_isComposite = false;
/**
- * Core store config
- *
* @var \Magento\Framework\App\Config\ScopeConfigInterface
*/
protected $_scopeConfig;
@@ -58,12 +53,15 @@ class DefaultStock extends AbstractIndexer implements StockInterface
protected $stockConfiguration;
/**
- * Param for switching logic which depends on action type (full reindex or partial)
- *
* @var string
*/
private $actionType;
+ /**
+ * @var GetStatusExpression
+ */
+ private $getStatusExpression;
+
/**
* Class constructor
*
@@ -72,16 +70,21 @@ class DefaultStock extends AbstractIndexer implements StockInterface
* @param \Magento\Eav\Model\Config $eavConfig
* @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
* @param string $connectionName
+ * @param \Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock\GetStatusExpression|null $getStatusExpression
*/
public function __construct(
\Magento\Framework\Model\ResourceModel\Db\Context $context,
\Magento\Framework\Indexer\Table\StrategyInterface $tableStrategy,
\Magento\Eav\Model\Config $eavConfig,
\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
- $connectionName = null
+ $connectionName = null,
+ \Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock\GetStatusExpression $getStatusExpression = null
) {
$this->_scopeConfig = $scopeConfig;
parent::__construct($context, $tableStrategy, $eavConfig, $connectionName);
+ $this->getStatusExpression = $getStatusExpression ?: ObjectManager::getInstance()->get(
+ \Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock\GetStatusExpression::class
+ );
}
/**
@@ -251,6 +254,10 @@ protected function _getStockStatusSelect($entityIds = null, $usePrimaryTable = f
. ' AND mcpei.attribute_id = ' . $this->_getAttribute('status')->getId()
. ' AND mcpei.value = ' . ProductStatus::STATUS_ENABLED,
[]
+ )->joinLeft(
+ ['css' => $this->getTable('cataloginventory_stock_status')],
+ 'css.product_id = e.entity_id',
+ []
)->columns(
['qty' => $qtyExpr]
)->where(
@@ -292,7 +299,6 @@ protected function _prepareIndexTable($entityIds = null)
*/
protected function _updateIndex($entityIds)
{
- $this->deleteOldRecords($entityIds);
$connection = $this->getConnection();
$select = $this->_getStockStatusSelect($entityIds, true);
$select = $this->getQueryProcessorComposite()->processQuery($select, $entityIds, true);
@@ -300,6 +306,7 @@ protected function _updateIndex($entityIds)
$i = 0;
$data = [];
+ $savedEntityIds = [];
while ($row = $query->fetch(\PDO::FETCH_ASSOC)) {
$i++;
$data[] = [
@@ -309,6 +316,7 @@ protected function _updateIndex($entityIds)
'qty' => (double)$row['qty'],
'stock_status' => (int)$row['status'],
];
+ $savedEntityIds[] = (int)$row['entity_id'];
if ($i % 1000 == 0) {
$this->_updateIndexTable($data);
$data = [];
@@ -317,6 +325,7 @@ protected function _updateIndex($entityIds)
$this->_updateIndexTable($data);
+ $this->deleteOldRecords(array_diff($entityIds, $savedEntityIds));
return $this;
}
@@ -376,21 +385,7 @@ public function getIdxTable($table = null)
*/
protected function getStatusExpression(AdapterInterface $connection, $isAggregate = false)
{
- $isInStockExpression = $isAggregate ? 'MAX(cisi.is_in_stock)' : 'cisi.is_in_stock';
- if ($this->_isManageStock()) {
- $statusExpr = $connection->getCheckSql(
- 'cisi.use_config_manage_stock = 0 AND cisi.manage_stock = 0',
- 1,
- $isInStockExpression
- );
- } else {
- $statusExpr = $connection->getCheckSql(
- 'cisi.use_config_manage_stock = 0 AND cisi.manage_stock = 1',
- $isInStockExpression,
- 1
- );
- }
- return $statusExpr;
+ return $this->getStatusExpression->execute($this->getTypeId(), $connection, $isAggregate);
}
/**
diff --git a/vendor/magento/module-catalog-inventory/Model/ResourceModel/Indexer/Stock/GetStatusExpression.php b/vendor/magento/module-catalog-inventory/Model/ResourceModel/Indexer/Stock/GetStatusExpression.php
new file mode 100644
index 000000000000..0b7ee4f578ae
--- /dev/null
+++ b/vendor/magento/module-catalog-inventory/Model/ResourceModel/Indexer/Stock/GetStatusExpression.php
@@ -0,0 +1,52 @@
+statusExpressions = $statusExpressions;
+ }
+
+ /**
+ * Returns stock status expression for MySQL query.
+ *
+ * @param string $productType
+ * @param AdapterInterface $connection
+ * @param bool $isAggregate
+ * @return Zend_Db_Expr|null
+ */
+ public function execute(string $productType, AdapterInterface $connection, bool $isAggregate): ?Zend_Db_Expr
+ {
+ $expression = $this->statusExpressions[$productType] ?? $this->statusExpressions['default'];
+ return $expression->getExpression($connection, $isAggregate);
+ }
+}
diff --git a/vendor/magento/module-catalog-inventory/Model/ResourceModel/Indexer/Stock/StatusExpression/DefaultExpression.php b/vendor/magento/module-catalog-inventory/Model/ResourceModel/Indexer/Stock/StatusExpression/DefaultExpression.php
new file mode 100644
index 000000000000..a7e97f7b6d6e
--- /dev/null
+++ b/vendor/magento/module-catalog-inventory/Model/ResourceModel/Indexer/Stock/StatusExpression/DefaultExpression.php
@@ -0,0 +1,61 @@
+scopeConfig = $scopeConfig;
+ }
+
+ /**
+ * Returns status expressions for MySQL query
+ *
+ * @ingeritdoc
+ * @param AdapterInterface $connection
+ * @param bool $isAggregate
+ * @return Zend_Db_Expr
+ */
+ public function getExpression(AdapterInterface $connection, bool $isAggregate): Zend_Db_Expr
+ {
+ $isManageStock = $this->scopeConfig->isSetFlag(
+ Configuration::XML_PATH_MANAGE_STOCK,
+ ScopeInterface::SCOPE_STORE
+ );
+
+ $isInStockExpression = $isAggregate ? 'MAX(cisi.is_in_stock)' : 'cisi.is_in_stock';
+ if ($isManageStock) {
+ $statusExpr = $connection->getCheckSql(
+ 'cisi.use_config_manage_stock = 0 AND cisi.manage_stock = 0',
+ 1,
+ $isInStockExpression
+ );
+ } else {
+ $statusExpr = $connection->getCheckSql(
+ 'cisi.use_config_manage_stock = 0 AND cisi.manage_stock = 1',
+ $isInStockExpression,
+ 1
+ );
+ }
+ return $statusExpr;
+ }
+}
diff --git a/vendor/magento/module-catalog-inventory/Model/ResourceModel/Indexer/Stock/StatusExpression/ExpressionInterface.php b/vendor/magento/module-catalog-inventory/Model/ResourceModel/Indexer/Stock/StatusExpression/ExpressionInterface.php
new file mode 100644
index 000000000000..1e2738ff983c
--- /dev/null
+++ b/vendor/magento/module-catalog-inventory/Model/ResourceModel/Indexer/Stock/StatusExpression/ExpressionInterface.php
@@ -0,0 +1,25 @@
+
+
+
+
+ - Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock\StatusExpression\DefaultExpression
+
+
+
diff --git a/vendor/magento/module-configurable-import-export/Plugin/Import/Product/UpdateConfigurableProductsStockItemStatusPlugin.php b/vendor/magento/module-configurable-import-export/Plugin/Import/Product/UpdateConfigurableProductsStockItemStatusPlugin.php
new file mode 100644
index 000000000000..9a2881d3031b
--- /dev/null
+++ b/vendor/magento/module-configurable-import-export/Plugin/Import/Product/UpdateConfigurableProductsStockItemStatusPlugin.php
@@ -0,0 +1,49 @@
+changeParentStockStatus = $changeParentStockStatus;
+ }
+
+ /**
+ * Update configurable products stock item status based on children products stock status after import
+ *
+ * @param StockItemImporterInterface $subject
+ * @param mixed $result
+ * @param array $stockData
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function afterImport(
+ StockItemImporterInterface $subject,
+ $result,
+ array $stockData
+ ): void {
+ if ($stockData) {
+ $this->changeParentStockStatus->execute(array_column($stockData, 'product_id'));
+ }
+ }
+}
diff --git a/vendor/magento/module-configurable-import-export/etc/di.xml b/vendor/magento/module-configurable-import-export/etc/di.xml
index f72f3885d45c..c30eae0aa9a7 100644
--- a/vendor/magento/module-configurable-import-export/etc/di.xml
+++ b/vendor/magento/module-configurable-import-export/etc/di.xml
@@ -13,4 +13,9 @@
+
+
+
diff --git a/vendor/magento/module-configurable-product/Model/Inventory/ChangeParentStockStatus.php b/vendor/magento/module-configurable-product/Model/Inventory/ChangeParentStockStatus.php
new file mode 100644
index 000000000000..4ad15ea905f0
--- /dev/null
+++ b/vendor/magento/module-configurable-product/Model/Inventory/ChangeParentStockStatus.php
@@ -0,0 +1,128 @@
+configurableType = $configurableType;
+ $this->criteriaInterfaceFactory = $criteriaInterfaceFactory;
+ $this->stockItemRepository = $stockItemRepository;
+ $this->stockConfiguration = $stockConfiguration;
+ }
+
+ /**
+ * Update stock status of configurable products based on children products stock status
+ *
+ * @param array $childrenIds
+ * @return void
+ */
+ public function execute(array $childrenIds): void
+ {
+ $parentIds = $this->configurableType->getParentIdsByChild($childrenIds);
+ foreach (array_unique($parentIds) as $productId) {
+ $this->processStockForParent((int)$productId);
+ }
+ }
+
+ /**
+ * Update stock status of configurable product based on children products stock status
+ *
+ * @param int $productId
+ * @return void
+ */
+ private function processStockForParent(int $productId): void
+ {
+ $criteria = $this->criteriaInterfaceFactory->create();
+ $criteria->setScopeFilter($this->stockConfiguration->getDefaultScopeId());
+
+ $criteria->setProductsFilter($productId);
+ $stockItemCollection = $this->stockItemRepository->getList($criteria);
+ $allItems = $stockItemCollection->getItems();
+ if (empty($allItems)) {
+ return;
+ }
+ $parentStockItem = array_shift($allItems);
+
+ $childrenIds = $this->configurableType->getChildrenIds($productId);
+ $criteria->setProductsFilter($childrenIds);
+ $stockItemCollection = $this->stockItemRepository->getList($criteria);
+ $allItems = $stockItemCollection->getItems();
+
+ $childrenIsInStock = false;
+
+ foreach ($allItems as $childItem) {
+ if ($childItem->getIsInStock() === true) {
+ $childrenIsInStock = true;
+ break;
+ }
+ }
+
+ if ($this->isNeedToUpdateParent($parentStockItem, $childrenIsInStock)) {
+ $parentStockItem->setIsInStock($childrenIsInStock);
+ $parentStockItem->setStockStatusChangedAuto(1);
+ $parentStockItem->setStockStatusChangedAutomaticallyFlag(true);
+ $this->stockItemRepository->save($parentStockItem);
+ }
+ }
+
+ /**
+ * Check if parent item should be updated
+ *
+ * @param StockItemInterface $parentStockItem
+ * @param bool $childrenIsInStock
+ * @return bool
+ */
+ private function isNeedToUpdateParent(
+ StockItemInterface $parentStockItem,
+ bool $childrenIsInStock
+ ): bool {
+ return $parentStockItem->getIsInStock() !== $childrenIsInStock &&
+ ($childrenIsInStock === false || $parentStockItem->getStockStatusChangedAuto());
+ }
+}
diff --git a/vendor/magento/module-configurable-product/Model/Inventory/ParentItemProcessor.php b/vendor/magento/module-configurable-product/Model/Inventory/ParentItemProcessor.php
index f1567f2b196d..4ae3efdd6aca 100644
--- a/vendor/magento/module-configurable-product/Model/Inventory/ParentItemProcessor.php
+++ b/vendor/magento/module-configurable-product/Model/Inventory/ParentItemProcessor.php
@@ -12,8 +12,8 @@
use Magento\CatalogInventory\Api\StockItemCriteriaInterfaceFactory;
use Magento\CatalogInventory\Api\StockItemRepositoryInterface;
use Magento\CatalogInventory\Api\StockConfigurationInterface;
-use Magento\CatalogInventory\Api\Data\StockItemInterface;
use Magento\CatalogInventory\Observer\ParentItemProcessorInterface;
+use Magento\Framework\App\ObjectManager;
/**
* Process parent stock item
@@ -21,41 +21,27 @@
class ParentItemProcessor implements ParentItemProcessorInterface
{
/**
- * @var Configurable
+ * @var ChangeParentStockStatus
*/
- private $configurableType;
-
- /**
- * @var StockItemCriteriaInterfaceFactory
- */
- private $criteriaInterfaceFactory;
-
- /**
- * @var StockItemRepositoryInterface
- */
- private $stockItemRepository;
-
- /**
- * @var StockConfigurationInterface
- */
- private $stockConfiguration;
+ private $changeParentStockStatus;
/**
* @param Configurable $configurableType
* @param StockItemCriteriaInterfaceFactory $criteriaInterfaceFactory
* @param StockItemRepositoryInterface $stockItemRepository
* @param StockConfigurationInterface $stockConfiguration
+ * @param ChangeParentStockStatus|null $changeParentStockStatus
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter) Deprecated dependencies
*/
public function __construct(
Configurable $configurableType,
StockItemCriteriaInterfaceFactory $criteriaInterfaceFactory,
StockItemRepositoryInterface $stockItemRepository,
- StockConfigurationInterface $stockConfiguration
+ StockConfigurationInterface $stockConfiguration,
+ ?ChangeParentStockStatus $changeParentStockStatus = null
) {
- $this->configurableType = $configurableType;
- $this->criteriaInterfaceFactory = $criteriaInterfaceFactory;
- $this->stockItemRepository = $stockItemRepository;
- $this->stockConfiguration = $stockConfiguration;
+ $this->changeParentStockStatus = $changeParentStockStatus
+ ?? ObjectManager::getInstance()->get(ChangeParentStockStatus::class);
}
/**
@@ -66,64 +52,6 @@ public function __construct(
*/
public function process(Product $product)
{
- $parentIds = $this->configurableType->getParentIdsByChild($product->getId());
- foreach ($parentIds as $productId) {
- $this->processStockForParent((int)$productId);
- }
- }
-
- /**
- * Change stock item for parent product depending on children stock items
- *
- * @param int $productId
- * @return void
- */
- private function processStockForParent(int $productId)
- {
- $criteria = $this->criteriaInterfaceFactory->create();
- $criteria->setScopeFilter($this->stockConfiguration->getDefaultScopeId());
-
- $criteria->setProductsFilter($productId);
- $stockItemCollection = $this->stockItemRepository->getList($criteria);
- $allItems = $stockItemCollection->getItems();
- if (empty($allItems)) {
- return;
- }
- $parentStockItem = array_shift($allItems);
-
- $childrenIds = $this->configurableType->getChildrenIds($productId);
- $criteria->setProductsFilter($childrenIds);
- $stockItemCollection = $this->stockItemRepository->getList($criteria);
- $allItems = $stockItemCollection->getItems();
-
- $childrenIsInStock = false;
-
- foreach ($allItems as $childItem) {
- if ($childItem->getIsInStock() === true) {
- $childrenIsInStock = true;
- break;
- }
- }
-
- if ($this->isNeedToUpdateParent($parentStockItem, $childrenIsInStock)) {
- $parentStockItem->setIsInStock($childrenIsInStock);
- $parentStockItem->setStockStatusChangedAuto(1);
- $this->stockItemRepository->save($parentStockItem);
- }
- }
-
- /**
- * Check is parent item should be updated
- *
- * @param StockItemInterface $parentStockItem
- * @param bool $childrenIsInStock
- * @return bool
- */
- private function isNeedToUpdateParent(
- StockItemInterface $parentStockItem,
- bool $childrenIsInStock
- ): bool {
- return $parentStockItem->getIsInStock() !== $childrenIsInStock &&
- ($childrenIsInStock === false || $parentStockItem->getStockStatusChangedAuto());
+ $this->changeParentStockStatus->execute([$product->getId()]);
}
}
diff --git a/vendor/magento/module-configurable-product/Model/Plugin/UpdateStockChangedAuto.php b/vendor/magento/module-configurable-product/Model/Plugin/UpdateStockChangedAuto.php
new file mode 100644
index 000000000000..c5a0cd5eae7f
--- /dev/null
+++ b/vendor/magento/module-configurable-product/Model/Plugin/UpdateStockChangedAuto.php
@@ -0,0 +1,49 @@
+getProductTypeById = $getProductTypeById;
+ }
+
+ /**
+ * Updates stock_status_changed_auto for configurable product
+ *
+ * @param ItemResourceModel $subject
+ * @param StockItem $stockItem
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function beforeSave(ItemResourceModel $subject, StockItem $stockItem): void
+ {
+ if (!$stockItem->getIsInStock() &&
+ !$stockItem->hasStockStatusChangedAutomaticallyFlag() &&
+ $this->getProductTypeById->execute($stockItem->getProductId()) == Configurable::TYPE_CODE
+ ) {
+ $stockItem->setStockStatusChangedAuto(0);
+ }
+ }
+}
diff --git a/vendor/magento/module-configurable-product/etc/di.xml b/vendor/magento/module-configurable-product/etc/di.xml
index c7f67a69d669..699a812f6005 100644
--- a/vendor/magento/module-configurable-product/etc/di.xml
+++ b/vendor/magento/module-configurable-product/etc/di.xml
@@ -265,4 +265,7 @@
Magento\Framework\Serialize\Serializer\Json
+
+
+