Skip to content

Commit

Permalink
Merge pull request magento#3170 from magento-mpi/MC-34177
Browse files Browse the repository at this point in the history
[Support] Product salability status is not updated for default stock after order is placed until it is shipped
  • Loading branch information
Stanislav Idolov authored Sep 2, 2020
2 parents 06dc12c + d212a12 commit 40e199b
Show file tree
Hide file tree
Showing 10 changed files with 691 additions and 69 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
-->

<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd">
<test name="StorefrontCreateOrderAllQuantityGroupedProductOptionDefaultStockTest">
<annotations>
<stories value="Grouped Product Default Stock."/>
<title value="Place order with all quantity with grouped product option on default stock."/>
<description value="Verify, grouped product option will change status after order placement with all it's quantity on default stock"/>
<severity value="CRITICAL"/>
<group value="msi"/>
<group value="multi_mode"/>
</annotations>
<before>
<!--Login to admin-->
<actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/>
<!--Disable all custom sources-->
<actionGroup ref="DisableAllSourcesActionGroup" stepKey="disableSource"/>
<!--Create category-->
<createData entity="ApiCategory" stepKey="createCategory"/>
<!--Create grouped product-->
<createData entity="ApiGroupedProduct" stepKey="groupedProduct"/>
<!--Create simple product (1) with qty 10-->
<createData entity="ApiSimplePrice10Qty10" stepKey="product1">
<requiredEntity createDataKey="createCategory"/>
</createData>
<!--Create simple product (2) with qty 10-->
<createData entity="ApiSimplePrice10Qty10" stepKey="product2">
<requiredEntity createDataKey="createCategory"/>
</createData>
<!--Add simple product (1) to grouped product-->
<createData entity="OneSimpleProductLink" stepKey="addProductOne">
<requiredEntity createDataKey="groupedProduct"/>
<requiredEntity createDataKey="product1"/>
</createData>
<!--Add simple product (2) to grouped product-->
<updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo">
<requiredEntity createDataKey="groupedProduct"/>
<requiredEntity createDataKey="product2"/>
</updateData>
<!--Create customer-->
<createData entity="MsiCustomer1" stepKey="customer"/>
</before>
<after>
<!--Logout from admin-->
<actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/>
<!--Delete category-->
<deleteData createDataKey="createCategory" stepKey="deleteCategory"/>
<!--Delete grouped product-->
<deleteData createDataKey="groupedProduct" stepKey="deleteGroupedProduct"/>
<!--Delete simple product (1)-->
<deleteData createDataKey="product1" stepKey="deleteSimpleProduct1"/>
<!--Delete simple product (2)-->
<deleteData createDataKey="product2" stepKey="deleteSimpleProduct2"/>
<!--Delete customer-->
<deleteData createDataKey="customer" stepKey="deleteCustomer"/>
</after>

<!--Login To storefront as Customer-->
<actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefront">
<argument name="Customer" value="$$customer$$"/>
</actionGroup>
<!--Navigate to group product PDP.-->
<actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="navigateToGroupedProductPDP">
<argument name="product" value="$groupedProduct$"/>
</actionGroup>
<!--Add grouped product to cart with all available quantity of product (1).-->
<actionGroup ref="StorefrontAddGroupedProductWithTwoLinksToCartActionGroup" stepKey="addGroupedProductToCart">
<argument name="product" value="$groupedProduct$"/>
<argument name="linkedProduct1Name" value="$product1.name$"/>
<argument name="linkedProduct2Name" value="$product2.name$"/>
<argument name="linkedProduct1Qty" value="{{Qty_10.qty}}"/>
<argument name="linkedProduct2Qty" value="{{Qty_1.qty}}"/>
</actionGroup>
<!--Place order.-->
<actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="navigateToCheckoutPage"/>
<click selector="{{CheckoutShippingSection.next}}" stepKey="clickNextButton"/>
<actionGroup ref="ClickPlaceOrderActionGroup" stepKey="clickOnPlaceOrder"/>
<!--Run queue consumer.-->
<magentoCLI command="queue:consumers:start" arguments="inventory.reservations.updateSalabilityStatus" stepKey="startSalabilityUpdate" timeout="30"/>
<!--Navigate to group product PDP.-->
<actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="navigateToGroupedProductPDPAfterOrderPlacement">
<argument name="product" value="$groupedProduct$"/>
</actionGroup>
<!--Verify that product1 is not present.-->
<dontSee selector="{{StorefrontProductInfoMainSection.groupedProductsTable}}" userInput="$product1.name$" stepKey="dontSeeProduct1"/>
<!--Verify that product2 is present.-->
<actionGroup ref="AssertLinkPresenceOnGroupedProductPage" stepKey="verifySecondOptionIsStillPresentedOnPage">
<argument name="productName" value="$product2.name$"/>
</actionGroup>
</test>
</tests>
87 changes: 87 additions & 0 deletions InventoryIndexer/Model/Queue/GetSalabilityDataForUpdate.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\InventoryIndexer\Model\Queue;

