Skip to content

Commit ef33008

Browse files
authored
Merge branch '2.4-develop' into feature/php8.1-constructor-property-promotion-wee-graph-ql
2 parents c8de27d + 581b7ef commit ef33008

File tree

38 files changed

+1755
-162
lines changed

38 files changed

+1755
-162
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/ImportExport/Controller/Adminhtml/Import/Validate.php

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ class Validate extends ImportResultController implements HttpPostActionInterface
2727
*/
2828
private $import;
2929

30+
/**
31+
* @var Import
32+
*/
33+
private $_validateRowError = false;
34+
3035
/**
3136
* Validate uploaded files action
3237
*
@@ -80,11 +85,11 @@ private function processValidationResult($validationResult, $resultBlock)
8085
{
8186
$import = $this->getImport();
8287
$errorAggregator = $import->getErrorAggregator();
83-
8488
if ($import->getProcessedRowsCount()) {
8589
if ($validationResult) {
8690
$totalError = $errorAggregator->getErrorsCount();
8791
$totalRows = $import->getProcessedRowsCount();
92+
$this->validateRowError($errorAggregator, $totalRows);
8893
$this->addMessageForValidResult($resultBlock, $totalError, $totalRows);
8994
} else {
9095
$resultBlock->addError(
@@ -115,6 +120,24 @@ private function processValidationResult($validationResult, $resultBlock)
115120
}
116121
}
117122

123+
/**
124+
* Validate row error.
125+
*
126+
* @param object $errorAggregator
127+
* @param int $totalRows
128+
* @return bool
129+
*/
130+
private function validateRowError(object $errorAggregator, int $totalRows): bool
131+
{
132+
$errors = $errorAggregator->getAllErrors();
133+
$rowNumber = [];
134+
foreach ($errors as $error) {
135+
$rowNumber = array_unique([...$rowNumber , ...[$error->getRowNumber()]]);
136+
}
137+
(count($rowNumber) < $totalRows)? $this->_validateRowError = true : $this->_validateRowError = false;
138+
return $this->_validateRowError;
139+
}
140+
118141
/**
119142
* Provides import model.
120143
*
@@ -163,7 +186,7 @@ private function addMessageToSkipErrors(Result $resultBlock)
163186
*/
164187
private function addMessageForValidResult(Result $resultBlock, $totalError, $totalRows)
165188
{
166-
if ($this->getImport()->isImportAllowed() && $totalRows > $totalError) {
189+
if ($this->getImport()->isImportAllowed() && ($totalRows > $totalError || $this->_validateRowError)) {
167190
$resultBlock->addSuccess(__('File is valid! To start import process press "Import" button'), true);
168191
} else {
169192
$resultBlock->addError(__('The file is valid, but we can\'t import it for some reason.'));

0 commit comments

Comments
 (0)