diff --git a/vendor/magento/module-bundle/Model/Inventory/ChangeParentStockStatus.php b/vendor/magento/module-bundle/Model/Inventory/ChangeParentStockStatus.php new file mode 100644 index 000000000000..023893d7317e --- /dev/null +++ b/vendor/magento/module-bundle/Model/Inventory/ChangeParentStockStatus.php @@ -0,0 +1,160 @@ +bundleType = $bundleType; + $this->criteriaInterfaceFactory = $criteriaInterfaceFactory; + $this->stockItemRepository = $stockItemRepository; + $this->stockConfiguration = $stockConfiguration; + } + + /** + * Update stock status of bundle products based on children products stock status + * + * @param array $childrenIds + * @return void + */ + public function execute(array $childrenIds): void + { + $parentIds = $this->bundleType->getParentIdsByChild($childrenIds); + foreach (array_unique($parentIds) as $productId) { + $this->processStockForParent((int)$productId); + } + } + + /** + * Update stock status of bundle product based on children products stock status + * + * @param int $productId + * @return void + */ + private function processStockForParent(int $productId): void + { + $stockItems = $this->getStockItems([$productId]); + $parentStockItem = $stockItems[$productId] ?? null; + if ($parentStockItem) { + $childrenIsInStock = $this->isChildrenInStock($productId); + if ($this->isNeedToUpdateParent($parentStockItem, $childrenIsInStock)) { + $parentStockItem->setIsInStock($childrenIsInStock); + $parentStockItem->setStockStatusChangedAuto(1); + $this->stockItemRepository->save($parentStockItem); + } + } + } + + /** + * Returns stock status of bundle product based on children stock status + * + * Returns TRUE if any of the following conditions is true: + * - At least one product is in-stock in each required option + * - Any product is in-stock (if all options are optional) + * + * @param int $productId + * @return bool + */ + private function isChildrenInStock(int $productId) : bool + { + $childrenIsInStock = false; + $childrenIds = $this->bundleType->getChildrenIds($productId, true); + $stockItems = $this->getStockItems(array_merge(...array_values($childrenIds))); + foreach ($childrenIds as $childrenIdsPerOption) { + $childrenIsInStock = false; + foreach ($childrenIdsPerOption as $id) { + $stockItem = $stockItems[$id] ?? null; + if ($stockItem && $stockItem->getIsInStock()) { + $childrenIsInStock = true; + break; + } + } + if (!$childrenIsInStock) { + break; + } + } + + return $childrenIsInStock; + } + + /** + * Check if parent item should be updated + * + * @param StockItemInterface $parentStockItem + * @param bool $childrenIsInStock + * @return bool + */ + private function isNeedToUpdateParent( + StockItemInterface $parentStockItem, + bool $childrenIsInStock + ): bool { + return $parentStockItem->getIsInStock() !== $childrenIsInStock && + ($childrenIsInStock === false || $parentStockItem->getStockStatusChangedAuto()); + } + + /** + * Get stock items for provided product IDs + * + * @param array $productIds + * @return StockItemInterface[] + */ + private function getStockItems(array $productIds): array + { + $criteria = $this->criteriaInterfaceFactory->create(); + $criteria->setScopeFilter($this->stockConfiguration->getDefaultScopeId()); + $criteria->setProductsFilter(array_unique($productIds)); + $stockItemCollection = $this->stockItemRepository->getList($criteria); + + $stockItems = []; + foreach ($stockItemCollection->getItems() as $stockItem) { + $stockItems[$stockItem->getProductId()] = $stockItem; + } + + return $stockItems; + } +} diff --git a/vendor/magento/module-bundle/Model/Inventory/ParentItemProcessor.php b/vendor/magento/module-bundle/Model/Inventory/ParentItemProcessor.php new file mode 100644 index 000000000000..5e013ced75c3 --- /dev/null +++ b/vendor/magento/module-bundle/Model/Inventory/ParentItemProcessor.php @@ -0,0 +1,39 @@ +changeParentStockStatus = $changeParentStockStatus; + } + + /** + * @inheritdoc + */ + public function process(Product $product) + { + $this->changeParentStockStatus->execute([$product->getId()]); + } +} diff --git a/vendor/magento/module-bundle/etc/di.xml b/vendor/magento/module-bundle/etc/di.xml index 17b6d228e88e..5224151f306c 100644 --- a/vendor/magento/module-bundle/etc/di.xml +++ b/vendor/magento/module-bundle/etc/di.xml @@ -271,4 +271,11 @@ + + + + Magento\Bundle\Model\Inventory\ParentItemProcessor + + + diff --git a/vendor/magento/module-bundle-import-export/Plugin/Import/Product/UpdateBundleProductsStockItemStatusPlugin.php b/vendor/magento/module-bundle-import-export/Plugin/Import/Product/UpdateBundleProductsStockItemStatusPlugin.php new file mode 100644 index 000000000000..45013372a440 --- /dev/null +++ b/vendor/magento/module-bundle-import-export/Plugin/Import/Product/UpdateBundleProductsStockItemStatusPlugin.php @@ -0,0 +1,49 @@ +changeParentStockStatus = $changeParentStockStatus; + } + + /** + * Update bundle products stock item status based on children products stock status after import + * + * @param StockItemImporterInterface $subject + * @param mixed $result + * @param array $stockData + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterImport( + StockItemImporterInterface $subject, + $result, + array $stockData + ): void { + if ($stockData) { + $this->changeParentStockStatus->execute(array_column($stockData, 'product_id')); + } + } +} diff --git a/vendor/magento/module-bundle-import-export/etc/di.xml b/vendor/magento/module-bundle-import-export/etc/di.xml index 2bcbbedcc910..d697b52f628a 100644 --- a/vendor/magento/module-bundle-import-export/etc/di.xml +++ b/vendor/magento/module-bundle-import-export/etc/di.xml @@ -13,4 +13,9 @@ + + +