Skip to content

Commit c0023b2

Browse files
committed
Merge remote-tracking branch 'adobe-commerce-tier-4/ACP2E-3256' into Tier4-Kings-PR-01-20-2025
2 parents 5f885cc + 11f6b80 commit c0023b2

File tree

5 files changed

+249
-2
lines changed

5 files changed

+249
-2
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\CatalogInventoryGraphQl\Model\Resolver;
9+
10+
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
11+
use Magento\Framework\GraphQl\Config\Element\Field;
12+
use Magento\Framework\GraphQl\Query\ResolverInterface;
13+
use Magento\CatalogInventoryGraphQl\Model\StockItemService;
14+
15+
/**
16+
* Resolver for ProductInterface max quantity
17+
* Returns the available stock max quantity
18+
*/
19+
class MaxSaleQtyResolver implements ResolverInterface
20+
{
21+
/**
22+
* @var StockItemService
23+
*/
24+
private $stockItemService;
25+
26+
/**
27+
* @param StockItemService $stockItemService
28+
*/
29+
public function __construct(
30+
StockItemService $stockItemService
31+
) {
32+
$this->stockItemService = $stockItemService;
33+
}
34+
35+
/**
36+
* @inheritdoc
37+
*/
38+
public function resolve(
39+
Field $field,
40+
$context,
41+
ResolveInfo $info,
42+
?array $value = null,
43+
?array $args = null
44+
) {
45+
$stockItem = $this->stockItemService->getStockItem($value['model']);
46+
return $stockItem?->getMaxSaleQty();
47+
}
48+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\CatalogInventoryGraphQl\Model\Resolver;
9+
10+
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
11+
use Magento\Framework\GraphQl\Config\Element\Field;
12+
use Magento\Framework\GraphQl\Query\ResolverInterface;
13+
use Magento\CatalogInventoryGraphQl\Model\StockItemService;
14+
15+
/**
16+
* Resolver for ProductInterface min quantity
17+
* Returns the available stock min quantity
18+
*/
19+
class MinSaleQtyResolver implements ResolverInterface
20+
{
21+
/**
22+
* @var StockItemService
23+
*/
24+
private $stockItemService;
25+
26+
/**
27+
* @param StockItemService $stockItemService
28+
*/
29+
public function __construct(
30+
StockItemService $stockItemService
31+
) {
32+
$this->stockItemService = $stockItemService;
33+
}
34+
35+
/**
36+
* @inheritdoc
37+
*/
38+
public function resolve(
39+
Field $field,
40+
$context,
41+
ResolveInfo $info,
42+
?array $value = null,
43+
?array $args = null
44+
) {
45+
$stockItem = $this->stockItemService->getStockItem($value['model']);
46+
return $stockItem?->getMinSaleQty();
47+
}
48+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\CatalogInventoryGraphQl\Model;
9+
10+
use Magento\Catalog\Api\ProductRepositoryInterface;
11+
use Magento\Catalog\Model\Product;
12+
use Magento\Framework\Exception\LocalizedException;
13+
use Magento\CatalogInventory\Model\StockRegistry;
14+
use Magento\CatalogInventory\Model\Stock\Item;
15+
16+
/**
17+
* Service to provide stock item for given product
18+
*/
19+
class StockItemService
20+
{
21+
/**
22+
* Configurable product type code
23+
*/
24+
private const PRODUCT_TYPE_CONFIGURABLE = "configurable";
25+
26+
/**
27+
* @var ProductRepositoryInterface
28+
*/
29+
private $productRepositoryInterface;
30+
31+
/**
32+
* @var StockRegistry
33+
*/
34+
private $stockRegistry;
35+
36+
/**
37+
* @param ProductRepositoryInterface $productRepositoryInterface
38+
* @param StockRegistry $stockRegistry
39+
*/
40+
public function __construct(
41+
ProductRepositoryInterface $productRepositoryInterface,
42+
StockRegistry $stockRegistry
43+
) {
44+
$this->productRepositoryInterface = $productRepositoryInterface;
45+
$this->stockRegistry = $stockRegistry;
46+
}
47+
48+
/**
49+
* Returns stock item if the product is available
50+
*
51+
* @param Product|null $product
52+
* @return Item|null
53+
* @throws LocalizedException
54+
*/
55+
public function getStockItem(?Product $product): ?Item
56+
{
57+
if (!isset($product)) {
58+
throw new LocalizedException(__('"model" value should be specified'));
59+
}
60+
if ($product->getTypeId() === self::PRODUCT_TYPE_CONFIGURABLE) {
61+
$product = $this->productRepositoryInterface->get($product->getSku());
62+
}
63+
return $this->stockRegistry->getStockItem($product->getId(), $product->getStore()->getWebsiteId());
64+
}
65+
}

app/code/Magento/CatalogInventoryGraphQl/etc/schema.graphqls

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
# Copyright © Magento, Inc. All rights reserved.
2-
# See COPYING.txt for license details.
1+
# Copyright 2018 Adobe
2+
# All Rights Reserved.
33

44
interface ProductInterface {
55
only_x_left_in_stock: Float @doc(description: "Remaining stock if it is below the value assigned to the Only X Left Threshold option in the Admin.") @resolver(class: "Magento\\CatalogInventoryGraphQl\\Model\\Resolver\\OnlyXLeftInStockResolver")
66
stock_status: ProductStockStatus @doc(description: "The stock status of the product.") @resolver(class: "Magento\\CatalogInventoryGraphQl\\Model\\Resolver\\StockStatusProvider")
77
quantity: Float @doc(description: "Amount of available stock") @resolver(class: "Magento\\CatalogInventoryGraphQl\\Model\\Resolver\\QuantityResolver")
8+
min_sale_qty: Float @doc(description: "Minimum Qty Allowed in Shopping Cart") @resolver(class: "Magento\\CatalogInventoryGraphQl\\Model\\Resolver\\MinSaleQtyResolver")
9+
max_sale_qty: Float @doc(description: "Maximum Qty Allowed in Shopping Cart") @resolver(class: "Magento\\CatalogInventoryGraphQl\\Model\\Resolver\\MaxSaleQtyResolver")
810
}
911

1012
enum ProductStockStatus @doc(description: "States whether a product stock status is in stock or out of stock.") {
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\GraphQl\CatalogInventory;
9+
10+
use Exception;
11+
use Magento\Framework\Exception\LocalizedException;
12+
use Magento\TestFramework\TestCase\GraphQlAbstract;
13+
use Magento\TestFramework\Fixture\DataFixtureStorageManager;
14+
use Magento\TestFramework\Fixture\DataFixture;
15+
use Magento\TestFramework\Fixture\DataFixtureStorage;
16+
use Magento\Catalog\Test\Fixture\Product as ProductFixture;
17+
use Magento\Framework\DataObject;
18+
19+
/**
20+
* Test for product min/max allowed quantity for shopping cart
21+
*/
22+
class MinMaxSaleQtyTest extends GraphQlAbstract
23+
{
24+
/**
25+
* @var DataFixtureStorage
26+
*/
27+
private $fixtures;
28+
29+
/**
30+
* @throws LocalizedException
31+
*/
32+
protected function setUp(): void
33+
{
34+
parent::setUp();
35+
$this->fixtures = DataFixtureStorageManager::getStorage();
36+
}
37+
38+
/**
39+
* Test min/max allowed item quantity for shopping cart
40+
*
41+
* @throws Exception
42+
*/
43+
#[
44+
DataFixture(ProductFixture::class, ['price' => 100.00, 'stock_item' => ['qty' => 1000]], 'product')
45+
]
46+
public function testMinMaxSaleQty(): void
47+
{
48+
$product = $this->fixtures->get('product');
49+
$sku = $product->getSku();
50+
$query = $this->getQuery($sku);
51+
$response = $this->graphQlMutation($query);
52+
$responseDataObject = new DataObject($response);
53+
54+
self::assertEquals(
55+
1,
56+
$responseDataObject->getData('products/items/0/min_sale_qty')
57+
);
58+
self::assertGreaterThanOrEqual(
59+
$responseDataObject->getData('products/items/0/min_sale_qty'),
60+
$responseDataObject->getData('products/items/0/max_sale_qty')
61+
);
62+
}
63+
64+
/**
65+
* query to return product with product.min_sale_qty and product.max_sale_qty
66+
*
67+
* @param string $productSku
68+
* @return string
69+
*/
70+
private function getQuery(string $productSku): string
71+
{
72+
return <<<QUERY
73+
{
74+
products(filter: {sku: {eq: "{$productSku}"}})
75+
{
76+
items {
77+
min_sale_qty,
78+
max_sale_qty
79+
}
80+
}
81+
}
82+
QUERY;
83+
}
84+
}

0 commit comments

Comments
 (0)