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);