Skip to content

Commit d7937c4

Browse files
Merge branch '2.4-develop' of github.com:magento-gl/magento2ce into ACQE-Version4-1-functional-mainline-deployment
2 parents cd49830 + eb491c0 commit d7937c4

File tree

51 files changed

+2850
-407
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+2850
-407
lines changed

.github/app-projects-boards-automation.config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ automations:
219219
- ['merged', 'eq', true]
220220
actions:
221221
- moveTo: [23, 'Recently Merged'] #['Pull Requests Dashboard', 'Recently Merged']
222+
- moveTo: [22, 'Recently Merged'] #['Community Dashboard', 'Recently Merged']
222223

223224
# 18. Whenever a pull request is closed:
224225
# a. it must be removed from the "Pull Requests Dashboard" project

app/code/Magento/Catalog/etc/db_schema.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,10 @@
135135
<index referenceId="CATALOG_PRODUCT_ENTITY_INT_ATTRIBUTE_ID" indexType="btree">
136136
<column name="attribute_id"/>
137137
</index>
138+
<index referenceId="CATALOG_PRODUCT_ENTITY_INT_ATTRIBUTE_ID_ENTITY_ID" indexType="btree">
139+
<column name="attribute_id"/>
140+
<column name="entity_id"/>
141+
</index>
138142
<index referenceId="CATALOG_PRODUCT_ENTITY_INT_STORE_ID" indexType="btree">
139143
<column name="store_id"/>
140144
</index>

app/code/Magento/Catalog/etc/db_schema_whitelist.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
},
7070
"index": {
7171
"CATALOG_PRODUCT_ENTITY_INT_ATTRIBUTE_ID": true,
72+
"CATALOG_PRODUCT_ENTITY_INT_ATTRIBUTE_ID_ENTITY_ID": true,
7273
"CATALOG_PRODUCT_ENTITY_INT_STORE_ID": true,
7374
"CATALOG_PRODUCT_ENTITY_INT_ATTRIBUTE_ID_STORE_ID_VALUE": true
7475
},

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

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
<?php
22
/**
3-
* Copyright © Magento, Inc. All rights reserved.
4-
* See COPYING.txt for license details.
3+
* Copyright 2024 Adobe
4+
* All Rights Reserved.
55
*/
66
declare(strict_types=1);
77

88
namespace Magento\CatalogInventoryGraphQl\Model\Resolver;
99

1010
use Magento\Catalog\Api\ProductRepositoryInterface;
1111
use Magento\Catalog\Model\Product;
12-
use Magento\CatalogInventory\Model\StockState;
1312
use Magento\CatalogInventory\Model\Config\Source\NotAvailableMessage;
1413
use Magento\Framework\App\Config\ScopeConfigInterface;
1514
use Magento\Framework\Exception\LocalizedException;
@@ -36,16 +35,16 @@ class QuantityResolver implements ResolverInterface
3635
private const CONFIG_PATH_NOT_AVAILABLE_MESSAGE = "cataloginventory/options/not_available_message";
3736

3837
/**
38+
* QuantityResolver Constructor
39+
*
3940
* @param ProductRepositoryInterface $productRepositoryInterface
4041
* @param ScopeConfigInterface $scopeConfig
41-
* @param StockState $stockState
4242
* @param ProductStock $productStock
4343
*/
4444
public function __construct(
4545
private readonly ProductRepositoryInterface $productRepositoryInterface,
4646
private readonly ScopeConfigInterface $scopeConfig,
47-
private readonly StockState $stockState,
48-
private readonly ProductStock $productStock,
47+
private readonly ProductStock $productStock
4948
) {
5049
}
5150

@@ -69,7 +68,7 @@ public function resolve(
6968
}
7069

7170
if (isset($value['cart_item']) && $value['cart_item'] instanceof Item) {
72-
return $this->productStock->getProductAvailableStock($value['cart_item']);
71+
return $this->productStock->getSaleableQtyByCartItem($value['cart_item'], null);
7372
}
7473

