88namespace Magento \QuoteGraphQl \Model \CartItem ;
99
1010use Magento \Catalog \Api \Data \ProductInterface ;
11- use Magento \CatalogInventory \Model \StockState ;
1211use 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 ;
1317use Magento \Framework \Exception \NoSuchEntityException ;
1418use 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