diff --git a/app/code/Magento/InventoryConfigurableProduct/Model/ResourceModel/IsStockItemSalableCondition/GetConfigurableCondition.php b/app/code/Magento/InventoryConfigurableProduct/Model/ResourceModel/IsStockItemSalableCondition/GetConfigurableCondition.php
new file mode 100644
index 000000000000..aca1cd9ca531
--- /dev/null
+++ b/app/code/Magento/InventoryConfigurableProduct/Model/ResourceModel/IsStockItemSalableCondition/GetConfigurableCondition.php
@@ -0,0 +1,29 @@
+stockIndexTableNameResolver = $stockIndexTableNameResolver;
+ $this->stockConfig = $stockConfig;
+ $this->getStockIdForCurrentWebsite = $getStockIdForCurrentWebsite;
+ $this->storeManager = $storeManager;
+ $this->stockResolver = $stockResolver;
+ }
+
+ /**
+ * @param Select $select
+ * @return Select
+ */
+ public function process(Select $select)
+ {
+ if (!$this->stockConfig->isShowOutOfStock()) {
+ $websiteCode = $this->storeManager->getWebsite()->getCode();
+ $stock = $this->stockResolver->get(SalesChannelInterface::TYPE_WEBSITE, $websiteCode);
+ $stockId = (int)$stock->getStockId();
+
+ $stockTable = $this->stockIndexTableNameResolver->execute($stockId);
+
+ /** @var Select $select */
+ $select->join(
+ ['stock' => $stockTable],
+ sprintf('stock.sku = %s.sku', BaseSelectProcessorInterface::PRODUCT_TABLE_ALIAS),
+ []
+ )->where('stock.is_salable = ?', 1);
+ }
+
+ return $select;
+ }
+}
diff --git a/app/code/Magento/InventoryConfigurableProduct/Test/Integration/Price/FinalPriceResolverTest.php b/app/code/Magento/InventoryConfigurableProduct/Test/Integration/Price/FinalPriceResolverTest.php
new file mode 100644
index 000000000000..e9ea4cb55831
--- /dev/null
+++ b/app/code/Magento/InventoryConfigurableProduct/Test/Integration/Price/FinalPriceResolverTest.php
@@ -0,0 +1,116 @@
+storeManager = Bootstrap::getObjectManager()->get(StoreManagerInterface::class);
+ $this->productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class);
+ $this->storeCodeBefore = $this->storeManager->getStore()->getCode();
+ $finalPrice = Bootstrap::getObjectManager()->get(FinalPriceResolver::class);
+
+ $this->configurablePriceResolver = Bootstrap::getObjectManager()->create(
+ ConfigurablePriceResolver::class,
+ ['priceResolver' => $finalPrice]
+ );
+ }
+
+ // @codingStandardsIgnoreStart
+ /**
+ * @magentoDataFixture ../../../../app/code/Magento/InventorySalesApi/Test/_files/websites_with_stores.php
+ * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_attribute.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventoryConfigurableProduct/Test/_files/product_configurable.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/InventoryConfigurableProduct/Test/_files/source_items_configurable.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventorySalesApi/Test/_files/stock_website_sales_channels.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventoryIndexer/Test/_files/reindex_inventory.php
+ * @return void
+ */
+ // @codingStandardsIgnoreEnd
+ public function testResolvePriceWithAllChildren()
+ {
+ $this->storeManager->setCurrentStore('store_for_us_website');
+
+ $configurableProduct = $this->productRepository->get('configurable', false, null, true);
+ $actualPrice = $this->configurablePriceResolver->resolvePrice($configurableProduct);
+
+ self::assertEquals(10, $actualPrice);
+ }
+
+ /// @codingStandardsIgnoreStart
+ /**
+ * @magentoDataFixture ../../../../app/code/Magento/InventorySalesApi/Test/_files/websites_with_stores.php
+ * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_attribute.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventoryConfigurableProduct/Test/_files/product_configurable.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/InventoryConfigurableProduct/Test/_files/source_items_configurable.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventoryConfigurableProduct/Test/_files/set_product_configurable_out_of_stock.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventorySalesApi/Test/_files/stock_website_sales_channels.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventoryIndexer/Test/_files/reindex_inventory.php
+ * @return void
+ */
+ // @codingStandardsIgnoreEnd
+ public function testResolvePriceIfOneOfChildIsOutOfStock()
+ {
+ $this->storeManager->setCurrentStore('store_for_us_website');
+
+ $configurableProduct = $this->productRepository->get('configurable', false, null, true);
+ $actualPrice = $this->configurablePriceResolver->resolvePrice($configurableProduct);
+ self::assertEquals(20, $actualPrice);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown()
+ {
+ parent::tearDown();
+
+ if (null !== $this->storeCodeBefore) {
+ $this->storeManager->setCurrentStore($this->storeCodeBefore);
+ }
+ }
+}
diff --git a/app/code/Magento/InventoryConfigurableProduct/Test/Integration/Price/LowestPriceOptionProviderTest.php b/app/code/Magento/InventoryConfigurableProduct/Test/Integration/Price/LowestPriceOptionProviderTest.php
new file mode 100644
index 000000000000..bf8e6a22c137
--- /dev/null
+++ b/app/code/Magento/InventoryConfigurableProduct/Test/Integration/Price/LowestPriceOptionProviderTest.php
@@ -0,0 +1,133 @@
+storeManager = Bootstrap::getObjectManager()->get(StoreManagerInterface::class);
+ $this->productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class);
+ $this->storeCodeBefore = $this->storeManager->getStore()->getCode();
+ $finalPrice = Bootstrap::getObjectManager()->get(FinalPriceResolver::class);
+
+ $this->configurablePriceResolver = Bootstrap::getObjectManager()->create(
+ ConfigurablePriceResolver::class,
+ ['priceResolver' => $finalPrice]
+ );
+ }
+
+ // @codingStandardsIgnoreStart
+ /**
+ * @magentoDataFixture ../../../../app/code/Magento/InventorySalesApi/Test/_files/websites_with_stores.php
+ * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_attribute.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventoryConfigurableProduct/Test/_files/product_configurable.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/InventoryConfigurableProduct/Test/_files/source_items_configurable.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventorySalesApi/Test/_files/stock_website_sales_channels.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventoryIndexer/Test/_files/reindex_inventory.php
+ * @return void
+ */
+ // @codingStandardsIgnoreEnd
+ public function testGetProductsWithAllChildren()
+ {
+ $this->storeManager->setCurrentStore('store_for_us_website');
+
+ $configurableProduct = $this->productRepository->get('configurable', false, null, true);
+ $lowestPriceChildrenProducts = $this->createLowestPriceOptionsProvider()->getProducts($configurableProduct);
+ self::assertCount(1, $lowestPriceChildrenProducts);
+ $lowestPriceChildrenProduct = reset($lowestPriceChildrenProducts);
+ self::assertEquals(10, $lowestPriceChildrenProduct->getPrice());
+ }
+
+ /// @codingStandardsIgnoreStart
+ /**
+ * @magentoDataFixture ../../../../app/code/Magento/InventorySalesApi/Test/_files/websites_with_stores.php
+ * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_attribute.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventoryConfigurableProduct/Test/_files/product_configurable.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/InventoryConfigurableProduct/Test/_files/source_items_configurable.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventoryConfigurableProduct/Test/_files/set_product_configurable_out_of_stock.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventorySalesApi/Test/_files/stock_website_sales_channels.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventoryIndexer/Test/_files/reindex_inventory.php
+ * @return void
+ */
+ // @codingStandardsIgnoreEnd
+ public function testGetProductsIfOneOfChildIsOutOfStock()
+ {
+ $this->storeManager->setCurrentStore('store_for_us_website');
+
+ $configurableProduct = $this->productRepository->get('configurable', false, null, true);
+ $lowestPriceChildrenProducts = $this->createLowestPriceOptionsProvider()->getProducts($configurableProduct);
+ self::assertCount(1, $lowestPriceChildrenProducts);
+ $lowestPriceChildrenProduct = reset($lowestPriceChildrenProducts);
+ self::assertEquals(20, $lowestPriceChildrenProduct->getPrice());
+ }
+
+ /**
+ * As LowestPriceOptionsProviderInterface used multiple times in scope
+ * of one test we need to always recreate it and prevent internal caching in property
+ *
+ * @return LowestPriceOptionsProviderInterface
+ */
+ private function createLowestPriceOptionsProvider()
+ {
+ return Bootstrap::getObjectManager()->create(
+ LowestPriceOptionsProviderInterface::class
+ );
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown()
+ {
+ parent::tearDown();
+
+ if (null !== $this->storeCodeBefore) {
+ $this->storeManager->setCurrentStore($this->storeCodeBefore);
+ }
+ }
+}
diff --git a/app/code/Magento/InventoryConfigurableProduct/Test/Integration/Price/RegularPriceResolverTest.php b/app/code/Magento/InventoryConfigurableProduct/Test/Integration/Price/RegularPriceResolverTest.php
new file mode 100644
index 000000000000..b088c7da4699
--- /dev/null
+++ b/app/code/Magento/InventoryConfigurableProduct/Test/Integration/Price/RegularPriceResolverTest.php
@@ -0,0 +1,116 @@
+storeManager = Bootstrap::getObjectManager()->get(StoreManagerInterface::class);
+ $this->productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class);
+ $this->storeCodeBefore = $this->storeManager->getStore()->getCode();
+ $regularPrice = Bootstrap::getObjectManager()->get(RegularPriceResolver::class);
+
+ $this->configurablePriceResolver = Bootstrap::getObjectManager()->create(
+ ConfigurablePriceResolver::class,
+ ['priceResolver' => $regularPrice]
+ );
+ }
+
+ // @codingStandardsIgnoreStart
+ /**
+ * @magentoDataFixture ../../../../app/code/Magento/InventorySalesApi/Test/_files/websites_with_stores.php
+ * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_attribute.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventoryConfigurableProduct/Test/_files/product_configurable.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/InventoryConfigurableProduct/Test/_files/source_items_configurable.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventorySalesApi/Test/_files/stock_website_sales_channels.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventoryIndexer/Test/_files/reindex_inventory.php
+ * @return void
+ */
+ // @codingStandardsIgnoreEnd
+ public function testResolvePriceWithAllChildren()
+ {
+ $this->storeManager->setCurrentStore('store_for_us_website');
+
+ $configurableProduct = $this->productRepository->get('configurable', false, null, true);
+ $actualPrice = $this->configurablePriceResolver->resolvePrice($configurableProduct);
+
+ self::assertEquals(10, $actualPrice);
+ }
+
+ /// @codingStandardsIgnoreStart
+ /**
+ * @magentoDataFixture ../../../../app/code/Magento/InventorySalesApi/Test/_files/websites_with_stores.php
+ * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_attribute.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventoryConfigurableProduct/Test/_files/product_configurable.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/InventoryConfigurableProduct/Test/_files/source_items_configurable.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventoryConfigurableProduct/Test/_files/set_product_configurable_out_of_stock.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventorySalesApi/Test/_files/stock_website_sales_channels.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventoryIndexer/Test/_files/reindex_inventory.php
+ * @return void
+ */
+ // @codingStandardsIgnoreEnd
+ public function testResolvePriceIfOneOfChildIsOutOfStock()
+ {
+ $this->storeManager->setCurrentStore('store_for_us_website');
+
+ $configurableProduct = $this->productRepository->get('configurable', false, null, true);
+ $actualPrice = $this->configurablePriceResolver->resolvePrice($configurableProduct);
+ self::assertEquals(20, $actualPrice);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown()
+ {
+ parent::tearDown();
+
+ if (null !== $this->storeCodeBefore) {
+ $this->storeManager->setCurrentStore($this->storeCodeBefore);
+ }
+ }
+}
diff --git a/app/code/Magento/InventoryConfigurableProduct/Test/Integration/Price/SpecialPriceTest.php b/app/code/Magento/InventoryConfigurableProduct/Test/Integration/Price/SpecialPriceTest.php
new file mode 100644
index 000000000000..f925910dfe67
--- /dev/null
+++ b/app/code/Magento/InventoryConfigurableProduct/Test/Integration/Price/SpecialPriceTest.php
@@ -0,0 +1,130 @@
+storeManager = Bootstrap::getObjectManager()->get(StoreManagerInterface::class);
+ $this->productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class);
+ $this->storeCodeBefore = $this->storeManager->getStore()->getCode();
+ $finalPrice = Bootstrap::getObjectManager()->get(FinalPriceResolver::class);
+
+ $this->configurablePriceResolver = Bootstrap::getObjectManager()->create(
+ ConfigurablePriceResolver::class,
+ ['priceResolver' => $finalPrice]
+ );
+ }
+
+ // @codingStandardsIgnoreStart
+ /**
+ * @magentoDataFixture ../../../../app/code/Magento/InventorySalesApi/Test/_files/websites_with_stores.php
+ * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_attribute.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventoryConfigurableProduct/Test/_files/product_configurable.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/InventoryConfigurableProduct/Test/_files/source_items_configurable.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventorySalesApi/Test/_files/stock_website_sales_channels.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventoryIndexer/Test/_files/reindex_inventory.php
+ * @return void
+ */
+ // @codingStandardsIgnoreEnd
+ public function testResolvePrice()
+ {
+ $this->storeManager->setCurrentStore('store_for_us_website');
+
+ $specialPrice = 2;
+
+ /** @var Product $childProduct */
+ $childProduct = $this->productRepository->get('simple_10', true);
+ $childProduct->setData('special_price', $specialPrice);
+ $this->productRepository->save($childProduct);
+
+ $configurableProduct = $this->productRepository->get('configurable', false, null, true);
+ $actualPrice = $this->configurablePriceResolver->resolvePrice($configurableProduct);
+
+ self::assertEquals($specialPrice, $actualPrice);
+ }
+
+ // @codingStandardsIgnoreStart
+ /**
+ * @magentoDataFixture ../../../../app/code/Magento/InventorySalesApi/Test/_files/websites_with_stores.php
+ * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_attribute.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventoryConfigurableProduct/Test/_files/product_configurable.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/InventoryConfigurableProduct/Test/_files/source_items_configurable.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventoryConfigurableProduct/Test/_files/set_product_configurable_out_of_stock.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventorySalesApi/Test/_files/stock_website_sales_channels.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventoryIndexer/Test/_files/reindex_inventory.php
+ * @return void
+ */
+ // @codingStandardsIgnoreEnd
+ public function testResolvePriceIfChildWithSpecialPriceOutOfStock()
+ {
+ $this->storeManager->setCurrentStore('store_for_us_website');
+
+ /** @var Product $childProduct */
+ $childProduct = $this->productRepository->get('simple_10', true);
+ $childProduct->setData('special_price', 2);
+ $this->productRepository->save($childProduct);
+
+ $configurableProduct = $this->productRepository->get('configurable', false, null, true);
+ $actualPrice = $this->configurablePriceResolver->resolvePrice($configurableProduct);
+
+ self::assertEquals(20, $actualPrice);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown()
+ {
+ parent::tearDown();
+
+ if (null !== $this->storeCodeBefore) {
+ $this->storeManager->setCurrentStore($this->storeCodeBefore);
+ }
+ }
+}
diff --git a/app/code/Magento/InventoryConfigurableProduct/Test/Integration/Price/TierPriceTest.php b/app/code/Magento/InventoryConfigurableProduct/Test/Integration/Price/TierPriceTest.php
new file mode 100644
index 000000000000..2afcb44815c4
--- /dev/null
+++ b/app/code/Magento/InventoryConfigurableProduct/Test/Integration/Price/TierPriceTest.php
@@ -0,0 +1,170 @@
+storeManager = Bootstrap::getObjectManager()->get(StoreManagerInterface::class);
+ $this->productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class);
+ $this->storeCodeBefore = $this->storeManager->getStore()->getCode();
+ $finalPrice = Bootstrap::getObjectManager()->get(FinalPriceResolver::class);
+ $this->extensionAttributesFactory = Bootstrap::getObjectManager()->get(ProductTierPriceExtensionFactory::class);
+ $this->tierPriceFactory = Bootstrap::getObjectManager()->get(ProductTierPriceInterfaceFactory::class);
+
+ $this->configurablePriceResolver = Bootstrap::getObjectManager()->create(
+ ConfigurablePriceResolver::class,
+ ['priceResolver' => $finalPrice]
+ );
+ }
+
+ // @codingStandardsIgnoreStart
+ /**
+ * @magentoDataFixture ../../../../app/code/Magento/InventorySalesApi/Test/_files/websites_with_stores.php
+ * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_attribute.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventoryConfigurableProduct/Test/_files/product_configurable.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/InventoryConfigurableProduct/Test/_files/source_items_configurable.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventorySalesApi/Test/_files/stock_website_sales_channels.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventoryIndexer/Test/_files/reindex_inventory.php
+ * @return void
+ */
+ // @codingStandardsIgnoreEnd
+ public function testResolvePrice()
+ {
+ $this->storeManager->setCurrentStore('store_for_us_website');
+
+ $tierPrice = 3;
+
+ /** @var Product $simpleProduct */
+ $simpleProduct = $this->productRepository->get('simple_10', true);
+
+ $simpleProduct->setTierPrice([
+ [
+ 'website_id' => 0,
+ 'cust_group' => \Magento\Customer\Model\Group::CUST_GROUP_ALL,
+ 'price_qty' => 1,
+ 'price' => $tierPrice,
+ ],
+ ]);
+ $this->productRepository->save($simpleProduct);
+
+ $configurableProduct = $this->productRepository->get(
+ 'configurable',
+ false,
+ $this->storeManager->getStore()->getStoreId(),
+ true
+ );
+ $actualPrice = $this->configurablePriceResolver->resolvePrice($configurableProduct);
+
+ self::assertEquals($tierPrice, $actualPrice);
+ }
+
+ // @codingStandardsIgnoreStart
+ /**
+ * @magentoDataFixture ../../../../app/code/Magento/InventorySalesApi/Test/_files/websites_with_stores.php
+ * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_attribute.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventoryConfigurableProduct/Test/_files/product_configurable.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/InventoryConfigurableProduct/Test/_files/source_items_configurable.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventoryConfigurableProduct/Test/_files/set_product_configurable_out_of_stock.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventorySalesApi/Test/_files/stock_website_sales_channels.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventoryIndexer/Test/_files/reindex_inventory.php
+ * @return void
+ */
+ // @codingStandardsIgnoreEnd
+ public function testResolvePriceIfChildWithTierPriceIsOutOfStock()
+ {
+ $this->storeManager->setCurrentStore('store_for_us_website');
+
+ /** @var Product $simpleProduct */
+ $simpleProduct = $this->productRepository->get('simple_10', true);
+
+ $simpleProduct->setTierPrice([
+ [
+ 'website_id' => 0,
+ 'cust_group' => \Magento\Customer\Model\Group::CUST_GROUP_ALL,
+ 'price_qty' => 1,
+ 'price' => 3,
+ ],
+ ]);
+ $this->productRepository->save($simpleProduct);
+
+ $configurableProduct = $this->productRepository->get(
+ 'configurable',
+ false,
+ $this->storeManager->getStore()->getStoreId(),
+ true
+ );
+ $actualPrice = $this->configurablePriceResolver->resolvePrice($configurableProduct);
+
+ self::assertEquals(20, $actualPrice);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown()
+ {
+ parent::tearDown();
+
+ if (null !== $this->storeCodeBefore) {
+ $this->storeManager->setCurrentStore($this->storeCodeBefore);
+ }
+ }
+}
diff --git a/app/code/Magento/InventoryConfigurableProduct/Test/_files/product_configurable.php b/app/code/Magento/InventoryConfigurableProduct/Test/_files/product_configurable.php
new file mode 100644
index 000000000000..6e00a1b5a604
--- /dev/null
+++ b/app/code/Magento/InventoryConfigurableProduct/Test/_files/product_configurable.php
@@ -0,0 +1,151 @@
+reinitialize();
+
+/** @var ProductRepositoryInterface $productRepository */
+$productRepository = Bootstrap::getObjectManager()->create(ProductRepositoryInterface::class);
+
+/** @var $installer CategorySetup */
+$installer = Bootstrap::getObjectManager()->create(CategorySetup::class);
+
+/** @var Website $website */
+$website = Bootstrap::getObjectManager()->create(Website::class);
+$website->load('us_website', 'code');
+$websiteIds = [$website->getId()];
+
+/** @var Config $eavConfig */
+$eavConfig = Bootstrap::getObjectManager()->create(Config::class);
+$attribute = $eavConfig->getAttribute(\Magento\Catalog\Model\Product::ENTITY, 'test_configurable');
+
+/* Create simple products per each option value*/
+/** @var AttributeOptionInterface[] $options */
+$options = $attribute->getOptions();
+
+$attributeValues = [];
+$attributeSetId = $installer->getAttributeSetId('catalog_product', 'Default');
+$associatedProductIds = [];
+$productIds = [10, 20];
+array_shift($options); //remove the first option which is empty
+
+foreach ($options as $option) {
+ /** @var $product Product */
+ $product = Bootstrap::getObjectManager()->create(Product::class);
+ $productId = array_shift($productIds);
+ $product->setTypeId(Type::TYPE_SIMPLE)
+ ->setId($productId)
+ ->setAttributeSetId($attributeSetId)
+ ->setWebsiteIds($websiteIds)
+ ->setName('Configurable Option' . $option->getLabel())
+ ->setSku('simple_' . $productId)
+ ->setPrice($productId)
+ ->setTestConfigurable($option->getValue())
+ ->setVisibility(Visibility::VISIBILITY_NOT_VISIBLE)
+ ->setStatus(Status::STATUS_ENABLED)
+ ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]);
+
+ $product = $productRepository->save($product);
+
+ /** @var \Magento\CatalogInventory\Model\Stock\Item $stockItem */
+ $stockItem = Bootstrap::getObjectManager()->create(\Magento\CatalogInventory\Model\Stock\Item::class);
+ $stockItem->load($productId, 'product_id');
+
+ if (!$stockItem->getProductId()) {
+ $stockItem->setProductId($productId);
+ }
+ $stockItem->setUseConfigManageStock(1);
+ $stockItem->setQty(1000);
+ $stockItem->setIsQtyDecimal(0);
+ $stockItem->setIsInStock(1);
+ $stockItem->save();
+
+ $attributeValues[] = [
+ 'label' => 'test',
+ 'attribute_id' => $attribute->getId(),
+ 'value_index' => $option->getValue(),
+ ];
+ $associatedProductIds[] = $product->getId();
+}
+
+/** @var $product Product */
+$product = Bootstrap::getObjectManager()->create(Product::class);
+
+/** @var Factory $optionsFactory */
+$optionsFactory = Bootstrap::getObjectManager()->create(Factory::class);
+
+$configurableAttributesData = [
+ [
+ 'attribute_id' => $attribute->getId(),
+ 'code' => $attribute->getAttributeCode(),
+ 'label' => $attribute->getStoreLabel(),
+ 'position' => '0',
+ 'values' => $attributeValues,
+ ],
+];
+
+$configurableOptions = $optionsFactory->create($configurableAttributesData);
+
+$extensionConfigurableAttributes = $product->getExtensionAttributes();
+$extensionConfigurableAttributes->setConfigurableProductOptions($configurableOptions);
+$extensionConfigurableAttributes->setConfigurableProductLinks($associatedProductIds);
+
+$product->setExtensionAttributes($extensionConfigurableAttributes);
+
+// Remove any previously created product with the same id.
+/** @var \Magento\Framework\Registry $registry */
+$registry = Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class);
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+try {
+ $productToDelete = $productRepository->getById(1);
+ $productRepository->delete($productToDelete);
+
+ /** @var \Magento\Quote\Model\ResourceModel\Quote\Item $itemResource */
+ $itemResource = Bootstrap::getObjectManager()->get(\Magento\Quote\Model\ResourceModel\Quote\Item::class);
+ $itemResource->getConnection()->delete(
+ $itemResource->getMainTable(),
+ 'product_id = ' . $productToDelete->getId()
+ );
+} catch (\Exception $e) {
+ // Nothing to remove
+}
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
+
+$product->setTypeId(Configurable::TYPE_CODE)
+ ->setId(1)
+ ->setAttributeSetId($attributeSetId)
+ ->setWebsiteIds($websiteIds)
+ ->setName('Configurable Product')
+ ->setSku('configurable')
+ ->setVisibility(Visibility::VISIBILITY_BOTH)
+ ->setStatus(Status::STATUS_ENABLED)
+ ->setStockData(['use_config_manage_stock' => 1, 'is_in_stock' => 1]);
+
+$productRepository->save($product);
+
+/** @var \Magento\Catalog\Api\CategoryLinkManagementInterface $categoryLinkManagement */
+$categoryLinkManagement = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
+ ->create(\Magento\Catalog\Api\CategoryLinkManagementInterface::class);
+
+$categoryLinkManagement->assignProductToCategories(
+ $product->getSku(),
+ [2]
+);
diff --git a/app/code/Magento/InventoryConfigurableProduct/Test/_files/product_configurable_rollback.php b/app/code/Magento/InventoryConfigurableProduct/Test/_files/product_configurable_rollback.php
new file mode 100644
index 000000000000..eff19d1da23f
--- /dev/null
+++ b/app/code/Magento/InventoryConfigurableProduct/Test/_files/product_configurable_rollback.php
@@ -0,0 +1,34 @@
+get(\Magento\Framework\Registry::class);
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+
+/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */
+$productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
+ ->get(\Magento\Catalog\Api\ProductRepositoryInterface::class);
+
+foreach (['simple_10', 'simple_20', 'configurable'] as $sku) {
+ try {
+ $product = $productRepository->get($sku, false, null, true);
+
+ $stockStatus = $objectManager->create(\Magento\CatalogInventory\Model\Stock\Status::class);
+ $stockStatus->load($product->getEntityId(), 'product_id');
+ $stockStatus->delete();
+
+ $productRepository->delete($product);
+ } catch (\Magento\Framework\Exception\NoSuchEntityException $e) {
+ //Product already removed
+ }
+}
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
diff --git a/app/code/Magento/InventoryConfigurableProduct/Test/_files/set_product_configurable_out_of_stock.php b/app/code/Magento/InventoryConfigurableProduct/Test/_files/set_product_configurable_out_of_stock.php
new file mode 100644
index 000000000000..a23b78169e77
--- /dev/null
+++ b/app/code/Magento/InventoryConfigurableProduct/Test/_files/set_product_configurable_out_of_stock.php
@@ -0,0 +1,30 @@
+create(SourceItemRepositoryInterface::class);
+
+$searchCriteriaBuilder = Bootstrap::getObjectManager()->create(SearchCriteriaBuilder::class);
+$searchCriteria = $searchCriteriaBuilder
+ ->addFilter(SourceItemInterface::SKU, 'simple_10')
+ ->addFilter(SourceItemInterface::SOURCE_CODE, 'us-1')
+ ->create();
+
+$sourceItems = $sourceItemRepository->getList($searchCriteria)->getItems();
+$sourceItem = reset($sourceItems);
+$sourceItem->setQuantity(0);
+$sourceItem->setStatus(0);
+
+/** @var SourceItemsSave $sourceItemSave */
+$sourceItemSave = Bootstrap::getObjectManager()->create(SourceItemsSave::class);
+$sourceItemSave->execute([$sourceItem]);
diff --git a/app/code/Magento/InventoryConfigurableProduct/Test/_files/source_items_configurable.php b/app/code/Magento/InventoryConfigurableProduct/Test/_files/source_items_configurable.php
new file mode 100644
index 000000000000..2af51efd6a91
--- /dev/null
+++ b/app/code/Magento/InventoryConfigurableProduct/Test/_files/source_items_configurable.php
@@ -0,0 +1,50 @@
+get(DataObjectHelper::class);
+/** @var SourceItemInterfaceFactory $sourceItemFactory */
+$sourceItemFactory = Bootstrap::getObjectManager()->get(SourceItemInterfaceFactory::class);
+/** @var SourceItemsSaveInterface $sourceItemsSave */
+$sourceItemsSave = Bootstrap::getObjectManager()->get(SourceItemsSaveInterface::class);
+
+$sourcesItemsData = [
+ [
+ SourceItemInterface::SOURCE_CODE => 'us-1',
+ SourceItemInterface::SKU => 'configurable',
+ SourceItemInterface::QUANTITY => 100,
+ SourceItemInterface::STATUS => SourceItemInterface::STATUS_IN_STOCK,
+ ],
+ [
+ SourceItemInterface::SOURCE_CODE => 'us-1',
+ SourceItemInterface::SKU => 'simple_10',
+ SourceItemInterface::QUANTITY => 100,
+ SourceItemInterface::STATUS => SourceItemInterface::STATUS_IN_STOCK,
+ ],
+ [
+ SourceItemInterface::SOURCE_CODE => 'us-1',
+ SourceItemInterface::SKU => 'simple_20',
+ SourceItemInterface::QUANTITY => 100,
+ SourceItemInterface::STATUS => SourceItemInterface::STATUS_IN_STOCK,
+ ],
+
+];
+
+$sourceItems = [];
+foreach ($sourcesItemsData as $sourceItemData) {
+ /** @var SourceItemInterface $source */
+ $sourceItem = $sourceItemFactory->create();
+ $dataObjectHelper->populateWithArray($sourceItem, $sourceItemData, SourceItemInterface::class);
+ $sourceItems[] = $sourceItem;
+}
+$sourceItemsSave->execute($sourceItems);
diff --git a/app/code/Magento/InventoryConfigurableProduct/Test/_files/source_items_configurable_rollback.php b/app/code/Magento/InventoryConfigurableProduct/Test/_files/source_items_configurable_rollback.php
new file mode 100644
index 000000000000..9ae12ef95ebb
--- /dev/null
+++ b/app/code/Magento/InventoryConfigurableProduct/Test/_files/source_items_configurable_rollback.php
@@ -0,0 +1,34 @@
+get(SourceItemRepositoryInterface::class);
+/** @var SourceItemsDeleteInterface $sourceItemsDelete */
+$sourceItemsDelete = Bootstrap::getObjectManager()->get(SourceItemsDeleteInterface::class);
+/** @var SearchCriteriaBuilder $searchCriteriaBuilder */
+$searchCriteriaBuilder = Bootstrap::getObjectManager()->get(SearchCriteriaBuilder::class);
+
+$searchCriteria = $searchCriteriaBuilder->addFilter(
+ SourceItemInterface::SKU,
+ ['configurable_out_of_stock', 'configurable', 'simple_10', 'simple_20', 'simple_30', 'simple_40'],
+ 'in'
+)->create();
+$sourceItems = $sourceItemRepository->getList($searchCriteria)->getItems();
+
+/**
+ * Tests which are wrapped with MySQL transaction clear all data by transaction rollback.
+ * In that case there is "if" which checks that SKU1, SKU2 and SKU3 still exists in database.
+ */
+if (!empty($sourceItems)) {
+ $sourceItemsDelete->execute($sourceItems);
+}
diff --git a/app/code/Magento/InventoryConfigurableProduct/composer.json b/app/code/Magento/InventoryConfigurableProduct/composer.json
index 2d4b2e48337a..6e348f0778f9 100644
--- a/app/code/Magento/InventoryConfigurableProduct/composer.json
+++ b/app/code/Magento/InventoryConfigurableProduct/composer.json
@@ -6,7 +6,13 @@
"magento/framework": "100.2.*",
"magento/module-catalog": "101.2.*",
"magento/module-configurable-product": "100.3.*",
- "magento/module-ui": "100.3.*"
+ "magento/module-ui": "100.3.*",
+ "magento/module-inventory-sales": "100.0.0-dev",
+ "magento/module-inventory-sales-api": "100.0.0-dev",
+ "magento/module-inventory-catalog": "100.0.0-dev",
+ "magento/module-inventory-indexer": "100.0.0-dev",
+ "magento/module-store": "100.3.*",
+ "magento/module-catalog-inventory": "100.3.*"
},
"type": "magento2-module",
"version": "100.0.0-dev",
diff --git a/app/code/Magento/InventoryConfigurableProduct/etc/di.xml b/app/code/Magento/InventoryConfigurableProduct/etc/di.xml
index abcce17e5cca..f77c7e46dafb 100644
--- a/app/code/Magento/InventoryConfigurableProduct/etc/di.xml
+++ b/app/code/Magento/InventoryConfigurableProduct/etc/di.xml
@@ -12,5 +12,20 @@
uiComponentConfigFactory
-
-
\ No newline at end of file
+
+
+
+
+
+ - Magento\InventoryConfigurableProduct\Model\ResourceModel\Product\StockStatusBaseSelectProcessor
+
+
+
+
+
+
+ - Magento\InventoryConfigurableProduct\Model\ResourceModel\IsStockItemSalableCondition\GetConfigurableCondition
+
+
+
+
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/ProductTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/ProductTest.php
index fc571d13aee5..4254a6ce9c71 100644
--- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/ProductTest.php
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/ProductTest.php
@@ -21,8 +21,6 @@ class ProductTest extends \Magento\TestFramework\TestCase\AbstractBackendControl
*/
public function testSaveActionAssociatedProductIds()
{
- $this->markTestSkipped('https://github.com/magento-engcom/msi/issues/456');
-
$associatedProductIds = ['3', '14', '15', '92'];
$associatedProductIdsJSON = json_encode($associatedProductIds);
$this->getRequest()->setPostValue(
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/ResourceModel/Product/Indexer/Price/ConfigurableTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/ResourceModel/Product/Indexer/Price/ConfigurableTest.php
index a0d00f661188..9d6ef7dfb1ae 100644
--- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/ResourceModel/Product/Indexer/Price/ConfigurableTest.php
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/ResourceModel/Product/Indexer/Price/ConfigurableTest.php
@@ -39,8 +39,6 @@ protected function setUp()
*/
public function testGetProductFinalPriceIfOneOfChildIsDisabled()
{
- $this->markTestSkipped('https://github.com/magento-engcom/msi/issues/456');
-
/** @var Collection $collection */
$collection = Bootstrap::getObjectManager()->get(CollectionFactory::class)
->create();
@@ -75,8 +73,6 @@ public function testGetProductFinalPriceIfOneOfChildIsDisabled()
*/
public function testGetProductFinalPriceIfOneOfChildIsDisabledPerStore()
{
- $this->markTestSkipped('https://github.com/magento-engcom/msi/issues/456');
-
/** @var Collection $collection */
$collection = Bootstrap::getObjectManager()->get(CollectionFactory::class)
->create();
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Price/LowestPriceOptionProviderTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Price/LowestPriceOptionProviderTest.php
index 44da28e2dbd9..c24baa36461c 100644
--- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Price/LowestPriceOptionProviderTest.php
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Price/LowestPriceOptionProviderTest.php
@@ -34,8 +34,6 @@ protected function setUp()
*/
public function testGetProductsIfOneOfChildIsDisabled()
{
- $this->markTestSkipped('https://github.com/magento-engcom/msi/issues/456');
-
$configurableProduct = $this->productRepository->get('configurable', false, null, true);
$lowestPriceChildrenProducts = $this->createLowestPriceOptionsProvider()->getProducts($configurableProduct);
self::assertCount(1, $lowestPriceChildrenProducts);
@@ -67,8 +65,6 @@ public function testGetProductsIfOneOfChildIsDisabled()
*/
public function testGetProductsIfOneOfChildIsDisabledPerStore()
{
- $this->markTestSkipped('https://github.com/magento-engcom/msi/issues/456');
-
$configurableProduct = $this->productRepository->get('configurable', false, null, true);
$lowestPriceChildrenProducts = $this->createLowestPriceOptionsProvider()->getProducts($configurableProduct);
self::assertCount(1, $lowestPriceChildrenProducts);
@@ -101,8 +97,6 @@ public function testGetProductsIfOneOfChildIsDisabledPerStore()
*/
public function testGetProductsIfOneOfChildIsOutOfStock()
{
- $this->markTestSkipped('https://github.com/magento-engcom/msi/issues/456');
-
$configurableProduct = $this->productRepository->get('configurable', false, null, true);
$lowestPriceChildrenProducts = $this->createLowestPriceOptionsProvider()->getProducts($configurableProduct);
self::assertCount(1, $lowestPriceChildrenProducts);
@@ -131,8 +125,6 @@ public function testGetProductsIfOneOfChildIsOutOfStock()
*/
public function testGetProductsIfOneOfChildrenIsAssignedToOtherWebsite()
{
- $this->markTestSkipped('https://github.com/magento-engcom/msi/issues/456');
-
$configurableProduct = $this->productRepository->getById(1, false, null, true);
$lowestPriceChildrenProducts = $this->createLowestPriceOptionsProvider()->getProducts($configurableProduct);
self::assertCount(1, $lowestPriceChildrenProducts);