diff --git a/InventoryBundleProduct/Model/IsProductSalableCondition/IsBundleSaleableCondition.php b/InventoryBundleProduct/Model/IsProductSalableCondition/IsBundleSaleableCondition.php
deleted file mode 100644
index 5430921c4b8f..000000000000
--- a/InventoryBundleProduct/Model/IsProductSalableCondition/IsBundleSaleableCondition.php
+++ /dev/null
@@ -1,77 +0,0 @@
-bundleProductType = $type;
- $this->repository = $repository;
- $this->getBundleProductStockStatus = $getBundleProductStockStatus;
- }
-
- /**
- * Is product salable for bundle product.
- *
- * @param string $sku
- * @param int $stockId
- *
- * @return bool
- */
- public function execute(string $sku, int $stockId): bool
- {
- $status = false;
- try {
- $product = $this->repository->get($sku);
- if ($product->getTypeId() === Type::TYPE_CODE) {
- /** @noinspection PhpParamsInspection */
- $options = $this->bundleProductType->getOptionsCollection($product);
- $status = (int)$this->getBundleProductStockStatus->execute(
- $product,
- $options->getItems(),
- $stockId
- );
- }
- } catch (LocalizedException $e) {
- $status = false;
- }
-
- return $status;
- }
-}
diff --git a/InventoryBundleProduct/Plugin/CatalogInventory/Helper/Stock/AdaptAssignStatusToProductPlugin.php b/InventoryBundleProduct/Plugin/CatalogInventory/Helper/Stock/AdaptAssignStatusToProductPlugin.php
deleted file mode 100644
index 5e95a438df8a..000000000000
--- a/InventoryBundleProduct/Plugin/CatalogInventory/Helper/Stock/AdaptAssignStatusToProductPlugin.php
+++ /dev/null
@@ -1,96 +0,0 @@
-bundleProductType = $bundleProductType;
- $this->getBundleProductStockStatus = $getBundleProductStockStatus;
- $this->storeManager = $storeManager;
- $this->stockResolver = $stockResolver;
- }
-
- /**
- * Process bundle product stock status, considering bundle selections.
- *
- * @param Stock $subject
- * @param Product $product
- * @param int|null $status
- * @return array
- * @throws LocalizedException
- * @throws NoSuchEntityException
- * @SuppressWarnings(PHPMD.UnusedFormalParameter)
- */
- public function beforeAssignStatusToProduct(
- Stock $subject,
- Product $product,
- $status = null
- ): array {
- if ($product->getTypeId() === Type::TYPE_CODE) {
- $website = $this->storeManager->getWebsite();
- $stock = $this->stockResolver->execute(SalesChannelInterface::TYPE_WEBSITE, $website->getCode());
- $options = $this->bundleProductType->getOptionsCollection($product);
- try {
- $status = (int)$this->getBundleProductStockStatus->execute(
- $product,
- $options->getItems(),
- $stock->getStockId()
- );
- } catch (LocalizedException $e) {
- $status = 0;
- }
- }
-
- return [$product, $status];
- }
-}
diff --git a/InventoryBundleProduct/Plugin/InventorySales/IsBundleProductSalable.php b/InventoryBundleProduct/Plugin/InventorySales/IsBundleProductSalable.php
index a519a4456a76..6cb952962f14 100644
--- a/InventoryBundleProduct/Plugin/InventorySales/IsBundleProductSalable.php
+++ b/InventoryBundleProduct/Plugin/InventorySales/IsBundleProductSalable.php
@@ -78,26 +78,17 @@ public function aroundExecute(
int $stockId
): bool {
try {
- $types = $this->getProductTypesBySkus->execute([$sku]);
-
$isProductSalable = $proceed($sku, $stockId);
- if (!isset($types[$sku]) || $types[$sku] !== Type::TYPE_CODE || !$isProductSalable) {
+ if (!$isProductSalable) {
return $isProductSalable;
}
- // TODO: remove in https://github.com/magento/inventory/issues/3201
- // Product salability MUST NOT BE CALLED during product load.
- // Tests stabilization.
- /** @var \Magento\Framework\Registry $registry */
- $registry = ObjectManager::getInstance()->get(\Magento\Framework\Registry::class);
- $key = 'inventory_check_product' . $sku;
-
- if ($registry->registry($key)) {
- $product = $registry->registry($key);
- } else {
- $product = $this->productRepository->get($sku);
+ $types = $this->getProductTypesBySkus->execute([$sku]);
+ if (!isset($types[$sku]) || $types[$sku] !== Type::TYPE_CODE) {
+ return $isProductSalable;
}
+ $product = $this->productRepository->get($sku);
/** @noinspection PhpParamsInspection */
$options = $this->bundleProductType->getOptionsCollection($product);
$status = $this->getBundleProductStockStatus->execute(
diff --git a/InventoryBundleProduct/etc/di.xml b/InventoryBundleProduct/etc/di.xml
index 665ab2b50cd6..e9d615337e5a 100644
--- a/InventoryBundleProduct/etc/di.xml
+++ b/InventoryBundleProduct/etc/di.xml
@@ -26,9 +26,6 @@
-
-
-
diff --git a/InventoryCatalog/Model/IsProductSalable.php b/InventoryCatalog/Model/IsProductSalable.php
new file mode 100644
index 000000000000..148006a0ef96
--- /dev/null
+++ b/InventoryCatalog/Model/IsProductSalable.php
@@ -0,0 +1,73 @@
+getStockIdForCurrentWebsite = $getStockIdForCurrentWebsite;
+ $this->areProductsSalable = $areProductsSalable;
+ }
+
+ /**
+ * Verify product salable status.
+ *
+ * @param Product $product
+ * @return bool
+ */
+ public function execute(Product $product): bool
+ {
+ if (null === $product->getSku() || (int)$product->getStatus() === Status::STATUS_DISABLED) {
+ return false;
+ }
+ if ($product->getData('is_salable') !== null) {
+ return (bool)$product->getData('is_salable');
+ }
+ $stockId = $this->getStockIdForCurrentWebsite->execute();
+ if (isset($this->productStatusCache[$stockId][$product->getSku()])) {
+ return $this->productStatusCache[$stockId][$product->getSku()];
+ }
+
+ $stockId = $this->getStockIdForCurrentWebsite->execute();
+ $result = current($this->areProductsSalable->execute([$product->getSku()], $stockId));
+ $salabilityStatus = $result->isSalable();
+ $this->productStatusCache[$stockId][$product->getSku()] = $salabilityStatus;
+
+ return $salabilityStatus;
+ }
+}
diff --git a/InventoryCatalog/Plugin/Catalog/Model/Type/Simple/IsSalablePlugin.php b/InventoryCatalog/Plugin/Catalog/Model/Type/Simple/IsSalablePlugin.php
new file mode 100644
index 000000000000..3a6ea88803ef
--- /dev/null
+++ b/InventoryCatalog/Plugin/Catalog/Model/Type/Simple/IsSalablePlugin.php
@@ -0,0 +1,46 @@
+isProductSalable = $isProductSalable;
+ }
+
+ /**
+ * Fetches is salable status from multi-stock.
+ *
+ * @param Simple $subject
+ * @param \Closure $proceed
+ * @param Product $product
+ * @return bool
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function aroundIsSalable(Simple $subject, \Closure $proceed, Product $product): bool
+ {
+ return $this->isProductSalable->execute($product);
+ }
+}
diff --git a/InventoryCatalog/Plugin/Catalog/Model/Type/Virtual/IsSalablePlugin.php b/InventoryCatalog/Plugin/Catalog/Model/Type/Virtual/IsSalablePlugin.php
new file mode 100644
index 000000000000..2554d070a554
--- /dev/null
+++ b/InventoryCatalog/Plugin/Catalog/Model/Type/Virtual/IsSalablePlugin.php
@@ -0,0 +1,45 @@
+isProductSalable = $isProductSalable;
+ }
+
+ /**
+ * Fetches is salable status from multi-stock.
+ *
+ * @param Product\Type\Virtual $subject
+ * @param \Closure $proceed
+ * @param Product $product
+ * @return bool
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function aroundIsSalable(Product\Type\Virtual $subject, \Closure $proceed, Product $product): bool
+ {
+ return $this->isProductSalable->execute($product);
+ }
+}
diff --git a/InventoryCatalog/Plugin/CatalogInventory/Api/StockRegistry/AdaptGetStockStatusPlugin.php b/InventoryCatalog/Plugin/CatalogInventory/Api/StockRegistry/AdaptGetStockStatusPlugin.php
index 6593389973a0..721fc72de5e1 100644
--- a/InventoryCatalog/Plugin/CatalogInventory/Api/StockRegistry/AdaptGetStockStatusPlugin.php
+++ b/InventoryCatalog/Plugin/CatalogInventory/Api/StockRegistry/AdaptGetStockStatusPlugin.php
@@ -12,6 +12,7 @@
use Magento\Framework\Exception\InputException;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\NoSuchEntityException;
+use Magento\InventoryCatalogApi\Api\DefaultStockProviderInterface;
use Magento\InventoryCatalogApi\Model\GetSkusByProductIdsInterface;
use Magento\InventorySalesApi\Api\AreProductsSalableInterface;
use Magento\InventorySalesApi\Api\Data\SalesChannelInterface;
@@ -49,25 +50,33 @@ class AdaptGetStockStatusPlugin
*/
private $stockResolver;
+ /**
+ * @var DefaultStockProviderInterface
+ */
+ private $defaultStockProvider;
+
/**
* @param AreProductsSalableInterface $areProductsSalable
* @param GetProductSalableQtyInterface $getProductSalableQty
* @param GetSkusByProductIdsInterface $getSkusByProductIds
* @param StoreManagerInterface $storeManager
* @param StockResolverInterface $stockResolver
+ * @param DefaultStockProviderInterface $defaultStockProvider
*/
public function __construct(
AreProductsSalableInterface $areProductsSalable,
GetProductSalableQtyInterface $getProductSalableQty,
GetSkusByProductIdsInterface $getSkusByProductIds,
StoreManagerInterface $storeManager,
- StockResolverInterface $stockResolver
+ StockResolverInterface $stockResolver,
+ DefaultStockProviderInterface $defaultStockProvider
) {
$this->areProductsSalable = $areProductsSalable;
$this->getProductSalableQty = $getProductSalableQty;
$this->getSkusByProductIds = $getSkusByProductIds;
$this->storeManager = $storeManager;
$this->stockResolver = $stockResolver;
+ $this->defaultStockProvider = $defaultStockProvider;
}
/**
@@ -92,6 +101,9 @@ public function afterGetStockStatus(
? $this->storeManager->getWebsite()->getCode()
: $this->storeManager->getWebsite($scopeId)->getCode();
$stockId = $this->stockResolver->execute(SalesChannelInterface::TYPE_WEBSITE, $websiteCode)->getStockId();
+ if ($this->defaultStockProvider->getId() === $stockId) {
+ return $stockStatus;
+ }
$sku = $this->getSkusByProductIds->execute([$productId])[$productId];
$result = $this->areProductsSalable->execute([$sku], $stockId);
diff --git a/InventoryCatalog/Plugin/CatalogInventory/Helper/Stock/AdaptAssignStatusToProductPlugin.php b/InventoryCatalog/Plugin/CatalogInventory/Helper/Stock/AdaptAssignStatusToProductPlugin.php
deleted file mode 100644
index ba54a8a82e64..000000000000
--- a/InventoryCatalog/Plugin/CatalogInventory/Helper/Stock/AdaptAssignStatusToProductPlugin.php
+++ /dev/null
@@ -1,108 +0,0 @@
-getStockIdForCurrentWebsite = $getStockIdForCurrentWebsite;
- $this->areProductsSalable = $areProductsSalable;
- $this->defaultStockProvider = $defaultStockProvider;
- $this->getProductIdsBySkus = $getProductIdsBySkus;
- }
-
- /**
- * Assign stock status to product considering multi stock environment.
- *
- * @param Stock $subject
- * @param Product $product
- * @param int|null $status
- * @return array
- *
- * @SuppressWarnings(PHPMD.UnusedFormalParameter)
- */
- public function beforeAssignStatusToProduct(
- Stock $subject,
- Product $product,
- ?int $status = null
- ): array {
- if (null === $product->getSku()) {
- return [$product, $status];
- }
-
- // TODO: remove in https://github.com/magento/inventory/issues/3201
- // Product salability MUST NOT BE CALLED during product load.
- // Tests stabilization.
- /** @var \Magento\Framework\Registry $registry */
- $registry = ObjectManager::getInstance()->get(\Magento\Framework\Registry::class);
- $key = 'inventory_check_product' . $product->getSku();
- try {
- if ($registry->registry($key)) {
- $registry->unregister($key);
- }
- $registry->register($key, $product);
-
- $this->getProductIdsBySkus->execute([$product->getSku()]);
- if (null === $status) {
- $stockId = $this->getStockIdForCurrentWebsite->execute();
- $result = $this->areProductsSalable->execute([$product->getSku()], $stockId);
- $result = current($result);
- $registry->unregister($key);
- return [$product, (int)$result->isSalable()];
- }
- $registry->unregister($key);
- } catch (NoSuchEntityException $e) {
- $registry->unregister($key);
- return [$product, $status];
- }
- return [$product, $status];
- }
-}
diff --git a/InventoryCatalog/Plugin/Downloadable/Model/Product/Type/IsSalablePlugin.php b/InventoryCatalog/Plugin/Downloadable/Model/Product/Type/IsSalablePlugin.php
new file mode 100644
index 000000000000..b4f41a6f021b
--- /dev/null
+++ b/InventoryCatalog/Plugin/Downloadable/Model/Product/Type/IsSalablePlugin.php
@@ -0,0 +1,46 @@
+isProductSalable = $isProductSalable;
+ }
+
+ /**
+ * Fetches is salable status from multi-stock.
+ *
+ * @param Type $subject
+ * @param \Closure $proceed
+ * @param Product $product
+ * @return bool
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function aroundIsSalable(Type $subject, \Closure $proceed, Product $product): bool
+ {
+ return $subject->hasLinks($product) && $this->isProductSalable->execute($product);
+ }
+}
diff --git a/InventoryCatalog/composer.json b/InventoryCatalog/composer.json
index c3651026905c..939c8bc967db 100644
--- a/InventoryCatalog/composer.json
+++ b/InventoryCatalog/composer.json
@@ -5,6 +5,7 @@
"php": "~7.3.0||~7.4.0",
"magento/framework": "*",
"magento/module-catalog": "*",
+ "magento/module-downloadable": "*",
"magento/module-catalog-inventory": "*",
"magento/module-inventory-api": "*",
"magento/module-inventory-catalog-api": "*",
diff --git a/InventoryCatalog/etc/di.xml b/InventoryCatalog/etc/di.xml
index 5c29afadc6e6..a67b3c398975 100644
--- a/InventoryCatalog/etc/di.xml
+++ b/InventoryCatalog/etc/di.xml
@@ -52,7 +52,6 @@
-
@@ -164,4 +163,13 @@
+
+
+
+
+
+
+
+
+
diff --git a/InventoryCatalog/etc/events.xml b/InventoryCatalog/etc/events.xml
new file mode 100644
index 000000000000..3ff4e7df341a
--- /dev/null
+++ b/InventoryCatalog/etc/events.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
diff --git a/InventoryConfigurableProduct/Model/IsProductSalableCondition/IsConfigurableProductSalable.php b/InventoryConfigurableProduct/Model/IsProductSalableCondition/IsConfigurableProductSalable.php
deleted file mode 100644
index 05d5084f8088..000000000000
--- a/InventoryConfigurableProduct/Model/IsProductSalableCondition/IsConfigurableProductSalable.php
+++ /dev/null
@@ -1,84 +0,0 @@
-repository = $repository;
- $this->areProductsSalable = $areProductsSalable;
- $this->configurable = $configurable;
- }
-
- /**
- * Is configurable product salable.
- *
- * @param string $sku
- * @param int $stockId
- *
- * @return bool
- */
- public function execute(string $sku, int $stockId): bool
- {
- try {
- $status = false;
- $product = $this->repository->get($sku);
- if ($product->getTypeId() === Configurable::TYPE_CODE) {
- /** @noinspection PhpParamsInspection */
- $options = $this->configurable->getConfigurableOptions($product);
- $skus = [[]];
- foreach ($options as $attribute) {
- $skus[] = array_column($attribute, 'sku');
- }
- $skus = array_merge(...$skus);
- $results = $this->areProductsSalable->execute($skus, $stockId);
- foreach ($results as $result) {
- if ($result->isSalable()) {
- $status = true;
- break;
- }
- }
- }
- } catch (NoSuchEntityException $e) {
- $status = false;
- }
-
- return $status;
- }
-}
diff --git a/InventoryConfigurableProduct/Plugin/CatalogInventory/Helper/Stock/AdaptAssignStatusToProductPlugin.php b/InventoryConfigurableProduct/Plugin/CatalogInventory/Helper/Stock/AdaptAssignStatusToProductPlugin.php
deleted file mode 100644
index 68449ceca0ef..000000000000
--- a/InventoryConfigurableProduct/Plugin/CatalogInventory/Helper/Stock/AdaptAssignStatusToProductPlugin.php
+++ /dev/null
@@ -1,97 +0,0 @@
-configurable = $configurable;
- $this->areProductsSalable = $areProductsSalable;
- $this->storeManager = $storeManager;
- $this->stockResolver = $stockResolver;
- }
-
- /**
- * Process configurable product stock status, considering configurable options.
- *
- * @param Stock $subject
- * @param Product $product
- * @param int|null $status
- * @return array
- *
- * @SuppressWarnings(PHPMD.UnusedFormalParameter)
- */
- public function beforeAssignStatusToProduct(
- Stock $subject,
- Product $product,
- $status = null
- ): array {
- if ($product->getTypeId() === Configurable::TYPE_CODE) {
- $website = $this->storeManager->getWebsite();
- $stock = $this->stockResolver->execute(SalesChannelInterface::TYPE_WEBSITE, $website->getCode());
- $options = $this->configurable->getConfigurableOptions($product);
- $status = 0;
- $skus = [[]];
- foreach ($options as $attribute) {
- $skus[] = array_column($attribute, 'sku');
- }
- $skus = array_merge(...$skus);
- $results = $this->areProductsSalable->execute($skus, $stock->getStockId());
- foreach ($results as $result) {
- if ($result->isSalable()) {
- $status = 1;
- break;
- }
- }
- }
-
- return [$product, $status];
- }
-}
diff --git a/InventoryConfigurableProduct/Plugin/InventorySales/IsConfigurableProductSalable.php b/InventoryConfigurableProduct/Plugin/InventorySales/IsConfigurableProductSalable.php
index 49922c0971b6..54b77e3a7f54 100644
--- a/InventoryConfigurableProduct/Plugin/InventorySales/IsConfigurableProductSalable.php
+++ b/InventoryConfigurableProduct/Plugin/InventorySales/IsConfigurableProductSalable.php
@@ -80,30 +80,18 @@ public function aroundExecute(
int $stockId
): bool {
try {
- $types = $this->getProductTypesBySkus->execute([$sku]);
-
$isProductSalable = $proceed($sku, $stockId);
- if (!isset($types[$sku]) || $types[$sku] !== Configurable::TYPE_CODE || !$isProductSalable) {
+ if (!$isProductSalable) {
return $isProductSalable;
}
- // TODO: remove in https://github.com/magento/inventory/issues/3201
- // Product salability MUST NOT BE CALLED during product load.
- // Tests stabilization.
- /** @var \Magento\Framework\Registry $registry */
- $registry = ObjectManager::getInstance()->get(\Magento\Framework\Registry::class);
- $key = 'inventory_check_product' . $sku;
-
- if ($registry->registry($key)) {
- $product = $registry->registry($key);
- } else {
- $product = $this->productRepository->get($sku);
+ $types = $this->getProductTypesBySkus->execute([$sku]);
+ if (!isset($types[$sku]) || $types[$sku] !== Configurable::TYPE_CODE) {
+ return $isProductSalable;
}
+ $product = $this->productRepository->get($sku);
$resultStatus = false;
- // TODO: remove in https://github.com/magento/inventory/issues/3201
- $product->unsetData('_cache_instance_used_product_attributes');
- $product->unsetData('_cache_instance_configurable_attributes');
$options = $this->configurableProductType->getConfigurableOptions($product);
$skus = [[]];
foreach ($options as $attribute) {
diff --git a/InventoryConfigurableProduct/composer.json b/InventoryConfigurableProduct/composer.json
index bee13a97a54d..a04c0f281477 100644
--- a/InventoryConfigurableProduct/composer.json
+++ b/InventoryConfigurableProduct/composer.json
@@ -14,6 +14,9 @@
"magento/module-configurable-product": "*",
"magento/module-inventory-sales": "*"
},
+ "suggest": {
+ "magento/module-inventory-sales": "*"
+ },
"type": "magento2-module",
"license": [
"OSL-3.0",
diff --git a/InventoryConfigurableProduct/etc/frontend/di.xml b/InventoryConfigurableProduct/etc/frontend/di.xml
index e2e71d3e3cdf..68515ce01275 100644
--- a/InventoryConfigurableProduct/etc/frontend/di.xml
+++ b/InventoryConfigurableProduct/etc/frontend/di.xml
@@ -12,7 +12,4 @@
-
-
-