diff --git a/vendor/magento/module-inventory-configurable-product/Pricing/Price/Indexer/BaseStockStatusSelectProcessor.php b/vendor/magento/module-inventory-configurable-product/Pricing/Price/Indexer/BaseStockStatusSelectProcessor.php index 873b1ab89a86..ce63e4f3406b 100644 --- a/vendor/magento/module-inventory-configurable-product/Pricing/Price/Indexer/BaseStockStatusSelectProcessor.php +++ b/vendor/magento/module-inventory-configurable-product/Pricing/Price/Indexer/BaseStockStatusSelectProcessor.php @@ -7,152 +7,22 @@ namespace Magento\InventoryConfigurableProduct\Pricing\Price\Indexer; -use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Model\ResourceModel\Product\BaseSelectProcessorInterface; -use Magento\CatalogInventory\Api\StockConfigurationInterface; -use Magento\CatalogInventory\Model\Stock; -use Magento\Framework\App\ResourceConnection; use Magento\Framework\DB\Select; -use Magento\Framework\EntityManager\MetadataPool; -use Magento\Framework\Exception\NoSuchEntityException; -use Magento\InventoryCatalogApi\Api\DefaultStockProviderInterface; -use Magento\InventoryIndexer\Model\StockIndexTableNameResolverInterface; -use Magento\InventorySalesApi\Api\Data\SalesChannelInterface; -use Magento\InventorySalesApi\Api\StockResolverInterface; -use Magento\Store\Model\StoreManagerInterface; /** * Base select processor. */ class BaseStockStatusSelectProcessor implements BaseSelectProcessorInterface { - /** - * @var StockIndexTableNameResolverInterface - */ - private $stockIndexTableNameResolver; - - /** - * @var StockConfigurationInterface - */ - private $stockConfig; - - /** - * @var StoreManagerInterface - */ - private $storeManager; - - /** - * @var StockResolverInterface - */ - private $stockResolver; - - /** - * @var DefaultStockProviderInterface - */ - private $defaultStockProvider; - - /** - * @var ResourceConnection - */ - private $resourceConnection; - - /** - * @var MetadataPool - */ - private $metadataPool; - - /** - * @param StockIndexTableNameResolverInterface $stockIndexTableNameResolver - * @param StockConfigurationInterface $stockConfig - * @param StoreManagerInterface $storeManager - * @param StockResolverInterface $stockResolver - * @param ResourceConnection $resourceConnection - * @param DefaultStockProviderInterface $defaultStockProvider - * @param MetadataPool $metadataPool - */ - public function __construct( - StockIndexTableNameResolverInterface $stockIndexTableNameResolver, - StockConfigurationInterface $stockConfig, - StoreManagerInterface $storeManager, - StockResolverInterface $stockResolver, - ResourceConnection $resourceConnection, - DefaultStockProviderInterface $defaultStockProvider, - MetadataPool $metadataPool - ) { - $this->stockIndexTableNameResolver = $stockIndexTableNameResolver; - $this->stockConfig = $stockConfig; - $this->storeManager = $storeManager; - $this->stockResolver = $stockResolver; - $this->resourceConnection = $resourceConnection; - $this->defaultStockProvider = $defaultStockProvider; - $this->metadataPool = $metadataPool; - } - /** * Improves the select with stock status sub query. * * @param Select $select * @return Select - * @throws NoSuchEntityException */ public function process(Select $select) { - if (!$this->stockConfig->isShowOutOfStock()) { - return $select; - } - - $websiteCode = $this->storeManager->getWebsite()->getCode(); - $stock = $this->stockResolver->execute(SalesChannelInterface::TYPE_WEBSITE, $websiteCode); - $stockId = (int)$stock->getStockId(); - if ($stockId === $this->defaultStockProvider->getId()) { - $select->join( - ['si' => $this->resourceConnection->getTableName('cataloginventory_stock_item')], - 'si.product_id = l.product_id', - [] - ); - $select->joinInner( - ['stock_parent' => $this->stockIndexTableNameResolver->execute($stockId)], - 'stock_parent.sku = le.sku', - [] - ); - $select->joinLeft( - ['si_parent' => $this->resourceConnection->getTableName('cataloginventory_stock_item')], - 'si_parent.product_id = l.parent_id', - [] - ); - $select->where('si.is_in_stock = ?', Stock::STOCK_IN_STOCK); - $select->orWhere( - 'IFNULL(si_parent.is_in_stock, stock_parent.is_salable) = ?', - Stock::STOCK_OUT_OF_STOCK - ); - } else { - $metadata = $this->metadataPool->getMetadata(ProductInterface::class); - $linkField = $metadata->getLinkField(); - - $select->joinInner( - ['le2' => $this->resourceConnection->getTableName('catalog_product_entity')], - 'le2.' . $linkField . ' = l.product_id', - [] - )->joinInner( - ['stock' => $this->stockIndexTableNameResolver->execute($stockId)], - 'stock.sku = le2.sku', - [] - )->joinInner( - ['stock_parent' => $this->stockIndexTableNameResolver->execute($stockId)], - 'stock_parent.sku = le.sku', - [] - )->where( - 'stock.is_salable = ?', - 1 - )->orWhere( - 'stock.is_salable = ?', - 0 - )->where( - 'stock_parent.is_salable = ?', - 0 - ); - } - return $select; } } diff --git a/vendor/magento/module-inventory-configurable-product/Pricing/Price/Indexer/OptionsIndexer.php b/vendor/magento/module-inventory-configurable-product/Pricing/Price/Indexer/OptionsIndexer.php new file mode 100644 index 000000000000..f985066b41a0 --- /dev/null +++ b/vendor/magento/module-inventory-configurable-product/Pricing/Price/Indexer/OptionsIndexer.php @@ -0,0 +1,205 @@ +stockIndexTableNameResolver = $stockIndexTableNameResolver; + $this->stockConfig = $stockConfig; + $this->storeManager = $storeManager; + $this->stockResolver = $stockResolver; + $this->defaultStockProvider = $defaultStockProvider; + $this->resourceConnection = $resourceConnection; + $this->optionsSelectBuilder = $optionsSelectBuilder; + $this->tableMaintainer = $tableMaintainer; + $this->connectionName = $connectionName; + } + + /** + * @inheritdoc + */ + public function execute(string $indexTable, string $tempIndexTable, ?array $entityIds = null): void + { + $select = $this->optionsSelectBuilder->execute($indexTable, $entityIds); + if ($this->stockConfig->isShowOutOfStock()) { + $stocks = []; + foreach ($this->storeManager->getWebsites() as $website) { + $stock = $this->stockResolver->execute(SalesChannelInterface::TYPE_WEBSITE, $website->getCode()); + $stocks[(int) $stock->getStockId()][] = (int) $website->getId(); + } + $defaultStockId = $this->defaultStockProvider->getId(); + $stocksCount = count($stocks); + foreach ($stocks as $stockId => $websiteIds) { + $selectClone = clone $select; + if ($stockId === $defaultStockId) { + $stockIndexTableName = $this->resourceConnection->getTableName('cataloginventory_stock_status'); + $selectClone->joinInner( + ['child_stock_default' => $stockIndexTableName], + 'child_stock_default.product_id = l.product_id', + [] + )->joinInner( + ['parent_stock_default' => $stockIndexTableName], + 'parent_stock_default.product_id = le.entity_id', + [] + )->where( + 'child_stock_default.stock_status = 1 OR parent_stock_default.stock_status = 0' + ); + } else { + $selectClone->joinInner( + ['child_entity' => $this->resourceConnection->getTableName('catalog_product_entity')], + 'child_entity.entity_id = l.product_id', + [] + )->joinInner( + ['child_stock' => $this->stockIndexTableNameResolver->execute($stockId)], + 'child_stock.sku = child_entity.sku', + [] + )->joinInner( + ['parent_stock_item' => $this->resourceConnection->getTableName('cataloginventory_stock_item')], + 'parent_stock_item.product_id = le.entity_id', + [] + )->where( + 'child_stock.is_salable = 1' + . ' OR parent_stock_item.is_in_stock = 0' + . ' OR NOT EXISTS ('. $this->getInStockOptionsSelect($indexTable, $stockId)->assemble() .')' + ); + } + + if ($stocksCount > 1) { + $selectClone->where( + 'i.website_id IN (?)', + $websiteIds + ); + } + + $this->tableMaintainer->insertFromSelect($selectClone, $tempIndexTable, []); + } + } else { + $this->tableMaintainer->insertFromSelect($select, $tempIndexTable, []); + } + } + + /** + * Return select with in-stock configurable product options + * + * The indexed stock status of configurable product does not take into account disabled products + * This select is intended only to check if any active option of the configurable product is in stock + * + * @param string $indexTableName + * @param int $stockId + */ + public function getInStockOptionsSelect(string $indexTableName, int $stockId): Select + { + $select = $this->resourceConnection->getConnection($this->connectionName)->select(); + + $select->from( + ['i_2' => $indexTableName], + [new Expression('1')] + )->joinInner( + ['l_2' => $this->resourceConnection->getTableName('catalog_product_super_link')], + 'l_2.product_id = i_2.entity_id', + [] + )->joinInner( + ['ce_2' => $this->resourceConnection->getTableName('catalog_product_entity')], + 'ce_2.entity_id = l_2.product_id', + [] + )->joinInner( + ['cs_2' => $this->stockIndexTableNameResolver->execute($stockId)], + 'cs_2.sku = ce_2.sku', + [] + )->where( + 'l_2.parent_id = l.parent_id' . + ' AND i_2.website_id = i.website_id' . + ' AND cs_2.is_salable = 1' + ); + + return $select; + } +} diff --git a/vendor/magento/module-inventory-configurable-product/etc/di.xml b/vendor/magento/module-inventory-configurable-product/etc/di.xml index 93313cc5fbc8..9e1ad0985299 100644 --- a/vendor/magento/module-inventory-configurable-product/etc/di.xml +++ b/vendor/magento/module-inventory-configurable-product/etc/di.xml @@ -7,6 +7,7 @@ --> + @@ -26,9 +27,9 @@ - + - Magento\InventoryConfigurableProduct\Pricing\Price\Indexer\BaseStockStatusSelectProcessor + Magento\InventoryConfigurableProduct\Pricing\Price\Indexer\BaseStockStatusSelectProcessor