use Magento\Framework\Exception\LocalizedException;
use Magento\InventorySalesApi\Api\AreProductsSalableInterface;
use Magento\InventorySalesApi\Model\GetStockItemDataInterface;

/**
* Get stock status changes for given reservation.
*/
class GetSalabilityDataForUpdate
{
/**
* @var AreProductsSalableInterface
*/
private $areProductsSalable;

/**
* @var GetStockItemDataInterface
*/
private $getStockItemData;

/**
* @param AreProductsSalableInterface $areProductsSalable
* @param GetStockItemDataInterface $getStockItemData
*/
public function __construct(
AreProductsSalableInterface $areProductsSalable,
GetStockItemDataInterface $getStockItemData
) {
$this->areProductsSalable = $areProductsSalable;
$this->getStockItemData = $getStockItemData;
}

/**
* Get stock status changes for given reservation.
*
* @param ReservationData $reservationData
* @return bool[] - ['sku' => bool]
*/
public function execute(ReservationData $reservationData): array
{
$salabilityData = $this->areProductsSalable->execute(
$reservationData->getSkus(),
$reservationData->getStock()
);

$data = [];
foreach ($salabilityData as $isProductSalableResult) {
$currentStatus = $this->isCurrentlySalable(
$isProductSalableResult->getSku(),
$reservationData->getStock()
);
if ($isProductSalableResult->isSalable() !== $currentStatus) {
$data[$isProductSalableResult->getSku()] = $isProductSalableResult->isSalable();
}
}

return $data;
}

/**
* Get current is_salable value.
*
* @param string $sku
* @param int $stockId
*
* @return bool
*/
private function isCurrentlySalable(string $sku, int $stockId): bool
{
try {
$data = $this->getStockItemData->execute($sku, $stockId);
$isSalable = $data ? (bool)$data[GetStockItemDataInterface::IS_SALABLE] : false;
} catch (LocalizedException $e) {
$isSalable = false;
}

return $isSalable;
}
}
18 changes: 15 additions & 3 deletions InventoryIndexer/Model/Queue/UpdateIndexSalabilityStatus.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

use Magento\Framework\Exception\StateException;
use Magento\InventoryCatalogApi\Api\DefaultStockProviderInterface;
use Magento\InventoryIndexer\Model\Queue\UpdateIndexSalabilityStatus\UpdateLegacyStock;
use Magento\InventoryIndexer\Model\Queue\UpdateIndexSalabilityStatus\IndexProcessor;

/**
Expand All @@ -25,17 +26,24 @@ class UpdateIndexSalabilityStatus
* @var IndexProcessor
*/
private $indexProcessor;
/**
* @var UpdateLegacyStock
*/
private $updateLegacyStock;

/**
* @param DefaultStockProviderInterface $defaultStockProvider
* @param IndexProcessor $indexProcessor
* @param UpdateLegacyStock $updateLegacyStock
*/
public function __construct(
DefaultStockProviderInterface $defaultStockProvider,
IndexProcessor $indexProcessor
IndexProcessor $indexProcessor,
UpdateLegacyStock $updateLegacyStock
) {
$this->defaultStockProvider = $defaultStockProvider;
$this->indexProcessor = $indexProcessor;
$this->updateLegacyStock = $updateLegacyStock;
}

