Skip to content

Commit bb4ea82

Browse files
committed
Merge branch '2.4-develop' of https://github.com/adobe-commerce-tier-4/magento2ce into ACP2E-3286
2 parents 57b3690 + 581b7ef commit bb4ea82

File tree

36 files changed

+1623
-160
lines changed

36 files changed

+1623
-160
lines changed

app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductCustomAttributes.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ function (AttributeInterface $customAttribute) {
106106
if (!array_key_exists($attributeCode, $productData)) {
107107
continue;
108108
}
109-
$attributeValue = $productData[$attributeCode];
109+
$attributeValue = $productData[$attributeCode] ?? "";
110110
if (is_array($attributeValue)) {
111111
$attributeValue = implode(',', $attributeValue);
112112
}

app/code/Magento/CatalogInventory/Model/Config/Source/NotAvailableMessage.php

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@
1414

1515
class NotAvailableMessage implements OptionSourceInterface
1616
{
17+
/**
18+
* Message config values
19+
*/
20+
public const VALUE_ONLY_X_OF_Y = 1;
21+
public const VALUE_NOT_ENOUGH_ITEMS = 2;
22+
1723
/**
1824
* Options getter
1925
*
@@ -23,12 +29,12 @@ public function toOptionArray(): array
2329
{
2430
$options = [];
2531
$options[] = [
26-
'value' => 1,
27-
'label' => __('Only X available for sale. Please adjust the quantity to continue'),
32+
'value' => self::VALUE_ONLY_X_OF_Y,
33+
'label' => __('Only X of Y available'),
2834
];
2935
$options[] = [
30-
'value' => 2,
31-
'label' => __('Not enough items for sale. Please adjust the quantity to continue'),
36+
'value' => self::VALUE_NOT_ENOUGH_ITEMS,
37+
'label' => __('Not enough items for sale'),
3238
];
3339
return $options;
3440
}
@@ -41,8 +47,8 @@ public function toOptionArray(): array
4147
public function toArray(): array
4248
{
4349
return [
44-
1 => __('Only X available for sale. Please adjust the quantity to continue'),
45-
2 => __('Not enough items for sale. Please adjust the quantity to continue')
50+
self::VALUE_ONLY_X_OF_Y => __('Only X of Y available'),
51+
self::VALUE_NOT_ENOUGH_ITEMS => __('Not enough items for sale')
4652
];
4753
}
4854
}

app/code/Magento/CatalogInventory/Model/StockStateProvider.php

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -167,14 +167,15 @@ public function checkQuoteItemQty(StockItemInterface $stockItem, $qty, $summaryQ
167167
}
168168

169169
if (!$this->checkQty($stockItem, $summaryQty) || !$this->checkQty($stockItem, $qty)) {
170-
$message = __('The requested qty is not available');
170+
$message = __('The requested qty. is not available');
171171
if ((int) $this->scopeConfig->getValue('cataloginventory/options/not_available_message') === 1) {
172172
$itemMessage = (__(sprintf(
173-
'Only %s available for sale. Please adjust the quantity to continue',
174-
$stockItem->getQty() - $stockItem->getMinQty()
173+
'Only %s of %s available',
174+
$stockItem->getQty() - $stockItem->getMinQty(),
175+
$this->localeFormat->getNumber($qty)
175176
)));
176177
} else {
177-
$itemMessage = (__('Not enough items for sale. Please adjust the quantity to continue'));
178+
$itemMessage = (__('Not enough items for sale'));
178179
}
179180
$result->setHasError(true)
180181
->setErrorCode('qty_available')
@@ -231,7 +232,7 @@ public function checkQuoteItemQty(StockItemInterface $stockItem, $qty, $summaryQ
231232
}
232233
} elseif ($stockItem->getShowDefaultNotificationMessage()) {
233234
$result->setMessage(
234-
__('The requested qty is not available')
235+
__('The requested qty. is not available')
235236
);
236237
}
237238
}

app/code/Magento/CatalogInventory/i18n/en_US.csv

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ Stock,Stock
7373
"Done","Done"
7474
"The requested qty exceeds the maximum qty allowed in shopping cart","The requested qty exceeds the maximum qty allowed in shopping cart"
7575
"You cannot use decimal quantity for this product.","You cannot use decimal quantity for this product."
76-
"Not enough items for sale. Please adjust the quantity to continue","Not enough items for sale. Please adjust the quantity to continue"
77-
"Only X available for sale. Please adjust the quantity to continue","Only X available for sale. Please adjust the quantity to continue"
78-
"Only %s available for sale. Please adjust the quantity to continue","Only %s available for sale. Please adjust the quantity to continue"
76+
"Only X of Y available","Only X of Y available"
77+
"Only %s of %s available","Only %s of %s available"
78+
"Not enough items for sale","Not enough items for sale"
79+
"The requested qty. is not available","The requested qty. is not available"

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,14 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
4747
}
4848

4949
if ((int) $this->scopeConfig->getValue('cataloginventory/options/not_available_message') === 1) {
50+
$requiredItemQty = ($cartItem->getQtyToAdd() ?? $cartItem->getQty()) + ($cartItem->getPreviousQty() ?? 0);
5051
return sprintf(
51-
'Only %s available for sale. Please adjust the quantity to continue',
52-
(string) $this->productStock->getProductSaleableQty($cartItem)
52+
'Only %s of %s available',
53+
(string) $this->productStock->getProductSaleableQty($cartItem),
54+
(string) $requiredItemQty
5355
);
5456
}
5557

56-
return 'Not enough items for sale. Please adjust the quantity to continue';
58+
return 'Not enough items for sale';
5759
}
5860
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\CatalogInventoryGraphQl\Model\Resolver;
9+
10+
use Magento\Catalog\Api\ProductRepositoryInterface;
11+
use Magento\Catalog\Model\Product;
12+
use Magento\CatalogInventory\Model\StockState;
13+
use Magento\CatalogInventory\Model\Config\Source\NotAvailableMessage;
14+
use Magento\Framework\App\Config\ScopeConfigInterface;
15+
use Magento\Framework\Exception\LocalizedException;
16+
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
17+
use Magento\Framework\GraphQl\Config\Element\Field;
18+
use Magento\Framework\GraphQl\Query\ResolverInterface;
19+
use Magento\Quote\Model\Quote\Item;
20+
use Magento\QuoteGraphQl\Model\CartItem\ProductStock;
21+
22+
/**
23+
* Resolver for ProductInterface quantity
24+
* Returns the available stock quantity based on cataloginventory/options/not_available_message
25+
*/
26+
class QuantityResolver implements ResolverInterface
27+
{
28+
/**
29+
* Configurable product type code
30+
*/
31+
private const PRODUCT_TYPE_CONFIGURABLE = "configurable";
32+
33+
/**
34+
* Scope config path for not_available_message
35+
*/
36+
private const CONFIG_PATH_NOT_AVAILABLE_MESSAGE = "cataloginventory/options/not_available_message";
37+
38+
/**
39+
* @param ProductRepositoryInterface $productRepositoryInterface
40+
* @param ScopeConfigInterface $scopeConfig
41+
* @param StockState $stockState
42+
* @param ProductStock $productStock
43+
*/
44+
public function __construct(
45+
private readonly ProductRepositoryInterface $productRepositoryInterface,
46+
private readonly ScopeConfigInterface $scopeConfig,
47+
private readonly StockState $stockState,
48+
private readonly ProductStock $productStock,
49+
) {
50+
}
51+
52+
/**
53+
* @inheritdoc
54+
*
55+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
56+
*/
57+
public function resolve(
58+
Field $field,
59+
$context,
60+
ResolveInfo $info,
61+
array $value = null,
62+
array $args = null
63+
): ?float {
64+
65+
if ((int) $this->scopeConfig->getValue(
66+
self::CONFIG_PATH_NOT_AVAILABLE_MESSAGE
67+
) === NotAvailableMessage::VALUE_NOT_ENOUGH_ITEMS) {
68+
return null;
69+
}
70+
71+
if (isset($value['cart_item']) && $value['cart_item'] instanceof Item) {
72+
return $this->productStock->getProductAvailableStock($value['cart_item']);
73+
}
74+
75+
if (!isset($value['model'])) {
76+
throw new LocalizedException(__('"model" value should be specified'));
77+
}
78+
79+
/** @var Product $product */
80+
$product = $value['model'];
81+
82+
if ($product->getTypeId() === self::PRODUCT_TYPE_CONFIGURABLE) {
83+
$product = $this->productRepositoryInterface->get($product->getSku());
84+
}
85+
return $this->stockState->getStockQty($product->getId());
86+
}
87+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
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")
7+
quantity: Float @doc(description: "Amount of available stock") @resolver(class: "Magento\\CatalogInventoryGraphQl\\Model\\Resolver\\QuantityResolver")
78
}
89

910
enum ProductStockStatus @doc(description: "States whether a product stock status is in stock or out of stock.") {

app/code/Magento/CustomerGraphQl/Model/Context/AddUserInfoToContext.php

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@
99

1010
use Magento\Authorization\Model\UserContextInterface;
1111
use Magento\Customer\Api\Data\CustomerInterface;
12+
use Magento\Customer\Model\Config\Share;
1213
use Magento\Customer\Model\ResourceModel\CustomerRepository;
1314
use Magento\Customer\Model\Session;
1415
use Magento\Framework\ObjectManager\ResetAfterRequestInterface;
1516
use Magento\GraphQl\Model\Query\ContextParametersInterface;
1617
use Magento\GraphQl\Model\Query\UserContextParametersProcessorInterface;
18+
use Magento\Store\Model\StoreManagerInterface;
1719

1820
/**
1921
* @SuppressWarnings(PHPMD.CookieAndSessionMisuse)
@@ -40,20 +42,35 @@ class AddUserInfoToContext implements UserContextParametersProcessorInterface, R
4042
*/
4143
private $customerRepository;
4244

45+
/**
46+
* @var Share
47+
*/
48+
private $configShare;
49+
50+
/**
51+
* @var StoreManagerInterface
52+
*/
53+
private $storeManager;
4354
/**
4455
* @param UserContextInterface $userContext
4556
* @param Session $session
4657
* @param CustomerRepository $customerRepository
58+
* @param Share $configShare
59+
* @param StoreManagerInterface $storeManager
4760
*/
4861
public function __construct(
4962
UserContextInterface $userContext,
5063
Session $session,
51-
CustomerRepository $customerRepository
64+
CustomerRepository $customerRepository,
65+
Share $configShare,
66+
StoreManagerInterface $storeManager
5267
) {
5368
$this->userContext = $userContext;
5469
$this->userContextFromConstructor = $userContext;
5570
$this->session = $session;
5671
$this->customerRepository = $customerRepository;
72+
$this->configShare = $configShare;
73+
$this->storeManager = $storeManager;
5774
}
5875

5976
/**
@@ -119,8 +136,14 @@ public function getLoggedInCustomerData(): ?CustomerInterface
119136
*/
120137
private function isCustomer(?int $customerId, ?int $customerType): bool
121138
{
122-
return !empty($customerId)
139+
$result = !empty($customerId)
123140
&& !empty($customerType)
124141
&& $customerType === UserContextInterface::USER_TYPE_CUSTOMER;
142+
143+
if ($result && $this->configShare->isWebsiteScope()) {
144+
$customer = $this->customerRepository->getById($customerId);
145+
return (int)$customer->getWebsiteId() === (int)$this->storeManager->getStore()->getWebsiteId();
146+
}
147+
return $result;
125148
}
126149
}

app/code/Magento/QuoteGraphQl/Model/Cart/AddSimpleProductToCart.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ public function execute(Quote $cart, array $cartItemData): void
6464
try {
6565
$result = $cart->addProduct($product, $this->buyRequestBuilder->build($cartItemData));
6666
} catch (Exception $e) {
67+
68+
if (str_contains($e->getMessage(), 'The requested qty is not available')) {
69+
throw new GraphQlInputException(__('The requested qty. is not available'));
70+
}
71+
6772
throw new GraphQlInputException(
6873
__(
6974
'Could not add the product with SKU %sku to the shopping cart: %message',

app/code/Magento/QuoteGraphQl/Model/Cart/UpdateCartItem.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,6 @@
1616
use Magento\Quote\Model\Quote;
1717
use Magento\Quote\Model\Quote\Item;
1818

19-
/**
20-
* Update cart item
21-
*/
2219
class UpdateCartItem
2320
{
2421
/**
@@ -129,6 +126,9 @@ private function validateCartItem(Item $cartItem): void
129126
if ($cartItem->getHasError()) {
130127
$errors = [];
131128
foreach ($cartItem->getMessage(false) as $message) {
129+
if (str_contains($message, 'The requested qty is not available')) {
130+
throw new GraphQlInputException(__('The requested qty. is not available'));
131+
}
132132
$errors[] = $message;
133133
}
134134
if (!empty($errors)) {

0 commit comments

Comments
 (0)