diff --git a/InventoryAdminUi/Test/Mftf/Test/StorefrontPriceOfConfigurableProductAssignedToTestStockAndTestWebsiteWhenOptionOfDisplayingOutOfStockProductsEnabledTest.xml b/InventoryAdminUi/Test/Mftf/Test/StorefrontPriceOfConfigurableProductAssignedToTestStockAndTestWebsiteWhenOptionOfDisplayingOutOfStockProductsEnabledTest.xml
index c9dd470ef25a..df768c1dd2ac 100644
--- a/InventoryAdminUi/Test/Mftf/Test/StorefrontPriceOfConfigurableProductAssignedToTestStockAndTestWebsiteWhenOptionOfDisplayingOutOfStockProductsEnabledTest.xml
+++ b/InventoryAdminUi/Test/Mftf/Test/StorefrontPriceOfConfigurableProductAssignedToTestStockAndTestWebsiteWhenOptionOfDisplayingOutOfStockProductsEnabledTest.xml
@@ -114,6 +114,8 @@
+
+
diff --git a/InventoryConfigurableProduct/Plugin/CatalogInventory/Helper/Stock/AdaptAssignStatusToProductPlugin.php b/InventoryConfigurableProduct/Plugin/CatalogInventory/Helper/Stock/AdaptAssignStatusToProductPlugin.php
index bf7d279336db..e9e341ba30d4 100644
--- a/InventoryConfigurableProduct/Plugin/CatalogInventory/Helper/Stock/AdaptAssignStatusToProductPlugin.php
+++ b/InventoryConfigurableProduct/Plugin/CatalogInventory/Helper/Stock/AdaptAssignStatusToProductPlugin.php
@@ -8,6 +8,7 @@
namespace Magento\InventoryConfigurableProduct\Plugin\CatalogInventory\Helper\Stock;
use Magento\Catalog\Model\Product;
+use Magento\Catalog\Model\Product\Attribute\Source\Status;
use Magento\CatalogInventory\Helper\Stock;
use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
use Magento\InventorySalesApi\Api\Data\SalesChannelInterface;
@@ -74,6 +75,12 @@ public function beforeAssignStatusToProduct(
$status = null
): array {
if ($product->getTypeId() === Configurable::TYPE_CODE) {
+ $salable = (int)$product->getStatus() === Status::STATUS_ENABLED
+ && $product->getQuantityAndStockStatus()['is_in_stock'];
+ if (!$salable) {
+ return [$product, (int)$salable];
+ }
+
$website = $this->storeManager->getWebsite();
$stock = $this->stockResolver->execute(SalesChannelInterface::TYPE_WEBSITE, $website->getCode());
$options = $this->configurable->getConfigurableOptions($product);
diff --git a/InventoryConfigurableProductIndexer/Indexer/SelectBuilder.php b/InventoryConfigurableProductIndexer/Indexer/SelectBuilder.php
index 481056b5eb18..c9fdcc77da0d 100644
--- a/InventoryConfigurableProductIndexer/Indexer/SelectBuilder.php
+++ b/InventoryConfigurableProductIndexer/Indexer/SelectBuilder.php
@@ -8,6 +8,8 @@
namespace Magento\InventoryConfigurableProductIndexer\Indexer;
use Exception;
+use Magento\Framework\App\Config\ScopeConfigInterface;
+use Magento\Framework\App\ObjectManager;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\DB\Select;
use Magento\InventoryMultiDimensionalIndexerApi\Model\Alias;
@@ -40,22 +42,30 @@ class SelectBuilder
*/
private $metadataPool;
+ /**
+ * @var ScopeConfigInterface|null
+ */
+ private $config;
+
/**
* @param ResourceConnection $resourceConnection
* @param IndexNameBuilder $indexNameBuilder
* @param IndexNameResolverInterface $indexNameResolver
* @param MetadataPool $metadataPool
+ * @param ScopeConfigInterface|null $config
*/
public function __construct(
ResourceConnection $resourceConnection,
IndexNameBuilder $indexNameBuilder,
IndexNameResolverInterface $indexNameResolver,
- MetadataPool $metadataPool
+ MetadataPool $metadataPool,
+ ScopeConfigInterface $config = null
) {
$this->resourceConnection = $resourceConnection;
$this->indexNameBuilder = $indexNameBuilder;
$this->indexNameResolver = $indexNameResolver;
$this->metadataPool = $metadataPool;
+ $this->config = $config ?: ObjectManager::getInstance()->get(ScopeConfigInterface::class);
}
/**
@@ -79,14 +89,19 @@ public function execute(int $stockId): Select
$metadata = $this->metadataPool->getMetadata(ProductInterface::class);
$linkField = $metadata->getLinkField();
-
+ $manageStock = (int)$this->config->getValue('cataloginventory/item_options/manage_stock');
+ $isSalableExpr = $manageStock
+ ? 'IF(MAX(parent_stock.manage_stock = 0), MAX(stock.is_salable), IF(MAX(parent_stock.is_in_stock) = 1, '
+ . 'MAX(stock.is_salable), 0))'
+ : 'IF(MAX(parent_stock.use_config_manage_stock) = 0, IF(MAX(parent_stock.manage_stock) = 1 AND '
+ . 'MAX(parent_stock.is_in_stock = 0), 0, MAX(stock.is_salable)), MAX(stock.is_salable))';
$select = $connection->select()
->from(
['stock' => $indexTableName],
[
IndexStructure::SKU => 'parent_product_entity.sku',
IndexStructure::QUANTITY => 'SUM(stock.quantity)',
- IndexStructure::IS_SALABLE => 'MAX(stock.is_salable)',
+ IndexStructure::IS_SALABLE => $isSalableExpr,
]
)->joinInner(
['product_entity' => $this->resourceConnection->getTableName('catalog_product_entity')],
@@ -100,6 +115,10 @@ public function execute(int $stockId): Select
['parent_product_entity' => $this->resourceConnection->getTableName('catalog_product_entity')],
'parent_product_entity.' . $linkField . ' = parent_link.parent_id',
[]
+ )->joinInner(
+ ['parent_stock' => $this->resourceConnection->getTableName('cataloginventory_stock_item')],
+ 'parent_product_entity.entity_id = parent_stock.product_id',
+ []
)
->group(['parent_product_entity.sku']);
diff --git a/InventoryGroupedProductAdminUi/Plugin/Catalog/Model/Product/Link/ProcessSourceItemsAfterSaveAssociatedLinks.php b/InventoryGroupedProductAdminUi/Plugin/Catalog/Model/Product/ProcessSourceItemsPlugin.php
similarity index 76%
rename from InventoryGroupedProductAdminUi/Plugin/Catalog/Model/Product/Link/ProcessSourceItemsAfterSaveAssociatedLinks.php
rename to InventoryGroupedProductAdminUi/Plugin/Catalog/Model/Product/ProcessSourceItemsPlugin.php
index 24ac54936804..de903734816c 100644
--- a/InventoryGroupedProductAdminUi/Plugin/Catalog/Model/Product/Link/ProcessSourceItemsAfterSaveAssociatedLinks.php
+++ b/InventoryGroupedProductAdminUi/Plugin/Catalog/Model/Product/ProcessSourceItemsPlugin.php
@@ -5,19 +5,18 @@
*/
declare(strict_types=1);
-namespace Magento\InventoryGroupedProductAdminUi\Plugin\Catalog\Model\Product\Link;
+namespace Magento\InventoryGroupedProductAdminUi\Plugin\Catalog\Model\Product;
-use Magento\Catalog\Api\Data\ProductInterface;
-use Magento\Catalog\Model\Product\Link;
+use Magento\Catalog\Model\Product;
use Magento\GroupedProduct\Model\Product\Type\Grouped as GroupedProductType;
use Magento\InventoryApi\Api\Data\SourceItemInterface;
use Magento\InventoryApi\Api\GetSourceItemsBySkuInterface;
use Magento\InventoryCatalogAdminUi\Observer\SourceItemsProcessor;
/**
- * After save source links process child source items for reindex grouped product inventory.
+ * After save grouped product process child source items for reindex grouped product inventory.
*/
-class ProcessSourceItemsAfterSaveAssociatedLinks
+class ProcessSourceItemsPlugin
{
/**
* @var GetSourceItemsBySkuInterface
@@ -42,22 +41,22 @@ public function __construct(
}
/**
- * @param Link $subject
- * @param Link $result
- * @param ProductInterface $product
- * @return Link
+ * Process source items after grouped product has been saved.
+ *
+ * @param Product $subject
+ * @param Product $result
+ * @return Product
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
- public function afterSaveProductRelations(
- Link $subject,
- Link $result,
- ProductInterface $product
- ): Link {
- if ($product->getTypeId() !== GroupedProductType::TYPE_CODE) {
+ public function afterAfterSave(
+ Product $subject,
+ Product $result
+ ): Product {
+ if ($result->getTypeId() !== GroupedProductType::TYPE_CODE) {
return $result;
}
- foreach ($product->getProductLinks() as $productLink) {
+ foreach ($result->getProductLinks() as $productLink) {
if ($productLink->getLinkType() === 'associated') {
$this->processSourceItemsForSku($productLink->getLinkedProductSku());
}
@@ -80,7 +79,7 @@ private function processSourceItemsForSku(string $sku): void
$processData[] = [
SourceItemInterface::SOURCE_CODE => $sourceItem->getSourceCode(),
SourceItemInterface::QUANTITY => $sourceItem->getQuantity(),
- SourceItemInterface::STATUS => $sourceItem->getStatus()
+ SourceItemInterface::STATUS => $sourceItem->getStatus(),
];
}
diff --git a/InventoryGroupedProductAdminUi/etc/di.xml b/InventoryGroupedProductAdminUi/etc/di.xml
index 47d6c6e308f2..8c79c708d678 100644
--- a/InventoryGroupedProductAdminUi/etc/di.xml
+++ b/InventoryGroupedProductAdminUi/etc/di.xml
@@ -6,7 +6,7 @@
*/
-->
-
-
+
+
diff --git a/InventoryGroupedProductIndexer/Indexer/SelectBuilder.php b/InventoryGroupedProductIndexer/Indexer/SelectBuilder.php
index 262e4951b661..412654c93f1e 100644
--- a/InventoryGroupedProductIndexer/Indexer/SelectBuilder.php
+++ b/InventoryGroupedProductIndexer/Indexer/SelectBuilder.php
@@ -9,6 +9,8 @@
use Exception;
use Magento\Catalog\Api\Data\ProductInterface;
+use Magento\Framework\App\Config\ScopeConfigInterface;
+use Magento\Framework\App\ObjectManager;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\DB\Select;
use Magento\Framework\EntityManager\MetadataPool;
@@ -41,22 +43,30 @@ class SelectBuilder
*/
private $metadataPool;
+ /**
+ * @var ScopeConfigInterface|null
+ */
+ private $config;
+
/**
* @param ResourceConnection $resourceConnection
* @param IndexNameBuilder $indexNameBuilder
* @param IndexNameResolverInterface $indexNameResolver
* @param MetadataPool $metadataPool
+ * @param ScopeConfigInterface|null $config
*/
public function __construct(
ResourceConnection $resourceConnection,
IndexNameBuilder $indexNameBuilder,
IndexNameResolverInterface $indexNameResolver,
- MetadataPool $metadataPool
+ MetadataPool $metadataPool,
+ ScopeConfigInterface $config = null
) {
$this->resourceConnection = $resourceConnection;
$this->indexNameBuilder = $indexNameBuilder;
$this->indexNameResolver = $indexNameResolver;
$this->metadataPool = $metadataPool;
+ $this->config = $config ?: ObjectManager::getInstance()->get(ScopeConfigInterface::class);
}
/**
@@ -80,14 +90,19 @@ public function execute(int $stockId): Select
$metadata = $this->metadataPool->getMetadata(ProductInterface::class);
$linkField = $metadata->getLinkField();
-
+ $manageStock = (int)$this->config->getValue('cataloginventory/item_options/manage_stock');
+ $isSalableExpr = $manageStock
+ ? 'IF(MAX(parent_stock.manage_stock = 0), MAX(stock.is_salable), IF(MAX(parent_stock.is_in_stock) = 1, '
+ . 'MAX(stock.is_salable), 0))'
+ : 'IF(MAX(parent_stock.use_config_manage_stock) = 0, IF(MAX(parent_stock.manage_stock) = 1 AND '
+ . 'MAX(parent_stock.is_in_stock = 0), 0, MAX(stock.is_salable)), MAX(stock.is_salable))';
$select = $connection->select()
->from(
['stock' => $indexTableName],
[
IndexStructure::SKU => 'parent_product_entity.sku',
IndexStructure::QUANTITY => 'SUM(stock.quantity)',
- IndexStructure::IS_SALABLE => 'MAX(stock.is_salable)',
+ IndexStructure::IS_SALABLE => $isSalableExpr,
]
)->joinInner(
['product_entity' => $this->resourceConnection->getTableName('catalog_product_entity')],
@@ -95,13 +110,17 @@ public function execute(int $stockId): Select
[]
)->joinInner(
['parent_link' => $this->resourceConnection->getTableName('catalog_product_link')],
- 'parent_link.linked_product_id = product_entity.entity_id
+ 'parent_link.linked_product_id = product_entity.entity_id
AND parent_link.link_type_id = ' . Link::LINK_TYPE_GROUPED,
[]
)->joinInner(
['parent_product_entity' => $this->resourceConnection->getTableName('catalog_product_entity')],
'parent_product_entity.' . $linkField . ' = parent_link.product_id',
[]
+ )->joinInner(
+ ['parent_stock' => $this->resourceConnection->getTableName('cataloginventory_stock_item')],
+ 'parent_product_entity.entity_id = parent_stock.product_id',
+ []
)
->group(['parent_product_entity.sku']);