Skip to content

Commit 68c504d

Browse files
MC-37249: Grouped product remains In Stock on product edit page
1 parent 4ae0f74 commit 68c504d

File tree

3 files changed

+191
-0
lines changed

3 files changed

+191
-0
lines changed
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\GroupedProduct\Model\Inventory;
9+
10+
use Magento\GroupedProduct\Model\Product\Type\Grouped;
11+
use Magento\Catalog\Api\Data\ProductInterface as Product;
12+
use Magento\CatalogInventory\Api\StockItemCriteriaInterfaceFactory;
13+
use Magento\CatalogInventory\Api\StockItemRepositoryInterface;
14+
use Magento\CatalogInventory\Api\StockConfigurationInterface;
15+
use Magento\CatalogInventory\Observer\ParentItemProcessorInterface;
16+
use Magento\CatalogInventory\Api\Data\StockItemInterface;
17+
18+
/**
19+
* Process parent stock item for grouped product
20+
*/
21+
class ParentItemProcessor implements ParentItemProcessorInterface
22+
{
23+
/**
24+
* @var Grouped
25+
*/
26+
private $groupedType;
27+
28+
/**
29+
* @var StockItemRepositoryInterface
30+
*/
31+
private $stockItemRepository;
32+
33+
/**
34+
* @var StockConfigurationInterface
35+
*/
36+
private $stockConfiguration;
37+
38+
/**
39+
* @var StockItemCriteriaInterfaceFactory
40+
*/
41+
private $criteriaInterfaceFactory;
42+
43+
/**
44+
* @param Grouped $groupedType
45+
* @param StockItemCriteriaInterfaceFactory $criteriaInterfaceFactory
46+
* @param StockItemRepositoryInterface $stockItemRepository
47+
* @param StockConfigurationInterface $stockConfiguration
48+
*/
49+
public function __construct(
50+
Grouped $groupedType,
51+
StockItemCriteriaInterfaceFactory $criteriaInterfaceFactory,
52+
StockItemRepositoryInterface $stockItemRepository,
53+
StockConfigurationInterface $stockConfiguration
54+
) {
55+
$this->groupedType = $groupedType;
56+
$this->criteriaInterfaceFactory = $criteriaInterfaceFactory;
57+
$this->stockConfiguration = $stockConfiguration;
58+
$this->stockItemRepository = $stockItemRepository;
59+
}
60+
61+
/**
62+
* Process parent products
63+
*
64+
* @param Product $product
65+
* @return void
66+
*/
67+
public function process(Product $product)
68+
{
69+
$parentIds = $this->groupedType->getParentIdsByChild($product->getId());
70+
foreach ($parentIds as $productId) {
71+
$this->processStockForParent((int)$productId);
72+
}
73+
}
74+
75+
/**
76+
* Change stock item for parent product depending on children stock items
77+
*
78+
* @param int $productId
79+
* @return void
80+
*/
81+
private function processStockForParent(int $productId)
82+
{
83+
$criteria = $this->criteriaInterfaceFactory->create();
84+
$criteria->setScopeFilter($this->stockConfiguration->getDefaultScopeId());
85+
$criteria->setProductsFilter($productId);
86+
$stockItemCollection = $this->stockItemRepository->getList($criteria);
87+
$allItems = $stockItemCollection->getItems();
88+
if (empty($allItems)) {
89+
return;
90+
}
91+
$parentStockItem = array_shift($allItems);
92+
$groupedChildrenIds = $this->groupedType->getChildrenIds($productId);
93+
$criteria->setProductsFilter($groupedChildrenIds);
94+
$stockItemCollection = $this->stockItemRepository->getList($criteria);
95+
$allItems = $stockItemCollection->getItems();
96+
97+
$groupedChildrenIsInStock = false;
98+
99+
foreach ($allItems as $childItem) {
100+
if ($childItem->getIsInStock() === true) {
101+
$groupedChildrenIsInStock = true;
102+
break;
103+
}
104+
}
105+
106+
if ($this->isNeedToUpdateParent($parentStockItem, $groupedChildrenIsInStock)) {
107+
$parentStockItem->setIsInStock($groupedChildrenIsInStock);
108+
$parentStockItem->setStockStatusChangedAuto(1);
109+
$this->stockItemRepository->save($parentStockItem);
110+
}
111+
}
112+
113+
/**
114+
* Check is parent item should be updated
115+
*
116+
* @param StockItemInterface $parentStockItem
117+
* @param bool $childrenIsInStock
118+
* @return bool
119+
*/
120+
private function isNeedToUpdateParent(StockItemInterface $parentStockItem, bool $childrenIsInStock): bool
121+
{
122+
return $parentStockItem->getIsInStock() !== $childrenIsInStock &&
123+
($childrenIsInStock === false || $parentStockItem->getStockStatusChangedAuto());
124+
}
125+
}

app/code/Magento/GroupedProduct/etc/di.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,4 +105,11 @@
105105
</argument>
106106
</arguments>
107107
</type>
108+
<type name="Magento\CatalogInventory\Observer\SaveInventoryDataObserver">
109+
<arguments>
110+
<argument name="parentItemProcessorPool" xsi:type="array">
111+
<item name="grouped" xsi:type="object"> Magento\GroupedProduct\Model\Inventory\ParentItemProcessor</item>
112+
</argument>
113+
</arguments>
114+
</type>
108115
</config>
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\GroupedProduct\Model\Inventory;
9+
10+
use Magento\Catalog\Api\ProductRepositoryInterface;
11+
use Magento\Catalog\Model\Product;
12+
use Magento\CatalogInventory\Api\StockRegistryInterface;
13+
use Magento\CatalogInventory\Model\Stock\StockItemRepository;
14+
use PHPUnit\Framework\TestCase;
15+
use Magento\TestFramework\Helper\Bootstrap;
16+
use Magento\Framework\ObjectManagerInterface;
17+
18+
/**
19+
* Test stock status parent product
20+
*/
21+
class ParentItemProcessorTest extends TestCase
22+
{
23+
/**
24+
* @var ObjectManagerInterface
25+
*/
26+
protected $objectManager;
27+
28+
/**
29+
* @inheritDoc
30+
*/
31+
protected function setUp(): void
32+
{
33+
$this->objectManager = Bootstrap::getObjectManager();
34+
}
35+
36+
/**
37+
* Test stock status parent product if children are out of stock
38+
*
39+
* @magentoDataFixture Magento/GroupedProduct/_files/product_grouped_with_simple_out_of_stock.php
40+
*
41+
* @return void
42+
*/
43+
public function testOutOfStockParentProduct(): void
44+
{
45+
$productRepository = $this->objectManager->create(ProductRepositoryInterface::class);
46+
/** @var Product $product */
47+
$product = $productRepository->get('simple_100000001');
48+
$product->setStockData(['qty' => 0, 'is_in_stock' => 0]);
49+
$productRepository->save($product);
50+
/** @var StockItemRepository $stockItemRepository */
51+
$stockItemRepository = $this->objectManager->create(StockItemRepository::class);
52+
/** @var StockRegistryInterface $stockRegistry */
53+
$stockRegistry = $this->objectManager->create(StockRegistryInterface::class);
54+
$stockItem = $stockRegistry->getStockItemBySku('grouped');
55+
$stockItem = $stockItemRepository->get($stockItem->getItemId());
56+
57+
$this->assertEquals(false, $stockItem->getIsInStock());
58+
}
59+
}

0 commit comments

Comments
 (0)