diff --git a/app/code/Magento/InventoryCatalog/Model/ResourceModel/SetDataToLegacyStockStatus.php b/app/code/Magento/InventoryCatalog/Model/ResourceModel/SetDataToLegacyStockStatus.php deleted file mode 100644 index c0a5cd110284..000000000000 --- a/app/code/Magento/InventoryCatalog/Model/ResourceModel/SetDataToLegacyStockStatus.php +++ /dev/null @@ -1,68 +0,0 @@ -resourceConnection = $resourceConnection; - $this->getProductIdsBySkus = $getProductIdsBySkus; - } - - /** - * @param string $sku - * @param float $quantity - * @param int $status - * @return void - */ - public function execute(string $sku, float $quantity, int $status) - { - $productIds = $this->getProductIdsBySkus->execute([$sku]); - - if (isset($productIds[$sku])) { - $productId = $productIds[$sku]; - - $connection = $this->resourceConnection->getConnection(); - $connection->update( - $this->resourceConnection->getTableName('cataloginventory_stock_status'), - [ - StockStatusInterface::QTY => $quantity, - StockStatusInterface::STOCK_STATUS => $status, - ], - [ - StockStatusInterface::PRODUCT_ID . ' = ?' => $productId, - 'website_id = ?' => 0, - ] - ); - } - } -} diff --git a/app/code/Magento/InventoryCatalog/Model/SourceItem/SourceItemsSave.php b/app/code/Magento/InventoryCatalog/Model/SourceItem/SourceItemsSave.php new file mode 100644 index 000000000000..6918445ce1e1 --- /dev/null +++ b/app/code/Magento/InventoryCatalog/Model/SourceItem/SourceItemsSave.php @@ -0,0 +1,74 @@ +sourceItemsValidator = $sourceItemsValidator; + $this->saveMultiple = $saveMultiple; + $this->logger = $logger; + } + + /** + * @inheritdoc + */ + public function execute(array $sourceItems) + { + if (empty($sourceItems)) { + throw new InputException(__('Input data is empty')); + } + + $validationResult = $this->sourceItemsValidator->validate($sourceItems); + if (!$validationResult->isValid()) { + throw new ValidationException(__('Validation Failed'), null, 0, $validationResult); + } + + try { + $this->saveMultiple->execute($sourceItems); + } catch (\Exception $e) { + $this->logger->error($e->getMessage()); + throw new CouldNotSaveException(__('Could not save Source Item'), $e); + } + } +} diff --git a/app/code/Magento/InventoryCatalog/Plugin/InventoryApi/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php b/app/code/Magento/InventoryCatalog/Plugin/InventoryApi/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php index d18e6a14deca..e7eca12d4fe1 100644 --- a/app/code/Magento/InventoryCatalog/Plugin/InventoryApi/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php +++ b/app/code/Magento/InventoryCatalog/Plugin/InventoryApi/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php @@ -7,17 +7,27 @@ namespace Magento\InventoryCatalog\Plugin\InventoryApi; +use Magento\CatalogInventory\Api\Data\StockItemInterface; +use Magento\CatalogInventory\Api\StockItemRepositoryInterface; +use Magento\CatalogInventory\Api\StockItemCriteriaInterfaceFactory; +use Magento\CatalogInventory\Model\Indexer\Stock\Processor; +use Magento\CatalogInventory\Model\Spi\StockStateProviderInterface; +use Magento\CatalogInventory\Model\Stock; +use Magento\Framework\Exception\InputException; use Magento\InventoryApi\Api\Data\SourceItemInterface; use Magento\InventoryApi\Api\SourceItemsSaveInterface; use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; +use Magento\InventoryCatalogApi\Model\GetProductIdsBySkusInterface; +use Magento\InventoryCatalogApi\Model\GetProductTypesBySkusInterface; use Magento\InventoryCatalog\Model\ResourceModel\SetDataToLegacyStockItem; -use Magento\InventoryCatalog\Model\ResourceModel\SetDataToLegacyStockStatus; -use Magento\InventorySalesApi\Api\IsProductSalableInterface; -use Magento\InventoryCatalogApi\Api\DefaultStockProviderInterface; +use Magento\InventoryConfigurationApi\Model\IsSourceItemManagementAllowedForProductTypeInterface; /** * Set Qty and status for legacy CatalogInventory Stock Status and Stock Item DB tables, * if corresponding MSI SourceItem assigned to Default Source has been saved + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * TODO: https://github.com/magento-engcom/msi/pull/1082/ */ class SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin { @@ -32,39 +42,71 @@ class SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin private $setDataToLegacyStockItem; /** - * @var SetDataToLegacyStockStatus + * @var StockItemCriteriaInterfaceFactory */ - private $setDataToLegacyStockStatus; + private $legacyStockItemCriteriaFactory; /** - * @var IsProductSalableInterface + * @var StockItemRepositoryInterface */ - private $isProductSalable; + private $legacyStockItemRepository; /** - * @var DefaultStockProviderInterface + * @var GetProductIdsBySkusInterface */ - private $defaultStockProvider; + private $getProductIdsBySkus; + + /** + * @var StockStateProviderInterface + */ + private $stockStateProvider; + + /** + * @var Processor + */ + private $indexerProcessor; + + /** + * @var IsSourceItemManagementAllowedForProductTypeInterface + */ + private $isSourceItemsAllowedForProductType; + + /** + * @var GetProductTypesBySkusInterface + */ + private $getProductTypeBySku; /** * @param DefaultSourceProviderInterface $defaultSourceProvider * @param SetDataToLegacyStockItem $setDataToLegacyStockItem - * @param SetDataToLegacyStockStatus $setDataToLegacyStockStatus - * @param IsProductSalableInterface $isProductSalable - * @param DefaultStockProviderInterface $defaultStockProvider + * @param StockItemCriteriaInterfaceFactory $legacyStockItemCriteriaFactory + * @param StockItemRepositoryInterface $legacyStockItemRepository + * @param GetProductIdsBySkusInterface $getProductIdsBySkus + * @param StockStateProviderInterface $stockStateProvider + * @param Processor $indexerProcessor + * @param IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemsAllowedForProductType + * @param GetProductTypesBySkusInterface $getProductTypeBySku */ public function __construct( DefaultSourceProviderInterface $defaultSourceProvider, SetDataToLegacyStockItem $setDataToLegacyStockItem, - SetDataToLegacyStockStatus $setDataToLegacyStockStatus, - IsProductSalableInterface $isProductSalable, - DefaultStockProviderInterface $defaultStockProvider + StockItemCriteriaInterfaceFactory $legacyStockItemCriteriaFactory, + StockItemRepositoryInterface $legacyStockItemRepository, + GetProductIdsBySkusInterface $getProductIdsBySkus, + StockStateProviderInterface $stockStateProvider, + Processor $indexerProcessor, + IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemsAllowedForProductType, + GetProductTypesBySkusInterface $getProductTypeBySku ) { $this->defaultSourceProvider = $defaultSourceProvider; $this->setDataToLegacyStockItem = $setDataToLegacyStockItem; - $this->setDataToLegacyStockStatus = $setDataToLegacyStockStatus; - $this->isProductSalable = $isProductSalable; - $this->defaultStockProvider = $defaultStockProvider; + $this->legacyStockItemCriteriaFactory = $legacyStockItemCriteriaFactory; + $this->legacyStockItemRepository = $legacyStockItemRepository; + $this->getProductIdsBySkus = $getProductIdsBySkus; + $this->stockStateProvider = $stockStateProvider; + $this->indexerProcessor = $indexerProcessor; + $this->isSourceItemsAllowedForProductType = $isSourceItemsAllowedForProductType; + $this->getProductTypeBySku = $getProductTypeBySku; } /** @@ -72,35 +114,77 @@ public function __construct( * @param void $result * @param SourceItemInterface[] $sourceItems * @return void - * @see SourceItemsSaveInterface::execute * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function afterExecute(SourceItemsSaveInterface $subject, $result, array $sourceItems) + public function afterExecute(SourceItemsSaveInterface $subject, $result, array $sourceItems): void { + $productIds = []; foreach ($sourceItems as $sourceItem) { if ($sourceItem->getSourceCode() !== $this->defaultSourceProvider->getCode()) { continue; } + + $sku = $sourceItem->getSku(); + + try { + $productId = (int)$this->getProductIdsBySkus->execute([$sku])[$sku]; + } catch (InputException $e) { + // Saving source item data for not existed product + continue; + } + + $typeId = $this->getProductTypeBySku->execute([$sku])[$sku]; + if (false === $this->isSourceItemsAllowedForProductType->execute($typeId)) { + continue; + } + + $legacyStockItem = $this->getLegacyStockItem($productId); + if (null === $legacyStockItem) { + continue; + } + + $isInStock = (int)$sourceItem->getStatus(); + + if ($legacyStockItem->getManageStock()) { + $legacyStockItem->setIsInStock($isInStock); + $legacyStockItem->setQty((float)$sourceItem->getQuantity()); + + if (false === $this->stockStateProvider->verifyStock($legacyStockItem)) { + $isInStock = 0; + } + } + $this->setDataToLegacyStockItem->execute( $sourceItem->getSku(), (float)$sourceItem->getQuantity(), - (int)$sourceItem->getStatus() - ); - $this->setDataToLegacyStockStatus->execute( - $sourceItem->getSku(), - (float)$sourceItem->getQuantity(), - (int)$sourceItem->getStatus() - ); - /** - * We need to call setDataToLegacyStockStatus second time because we don't have On Save re-indexation - * as cataloginventory_stock_item table updated with plane SQL queries - * Thus, initially we put the raw data there, and after that persist the calculated value - */ - $this->setDataToLegacyStockStatus->execute( - $sourceItem->getSku(), - (float)$sourceItem->getQuantity(), - (int)$this->isProductSalable->execute($sourceItem->getSku(), $this->defaultStockProvider->getId()) + $isInStock ); + $productIds[] = $productId; } + + if ($productIds) { + $this->indexerProcessor->reindexList($productIds); + } + } + + /** + * @param int $productId + * @return null|StockItemInterface + */ + private function getLegacyStockItem(int $productId): ?StockItemInterface + { + $searchCriteria = $this->legacyStockItemCriteriaFactory->create(); + + $searchCriteria->addFilter(StockItemInterface::PRODUCT_ID, StockItemInterface::PRODUCT_ID, $productId); + $searchCriteria->addFilter(StockItemInterface::STOCK_ID, StockItemInterface::STOCK_ID, Stock::DEFAULT_STOCK_ID); + + $stockItemCollection = $this->legacyStockItemRepository->getList($searchCriteria); + if ($stockItemCollection->getTotalCount() === 0) { + return null; + } + + $stockItems = $stockItemCollection->getItems(); + $stockItem = reset($stockItems); + return $stockItem; } } diff --git a/app/code/Magento/InventoryCatalog/Plugin/InventoryApi/SetToZeroLegacyCatalogInventoryAtSourceItemsDeletePlugin.php b/app/code/Magento/InventoryCatalog/Plugin/InventoryApi/SetToZeroLegacyCatalogInventoryAtSourceItemsDeletePlugin.php index 7e51af2e6474..79812e026052 100644 --- a/app/code/Magento/InventoryCatalog/Plugin/InventoryApi/SetToZeroLegacyCatalogInventoryAtSourceItemsDeletePlugin.php +++ b/app/code/Magento/InventoryCatalog/Plugin/InventoryApi/SetToZeroLegacyCatalogInventoryAtSourceItemsDeletePlugin.php @@ -7,13 +7,15 @@ namespace Magento\InventoryCatalog\Plugin\InventoryApi; -use Magento\CatalogInventory\Model\Stock\Status; +use Magento\CatalogInventory\Model\Indexer\Stock\Processor; +use Magento\Framework\Exception\InputException; use Magento\InventoryApi\Api\Data\SourceItemInterface; use Magento\InventoryApi\Api\SourceItemsDeleteInterface; use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; +use Magento\InventoryCatalogApi\Model\GetProductIdsBySkusInterface; +use Magento\InventoryCatalogApi\Model\GetProductTypesBySkusInterface; use Magento\InventoryCatalog\Model\ResourceModel\SetDataToLegacyStockItem; -use Magento\InventoryCatalog\Model\ResourceModel\SetDataToLegacyStockStatus; -use Psr\Log\LoggerInterface; +use Magento\InventoryConfigurationApi\Model\IsSourceItemManagementAllowedForProductTypeInterface; /** * Set to zero Qty and status to ‘Out of Stock’ for legacy CatalogInventory Stock Status and Stock Item DB tables, @@ -32,23 +34,47 @@ class SetToZeroLegacyCatalogInventoryAtSourceItemsDeletePlugin private $setDataToLegacyStockItem; /** - * @var SetDataToLegacyStockStatus + * @var GetProductIdsBySkusInterface */ - private $setDataToLegacyStockStatus; + private $getProductIdsBySkus; + + /** + * @var Processor + */ + private $indexerProcessor; + + /** + * @var IsSourceItemManagementAllowedForProductTypeInterface + */ + private $isSourceItemsAllowedForProductType; + + /** + * @var GetProductTypesBySkusInterface + */ + private $getProductTypeBySku; /** * @param DefaultSourceProviderInterface $defaultSourceProvider * @param SetDataToLegacyStockItem $setDataToLegacyStockItem - * @param SetDataToLegacyStockStatus $setDataToLegacyStockStatus + * @param GetProductIdsBySkusInterface $getProductIdsBySkus + * @param Processor $indexerProcessor + * @param IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemsAllowedForProductType + * @param GetProductTypesBySkusInterface $getProductTypeBySku */ public function __construct( DefaultSourceProviderInterface $defaultSourceProvider, SetDataToLegacyStockItem $setDataToLegacyStockItem, - SetDataToLegacyStockStatus $setDataToLegacyStockStatus + GetProductIdsBySkusInterface $getProductIdsBySkus, + Processor $indexerProcessor, + IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemsAllowedForProductType, + GetProductTypesBySkusInterface $getProductTypeBySku ) { $this->defaultSourceProvider = $defaultSourceProvider; $this->setDataToLegacyStockItem = $setDataToLegacyStockItem; - $this->setDataToLegacyStockStatus = $setDataToLegacyStockStatus; + $this->getProductIdsBySkus = $getProductIdsBySkus; + $this->indexerProcessor = $indexerProcessor; + $this->isSourceItemsAllowedForProductType = $isSourceItemsAllowedForProductType; + $this->getProductTypeBySku = $getProductTypeBySku; } /** @@ -56,17 +82,36 @@ public function __construct( * @param void $result * @param SourceItemInterface[] $sourceItems * @return void - * @see SourceItemsDeleteInterface::execute * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function afterExecute(SourceItemsDeleteInterface $subject, $result, array $sourceItems) { + $productIds = []; foreach ($sourceItems as $sourceItem) { if ($sourceItem->getSourceCode() !== $this->defaultSourceProvider->getCode()) { continue; } + + $sku = $sourceItem->getSku(); + + try { + $productId = (int)$this->getProductIdsBySkus->execute([$sku])[$sku]; + } catch (InputException $e) { + // Delete source item data for not existed product + continue; + } + + $typeId = $this->getProductTypeBySku->execute([$sku])[$sku]; + if (false === $this->isSourceItemsAllowedForProductType->execute($typeId)) { + continue; + } + $this->setDataToLegacyStockItem->execute($sourceItem->getSku(), 0, 0); - $this->setDataToLegacyStockStatus->execute($sourceItem->getSku(), 0, Status::STATUS_OUT_OF_STOCK); + $productIds[] = $productId; + } + + if ($productIds) { + $this->indexerProcessor->reindexList($productIds); } } } diff --git a/app/code/Magento/InventoryCatalog/composer.json b/app/code/Magento/InventoryCatalog/composer.json index 5ce3ebe14067..f18c5ab0e3b4 100644 --- a/app/code/Magento/InventoryCatalog/composer.json +++ b/app/code/Magento/InventoryCatalog/composer.json @@ -6,6 +6,7 @@ "magento/framework": "*", "magento/module-catalog": "*", "magento/module-catalog-inventory": "*", + "magento/module-inventory": "*", "magento/module-inventory-api": "*", "magento/module-inventory-catalog-api": "*", "magento/module-store": "*", diff --git a/app/code/Magento/InventoryCatalog/etc/di.xml b/app/code/Magento/InventoryCatalog/etc/di.xml index 7eb0d9dc511a..2c1e3b77ac2e 100644 --- a/app/code/Magento/InventoryCatalog/etc/di.xml +++ b/app/code/Magento/InventoryCatalog/etc/di.xml @@ -65,4 +65,12 @@ Magento\Inventory\Model\ResourceModel\SourceItem::TABLE_NAME_SOURCE_ITEM + + + Magento\InventoryCatalog\Model\SourceItem\SourceItemsSave + + + + + diff --git a/app/code/Magento/InventoryConfiguration/Model/GetStockItemConfiguration.php b/app/code/Magento/InventoryConfiguration/Model/GetStockItemConfiguration.php index f71fb11619a1..daa1bf94e169 100644 --- a/app/code/Magento/InventoryConfiguration/Model/GetStockItemConfiguration.php +++ b/app/code/Magento/InventoryConfiguration/Model/GetStockItemConfiguration.php @@ -10,7 +10,7 @@ use Magento\CatalogInventory\Api\StockItemRepositoryInterface; use Magento\CatalogInventory\Api\Data\StockItemInterface; use Magento\CatalogInventory\Api\StockItemCriteriaInterfaceFactory; -use Magento\InventoryCatalogApi\Api\DefaultStockProviderInterface; +use Magento\CatalogInventory\Model\Stock; use Magento\InventoryCatalogApi\Model\GetProductIdsBySkusInterface; use Magento\InventoryConfigurationApi\Api\GetStockItemConfigurationInterface; use Magento\InventorySales\Model\GetStockItemDataInterface; @@ -46,33 +46,25 @@ class GetStockItemConfiguration implements GetStockItemConfigurationInterface */ private $stockItemConfigurationFactory; - /** - * @var DefaultStockProviderInterface - */ - private $defaultStockProvider; - /** * @param GetStockItemDataInterface $getStockItemData * @param StockItemCriteriaInterfaceFactory $legacyStockItemCriteriaFactory * @param StockItemRepositoryInterface $legacyStockItemRepository * @param GetProductIdsBySkusInterface $getProductIdsBySkus * @param StockItemConfigurationFactory $stockItemConfigurationFactory - * @param DefaultStockProviderInterface $defaultStockProvider */ public function __construct( GetStockItemDataInterface $getStockItemData, StockItemCriteriaInterfaceFactory $legacyStockItemCriteriaFactory, StockItemRepositoryInterface $legacyStockItemRepository, GetProductIdsBySkusInterface $getProductIdsBySkus, - StockItemConfigurationFactory $stockItemConfigurationFactory, - DefaultStockProviderInterface $defaultStockProvider + StockItemConfigurationFactory $stockItemConfigurationFactory ) { $this->getStockItemData = $getStockItemData; $this->legacyStockItemCriteriaFactory = $legacyStockItemCriteriaFactory; $this->legacyStockItemRepository = $legacyStockItemRepository; $this->getProductIdsBySkus = $getProductIdsBySkus; $this->stockItemConfigurationFactory = $stockItemConfigurationFactory; - $this->defaultStockProvider = $defaultStockProvider; } /** @@ -105,10 +97,8 @@ private function getLegacyStockItem(string $sku): StockItemInterface $productId = $this->getProductIdsBySkus->execute([$sku])[$sku]; $searchCriteria->addFilter(StockItemInterface::PRODUCT_ID, StockItemInterface::PRODUCT_ID, $productId); - // TODO We use $legacyStockId until we have proper multi-stock item configuration - $legacyStockId = $this->defaultStockProvider->getId(); - - $searchCriteria->addFilter(StockItemInterface::STOCK_ID, StockItemInterface::STOCK_ID, $legacyStockId); + // TODO We use Stock::DEFAULT_STOCK_ID until we have proper multi-stock item configuration + $searchCriteria->addFilter(StockItemInterface::STOCK_ID, StockItemInterface::STOCK_ID, Stock::DEFAULT_STOCK_ID); $stockItemCollection = $this->legacyStockItemRepository->getList($searchCriteria); if ($stockItemCollection->getTotalCount() === 0) {