Skip to content

Commit 791606e

Browse files
committed
Merge branch '2.4-develop' into PR-Tier4-OM-2025-01-24
2 parents 4a1154a + 4de008a commit 791606e

File tree

12 files changed

+406
-24
lines changed

12 files changed

+406
-24
lines changed
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+
}
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+
}
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

+4-2
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.") {

app/code/Magento/Customer/view/frontend/templates/js/customer-data.phtml

+12-11
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
11
<?php
22
/**
3-
* Copyright © Magento, Inc. All rights reserved.
4-
* See COPYING.txt for license details.
3+
* Copyright 2015 Adobe
4+
* All Rights Reserved.
55
*/
66
use Magento\Customer\ViewModel\Customer\Data;
7-
use Magento\Framework\App\ObjectManager;
87
use Magento\Customer\ViewModel\CookieSettings;
98

10-
/** @var \Magento\Customer\Block\CustomerData $block */
9+
/**
10+
* @var \Magento\Customer\Block\CustomerData $block
11+
* @var \Magento\Framework\Escaper $escaper
12+
*/
1113

1214
// phpcs:disable Magento2.Templates.ThisInTemplate.FoundHelper
1315
/** @var Auth $auth */
14-
$auth = $block->getAuth() ?? ObjectManager::getInstance()->get(Auth::class);
16+
$auth = $block->getAuth();
1517
/** @var JsonSerializer $jsonSerializer */
16-
$jsonSerializer = $block->getJsonSerializer() ??
17-
ObjectManager::getInstance()->get(JsonSerializer::class);
18+
$jsonSerializer = $block->getJsonSerializer();
1819
$customerDataUrl = $block->getCustomerDataUrl('customer/account/updateSession');
1920
$expirableSectionNames = $block->getExpirableSectionNames();
2021
/** @var CookieSettings $cookieSettings */
@@ -24,14 +25,14 @@ $cookieSettings = $block->getCookieSettings();
2425
{
2526
"*": {
2627
"Magento_Customer/js/customer-data": {
27-
"sectionLoadUrl": "<?= $block->escapeJs($block->getCustomerDataUrl('customer/section/load')) ?>",
28+
"sectionLoadUrl": "<?= $escaper->escapeJs($block->getCustomerDataUrl('customer/section/load')) ?>",
2829
"expirableSectionLifetime": <?= (int)$block->getExpirableSectionLifetime() ?>,
2930
"expirableSectionNames": <?= /* @noEscape */ $jsonSerializer->serialize(
3031
$expirableSectionNames
3132
) ?>,
32-
"cookieLifeTime": "<?= $block->escapeJs($block->getCookieLifeTime()) ?>",
33-
"cookieDomain": "<?= $block->escapeJs($cookieSettings->getCookieDomain()) ?>",
34-
"updateSessionUrl": "<?= $block->escapeJs($customerDataUrl) ?>",
33+
"cookieLifeTime": "<?= $escaper->escapeJs($block->getCookieLifeTime()) ?>",
34+
"cookieDomain": "<?= $escaper->escapeJs($cookieSettings->getCookieDomain()) ?>",
35+
"updateSessionUrl": "<?= $escaper->escapeJs($customerDataUrl) ?>",
3536
"isLoggedIn": "<?= /* @noEscape */ $auth->isLoggedIn() ?>"
3637
}
3738
}

app/code/Magento/User/Controller/Adminhtml/User/Delete.php

+12-4
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
<?php
22
/**
3-
*
4-
* Copyright © Magento, Inc. All rights reserved.
5-
* See COPYING.txt for license details.
3+
* Copyright 2014 Adobe
4+
* All Rights Reserved.
65
*/
76

87
namespace Magento\User\Controller\Adminhtml\User;
98

9+
use Magento\Framework\App\Action\HttpPostActionInterface;
1010
use Magento\User\Block\User\Edit\Tab\Main as UserEdit;
1111
use Magento\Framework\Exception\AuthenticationException;
12+
use Magento\User\Controller\Adminhtml\User;
1213

13-
class Delete extends \Magento\User\Controller\Adminhtml\User
14+
class Delete extends User implements HttpPostActionInterface
1415
{
1516
/**
17+
* Execute
18+
*
1619
* @return void
1720
*/
1821
public function execute()
@@ -38,7 +41,12 @@ public function execute()
3841
/** @var \Magento\User\Model\User $model */
3942
$model = $this->_userFactory->create();
4043
$model->setId($userId);
44+
$deletedUser = $model->load($userId);
4145
$model->delete();
46+
$this->_eventManager->dispatch('log_user_after_delete', [
47+
'deletedUser' => $deletedUser,
48+
'model' => $model,
49+
]);
4250
$this->messageManager->addSuccess(__('You deleted the user.'));
4351
$this->_redirect('adminhtml/*/');
4452
return;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\User\Observer;
9+
10+
use Magento\Framework\Event\ManagerInterface;
11+
use Magento\Framework\Event\Observer;
12+
use Magento\Framework\Event\ObserverInterface;
13+
use Magento\User\Model\User;
14+
15+
/**
16+
* Class used to log details of user deletions in action reports.
17+
*/
18+
class ValidateModelDeleteAfter implements ObserverInterface
19+
{
20+
/**
21+
* @var ManagerInterface
22+
*/
23+
private $_eventManager;
24+
25+
/**
26+
* Constructor
27+
*
28+
* @param ManagerInterface $eventManager
29+
*/
30+
public function __construct(
31+
ManagerInterface $eventManager
32+
) {
33+
$this->_eventManager = $eventManager;
34+
}
35+
36+
/**
37+
* Execute observer
38+
*
39+
* @param Observer $observer
40+
* @return void
41+
*/
42+
public function execute(Observer $observer): void
43+
{
44+
/** @var User $deletedUser */
45+
$deletedUser = $observer->getEvent()->getData('deletedUser');
46+
/** @var User $model */
47+
$model = $observer->getEvent()->getData('model');
48+
if ($deletedUser && $model) {
49+
$model->setData($deletedUser->getData());
50+
if ($model->getOrigData() === null) {
51+
foreach ($model->getData() as $key => $val) {
52+
$model->setOrigData($key, $val);
53+
}
54+
}
55+
$this->_eventManager->dispatch('model_delete_after', ['object' => $model]);
56+
}
57+
}
58+
}

app/code/Magento/User/Test/Unit/Controller/Adminhtml/User/DeleteTest.php

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?php
22
/**
3-
* Copyright © Magento, Inc. All rights reserved.
4-
* See COPYING.txt for license details.
3+
* Copyright 2016 Adobe
4+
* All Rights Reserved.
55
*/
66
declare(strict_types=1);
77

@@ -92,7 +92,7 @@ protected function setUp(): void
9292

9393
$this->userMock = $this->getMockBuilder(User::class)
9494
->disableOriginalConstructor()
95-
->onlyMethods(['getId', 'performIdentityCheck', 'delete'])
95+
->onlyMethods(['getId', 'performIdentityCheck', 'delete', 'load'])
9696
->getMock();
9797

9898
$this->userFactoryMock = $this->getMockBuilder(UserFactory::class)

0 commit comments

Comments
 (0)