Skip to content

Commit 0cf6c27

Browse files
authored
LYNX-816: GQL - quantity field in ProductInterface not return saleable qty
1 parent 8116770 commit 0cf6c27

File tree

3 files changed

+76
-19
lines changed

3 files changed

+76
-19
lines changed

app/code/Magento/CatalogInventoryGraphQl/Model/Resolver/QuantityResolver.php

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
<?php
22
/**
3-
* Copyright © Magento, Inc. All rights reserved.
4-
* See COPYING.txt for license details.
3+
* Copyright 2024 Adobe
4+
* All Rights Reserved.
55
*/
66
declare(strict_types=1);
77

88
namespace Magento\CatalogInventoryGraphQl\Model\Resolver;
99

1010
use Magento\Catalog\Api\ProductRepositoryInterface;
1111
use Magento\Catalog\Model\Product;
12-
use Magento\CatalogInventory\Model\StockState;
1312
use Magento\CatalogInventory\Model\Config\Source\NotAvailableMessage;
1413
use Magento\Framework\App\Config\ScopeConfigInterface;
1514
use Magento\Framework\Exception\LocalizedException;
@@ -36,16 +35,16 @@ class QuantityResolver implements ResolverInterface
3635
private const CONFIG_PATH_NOT_AVAILABLE_MESSAGE = "cataloginventory/options/not_available_message";
3736

3837
/**
38+
* QuantityResolver Constructor
39+
*
3940
* @param ProductRepositoryInterface $productRepositoryInterface
4041
* @param ScopeConfigInterface $scopeConfig
41-
* @param StockState $stockState
4242
* @param ProductStock $productStock
4343
*/
4444
public function __construct(
4545
private readonly ProductRepositoryInterface $productRepositoryInterface,
4646
private readonly ScopeConfigInterface $scopeConfig,
47-
private readonly StockState $stockState,
48-
private readonly ProductStock $productStock,
47+
private readonly ProductStock $productStock
4948
) {
5049
}
5150

@@ -69,7 +68,7 @@ public function resolve(
6968
}
7069

7170
if (isset($value['cart_item']) && $value['cart_item'] instanceof Item) {
72-
return $this->productStock->getProductAvailableStock($value['cart_item']);
71+
return $this->productStock->getSaleableQtyByCartItem($value['cart_item'], null);
7372
}
7473

7574
if (!isset($value['model'])) {
@@ -82,6 +81,7 @@ public function resolve(
8281
if ($product->getTypeId() === self::PRODUCT_TYPE_CONFIGURABLE) {
8382
$product = $this->productRepositoryInterface->get($product->getSku());
8483
}
85-
return $this->stockState->getStockQty($product->getId());
84+
85+
return $this->productStock->getSaleableQty($product, null);
8686
}
8787
}

