Skip to content

Commit df15d08

Browse files
committed
TP-34177: Out of stock label is not working correctly for different type of products
- Backport 1.1.x
1 parent c86a881 commit df15d08

File tree

10 files changed

+673
-68
lines changed

10 files changed

+673
-68
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
/**
4+
* Copyright © Magento, Inc. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
-->
8+
9+
<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd">
10+
<test name="StorefrontCreateOrderAllQuantityGroupedProductOptionDefaultStockTest">
11+
<annotations>
12+
<stories value="Grouped Product Default Stock."/>
13+
<title value="Place order with all quantity with grouped product option on default stock."/>
14+
<description value="Verify, grouped product option will change status after order placement with all it's quantity on default stock"/>
15+
<severity value="CRITICAL"/>
16+
<group value="msi"/>
17+
<group value="multi_mode"/>
18+
</annotations>
19+
<before>
20+
<!--Login to admin-->
21+
<actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/>
22+
<!--Disable all custom sources-->
23+
<actionGroup ref="DisableAllSourcesActionGroup" stepKey="disableSource"/>
24+
<!--Create category-->
25+
<createData entity="ApiCategory" stepKey="createCategory"/>
26+
<!--Create grouped product-->
27+
<createData entity="ApiGroupedProduct" stepKey="groupedProduct"/>
28+
<!--Create simple product (1) with qty 10-->
29+
<createData entity="ApiSimplePrice10Qty10" stepKey="product1">
30+
<requiredEntity createDataKey="createCategory"/>
31+
</createData>
32+
<!--Create simple product (2) with qty 10-->
33+
<createData entity="ApiSimplePrice10Qty10" stepKey="product2">
34+
<requiredEntity createDataKey="createCategory"/>
35+
</createData>
36+
<!--Add simple product (1) to grouped product-->
37+
<createData entity="OneSimpleProductLink" stepKey="addProductOne">
38+
<requiredEntity createDataKey="groupedProduct"/>
39+
<requiredEntity createDataKey="product1"/>
40+
</createData>
41+
<!--Add simple product (2) to grouped product-->
42+
<updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo">
43+
<requiredEntity createDataKey="groupedProduct"/>
44+
<requiredEntity createDataKey="product2"/>
45+
</updateData>
46+
<!--Create customer-->
47+
<createData entity="MsiCustomer1" stepKey="customer"/>
48+
</before>
49+
<after>
50+
<!--Logout from admin-->
51+
<actionGroup ref="logout" stepKey="logoutOfAdmin"/>
52+
<!--Delete category-->
53+
<deleteData createDataKey="createCategory" stepKey="deleteCategory"/>
54+
<!--Delete grouped product-->
55+
<deleteData createDataKey="groupedProduct" stepKey="deleteGroupedProduct"/>
56+
<!--Delete simple product (1)-->
57+
<deleteData createDataKey="product1" stepKey="deleteSimpleProduct1"/>
58+
<!--Delete simple product (2)-->
59+
<deleteData createDataKey="product2" stepKey="deleteSimpleProduct2"/>
60+
<!--Delete customer-->
61+
<deleteData createDataKey="customer" stepKey="deleteCustomer"/>
62+
</after>
63+
64+
<!--Login To storefront as Customer-->
65+
<actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefront">
66+
<argument name="Customer" value="$$customer$$"/>
67+
</actionGroup>
68+
<!--Navigate to group product PDP.-->
69+
<amOnPage url="{{StorefrontProductPage.url($groupedProduct.custom_attributes[url_key]$)}}" stepKey="goToProductPage"/>
70+
<waitForPageLoad stepKey="waitForProductPageLoaded"/>
71+
<!--Add grouped product to cart with all available quantity of product (1).-->
72+
<actionGroup ref="StorefrontAddGroupedProductWithTwoLinksToCartActionGroup" stepKey="addGroupedProductToCart">
73+
<argument name="product" value="$groupedProduct$"/>
74+
<argument name="linkedProduct1Name" value="$product1.name$"/>
75+
<argument name="linkedProduct2Name" value="$product2.name$"/>
76+
<argument name="linkedProduct1Qty" value="{{Qty_10.qty}}"/>
77+
<argument name="linkedProduct2Qty" value="{{Qty_1.qty}}"/>
78+
</actionGroup>
79+
<!--Place order.-->
80+
<actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="navigateToCheckoutPage"/>
81+
<click selector="{{CheckoutShippingSection.next}}" stepKey="clickNextButton"/>
82+
<actionGroup ref="ClickPlaceOrderActionGroup" stepKey="clickOnPlaceOrder"/>
83+
<!--Run queue consumer.-->
84+
<magentoCLI command="queue:consumers:start" arguments="inventory.reservations.updateSalabilityStatus" stepKey="startSalabilityUpdate" timeout="30"/>
85+
<!--Navigate to group product PDP.-->
86+
<amOnPage url="{{StorefrontProductPage.url($groupedProduct.custom_attributes[url_key]$)}}" stepKey="navigateToGroupedProductPDPAfterOrderPlacement"/>
87+
<waitForPageLoad stepKey="waitForProductPageLoaded2"/>
88+
<!--Verify that product1 is not present.-->
89+
<dontSee selector="{{StorefrontProductInfoMainSection.groupedProductsTable}}" userInput="$product1.name$" stepKey="dontSeeProduct1"/>
90+
<!--Verify that product2 is present.-->
91+
<actionGroup ref="AssertLinkPresenceOnGroupedProductPage" stepKey="verifySecondOptionIsStillPresentedOnPage">
92+
<argument name="productName" value="$product2.name$"/>
93+
</actionGroup>
94+
</test>
95+
</tests>
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
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\InventoryIndexer\Model\Queue;
9+
10+
use Magento\Framework\Exception\LocalizedException;
11+
use Magento\InventorySalesApi\Api\IsProductSalableInterface;
12+
use Magento\InventorySalesApi\Model\GetStockItemDataInterface;
13+
14+
/**
15+
* Get stock status changes for given reservation.
16+
*/
17+
class GetSalabilityDataForUpdate
18+
{
19+
/**
20+
* @var IsProductSalableInterface
21+
*/
22+
private $isProductSalable;
23+
24+
/**
25+
* @var GetStockItemDataInterface
26+
*/
27+
private $getStockItemData;
28+
29+
/**
30+
* @param IsProductSalableInterface $isProductSalable
31+
* @param GetStockItemDataInterface $getStockItemData
32+
*/
33+
public function __construct(
34+
IsProductSalableInterface $isProductSalable,
35+
GetStockItemDataInterface $getStockItemData
36+
) {
37+
$this->isProductSalable = $isProductSalable;
38+
$this->getStockItemData = $getStockItemData;
39+
}
40+
41+
/**
42+
* Get stock status changes for given reservation.
43+
*
44+
* @param ReservationData $reservationData
45+
* @return bool[] - ['sku' => bool]
46+
*/
47+
public function execute(ReservationData $reservationData): array
48+
{
49+
$salabilityData = [];
50+
foreach ($reservationData->getSkus() as $sku) {
51+
$salabilityData[$sku] = $this->isProductSalable->execute($sku, $reservationData->getStock());
52+
}
53+
54+
$data = [];
55+
foreach ($salabilityData as $sku => $isSalable) {
56+
$currentStatus = $this->isCurrentlySalable(
57+
$sku,
58+
$reservationData->getStock()
59+
);
60+
if ($isSalable !== $currentStatus) {
61+
$data[$sku] = $isSalable;
62+
}
63+
}
64+
65+
return $data;
66+
}
67+
68+
/**
69+
* Get current is_salable value.
70+
*
71+
* @param string $sku
72+
* @param int $stockId
73+
*
74+
* @return bool
75+
*/
76+
private function isCurrentlySalable(string $sku, int $stockId): bool
77+
{
78+
try {
79+
$data = $this->getStockItemData->execute($sku, $stockId);
80+
$isSalable = $data ? (bool)$data[GetStockItemDataInterface::IS_SALABLE] : false;
81+
} catch (LocalizedException $e) {
82+
$isSalable = false;
83+
}
84+
85+
return $isSalable;
86+
}
87+
}

