Skip to content

Commit f2e05c4

Browse files
committed
Merge branch 'ACP2E-2043' of https://github.com/magento-l3/magento2ce into PR-L3-2023-07-14
2 parents 582e0ac + 25bf8bb commit f2e05c4

File tree

4 files changed

+330
-54
lines changed

4 files changed

+330
-54
lines changed

app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php

Lines changed: 58 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ class StockItemRepository implements StockItemRepositoryInterface
8080
/**
8181
* @var Processor
8282
* @deprecated 100.2.0
83+
* @see No longer used
8384
*/
8485
protected $indexProcessor;
8586

@@ -117,6 +118,7 @@ class StockItemRepository implements StockItemRepositoryInterface
117118
* @param DateTime $dateTime
118119
* @param CollectionFactory|null $productCollectionFactory
119120
* @param PsrLogger|null $psrLogger
121+
* @param StockRegistryStorage|null $stockRegistryStorage
120122
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
121123
*/
122124
public function __construct(
@@ -132,7 +134,8 @@ public function __construct(
132134
Processor $indexProcessor,
133135
DateTime $dateTime,
134136
\Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory = null,
135-
PsrLogger $psrLogger = null
137+
PsrLogger $psrLogger = null,
138+
?StockRegistryStorage $stockRegistryStorage = null
136139
) {
137140
$this->stockConfiguration = $stockConfiguration;
138141
$this->stockStateProvider = $stockStateProvider;
@@ -149,12 +152,14 @@ public function __construct(
149152
->get(CollectionFactory::class);
150153
$this->psrLogger = $psrLogger ?: ObjectManager::getInstance()
151154
->get(PsrLogger::class);
155+
$this->stockRegistryStorage = $stockRegistryStorage
156+
?? ObjectManager::getInstance()->get(StockRegistryStorage::class);
152157
}
153158

154159
/**
155160
* @inheritdoc
156161
*/
157-
public function save(\Magento\CatalogInventory\Api\Data\StockItemInterface $stockItem)
162+
public function save(StockItemInterface $stockItem)
158163
{
159164
try {
160165
/** @var \Magento\Catalog\Model\Product $product */
@@ -170,16 +175,13 @@ public function save(\Magento\CatalogInventory\Api\Data\StockItemInterface $stoc
170175
$typeId = $product->getTypeId() ?: $product->getTypeInstance()->getTypeId();
171176
$isQty = $this->stockConfiguration->isQty($typeId);
172177
if ($isQty) {
173-
$isInStock = $this->stockStateProvider->verifyStock($stockItem);
174-
if ($stockItem->getManageStock() && !$isInStock) {
175-
$stockItem->setIsInStock(false)->setStockStatusChangedAutomaticallyFlag(true);
176-
}
178+
$this->updateStockStatus($stockItem);
177179
// if qty is below notify qty, update the low stock date to today date otherwise set null
178180
$stockItem->setLowStockDate(null);
179181
if ($this->stockStateProvider->verifyNotification($stockItem)) {
180182
$stockItem->setLowStockDate($this->dateTime->gmtDate());
181183
}
182-
$stockItem->setStockStatusChangedAuto(0);
184+
183185
if ($stockItem->hasStockStatusChangedAutomaticallyFlag()) {
184186
$stockItem->setStockStatusChangedAuto((int)$stockItem->getStockStatusChangedAutomaticallyFlag());
185187
}
@@ -198,6 +200,53 @@ public function save(\Magento\CatalogInventory\Api\Data\StockItemInterface $stoc
198200
return $stockItem;
199201
}
200202

203+
/**
204+
* Update stock status based on stock configuration
205+
*
206+
* @param StockItemInterface $stockItem
207+
* @return void
208+
*/
209+
private function updateStockStatus(StockItemInterface $stockItem): void
210+
{
211+
$isInStock = $this->stockStateProvider->verifyStock($stockItem);
212+
if ($stockItem->getManageStock()) {
213+
if (!$isInStock) {
214+
if ($stockItem->getIsInStock() === true) {
215+
$stockItem->setIsInStock(false);
216+
$stockItem->setStockStatusChangedAuto(1);
217+
}
218+
} else {
219+
if ($this->hasStockStatusChanged($stockItem)) {
220+
$stockItem->setStockStatusChangedAuto(0);
221+
}
222+
if ($stockItem->getIsInStock() === false && $stockItem->getStockStatusChangedAuto()) {
223+
$stockItem->setIsInStock(true);
224+
}
225+
}
226+
} else {
227+
$stockItem->setStockStatusChangedAuto(0);
228+
}
229+
}
230+
231+
/**
232+
* Check if stock status has changed
233+
*
234+
* @param StockItemInterface $stockItem
235+
* @return bool
236+
*/
237+
private function hasStockStatusChanged(StockItemInterface $stockItem): bool
238+
{
239+
if ($stockItem->getItemId()) {
240+
try {
241+
$existingStockItem = $this->get($stockItem->getItemId());
242+
return $existingStockItem->getIsInStock() !== $stockItem->getIsInStock();
243+
} catch (NoSuchEntityException $e) {
244+
return true;
245+
}
246+
}
247+
return true;
248+
}
249+
201250
/**
202251
* @inheritdoc
203252
*/
@@ -233,8 +282,8 @@ public function delete(StockItemInterface $stockItem)
233282
{
234283
try {
235284
$this->resource->delete($stockItem);
236-
$this->getStockRegistryStorage()->removeStockItem($stockItem->getProductId());
237-
$this->getStockRegistryStorage()->removeStockStatus($stockItem->getProductId());
285+
$this->stockRegistryStorage->removeStockItem($stockItem->getProductId());
286+
$this->stockRegistryStorage->removeStockStatus($stockItem->getProductId());
238287
} catch (\Exception $exception) {
239288
throw new CouldNotDeleteException(
240289
__(
@@ -263,16 +312,4 @@ public function deleteById($id)
263312
}
264313
return true;
265314
}
266-
267-
/**
268-
* @return StockRegistryStorage
269-
*/
270-
private function getStockRegistryStorage()
271-
{
272-
if (null === $this->stockRegistryStorage) {
273-
$this->stockRegistryStorage = \Magento\Framework\App\ObjectManager::getInstance()
274-
->get(\Magento\CatalogInventory\Model\StockRegistryStorage::class);
275-
}
276-
return $this->stockRegistryStorage;
277-
}
278315
}

app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockItemRepositoryTest.php

Lines changed: 160 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
use Magento\Framework\DB\QueryBuilder;
2727
use Magento\Framework\DB\QueryBuilderFactory;
2828
use Magento\Framework\DB\QueryInterface;
29+
use Magento\Framework\Exception\CouldNotSaveException;
2930
use Magento\Framework\Stdlib\DateTime\DateTime;
3031
use Magento\Framework\Stdlib\DateTime\TimezoneInterface;
3132
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
@@ -124,8 +125,10 @@ protected function setUp(): void
124125
'getItemId',
125126
'getProductId',
126127
'setIsInStock',
128+
'getIsInStock',
127129
'setStockStatusChangedAutomaticallyFlag',
128130
'getStockStatusChangedAutomaticallyFlag',
131+
'getStockStatusChangedAuto',
129132
'getManageStock',
130133
'setLowStockDate',
131134
'setStockStatusChangedAuto',
@@ -282,42 +285,72 @@ public function testDeleteByIdException()
282285
$this->assertTrue($this->model->deleteById($id));
283286
}
284287

285-
public function testSave()
286-
{
288+
/**
289+
* @param array $stockStateProviderMockConfig
290+
* @param array $stockItemMockConfig
291+
* @param array $existingStockItemMockConfig
292+
* @return void
293+
* @throws CouldNotSaveException
294+
* @dataProvider saveDataProvider
295+
*/
296+
public function testSave(
297+
array $stockStateProviderMockConfig,
298+
array $stockItemMockConfig,
299+
array $existingStockItemMockConfig
300+
) {
287301
$productId = 1;
288-
289-
$this->stockItemMock->expects($this->any())->method('getProductId')->willReturn($productId);
290-
$this->productMock->expects($this->once())->method('getId')->willReturn($productId);
291-
$this->productMock->expects($this->once())->method('getTypeId')->willReturn('typeId');
292-
$this->stockConfigurationMock->expects($this->once())->method('isQty')->with('typeId')->willReturn(true);
293-
$this->stockStateProviderMock->expects($this->once())
294-
->method('verifyStock')
295-
->with($this->stockItemMock)
296-
->willReturn(false);
297-
$this->stockItemMock->expects($this->once())->method('getManageStock')->willReturn(true);
298-
$this->stockItemMock->expects($this->once())->method('setIsInStock')->with(false)->willReturnSelf();
299-
$this->stockItemMock->expects($this->once())
300-
->method('setStockStatusChangedAutomaticallyFlag')
301-
->with(true)
302-
->willReturnSelf();
303-
$this->stockItemMock->expects($this->any())->method('setLowStockDate')->willReturnSelf();
304-
$this->stockStateProviderMock->expects($this->once())
305-
->method('verifyNotification')
306-
->with($this->stockItemMock)
302+
$date = '2023-01-01 00:00:00';
303+
$stockStateProviderMockConfig += [
304+
'verifyStock' => ['expects' => $this->once(), 'with' => [$this->stockItemMock], 'willReturn' => true,],
305+
'verifyNotification' => [
306+
'expects' => $this->once(),
307+
'with' => [$this->stockItemMock],
308+
'willReturn' => true,
309+
],
310+
];
311+
$existingStockItemMockConfig += [
312+
'getItemId' => ['expects' => $this->any(), 'willReturn' => 1,],
313+
'getIsInStock' => ['expects' => $this->any(), 'willReturn' => false,],
314+
];
315+
$stockItemMockConfig += [
316+
'getItemId' => ['expects' => $this->any(), 'willReturn' => 1,],
317+
'getManageStock' => ['expects' => $this->once(), 'willReturn' => true,],
318+
'getIsInStock' => ['expects' => $this->any(), 'willReturn' => false,],
319+
'getStockStatusChangedAuto' => ['expects' => $this->once(), 'willReturn' => 1,],
320+
'getProductId' => ['expects' => $this->once(), 'willReturn' => $productId,],
321+
'getWebsiteId' => ['expects' => $this->once(), 'willReturn' => 1,],
322+
'getStockId' => ['expects' => $this->once(), 'willReturn' => 1,],
323+
'setStockStatusChangedAuto' => ['expects' => $this->never(), 'with' => [1],],
324+
'setIsInStock' => ['expects' => $this->once(), 'with' => [true],],
325+
'setWebsiteId' => ['expects' => $this->once(), 'with' => [1], 'willReturnSelf' => true,],
326+
'setStockId' => ['expects' => $this->once(), 'with' => [1], 'willReturnSelf' => true,],
327+
'setLowStockDate' => [
328+
'expects' => $this->exactly(2),
329+
'withConsecutive' => [[null], [$date],],
330+
'willReturnSelf' => true,
331+
],
332+
'hasStockStatusChangedAutomaticallyFlag' => ['expects' => $this->once(), 'willReturn' => false,],
333+
334+
];
335+
$existingStockItem = $this->createMock(Item::class);
336+
$this->stockItemFactoryMock->expects($this->any())->method('create')->willReturn($existingStockItem);
337+
$this->configMock($existingStockItem, $existingStockItemMockConfig);
338+
$this->configMock($this->stockItemMock, $stockItemMockConfig);
339+
$this->configMock($this->stockStateProviderMock, $stockStateProviderMockConfig);
340+
341+
$this->productMock->expects($this->once())
342+
->method('getId')
343+
->willReturn($productId);
344+
$this->productMock->expects($this->once())
345+
->method('getTypeId')
346+
->willReturn('typeId');
347+
$this->stockConfigurationMock->expects($this->once())
348+
->method('isQty')
349+
->with('typeId')
307350
->willReturn(true);
308351
$this->dateTime->expects($this->once())
309-
->method('gmtDate');
310-
$this->stockItemMock->expects($this->atLeastOnce())->method('setStockStatusChangedAuto')->willReturnSelf();
311-
$this->stockItemMock->expects($this->once())
312-
->method('hasStockStatusChangedAutomaticallyFlag')
313-
->willReturn(true);
314-
$this->stockItemMock->expects($this->once())
315-
->method('getStockStatusChangedAutomaticallyFlag')
316-
->willReturn(true);
317-
$this->stockItemMock->expects($this->once())->method('getWebsiteId')->willReturn(1);
318-
$this->stockItemMock->expects($this->once())->method('setWebsiteId')->with(1)->willReturnSelf();
319-
$this->stockItemMock->expects($this->once())->method('getStockId')->willReturn(1);
320-
$this->stockItemMock->expects($this->once())->method('setStockId')->with(1)->willReturnSelf();
352+
->method('gmtDate')
353+
->willReturn($date);
321354
$this->stockItemResourceMock->expects($this->once())
322355
->method('save')
323356
->with($this->stockItemMock)
@@ -385,4 +418,98 @@ public function testGetList()
385418

386419
$this->assertEquals($queryCollectionMock, $this->model->getList($criteriaMock));
387420
}
421+
422+
/**
423+
* @return array
424+
*/
425+
public function saveDataProvider(): array
426+
{
427+
return [
428+
'should set isInStock=true if: verifyStock=true, isInStock=false, stockStatusChangedAuto=true' => [
429+
'stockStateProviderMockConfig' => [],
430+
'stockItemMockConfig' => [],
431+
'existingStockItemMockConfig' => [],
432+
],
433+
'should not set isInStock=true if: verifyStock=true, isInStock=false, stockStatusChangedAuto=false' => [
434+
'stockStateProviderMockConfig' => [],
435+
'stockItemMockConfig' => [
436+
'setIsInStock' => ['expects' => $this->never(),],
437+
'setStockStatusChangedAuto' => ['expects' => $this->never()],
438+
'getStockStatusChangedAuto' => ['expects' => $this->once(), 'willReturn' => false,],
439+
],
440+
'existingStockItemMockConfig' => [],
441+
],
442+
'should set isInStock=false and stockStatusChangedAuto=true if: verifyStock=false and isInStock=true' => [
443+
'stockStateProviderMockConfig' => [
444+
'verifyStock' => ['expects' => $this->once(), 'willReturn' => false,],
445+
],
446+
'stockItemMockConfig' => [
447+
'getIsInStock' => ['expects' => $this->any(), 'willReturn' => true,],
448+
'getStockStatusChangedAuto' => ['expects' => $this->never(),],
449+
'setIsInStock' => ['expects' => $this->once(), 'with' => [false],],
450+
'setStockStatusChangedAuto' => ['expects' => $this->once(), 'with' => [1],],
451+
],
452+
'existingStockItemMockConfig' => [],
453+
],
454+
'should set stockStatusChangedAuto=true if: verifyStock=false and isInStock=false' => [
455+
'stockStateProviderMockConfig' => [
456+
'verifyStock' => ['expects' => $this->once(), 'willReturn' => false,],
457+
],
458+
'stockItemMockConfig' => [
459+
'getIsInStock' => ['expects' => $this->any(), 'willReturn' => false,],
460+
'getStockStatusChangedAuto' => ['expects' => $this->never(),],
461+
'setIsInStock' => ['expects' => $this->never(),],
462+
'setStockStatusChangedAuto' => ['expects' => $this->never(),],
463+
],
464+
'existingStockItemMockConfig' => [],
465+
],
466+
'should set stockStatusChangedAuto=true if: stockStatusChangedAutomaticallyFlag=true' => [
467+
'stockStateProviderMockConfig' => [],
468+
'stockItemMockConfig' => [
469+
'getStockStatusChangedAuto' => ['expects' => $this->once(), 'willReturn' => false,],
470+
'setIsInStock' => ['expects' => $this->never(),],
471+
'setStockStatusChangedAuto' => ['expects' => $this->once(), 'with' => [1],],
472+
'hasStockStatusChangedAutomaticallyFlag' => ['expects' => $this->once(), 'willReturn' => true,],
473+
'getStockStatusChangedAutomaticallyFlag' => ['expects' => $this->once(), 'willReturn' => true,],
474+
],
475+
'existingStockItemMockConfig' => [
476+
],
477+
],
478+
'should set stockStatusChangedAuto=false if: getManageStock=false' => [
479+
'stockStateProviderMockConfig' => [],
480+
'stockItemMockConfig' => [
481+
'getManageStock' => ['expects' => $this->once(), 'willReturn' => false],
482+
'getStockStatusChangedAuto' => ['expects' => $this->never(), 'willReturn' => false,],
483+
'setIsInStock' => ['expects' => $this->never(),],
484+
'setStockStatusChangedAuto' => ['expects' => $this->once(), 'with' => [0],],
485+
],
486+
'existingStockItemMockConfig' => [
487+
],
488+
]
489+
];
490+
}
491+
492+
/**
493+
* @param MockObject $mockObject
494+
* @param array $configs
495+
* @return void
496+
*/
497+
private function configMock(MockObject $mockObject, array $configs): void
498+
{
499+
foreach ($configs as $method => $config) {
500+
$mockMethod = $mockObject->expects($config['expects'])->method($method);
501+
if (isset($config['with'])) {
502+
$mockMethod->with(...$config['with']);
503+
}
504+
if (isset($config['withConsecutive'])) {
505+
$mockMethod->withConsecutive(...$config['withConsecutive']);
506+
}
507+
if (isset($config['willReturnSelf'])) {
508+
$mockMethod->willReturnSelf();
509+
}
510+
if (isset($config['willReturn'])) {
511+
$mockMethod->willReturn($config['willReturn']);
512+
}
513+
}
514+
}
388515
}

dev/tests/integration/testsuite/Magento/CatalogInventory/Model/StockItemSave/OnProductCreate/ByProductModel/ByStockItemTest.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,4 +109,29 @@ public function testSaveManuallyCreatedStockItem()
109109

110110
$this->stockItemDataChecker->checkStockItemData('simpleByStockItemTest', $this->stockItemData);
111111
}
112+
113+
public function testAutomaticIsInStockUpdate(): void
114+
{
115+
$stockItemData = [
116+
StockItemInterface::QTY => 0,
117+
StockItemInterface::IS_IN_STOCK => true,
118+
StockItemInterface::MANAGE_STOCK => 1,
119+
];
120+
$expected = [
121+
StockItemInterface::QTY => 0,
122+
StockItemInterface::IS_IN_STOCK => false,
123+
StockItemInterface::STOCK_STATUS_CHANGED_AUTO => true,
124+
];
125+
/** @var StockItemInterface $stockItem */
126+
$stockItem = $this->stockItemFactory->create();
127+
$this->dataObjectHelper->populateWithArray($stockItem, $stockItemData, StockItemInterface::class);
128+
129+
/** @var Product $product */
130+
$product = $this->productFactory->create();
131+
$this->dataObjectHelper->populateWithArray($product, $this->productData, ProductInterface::class);
132+
$product->getExtensionAttributes()->setStockItem($stockItem);
133+
$product->save();
134+
135+
$this->stockItemDataChecker->checkStockItemData('simpleByStockItemTest', $expected);
136+
}
112137
}

0 commit comments

Comments
 (0)