diff --git a/Inventory/Test/Integration/GetSourceCodesBySkusTest.php b/Inventory/Test/Integration/GetSourceCodesBySkusTest.php
index be649065b1f6..e0971d032fe4 100644
--- a/Inventory/Test/Integration/GetSourceCodesBySkusTest.php
+++ b/Inventory/Test/Integration/GetSourceCodesBySkusTest.php
@@ -11,6 +11,9 @@
use Magento\TestFramework\Helper\Bootstrap;
use PHPUnit\Framework\TestCase;
+/**
+ * @magentoDbIsolation disabled
+ */
class GetSourceCodesBySkusTest extends TestCase
{
/**
diff --git a/InventoryAdminUi/Test/Mftf/Test/AdminManageStockInConfigurationTurnedOffForDownloadableProductTest.xml b/InventoryAdminUi/Test/Mftf/Test/AdminManageStockInConfigurationTurnedOffForDownloadableProductTest.xml
index 42ba567b3c32..ded4c92b1e09 100644
--- a/InventoryAdminUi/Test/Mftf/Test/AdminManageStockInConfigurationTurnedOffForDownloadableProductTest.xml
+++ b/InventoryAdminUi/Test/Mftf/Test/AdminManageStockInConfigurationTurnedOffForDownloadableProductTest.xml
@@ -23,7 +23,7 @@
-
+
@@ -76,7 +76,7 @@
-
+
diff --git a/InventoryAdvancedCheckout/Plugin/Model/AreProductsSalablePlugin.php b/InventoryAdvancedCheckout/Plugin/Model/AreProductsSalablePlugin.php
index ed99695aae25..55e5996244b2 100644
--- a/InventoryAdvancedCheckout/Plugin/Model/AreProductsSalablePlugin.php
+++ b/InventoryAdvancedCheckout/Plugin/Model/AreProductsSalablePlugin.php
@@ -8,14 +8,10 @@
namespace Magento\InventoryAdvancedCheckout\Plugin\Model;
use Magento\AdvancedCheckout\Model\AreProductsSalableForRequestedQtyInterface;
-use Magento\AdvancedCheckout\Model\Data\IsProductsSalableForRequestedQtyResult;
+use Magento\AdvancedCheckout\Model\Data\IsProductsSalableForRequestedQtyResultFactory;
+use Magento\AdvancedCheckout\Model\Data\ProductQuantity;
+use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Framework\Exception\NoSuchEntityException;
-use Magento\Framework\ObjectManagerInterface;
-use Magento\InventoryCatalogApi\Api\DefaultStockProviderInterface;
-use Magento\InventorySalesApi\Api\AreProductsSalableInterface;
-use Magento\InventorySalesApi\Api\Data\SalesChannelInterface;
-use Magento\InventorySalesApi\Api\StockResolverInterface;
-use Magento\Store\Api\WebsiteRepositoryInterface;
/**
* Provides multi-sourcing capabilities for Advanced Checkout Order By SKU feature.
@@ -23,60 +19,35 @@
class AreProductsSalablePlugin
{
/**
- * @var AreProductsSalableInterface
+ * @var ProductRepositoryInterface
*/
- private $areProductsSalable;
+ private $productRepository;
/**
- * @var StockResolverInterface
+ * @var IsProductsSalableForRequestedQtyResultFactory
*/
- private $stockResolver;
+ private $isProductsSalableForRequestedQtyResultFactory;
/**
- * @var WebsiteRepositoryInterface
- */
- private $websiteRepository;
-
- /**
- * @var DefaultStockProviderInterface
- */
- private $defaultStockProvider;
-
- /**
- * @var ObjectManagerInterface
- */
- private $objectManager;
-
- /**
- * @param AreProductsSalableInterface $areProductsSalable
- * @param StockResolverInterface $stockResolver
- * @param WebsiteRepositoryInterface $websiteRepository
- * @param DefaultStockProviderInterface $defaultStockProvider
- * @param ObjectManagerInterface $objectManager
+ * @param ProductRepositoryInterface $productRepository
+ * @param IsProductsSalableForRequestedQtyResultFactory $isProductsSalableForRequestedQtyResultFactory
*/
public function __construct(
- AreProductsSalableInterface $areProductsSalable,
- StockResolverInterface $stockResolver,
- WebsiteRepositoryInterface $websiteRepository,
- DefaultStockProviderInterface $defaultStockProvider,
- ObjectManagerInterface $objectManager
+ ProductRepositoryInterface $productRepository,
+ IsProductsSalableForRequestedQtyResultFactory $isProductsSalableForRequestedQtyResultFactory
) {
- $this->areProductsSalable = $areProductsSalable;
- $this->stockResolver = $stockResolver;
- $this->websiteRepository = $websiteRepository;
- $this->defaultStockProvider = $defaultStockProvider;
- $this->objectManager = $objectManager;
+ $this->productRepository = $productRepository;
+ $this->isProductsSalableForRequestedQtyResultFactory = $isProductsSalableForRequestedQtyResultFactory;
}
/**
- * Get is product out of stock for given Product id in a given Website id in MSI context.
+ * Get products salable status for given sku requests.
*
* @param AreProductsSalableForRequestedQtyInterface $subject
* @param callable $proceed
- * @param \Magento\AdvancedCheckout\Model\Data\ProductQuantity[] $productQuantities
+ * @param ProductQuantity[] $productQuantities
* @param int $websiteId
* @return array
- * @throws NoSuchEntityException
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function aroundExecute(
@@ -85,22 +56,18 @@ public function aroundExecute(
array $productQuantities,
int $websiteId
): array {
- $website = $this->websiteRepository->getById($websiteId);
- $stock = $this->stockResolver->execute(SalesChannelInterface::TYPE_WEBSITE, $website->getCode());
- if ($this->defaultStockProvider->getId() === $stock->getStockId()) {
- return $proceed($productQuantities, $websiteId);
- }
-
- $skus = [];
- foreach ($productQuantities as $productQuantity) {
- $skus[] = $productQuantity->getSku();
- }
$result = [];
- foreach ($this->areProductsSalable->execute($skus, $stock->getStockId()) as $productStock) {
- $result[] = $this->objectManager->create(
- IsProductsSalableForRequestedQtyResult::class,
- ['sku' => $productStock->getSku(), 'isSalable' => $productStock->isSalable()]
- );
+ foreach ($productQuantities as $productQuantity) {
+ try {
+ $product = $this->productRepository->get($productQuantity->getSku());
+ $result[] = $this->isProductsSalableForRequestedQtyResultFactory->create(
+ ['sku' => $product->getSku(), 'isSalable' => $product->isSalable()]
+ );
+ } catch (NoSuchEntityException $e) {
+ $result[] = $this->isProductsSalableForRequestedQtyResultFactory->create(
+ ['sku' => $productQuantity->getSku(), 'isSalable' => false]
+ );
+ }
}
return $result;
diff --git a/InventoryAdvancedCheckout/composer.json b/InventoryAdvancedCheckout/composer.json
index 0e60183b67fa..42df77216c9a 100644
--- a/InventoryAdvancedCheckout/composer.json
+++ b/InventoryAdvancedCheckout/composer.json
@@ -4,9 +4,7 @@
"require": {
"php": "~7.3.0||~7.4.0",
"magento/framework": "*",
- "magento/module-store": "*",
- "magento/module-inventory-catalog-api": "*",
- "magento/module-inventory-sales-api": "*"
+ "magento/module-catalog": "*"
},
"suggest": {
"magento/module-advanced-checkout": "*"
diff --git a/InventoryBundleProduct/Model/GetBundleProductStockStatus.php b/InventoryBundleProduct/Model/GetBundleProductStockStatus.php
index d99449f503e5..f520f502c8e7 100644
--- a/InventoryBundleProduct/Model/GetBundleProductStockStatus.php
+++ b/InventoryBundleProduct/Model/GetBundleProductStockStatus.php
@@ -24,7 +24,7 @@
class GetBundleProductStockStatus
{
/**
- * @var GetProductSelection
+ * @var GetProductSelections
*/
private $getProductSelection;
@@ -44,13 +44,13 @@ class GetBundleProductStockStatus
private $getStockItemConfiguration;
/**
- * @param GetProductSelection $getProductSelection
+ * @param GetProductSelections $getProductSelection
* @param AreProductsSalableForRequestedQtyInterface $areProductsSalableForRequestedQty
* @param IsProductSalableForRequestedQtyRequestInterfaceFactory $isProductSalableForRequestedQtyRequestFactory
* @param GetStockItemConfigurationInterface $getStockItemConfiguration
*/
public function __construct(
- GetProductSelection $getProductSelection,
+ GetProductSelections $getProductSelection,
AreProductsSalableForRequestedQtyInterface $areProductsSalableForRequestedQty,
IsProductSalableForRequestedQtyRequestInterfaceFactory $isProductSalableForRequestedQtyRequestFactory,
GetStockItemConfigurationInterface $getStockItemConfiguration
diff --git a/InventoryBundleProduct/Model/GetProductSelection.php b/InventoryBundleProduct/Model/GetProductSelections.php
similarity index 96%
rename from InventoryBundleProduct/Model/GetProductSelection.php
rename to InventoryBundleProduct/Model/GetProductSelections.php
index 04e16baee82f..2afc938c6df8 100644
--- a/InventoryBundleProduct/Model/GetProductSelection.php
+++ b/InventoryBundleProduct/Model/GetProductSelections.php
@@ -15,9 +15,9 @@
use Magento\Framework\EntityManager\MetadataPool;
/**
- * Retrieve bundle product selection service.
+ * Retrieve bundle product selections service.
*/
-class GetProductSelection
+class GetProductSelections
{
/**
* @var CollectionFactory
@@ -65,7 +65,6 @@ public function execute(ProductInterface $product, OptionInterface $option): Col
$selectionsCollection->setFlag('product_children', true);
$selectionsCollection->addFilterByRequiredOptions();
$selectionsCollection->setOptionIdsFilter([$option->getId()]);
-
$this->selectionCollectionFilterApplier->apply(
$selectionsCollection,
'parent_product_id',
diff --git a/InventoryBundleProduct/Model/IsBundleProductSalable.php b/InventoryBundleProduct/Model/IsBundleProductSalable.php
new file mode 100644
index 000000000000..74bf8e1654e9
--- /dev/null
+++ b/InventoryBundleProduct/Model/IsBundleProductSalable.php
@@ -0,0 +1,142 @@
+getProductSelection = $getProductSelection;
+ $this->areProductsSalableForRequestedQty = $areProductsSalableForRequestedQty;
+ $this->isProductSalableForRequestedQtyRequestFactory = $isProductSalableForRequestedQtyRequestFactory;
+ $this->getStockItemConfiguration = $getStockItemConfiguration;
+ }
+
+ /**
+ * Provides bundle product stock status.
+ *
+ * @param ProductInterface $product
+ * @param OptionInterface[] $bundleOptions
+ * @param int $stockId
+ * @return bool
+ * @throws LocalizedException
+ * @throws SkuIsNotAssignedToStockException
+ */
+ public function execute(ProductInterface $product, array $bundleOptions, int $stockId): bool
+ {
+ $isSalable = false;
+ foreach ($bundleOptions as $option) {
+ $hasSalable = false;
+ $results = $this->getAreSalableSelections($product, $option, $stockId);
+ foreach ($results as $result) {
+ if ($result->isSalable()) {
+ $hasSalable = true;
+ break;
+ }
+ }
+ if ($hasSalable) {
+ $isSalable = true;
+ }
+
+ if (!$hasSalable && $option->getRequired()) {
+ $isSalable = false;
+ break;
+ }
+ }
+
+ return $isSalable;
+ }
+
+ /**
+ * Get are bundle product selections salable.
+ *
+ * @param ProductInterface $product
+ * @param OptionInterface $option
+ * @param int $stockId
+ * @return IsProductSalableForRequestedQtyResultInterface[]
+ * @throws LocalizedException
+ * @throws SkuIsNotAssignedToStockException
+ */
+ private function getAreSalableSelections(ProductInterface $product, OptionInterface $option, int $stockId): array
+ {
+ $bundleSelections = $this->getProductSelection->execute($product, $option);
+ $skuRequests = [];
+ foreach ($bundleSelections->getItems() as $selection) {
+ $skuRequests[] = $this->isProductSalableForRequestedQtyRequestFactory->create(
+ [
+ 'sku' => (string)$selection->getSku(),
+ 'qty' => $this->getRequestedQty($selection, $stockId),
+ ]
+ );
+ }
+
+ return $this->areProductsSalableForRequestedQty->execute($skuRequests, $stockId);
+ }
+
+ /**
+ * Get bundle product selection qty.
+ *
+ * @param Product $product
+ * @param int $stockId
+ * @return float
+ * @throws LocalizedException
+ * @throws SkuIsNotAssignedToStockException
+ */
+ private function getRequestedQty(Product $product, int $stockId): float
+ {
+ if ((int)$product->getSelectionCanChangeQty()) {
+ $stockItemConfiguration = $this->getStockItemConfiguration->execute((string)$product->getSku(), $stockId);
+ return $stockItemConfiguration->getMinSaleQty();
+ }
+
+ return (float)$product->getSelectionQty();
+ }
+}
diff --git a/InventoryBundleProduct/Model/IsProductSalableCondition/ProductOptionsCondition.php b/InventoryBundleProduct/Model/IsProductSalableCondition/ProductOptionsCondition.php
new file mode 100644
index 000000000000..1f6878b8a580
--- /dev/null
+++ b/InventoryBundleProduct/Model/IsProductSalableCondition/ProductOptionsCondition.php
@@ -0,0 +1,63 @@
+type = $type;
+ $this->productRepository = $productRepository;
+ $this->isBundleProductSalable = $isBundleProductSalable;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function execute(string $sku, int $stockId): bool
+ {
+ $product = $this->productRepository->get($sku);
+ if ($product->getTypeId() !== Type::TYPE_CODE) {
+ return true;
+ }
+ $options = $this->type->getOptionsCollection($product);
+
+ return $this->isBundleProductSalable->execute($product, $options->getItems(), $stockId);
+ }
+}
diff --git a/InventoryBundleProduct/Plugin/Bundle/Model/Product/Type/AdaptIsSalablePlugin.php b/InventoryBundleProduct/Plugin/Bundle/Model/Product/Type/AdaptIsSalablePlugin.php
deleted file mode 100644
index 481881e3cd66..000000000000
--- a/InventoryBundleProduct/Plugin/Bundle/Model/Product/Type/AdaptIsSalablePlugin.php
+++ /dev/null
@@ -1,104 +0,0 @@
-isProductSalable = $isProductSalable;
- $this->storeManager = $storeManager;
- $this->stockByWebsiteIdResolver = $stockByWebsiteIdResolver;
- $this->getBundleProductStockStatus = $getBundleProductStockStatus;
- $this->defaultStockProvider = $defaultStockProvider;
- }
-
- /**
- * Verify, is product salable in multi stock environment.
- *
- * @param Type $subject
- * @param \Closure $proceed
- * @param Product $product
- * @return bool
- */
- public function aroundIsSalable(Type $subject, \Closure $proceed, Product $product): bool
- {
- $salable = $product->getStatus() == Status::STATUS_ENABLED;
- if ($salable && $product->hasData('is_salable')) {
- $salable = $product->getData('is_salable');
- }
-
- if (!(bool)(int)$salable) {
- return false;
- }
-
- if ($product->hasData('all_items_salable')) {
- return $product->getData('all_items_salable');
- }
-
- $website = $this->storeManager->getWebsite();
- $stock = $this->stockByWebsiteIdResolver->execute((int)$website->getId());
- if ($this->defaultStockProvider->getId() === $stock->getStockId()) {
- return $proceed($product);
- }
- $options = $subject->getOptionsCollection($product);
- $isSalable = $this->getBundleProductStockStatus->execute($product, $options->getItems(), $stock->getStockId());
- $product->setData('all_items_salable', $isSalable);
-
- return $isSalable;
- }
-}
diff --git a/InventoryBundleProduct/Plugin/Bundle/Model/ResourceModel/Selection/Collection/AdaptAddQuantityFilterPlugin.php b/InventoryBundleProduct/Plugin/Bundle/Model/ResourceModel/Selection/Collection/AdaptAddQuantityFilterPlugin.php
index a3192b94b108..bab6006cc510 100644
--- a/InventoryBundleProduct/Plugin/Bundle/Model/ResourceModel/Selection/Collection/AdaptAddQuantityFilterPlugin.php
+++ b/InventoryBundleProduct/Plugin/Bundle/Model/ResourceModel/Selection/Collection/AdaptAddQuantityFilterPlugin.php
@@ -8,8 +8,9 @@
namespace Magento\InventoryBundleProduct\Plugin\Bundle\Model\ResourceModel\Selection\Collection;
use Magento\Bundle\Model\ResourceModel\Selection\Collection;
-use Magento\InventoryCatalogApi\Api\DefaultStockProviderInterface;
-use Magento\InventorySalesApi\Api\AreProductsSalableInterface;
+use Magento\CatalogInventory\Model\ResourceModel\Stock\Item;
+use Magento\CatalogInventory\Model\Stock;
+use Magento\InventoryIndexer\Model\StockIndexTableNameResolverInterface;
use Magento\InventorySalesApi\Model\StockByWebsiteIdResolverInterface;
use Magento\Store\Model\StoreManagerInterface;
@@ -19,9 +20,9 @@
class AdaptAddQuantityFilterPlugin
{
/**
- * @var AreProductsSalableInterface
+ * @var Item
*/
- private $areProductsSalable;
+ private $stockItem;
/**
* @var StoreManagerInterface
@@ -34,26 +35,26 @@ class AdaptAddQuantityFilterPlugin
private $stockByWebsiteIdResolver;
/**
- * @var DefaultStockProviderInterface
+ * @var StockIndexTableNameResolverInterface
*/
- private $defaultStockProvider;
+ private $stockIndexTableNameResolver;
/**
- * @param AreProductsSalableInterface $areProductsSalable
+ * @param Item $stockItem
* @param StoreManagerInterface $storeManager
* @param StockByWebsiteIdResolverInterface $stockByWebsiteIdResolver
- * @param DefaultStockProviderInterface $defaultStockProvider
+ * @param StockIndexTableNameResolverInterface $stockIndexTableNameResolver
*/
public function __construct(
- AreProductsSalableInterface $areProductsSalable,
+ Item $stockItem,
StoreManagerInterface $storeManager,
StockByWebsiteIdResolverInterface $stockByWebsiteIdResolver,
- DefaultStockProviderInterface $defaultStockProvider
+ StockIndexTableNameResolverInterface $stockIndexTableNameResolver
) {
- $this->areProductsSalable = $areProductsSalable;
+ $this->stockItem = $stockItem;
$this->storeManager = $storeManager;
$this->stockByWebsiteIdResolver = $stockByWebsiteIdResolver;
- $this->defaultStockProvider = $defaultStockProvider;
+ $this->stockIndexTableNameResolver = $stockIndexTableNameResolver;
}
/**
@@ -62,31 +63,43 @@ public function __construct(
* @param Collection $subject
* @param \Closure $proceed
* @return Collection
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function aroundAddQuantityFilter(
Collection $subject,
\Closure $proceed
): Collection {
- $website = $this->storeManager->getWebsite();
- $stock = $this->stockByWebsiteIdResolver->execute((int)$website->getId());
- if ($this->defaultStockProvider->getId() === $stock->getStockId()) {
- return $proceed();
- }
- $skus = [];
- $skusToExclude = [];
- foreach ($subject->getData() as $item) {
- $skus[] = (string)$item['sku'];
- }
- $results = $this->areProductsSalable->execute($skus, $stock->getStockId());
- foreach ($results as $result) {
- if (!$result->isSalable()) {
- $skusToExclude[] = $result->getSku();
- }
- }
- if ($skusToExclude) {
- $subject->getSelect()->where('e.sku NOT IN(?)', implode(',', $skusToExclude));
- }
- $subject->resetData();
+ $store = $this->storeManager->getStore($subject->getStoreId());
+ $stock = $this->stockByWebsiteIdResolver->execute((int)$store->getWebsiteId());
+ $stockIndexTableName = $this->stockIndexTableNameResolver->execute($stock->getStockId());
+ $manageStockExpr = $this->stockItem->getManageStockExpr('stock_item');
+ $backordersExpr = $this->stockItem->getBackordersExpr('stock_item');
+ $minQtyExpr = $subject->getConnection()->getCheckSql(
+ 'selection.selection_can_change_qty',
+ $this->stockItem->getMinSaleQtyExpr('stock_item'),
+ 'selection.selection_qty'
+ );
+ $where = $manageStockExpr . ' = 0';
+ $where .= ' OR ('
+ . 'inventory_stock.is_salable = ' . Stock::STOCK_IN_STOCK
+ . ' AND ('
+ . $backordersExpr . ' != ' . Stock::BACKORDERS_NO
+ . ' OR '
+ . $minQtyExpr . ' <= inventory_stock.quantity'
+ . ')'
+ . ')';
+ $subject->getSelect()
+ ->joinInner(
+ ['inventory_stock' => $stockIndexTableName],
+ 'e.sku = inventory_stock.sku',
+ []
+ )
+ ->joinInner(
+ ['stock_item' => $this->stockItem->getMainTable()],
+ 'selection.product_id = stock_item.product_id',
+ []
+ )
+ ->where($where);
return $subject;
}
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/InventoryCatalog/Model/IsProductSalable/IsBundleProductSalablePlugin.php b/InventoryBundleProduct/Plugin/InventoryCatalog/Model/IsProductSalable/IsBundleProductSalablePlugin.php
new file mode 100644
index 000000000000..896474d88142
--- /dev/null
+++ b/InventoryBundleProduct/Plugin/InventoryCatalog/Model/IsProductSalable/IsBundleProductSalablePlugin.php
@@ -0,0 +1,103 @@
+type = $type;
+ $this->getProductSelections = $getProductSelections;
+ }
+
+ /**
+ * Get bundle product status.
+ *
+ * @param IsProductSalable $subject
+ * @param \Closure $proceed
+ * @param ProductInterface $product
+ * @return bool
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function aroundExecute(IsProductSalable $subject, \Closure $proceed, ProductInterface $product): bool
+ {
+ if ($product->getTypeId() !== Type::TYPE_CODE) {
+ return $proceed($product);
+ }
+ if ($product->hasData('all_items_salable')) {
+ return $product->getData('all_items_salable');
+ }
+
+ $isSalable = $proceed($product);
+
+ if (!$isSalable) {
+ return false;
+ }
+ $bundleOptions = $this->type->getOptionsCollection($product);
+ $isSalable = $this->isSalable($bundleOptions, $product);
+ $product->setData('all_items_salable', $isSalable);
+
+ return $isSalable;
+ }
+
+ /**
+ * Verify bundle product has salable option.
+ *
+ * @param Collection $bundleOptions
+ * @param ProductInterface $product
+ * @return bool
+ * @throws \Exception
+ */
+ private function isSalable(Collection $bundleOptions, ProductInterface $product): bool
+ {
+ $isSalable = false;
+ foreach ($bundleOptions as $option) {
+ $selections = $this->getProductSelections->execute($product, $option);
+ $hasSalable = false;
+ foreach ($selections as $selection) {
+ if ((int)$selection->getStatus() === Status::STATUS_ENABLED) {
+ $hasSalable = true;
+ break;
+ }
+ }
+ if ($hasSalable) {
+ $isSalable = true;
+ }
+ if (!$hasSalable && $option->getRequired()) {
+ $isSalable = false;
+ break;
+ }
+ }
+
+ return $isSalable;
+ }
+}
diff --git a/InventoryBundleProduct/Test/Integration/Order/PlaceOrderOnDefaultStockTest.php b/InventoryBundleProduct/Test/Integration/Order/PlaceOrderOnDefaultStockTest.php
index 5f32df6c82d9..5624c2452353 100644
--- a/InventoryBundleProduct/Test/Integration/Order/PlaceOrderOnDefaultStockTest.php
+++ b/InventoryBundleProduct/Test/Integration/Order/PlaceOrderOnDefaultStockTest.php
@@ -32,6 +32,8 @@
* @magentoDataFixture Magento_InventoryBundleProduct::Test/_files/source_items_for_bundle_options_on_default_source.php
* @magentoDataFixture Magento_InventorySalesApi::Test/_files/quote.php
* @magentoDataFixture Magento_InventoryIndexer::Test/_files/reindex_inventory.php
+ *
+ * @magentoDbIsolation disabled
*/
class PlaceOrderOnDefaultStockTest extends TestCase
{
diff --git a/InventoryBundleProduct/Test/Integration/SalesQuoteItem/AddSalesQuoteItemOnDefaultStockTest.php b/InventoryBundleProduct/Test/Integration/SalesQuoteItem/AddSalesQuoteItemOnDefaultStockTest.php
index 3efe1fa18cc0..f29b9d89d3e9 100644
--- a/InventoryBundleProduct/Test/Integration/SalesQuoteItem/AddSalesQuoteItemOnDefaultStockTest.php
+++ b/InventoryBundleProduct/Test/Integration/SalesQuoteItem/AddSalesQuoteItemOnDefaultStockTest.php
@@ -21,6 +21,9 @@
use Magento\TestFramework\Helper\Bootstrap;
use PHPUnit\Framework\TestCase;
+/**
+ * @magentoDbIsolation disabled
+ */
class AddSalesQuoteItemOnDefaultStockTest extends TestCase
{
/**
diff --git a/InventoryBundleProduct/composer.json b/InventoryBundleProduct/composer.json
index 3c27572ebecc..f4aff7254a0c 100644
--- a/InventoryBundleProduct/composer.json
+++ b/InventoryBundleProduct/composer.json
@@ -6,14 +6,18 @@
"magento/framework": "*",
"magento/module-bundle": "*",
"magento/module-catalog": "*",
+ "magento/module-catalog-inventory": "*",
"magento/module-inventory-api": "*",
+ "magento/module-inventory-catalog": "*",
"magento/module-inventory-catalog-api": "*",
"magento/module-inventory-configuration-api": "*",
+ "magento/module-inventory-indexer": "*",
"magento/module-inventory-sales-api": "*",
"magento/module-store": "*"
},
"suggest": {
- "magento/module-catalog-inventory": "*"
+ "magento/module-catalog-inventory": "*",
+ "magento/module-inventory-sales": "*"
},
"type": "magento2-module",
"license": [
diff --git a/InventoryBundleProduct/etc/di.xml b/InventoryBundleProduct/etc/di.xml
index 622c345aecdb..7108f0851596 100644
--- a/InventoryBundleProduct/etc/di.xml
+++ b/InventoryBundleProduct/etc/di.xml
@@ -9,9 +9,6 @@
-
-
-
@@ -26,7 +23,17 @@
-
-
+
+
+
+
+
+
+ -
+
- true
+ - Magento\InventoryBundleProduct\Model\IsProductSalableCondition\ProductOptionsCondition
+
+
+
diff --git a/InventoryBundleProductIndexer/Indexer/SelectBuilder.php b/InventoryBundleProductIndexer/Indexer/SelectBuilder.php
index dc78c4bfdb8b..0a3e660a1725 100644
--- a/InventoryBundleProductIndexer/Indexer/SelectBuilder.php
+++ b/InventoryBundleProductIndexer/Indexer/SelectBuilder.php
@@ -79,7 +79,6 @@ public function execute(int $stockId): Select
->build();
$indexTableName = $this->indexNameResolver->resolveName($indexName);
-
$metadata = $this->metadataPool->getMetadata(ProductInterface::class);
$linkField = $metadata->getLinkField();
@@ -89,7 +88,6 @@ public function execute(int $stockId): Select
[
IndexStructure::SKU => 'parent_product_entity.sku',
IndexStructure::QUANTITY => 'SUM(stock.quantity)',
- IndexStructure::IS_SALABLE => 'MAX(stock.is_salable)',
]
)->joinInner(
['product_entity' => $this->resourceConnection->getTableName('catalog_product_entity')],
diff --git a/InventoryBundleProductIndexer/Indexer/SourceItem/IndexDataBySkuListProvider.php b/InventoryBundleProductIndexer/Indexer/SourceItem/IndexDataBySkuListProvider.php
index c6b83c5a22bd..4638d0d4b2d2 100644
--- a/InventoryBundleProductIndexer/Indexer/SourceItem/IndexDataBySkuListProvider.php
+++ b/InventoryBundleProductIndexer/Indexer/SourceItem/IndexDataBySkuListProvider.php
@@ -10,6 +10,7 @@
use Magento\Framework\App\ResourceConnection;
use Magento\InventoryBundleProductIndexer\Indexer\SelectBuilder;
use Magento\InventoryIndexer\Indexer\IndexStructure;
+use Magento\InventorySalesApi\Api\AreProductsSalableInterface;
/**
* Returns all data for the index by source item list condition.
@@ -26,16 +27,24 @@ class IndexDataBySkuListProvider
*/
private $selectBuilder;
+ /**
+ * @var AreProductsSalableInterface
+ */
+ private $areProductsSalable;
+
/**
* @param ResourceConnection $resourceConnection
* @param SelectBuilder $selectBuilder
+ * @param AreProductsSalableInterface $areProductsSalable
*/
public function __construct(
ResourceConnection $resourceConnection,
- SelectBuilder $selectBuilder
+ SelectBuilder $selectBuilder,
+ AreProductsSalableInterface $areProductsSalable
) {
$this->resourceConnection = $resourceConnection;
$this->selectBuilder = $selectBuilder;
+ $this->areProductsSalable = $areProductsSalable;
}
/**
@@ -53,7 +62,17 @@ public function execute(int $stockId, array $skuList): \ArrayIterator
$select->where('stock.' . IndexStructure::SKU . ' IN (?)', $skuList);
}
$connection = $this->resourceConnection->getConnection();
+ $results = $connection->fetchAll($select);
+ $bundleSkus = array_column($results, 'sku');
+ $salableResults = $this->areProductsSalable->execute($bundleSkus, $stockId);
+ foreach ($salableResults as $salableResult) {
+ foreach ($results as &$result) {
+ if ($salableResult->getSku() === $result['sku']) {
+ $result['is_salable'] = (string)(int)$salableResult->isSalable();
+ }
+ }
+ }
- return new \ArrayIterator($connection->fetchAll($select));
+ return new \ArrayIterator($results);
}
}
diff --git a/InventoryBundleProductIndexer/Indexer/SourceItem/SourceItemIndexer.php b/InventoryBundleProductIndexer/Indexer/SourceItem/SourceItemIndexer.php
index 2c0d805ec8f9..0ba622500574 100644
--- a/InventoryBundleProductIndexer/Indexer/SourceItem/SourceItemIndexer.php
+++ b/InventoryBundleProductIndexer/Indexer/SourceItem/SourceItemIndexer.php
@@ -9,8 +9,8 @@
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\Exception\StateException;
-use Magento\InventoryCatalogApi\Api\DefaultStockProviderInterface;
use Magento\InventoryIndexer\Indexer\InventoryIndexer;
+use Magento\InventoryIndexer\Indexer\Stock\PrepareIndexDataForClearingIndex;
use Magento\InventoryMultiDimensionalIndexerApi\Model\Alias;
use Magento\InventoryMultiDimensionalIndexerApi\Model\IndexHandlerInterface;
use Magento\InventoryMultiDimensionalIndexerApi\Model\IndexNameBuilder;
@@ -52,9 +52,9 @@ class SourceItemIndexer
private $siblingSkuListInStockProvider;
/**
- * @var DefaultStockProviderInterface
+ * @var PrepareIndexDataForClearingIndex
*/
- private $defaultStockProvider;
+ private $prepareIndexDataForClearingIndex;
/**
* @param ResourceConnection $resourceConnection
@@ -63,7 +63,7 @@ class SourceItemIndexer
* @param IndexStructureInterface $indexStructure
* @param IndexDataBySkuListProvider $indexDataBySkuListProvider
* @param SiblingSkuListInStockProvider $siblingSkuListInStockProvider
- * @param DefaultStockProviderInterface $defaultStockProvider
+ * @param PrepareIndexDataForClearingIndex $prepareIndexDataForClearingIndex
*/
public function __construct(
ResourceConnection $resourceConnection,
@@ -72,7 +72,7 @@ public function __construct(
IndexStructureInterface $indexStructure,
IndexDataBySkuListProvider $indexDataBySkuListProvider,
SiblingSkuListInStockProvider $siblingSkuListInStockProvider,
- DefaultStockProviderInterface $defaultStockProvider
+ PrepareIndexDataForClearingIndex $prepareIndexDataForClearingIndex
) {
$this->resourceConnection = $resourceConnection;
$this->indexNameBuilder = $indexNameBuilder;
@@ -80,7 +80,7 @@ public function __construct(
$this->indexDataBySkuListProvider = $indexDataBySkuListProvider;
$this->indexStructure = $indexStructure;
$this->siblingSkuListInStockProvider = $siblingSkuListInStockProvider;
- $this->defaultStockProvider = $defaultStockProvider;
+ $this->prepareIndexDataForClearingIndex = $prepareIndexDataForClearingIndex;
}
/**
@@ -97,10 +97,6 @@ public function executeList(array $sourceItemIds): void
foreach ($skuListInStockList as $skuListInStock) {
$stockId = $skuListInStock->getStockId();
-
- if ($this->defaultStockProvider->getId() === $stockId) {
- continue;
- }
$skuList = $skuListInStock->getSkuList();
$mainIndexName = $this->indexNameBuilder
@@ -117,7 +113,7 @@ public function executeList(array $sourceItemIds): void
$this->indexHandler->cleanIndex(
$mainIndexName,
- $indexData,
+ $this->prepareIndexDataForClearingIndex->execute($indexData),
ResourceConnection::DEFAULT_CONNECTION
);
diff --git a/InventoryBundleProductIndexer/Indexer/Stock/IndexDataByStockIdProvider.php b/InventoryBundleProductIndexer/Indexer/Stock/IndexDataByStockIdProvider.php
index f2c35d9f2f72..26c25b92b4af 100644
--- a/InventoryBundleProductIndexer/Indexer/Stock/IndexDataByStockIdProvider.php
+++ b/InventoryBundleProductIndexer/Indexer/Stock/IndexDataByStockIdProvider.php
@@ -9,6 +9,7 @@
use Magento\Framework\App\ResourceConnection;
use Magento\InventoryBundleProductIndexer\Indexer\SelectBuilder;
+use Magento\InventorySalesApi\Api\AreProductsSalableInterface;
/**
* Bundle products for given stock provider.
@@ -25,21 +26,30 @@ class IndexDataByStockIdProvider
*/
private $resourceConnection;
+ /**
+ * @var AreProductsSalableInterface
+ */
+ private $areProductsSalable;
+
/**
* @param SelectBuilder $selectBuilder
* @param ResourceConnection $resourceConnection
+ * @param AreProductsSalableInterface $areProductsSalable
*/
- public function __construct(SelectBuilder $selectBuilder, ResourceConnection $resourceConnection)
- {
+ public function __construct(
+ SelectBuilder $selectBuilder,
+ ResourceConnection $resourceConnection,
+ AreProductsSalableInterface $areProductsSalable
+ ) {
$this->selectBuilder = $selectBuilder;
$this->resourceConnection = $resourceConnection;
+ $this->areProductsSalable = $areProductsSalable;
}
/**
* Get bundle products for given stock id.
*
* @param int $stockId
- *
* @return \ArrayIterator
* @throws \Exception
*/
@@ -47,7 +57,17 @@ public function execute(int $stockId): \ArrayIterator
{
$select = $this->selectBuilder->execute($stockId);
$connection = $this->resourceConnection->getConnection();
+ $results = $connection->fetchAll($select);
+ $bundleSkus = array_column($results, 'sku');
+ $salableResults = $this->areProductsSalable->execute($bundleSkus, $stockId);
+ foreach ($salableResults as $salableResult) {
+ foreach ($results as &$result) {
+ if ($salableResult->getSku() === $result['sku']) {
+ $result['is_salable'] = (string)(int)$salableResult->isSalable();
+ }
+ }
+ }
- return new \ArrayIterator($connection->fetchAll($select));
+ return new \ArrayIterator($results);
}
}
diff --git a/InventoryBundleProductIndexer/Indexer/StockIndexer.php b/InventoryBundleProductIndexer/Indexer/StockIndexer.php
index 9ffc7e3145e6..106aa2618c87 100644
--- a/InventoryBundleProductIndexer/Indexer/StockIndexer.php
+++ b/InventoryBundleProductIndexer/Indexer/StockIndexer.php
@@ -10,7 +10,6 @@
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\Exception\StateException;
use Magento\InventoryBundleProductIndexer\Indexer\Stock\IndexDataByStockIdProvider;
-use Magento\InventoryCatalogApi\Api\DefaultStockProviderInterface;
use Magento\InventoryIndexer\Indexer\InventoryIndexer;
use Magento\InventoryIndexer\Indexer\Stock\GetAllStockIds;
use Magento\InventoryIndexer\Indexer\Stock\PrepareIndexDataForClearingIndex;
@@ -55,11 +54,6 @@ class StockIndexer
*/
private $indexTableSwitcher;
- /**
- * @var DefaultStockProviderInterface
- */
- private $defaultStockProvider;
-
/**
* @var PrepareIndexDataForClearingIndex
*/
@@ -74,7 +68,6 @@ class StockIndexer
* @param IndexNameBuilder $indexNameBuilder
* @param IndexDataByStockIdProvider $indexDataByStockIdProvider
* @param IndexTableSwitcherInterface $indexTableSwitcher
- * @param DefaultStockProviderInterface $defaultStockProvider
* @param PrepareIndexDataForClearingIndex $prepareIndexDataForClearingIndex
*/
public function __construct(
@@ -84,7 +77,6 @@ public function __construct(
IndexNameBuilder $indexNameBuilder,
IndexDataByStockIdProvider $indexDataByStockIdProvider,
IndexTableSwitcherInterface $indexTableSwitcher,
- DefaultStockProviderInterface $defaultStockProvider,
PrepareIndexDataForClearingIndex $prepareIndexDataForClearingIndex
) {
$this->getAllStockIds = $getAllStockIds;
@@ -93,7 +85,6 @@ public function __construct(
$this->indexNameBuilder = $indexNameBuilder;
$this->indexDataByStockIdProvider = $indexDataByStockIdProvider;
$this->indexTableSwitcher = $indexTableSwitcher;
- $this->defaultStockProvider = $defaultStockProvider;
$this->prepareIndexDataForClearingIndex = $prepareIndexDataForClearingIndex;
}
@@ -131,10 +122,6 @@ public function executeRow(int $stockId)
public function executeList(array $stockIds)
{
foreach ($stockIds as $stockId) {
- if ($this->defaultStockProvider->getId() === $stockId) {
- continue;
- }
-
$mainIndexName = $this->indexNameBuilder
->setIndexId(InventoryIndexer::INDEXER_ID)
->addDimension('stock_', (string)$stockId)
diff --git a/InventoryBundleProductIndexer/Plugin/Catalog/Model/Product/ReindexSourceItemsPlugin.php b/InventoryBundleProductIndexer/Plugin/Catalog/Model/Product/ReindexSourceItemsPlugin.php
new file mode 100644
index 000000000000..dd1ca6267b7c
--- /dev/null
+++ b/InventoryBundleProductIndexer/Plugin/Catalog/Model/Product/ReindexSourceItemsPlugin.php
@@ -0,0 +1,134 @@
+getSourceItemsBySku = $getSourceItemsBySku;
+ $this->getSourceItemIds = $getSourceItemIds;
+ $this->sourceItemIndexer = $sourceItemIndexer;
+ $this->getSkusByProductIds = $getSkusByProductIds;
+ }
+
+ /**
+ * Reindex bundle source items after product save.
+ *
+ * @param Product $subject
+ * @param Product $result
+ * @return Product
+ * @throws LocalizedException
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function afterAfterSave(Product $subject, Product $result): Product
+ {
+ if ($result->getTypeId() !== Type::TYPE_CODE) {
+ return $result;
+ }
+ $skus = $this->getSelectionsSku($result);
+ $sourceItems = [[]];
+ foreach ($skus as $sku) {
+ $sourceItems[] = $this->getSourceItemsBySku->execute($sku);
+ }
+ $sourceItems = array_merge(...$sourceItems);
+ $sourceItemIds = $this->getSourceItemIds->execute($sourceItems);
+ $this->sourceItemIndexer->executeList($sourceItemIds);
+
+ return $result;
+ }
+
+ /**
+ * Retrieve bundle selections skus.
+ *
+ * @param Product $result
+ * @return array
+ */
+ private function getSelectionsSku(Product $result): array
+ {
+ $skus = $this->getSkusFromOptions($result);
+ if ($skus) {
+ return $skus;
+ }
+ $bundleSelectionsData = $result->getBundleSelectionsData() ?: [];
+ foreach ($bundleSelectionsData as $option) {
+ $skus[] = array_column($option, 'sku');
+ }
+ $skus = $skus ? array_merge(...$skus) : $skus;
+ if ($skus) {
+ return $skus;
+ }
+ $ids = [];
+ foreach ($bundleSelectionsData as $option) {
+ $ids[] = array_column($option, 'product_id');
+ }
+ $ids = $ids ? array_merge(...$ids) : $ids;
+
+ return $this->getSkusByProductIds->execute($ids);
+ }
+
+ /**
+ * Retrieve selection's skus from bundle product options.
+ *
+ * @param Product $result
+ * @return array
+ */
+ private function getSkusFromOptions(Product $result): array
+ {
+ $skus = [];
+ $options = $result->getExtensionAttributes()->getBundleProductOptions() ?: [];
+ foreach ($options as $option) {
+ $links = $option->getProductLinks();
+ foreach ($links as $link) {
+ $skus[] = $link->getSku();
+ }
+ }
+ return $skus;
+ }
+}
diff --git a/InventoryBundleProductIndexer/Plugin/InventoryIndexer/Model/Queue/GetDataForUpdate/AddBundleProductDataPlugin.php b/InventoryBundleProductIndexer/Plugin/InventoryIndexer/Model/Queue/GetDataForUpdate/AddBundleProductDataPlugin.php
new file mode 100644
index 000000000000..8271e3e786ef
--- /dev/null
+++ b/InventoryBundleProductIndexer/Plugin/InventoryIndexer/Model/Queue/GetDataForUpdate/AddBundleProductDataPlugin.php
@@ -0,0 +1,130 @@
+type = $type;
+ $this->getProductIdsBySkus = $getProductIdsBySkus;
+ $this->getSkusByProductIds = $getSkusByProductIds;
+ $this->getStockItemData = $getStockItemData;
+ $this->logger = $logger;
+ $this->areProductsSalable = $areProductsSalable;
+ }
+
+ /**
+ * Add bundle product data to index data.
+ *
+ * @param GetDataForUpdate $subject
+ * @param array $result
+ * @param array $salabilityData
+ * @param int $stockId
+ * @return array
+ * @throws NoSuchEntityException
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function afterExecute(GetDataForUpdate $subject, array $result, array $salabilityData, int $stockId): array
+ {
+ $bundleData = [];
+ $skus = array_keys($result);
+ $childrenIds = $this->getProductIdsBySkus->execute($skus);
+ $bundleProductsIds = [];
+ foreach ($childrenIds as $childId) {
+ $bundleProductsIds[] = $this->type->getParentIdsByChild($childId);
+ }
+ $bundleProductsIds = array_merge(...$bundleProductsIds);
+ $bundleSkus = $bundleProductsIds ? $this->getSkusByProductIds->execute($bundleProductsIds) : [];
+ $areBundleProdcutsSalable = $this->areProductsSalable->execute($bundleSkus, $stockId);
+ foreach ($areBundleProdcutsSalable as $salableResult) {
+ if ($salableResult->isSalable() !== $this->getIndexSalabilityStatus($salableResult->getSku(), $stockId)) {
+ $bundleData[$salableResult->getSku()] = $salableResult->isSalable();
+ }
+ }
+
+ return array_merge($result, $bundleData);
+ }
+
+ /**
+ * Get current index is_salable value.
+ *
+ * @param string $sku
+ * @param int $stockId
+ * @return bool|null
+ */
+ private function getIndexSalabilityStatus(string $sku, int $stockId): ?bool
+ {
+ try {
+ $data = $this->getStockItemData->execute($sku, $stockId);
+ $isSalable = $data ? (bool)$data[GetStockItemDataInterface::IS_SALABLE] : false;
+ } catch (LocalizedException $e) {
+ $this->logger->error($e->getLogMessage());
+ return null;
+ }
+
+ return $isSalable;
+ }
+}
diff --git a/InventoryBundleProductIndexer/composer.json b/InventoryBundleProductIndexer/composer.json
index 9650c2c35df5..f06ad139d1a6 100644
--- a/InventoryBundleProductIndexer/composer.json
+++ b/InventoryBundleProductIndexer/composer.json
@@ -9,7 +9,8 @@
"magento/module-inventory-api": "*",
"magento/module-inventory-catalog-api": "*",
"magento/module-inventory-indexer": "*",
- "magento/module-inventory-multi-dimensional-indexer-api": "*"
+ "magento/module-inventory-multi-dimensional-indexer-api": "*",
+ "magento/module-inventory-sales-api": "*"
},
"suggest": {
"magento/module-inventory": "*"
diff --git a/InventoryBundleProductIndexer/etc/di.xml b/InventoryBundleProductIndexer/etc/di.xml
index 73b7a0bf459e..1232eba41a50 100644
--- a/InventoryBundleProductIndexer/etc/di.xml
+++ b/InventoryBundleProductIndexer/etc/di.xml
@@ -25,9 +25,10 @@
Magento\InventoryIndexer\Indexer\IndexStructure
-
-
-
-
+
+
+
+
+
diff --git a/InventoryBundleProductIndexer/etc/webapi_rest/di.xml b/InventoryBundleProductIndexer/etc/webapi_rest/di.xml
new file mode 100644
index 000000000000..d8d2a041908c
--- /dev/null
+++ b/InventoryBundleProductIndexer/etc/webapi_rest/di.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
diff --git a/InventoryBundleProductIndexer/etc/webapi_soap/di.xml b/InventoryBundleProductIndexer/etc/webapi_soap/di.xml
new file mode 100644
index 000000000000..d8d2a041908c
--- /dev/null
+++ b/InventoryBundleProductIndexer/etc/webapi_soap/di.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
diff --git a/InventoryCache/etc/di.xml b/InventoryCache/etc/di.xml
index 1c422230af15..706222f90b8a 100644
--- a/InventoryCache/etc/di.xml
+++ b/InventoryCache/etc/di.xml
@@ -12,12 +12,12 @@
+
+
+
Magento\Catalog\Model\Product::CACHE_TAG
-
-
-
diff --git a/InventoryCatalog/Model/IsProductSalable.php b/InventoryCatalog/Model/IsProductSalable.php
new file mode 100644
index 000000000000..7499d66e4cb2
--- /dev/null
+++ b/InventoryCatalog/Model/IsProductSalable.php
@@ -0,0 +1,80 @@
+stockByWebsiteIdResolver = $stockByWebsiteIdResolver;
+ $this->areProductsSalable = $areProductsSalable;
+ $this->isProductSalableDataStorage = $isProductSalableDataStorage;
+ }
+
+ /**
+ * Verify product salable status.
+ *
+ * @param ProductInterface $product
+ * @return bool
+ */
+ public function execute(ProductInterface $product): bool
+ {
+ if (null === $product->getSku() ||
+ (null !== $product->getStatus() && (int)$product->getStatus() !== Status::STATUS_ENABLED)) {
+ return false;
+ }
+ if ($product->getData('is_salable') !== null) {
+ return (bool)$product->getData('is_salable');
+ }
+ $websiteId = (int)$product->getStore()->getWebsite()->getId();
+ $stockId = $this->stockByWebsiteIdResolver->execute($websiteId)->getStockId();
+ //use getData('sku') to get non processed product sku for complex products.
+ if (null !== $this->isProductSalableDataStorage->getIsSalable($product->getData('sku'), $stockId)) {
+ return $this->isProductSalableDataStorage->getIsSalable($product->getData('sku'), $stockId);
+ }
+
+ $result = current($this->areProductsSalable->execute([$product->getData('sku')], $stockId));
+ $salabilityStatus = $result->isSalable();
+ $this->isProductSalableDataStorage->setIsSalable($product->getData('sku'), $stockId, $salabilityStatus);
+
+ return $salabilityStatus;
+ }
+}
diff --git a/InventoryCatalog/Model/IsProductSalable/IsProductSalableDataStorage.php b/InventoryCatalog/Model/IsProductSalable/IsProductSalableDataStorage.php
new file mode 100644
index 000000000000..24e68b52045d
--- /dev/null
+++ b/InventoryCatalog/Model/IsProductSalable/IsProductSalableDataStorage.php
@@ -0,0 +1,66 @@
+statuses[$sku][$stockId] = $status;
+ }
+
+ /**
+ * Get is salable status for given product and stock.
+ *
+ * @param string $sku
+ * @param int $stockId
+ * @return bool|null
+ */
+ public function getIsSalable(string $sku, int $stockId): ?bool
+ {
+ return $this->statuses[$sku][$stockId] ?? null;
+ }
+
+ /**
+ * Clean is salable status for given product and stock.
+ *
+ * @param string $sku
+ * @param int $stockId
+ * @return void
+ */
+ public function removeIsSalable(string $sku, int $stockId): void
+ {
+ unset($this->statuses[$sku][$stockId]);
+ }
+
+ /**
+ * Clean is salable statuses for all saved products and stocks.
+ *
+ * @return void
+ */
+ public function cleanIsSalable(): void
+ {
+ $this->statuses = [];
+ }
+}
diff --git a/InventoryCatalog/Model/ResourceModel/AddStockDataToCollection.php b/InventoryCatalog/Model/ResourceModel/AddStockDataToCollection.php
index 4edaa61f88a4..38a4dad8b2b5 100644
--- a/InventoryCatalog/Model/ResourceModel/AddStockDataToCollection.php
+++ b/InventoryCatalog/Model/ResourceModel/AddStockDataToCollection.php
@@ -8,13 +8,11 @@
namespace Magento\InventoryCatalog\Model\ResourceModel;
use Magento\Catalog\Model\ResourceModel\Product\Collection;
-use Magento\Framework\App\ObjectManager;
-use Magento\InventoryCatalogApi\Api\DefaultStockProviderInterface;
use Magento\InventoryIndexer\Indexer\IndexStructure;
use Magento\InventoryIndexer\Model\StockIndexTableNameResolverInterface;
/**
- * Add Stock data to collection
+ * Add inventory stock data to collection resource.
*/
class AddStockDataToCollection
{
@@ -23,25 +21,18 @@ class AddStockDataToCollection
*/
private $stockIndexTableNameResolver;
- /**
- * @var DefaultStockProviderInterface
- */
- private $defaultStockProvider;
-
/**
* @param StockIndexTableNameResolverInterface $stockIndexTableNameResolver
- * @param DefaultStockProviderInterface $defaultStockProvider
*/
public function __construct(
- StockIndexTableNameResolverInterface $stockIndexTableNameResolver,
- DefaultStockProviderInterface $defaultStockProvider = null
+ StockIndexTableNameResolverInterface $stockIndexTableNameResolver
) {
$this->stockIndexTableNameResolver = $stockIndexTableNameResolver;
- $this->defaultStockProvider = $defaultStockProvider ?: ObjectManager::getInstance()
- ->get(DefaultStockProviderInterface::class);
}
/**
+ * Add inventory stock data for multi stock environment.
+ *
* @param Collection $collection
* @param bool $isFilterInStock
* @param int $stockId
@@ -49,31 +40,20 @@ public function __construct(
*/
public function execute(Collection $collection, bool $isFilterInStock, int $stockId)
{
- if ($stockId === $this->defaultStockProvider->getId()) {
- $isSalableColumnName = 'stock_status';
- $resource = $collection->getResource();
- $collection->getSelect()
- ->join(
- ['stock_status_index' => $resource->getTable('cataloginventory_stock_status')],
- sprintf('%s.entity_id = stock_status_index.product_id', Collection::MAIN_TABLE_ALIAS),
- [IndexStructure::IS_SALABLE => $isSalableColumnName]
- );
- } else {
- $stockIndexTableName = $this->stockIndexTableNameResolver->execute($stockId);
- $resource = $collection->getResource();
- $collection->getSelect()->join(
- ['product' => $resource->getTable('catalog_product_entity')],
- sprintf('product.entity_id = %s.entity_id', Collection::MAIN_TABLE_ALIAS),
- []
+ $stockIndexTableName = $this->stockIndexTableNameResolver->execute($stockId);
+ $resource = $collection->getResource();
+ $collection->getSelect()->join(
+ ['product' => $resource->getTable('catalog_product_entity')],
+ sprintf('product.entity_id = %s.entity_id', Collection::MAIN_TABLE_ALIAS),
+ []
+ );
+ $isSalableColumnName = IndexStructure::IS_SALABLE;
+ $collection->getSelect()
+ ->join(
+ ['stock_status_index' => $stockIndexTableName],
+ 'product.sku = stock_status_index.' . IndexStructure::SKU,
+ [$isSalableColumnName]
);
- $isSalableColumnName = IndexStructure::IS_SALABLE;
- $collection->getSelect()
- ->join(
- ['stock_status_index' => $stockIndexTableName],
- 'product.sku = stock_status_index.' . IndexStructure::SKU,
- [$isSalableColumnName]
- );
- }
if ($isFilterInStock) {
$collection->getSelect()
diff --git a/InventoryCatalog/Plugin/Catalog/Model/Product/GetIsSalablePlugin.php b/InventoryCatalog/Plugin/Catalog/Model/Product/GetIsSalablePlugin.php
new file mode 100644
index 000000000000..3b8f26cc9a6d
--- /dev/null
+++ b/InventoryCatalog/Plugin/Catalog/Model/Product/GetIsSalablePlugin.php
@@ -0,0 +1,47 @@
+isProductSalable = $isProductSalable;
+ }
+
+ /**
+ * Fetches is salable status for multi-stock environment.
+ *
+ * @param Product $product
+ * @param \Closure $proceed
+ * @return bool
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function aroundGetIsSalable(Product $product, \Closure $proceed): bool
+ {
+ if ($product->hasData('is_saleable')) {
+ return (bool)$product->getData('is_saleable');
+ }
+ return $this->isProductSalable->execute($product);
+ }
+}
diff --git a/InventoryCatalog/Plugin/Catalog/Model/Product/IsAvailablePlugin.php b/InventoryCatalog/Plugin/Catalog/Model/Product/IsAvailablePlugin.php
new file mode 100644
index 000000000000..92592182305f
--- /dev/null
+++ b/InventoryCatalog/Plugin/Catalog/Model/Product/IsAvailablePlugin.php
@@ -0,0 +1,44 @@
+isProductSalable = $isProductSalable;
+ }
+
+ /**
+ * Fetches is salable status from multi-stock.
+ *
+ * @param Product $product
+ * @param \Closure $proceed
+ * @return bool
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function aroundIsAvailable(Product $product, \Closure $proceed): bool
+ {
+ return $this->isProductSalable->execute($product);
+ }
+}
diff --git a/InventoryCatalog/Plugin/Catalog/Model/ProductLink/Search/FilterDisabledProductsPlugin.php b/InventoryCatalog/Plugin/Catalog/Model/ProductLink/Search/FilterDisabledProductsPlugin.php
new file mode 100644
index 000000000000..6d89a0ec6bdc
--- /dev/null
+++ b/InventoryCatalog/Plugin/Catalog/Model/ProductLink/Search/FilterDisabledProductsPlugin.php
@@ -0,0 +1,34 @@
+addAttributeToFilter(ProductInterface::STATUS, ['eq' => Status::STATUS_ENABLED]);
+
+ return $collection;
+ }
+}
diff --git a/InventoryCatalog/Plugin/CatalogInventory/Api/StockRegistry/AdaptGetProductStockStatusBySkuPlugin.php b/InventoryCatalog/Plugin/CatalogInventory/Api/StockRegistry/AdaptGetProductStockStatusBySkuPlugin.php
index 532db9d8ff58..772862bc7020 100644
--- a/InventoryCatalog/Plugin/CatalogInventory/Api/StockRegistry/AdaptGetProductStockStatusBySkuPlugin.php
+++ b/InventoryCatalog/Plugin/CatalogInventory/Api/StockRegistry/AdaptGetProductStockStatusBySkuPlugin.php
@@ -7,12 +7,10 @@
namespace Magento\InventoryCatalog\Plugin\CatalogInventory\Api\StockRegistry;
+use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\CatalogInventory\Api\StockRegistryInterface;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\NoSuchEntityException;
-use Magento\InventorySalesApi\Api\AreProductsSalableInterface;
-use Magento\InventorySalesApi\Api\Data\SalesChannelInterface;
-use Magento\InventorySalesApi\Api\StockResolverInterface;
use Magento\Store\Model\StoreManagerInterface;
/**
@@ -21,9 +19,9 @@
class AdaptGetProductStockStatusBySkuPlugin
{
/**
- * @var AreProductsSalableInterface
+ * @var ProductRepositoryInterface
*/
- private $areProductsSalable;
+ private $productRepository;
/**
* @var StoreManagerInterface
@@ -31,23 +29,15 @@ class AdaptGetProductStockStatusBySkuPlugin
private $storeManager;
/**
- * @var StockResolverInterface
- */
- private $stockResolver;
-
- /**
- * @param AreProductsSalableInterface $areProductsSalable
+ * @param ProductRepositoryInterface $productRepository
* @param StoreManagerInterface $storeManager
- * @param StockResolverInterface $stockResolver
*/
public function __construct(
- AreProductsSalableInterface $areProductsSalable,
- StoreManagerInterface $storeManager,
- StockResolverInterface $stockResolver
+ ProductRepositoryInterface $productRepository,
+ StoreManagerInterface $storeManager
) {
- $this->areProductsSalable = $areProductsSalable;
+ $this->productRepository = $productRepository;
$this->storeManager = $storeManager;
- $this->stockResolver = $stockResolver;
}
/**
@@ -58,8 +48,7 @@ public function __construct(
* @param string $productSku
* @param int $scopeId
* @return int
- * @throws LocalizedException
- * @throws NoSuchEntityException
+ * @throws NoSuchEntityException|LocalizedException
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function aroundGetProductStockStatusBySku(
@@ -68,14 +57,11 @@ public function aroundGetProductStockStatusBySku(
$productSku,
$scopeId = null
): int {
- $websiteCode = null === $scopeId
- ? $this->storeManager->getWebsite()->getCode()
- : $this->storeManager->getWebsite($scopeId)->getCode();
- $stockId = $this->stockResolver->execute(SalesChannelInterface::TYPE_WEBSITE, $websiteCode)->getStockId();
-
- $result = $this->areProductsSalable->execute([$productSku], $stockId);
- $result = current($result);
+ $store = $this->storeManager->getWebsite($scopeId)->getDefaultStore();
+ $product = $store
+ ? $this->productRepository->get($productSku, false, (int)$store->getId())
+ : $this->productRepository->get($productSku);
- return (int)$result->isSalable();
+ return (int)$product->isAvailable();
}
}
diff --git a/InventoryCatalog/Plugin/CatalogInventory/Api/StockRegistry/AdaptGetProductStockStatusPlugin.php b/InventoryCatalog/Plugin/CatalogInventory/Api/StockRegistry/AdaptGetProductStockStatusPlugin.php
index 59c3d9f291df..2cb7b077cf8f 100644
--- a/InventoryCatalog/Plugin/CatalogInventory/Api/StockRegistry/AdaptGetProductStockStatusPlugin.php
+++ b/InventoryCatalog/Plugin/CatalogInventory/Api/StockRegistry/AdaptGetProductStockStatusPlugin.php
@@ -7,13 +7,10 @@
namespace Magento\InventoryCatalog\Plugin\CatalogInventory\Api\StockRegistry;
+use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\CatalogInventory\Api\StockRegistryInterface;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\NoSuchEntityException;
-use Magento\InventoryCatalogApi\Model\GetSkusByProductIdsInterface;
-use Magento\InventorySalesApi\Api\AreProductsSalableInterface;
-use Magento\InventorySalesApi\Api\Data\SalesChannelInterface;
-use Magento\InventorySalesApi\Api\StockResolverInterface;
use Magento\Store\Model\StoreManagerInterface;
/**
@@ -22,14 +19,9 @@
class AdaptGetProductStockStatusPlugin
{
/**
- * @var AreProductsSalableInterface
+ * @var ProductRepositoryInterface
*/
- private $areProductsSalable;
-
- /**
- * @var GetSkusByProductIdsInterface
- */
- private $getSkusByProductIds;
+ private $productRepository;
/**
* @var StoreManagerInterface
@@ -37,26 +29,15 @@ class AdaptGetProductStockStatusPlugin
private $storeManager;
/**
- * @var StockResolverInterface
- */
- private $stockResolver;
-
- /**
- * @param AreProductsSalableInterface $areProductsSalable
- * @param GetSkusByProductIdsInterface $getSkusByProductIds
+ * @param ProductRepositoryInterface $productRepository
* @param StoreManagerInterface $storeManager
- * @param StockResolverInterface $stockResolver
*/
public function __construct(
- AreProductsSalableInterface $areProductsSalable,
- GetSkusByProductIdsInterface $getSkusByProductIds,
- StoreManagerInterface $storeManager,
- StockResolverInterface $stockResolver
+ ProductRepositoryInterface $productRepository,
+ StoreManagerInterface $storeManager
) {
- $this->areProductsSalable = $areProductsSalable;
- $this->getSkusByProductIds = $getSkusByProductIds;
+ $this->productRepository = $productRepository;
$this->storeManager = $storeManager;
- $this->stockResolver = $stockResolver;
}
/**
@@ -67,9 +48,8 @@ public function __construct(
* @param int $productId
* @param int $scopeId
* @return int
- * @throws LocalizedException
- * @throws NoSuchEntityException
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ * @throws NoSuchEntityException|LocalizedException
*/
public function aroundGetProductStockStatus(
StockRegistryInterface $subject,
@@ -77,15 +57,11 @@ public function aroundGetProductStockStatus(
$productId,
$scopeId = null
): int {
- $websiteCode = null === $scopeId
- ? $this->storeManager->getWebsite()->getCode()
- : $this->storeManager->getWebsite($scopeId)->getCode();
- $stockId = $this->stockResolver->execute(SalesChannelInterface::TYPE_WEBSITE, $websiteCode)->getStockId();
- $sku = $this->getSkusByProductIds->execute([$productId])[$productId];
-
- $result = $this->areProductsSalable->execute([$sku], $stockId);
- $result = current($result);
+ $store = $this->storeManager->getWebsite($scopeId)->getDefaultStore();
+ $product = $store
+ ? $this->productRepository->getById($productId, false, (int)$store->getId())
+ : $this->productRepository->getById($productId);
- return (int)$result->isSalable();
+ return (int)$product->isAvailable();
}
}
diff --git a/InventoryCatalog/Plugin/CatalogInventory/Api/StockRegistry/AdaptGetStockStatusBySkuPlugin.php b/InventoryCatalog/Plugin/CatalogInventory/Api/StockRegistry/AdaptGetStockStatusBySkuPlugin.php
index 60929a190f36..ee77e8049356 100644
--- a/InventoryCatalog/Plugin/CatalogInventory/Api/StockRegistry/AdaptGetStockStatusBySkuPlugin.php
+++ b/InventoryCatalog/Plugin/CatalogInventory/Api/StockRegistry/AdaptGetStockStatusBySkuPlugin.php
@@ -7,12 +7,12 @@
namespace Magento\InventoryCatalog\Plugin\CatalogInventory\Api\StockRegistry;
+use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\CatalogInventory\Api\Data\StockStatusInterface;
use Magento\CatalogInventory\Api\StockRegistryInterface;
use Magento\Framework\Exception\InputException;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\NoSuchEntityException;
-use Magento\InventorySalesApi\Api\AreProductsSalableInterface;
use Magento\InventorySalesApi\Api\Data\SalesChannelInterface;
use Magento\InventorySalesApi\Api\GetProductSalableQtyInterface;
use Magento\InventorySalesApi\Api\StockResolverInterface;
@@ -23,11 +23,6 @@
*/
class AdaptGetStockStatusBySkuPlugin
{
- /**
- * @var AreProductsSalableInterface
- */
- private $areProductsSalable;
-
/**
* @var GetProductSalableQtyInterface
*/
@@ -44,21 +39,26 @@ class AdaptGetStockStatusBySkuPlugin
private $stockResolver;
/**
- * @param AreProductsSalableInterface $areProductsSalable
+ * @var ProductRepositoryInterface
+ */
+ private $productRepository;
+
+ /**
* @param GetProductSalableQtyInterface $getProductSalableQty
* @param StoreManagerInterface $storeManager
* @param StockResolverInterface $stockResolver
+ * @param ProductRepositoryInterface $productRepository
*/
public function __construct(
- AreProductsSalableInterface $areProductsSalable,
GetProductSalableQtyInterface $getProductSalableQty,
StoreManagerInterface $storeManager,
- StockResolverInterface $stockResolver
+ StockResolverInterface $stockResolver,
+ ProductRepositoryInterface $productRepository
) {
- $this->areProductsSalable = $areProductsSalable;
$this->getProductSalableQty = $getProductSalableQty;
$this->storeManager = $storeManager;
$this->stockResolver = $stockResolver;
+ $this->productRepository = $productRepository;
}
/**
@@ -79,22 +79,20 @@ public function afterGetStockStatusBySku(
$productSku,
$scopeId = null
): StockStatusInterface {
- $websiteCode = null === $scopeId
- ? $this->storeManager->getWebsite()->getCode()
- : $this->storeManager->getWebsite($scopeId)->getCode();
- $stockId = $this->stockResolver->execute(SalesChannelInterface::TYPE_WEBSITE, $websiteCode)->getStockId();
-
- $result = $this->areProductsSalable->execute([$productSku], $stockId);
- $result = current($result);
-
+ $website = $this->storeManager->getWebsite($scopeId);
+ $stockId = $this->stockResolver->execute(SalesChannelInterface::TYPE_WEBSITE, $website->getCode())
+ ->getStockId();
try {
$qty = $this->getProductSalableQty->execute($productSku, $stockId);
} catch (InputException $e) {
$qty = 0;
}
-
- $stockStatus->setStockStatus((int)$result->isSalable());
+ $product = $website->getDefaultStore()
+ ? $this->productRepository->get($productSku, false, (int)$website->getDefaultStore()->getId())
+ : $this->productRepository->get($productSku);
+ $stockStatus->setStockStatus((int)$product->isAvailable());
$stockStatus->setQty($qty);
+
return $stockStatus;
}
}
diff --git a/InventoryCatalog/Plugin/CatalogInventory/Api/StockRegistry/AdaptGetStockStatusPlugin.php b/InventoryCatalog/Plugin/CatalogInventory/Api/StockRegistry/AdaptGetStockStatusPlugin.php
index 6593389973a0..d01dcfdee51a 100644
--- a/InventoryCatalog/Plugin/CatalogInventory/Api/StockRegistry/AdaptGetStockStatusPlugin.php
+++ b/InventoryCatalog/Plugin/CatalogInventory/Api/StockRegistry/AdaptGetStockStatusPlugin.php
@@ -7,13 +7,13 @@
namespace Magento\InventoryCatalog\Plugin\CatalogInventory\Api\StockRegistry;
+use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\CatalogInventory\Api\Data\StockStatusInterface;
use Magento\CatalogInventory\Api\StockRegistryInterface;
use Magento\Framework\Exception\InputException;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\InventoryCatalogApi\Model\GetSkusByProductIdsInterface;
-use Magento\InventorySalesApi\Api\AreProductsSalableInterface;
use Magento\InventorySalesApi\Api\Data\SalesChannelInterface;
use Magento\InventorySalesApi\Api\GetProductSalableQtyInterface;
use Magento\InventorySalesApi\Api\StockResolverInterface;
@@ -24,11 +24,6 @@
*/
class AdaptGetStockStatusPlugin
{
- /**
- * @var AreProductsSalableInterface
- */
- private $areProductsSalable;
-
/**
* @var GetProductSalableQtyInterface
*/
@@ -50,24 +45,29 @@ class AdaptGetStockStatusPlugin
private $stockResolver;
/**
- * @param AreProductsSalableInterface $areProductsSalable
+ * @var ProductRepositoryInterface
+ */
+ private $productRepository;
+
+ /**
* @param GetProductSalableQtyInterface $getProductSalableQty
* @param GetSkusByProductIdsInterface $getSkusByProductIds
* @param StoreManagerInterface $storeManager
* @param StockResolverInterface $stockResolver
+ * @param ProductRepositoryInterface $productRepository
*/
public function __construct(
- AreProductsSalableInterface $areProductsSalable,
GetProductSalableQtyInterface $getProductSalableQty,
GetSkusByProductIdsInterface $getSkusByProductIds,
StoreManagerInterface $storeManager,
- StockResolverInterface $stockResolver
+ StockResolverInterface $stockResolver,
+ ProductRepositoryInterface $productRepository
) {
- $this->areProductsSalable = $areProductsSalable;
$this->getProductSalableQty = $getProductSalableQty;
$this->getSkusByProductIds = $getSkusByProductIds;
$this->storeManager = $storeManager;
$this->stockResolver = $stockResolver;
+ $this->productRepository = $productRepository;
}
/**
@@ -88,14 +88,13 @@ public function afterGetStockStatus(
$productId,
$scopeId = null
): StockStatusInterface {
- $websiteCode = null === $scopeId
- ? $this->storeManager->getWebsite()->getCode()
- : $this->storeManager->getWebsite($scopeId)->getCode();
- $stockId = $this->stockResolver->execute(SalesChannelInterface::TYPE_WEBSITE, $websiteCode)->getStockId();
+ $website = $this->storeManager->getWebsite($scopeId);
+ $stockId = $this->stockResolver->execute(SalesChannelInterface::TYPE_WEBSITE, $website->getCode())
+ ->getStockId();
$sku = $this->getSkusByProductIds->execute([$productId])[$productId];
-
- $result = $this->areProductsSalable->execute([$sku], $stockId);
- $result = current($result);
+ $product = $website->getCurrentStore() === null
+ ? $this->productRepository->get($sku)
+ : $this->productRepository->get($sku, false, (int)$website->getDefaultStore()->getId());
try {
$qty = $this->getProductSalableQty->execute($sku, $stockId);
@@ -103,8 +102,9 @@ public function afterGetStockStatus(
$qty = 0;
}
- $stockStatus->setStockStatus((int)$result->isSalable());
+ $stockStatus->setStockStatus((int)$product->isAvailable());
$stockStatus->setQty($qty);
+
return $stockStatus;
}
}
diff --git a/InventoryCatalog/Plugin/CatalogInventory/Helper/Stock/AdaptAssignStatusToProductPlugin.php b/InventoryCatalog/Plugin/CatalogInventory/Helper/Stock/AdaptAssignStatusToProductPlugin.php
deleted file mode 100644
index fe79eefdd1a3..000000000000
--- a/InventoryCatalog/Plugin/CatalogInventory/Helper/Stock/AdaptAssignStatusToProductPlugin.php
+++ /dev/null
@@ -1,93 +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
- ): array {
- if (null === $product->getSku()) {
- return [$product, $status];
- }
-
- try {
- $this->getProductIdsBySkus->execute([$product->getSku()]);
- if (null === $status) {
- $stockId = $this->getStockIdForCurrentWebsite->execute();
- $result = $this->areProductsSalable->execute([$product->getSku()], $stockId);
- $result = current($result);
- return [$product, (int)$result->isSalable()];
- }
- } catch (NoSuchEntityException $e) {
- return [$product, $status];
- }
- return [$product, $status];
- }
-}
diff --git a/InventoryCatalog/Plugin/CatalogInventory/Model/ResourceModel/Stock/Status/AdaptAddIsInStockFilterToCollectionPlugin.php b/InventoryCatalog/Plugin/CatalogInventory/Model/ResourceModel/Stock/Status/AdaptAddIsInStockFilterToCollectionPlugin.php
index ae86cb2646fe..397b068ddf36 100644
--- a/InventoryCatalog/Plugin/CatalogInventory/Model/ResourceModel/Stock/Status/AdaptAddIsInStockFilterToCollectionPlugin.php
+++ b/InventoryCatalog/Plugin/CatalogInventory/Model/ResourceModel/Stock/Status/AdaptAddIsInStockFilterToCollectionPlugin.php
@@ -11,7 +11,6 @@
use Magento\CatalogInventory\Model\ResourceModel\Stock\Status;
use Magento\InventoryCatalog\Model\GetStockIdForCurrentWebsite;
use Magento\InventoryCatalog\Model\ResourceModel\AddIsInStockFilterToCollection;
-use Magento\InventoryCatalogApi\Api\DefaultStockProviderInterface;
/**
* Adapt adding is in stock filter to collection for multi stocks.
@@ -31,16 +30,13 @@ class AdaptAddIsInStockFilterToCollectionPlugin
/**
* @param GetStockIdForCurrentWebsite $getStockIdForCurrentWebsite
* @param AddIsInStockFilterToCollection $addIsInStockFilterToCollection
- * @param DefaultStockProviderInterface $defaultStockProvider
*/
public function __construct(
GetStockIdForCurrentWebsite $getStockIdForCurrentWebsite,
- AddIsInStockFilterToCollection $addIsInStockFilterToCollection,
- DefaultStockProviderInterface $defaultStockProvider
+ AddIsInStockFilterToCollection $addIsInStockFilterToCollection
) {
$this->getStockIdForCurrentWebsite = $getStockIdForCurrentWebsite;
$this->addIsInStockFilterToCollection = $addIsInStockFilterToCollection;
- $this->defaultStockProvider = $defaultStockProvider;
}
/**
@@ -59,11 +55,7 @@ public function aroundAddIsInStockFilterToCollection(
$collection
) {
$stockId = $this->getStockIdForCurrentWebsite->execute();
- if ($this->defaultStockProvider->getId() === $stockId) {
- return $proceed($collection);
- } else {
- $this->addIsInStockFilterToCollection->execute($collection, $stockId);
- }
+ $this->addIsInStockFilterToCollection->execute($collection, $stockId);
return $stockStatus;
}
diff --git a/InventoryCatalog/Plugin/CatalogInventory/Model/ResourceModel/Stock/Status/AdaptAddStockDataToCollectionPlugin.php b/InventoryCatalog/Plugin/CatalogInventory/Model/ResourceModel/Stock/Status/AdaptAddStockDataToCollectionPlugin.php
index 342c4adb0d25..0e4fb9d485b0 100644
--- a/InventoryCatalog/Plugin/CatalogInventory/Model/ResourceModel/Stock/Status/AdaptAddStockDataToCollectionPlugin.php
+++ b/InventoryCatalog/Plugin/CatalogInventory/Model/ResourceModel/Stock/Status/AdaptAddStockDataToCollectionPlugin.php
@@ -40,6 +40,8 @@ public function __construct(
}
/**
+ * Adapt add stock data for collection for multi stock environment.
+ *
* @param Status $stockStatus
* @param callable $proceed
* @param Collection $collection
diff --git a/InventoryCatalog/Plugin/CatalogInventory/Model/ResourceModel/Stock/Status/AdaptAddStockStatusToSelectPlugin.php b/InventoryCatalog/Plugin/CatalogInventory/Model/ResourceModel/Stock/Status/AdaptAddStockStatusToSelectPlugin.php
index 2c051c39de77..99bac2255083 100644
--- a/InventoryCatalog/Plugin/CatalogInventory/Model/ResourceModel/Stock/Status/AdaptAddStockStatusToSelectPlugin.php
+++ b/InventoryCatalog/Plugin/CatalogInventory/Model/ResourceModel/Stock/Status/AdaptAddStockStatusToSelectPlugin.php
@@ -10,8 +10,8 @@
use Magento\CatalogInventory\Model\ResourceModel\Stock\Status;
use Magento\Framework\DB\Select;
use Magento\Framework\Exception\LocalizedException;
+use Magento\Framework\Exception\NoSuchEntityException;
use Magento\InventoryCatalog\Model\ResourceModel\AddStockStatusToSelect;
-use Magento\InventoryCatalogApi\Api\DefaultStockProviderInterface;
use Magento\InventorySalesApi\Api\Data\SalesChannelInterface;
use Magento\InventorySalesApi\Api\StockResolverInterface;
use Magento\Store\Model\Website;
@@ -31,24 +31,16 @@ class AdaptAddStockStatusToSelectPlugin
*/
private $addStockStatusToSelect;
- /**
- * @var DefaultStockProviderInterface
- */
- private $defaultStockProvider;
-
/**
* @param StockResolverInterface $stockResolver
* @param AddStockStatusToSelect $addStockStatusToSelect
- * @param DefaultStockProviderInterface $defaultStockProvider
*/
public function __construct(
StockResolverInterface $stockResolver,
- AddStockStatusToSelect $addStockStatusToSelect,
- DefaultStockProviderInterface $defaultStockProvider
+ AddStockStatusToSelect $addStockStatusToSelect
) {
$this->stockResolver = $stockResolver;
$this->addStockStatusToSelect = $addStockStatusToSelect;
- $this->defaultStockProvider = $defaultStockProvider;
}
/**
@@ -60,6 +52,7 @@ public function __construct(
* @param Website $website
* @return Status
* @throws LocalizedException
+ * @throws NoSuchEntityException
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function aroundAddStockStatusToSelect(
@@ -75,11 +68,7 @@ public function aroundAddStockStatusToSelect(
$stock = $this->stockResolver->execute(SalesChannelInterface::TYPE_WEBSITE, $websiteCode);
$stockId = (int)$stock->getStockId();
- if ($this->defaultStockProvider->getId() === $stockId) {
- return $proceed($select, $website);
- } else {
- $this->addStockStatusToSelect->execute($select, $stockId);
- }
+ $this->addStockStatusToSelect->execute($select, $stockId);
return $stockStatus;
}
diff --git a/InventoryCatalog/Plugin/CatalogInventory/Model/StockRegistryStorage/CleanIsSalableDataStoragePlugin.php b/InventoryCatalog/Plugin/CatalogInventory/Model/StockRegistryStorage/CleanIsSalableDataStoragePlugin.php
new file mode 100644
index 000000000000..0a86d2db21f9
--- /dev/null
+++ b/InventoryCatalog/Plugin/CatalogInventory/Model/StockRegistryStorage/CleanIsSalableDataStoragePlugin.php
@@ -0,0 +1,42 @@
+isProductSalableDataStorage = $isProductSalableDataStorage;
+ }
+
+ /**
+ * Clean is product salable storage after stock registry storage has been cleaned.
+ *
+ * @param StockRegistryStorage $subject
+ * @param void $result
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function afterClean(StockRegistryStorage $subject, $result): void
+ {
+ $this->isProductSalableDataStorage->cleanIsSalable();
+ }
+}
diff --git a/InventoryCatalog/Plugin/InventoryIndexer/Indexer/Stock/Strategy/Sync/PriceIndexUpdatePlugin.php b/InventoryCatalog/Plugin/InventoryIndexer/Indexer/Stock/Strategy/Sync/PriceIndexUpdatePlugin.php
index 77e2f3e12a23..62d942bc511a 100644
--- a/InventoryCatalog/Plugin/InventoryIndexer/Indexer/Stock/Strategy/Sync/PriceIndexUpdatePlugin.php
+++ b/InventoryCatalog/Plugin/InventoryIndexer/Indexer/Stock/Strategy/Sync/PriceIndexUpdatePlugin.php
@@ -47,7 +47,7 @@ public function __construct(
* @return void
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
- public function afterExecuteList(Sync $subject, $result, array $stockIds)
+ public function afterExecuteList(Sync $subject, $result, array $stockIds): void
{
$productIds = $this->getProductIdsByStockIds->execute($stockIds);
if (!empty($productIds)) {
diff --git a/InventoryCatalog/Setup/Operation/ReindexDefaultStock.php b/InventoryCatalog/Setup/Operation/ReindexDefaultStock.php
index f4309ccce90b..95d2abdb7a4e 100644
--- a/InventoryCatalog/Setup/Operation/ReindexDefaultStock.php
+++ b/InventoryCatalog/Setup/Operation/ReindexDefaultStock.php
@@ -7,11 +7,17 @@
namespace Magento\InventoryCatalog\Setup\Operation;
-use Magento\InventoryIndexer\Indexer\Stock\StockIndexer;
+use Magento\Framework\App\ResourceConnection;
+use Magento\Framework\Exception\StateException;
use Magento\InventoryCatalogApi\Api\DefaultStockProviderInterface;
+use Magento\InventoryIndexer\Indexer\InventoryIndexer;
+use Magento\InventoryIndexer\Indexer\Stock\StockIndexer;
+use Magento\InventoryMultiDimensionalIndexerApi\Model\Alias;
+use Magento\InventoryMultiDimensionalIndexerApi\Model\IndexNameBuilder;
+use Magento\InventoryMultiDimensionalIndexerApi\Model\IndexStructureInterface;
/**
- * CReindex default stock during installation
+ * Create default stock index during installation.
*/
class ReindexDefaultStock
{
@@ -25,25 +31,52 @@ class ReindexDefaultStock
*/
private $stockIndexer;
+ /**
+ * @var IndexNameBuilder
+ */
+ private $indexNameBuilder;
+
+ /**
+ * @var IndexStructureInterface
+ */
+ private $indexStructure;
+
/**
* @param DefaultStockProviderInterface $defaultStockProvider
* @param StockIndexer $stockIndexer
+ * @param IndexNameBuilder $indexNameBuilder
+ * @param IndexStructureInterface $indexStructure
*/
public function __construct(
DefaultStockProviderInterface $defaultStockProvider,
- StockIndexer $stockIndexer
+ StockIndexer $stockIndexer,
+ IndexNameBuilder $indexNameBuilder,
+ IndexStructureInterface $indexStructure
) {
$this->defaultStockProvider = $defaultStockProvider;
$this->stockIndexer = $stockIndexer;
+ $this->indexNameBuilder = $indexNameBuilder;
+ $this->indexStructure = $indexStructure;
}
/**
- * Create default stock
+ * Create default stock.
*
* @return void
+ * @throws StateException
*/
public function execute()
{
+ $stockId = $this->defaultStockProvider->getId();
+ $mainIndexName = $this->indexNameBuilder
+ ->setIndexId(InventoryIndexer::INDEXER_ID)
+ ->addDimension('stock_', (string)$stockId)
+ ->setAlias(Alias::ALIAS_MAIN)
+ ->build();
+
+ if (!$this->indexStructure->isExist($mainIndexName, ResourceConnection::DEFAULT_CONNECTION)) {
+ $this->indexStructure->create($mainIndexName, ResourceConnection::DEFAULT_CONNECTION);
+ }
//$this->stockIndexer->executeRow($this->defaultStockProvider->getId());
}
}
diff --git a/InventoryCatalog/Setup/Patch/Schema/CreateLegacyStockStatusView.php b/InventoryCatalog/Setup/Patch/Schema/CreateLegacyStockStatusView.php
deleted file mode 100644
index eec200ce0b00..000000000000
--- a/InventoryCatalog/Setup/Patch/Schema/CreateLegacyStockStatusView.php
+++ /dev/null
@@ -1,96 +0,0 @@
-schemaSetup = $schemaSetup;
- $this->stockIndexTableNameResolver = $stockIndexTableNameResolver;
- $this->defaultStockProvider = $defaultStockProvider;
- }
-
- /**
- * @inheritdoc
- */
- public function getAliases()
- {
- return [];
- }
-
- /**
- * @inheritdoc
- */
- public function apply()
- {
- $this->schemaSetup->startSetup();
- $defaultStockId = $this->defaultStockProvider->getId();
- $viewToLegacyIndex = $this->stockIndexTableNameResolver->execute($defaultStockId);
- $legacyStockStatusTable = $this->schemaSetup->getTable('cataloginventory_stock_status');
- $productTable = $this->schemaSetup->getTable('catalog_product_entity');
- $sql = "CREATE
- SQL SECURITY INVOKER
- VIEW {$viewToLegacyIndex}
- AS
- SELECT
- DISTINCT
- legacy_stock_status.product_id,
- legacy_stock_status.website_id,
- legacy_stock_status.stock_id,
- legacy_stock_status.qty quantity,
- legacy_stock_status.stock_status is_salable,
- product.sku
- FROM {$legacyStockStatusTable} legacy_stock_status
- INNER JOIN {$productTable} product
- ON legacy_stock_status.product_id = product.entity_id;";
- $this->schemaSetup->getConnection()->query($sql);
- $this->schemaSetup->endSetup();
-
- return $this;
- }
-
- /**
- * @inheritdoc
- */
- public static function getDependencies()
- {
- return [];
- }
-}
diff --git a/InventoryCatalog/Test/Integration/Bulk/InventoryTransferTest.php b/InventoryCatalog/Test/Integration/Bulk/InventoryTransferTest.php
index 0a7a93a5fdde..f607274c7478 100644
--- a/InventoryCatalog/Test/Integration/Bulk/InventoryTransferTest.php
+++ b/InventoryCatalog/Test/Integration/Bulk/InventoryTransferTest.php
@@ -15,6 +15,9 @@
use Magento\TestFramework\Helper\Bootstrap;
use PHPUnit\Framework\TestCase;
+/**
+ * @magentoDbIsolation disabled
+ */
class InventoryTransferTest extends TestCase
{
/**
@@ -89,7 +92,6 @@ private function getSourceItem(string $sku, string $sourceCode): ?SourceItemInte
* @magentoDataFixture Magento_InventoryApi::Test/_files/sources.php
* @magentoDataFixture Magento_InventoryApi::Test/_files/products.php
* @magentoDataFixture Magento_InventoryApi::Test/_files/source_items.php
- * @magentoDbIsolation enabled
*/
public function testBulkInventoryTransferAndUnassign()
{
@@ -116,7 +118,6 @@ public function testBulkInventoryTransferAndUnassign()
* @magentoDataFixture Magento_InventoryApi::Test/_files/sources.php
* @magentoDataFixture Magento_InventoryApi::Test/_files/products.php
* @magentoDataFixture Magento_InventoryApi::Test/_files/source_items.php
- * @magentoDbIsolation enabled
*/
public function testBulkInventoryTransferWithOutOfStockOrigin()
{
@@ -150,7 +151,6 @@ public function testBulkInventoryTransferWithOutOfStockOrigin()
* @magentoDataFixture Magento_InventoryApi::Test/_files/sources.php
* @magentoDataFixture Magento_InventoryApi::Test/_files/products.php
* @magentoDataFixture Magento_InventoryApi::Test/_files/source_items.php
- * @magentoDbIsolation enabled
*/
public function testBulkInventoryTransferToNewSource()
{
@@ -190,7 +190,6 @@ public function testBulkInventoryTransferToNewSource()
* @magentoDataFixture Magento_InventoryApi::Test/_files/sources.php
* @magentoDataFixture Magento_InventoryApi::Test/_files/products.php
* @magentoDataFixture Magento_InventoryApi::Test/_files/source_items.php
- * @magentoDbIsolation enabled
*/
public function testBulkInventoryTransferFromUnassignedOriginSource()
{
@@ -224,7 +223,6 @@ public function testBulkInventoryTransferFromUnassignedOriginSource()
* @magentoDataFixture Magento_InventoryApi::Test/_files/sources.php
* @magentoDataFixture Magento_InventoryApi::Test/_files/products.php
* @magentoDataFixture Magento_InventoryApi::Test/_files/source_items.php
- * @magentoDbIsolation enabled
*/
public function testBulkInventoryTransferToAssignedSource()
{
diff --git a/InventoryCatalog/Test/Integration/Bulk/SourceAssignTest.php b/InventoryCatalog/Test/Integration/Bulk/SourceAssignTest.php
index 7104720ffad5..37ddec25953c 100644
--- a/InventoryCatalog/Test/Integration/Bulk/SourceAssignTest.php
+++ b/InventoryCatalog/Test/Integration/Bulk/SourceAssignTest.php
@@ -62,7 +62,7 @@ private function getSourceItemCodesBySku(string $sku): array
* @magentoDataFixture Magento_InventoryApi::Test/_files/sources.php
* @magentoDataFixture Magento_InventoryApi::Test/_files/products.php
* @magentoDataFixture Magento_InventoryCatalog::Test/_files/source_items_on_default_source.php
- * @magentoDbIsolation enabled
+ * @magentoDbIsolation disabled
*/
public function testBulkSourceAssignment()
{
@@ -121,7 +121,7 @@ public function testBulkSourceAssignment()
/**
* @magentoDataFixture Magento_InventoryApi::Test/_files/sources.php
* @magentoDataFixture Magento_InventoryCatalog::Test/_files/products_all_types.php
- * @magentoDbIsolation enabled
+ * @magentoDbIsolation disabled
*/
public function testBulkSourceAssignmentOnMixedProducts()
{
diff --git a/InventoryCatalog/Test/Integration/Bulk/SourceUnassignTest.php b/InventoryCatalog/Test/Integration/Bulk/SourceUnassignTest.php
index 36367e33ee41..a98c9b6d55c0 100644
--- a/InventoryCatalog/Test/Integration/Bulk/SourceUnassignTest.php
+++ b/InventoryCatalog/Test/Integration/Bulk/SourceUnassignTest.php
@@ -62,7 +62,7 @@ private function getSourceItemCodesBySku(string $sku): array
* @magentoDataFixture Magento_InventoryApi::Test/_files/sources.php
* @magentoDataFixture Magento_InventoryApi::Test/_files/products.php
* @magentoDataFixture Magento_InventoryApi::Test/_files/source_items.php
- * @magentoDbIsolation enabled
+ * @magentoDbIsolation disabled
*/
public function testBulkSourceUnassignment()
{
diff --git a/InventoryCatalog/Test/Integration/CatalogInventory/Api/StockRegistry/GetProductStockStatusBySkuOnDefaultStockTest.php b/InventoryCatalog/Test/Integration/CatalogInventory/Api/StockRegistry/GetProductStockStatusBySkuOnDefaultStockTest.php
index d156512abc94..b813f7e61c1e 100644
--- a/InventoryCatalog/Test/Integration/CatalogInventory/Api/StockRegistry/GetProductStockStatusBySkuOnDefaultStockTest.php
+++ b/InventoryCatalog/Test/Integration/CatalogInventory/Api/StockRegistry/GetProductStockStatusBySkuOnDefaultStockTest.php
@@ -12,6 +12,9 @@
use Magento\TestFramework\Helper\Bootstrap;
use PHPUnit\Framework\TestCase;
+/**
+ * @magentoDbIsolation disabled
+ */
class GetProductStockStatusBySkuOnDefaultStockTest extends TestCase
{
/**
diff --git a/InventoryCatalog/Test/Integration/CatalogInventory/Api/StockRegistry/GetProductStockStatusOnDefaultStockTest.php b/InventoryCatalog/Test/Integration/CatalogInventory/Api/StockRegistry/GetProductStockStatusOnDefaultStockTest.php
index 5d4fc18323d2..3f3ff44d7f8a 100644
--- a/InventoryCatalog/Test/Integration/CatalogInventory/Api/StockRegistry/GetProductStockStatusOnDefaultStockTest.php
+++ b/InventoryCatalog/Test/Integration/CatalogInventory/Api/StockRegistry/GetProductStockStatusOnDefaultStockTest.php
@@ -13,6 +13,9 @@
use Magento\TestFramework\Helper\Bootstrap;
use PHPUnit\Framework\TestCase;
+/**
+ * @magentoDbIsolation disabled
+ */
class GetProductStockStatusOnDefaultStockTest extends TestCase
{
/**
diff --git a/InventoryCatalog/Test/Integration/CatalogInventory/Api/StockRegistry/GetStockStatusBySkuOnDefaultStockTest.php b/InventoryCatalog/Test/Integration/CatalogInventory/Api/StockRegistry/GetStockStatusBySkuOnDefaultStockTest.php
index bbe8aa01d9af..c958de766e88 100644
--- a/InventoryCatalog/Test/Integration/CatalogInventory/Api/StockRegistry/GetStockStatusBySkuOnDefaultStockTest.php
+++ b/InventoryCatalog/Test/Integration/CatalogInventory/Api/StockRegistry/GetStockStatusBySkuOnDefaultStockTest.php
@@ -13,6 +13,9 @@
use Magento\TestFramework\Helper\Bootstrap;
use PHPUnit\Framework\TestCase;
+/**
+ * @magentoDbIsolation disabled
+ */
class GetStockStatusBySkuOnDefaultStockTest extends TestCase
{
/**
diff --git a/InventoryCatalog/Test/Integration/CatalogInventory/Api/StockRegistry/GetStockStatusOnDefaultStockTest.php b/InventoryCatalog/Test/Integration/CatalogInventory/Api/StockRegistry/GetStockStatusOnDefaultStockTest.php
index 6c444988daa2..066c7f080e71 100644
--- a/InventoryCatalog/Test/Integration/CatalogInventory/Api/StockRegistry/GetStockStatusOnDefaultStockTest.php
+++ b/InventoryCatalog/Test/Integration/CatalogInventory/Api/StockRegistry/GetStockStatusOnDefaultStockTest.php
@@ -13,6 +13,9 @@
use Magento\TestFramework\Helper\Bootstrap;
use PHPUnit\Framework\TestCase;
+/**
+ * @magentoDbIsolation disabled
+ */
class GetStockStatusOnDefaultStockTest extends TestCase
{
/**
diff --git a/InventoryCatalog/Test/Integration/CatalogInventory/Helper/Stock/AddInStockFilterToCollectionOnDefaultStockTest.php b/InventoryCatalog/Test/Integration/CatalogInventory/Helper/Stock/AddInStockFilterToCollectionOnDefaultStockTest.php
index 9d63158a9ff9..e027760a68ba 100644
--- a/InventoryCatalog/Test/Integration/CatalogInventory/Helper/Stock/AddInStockFilterToCollectionOnDefaultStockTest.php
+++ b/InventoryCatalog/Test/Integration/CatalogInventory/Helper/Stock/AddInStockFilterToCollectionOnDefaultStockTest.php
@@ -32,6 +32,7 @@ protected function setUp(): void
/**
* @magentoDataFixture Magento_InventoryApi::Test/_files/products.php
* @magentoDataFixture Magento_InventoryCatalog::Test/_files/source_items_on_default_source.php
+ * @magentoDbIsolation disabled
*/
public function testAddInStockFilterToCollection()
{
diff --git a/InventoryCatalog/Test/Integration/CatalogInventory/Helper/Stock/AddStockStatusToProductsOnDefaultStockTest.php b/InventoryCatalog/Test/Integration/CatalogInventory/Helper/Stock/AddStockStatusToProductsOnDefaultStockTest.php
index 268fac12015f..016d6fd2ffe8 100644
--- a/InventoryCatalog/Test/Integration/CatalogInventory/Helper/Stock/AddStockStatusToProductsOnDefaultStockTest.php
+++ b/InventoryCatalog/Test/Integration/CatalogInventory/Helper/Stock/AddStockStatusToProductsOnDefaultStockTest.php
@@ -33,6 +33,7 @@ protected function setUp(): void
/**
* @magentoDataFixture Magento_InventoryApi::Test/_files/products.php
* @magentoDataFixture Magento_InventoryCatalog::Test/_files/source_items_on_default_source.php
+ * @magentoDbIsolation disabled
*/
public function testAddStockStatusToProducts()
{
diff --git a/InventoryCatalog/Test/Integration/CatalogInventory/Helper/Stock/AssignStatusToProductOnDefaultStockTest.php b/InventoryCatalog/Test/Integration/CatalogInventory/Helper/Stock/AssignStatusToProductOnDefaultStockTest.php
index 7dd74b9fb3aa..aadcd16b58f0 100644
--- a/InventoryCatalog/Test/Integration/CatalogInventory/Helper/Stock/AssignStatusToProductOnDefaultStockTest.php
+++ b/InventoryCatalog/Test/Integration/CatalogInventory/Helper/Stock/AssignStatusToProductOnDefaultStockTest.php
@@ -13,6 +13,9 @@
use Magento\TestFramework\Helper\Bootstrap;
use PHPUnit\Framework\TestCase;
+/**
+ * @magentoDbIsolation disabled
+ */
class AssignStatusToProductOnDefaultStockTest extends TestCase
{
/**
diff --git a/InventoryCatalog/Test/Integration/CatalogInventory/Helper/Stock/AssignStatusToProductTest.php b/InventoryCatalog/Test/Integration/CatalogInventory/Helper/Stock/AssignStatusToProductTest.php
deleted file mode 100644
index 79b535cb86ce..000000000000
--- a/InventoryCatalog/Test/Integration/CatalogInventory/Helper/Stock/AssignStatusToProductTest.php
+++ /dev/null
@@ -1,151 +0,0 @@
-stockHelper = Bootstrap::getObjectManager()->get(Stock::class);
- $this->productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class);
- $this->storeManager = Bootstrap::getObjectManager()->get(StoreManagerInterface::class);
- $this->storeCodeBefore = $this->storeManager->getStore()->getCode();
- }
-
- /**
- * @magentoDataFixture Magento_InventoryApi::Test/_files/products.php
- * @magentoDataFixture Magento_InventoryApi::Test/_files/sources.php
- * @magentoDataFixture Magento_InventoryApi::Test/_files/stocks.php
- * @magentoDataFixture Magento_InventoryApi::Test/_files/stock_source_links.php
- * @magentoDataFixture Magento_InventoryApi::Test/_files/source_items.php
- * @magentoDataFixture Magento_InventorySalesApi::Test/_files/websites_with_stores.php
- * @magentoDataFixture Magento_InventorySalesApi::Test/_files/stock_website_sales_channels.php
- * @magentoDataFixture Magento_InventoryIndexer::Test/_files/reindex_inventory.php
- * @dataProvider assignStatusToProductDataProvider
- * @param string $storeCode
- * @param array $productsData
- *
- * @magentoDbIsolation disabled
- */
- public function testAssignStatusToProductIfStatusParameterIsNotPassed(string $storeCode, array $productsData)
- {
- $this->storeManager->setCurrentStore($storeCode);
-
- foreach ($productsData as $sku => $expectedStatus) {
- $product = $this->productRepository->get($sku);
- /** @var Product $product */
- $this->stockHelper->assignStatusToProduct($product);
-
- self::assertEquals($expectedStatus, $product->isSalable());
- }
- }
-
- /**
- * @magentoDataFixture Magento_InventoryApi::Test/_files/products.php
- * @magentoDataFixture Magento_InventoryApi::Test/_files/sources.php
- * @magentoDataFixture Magento_InventoryApi::Test/_files/stocks.php
- * @magentoDataFixture Magento_InventoryApi::Test/_files/stock_source_links.php
- * @magentoDataFixture Magento_InventoryApi::Test/_files/source_items.php
- * @magentoDataFixture Magento_InventorySalesApi::Test/_files/websites_with_stores.php
- * @magentoDataFixture Magento_InventorySalesApi::Test/_files/stock_website_sales_channels.php
- * @magentoDataFixture Magento_InventoryIndexer::Test/_files/reindex_inventory.php
- * @dataProvider assignStatusToProductDataProvider
- * @param string $storeCode
- * @param array $productsData
- *
- * @magentoDbIsolation disabled
- */
- public function testAssignStatusToProductIfStatusParameterIsPassed(string $storeCode, array $productsData)
- {
- $expectedStatus = 1;
- $this->storeManager->setCurrentStore($storeCode);
-
- foreach (array_keys($productsData) as $sku) {
- $product = $this->productRepository->get($sku);
- /** @var Product $product */
- $this->stockHelper->assignStatusToProduct($product, $expectedStatus);
-
- self::assertEquals($expectedStatus, $product->isSalable());
- }
- }
-
- /**
- * @return array
- */
- public function assignStatusToProductDataProvider(): array
- {
- return [
- 'eu_website' => [
- 'store_for_eu_website',
- [
- 'SKU-1' => 1,
- 'SKU-2' => 0,
- 'SKU-3' => 0,
- ],
- ],
- 'us_website' => [
- 'store_for_us_website',
- [
- 'SKU-1' => 0,
- 'SKU-2' => 1,
- 'SKU-3' => 0,
- ],
- ],
- 'global_website' => [
- 'store_for_global_website',
- [
- 'SKU-1' => 1,
- 'SKU-2' => 1,
- 'SKU-3' => 0,
- ],
- ],
- ];
- }
-
- /**
- * @inheritdoc
- */
- protected function tearDown(): void
- {
- $this->storeManager->setCurrentStore($this->storeCodeBefore);
-
- parent::tearDown();
- }
-}
diff --git a/InventoryCatalog/Test/Integration/CatalogInventory/Model/ResourceModel/Stock/Status/AddIsInStockFilterToCollectionOnDefaultStockTest.php b/InventoryCatalog/Test/Integration/CatalogInventory/Model/ResourceModel/Stock/Status/AddIsInStockFilterToCollectionOnDefaultStockTest.php
index 3deaecd66d62..52aeb76d896a 100644
--- a/InventoryCatalog/Test/Integration/CatalogInventory/Model/ResourceModel/Stock/Status/AddIsInStockFilterToCollectionOnDefaultStockTest.php
+++ b/InventoryCatalog/Test/Integration/CatalogInventory/Model/ResourceModel/Stock/Status/AddIsInStockFilterToCollectionOnDefaultStockTest.php
@@ -35,6 +35,7 @@ protected function setUp(): void
/**
* @magentoDataFixture Magento_InventoryApi::Test/_files/products.php
* @magentoDataFixture Magento_InventoryCatalog::Test/_files/source_items_on_default_source.php
+ * @magentoDbIsolation disabled
*/
public function testAddIsInStockFilterToCollection()
{
diff --git a/InventoryCatalog/Test/Integration/CatalogInventory/Model/ResourceModel/Stock/Status/AddStockDataToCollectionOnDefaultStockTest.php b/InventoryCatalog/Test/Integration/CatalogInventory/Model/ResourceModel/Stock/Status/AddStockDataToCollectionOnDefaultStockTest.php
index 7294cbf05c59..86d22e82bdce 100644
--- a/InventoryCatalog/Test/Integration/CatalogInventory/Model/ResourceModel/Stock/Status/AddStockDataToCollectionOnDefaultStockTest.php
+++ b/InventoryCatalog/Test/Integration/CatalogInventory/Model/ResourceModel/Stock/Status/AddStockDataToCollectionOnDefaultStockTest.php
@@ -32,6 +32,7 @@ protected function setUp(): void
/**
* @magentoDataFixture Magento_InventoryApi::Test/_files/products.php
* @magentoDataFixture Magento_InventoryCatalog::Test/_files/source_items_on_default_source.php
+ * @magentoDbIsolation disabled
*
* @param int $expectedSize
* @param bool $isFilterInStock
@@ -54,7 +55,7 @@ public function addStockDataToCollectionDataProvider(): array
{
return [
[4, true],
- [6, false],
+ [5, false],
];
}
}
diff --git a/InventoryCatalog/Test/Integration/CatalogInventory/Model/ResourceModel/Stock/Status/AddStockStatusToSelectOnDefaultStockTest.php b/InventoryCatalog/Test/Integration/CatalogInventory/Model/ResourceModel/Stock/Status/AddStockStatusToSelectOnDefaultStockTest.php
index e4a53d61b16d..2051596670bb 100644
--- a/InventoryCatalog/Test/Integration/CatalogInventory/Model/ResourceModel/Stock/Status/AddStockStatusToSelectOnDefaultStockTest.php
+++ b/InventoryCatalog/Test/Integration/CatalogInventory/Model/ResourceModel/Stock/Status/AddStockStatusToSelectOnDefaultStockTest.php
@@ -42,6 +42,7 @@ protected function setUp(): void
/**
* @magentoDataFixture Magento_InventoryApi::Test/_files/products.php
* @magentoDataFixture Magento_InventoryCatalog::Test/_files/source_items_on_default_source.php
+ * @magentoDbIsolation disabled
*/
public function testAddStockStatusToSelect()
{
diff --git a/InventoryCatalog/Test/Integration/CatalogInventory/Model/ResourceModel/Stock/Status/AddStockStatusToSelectTest.php b/InventoryCatalog/Test/Integration/CatalogInventory/Model/ResourceModel/Stock/Status/AddStockStatusToSelectTest.php
index 786bae1a5ca8..72ae4a590721 100644
--- a/InventoryCatalog/Test/Integration/CatalogInventory/Model/ResourceModel/Stock/Status/AddStockStatusToSelectTest.php
+++ b/InventoryCatalog/Test/Integration/CatalogInventory/Model/ResourceModel/Stock/Status/AddStockStatusToSelectTest.php
@@ -70,7 +70,7 @@ public function testAddStockStatusToSelect(
$this->stockStatus->addStockStatusToSelect($collection->getSelect(), $this->website);
foreach ($collection as $item) {
- $item->getIsSalable() == 1 ? $actualIsSalableCount++ : $actualNotSalableCount++;
+ $item->getData('is_salable') == 1 ? $actualIsSalableCount++ : $actualNotSalableCount++;
}
self::assertEquals($expectedIsSalableCount, $actualIsSalableCount);
diff --git a/InventoryCatalog/Test/Integration/GetDefaultSourceItemBySkuTest.php b/InventoryCatalog/Test/Integration/GetDefaultSourceItemBySkuTest.php
index 67c288c0b614..d569a610bb13 100644
--- a/InventoryCatalog/Test/Integration/GetDefaultSourceItemBySkuTest.php
+++ b/InventoryCatalog/Test/Integration/GetDefaultSourceItemBySkuTest.php
@@ -11,6 +11,9 @@
use Magento\TestFramework\Helper\Bootstrap;
use PHPUnit\Framework\TestCase;
+/**
+ * @magentoDbIsolation disabled
+ */
class GetDefaultSourceItemBySkuTest extends TestCase
{
/**
diff --git a/InventoryCatalog/Test/Integration/GetSourceItemsBySkuAndSourceCodesTest.php b/InventoryCatalog/Test/Integration/GetSourceItemsBySkuAndSourceCodesTest.php
index 23d02c517f1c..b90f4b2e976e 100644
--- a/InventoryCatalog/Test/Integration/GetSourceItemsBySkuAndSourceCodesTest.php
+++ b/InventoryCatalog/Test/Integration/GetSourceItemsBySkuAndSourceCodesTest.php
@@ -11,6 +11,9 @@
use Magento\TestFramework\Helper\Bootstrap;
use PHPUnit\Framework\TestCase;
+/**
+ * @magentoDbIsolation disabled
+ */
class GetSourceItemsBySkuAndSourceCodesTest extends TestCase
{
/**
diff --git a/InventoryCatalog/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php b/InventoryCatalog/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php
index a2155262ce02..4da0160215b6 100644
--- a/InventoryCatalog/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php
+++ b/InventoryCatalog/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php
@@ -19,6 +19,9 @@
use Magento\CatalogInventory\Api\StockItemCriteriaInterface;
use Magento\CatalogInventory\Api\StockItemCriteriaInterfaceFactory;
+/**
+ * @magentoDbIsolation disabled
+ */
class SetDataToLegacyStockItemAtSourceItemsSaveTest extends TestCase
{
/**
diff --git a/InventoryCatalog/Test/Integration/SetDataToLegacyStockStatusAtSourceItemsSaveTest.php b/InventoryCatalog/Test/Integration/SetDataToLegacyStockStatusAtSourceItemsSaveTest.php
index 21461ffb5108..9d736b8f19c7 100644
--- a/InventoryCatalog/Test/Integration/SetDataToLegacyStockStatusAtSourceItemsSaveTest.php
+++ b/InventoryCatalog/Test/Integration/SetDataToLegacyStockStatusAtSourceItemsSaveTest.php
@@ -25,6 +25,8 @@
/**
* Tests legacy stock information synchronized with MSI's.
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ *
+ * @magentoDbIsolation disabled
*/
class SetDataToLegacyStockStatusAtSourceItemsSaveTest extends TestCase
{
diff --git a/InventoryCatalog/Test/Integration/SetOutOfStockToLegacyStockStatusAtSourceItemsDeleteTest.php b/InventoryCatalog/Test/Integration/SetOutOfStockToLegacyStockStatusAtSourceItemsDeleteTest.php
index d4f9a788da30..1aacc09a46a3 100644
--- a/InventoryCatalog/Test/Integration/SetOutOfStockToLegacyStockStatusAtSourceItemsDeleteTest.php
+++ b/InventoryCatalog/Test/Integration/SetOutOfStockToLegacyStockStatusAtSourceItemsDeleteTest.php
@@ -77,6 +77,7 @@ protected function setUp(): void
* @magentoDataFixture Magento_InventoryApi::Test/_files/products.php
* @magentoDataFixture Magento_InventoryCatalog::Test/_files/source_items_on_default_source.php
* @magentoDataFixture Magento_InventoryIndexer::Test/_files/reindex_inventory.php
+ * @magentoDbIsolation disabled
*/
public function testSetOutOfStock()
{
diff --git a/InventoryCatalog/Test/Integration/SetToZeroLegacyStockItemAtSourceItemsDeleteTest.php b/InventoryCatalog/Test/Integration/SetToZeroLegacyStockItemAtSourceItemsDeleteTest.php
index 47eef006d6de..645b71255cb6 100644
--- a/InventoryCatalog/Test/Integration/SetToZeroLegacyStockItemAtSourceItemsDeleteTest.php
+++ b/InventoryCatalog/Test/Integration/SetToZeroLegacyStockItemAtSourceItemsDeleteTest.php
@@ -76,6 +76,7 @@ protected function setUp(): void
* @magentoDataFixture Magento_InventoryApi::Test/_files/products.php
* @magentoDataFixture Magento_InventoryCatalog::Test/_files/source_items_on_default_source.php
* @magentoDataFixture Magento_InventoryIndexer::Test/_files/reindex_inventory.php
+ * @magentoDbIsolation disabled
*/
public function testSetToZero()
{
diff --git a/InventoryCatalog/Test/Integration/UpdateDefaultSourceItemAtLegacyStockItemSaveTest.php b/InventoryCatalog/Test/Integration/UpdateDefaultSourceItemAtLegacyStockItemSaveTest.php
index 111f9ecc8039..9d21fdc8def2 100644
--- a/InventoryCatalog/Test/Integration/UpdateDefaultSourceItemAtLegacyStockItemSaveTest.php
+++ b/InventoryCatalog/Test/Integration/UpdateDefaultSourceItemAtLegacyStockItemSaveTest.php
@@ -37,7 +37,7 @@ protected function setUp(): void
* @magentoDataFixture Magento_InventoryApi::Test/_files/products.php
* @magentoDataFixture Magento_InventoryApi::Test/_files/source_items.php
* @magentoDataFixture Magento_InventoryCatalog::Test/_files/source_items_on_default_source.php
- * @magentoDbIsolation enabled
+ * @magentoDbIsolation disabled
* @throws \Magento\Framework\Exception\NoSuchEntityException
*/
public function testSaveLegacyStockItemAssignedToDefaultSource()
@@ -60,7 +60,7 @@ public function testSaveLegacyStockItemAssignedToDefaultSource()
* @magentoDataFixture Magento_InventoryApi::Test/_files/products.php
* @magentoDataFixture Magento_InventoryApi::Test/_files/source_items.php
* @magentoDataFixture Magento_InventoryCatalog::Test/_files/source_items_on_default_source.php
- * @magentoDbIsolation enabled
+ * @magentoDbIsolation disabled
* @throws \Magento\Framework\Exception\NoSuchEntityException
*/
public function testSaveLegacyStockItemNotAssignedToDefaultSource()
@@ -94,7 +94,7 @@ public function testSaveLegacyStockItemNotAssignedToDefaultSource()
* @magentoDataFixture Magento_InventoryApi::Test/_files/sources.php
* @magentoDataFixture Magento_InventoryApi::Test/_files/products.php
* @magentoDataFixture Magento_InventoryApi::Test/_files/source_items.php
- * @magentoDbIsolation enabled
+ * @magentoDbIsolation disabled
* @throws \Magento\Framework\Exception\NoSuchEntityException
*/
public function testSaveLegacyStockItemWithoutDefaultSourceAssignment()
diff --git a/InventoryCatalog/Test/Integration/UpdateDefaultSourceItemAtProductSaveTest.php b/InventoryCatalog/Test/Integration/UpdateDefaultSourceItemAtProductSaveTest.php
index 5f85b63e1b0a..23ae8432cfcd 100644
--- a/InventoryCatalog/Test/Integration/UpdateDefaultSourceItemAtProductSaveTest.php
+++ b/InventoryCatalog/Test/Integration/UpdateDefaultSourceItemAtProductSaveTest.php
@@ -36,7 +36,7 @@ protected function setUp(): void
* @magentoDataFixture Magento_InventoryApi::Test/_files/sources.php
* @magentoDataFixture Magento_InventoryApi::Test/_files/products.php
* @magentoDataFixture Magento_InventoryApi::Test/_files/source_items.php
- * @magentoDbIsolation enabled
+ * @magentoDbIsolation disabled
*/
public function testSaveOutOfStockProductNotAssignedToDefaultSource()
{
diff --git a/InventoryCatalog/composer.json b/InventoryCatalog/composer.json
index c3651026905c..1a5c872ba15e 100644
--- a/InventoryCatalog/composer.json
+++ b/InventoryCatalog/composer.json
@@ -13,7 +13,8 @@
"magento/module-inventory-configuration-api": "*",
"magento/module-inventory-indexer": "*",
"magento/module-inventory-sales-api": "*",
- "magento/module-inventory-configuration": "*"
+ "magento/module-inventory-configuration": "*",
+ "magento/module-inventory-multi-dimensional-indexer-api": "*"
},
"suggest": {
"magento/module-inventory-reservations-api": "*"
diff --git a/InventoryCatalog/etc/adminhtml/di.xml b/InventoryCatalog/etc/adminhtml/di.xml
new file mode 100644
index 000000000000..07204db2bf2d
--- /dev/null
+++ b/InventoryCatalog/etc/adminhtml/di.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
diff --git a/InventoryCatalog/etc/di.xml b/InventoryCatalog/etc/di.xml
index b0dff41f921b..b9737b18770d 100644
--- a/InventoryCatalog/etc/di.xml
+++ b/InventoryCatalog/etc/di.xml
@@ -53,7 +53,6 @@
-
@@ -165,4 +164,11 @@
+
+
+
+
+
+
+
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/InventoryCatalogAdminUi/Test/Integration/GetSourceItemsDataBySkuTest.php b/InventoryCatalogAdminUi/Test/Integration/GetSourceItemsDataBySkuTest.php
index 15efe8dcb808..a87752eb469c 100644
--- a/InventoryCatalogAdminUi/Test/Integration/GetSourceItemsDataBySkuTest.php
+++ b/InventoryCatalogAdminUi/Test/Integration/GetSourceItemsDataBySkuTest.php
@@ -12,6 +12,9 @@
use Magento\TestFramework\Helper\Bootstrap;
use PHPUnit\Framework\TestCase;
+/**
+ * @magentoDbIsolation disabled
+ */
class GetSourceItemsDataBySkuTest extends TestCase
{
/**
diff --git a/InventoryConfigurableProduct/Model/IsConfigurableProductSalable.php b/InventoryConfigurableProduct/Model/IsConfigurableProductSalable.php
new file mode 100644
index 000000000000..c6eee7c32868
--- /dev/null
+++ b/InventoryConfigurableProduct/Model/IsConfigurableProductSalable.php
@@ -0,0 +1,65 @@
+type = $type;
+ $this->areProductsSalable = $areProductsSalable;
+ }
+
+ /**
+ * Verify configurable product salable status considering configurable options.
+ *
+ * @param ProductInterface $product
+ * @param int $stockId
+ * @return bool
+ */
+ public function execute(ProductInterface $product, int $stockId): bool
+ {
+ $salable = false;
+ $options = $this->type->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()) {
+ $salable = true;
+ break;
+ }
+ }
+
+ return $salable;
+ }
+}
diff --git a/InventoryConfigurableProduct/Model/IsProductSalableCondition/ProductOptionsCondition.php b/InventoryConfigurableProduct/Model/IsProductSalableCondition/ProductOptionsCondition.php
new file mode 100644
index 000000000000..e66a42f75162
--- /dev/null
+++ b/InventoryConfigurableProduct/Model/IsProductSalableCondition/ProductOptionsCondition.php
@@ -0,0 +1,54 @@
+productRepository = $productRepository;
+ $this->isConfigurableProductSalable = $isConfigurableProductSalable;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function execute(string $sku, int $stockId): bool
+ {
+ $product = $this->productRepository->get($sku);
+ if ($product->getTypeId() !== Configurable::TYPE_CODE) {
+ return true;
+ }
+
+ return $this->isConfigurableProductSalable->execute($product, $stockId);
+ }
+}
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/InventoryCatalog/Model/IsProductSalable/IsConfigurableProductSalablePlugin.php b/InventoryConfigurableProduct/Plugin/InventoryCatalog/Model/IsProductSalable/IsConfigurableProductSalablePlugin.php
new file mode 100644
index 000000000000..7b3576480c32
--- /dev/null
+++ b/InventoryConfigurableProduct/Plugin/InventoryCatalog/Model/IsProductSalable/IsConfigurableProductSalablePlugin.php
@@ -0,0 +1,61 @@
+type = $type;
+ }
+
+ /**
+ * Get configurable product status.
+ *
+ * @param IsProductSalable $subject
+ * @param bool $result
+ * @param ProductInterface $product
+ * @return bool
+ * @throws \Exception
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function afterExecute(IsProductSalable $subject, bool $result, ProductInterface $product): bool
+ {
+ if ($product->getTypeId() !== Configurable::TYPE_CODE || !$result) {
+ return $result;
+ }
+
+ $collection = $this->type->getUsedProductCollection($product)->addAttributeToFilter(
+ ProductInterface::STATUS,
+ Status::STATUS_ENABLED
+ );
+ foreach ($collection->getItems() as $item) {
+ if ($item->isSalable()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/InventoryConfigurableProduct/Test/Integration/SalesQuoteItem/AddSalesQuoteItemOnDefaultStockTest.php b/InventoryConfigurableProduct/Test/Integration/SalesQuoteItem/AddSalesQuoteItemOnDefaultStockTest.php
index 837d4759a096..54a6dcd9b7b0 100644
--- a/InventoryConfigurableProduct/Test/Integration/SalesQuoteItem/AddSalesQuoteItemOnDefaultStockTest.php
+++ b/InventoryConfigurableProduct/Test/Integration/SalesQuoteItem/AddSalesQuoteItemOnDefaultStockTest.php
@@ -22,6 +22,8 @@
/**
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ *
+ * @magentoDbIsolation disabled
*/
class AddSalesQuoteItemOnDefaultStockTest extends TestCase
{
diff --git a/InventoryConfigurableProduct/composer.json b/InventoryConfigurableProduct/composer.json
index 5a3e350f7cb3..bcf5b46c7c1a 100644
--- a/InventoryConfigurableProduct/composer.json
+++ b/InventoryConfigurableProduct/composer.json
@@ -13,6 +13,10 @@
"magento/module-sales": "*",
"magento/module-configurable-product": "*"
},
+ "suggest": {
+ "magento/module-inventory-catalog": "*",
+ "magento/module-inventory-sales": "*"
+ },
"type": "magento2-module",
"license": [
"OSL-3.0",
diff --git a/InventoryConfigurableProduct/etc/di.xml b/InventoryConfigurableProduct/etc/di.xml
index 77358c2d991e..c8c1ff6dfae3 100644
--- a/InventoryConfigurableProduct/etc/di.xml
+++ b/InventoryConfigurableProduct/etc/di.xml
@@ -26,4 +26,22 @@
+
+
+ Magento\InventorySalesApi\Api\AreProductsSalableInterface\Proxy
+
+
+
+
+
+
+
+
+ -
+
- true
+ - Magento\InventoryConfigurableProduct\Model\IsProductSalableCondition\ProductOptionsCondition
+
+
+
+
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 @@
-
-
-
diff --git a/InventoryConfigurableProductIndexer/Indexer/SourceItem/SourceItemIndexer.php b/InventoryConfigurableProductIndexer/Indexer/SourceItem/SourceItemIndexer.php
index c6a798592ada..03e93320c084 100644
--- a/InventoryConfigurableProductIndexer/Indexer/SourceItem/SourceItemIndexer.php
+++ b/InventoryConfigurableProductIndexer/Indexer/SourceItem/SourceItemIndexer.php
@@ -8,13 +8,15 @@
namespace Magento\InventoryConfigurableProductIndexer\Indexer\SourceItem;
use Magento\Framework\App\ResourceConnection;
+use Magento\InventoryIndexer\Indexer\InventoryIndexer;
use Magento\InventoryMultiDimensionalIndexerApi\Model\Alias;
use Magento\InventoryMultiDimensionalIndexerApi\Model\IndexHandlerInterface;
use Magento\InventoryMultiDimensionalIndexerApi\Model\IndexNameBuilder;
use Magento\InventoryMultiDimensionalIndexerApi\Model\IndexStructureInterface;
-use Magento\InventoryCatalogApi\Api\DefaultStockProviderInterface;
-use Magento\InventoryIndexer\Indexer\InventoryIndexer;
+/**
+ * Configurable product source item indexer.
+ */
class SourceItemIndexer
{
/**
@@ -47,11 +49,6 @@ class SourceItemIndexer
*/
private $siblingSkuListInStockProvider;
- /**
- * @var DefaultStockProviderInterface
- */
- private $defaultStockProvider;
-
/**
* @param ResourceConnection $resourceConnection
* @param IndexNameBuilder $indexNameBuilder
@@ -59,7 +56,6 @@ class SourceItemIndexer
* @param IndexStructureInterface $indexStructure
* @param IndexDataBySkuListProvider $indexDataBySkuListProvider
* @param SiblingSkuListInStockProvider $siblingSkuListInStockProvider
- * @param DefaultStockProviderInterface $defaultStockProvider
*/
public function __construct(
ResourceConnection $resourceConnection,
@@ -67,8 +63,7 @@ public function __construct(
IndexHandlerInterface $indexHandler,
IndexStructureInterface $indexStructure,
IndexDataBySkuListProvider $indexDataBySkuListProvider,
- SiblingSkuListInStockProvider $siblingSkuListInStockProvider,
- DefaultStockProviderInterface $defaultStockProvider
+ SiblingSkuListInStockProvider $siblingSkuListInStockProvider
) {
$this->resourceConnection = $resourceConnection;
$this->indexNameBuilder = $indexNameBuilder;
@@ -76,10 +71,11 @@ public function __construct(
$this->indexDataBySkuListProvider = $indexDataBySkuListProvider;
$this->indexStructure = $indexStructure;
$this->siblingSkuListInStockProvider = $siblingSkuListInStockProvider;
- $this->defaultStockProvider = $defaultStockProvider;
}
/**
+ * Reindex given source items.
+ *
* @param array $sourceItemIds
*/
public function executeList(array $sourceItemIds)
@@ -88,10 +84,6 @@ public function executeList(array $sourceItemIds)
foreach ($skuListInStockList as $skuListInStock) {
$stockId = $skuListInStock->getStockId();
-
- if ($this->defaultStockProvider->getId() === $stockId) {
- continue;
- }
$skuList = $skuListInStock->getSkuList();
$mainIndexName = $this->indexNameBuilder
diff --git a/InventoryConfigurableProductIndexer/Indexer/Stock/StockIndexer.php b/InventoryConfigurableProductIndexer/Indexer/Stock/StockIndexer.php
index 68bba4793de9..9b3064f4222c 100644
--- a/InventoryConfigurableProductIndexer/Indexer/Stock/StockIndexer.php
+++ b/InventoryConfigurableProductIndexer/Indexer/Stock/StockIndexer.php
@@ -10,7 +10,6 @@
use Magento\Framework\App\ObjectManager;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\Exception\StateException;
-use Magento\InventoryCatalogApi\Api\DefaultStockProviderInterface;
use Magento\InventoryIndexer\Indexer\InventoryIndexer;
use Magento\InventoryIndexer\Indexer\Stock\GetAllStockIds;
use Magento\InventoryIndexer\Indexer\Stock\PrepareIndexDataForClearingIndex;
@@ -57,11 +56,6 @@ class StockIndexer
*/
private $indexTableSwitcher;
- /**
- * @var DefaultStockProviderInterface
- */
- private $defaultStockProvider;
-
/**
* @var PrepareIndexDataForClearingIndex
*/
@@ -76,7 +70,6 @@ class StockIndexer
* @param IndexNameBuilder $indexNameBuilder
* @param IndexDataByStockIdProvider $indexDataByStockIdProvider
* @param IndexTableSwitcherInterface $indexTableSwitcher
- * @param DefaultStockProviderInterface $defaultStockProvider
* @param PrepareIndexDataForClearingIndex|null $prepareIndexDataForClearingIndex
*/
public function __construct(
@@ -86,7 +79,6 @@ public function __construct(
IndexNameBuilder $indexNameBuilder,
IndexDataByStockIdProvider $indexDataByStockIdProvider,
IndexTableSwitcherInterface $indexTableSwitcher,
- DefaultStockProviderInterface $defaultStockProvider,
PrepareIndexDataForClearingIndex $prepareIndexDataForClearingIndex = null
) {
$this->getAllStockIds = $getAllStockIds;
@@ -95,7 +87,6 @@ public function __construct(
$this->indexNameBuilder = $indexNameBuilder;
$this->indexDataByStockIdProvider = $indexDataByStockIdProvider;
$this->indexTableSwitcher = $indexTableSwitcher;
- $this->defaultStockProvider = $defaultStockProvider;
$this->prepareIndexDataForClearingIndex = $prepareIndexDataForClearingIndex ?: ObjectManager::getInstance()
->get(PrepareIndexDataForClearingIndex::class);
}
@@ -134,10 +125,6 @@ public function executeRow(int $stockId): void
public function executeList(array $stockIds): void
{
foreach ($stockIds as $stockId) {
- if ($this->defaultStockProvider->getId() === $stockId) {
- continue;
- }
-
$mainIndexName = $this->indexNameBuilder
->setIndexId(InventoryIndexer::INDEXER_ID)
->addDimension('stock_', (string)$stockId)
diff --git a/InventoryConfigurableProductIndexer/Plugin/Catalog/Model/ResourceModel/Product/ReindexSourceItemsPlugin.php b/InventoryConfigurableProductIndexer/Plugin/Catalog/Model/ResourceModel/Product/ReindexSourceItemsPlugin.php
new file mode 100644
index 000000000000..56b4d418ca7d
--- /dev/null
+++ b/InventoryConfigurableProductIndexer/Plugin/Catalog/Model/ResourceModel/Product/ReindexSourceItemsPlugin.php
@@ -0,0 +1,89 @@
+getSourceItemsBySku = $getSourceItemsBySku;
+ $this->getSkusByProductIds = $getSkusByProductIds;
+ $this->getSourceItemIds = $getSourceItemIds;
+ $this->sourceItemIndexer = $sourceItemIndexer;
+ }
+
+ /**
+ * Reindex configurable source items after product save.
+ *
+ * @param Product $subject
+ * @param Product $result
+ * @param AbstractModel $product
+ * @return Product
+ * @throws NoSuchEntityException
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function afterSave(Product $subject, Product $result, AbstractModel $product): Product
+ {
+ if ($product->getTypeId() !== Configurable::TYPE_CODE) {
+ return $result;
+ }
+ $childrenIds = $product->getExtensionAttributes()->getConfigurableProductLinks() ?: [];
+ $skus = $this->getSkusByProductIds->execute($childrenIds);
+ $sourceItems = [[]];
+ foreach ($skus as $sku) {
+ $sourceItems[] = $this->getSourceItemsBySku->execute($sku);
+ }
+ $sourceItems = array_merge(...$sourceItems);
+ $sourceItemIds = $this->getSourceItemIds->execute($sourceItems);
+ $this->sourceItemIndexer->executeList($sourceItemIds);
+
+ return $result;
+ }
+}
diff --git a/InventoryConfigurableProductIndexer/Plugin/InventoryIndexer/Model/Queue/GetDataForUpdate/AddConfigurableProductDataPlugin.php b/InventoryConfigurableProductIndexer/Plugin/InventoryIndexer/Model/Queue/GetDataForUpdate/AddConfigurableProductDataPlugin.php
new file mode 100644
index 000000000000..0e62f8fcfb12
--- /dev/null
+++ b/InventoryConfigurableProductIndexer/Plugin/InventoryIndexer/Model/Queue/GetDataForUpdate/AddConfigurableProductDataPlugin.php
@@ -0,0 +1,130 @@
+type = $type;
+ $this->getProductIdsBySkus = $getProductIdsBySkus;
+ $this->getSkusByProductIds = $getSkusByProductIds;
+ $this->getStockItemData = $getStockItemData;
+ $this->logger = $logger;
+ $this->areProductsSalable = $areProductsSalable;
+ }
+
+ /**
+ * Add configurable product to index data.
+ *
+ * @param GetDataForUpdate $subject
+ * @param array $result
+ * @param array $salabilityData
+ * @param int $stockId
+ * @return array
+ * @throws NoSuchEntityException
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function afterExecute(GetDataForUpdate $subject, array $result, array $salabilityData, int $stockId): array
+ {
+ $configurableData = [];
+ $skus = array_keys($result);
+ $childrenIds = $this->getProductIdsBySkus->execute($skus);
+ $configurableProductIds = [];
+ foreach ($childrenIds as $childId) {
+ $configurableProductIds[] = $this->type->getParentIdsByChild($childId);
+ }
+ $configurableProductIds = array_merge(...$configurableProductIds);
+ $configurableSkus = $configurableProductIds ? $this->getSkusByProductIds->execute($configurableProductIds) : [];
+ $areConfigurableProductsSalable = $this->areProductsSalable->execute($configurableSkus, $stockId);
+ foreach ($areConfigurableProductsSalable as $salableResult) {
+ if ($salableResult->isSalable() !== $this->getIndexSalabilityStatus($salableResult->getSku(), $stockId)) {
+ $configurableData[$salableResult->getSku()] = $salableResult->isSalable();
+ }
+ }
+
+ return array_merge($result, $configurableData);
+ }
+
+ /**
+ * Get current index is_salable value.
+ *
+ * @param string $sku
+ * @param int $stockId
+ * @return bool|null
+ */
+ private function getIndexSalabilityStatus(string $sku, int $stockId): ?bool
+ {
+ try {
+ $data = $this->getStockItemData->execute($sku, $stockId);
+ $isSalable = $data ? (bool)$data[GetStockItemDataInterface::IS_SALABLE] : false;
+ } catch (LocalizedException $e) {
+ $this->logger->error($e->getLogMessage());
+ return null;
+ }
+
+ return $isSalable;
+ }
+}
diff --git a/InventoryConfigurableProductIndexer/composer.json b/InventoryConfigurableProductIndexer/composer.json
index 7cddd52ebb9e..1bfa284d0bf5 100644
--- a/InventoryConfigurableProductIndexer/composer.json
+++ b/InventoryConfigurableProductIndexer/composer.json
@@ -5,10 +5,12 @@
"php": "~7.3.0||~7.4.0",
"magento/framework": "*",
"magento/module-catalog": "*",
+ "magento/module-configurable-product": "*",
"magento/module-inventory-api": "*",
"magento/module-inventory-catalog-api": "*",
"magento/module-inventory-indexer": "*",
- "magento/module-inventory-multi-dimensional-indexer-api": "*"
+ "magento/module-inventory-multi-dimensional-indexer-api": "*",
+ "magento/module-inventory-sales-api": "*"
},
"suggest": {
"magento/module-inventory": "*"
diff --git a/InventoryConfigurableProductIndexer/etc/di.xml b/InventoryConfigurableProductIndexer/etc/di.xml
index 9def048d6c77..3e614edc3e4f 100644
--- a/InventoryConfigurableProductIndexer/etc/di.xml
+++ b/InventoryConfigurableProductIndexer/etc/di.xml
@@ -32,4 +32,10 @@
Magento\Inventory\Model\ResourceModel\StockSourceLink::TABLE_NAME_STOCK_SOURCE_LINK
+
+
+
+
+
+
diff --git a/InventoryElasticsearch/Plugin/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider/StockedProductFilterByInventoryStock.php b/InventoryElasticsearch/Plugin/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider/StockedProductFilterByInventoryStock.php
index 00e38f931433..66f4f82b570d 100644
--- a/InventoryElasticsearch/Plugin/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider/StockedProductFilterByInventoryStock.php
+++ b/InventoryElasticsearch/Plugin/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider/StockedProductFilterByInventoryStock.php
@@ -7,13 +7,11 @@
namespace Magento\InventoryElasticsearch\Plugin\CatalogSearch\Model\Indexer\Fulltext\Action\DataProvider;
-use Magento\CatalogInventory\Api\Data\StockStatusInterface;
use Magento\CatalogInventory\Api\StockConfigurationInterface;
use Magento\CatalogInventory\Api\StockStatusCriteriaInterfaceFactory;
use Magento\CatalogInventory\Api\StockStatusRepositoryInterface;
use Magento\CatalogSearch\Model\Indexer\Fulltext\Action\DataProvider;
use Magento\Framework\App\ResourceConnection;
-use Magento\InventoryCatalogApi\Api\DefaultStockProviderInterface;
use Magento\InventoryIndexer\Model\StockIndexTableNameResolverInterface;
use Magento\InventorySalesApi\Model\StockByWebsiteIdResolverInterface;
use Magento\Store\Api\StoreRepositoryInterface;
@@ -53,11 +51,6 @@ class StockedProductFilterByInventoryStock
*/
private $stockStatusRepository;
- /**
- * @var DefaultStockProviderInterface
- */
- private $defaultStockProvider;
-
/**
* @var StoreRepositoryInterface
*/
@@ -70,7 +63,6 @@ class StockedProductFilterByInventoryStock
* @param StockByWebsiteIdResolverInterface $stockByWebsiteIdResolver
* @param StockStatusCriteriaInterfaceFactory $stockStatusCriteriaFactory
* @param StockStatusRepositoryInterface $stockStatusRepository
- * @param DefaultStockProviderInterface $defaultStockProvider
* @param StoreRepositoryInterface $storeRepository
*/
public function __construct(
@@ -80,7 +72,6 @@ public function __construct(
StockByWebsiteIdResolverInterface $stockByWebsiteIdResolver,
StockStatusCriteriaInterfaceFactory $stockStatusCriteriaFactory,
StockStatusRepositoryInterface $stockStatusRepository,
- DefaultStockProviderInterface $defaultStockProvider,
StoreRepositoryInterface $storeRepository
) {
$this->stockConfiguration = $stockConfiguration;
@@ -89,7 +80,6 @@ public function __construct(
$this->stockByWebsiteIdResolver = $stockByWebsiteIdResolver;
$this->stockStatusCriteriaFactory = $stockStatusCriteriaFactory;
$this->stockStatusRepository = $stockStatusRepository;
- $this->defaultStockProvider = $defaultStockProvider;
$this->storeRepository = $storeRepository;
}
@@ -114,13 +104,7 @@ public function beforePrepareProductIndex(
$store = $this->storeRepository->getById($storeId);
$stock = $this->stockByWebsiteIdResolver->execute((int)$store->getWebsiteId());
$stockId = $stock->getStockId();
-
- if ($this->defaultStockProvider->getId() === $stockId) {
- $stockStatuses = $this->getStockStatusesFromDefaultStock($productIds);
- } else {
- $stockStatuses = $this->getStockStatusesFromCustomStock($productIds, $stockId);
- }
-
+ $stockStatuses = $this->getStockStatusesFromStock($productIds, $stockId);
$indexData = array_intersect_key($indexData, $stockStatuses);
}
@@ -131,27 +115,6 @@ public function beforePrepareProductIndex(
];
}
- /**
- * Get product stock statuses on default stock.
- *
- * @param array $productIds
- * @return array
- */
- private function getStockStatusesFromDefaultStock(array $productIds): array
- {
- $stockStatusCriteria = $this->stockStatusCriteriaFactory->create();
- $stockStatusCriteria->setProductsFilter($productIds);
- $stockStatusCollection = $this->stockStatusRepository->getList($stockStatusCriteria);
- $stockStatuses = $stockStatusCollection->getItems();
-
- return array_filter(
- $stockStatuses,
- function (StockStatusInterface $stockStatus) {
- return StockStatusInterface::STATUS_IN_STOCK === (int)$stockStatus->getStockStatus();
- }
- );
- }
-
/**
* Get product stock statuses on custom stock.
*
@@ -159,7 +122,7 @@ function (StockStatusInterface $stockStatus) {
* @param int $stockId
* @return array
*/
- private function getStockStatusesFromCustomStock(array $productIds, int $stockId): array
+ private function getStockStatusesFromStock(array $productIds, int $stockId): array
{
$stockTable = $this->stockIndexTableNameResolver->execute($stockId);
$connection = $this->resourceConnection->getConnection();
diff --git a/InventoryElasticsearch/composer.json b/InventoryElasticsearch/composer.json
index bc435956cb52..478629f005bf 100644
--- a/InventoryElasticsearch/composer.json
+++ b/InventoryElasticsearch/composer.json
@@ -6,7 +6,6 @@
"magento/framework": "*",
"magento/module-catalog-inventory": "*",
"magento/module-catalog-search": "*",
- "magento/module-inventory-catalog-api": "*",
"magento/module-inventory-indexer": "*",
"magento/module-inventory-sales-api": "*",
"magento/module-store": "*"
diff --git a/InventoryGroupedProduct/Test/Integration/Order/PlaceOrderOnDefaultStockTest.php b/InventoryGroupedProduct/Test/Integration/Order/PlaceOrderOnDefaultStockTest.php
index 3413b9b3b47a..31465b58ae64 100644
--- a/InventoryGroupedProduct/Test/Integration/Order/PlaceOrderOnDefaultStockTest.php
+++ b/InventoryGroupedProduct/Test/Integration/Order/PlaceOrderOnDefaultStockTest.php
@@ -10,6 +10,9 @@
use Magento\Framework\Exception\LocalizedException;
use Magento\InventorySales\Test\Integration\Order\PlaceOrderOnDefaultStockTest as PlaceOrderTest;
+/**
+ * @magentoDbIsolation disabled
+ */
class PlaceOrderOnDefaultStockTest extends PlaceOrderTest
{
/**
diff --git a/InventoryGroupedProduct/Test/Integration/SalesQuoteItem/AddSalesQuoteItemOnDefaultStockTest.php b/InventoryGroupedProduct/Test/Integration/SalesQuoteItem/AddSalesQuoteItemOnDefaultStockTest.php
index cd2b4ddbf1c4..5bea0831fce1 100644
--- a/InventoryGroupedProduct/Test/Integration/SalesQuoteItem/AddSalesQuoteItemOnDefaultStockTest.php
+++ b/InventoryGroupedProduct/Test/Integration/SalesQuoteItem/AddSalesQuoteItemOnDefaultStockTest.php
@@ -19,6 +19,9 @@
use PHPUnit\Framework\TestCase;
use Magento\Framework\DataObject\Factory as DataObjectFactory;
+/**
+ * @magentoDbIsolation disabled
+ */
class AddSalesQuoteItemOnDefaultStockTest extends TestCase
{
/**
diff --git a/InventoryGroupedProductIndexer/Indexer/SourceItem/SourceItemIndexer.php b/InventoryGroupedProductIndexer/Indexer/SourceItem/SourceItemIndexer.php
index ff27210d6f29..17142a6df648 100644
--- a/InventoryGroupedProductIndexer/Indexer/SourceItem/SourceItemIndexer.php
+++ b/InventoryGroupedProductIndexer/Indexer/SourceItem/SourceItemIndexer.php
@@ -8,13 +8,15 @@
namespace Magento\InventoryGroupedProductIndexer\Indexer\SourceItem;
use Magento\Framework\App\ResourceConnection;
+use Magento\InventoryIndexer\Indexer\InventoryIndexer;
use Magento\InventoryMultiDimensionalIndexerApi\Model\Alias;
use Magento\InventoryMultiDimensionalIndexerApi\Model\IndexHandlerInterface;
use Magento\InventoryMultiDimensionalIndexerApi\Model\IndexNameBuilder;
use Magento\InventoryMultiDimensionalIndexerApi\Model\IndexStructureInterface;
-use Magento\InventoryCatalogApi\Api\DefaultStockProviderInterface;
-use Magento\InventoryIndexer\Indexer\InventoryIndexer;
+/**
+ * Grouped product source item indexer.
+ */
class SourceItemIndexer
{
/**
@@ -47,11 +49,6 @@ class SourceItemIndexer
*/
private $siblingSkuListInStockProvider;
- /**
- * @var DefaultStockProviderInterface
- */
- private $defaultStockProvider;
-
/**
* @param ResourceConnection $resourceConnection
* @param IndexNameBuilder $indexNameBuilder
@@ -59,7 +56,6 @@ class SourceItemIndexer
* @param IndexStructureInterface $indexStructure
* @param IndexDataBySkuListProvider $indexDataBySkuListProvider
* @param SiblingSkuListInStockProvider $siblingSkuListInStockProvider
- * @param DefaultStockProviderInterface $defaultStockProvider
*/
public function __construct(
ResourceConnection $resourceConnection,
@@ -67,8 +63,7 @@ public function __construct(
IndexHandlerInterface $indexHandler,
IndexStructureInterface $indexStructure,
IndexDataBySkuListProvider $indexDataBySkuListProvider,
- SiblingSkuListInStockProvider $siblingSkuListInStockProvider,
- DefaultStockProviderInterface $defaultStockProvider
+ SiblingSkuListInStockProvider $siblingSkuListInStockProvider
) {
$this->resourceConnection = $resourceConnection;
$this->indexNameBuilder = $indexNameBuilder;
@@ -76,10 +71,11 @@ public function __construct(
$this->indexDataBySkuListProvider = $indexDataBySkuListProvider;
$this->indexStructure = $indexStructure;
$this->siblingSkuListInStockProvider = $siblingSkuListInStockProvider;
- $this->defaultStockProvider = $defaultStockProvider;
}
/**
+ * Reindex given source items.
+ *
* @param array $sourceItemIds
*/
public function executeList(array $sourceItemIds)
@@ -88,10 +84,6 @@ public function executeList(array $sourceItemIds)
foreach ($skuListInStockList as $skuListInStock) {
$stockId = $skuListInStock->getStockId();
-
- if ($this->defaultStockProvider->getId() === $stockId) {
- continue;
- }
$skuList = $skuListInStock->getSkuList();
$mainIndexName = $this->indexNameBuilder
diff --git a/InventoryGroupedProductIndexer/Indexer/Stock/StockIndexer.php b/InventoryGroupedProductIndexer/Indexer/Stock/StockIndexer.php
index 5cded3aa5306..c812611c7040 100644
--- a/InventoryGroupedProductIndexer/Indexer/Stock/StockIndexer.php
+++ b/InventoryGroupedProductIndexer/Indexer/Stock/StockIndexer.php
@@ -10,7 +10,6 @@
use Magento\Framework\App\ObjectManager;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\Exception\StateException;
-use Magento\InventoryCatalogApi\Api\DefaultStockProviderInterface;
use Magento\InventoryIndexer\Indexer\InventoryIndexer;
use Magento\InventoryIndexer\Indexer\Stock\GetAllStockIds;
use Magento\InventoryIndexer\Indexer\Stock\PrepareIndexDataForClearingIndex;
@@ -57,11 +56,6 @@ class StockIndexer
*/
private $indexTableSwitcher;
- /**
- * @var DefaultStockProviderInterface
- */
- private $defaultStockProvider;
-
/**
* @var PrepareIndexDataForClearingIndex
*/
@@ -76,7 +70,6 @@ class StockIndexer
* @param IndexNameBuilder $indexNameBuilder
* @param IndexDataByStockIdProvider $indexDataByStockIdProvider
* @param IndexTableSwitcherInterface $indexTableSwitcher
- * @param DefaultStockProviderInterface $defaultStockProvider
* @param PrepareIndexDataForClearingIndex|null $prepareIndexDataForClearingIndex
*/
public function __construct(
@@ -86,7 +79,6 @@ public function __construct(
IndexNameBuilder $indexNameBuilder,
IndexDataByStockIdProvider $indexDataByStockIdProvider,
IndexTableSwitcherInterface $indexTableSwitcher,
- DefaultStockProviderInterface $defaultStockProvider,
PrepareIndexDataForClearingIndex $prepareIndexDataForClearingIndex = null
) {
$this->getAllStockIds = $getAllStockIds;
@@ -95,7 +87,6 @@ public function __construct(
$this->indexNameBuilder = $indexNameBuilder;
$this->indexDataByStockIdProvider = $indexDataByStockIdProvider;
$this->indexTableSwitcher = $indexTableSwitcher;
- $this->defaultStockProvider = $defaultStockProvider;
$this->prepareIndexDataForClearingIndex = $prepareIndexDataForClearingIndex ?: ObjectManager::getInstance()
->get(PrepareIndexDataForClearingIndex::class);
}
@@ -134,10 +125,6 @@ public function executeRow(int $stockId): void
public function executeList(array $stockIds): void
{
foreach ($stockIds as $stockId) {
- if ($this->defaultStockProvider->getId() === $stockId) {
- continue;
- }
-
$mainIndexName = $this->indexNameBuilder
->setIndexId(InventoryIndexer::INDEXER_ID)
->addDimension('stock_', (string)$stockId)
diff --git a/InventoryGroupedProductIndexer/composer.json b/InventoryGroupedProductIndexer/composer.json
index 7c746bd140b9..18fe1d252e3f 100644
--- a/InventoryGroupedProductIndexer/composer.json
+++ b/InventoryGroupedProductIndexer/composer.json
@@ -6,7 +6,6 @@
"magento/framework": "*",
"magento/module-catalog": "*",
"magento/module-inventory-api": "*",
- "magento/module-inventory-catalog-api": "*",
"magento/module-inventory-indexer": "*",
"magento/module-inventory-multi-dimensional-indexer-api": "*",
"magento/module-grouped-product": "*"
diff --git a/InventoryImportExport/Test/Integration/Model/Export/SourcesTest.php b/InventoryImportExport/Test/Integration/Model/Export/SourcesTest.php
index 9959aab207fc..baad856a9cc3 100644
--- a/InventoryImportExport/Test/Integration/Model/Export/SourcesTest.php
+++ b/InventoryImportExport/Test/Integration/Model/Export/SourcesTest.php
@@ -13,6 +13,9 @@
use Magento\TestFramework\Helper\Bootstrap;
use PHPUnit\Framework\TestCase;
+/**
+ * @magentoDbIsolation disabled
+ */
class SourcesTest extends TestCase
{
/**
diff --git a/InventoryIndexer/Indexer/SelectBuilder.php b/InventoryIndexer/Indexer/SelectBuilder.php
index ac1beea684bb..13af262568d3 100644
--- a/InventoryIndexer/Indexer/SelectBuilder.php
+++ b/InventoryIndexer/Indexer/SelectBuilder.php
@@ -18,7 +18,7 @@
use Magento\InventorySales\Model\ResourceModel\IsStockItemSalableCondition\GetIsStockItemSalableConditionInterface;
/**
- * Select builder
+ * Inventory select builder.
*/
class SelectBuilder
{
@@ -53,6 +53,8 @@ public function __construct(
}
/**
+ * Build inventory select for given stock.
+ *
* @param int $stockId
* @return Select
*/
@@ -79,12 +81,13 @@ public function execute(int $stockId): Select
[]
);
+ $isSalableExpression = new \Zend_Db_Expr($this->getIsStockItemSalableCondition->execute($select));
$select->from(
['source_item' => $sourceItemTable],
[
SourceItemInterface::SKU,
IndexStructure::QUANTITY => 'SUM(' . $quantityExpression . ')',
- IndexStructure::IS_SALABLE => $this->getIsStockItemSalableCondition->execute($select),
+ IndexStructure::IS_SALABLE => $isSalableExpression
]
)
->where('source_item.' . SourceItemInterface::SOURCE_CODE . ' IN (?)', $sourceCodes)
diff --git a/InventoryIndexer/Indexer/SourceItem/Strategy/Sync.php b/InventoryIndexer/Indexer/SourceItem/Strategy/Sync.php
index 85851a5942e7..54c9427356dc 100644
--- a/InventoryIndexer/Indexer/SourceItem/Strategy/Sync.php
+++ b/InventoryIndexer/Indexer/SourceItem/Strategy/Sync.php
@@ -8,7 +8,6 @@
namespace Magento\InventoryIndexer\Indexer\SourceItem\Strategy;
use Magento\Framework\App\ResourceConnection;
-use Magento\InventoryCatalogApi\Api\DefaultStockProviderInterface;
use Magento\InventoryIndexer\Indexer\InventoryIndexer;
use Magento\InventoryIndexer\Indexer\SourceItem\GetSkuListInStock;
use Magento\InventoryIndexer\Indexer\SourceItem\IndexDataBySkuListProvider;
@@ -53,11 +52,6 @@ class Sync
*/
private $stockIndexer;
- /**
- * @var DefaultStockProviderInterface
- */
- private $defaultStockProvider;
-
/**
* $indexStructure is reserved name for construct variable (in index internal mechanism)
*
@@ -67,7 +61,6 @@ class Sync
* @param IndexDataBySkuListProvider $indexDataBySkuListProvider
* @param IndexNameBuilder $indexNameBuilder
* @param StockIndexer $stockIndexer
- * @param DefaultStockProviderInterface $defaultStockProvider
*/
public function __construct(
GetSkuListInStock $getSkuListInStockToUpdate,
@@ -75,8 +68,7 @@ public function __construct(
IndexHandlerInterface $indexHandler,
IndexDataBySkuListProvider $indexDataBySkuListProvider,
IndexNameBuilder $indexNameBuilder,
- StockIndexer $stockIndexer,
- DefaultStockProviderInterface $defaultStockProvider
+ StockIndexer $stockIndexer
) {
$this->getSkuListInStock = $getSkuListInStockToUpdate;
$this->indexStructure = $indexStructureHandler;
@@ -84,7 +76,6 @@ public function __construct(
$this->indexDataBySkuListProvider = $indexDataBySkuListProvider;
$this->indexNameBuilder = $indexNameBuilder;
$this->stockIndexer = $stockIndexer;
- $this->defaultStockProvider = $defaultStockProvider;
}
/**
@@ -92,16 +83,12 @@ public function __construct(
*
* @param int[] $sourceItemIds
*/
- public function executeList(array $sourceItemIds) : void
+ public function executeList(array $sourceItemIds): void
{
$skuListInStockList = $this->getSkuListInStock->execute($sourceItemIds);
foreach ($skuListInStockList as $skuListInStock) {
$stockId = $skuListInStock->getStockId();
- if ($this->defaultStockProvider->getId() === $stockId) {
- continue;
- }
-
$skuList = $skuListInStock->getSkuList();
$mainIndexName = $this->indexNameBuilder
@@ -134,7 +121,7 @@ public function executeList(array $sourceItemIds) : void
*
* @return void
*/
- public function executeFull() : void
+ public function executeFull(): void
{
$this->stockIndexer->executeFull();
}
@@ -145,7 +132,7 @@ public function executeFull() : void
* @param int $sourceItemId
* @return void
*/
- public function executeRow(int $sourceItemId) : void
+ public function executeRow(int $sourceItemId): void
{
$this->executeList([$sourceItemId]);
}
diff --git a/InventoryIndexer/Indexer/Stock/Strategy/Sync.php b/InventoryIndexer/Indexer/Stock/Strategy/Sync.php
index fb5d6506a320..6ae098e783f6 100644
--- a/InventoryIndexer/Indexer/Stock/Strategy/Sync.php
+++ b/InventoryIndexer/Indexer/Stock/Strategy/Sync.php
@@ -7,7 +7,6 @@
namespace Magento\InventoryIndexer\Indexer\Stock\Strategy;
use Magento\Framework\App\ResourceConnection;
-use Magento\InventoryCatalogApi\Api\DefaultStockProviderInterface;
use Magento\InventoryIndexer\Indexer\InventoryIndexer;
use Magento\InventoryIndexer\Indexer\Stock\GetAllStockIds;
use Magento\InventoryIndexer\Indexer\Stock\IndexDataProviderByStockId;
@@ -52,11 +51,6 @@ class Sync
*/
private $indexTableSwitcher;
- /**
- * @var DefaultStockProviderInterface
- */
- private $defaultStockProvider;
-
/**
* $indexStructure is reserved name for construct variable in index internal mechanism
*
@@ -66,7 +60,6 @@ class Sync
* @param IndexNameBuilder $indexNameBuilder
* @param IndexDataProviderByStockId $indexDataProviderByStockId
* @param IndexTableSwitcherInterface $indexTableSwitcher
- * @param DefaultStockProviderInterface $defaultStockProvider
*/
public function __construct(
GetAllStockIds $getAllStockIds,
@@ -74,8 +67,7 @@ public function __construct(
IndexHandlerInterface $indexHandler,
IndexNameBuilder $indexNameBuilder,
IndexDataProviderByStockId $indexDataProviderByStockId,
- IndexTableSwitcherInterface $indexTableSwitcher,
- DefaultStockProviderInterface $defaultStockProvider
+ IndexTableSwitcherInterface $indexTableSwitcher
) {
$this->getAllStockIds = $getAllStockIds;
$this->indexStructure = $indexStructureHandler;
@@ -83,7 +75,6 @@ public function __construct(
$this->indexNameBuilder = $indexNameBuilder;
$this->indexDataProviderByStockId = $indexDataProviderByStockId;
$this->indexTableSwitcher = $indexTableSwitcher;
- $this->defaultStockProvider = $defaultStockProvider;
}
/**
@@ -117,10 +108,6 @@ public function executeRow(int $stockId): void
public function executeList(array $stockIds): void
{
foreach ($stockIds as $stockId) {
- if ($this->defaultStockProvider->getId() === (int)$stockId) {
- continue;
- }
-
$replicaIndexName = $this->indexNameBuilder
->setIndexId(InventoryIndexer::INDEXER_ID)
->addDimension('stock_', (string)$stockId)
diff --git a/InventoryIndexer/Model/IsProductSalable.php b/InventoryIndexer/Model/IsProductSalable.php
index 608029118a02..9c06aefc9b52 100644
--- a/InventoryIndexer/Model/IsProductSalable.php
+++ b/InventoryIndexer/Model/IsProductSalable.php
@@ -7,6 +7,7 @@
namespace Magento\InventoryIndexer\Model;
+use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\Exception\LocalizedException;
use Magento\InventorySalesApi\Api\IsProductSalableInterface;
use Magento\InventorySalesApi\Model\GetStockItemDataInterface;
@@ -21,21 +22,30 @@ class IsProductSalable implements IsProductSalableInterface
* @var GetStockItemDataInterface
*/
private $getStockItemData;
+
/**
* @var LoggerInterface
*/
private $logger;
+ /**
+ * @var ScopeConfigInterface
+ */
+ private $config;
+
/**
* @param GetStockItemDataInterface $getStockItemData
+ * @param ScopeConfigInterface $config
* @param LoggerInterface $logger
*/
public function __construct(
GetStockItemDataInterface $getStockItemData,
+ ScopeConfigInterface $config,
LoggerInterface $logger
) {
$this->getStockItemData = $getStockItemData;
$this->logger = $logger;
+ $this->config = $config;
}
/**
@@ -44,8 +54,9 @@ public function __construct(
public function execute(string $sku, int $stockId): bool
{
try {
+ $showOutOfStock = (int)$this->config->getValue('cataloginventory/options/show_out_of_stock');
$stockItem = $this->getStockItemData->execute($sku, $stockId);
- $isSalable = (bool)($stockItem[GetStockItemDataInterface::IS_SALABLE] ?? false);
+ $isSalable = $showOutOfStock ? true : (bool)($stockItem[GetStockItemDataInterface::IS_SALABLE] ?? false);
} catch (LocalizedException $exception) {
$this->logger->warning(
sprintf(
diff --git a/InventoryIndexer/Model/Queue/UpdateIndexSalabilityStatus/IndexProcessor/GetDataForUpdate.php b/InventoryIndexer/Model/Queue/UpdateIndexSalabilityStatus/IndexProcessor/GetDataForUpdate.php
new file mode 100644
index 000000000000..348c1b170bd3
--- /dev/null
+++ b/InventoryIndexer/Model/Queue/UpdateIndexSalabilityStatus/IndexProcessor/GetDataForUpdate.php
@@ -0,0 +1,71 @@
+getStockItemData = $getStockItemData;
+ }
+
+ /**
+ * Build data for index update.
+ *
+ * @param IsProductSalableResultInterface[] $salabilityData
+ * @param int $stockId
+ * @return bool[] - ['sku' => bool]
+ */
+ public function execute(array $salabilityData, int $stockId): array
+ {
+ $data = [];
+ foreach ($salabilityData as $isProductSalableResult) {
+ $currentStatus = $this->getIndexSalabilityStatus($isProductSalableResult->getSku(), $stockId);
+ if ($isProductSalableResult->isSalable() != $currentStatus && $currentStatus !== null) {
+ $data[$isProductSalableResult->getSku()] = $isProductSalableResult->isSalable();
+ }
+ }
+
+ return $data;
+ }
+
+ /**
+ * Get current index is_salable value.
+ *
+ * @param string $sku
+ * @param int $stockId
+ *
+ * @return bool|null
+ */
+ private function getIndexSalabilityStatus(string $sku, int $stockId): ?bool
+ {
+ try {
+ $data = $this->getStockItemData->execute($sku, $stockId);
+ $isSalable = $data ? (bool)$data[GetStockItemDataInterface::IS_SALABLE] : false;
+ } catch (LocalizedException $e) {
+ $isSalable = null;
+ }
+
+ return $isSalable;
+ }
+}
diff --git a/InventoryIndexer/Model/ResourceModel/GetProductIdsByStockIds.php b/InventoryIndexer/Model/ResourceModel/GetProductIdsByStockIds.php
index e8e3a32cfb9a..6cadf43106a8 100644
--- a/InventoryIndexer/Model/ResourceModel/GetProductIdsByStockIds.php
+++ b/InventoryIndexer/Model/ResourceModel/GetProductIdsByStockIds.php
@@ -8,7 +8,6 @@
namespace Magento\InventoryIndexer\Model\ResourceModel;
use Magento\Framework\App\ResourceConnection;
-use Magento\InventoryCatalogApi\Api\DefaultStockProviderInterface;
use Magento\InventoryIndexer\Indexer\IndexStructure;
use Magento\InventoryIndexer\Model\StockIndexTableNameResolverInterface;
@@ -27,11 +26,6 @@ class GetProductIdsByStockIds
*/
private $stockIndexTableNameResolver;
- /**
- * @var DefaultStockProviderInterface
- */
- private $defaultStockProvider;
-
/**
* @var string
*/
@@ -40,17 +34,14 @@ class GetProductIdsByStockIds
/**
* @param ResourceConnection $resource
* @param StockIndexTableNameResolverInterface $stockIndexTableNameResolver
- * @param DefaultStockProviderInterface $defaultStockProvider
* @param string $productTableName
*/
public function __construct(
ResourceConnection $resource,
StockIndexTableNameResolverInterface $stockIndexTableNameResolver,
- DefaultStockProviderInterface $defaultStockProvider,
string $productTableName
) {
$this->resource = $resource;
- $this->defaultStockProvider = $defaultStockProvider;
$this->stockIndexTableNameResolver = $stockIndexTableNameResolver;
$this->productTableName = $productTableName;
}
@@ -65,11 +56,9 @@ public function execute(array $stockIds): array
{
$productIds = [[]];
foreach ($stockIds as $stockId) {
- if ($this->defaultStockProvider->getId() === (int)$stockId) {
- continue;
- }
$stockIndexTableName = $this->stockIndexTableNameResolver->execute($stockId);
$connection = $this->resource->getConnection();
+
$sql = $connection->select()
->from(['stock_index' => $stockIndexTableName], [])
->join(
diff --git a/InventoryIndexer/Model/ResourceModel/GetStockItemData.php b/InventoryIndexer/Model/ResourceModel/GetStockItemData.php
index ddc4917c6715..d703481d5958 100644
--- a/InventoryIndexer/Model/ResourceModel/GetStockItemData.php
+++ b/InventoryIndexer/Model/ResourceModel/GetStockItemData.php
@@ -9,11 +9,10 @@
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\Exception\LocalizedException;
+use Magento\InventoryCatalogApi\Model\GetProductIdsBySkusInterface;
+use Magento\InventoryIndexer\Indexer\IndexStructure;
use Magento\InventoryIndexer\Model\StockIndexTableNameResolverInterface;
use Magento\InventorySalesApi\Model\GetStockItemDataInterface;
-use Magento\InventoryIndexer\Indexer\IndexStructure;
-use Magento\InventoryCatalogApi\Api\DefaultStockProviderInterface;
-use Magento\InventoryCatalogApi\Model\GetProductIdsBySkusInterface;
/**
* @inheritdoc
@@ -30,11 +29,6 @@ class GetStockItemData implements GetStockItemDataInterface
*/
private $stockIndexTableNameResolver;
- /**
- * @var DefaultStockProviderInterface
- */
- private $defaultStockProvider;
-
/**
* @var GetProductIdsBySkusInterface
*/
@@ -43,18 +37,15 @@ class GetStockItemData implements GetStockItemDataInterface
/**
* @param ResourceConnection $resource
* @param StockIndexTableNameResolverInterface $stockIndexTableNameResolver
- * @param DefaultStockProviderInterface $defaultStockProvider
* @param GetProductIdsBySkusInterface $getProductIdsBySkus
*/
public function __construct(
ResourceConnection $resource,
StockIndexTableNameResolverInterface $stockIndexTableNameResolver,
- DefaultStockProviderInterface $defaultStockProvider,
GetProductIdsBySkusInterface $getProductIdsBySkus
) {
$this->resource = $resource;
$this->stockIndexTableNameResolver = $stockIndexTableNameResolver;
- $this->defaultStockProvider = $defaultStockProvider;
$this->getProductIdsBySkus = $getProductIdsBySkus;
}
@@ -65,34 +56,19 @@ public function execute(string $sku, int $stockId): ?array
{
$connection = $this->resource->getConnection();
$select = $connection->select();
+ $stockItemTableName = $this->stockIndexTableNameResolver->execute($stockId);
+ $select->from(
+ $stockItemTableName,
+ [
+ GetStockItemDataInterface::QUANTITY => IndexStructure::QUANTITY,
+ GetStockItemDataInterface::IS_SALABLE => IndexStructure::IS_SALABLE,
+ ]
+ )->where(IndexStructure::SKU . ' = ?', $sku);
- if ($this->defaultStockProvider->getId() === $stockId) {
- $productId = current($this->getProductIdsBySkus->execute([$sku]));
- $stockItemTableName = $this->resource->getTableName('cataloginventory_stock_status');
- $select->from(
- $stockItemTableName,
- [
- GetStockItemDataInterface::QUANTITY => 'qty',
- GetStockItemDataInterface::IS_SALABLE => 'stock_status',
- ]
- )->where('product_id = ?', $productId);
-
+ try {
return $connection->fetchRow($select) ?: null;
- } else {
- $stockItemTableName = $this->stockIndexTableNameResolver->execute($stockId);
- $select->from(
- $stockItemTableName,
- [
- GetStockItemDataInterface::QUANTITY => IndexStructure::QUANTITY,
- GetStockItemDataInterface::IS_SALABLE => IndexStructure::IS_SALABLE,
- ]
- )->where(IndexStructure::SKU . ' = ?', $sku);
-
- try {
- return $connection->fetchRow($select) ?: null;
- } catch (\Exception $e) {
- throw new LocalizedException(__('Could not receive Stock Item data'), $e);
- }
+ } catch (\Exception $e) {
+ throw new LocalizedException(__('Could not receive Stock Item data'), $e);
}
}
}
diff --git a/InventoryIndexer/etc/communication.xml b/InventoryIndexer/etc/communication.xml
index f1e38ed2bbd4..b97f171a8316 100644
--- a/InventoryIndexer/etc/communication.xml
+++ b/InventoryIndexer/etc/communication.xml
@@ -6,13 +6,13 @@
*/
-->
+
+
+
-
-
-
diff --git a/InventoryIndexer/etc/queue_publisher.xml b/InventoryIndexer/etc/queue_publisher.xml
index 6c655d0858d6..7500e484259d 100644
--- a/InventoryIndexer/etc/queue_publisher.xml
+++ b/InventoryIndexer/etc/queue_publisher.xml
@@ -6,13 +6,13 @@
*/
-->
-
+
-
+
-
+
diff --git a/InventoryIndexer/etc/queue_topology.xml b/InventoryIndexer/etc/queue_topology.xml
index 3437ff521905..ef83ac53cfea 100644
--- a/InventoryIndexer/etc/queue_topology.xml
+++ b/InventoryIndexer/etc/queue_topology.xml
@@ -6,9 +6,9 @@
*/
-->
-
+
+
-
diff --git a/InventorySales/Model/IsProductSalableCondition/IsSalableWithReservationsCondition.php b/InventorySales/Model/IsProductSalableCondition/IsSalableWithReservationsCondition.php
index cc8dee8b5ba2..be9dfdb59999 100644
--- a/InventorySales/Model/IsProductSalableCondition/IsSalableWithReservationsCondition.php
+++ b/InventorySales/Model/IsProductSalableCondition/IsSalableWithReservationsCondition.php
@@ -71,17 +71,16 @@ public function __construct(
*/
public function execute(string $sku, int $stockId): bool
{
+ $productType = $this->getProductTypesBySkus->execute([$sku])[$sku];
+ if (false === $this->isSourceItemManagementAllowedForProductType->execute($productType)) {
+ return true;
+ }
$stockItemData = $this->getStockItemData->execute($sku, $stockId);
if (null === $stockItemData) {
// Sku is not assigned to Stock
return false;
}
- $productType = $this->getProductTypesBySkus->execute([$sku])[$sku];
- if (false === $this->isSourceItemManagementAllowedForProductType->execute($productType)) {
- return (bool)$stockItemData[GetStockItemDataInterface::IS_SALABLE];
- }
-
/** @var StockItemConfigurationInterface $stockItemConfiguration */
$stockItemConfiguration = $this->getStockItemConfiguration->execute($sku, $stockId);
$qtyWithReservation = $stockItemData[GetStockItemDataInterface::QUANTITY] +
diff --git a/InventorySales/Model/ResourceModel/IsStockItemSalableCondition/IsStockItemSalableConditionChain.php b/InventorySales/Model/ResourceModel/IsStockItemSalableCondition/IsStockItemSalableConditionChain.php
index fb93eb8a3b5c..d549cfd60795 100644
--- a/InventorySales/Model/ResourceModel/IsStockItemSalableCondition/IsStockItemSalableConditionChain.php
+++ b/InventorySales/Model/ResourceModel/IsStockItemSalableCondition/IsStockItemSalableConditionChain.php
@@ -7,6 +7,7 @@
namespace Magento\InventorySales\Model\ResourceModel\IsStockItemSalableCondition;
+use Magento\CatalogInventory\Api\StockConfigurationInterface;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\DB\Select;
use Magento\Framework\Exception\LocalizedException;
@@ -26,14 +27,20 @@ class IsStockItemSalableConditionChain implements GetIsStockItemSalableCondition
*/
private $resourceConnection;
+ /**
+ * @var StockConfigurationInterface
+ */
+ private $configuration;
+
/**
* @param ResourceConnection $resourceConnection
+ * @param StockConfigurationInterface $configuration
* @param array $conditions
- *
* @throws LocalizedException
*/
public function __construct(
ResourceConnection $resourceConnection,
+ StockConfigurationInterface $configuration,
array $conditions = []
) {
foreach ($conditions as $getIsSalableCondition) {
@@ -45,15 +52,15 @@ public function __construct(
}
$this->resourceConnection = $resourceConnection;
$this->conditions = $conditions;
+ $this->configuration = $configuration;
}
/**
* @inheritdoc
- * @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function execute(Select $select): string
{
- if (empty($this->conditions)) {
+ if (empty($this->conditions) || !$this->configuration->getManageStock()) {
return '1';
}
diff --git a/InventorySales/Test/Integration/GetStockItemData/BackorderConditionTest.php b/InventorySales/Test/Integration/GetStockItemData/BackorderConditionTest.php
index 8ba8afeb0caa..e2f1f188387c 100644
--- a/InventorySales/Test/Integration/GetStockItemData/BackorderConditionTest.php
+++ b/InventorySales/Test/Integration/GetStockItemData/BackorderConditionTest.php
@@ -185,7 +185,7 @@ public function backordersGlobalEnabledDataProvider(): array
return [
['SKU-1', 10, [GetStockItemDataInterface::QUANTITY => 8.5, GetStockItemDataInterface::IS_SALABLE => 1]],
['SKU-2', 10, null],
- ['SKU-3', 10, [GetStockItemDataInterface::QUANTITY => 0, GetStockItemDataInterface::IS_SALABLE => 1]],
+ ['SKU-3', 10, [GetStockItemDataInterface::QUANTITY => 0, GetStockItemDataInterface::IS_SALABLE => 0]],
];
}
@@ -230,7 +230,7 @@ public function backordersEnabledDataProvider(): array
10,
StockItemConfigurationInterface::BACKORDERS_YES_NONOTIFY,
[
- GetStockItemDataInterface::QUANTITY => 0, GetStockItemDataInterface::IS_SALABLE => 1
+ GetStockItemDataInterface::QUANTITY => 0, GetStockItemDataInterface::IS_SALABLE => 0
]
],
[
@@ -238,7 +238,7 @@ public function backordersEnabledDataProvider(): array
10,
StockItemConfigurationInterface::BACKORDERS_YES_NOTIFY,
[
- GetStockItemDataInterface::QUANTITY => 0, GetStockItemDataInterface::IS_SALABLE => 1
+ GetStockItemDataInterface::QUANTITY => 0, GetStockItemDataInterface::IS_SALABLE => 0
]
],
];
@@ -271,7 +271,6 @@ private function setStockItemBackorders(string $sku, int $backordersStatus): voi
$stockItemSearchCriteria->setProductsFilter($product->getId());
$stockItemsCollection = $this->stockItemRepository->getList($stockItemSearchCriteria);
- /** @var StockItemInterface $legacyStockItem */
$legacyStockItem = current($stockItemsCollection->getItems());
$legacyStockItem->setBackorders($backordersStatus);
$legacyStockItem->setUseConfigBackorders(false);
diff --git a/InventorySales/Test/Integration/Stock/GetProductSalableQtyTest.php b/InventorySales/Test/Integration/Stock/GetProductSalableQtyTest.php
index 11e0e6f226fa..1ee21419101b 100644
--- a/InventorySales/Test/Integration/Stock/GetProductSalableQtyTest.php
+++ b/InventorySales/Test/Integration/Stock/GetProductSalableQtyTest.php
@@ -7,8 +7,12 @@
namespace Magento\InventorySales\Test\Integration\Stock;
-use Magento\InventoryReservationsApi\Model\CleanupReservationsInterface;
+use Magento\CatalogInventory\Api\StockItemRepositoryInterface;
+use Magento\InventoryApi\Api\GetSourceItemsBySkuInterface;
+use Magento\InventoryApi\Api\SourceItemsSaveInterface;
+use Magento\InventoryConfiguration\Model\GetLegacyStockItem;
use Magento\InventoryReservationsApi\Model\AppendReservationsInterface;
+use Magento\InventoryReservationsApi\Model\CleanupReservationsInterface;
use Magento\InventoryReservationsApi\Model\ReservationBuilderInterface;
use Magento\InventorySalesApi\Api\GetProductSalableQtyInterface;
use Magento\TestFramework\Helper\Bootstrap;
@@ -119,4 +123,82 @@ public function testGetProductQuantityIfReservationsArePresent()
$this->reservationBuilder->setStockId(10)->setSku('SKU-1')->setQuantity(3.5)->build(),
]);
}
+
+ /**
+ * Verify 'Out of stock' source items will show '0' salable qty in case global 'out of stock' threshold is negative.
+ *
+ * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/products.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/sources.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/stocks.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/stock_source_links.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/source_items.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventoryIndexer/Test/_files/reindex_inventory.php
+ *
+ * @magentoConfigFixture default_store cataloginventory/item_options/min_qty -10
+ * @magentoConfigFixture default_store cataloginventory/item_options/backorders 1
+ *
+ * @magentoDbIsolation disabled
+ */
+ public function testGetSalableQuantityWithGlobalBackordersAndOutOfStockSourceItems()
+ {
+ $sku = 'SKU-2';
+ self::assertEquals(15, $this->getProductSalableQty->execute($sku, 20));
+ $this->setSourceItemsToOutOfStock($sku);
+ self::assertEquals(0, $this->getProductSalableQty->execute($sku, 20));
+ }
+
+ /**
+ * Verify 'Out of stock' source items will show '0' salable qty if stock item 'out of stock' threshold is negative.
+ *
+ * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/products.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/sources.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/stocks.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/stock_source_links.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/source_items.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventoryIndexer/Test/_files/reindex_inventory.php
+ *
+ * @magentoDbIsolation disabled
+ */
+ public function testGetSalableQuantityWithBackordersAndOutOfStockSourceItems()
+ {
+ $sku = 'SKU-2';
+ $this->enableBackorders();
+ self::assertEquals(15, $this->getProductSalableQty->execute($sku, 20));
+ $this->setSourceItemsToOutOfStock($sku);
+ self::assertEquals(0, $this->getProductSalableQty->execute($sku, 20));
+ }
+
+ /**
+ * Enable backorders and negative 'out or stock' threshold for stock item.
+ *
+ * @return void
+ */
+ private function enableBackorders(): void
+ {
+ $getLegacyItem = Bootstrap::getObjectManager()->get(GetLegacyStockItem::class);
+ $stockItemRepository = Bootstrap::getObjectManager()->get(StockItemRepositoryInterface::class);
+ $legacyStockItem = $getLegacyItem->execute('SKU-2');
+ $legacyStockItem->setBackorders(1);
+ $legacyStockItem->setUseConfigBackorders(false);
+ $legacyStockItem->setMinQty(-10);
+ $legacyStockItem->setUseConfigMinQty(false);
+ $stockItemRepository->save($legacyStockItem);
+ }
+
+ /**
+ * Set source items for given product 'out of stock' status.
+ *
+ * @param string $sku
+ * @return void
+ */
+ private function setSourceItemsToOutOfStock(string $sku): void
+ {
+ $sourceItemsSave = Bootstrap::getObjectManager()->get(SourceItemsSaveInterface::class);
+ $getSourceItems = Bootstrap::getObjectManager()->get(GetSourceItemsBySkuInterface::class);
+ $sourceItems = $getSourceItems->execute($sku);
+ foreach ($sourceItems as $sourceItem) {
+ $sourceItem->setStatus(0);
+ }
+ $sourceItemsSave->execute($sourceItems);
+ }
}
diff --git a/InventorySetupFixtureGenerator/Plugin/Setup/Model/FixtureGenerator/EntityGeneratorFactory/UpdateCustomTableMapPlugin.php b/InventorySetupFixtureGenerator/Plugin/Setup/Model/FixtureGenerator/EntityGeneratorFactory/UpdateCustomTableMapPlugin.php
index 11349256c774..e9a13d247cbc 100644
--- a/InventorySetupFixtureGenerator/Plugin/Setup/Model/FixtureGenerator/EntityGeneratorFactory/UpdateCustomTableMapPlugin.php
+++ b/InventorySetupFixtureGenerator/Plugin/Setup/Model/FixtureGenerator/EntityGeneratorFactory/UpdateCustomTableMapPlugin.php
@@ -22,6 +22,13 @@ class UpdateCustomTableMapPlugin
*/
private $sourceItems = [];
+ /**
+ * Processed stock statuses.
+ *
+ * @var array
+ */
+ private $stockStatuses = [];
+
/**
* Inject inventory_source_item table data to FixtureGenerator\EntityGeneratorFactory arguments.
*
@@ -49,6 +56,21 @@ public function beforeCreate(
return $binds;
},
];
+ $data['customTableMap']['inventory_stock_1'] = [
+ 'entity_id_field' => EntityGenerator::SKIP_ENTITY_ID_BINDING,
+ 'handler' => function ($productId, $entityNumber, $fixture, $binds) {
+ foreach ($binds as &$bind) {
+ $sku = $fixture['sku']($productId, $entityNumber);
+ if (in_array($sku, $this->stockStatuses)) {
+ return [];
+ }
+ $bind['sku'] = $sku;
+ $this->stockStatuses[] = $sku;
+ }
+
+ return $binds;
+ },
+ ];
return [$data];
}
diff --git a/dev/tests/integration/_files/Magento/TestModuleInventoryStateCache/Plugin/Catalog/Model/ProductRepository/GetNonCachedProductsPlugin.php b/dev/tests/integration/_files/Magento/TestModuleInventoryStateCache/Plugin/Catalog/Model/ProductRepository/GetNonCachedProductsPlugin.php
new file mode 100644
index 000000000000..3b712ddd9cfc
--- /dev/null
+++ b/dev/tests/integration/_files/Magento/TestModuleInventoryStateCache/Plugin/Catalog/Model/ProductRepository/GetNonCachedProductsPlugin.php
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
diff --git a/dev/tests/integration/_files/Magento/TestModuleInventoryStateCache/etc/frontend/di.xml b/dev/tests/integration/_files/Magento/TestModuleInventoryStateCache/etc/frontend/di.xml
index 30c34c0c4002..b2db5d70ab16 100644
--- a/dev/tests/integration/_files/Magento/TestModuleInventoryStateCache/etc/frontend/di.xml
+++ b/dev/tests/integration/_files/Magento/TestModuleInventoryStateCache/etc/frontend/di.xml
@@ -10,4 +10,8 @@
+
+
+