/**
Expand All @@ -50,8 +58,12 @@ public function execute(ReservationData $reservationData): array
{
$stockId = $reservationData->getStock();
$dataForUpdate = [];
if ($stockId !== $this->defaultStockProvider->getId() && $reservationData->getSkus()) {
$dataForUpdate = $this->indexProcessor->execute($reservationData, $stockId);
if ($reservationData->getSkus()) {
if ($stockId !== $this->defaultStockProvider->getId()) {
$dataForUpdate = $this->indexProcessor->execute($reservationData, $stockId);
} else {
$dataForUpdate = $this->updateLegacyStock->execute($reservationData);
}
}

return $dataForUpdate;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,14 @@
namespace Magento\InventoryIndexer\Model\Queue\UpdateIndexSalabilityStatus;

use Magento\Framework\App\ResourceConnection;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\StateException;
use Magento\InventoryIndexer\Indexer\InventoryIndexer;
use Magento\InventoryIndexer\Model\Queue\GetSalabilityDataForUpdate;
use Magento\InventoryIndexer\Model\Queue\ReservationData;
use Magento\InventoryIndexer\Model\ResourceModel\UpdateIsSalable;
use Magento\InventoryMultiDimensionalIndexerApi\Model\Alias;
use Magento\InventoryMultiDimensionalIndexerApi\Model\IndexNameBuilder;
use Magento\InventoryMultiDimensionalIndexerApi\Model\IndexStructureInterface;
use Magento\InventorySalesApi\Api\AreProductsSalableInterface;
use Magento\InventorySalesApi\Api\Data\IsProductSalableResultInterface;
use Magento\InventorySalesApi\Model\GetStockItemDataInterface;

/**
* Update 'is salable' index data processor.
Expand All @@ -36,39 +33,31 @@ class IndexProcessor
private $indexStructure;

/**
* @var AreProductsSalableInterface
*/
private $areProductsSalable;

/**
* @var GetStockItemDataInterface
* @var UpdateIsSalable
*/
private $getStockItemData;
private $updateIsSalable;

/**
* @var UpdateIsSalable
* @var GetSalabilityDataForUpdate
*/
private $updateIsSalable;
private $getSalabilityDataForUpdate;

/**
* @param IndexNameBuilder $indexNameBuilder
* @param IndexStructureInterface $indexStructure
* @param AreProductsSalableInterface $areProductsSalable
* @param GetStockItemDataInterface $getStockItemData
* @param UpdateIsSalable $updateIsSalable
* @param GetSalabilityDataForUpdate $getSalabilityDataForUpdate
*/
public function __construct(
IndexNameBuilder $indexNameBuilder,
IndexStructureInterface $indexStructure,
AreProductsSalableInterface $areProductsSalable,
GetStockItemDataInterface $getStockItemData,
UpdateIsSalable $updateIsSalable
UpdateIsSalable $updateIsSalable,
GetSalabilityDataForUpdate $getSalabilityDataForUpdate
) {
$this->indexNameBuilder = $indexNameBuilder;
$this->indexStructure = $indexStructure;
$this->areProductsSalable = $areProductsSalable;
$this->getStockItemData = $getStockItemData;
$this->updateIsSalable = $updateIsSalable;
$this->getSalabilityDataForUpdate = $getSalabilityDataForUpdate;
}

/**
Expand All @@ -78,6 +67,7 @@ public function __construct(
* @param int $stockId
* @return bool[]
* @throws StateException
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function execute(ReservationData $reservationData, int $stockId): array
{
Expand All @@ -89,12 +79,8 @@ public function execute(ReservationData $reservationData, int $stockId): array
if (!$this->indexStructure->isExist($mainIndexName, ResourceConnection::DEFAULT_CONNECTION)) {
$this->indexStructure->create($mainIndexName, ResourceConnection::DEFAULT_CONNECTION);
}
$salabilityData = $this->areProductsSalable->execute(
$reservationData->getSkus(),
$reservationData->getStock()
);

$dataForUpdate = $this->getDataForUpdate($salabilityData, $stockId);
$dataForUpdate = $this->getSalabilityDataForUpdate->execute($reservationData);
$this->updateIsSalable->execute(
$mainIndexName,
$dataForUpdate,
Expand All @@ -103,45 +89,4 @@ public function execute(ReservationData $reservationData, int $stockId): array

return $dataForUpdate;
}

/**
* Build data for index update.
*
* @param IsProductSalableResultInterface[] $salabilityData
* @param int $stockId
*
* @return bool[] - ['sku' => bool]
*/
private function getDataForUpdate(array $salabilityData, int $stockId): array
{
$data = [];
foreach ($salabilityData as $isProductSalableResult) {
$currentStatus = $this->getIndexSalabilityStatus($isProductSalableResult->getSku(), $stockId);
if ($isProductSalableResult->isSalable() != $currentStatus && $currentStatus !== null) {
$data[$isProductSalableResult->getSku()] = $isProductSalableResult->isSalable();
}
}

return $data;
}

/**
* Get current index is_salable value.
*
* @param string $sku
* @param int $stockId
*
* @return bool|null
*/
private function getIndexSalabilityStatus(string $sku, int $stockId): ?bool
{
try {
$data = $this->getStockItemData->execute($sku, $stockId);
$isSalable = $data ? (bool)$data[GetStockItemDataInterface::IS_SALABLE] : false;
} catch (LocalizedException $e) {
$isSalable = null;
}

return $isSalable;
}
}
Loading

0 comments on commit 40e199b

Please sign in to comment.