InventoryIndexer/Model/Queue/UpdateIndexSalabilityStatus.php

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
use Magento\Framework\Exception\StateException;
1111
use Magento\InventoryCatalogApi\Api\DefaultStockProviderInterface;
12+
use Magento\InventoryIndexer\Model\Queue\UpdateIndexSalabilityStatus\UpdateLegacyStock;
1213
use Magento\InventoryIndexer\Model\Queue\UpdateIndexSalabilityStatus\IndexProcessor;
1314

1415
/**
@@ -25,17 +26,24 @@ class UpdateIndexSalabilityStatus
2526
* @var IndexProcessor
2627
*/
2728
private $indexProcessor;
29+
/**
30+
* @var UpdateLegacyStock
31+
*/
32+
private $updateLegacyStock;
2833

2934
/**
3035
* @param DefaultStockProviderInterface $defaultStockProvider
3136
* @param IndexProcessor $indexProcessor
37+
* @param UpdateLegacyStock $updateLegacyStock
3238
*/
3339
public function __construct(
3440
DefaultStockProviderInterface $defaultStockProvider,
35-
IndexProcessor $indexProcessor
41+
IndexProcessor $indexProcessor,
42+
UpdateLegacyStock $updateLegacyStock
3643
) {
3744
$this->defaultStockProvider = $defaultStockProvider;
3845
$this->indexProcessor = $indexProcessor;
46+
$this->updateLegacyStock = $updateLegacyStock;
3947
}
4048

