Skip to content

Commit 2a406ec

Browse files
authored
LYNX-486: not_available_message and only_x_left_in_stock doesn't show the same available stock (#279)
1 parent 817ba4a commit 2a406ec

File tree

3 files changed

+230
-31
lines changed

3 files changed

+230
-31
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
4949
if ((int) $this->scopeConfig->getValue('cataloginventory/options/not_available_message') === 1) {
5050
return sprintf(
5151
'Only %s available for sale. Please adjust the quantity to continue',
52-
(string) $this->productStock->getProductAvailableStock($cartItem)
52+
(string) $this->productStock->getProductSaleableQty($cartItem)
5353
);
5454
}
5555

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

Lines changed: 74 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,15 @@
88
namespace Magento\QuoteGraphQl\Model\CartItem;
99

1010
use Magento\Catalog\Api\Data\ProductInterface;
11-
use Magento\CatalogInventory\Model\StockState;
1211
use Magento\Catalog\Api\ProductRepositoryInterface;
12+
use Magento\CatalogInventory\Api\StockConfigurationInterface;
13+
use Magento\CatalogInventory\Api\StockRegistryInterface;
14+
use Magento\CatalogInventory\Model\Configuration;
15+
use Magento\CatalogInventory\Model\StockState;
16+
use Magento\Framework\App\Config\ScopeConfigInterface;
1317
use Magento\Framework\Exception\NoSuchEntityException;
1418
use Magento\Quote\Model\Quote\Item;
15-
use Magento\CatalogInventory\Api\StockConfigurationInterface;
19+
use Magento\Store\Model\ScopeInterface;
1620

1721
/**
1822
* Product Stock class to check availability of product
@@ -35,11 +39,15 @@ class ProductStock
3539
* @param ProductRepositoryInterface $productRepositoryInterface
3640
* @param StockState $stockState
3741
* @param StockConfigurationInterface $stockConfiguration
42+
* @param ScopeConfigInterface $scopeConfig
43+
* @param StockRegistryInterface $stockRegistry
3844
*/
3945
public function __construct(
4046
private readonly ProductRepositoryInterface $productRepositoryInterface,
4147
private readonly StockState $stockState,
42-
private readonly StockConfigurationInterface $stockConfiguration
48+
private readonly StockConfigurationInterface $stockConfiguration,
49+
private readonly ScopeConfigInterface $scopeConfig,
50+
private readonly StockRegistryInterface $stockRegistry
4351
) {
4452
}
4553

@@ -74,6 +82,7 @@ public function isProductAvailable(Item $cartItem): bool
7482
* @param int $previousQty
7583
* @param int|float $requestedQty
7684
* @return bool
85+
* @throws NoSuchEntityException
7786
*/
7887
public function isStockAvailableBundle(Item $cartItem, int $previousQty, $requestedQty): bool
7988
{
@@ -188,4 +197,66 @@ private function getLowestStockValueOfBundleProduct(Item $cartItem): float
188197

189198
return min($bundleStock);
190199
}
200+
201+
/**
202+
* Returns the lowest stock value of bundle product
203+
*
204+
* @param Item $cartItem
205+
* @param float $thresholdQty
206+
* @return float
207+
*/
208+
private function getLowestSaleableQtyOfBundleProduct(Item $cartItem, float $thresholdQty): float
209+
{
210+
$bundleStock = [];
211+
foreach ($cartItem->getQtyOptions() as $qtyOption) {
212+
$bundleStock[] = $this->getSaleableQty($qtyOption->getProduct(), $thresholdQty);
213+
}
214+
return $bundleStock ? (float)min($bundleStock) : 0.0;
215+
}
216+
217+
/**
218+
* Returns the cart item's saleable qty value
219+
*
220+
* @param Item $cartItem
221+
* @return float
222+
* @throws NoSuchEntityException
223+
*/
224+
public function getProductSaleableQty(Item $cartItem): float
225+
{
226+
$thresholdQty = (float)$this->scopeConfig->getValue(
227+
Configuration::XML_PATH_STOCK_THRESHOLD_QTY,
228+
ScopeInterface::SCOPE_STORE
229+
);
230+
231+
if ($thresholdQty === 0.0) {
232+
return $this->getProductAvailableStock($cartItem);
233+
}
234+
235+
if ($cartItem->getProductType() === self::PRODUCT_TYPE_BUNDLE) {
236+
return $this->getLowestSaleableQtyOfBundleProduct($cartItem, $thresholdQty);
237+
}
238+
239+
$variantProduct = $this->getVariantProduct($cartItem);
240+
if ($variantProduct !== null) {
241+
return $this->getSaleableQty($variantProduct, $thresholdQty);
242+
}
243+
244+
return $this->getSaleableQty($cartItem->getProduct(), $thresholdQty);
245+
}
246+
247+
/**
248+
* Get product saleable qty when "Catalog > Inventory > Stock Options > Only X left Threshold" is greater than 0
249+
*
250+
* @param ProductInterface $product
251+
* @param float $thresholdQty
252+
* @return float
253+
*/
254+
private function getSaleableQty(ProductInterface $product, float $thresholdQty): float
255+
{
256+
$stockItem = $this->stockRegistry->getStockItem($product->getId());
257+
$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;
261+
}
191262
}

0 commit comments

Comments
 (0)