Skip to content

Commit 9851872

Browse files
committed
Merge remote-tracking branch 'origin/MC-30155' into 2.4-develop-pr10
2 parents 90c43f1 + 08608a6 commit 9851872

File tree

9 files changed

+345
-2
lines changed

9 files changed

+345
-2
lines changed
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace Magento\SalesRule\Model\Rule;
10+
11+
use Magento\Quote\Model\ResourceModel\Quote;
12+
use Magento\SalesRule\Model\Spi\RuleQuoteRecollectTotalsInterface;
13+
14+
/**
15+
* Forces related quotes to be recollected on demand.
16+
*/
17+
class RuleQuoteRecollectTotalsOnDemand implements RuleQuoteRecollectTotalsInterface
18+
{
19+
/**
20+
* @var Quote
21+
*/
22+
private $quoteResourceModel;
23+
24+
/**
25+
* Initializes dependencies
26+
*
27+
* @param Quote $quoteResourceModel
28+
*/
29+
public function __construct(Quote $quoteResourceModel)
30+
{
31+
$this->quoteResourceModel = $quoteResourceModel;
32+
}
33+
34+
/**
35+
* Set "trigger_recollect" flag for active quotes which the given rule is applied to.
36+
*
37+
* @param int $ruleId
38+
* @return void
39+
*/
40+
public function execute(int $ruleId): void
41+
{
42+
$this->quoteResourceModel->getConnection()
43+
->update(
44+
$this->quoteResourceModel->getMainTable(),
45+
['trigger_recollect' => 1],
46+
[
47+
'is_active = ?' => 1,
48+
'FIND_IN_SET(?, applied_rule_ids)' => $ruleId
49+
]
50+
);
51+
}
52+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace Magento\SalesRule\Model\Spi;
10+
11+
/**
12+
* Recollect totals for rule related quotes
13+
*/
14+
interface RuleQuoteRecollectTotalsInterface
15+
{
16+
/**
17+
* Recollect totals for rule related quotes.
18+
*
19+
* @param int $ruleId
20+
* @return void
21+
*/
22+
public function execute(int $ruleId): void;
23+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace Magento\SalesRule\Observer;
10+
11+
use Magento\Framework\Event\Observer;
12+
use Magento\Framework\Event\ObserverInterface;
13+
use Magento\SalesRule\Model\Rule;
14+
use Magento\SalesRule\Model\Spi\RuleQuoteRecollectTotalsInterface;
15+
16+
/**
17+
* Forces related quotes to be recollected for inactive rule.
18+
*/
19+
class RuleQuoteRecollectTotalsObserver implements ObserverInterface
20+
{
21+
/**
22+
* @var RuleQuoteRecollectTotalsInterface
23+
*/
24+
private $recollectTotals;
25+
26+
/**
27+
* Initializes dependencies
28+
*
29+
* @param RuleQuoteRecollectTotalsInterface $recollectTotals
30+
*/
31+
public function __construct(RuleQuoteRecollectTotalsInterface $recollectTotals)
32+
{
33+
$this->recollectTotals = $recollectTotals;
34+
}
35+
36+
/**
37+
* Forces related quotes to be recollected, if the rule was disabled or deleted.
38+
*
39+
* @param Observer $observer
40+
* @return void
41+
*/
42+
public function execute(Observer $observer): void
43+
{
44+
/** @var Rule $rule */
45+
$rule = $observer->getRule();
46+
if (!$rule->isObjectNew() && (!$rule->getIsActive() || $rule->isDeleted())) {
47+
$this->recollectTotals->execute((int) $rule->getId());
48+
}
49+
}
50+
}

app/code/Magento/SalesRule/etc/di.xml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
<preference for="Magento\SalesRule\Api\Data\ConditionInterface"
1414
type="Magento\SalesRule\Model\Data\Condition" />
1515
<preference for="Magento\SalesRule\Api\Data\RuleSearchResultInterface"
16-
type="Magento\SalesRule\Model\RuleSearchResult" />
16+
type="Magento\Framework\Api\SearchResults" />
1717
<preference for="Magento\SalesRule\Api\Data\RuleLabelInterface"
1818
type="Magento\SalesRule\Model\Data\RuleLabel" />
1919
<preference for="Magento\SalesRule\Api\Data\CouponInterface"
@@ -23,7 +23,7 @@
2323
<preference for="Magento\SalesRule\Model\Spi\CouponResourceInterface"
2424
type="Magento\SalesRule\Model\ResourceModel\Coupon" />
2525
<preference for="Magento\SalesRule\Api\Data\CouponSearchResultInterface"
26-
type="Magento\SalesRule\Model\CouponSearchResult" />
26+
type="Magento\Framework\Api\SearchResults" />
2727
<preference for="Magento\SalesRule\Api\Data\CouponGenerationSpecInterface"
2828
type="Magento\SalesRule\Model\Data\CouponGenerationSpec" />
2929
<preference for="Magento\SalesRule\Api\Data\CouponMassDeleteResultInterface"
@@ -34,6 +34,8 @@
3434
type="Magento\SalesRule\Model\Data\RuleDiscount" />
3535
<preference for="Magento\SalesRule\Api\Data\DiscountDataInterface"
3636
type="Magento\SalesRule\Model\Data\DiscountData" />
37+
<preference for="Magento\SalesRule\Model\Spi\RuleQuoteRecollectTotalsInterface"
38+
type="\Magento\SalesRule\Model\Rule\RuleQuoteRecollectTotalsOnDemand" />
3739
<type name="Magento\SalesRule\Helper\Coupon">
3840
<arguments>
3941
<argument name="couponParameters" xsi:type="array">

app/code/Magento/SalesRule/etc/events.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,10 @@
3030
<event name="sales_quote_address_collect_totals_before">
3131
<observer name="coupon_code_validation" instance="Magento\SalesRule\Observer\CouponCodeValidation" />
3232
</event>
33+
<event name="salesrule_rule_save_after">
34+
<observer name="salesrule_quote_recollect_totals_on_disabled" instance="\Magento\SalesRule\Observer\RuleQuoteRecollectTotalsObserver" />
35+
</event>
36+
<event name="salesrule_rule_delete_after">
37+
<observer name="salesrule_quote_recollect_totals_on_delete" instance="\Magento\SalesRule\Observer\RuleQuoteRecollectTotalsObserver" />
38+
</event>
3339
</config>
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?php
2+
/**
3+
*
4+
* Copyright © Magento, Inc. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
8+
namespace Magento\SalesRule\Api;
9+
10+
use Magento\Framework\Webapi\Rest\Request;
11+
use Magento\TestFramework\Helper\Bootstrap;
12+
use Magento\TestFramework\TestCase\WebapiAbstract;
13+
14+
/**
15+
* Tests disabled cart rules for guest's cart
16+
*/
17+
class GuestTotalsInformationManagement extends WebapiAbstract
18+
{
19+
private const SERVICE_OPERATION = 'calculate';
20+
private const SERVICE_NAME = 'checkoutGuestTotalsInformationManagementV1';
21+
private const SERVICE_VERSION = 'V1';
22+
private const RESOURCE_PATH = '/V1/guest-carts/:cartId/totals-information';
23+
private const QUOTE_RESERVED_ORDER_ID = 'test01';
24+
private const SALES_RULE_ID = 'Magento/SalesRule/_files/cart_rule_50_percent_off_no_condition/salesRuleId';
25+
26+
/**
27+
* Test sales rule changes should be persisted in the database
28+
*
29+
* @magentoApiDataFixture Magento/SalesRule/_files/cart_rule_50_percent_off_no_condition.php
30+
* @magentoApiDataFixture Magento/Sales/_files/quote.php
31+
*/
32+
public function testCalculate()
33+
{
34+
/** @var \Magento\Quote\Model\Quote $quote */
35+
/** @var \Magento\Quote\Model\QuoteIdMask $quoteIdMask */
36+
/** @var \Magento\SalesRule\Model\Rule $salesRule */
37+
/** @var \Magento\Framework\Registry $registry */
38+
$registry = Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class);
39+
$quote = Bootstrap::getObjectManager()->get(\Magento\Quote\Model\QuoteFactory::class)->create();
40+
$quote->load(self::QUOTE_RESERVED_ORDER_ID, 'reserved_order_id');
41+
$quoteIdMask = Bootstrap::getObjectManager()->get(\Magento\Quote\Model\QuoteIdMaskFactory::class)->create();
42+
$quoteIdMask->load($quote->getId(), 'quote_id');
43+
$salesRuleId = $registry->registry(self::SALES_RULE_ID);
44+
$salesRule = Bootstrap::getObjectManager()->create(\Magento\SalesRule\Model\RuleFactory::class)->create();
45+
$salesRule->load($salesRuleId);
46+
$this->assertContains($salesRule->getRuleId(), str_getcsv($quote->getAppliedRuleIds()));
47+
$salesRule->setIsActive(0);
48+
$salesRule->save();
49+
$response = $this->_webApiCall(
50+
[
51+
'rest' => [
52+
'resourcePath' => str_replace(':cartId', $quoteIdMask->getMaskedId(), self::RESOURCE_PATH),
53+
'httpMethod' => Request::HTTP_METHOD_POST,
54+
],
55+
'soap' => [
56+
'service' => self::SERVICE_NAME,
57+
'serviceVersion' => self::SERVICE_VERSION,
58+
'operation' => self::SERVICE_NAME . self::SERVICE_OPERATION,
59+
],
60+
],
61+
[
62+
'addressInformation' => [
63+
'address' => []
64+
]
65+
]
66+
);
67+
$this->assertNotEmpty($response);
68+
$quote->load(self::QUOTE_RESERVED_ORDER_ID, 'reserved_order_id');
69+
$this->assertNotContains($salesRule->getId(), str_getcsv($quote->getAppliedRuleIds()));
70+
}
71+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
/**
3+
*
4+
* Copyright © Magento, Inc. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
8+
namespace Magento\SalesRule\Api;
9+
10+
use Magento\Framework\Webapi\Rest\Request;
11+
use Magento\TestFramework\Helper\Bootstrap;
12+
use Magento\TestFramework\TestCase\WebapiAbstract;
13+
14+
/**
15+
* Tests disabled cart rules for customer's cart
16+
*/
17+
class TotalsInformationManagement extends WebapiAbstract
18+
{
19+
private const SERVICE_OPERATION = 'calculate';
20+
private const SERVICE_NAME = 'checkoutTotalsInformationManagementV1';
21+
private const SERVICE_VERSION = 'V1';
22+
private const RESOURCE_PATH = '/V1/carts/mine/totals-information';
23+
private const QUOTE_RESERVED_ORDER_ID = 'test01';
24+
private const SALES_RULE_ID = 'Magento/SalesRule/_files/cart_rule_50_percent_off_no_condition/salesRuleId';
25+
private const CUSTOMER_EMAIL = 'customer@example.com';
26+
private const CUSTOMER_PASSWORD = 'password';
27+
28+
/**
29+
* Test sales rule changes should be persisted in the database
30+
*
31+
* @magentoApiDataFixture Magento/SalesRule/_files/cart_rule_50_percent_off_no_condition.php
32+
* @magentoApiDataFixture Magento/Sales/_files/quote_with_customer.php
33+
*/
34+
public function testCalculate()
35+
{
36+
/** @var \Magento\Quote\Model\Quote $quote */
37+
/** @var \Magento\Quote\Model\QuoteIdMask $quoteIdMask */
38+
/** @var \Magento\SalesRule\Model\Rule $salesRule */
39+
/** @var \Magento\Framework\Registry $registry */
40+
$registry = Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class);
41+
$quote = Bootstrap::getObjectManager()->get(\Magento\Quote\Model\QuoteFactory::class)->create();
42+
$quote->load(self::QUOTE_RESERVED_ORDER_ID, 'reserved_order_id');
43+
$quoteIdMask = Bootstrap::getObjectManager()->get(\Magento\Quote\Model\QuoteIdMaskFactory::class)->create();
44+
$quoteIdMask->load($quote->getId(), 'quote_id');
45+
$salesRuleId = $registry->registry(self::SALES_RULE_ID);
46+
$salesRule = Bootstrap::getObjectManager()->create(\Magento\SalesRule\Model\RuleFactory::class)->create();
47+
$salesRule->load($salesRuleId);
48+
$this->assertContains($salesRule->getRuleId(), str_getcsv($quote->getAppliedRuleIds()));
49+
$salesRule->setIsActive(0);
50+
$salesRule->save();
51+
$response = $this->_webApiCall(
52+
[
53+
'rest' => [
54+
'resourcePath' => self::RESOURCE_PATH,
55+
'httpMethod' => Request::HTTP_METHOD_POST,
56+
'token' => Bootstrap::getObjectManager()
57+
->create(
58+
\Magento\Integration\Api\CustomerTokenServiceInterface::class
59+
)
60+
->createCustomerAccessToken(
61+
self::CUSTOMER_EMAIL,
62+
self::CUSTOMER_PASSWORD
63+
)
64+
],
65+
'soap' => [
66+
'service' => self::SERVICE_NAME,
67+
'serviceVersion' => self::SERVICE_VERSION,
68+
'operation' => self::SERVICE_NAME . self::SERVICE_OPERATION,
69+
],
70+
],
71+
[
72+
'cartId' => $quote->getId(),
73+
'addressInformation' => [
74+
'address' => []
75+
]
76+
]
77+
);
78+
$this->assertNotEmpty($response);
79+
$quote->load(self::QUOTE_RESERVED_ORDER_ID, 'reserved_order_id');
80+
$this->assertNotContains($salesRule->getId(), str_getcsv($quote->getAppliedRuleIds()));
81+
}
82+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
/** @var \Magento\Framework\Registry $registry */
7+
/** @var \Magento\SalesRule\Model\Rule $salesRule */
8+
/** @var \Magento\SalesRule\Model\RuleRepository $salesRuleRepository */
9+
10+
$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
11+
$registry = $objectManager->get(\Magento\Framework\Registry::class);
12+
$salesRule = $objectManager->create(\Magento\SalesRule\Model\Rule::class);
13+
$salesRuleRepository = $objectManager->create(\Magento\SalesRule\Model\RuleRepository::class);
14+
$allRules = $salesRuleRepository->getList($objectManager->get(\Magento\Framework\Api\SearchCriteriaInterface::class));
15+
foreach ($allRules->getItems() as $rule) {
16+
$salesRuleRepository->deleteById($rule->getRuleId());
17+
}
18+
$salesRule->setData(
19+
[
20+
'name' => '50% off - July 4',
21+
'is_active' => 1,
22+
'customer_group_ids' => [\Magento\Customer\Model\GroupManagement::NOT_LOGGED_IN_ID],
23+
'coupon_type' => \Magento\SalesRule\Model\Rule::COUPON_TYPE_NO_COUPON,
24+
'simple_action' => 'by_percent',
25+
'discount_amount' => 50,
26+
'discount_step' => 0,
27+
'stop_rules_processing' => 1,
28+
'website_ids' => [
29+
\Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(
30+
\Magento\Store\Model\StoreManagerInterface::class
31+
)->getWebsite()->getId()
32+
]
33+
]
34+
);
35+
$salesRule->save();
36+
37+
$registry->unregister('Magento/SalesRule/_files/cart_rule_50_percent_off_no_condition/salesRuleId');
38+
$registry->register('Magento/SalesRule/_files/cart_rule_50_percent_off_no_condition/salesRuleId', $salesRule->getId());
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
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+
/** @var \Magento\Framework\Registry $registry */
9+
/** @var \Magento\SalesRule\Model\Rule $salesRule */
10+
$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
11+
$registry = $objectManager->get(\Magento\Framework\Registry::class);
12+
$salesRule = $objectManager->create(\Magento\SalesRule\Model\Rule::class);
13+
$salesRuleId = $registry->registry('Magento/SalesRule/_files/cart_rule_50_percent_off_no_condition/salesRuleId');
14+
if ($salesRuleId) {
15+
$salesRule->load($salesRuleId);
16+
if ($salesRule->getId()) {
17+
$salesRule->delete();
18+
}
19+
}

0 commit comments

Comments
 (0)