4149
/**
@@ -50,8 +58,12 @@ public function execute(ReservationData $reservationData): array
5058
{
5159
$stockId = $reservationData->getStock();
5260
$dataForUpdate = [];
53-
if ($stockId !== $this->defaultStockProvider->getId() && $reservationData->getSkus()) {
54-
$dataForUpdate = $this->indexProcessor->execute($reservationData, $stockId);
61+
if ($reservationData->getSkus()) {
62+
if ($stockId !== $this->defaultStockProvider->getId()) {
63+
$dataForUpdate = $this->indexProcessor->execute($reservationData, $stockId);
64+
} else {
65+
$dataForUpdate = $this->updateLegacyStock->execute($reservationData);
66+
}
5567
}
5668

5769
return $dataForUpdate;

InventoryIndexer/Model/Queue/UpdateIndexSalabilityStatus/IndexProcessor.php

Lines changed: 10 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,14 @@
88
namespace Magento\InventoryIndexer\Model\Queue\UpdateIndexSalabilityStatus;
99

1010
use Magento\Framework\App\ResourceConnection;
11-
use Magento\Framework\Exception\LocalizedException;
1211
use Magento\Framework\Exception\StateException;
1312
use Magento\InventoryIndexer\Indexer\InventoryIndexer;
13+
use Magento\InventoryIndexer\Model\Queue\GetSalabilityDataForUpdate;
1414
use Magento\InventoryIndexer\Model\Queue\ReservationData;
1515
use Magento\InventoryIndexer\Model\ResourceModel\UpdateIsSalable;
1616
use Magento\InventoryMultiDimensionalIndexerApi\Model\Alias;
1717
use Magento\InventoryMultiDimensionalIndexerApi\Model\IndexNameBuilder;
1818
use Magento\InventoryMultiDimensionalIndexerApi\Model\IndexStructureInterface;
19-
use Magento\InventorySalesApi\Api\IsProductSalableInterface;
20-
use Magento\InventorySalesApi\Model\GetStockItemDataInterface;
2119

2220
/**
2321
* Update 'is salable' index data processor.
@@ -35,39 +33,31 @@ class IndexProcessor
3533
private $indexStructure;
3634

3735
/**
38-
* @var IsProductSalableInterface
39-
*/
40-
private $isProductSalable;
41-
42-
/**
43-
* @var GetStockItemDataInterface
36+
* @var UpdateIsSalable
4437
*/
45-
private $getStockItemData;
38+
private $updateIsSalable;
4639