7574
if (!isset($value['model'])) {
@@ -82,6 +81,7 @@ public function resolve(
8281
if ($product->getTypeId() === self::PRODUCT_TYPE_CONFIGURABLE) {
8382
$product = $this->productRepositoryInterface->get($product->getSku());
8483
}
85-
return $this->stockState->getStockQty($product->getId());
84+
85+
return $this->productStock->getSaleableQty($product, null);
8686
}
8787
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\CatalogRule\Controller\Adminhtml\Promo\Catalog;
9+
10+
use Magento\Backend\App\AbstractAction;
11+
12+
abstract class MassAction extends AbstractAction
13+
{
14+
/**
15+
* Authorization level of a basic admin session
16+
*
17+
* @see _isAllowed()
18+
*/
19+
public const ADMIN_RESOURCE = 'Magento_CatalogRule::promo_catalog';
20+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\CatalogRule\Controller\Adminhtml\Promo\Catalog;
9+
10+
use Magento\Backend\App\Action\Context;
11+
use Magento\CatalogRule\Api\CatalogRuleRepositoryInterface;
12+
use Magento\Framework\App\Action\HttpGetActionInterface;
13+
use Magento\Framework\App\Action\HttpPostActionInterface;
14+
use Magento\Framework\Controller\ResultFactory;
15+
16+
class MassActivate extends MassAction implements HttpPostActionInterface, HttpGetActionInterface
17+
{
18+
/**
19+
* @var CatalogRuleRepositoryInterface
20+
*/
21+
private $ruleRepository;
22+
23+
/**
24+
* @param Context $context
25+
* @param CatalogRuleRepositoryInterface $ruleRepository
26+
*/
27+
public function __construct(
28+
Context $context,
29+
CatalogRuleRepositoryInterface $ruleRepository
30+
) {
31+
$this->ruleRepository = $ruleRepository;
32+
parent::__construct($context);
33+
}
34+
35+
/**
36+
* Bulk activate catalog price rule
37+
*
38+
* @return \Magento\Framework\App\ResponseInterface|\Magento\Framework\Controller\ResultInterface
39+
*/
40+
public function execute()
41+
{
42+
$ids = $this->getRequest()->getParam('catalogpricerule');
43+
try {
44+
if ($ids) {
45+
foreach ($ids as $id) {
46+
$model = $this->ruleRepository->get($id);
47+
$model->setIsActive(1);
48+
$this->ruleRepository->save($model);
49+
}
50+
$this->messageManager->addSuccessMessage(__('You activated a total of %1 records.', count($ids)));
51+
} else {
52+
$this->messageManager->addErrorMessage(__('Please select a catalog price rule(s)'));
53+
}
54+
} catch (\Exception $e) {
55+
$this->messageManager->addErrorMessage($e->getMessage());
56+
}
57+
return $this->resultFactory->create(ResultFactory::TYPE_REDIRECT)->setPath('catalog_rule/*/');
58+
}
59+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\CatalogRule\Controller\Adminhtml\Promo\Catalog;
9+
10+
use Magento\Backend\App\Action\Context;
11+
use Magento\CatalogRule\Api\CatalogRuleRepositoryInterface;
12+
use Magento\Framework\App\Action\HttpGetActionInterface;
13+
use Magento\Framework\App\Action\HttpPostActionInterface;
14+
use Magento\Framework\Controller\ResultFactory;
15+
16+
class MassDeactivate extends MassAction implements HttpPostActionInterface, HttpGetActionInterface
17+
{
18+
/**
19+
* @var CatalogRuleRepositoryInterface
20+
*/
21+
private $ruleRepository;
22+
23+
/**
24+
* @param Context $context
25+
* @param CatalogRuleRepositoryInterface $ruleRepository
26+
*/
27+
public function __construct(
28+
Context $context,
29+
CatalogRuleRepositoryInterface $ruleRepository
30+
) {
31+
$this->ruleRepository = $ruleRepository;
32+
parent::__construct($context);
33+
}
34+
35+
/**
36+
* Bulk activate catalog price rule
37+
*
38+
* @return \Magento\Framework\App\ResponseInterface|\Magento\Framework\Controller\ResultInterface
39+
*/
40+
public function execute()
41+
{
42+
$ids = $this->getRequest()->getParam('catalogpricerule');
43+
try {
44+
if ($ids) {
45+
foreach ($ids as $id) {
46+
$model = $this->ruleRepository->get($id);
47+
$model->setIsActive(0);
48+
$this->ruleRepository->save($model);
49+
}
50+
$this->messageManager->addSuccessMessage(__('You deactivated a total of %1 records.', count($ids)));
51+
} else {
52+
$this->messageManager->addErrorMessage(__('Please select a catalog price rule(s)'));
53+
}
54+
} catch (\Exception $e) {
55+
$this->messageManager->addErrorMessage($e->getMessage());
56+
}
57+
return $this->resultFactory->create(ResultFactory::TYPE_REDIRECT)->setPath('catalog_rule/*/');
58+
}
59+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\CatalogRule\Controller\Adminhtml\Promo\Catalog;
9+
10+
use Magento\Backend\App\Action\Context;
11+
use Magento\CatalogRule\Api\CatalogRuleRepositoryInterface;
12+
use Magento\Framework\App\Action\HttpGetActionInterface;
13+
use Magento\Framework\App\Action\HttpPostActionInterface;
14+
use Magento\Framework\Controller\ResultFactory;
15+
16+
/**
17+
* Bulk Delete selected catalog price rules
18+
*/
19+
class MassDelete extends MassAction implements HttpPostActionInterface, HttpGetActionInterface
20+
{
21+
/**
22+
* @var CatalogRuleRepositoryInterface
23+
*/
24+
private $ruleRepository;
25+
26+
/**
27+
* @param Context $context
28+
* @param CatalogRuleRepositoryInterface $ruleRepository
29+
*/
30+
public function __construct(
31+
Context $context,
32+
CatalogRuleRepositoryInterface $ruleRepository
33+
) {
34+
$this->ruleRepository = $ruleRepository;
35+
parent::__construct($context);
36+
}
37+
38+
/**
39+
* Delete selected catalog price rules
40+
*
41+
* @return \Magento\Backend\Model\View\Result\Redirect
42+
* @throws \Magento\Framework\Exception\LocalizedException|\Exception
43+
*/
44+
public function execute()
45+
{
46+
$ids = $this->getRequest()->getParam('catalogpricerule');
47+
if ($ids) {
48+
foreach ($ids as $id) {
49+
$this->ruleRepository->deleteById($id);
50+
}
51+
$this->messageManager->addSuccessMessage(__('A total of %1 record(s) were deleted.', count($ids)));
52+
} else {
53+
$this->messageManager->addErrorMessage(__('Please select a catalog price rule(s)'));
54+
}
55+
56+
/** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
57+
$resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT);
58+
59+
return $resultRedirect->setPath('catalog_rule/*/');
60+
}
61+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\CatalogRule\Test\Unit\Controller\Adminhtml\Promo\Catalog;
9+
10+
use Magento\Backend\Model\View\Result\Redirect;
11+
use Magento\Framework\App\RequestInterface;
12+
use Magento\Framework\Controller\ResultFactory;
13+
use Magento\Framework\Message\ManagerInterface;
14+
use PHPUnit\Framework\MockObject\MockObject;
15+
use Magento\Backend\App\Action\Context;
16+
use PHPUnit\Framework\TestCase;
17+
use Magento\CatalogRule\Controller\Adminhtml\Promo\Catalog\MassActivate;
18+
use Magento\CatalogRule\Api\Data\RuleInterface;
19+
use Magento\CatalogRule\Model\Rule;
20+
use Magento\CatalogRule\Api\CatalogRuleRepositoryInterface;
21+
22+
class MassActivateTest extends TestCase
23+
{
24+
/**
25+
* @var CatalogRuleRepositoryInterface|MockObject
26+
*/
27+
private $catalogRuleRepositoryMock;
28+
29+
/**
30+
* @var Context|MockObject
31+
*/
32+
protected $contextMock;
33+
34+
/**
35+
* @var MassActivate
36+
*/
37+
protected $activate;
38+
39+
/**
40+
* @var RequestInterface|MockObject
41+
*/
42+
protected $requestMock;
43+
44+
/**
45+
* @var Rule|MockObject
46+
*/
47+
protected $ruleMock;
48+
49+
/**
50+
* @var ManagerInterface|MockObject
51+
*/
52+
protected $messageManagerMock;
53+
54+
/**
55+
* @var Redirect|MockObject
56+
*/
57+
protected $resultRedirectMock;
58+
59+
/**
60+
* @var ResultFactory|MockObject
61+
*/
62+
protected $resultFactory;
63+
64+
protected function setUp(): void
65+
{
66+
$this->requestMock = $this->getMockBuilder(RequestInterface::class)
67+
->onlyMethods(['getParam'])
68+
->getMockForAbstractClass();
69+
$this->contextMock = $this->createMock(Context::class);
70+
71+
$this->messageManagerMock = $this->getMockBuilder(ManagerInterface::class)
72+
->getMockForAbstractClass();
73+
74+
$this->resultRedirectMock = $this->getMockBuilder(Redirect::class)
75+
->onlyMethods(['setPath'])
76+
->disableOriginalConstructor()
77+
->getMock();
78+
79+
$this->resultFactory = $this->createMock(ResultFactory::class);
80+
$this->resultFactory->method('create')->willReturn($this->resultRedirectMock);
81+
$this->contextMock->method('getResultFactory')->willReturn($this->resultFactory);
82+
$this->contextMock->method('getMessageManager')->willReturn($this->messageManagerMock);
83+
$this->contextMock->method('getRequest')->willReturn($this->requestMock);
84+
$this->catalogRuleRepositoryMock = $this->createMock(
85+
CatalogRuleRepositoryInterface::class
86+
);
87+
$this->activate = new MassActivate($this->contextMock, $this->catalogRuleRepositoryMock);
88+
}
89+
90+
public function testExecute()
91+
{
92+
$data = [1];
93+
$this->requestMock->expects(self::any())
94+
->method('getParam')
95+
->willReturn($data);
96+
$catalogRuleMock = $this->getMockForAbstractClass(RuleInterface::class);
97+
$this->catalogRuleRepositoryMock->expects($this->once())
98+
->method('get')
99+
->with(1)
100+
->willReturn($catalogRuleMock);
101+
$this->messageManagerMock->expects($this->once())
102+
->method('addSuccessMessage')
103+
->with(__('You activated a total of %1 records.', $data));
104+
$this->activate->execute();
105+
}
106+
}

0 commit comments

Comments
 (0)