app/code/Magento/QuoteGraphQl/Model/CartItem/ProductStock.php

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -202,10 +202,10 @@ private function getLowestStockValueOfBundleProduct(Item $cartItem): float
202202
* Returns the lowest stock value of bundle product
203203
*
204204
* @param Item $cartItem
205-
* @param float $thresholdQty
205+
* @param float|null $thresholdQty
206206
* @return float
207207
*/
208-
private function getLowestSaleableQtyOfBundleProduct(Item $cartItem, float $thresholdQty): float
208+
private function getLowestSaleableQtyOfBundleProduct(Item $cartItem, ?float $thresholdQty): float
209209
{
210210
$bundleStock = [];
211211
foreach ($cartItem->getQtyOptions() as $qtyOption) {
@@ -231,7 +231,20 @@ public function getProductSaleableQty(Item $cartItem): float
231231
if ($thresholdQty === 0.0) {
232232
return $this->getProductAvailableStock($cartItem);
233233
}
234-
234+
235+
return $this->getSaleableQtyByCartItem($cartItem, $thresholdQty);
236+
}
237+
238+
/**
239+
* Returns the saleable qty value by cart item
240+
*
241+
* @param Item $cartItem
242+
* @param float|null $thresholdQty
243+
* @return float
244+
* @throws NoSuchEntityException
245+
*/
246+
public function getSaleableQtyByCartItem(Item $cartItem, ?float $thresholdQty): float
247+
{
235248
if ($cartItem->getProductType() === self::PRODUCT_TYPE_BUNDLE) {
236249
return $this->getLowestSaleableQtyOfBundleProduct($cartItem, $thresholdQty);
237250
}
@@ -248,15 +261,18 @@ public function getProductSaleableQty(Item $cartItem): float
248261
* Get product saleable qty when "Catalog > Inventory > Stock Options > Only X left Threshold" is greater than 0
249262
*
250263
* @param ProductInterface $product
251-
* @param float $thresholdQty
264+
* @param float|null $thresholdQty
252265
* @return float
253266
*/
254-
private function getSaleableQty(ProductInterface $product, float $thresholdQty): float
267+
public function getSaleableQty(ProductInterface $product, ?float $thresholdQty): float
255268
{
256-
$stockItem = $this->stockRegistry->getStockItem($product->getId());
257269
$stockStatus = $this->stockRegistry->getStockStatus($product->getId(), $product->getStore()->getWebsiteId());
258-
$stockCurrentQty = $stockStatus->getQty();
259-
$stockLeft = $stockCurrentQty - $stockItem->getMinQty();
260-
return ($stockCurrentQty >= 0 && $stockLeft <= $thresholdQty) ? (float)$stockCurrentQty : 0.0;
270+
$stockQty = (float)$stockStatus->getQty();
271+
if ($thresholdQty === null) {
272+
return $stockQty;
273+
}
274+
$stockLeft = $stockQty - $this->stockRegistry->getStockItem($product->getId())->getMinQty();
275+
276+
return ($stockQty >= 0 && $stockLeft <= $thresholdQty) ? $stockQty : 0.0;
261277
}
262278
}

dev/tests/api-functional/testsuite/Magento/GraphQl/CatalogInventory/StockQuantityTest.php

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?php
22
/**
3-
* Copyright © Magento, Inc. All rights reserved.
4-
* See COPYING.txt for license details.
3+
* Copyright 2024 Adobe
4+
* All Rights Reserved.
55
*/
66
declare(strict_types=1);
77

@@ -13,6 +13,12 @@
1313
use Magento\Bundle\Test\Fixture\Product as BundleProductFixture;
1414
use Magento\Catalog\Test\Fixture\Product as ProductFixture;
1515
use Magento\Catalog\Test\Fixture\ProductStock as ProductStockFixture;
16+
use Magento\Checkout\Test\Fixture\PlaceOrder as PlaceOrderFixture;
17+
use Magento\Checkout\Test\Fixture\SetBillingAddress as SetBillingAddressFixture;
18+
use Magento\Checkout\Test\Fixture\SetDeliveryMethod as SetDeliveryMethodFixture;
19+
use Magento\Checkout\Test\Fixture\SetGuestEmail as SetGuestEmailFixture;
20+
use Magento\Checkout\Test\Fixture\SetPaymentMethod as SetPaymentMethodFixture;
21+
use Magento\Checkout\Test\Fixture\SetShippingAddress as SetShippingAddressFixture;
1622
use Magento\ConfigurableProduct\Test\Fixture\AddProductToCart as AddConfigurableProductToCartFixture;
1723
use Magento\ConfigurableProduct\Test\Fixture\Attribute as AttributeFixture;
1824
use Magento\ConfigurableProduct\Test\Fixture\Product as ConfigurableProductFixture;
@@ -141,6 +147,41 @@ public function testStockQuantityEmpty(): void
141147
$this->assertProductStockQuantity(null);
142148
}
143149

150+
#[
151+
Config('cataloginventory/options/not_available_message', 1),
152+
DataFixture(ProductFixture::class, as: 'product'),
153+
DataFixture(GuestCartFixture::class, as: 'cart'),
154+
DataFixture(ProductStockFixture::class, ['prod_id' => '$product.id$', 'prod_qty' => 10]),
155+
DataFixture(AddProductToCart::class, ['cart_id' => '$cart.id$', 'product_id' => '$product.id$', 'qty' => 2]),
156+
DataFixture(QuoteMaskFixture::class, ['cart_id' => '$cart.id$'], 'quoteIdMask'),
157+
DataFixture(ProductStockFixture::class, ['prod_id' => '$product.id$', 'prod_qty' => 8])
158+
]
159+
public function testSaleableQuantitySimpleProductAfterStockUpdate(): void
160+
{
161+
$this->assertProductStockQuantity(8);
162+
}
163+
164+
#[
165+
Config('cataloginventory/options/not_available_message', 1),
166+
DataFixture(ProductFixture::class, as: 'product'),
167+
DataFixture(ProductStockFixture::class, ['prod_id' => '$product.id$', 'prod_qty' => 10]),
168+
DataFixture(GuestCartFixture::class, as: 'cart'),
169+
DataFixture(AddProductToCart::class, ['cart_id' => '$cart.id$', 'product_id' => '$product.id$', 'qty' => 1]),
170+
DataFixture(QuoteMaskFixture::class, ['cart_id' => '$cart.id$'], 'quoteIdMask'),
171+
DataFixture(GuestCartFixture::class, as: 'cart2'),
172+
DataFixture(AddProductToCart::class, ['cart_id' => '$cart2.id$', 'product_id' => '$product.id$', 'qty' => 5]),
173+
DataFixture(SetBillingAddressFixture::class, ['cart_id' => '$cart2.id$']),
174+
DataFixture(SetShippingAddressFixture::class, ['cart_id' => '$cart2.id$']),
175+
DataFixture(SetGuestEmailFixture::class, ['cart_id' => '$cart2.id$']),
176+
DataFixture(SetDeliveryMethodFixture::class, ['cart_id' => '$cart2.id$']),
177+
DataFixture(SetPaymentMethodFixture::class, ['cart_id' => '$cart2.id$']),
178+
DataFixture(PlaceOrderFixture::class, ['cart_id' => '$cart2.id$'], 'order')
179+
]
180+
public function testSaleableQuantitySimpleProductAfterPlaceOrder(): void
181+
{
182+
$this->assertProductStockQuantity(5);
183+
}
184+
144185
/**
145186
* Asserts products stock quantity from cart & product query
146187
*

0 commit comments

Comments
 (0)