4740
/**
48-
* @var UpdateIsSalable
41+
* @var GetSalabilityDataForUpdate
4942
*/
50-
private $updateIsSalable;
43+
private $getSalabilityDataForUpdate;
5144

5245
/**
5346
* @param IndexNameBuilder $indexNameBuilder
5447
* @param IndexStructureInterface $indexStructure
55-
* @param GetStockItemDataInterface $getStockItemData
5648
* @param UpdateIsSalable $updateIsSalable
57-
* @param IsProductSalableInterface $isProductSalable
49+
* @param GetSalabilityDataForUpdate $getSalabilityDataForUpdate
5850
*/
5951
public function __construct(
6052
IndexNameBuilder $indexNameBuilder,
6153
IndexStructureInterface $indexStructure,
62-
GetStockItemDataInterface $getStockItemData,
6354
UpdateIsSalable $updateIsSalable,
64-
IsProductSalableInterface $isProductSalable
55+
GetSalabilityDataForUpdate $getSalabilityDataForUpdate
6556
) {
6657
$this->indexNameBuilder = $indexNameBuilder;
6758
$this->indexStructure = $indexStructure;
68-
$this->isProductSalable = $isProductSalable;
69-
$this->getStockItemData = $getStockItemData;
7059
$this->updateIsSalable = $updateIsSalable;
60+
$this->getSalabilityDataForUpdate = $getSalabilityDataForUpdate;
7161
}
7262

7363
/**
@@ -77,6 +67,7 @@ public function __construct(
7767
* @param int $stockId
7868
* @return bool[]
7969
* @throws StateException
70+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
8071
*/
8172
public function execute(ReservationData $reservationData, int $stockId): array
8273
{
@@ -89,12 +80,7 @@ public function execute(ReservationData $reservationData, int $stockId): array
8980
$this->indexStructure->create($mainIndexName, ResourceConnection::DEFAULT_CONNECTION);
9081
}
9182

92-
$salabilityData = [];
93-
foreach ($reservationData->getSkus() as $sku) {
94-
$salabilityData[$sku] = $this->isProductSalable->execute($sku, $reservationData->getStock());
95-
}
96-
97-
$dataForUpdate = $this->getDataForUpdate($salabilityData, $stockId);
83+
$dataForUpdate = $this->getSalabilityDataForUpdate->execute($reservationData);
9884
$this->updateIsSalable->execute(
9985
$mainIndexName,
10086
$dataForUpdate,
@@ -103,45 +89,4 @@ public function execute(ReservationData $reservationData, int $stockId): array
10389

10490
return $dataForUpdate;
10591
}
106-
107-
/**
108-
* Build data for index update.
109-
*
110-
* @param array $salabilityData
111-
* @param int $stockId
112-
*
113-
* @return bool[] - ['sku' => bool]
114-
*/
115-
private function getDataForUpdate(array $salabilityData, int $stockId): array
116-
{
117-
$data = [];
118-
foreach ($salabilityData as $sku => $isSalable) {
119-
$currentStatus = $this->getIndexSalabilityStatus($sku, $stockId);
120-
if ($isSalable != $currentStatus && $currentStatus !== null) {
121-
$data[$sku] = $isSalable;
122-
}
123-
}
124-
125-
return $data;
126-
}
127-
128-
/**
129-
* Get current index is_salable value.
130-
*
131-
* @param string $sku
132-
* @param int $stockId
133-
*
134-
* @return bool|null
135-
*/
136-
private function getIndexSalabilityStatus(string $sku, int $stockId): ?bool
137-
{
138-
try {
139-
$data = $this->getStockItemData->execute($sku, $stockId);
140-
$isSalable = $data ? (bool)$data[GetStockItemDataInterface::IS_SALABLE] : false;
141-
} catch (LocalizedException $e) {
142-
$isSalable = null;
143-
}
144-
145-
return $isSalable;
146-
}
14792
}

0 commit comments

Comments
 (0)