Skip to content

Commit a21b3c3

Browse files
authored
ENGCOM-7746: Discount applying on Shipping Amount for the rule 'Buy X get Y free (discount amount is Y)' #28839
2 parents fa6d291 + c62267f commit a21b3c3

File tree

3 files changed

+87
-14
lines changed

3 files changed

+87
-14
lines changed

app/code/Magento/SalesRule/Model/Rule/Action/Discount/BuyXGetY.php

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,25 @@
33
* Copyright © Magento, Inc. All rights reserved.
44
* See COPYING.txt for license details.
55
*/
6+
declare(strict_types=1);
7+
68
namespace Magento\SalesRule\Model\Rule\Action\Discount;
79

10+
use Magento\Quote\Model\Quote\Item\AbstractItem;
11+
use Magento\SalesRule\Model\Rule;
12+
813
class BuyXGetY extends AbstractDiscount
914
{
1015
/**
11-
* @param \Magento\SalesRule\Model\Rule $rule
12-
* @param \Magento\Quote\Model\Quote\Item\AbstractItem $item
16+
* Calculate discount data for BuyXGetY action.
17+
*
18+
* @param Rule $rule
19+
* @param AbstractItem $item
1320
* @param float $qty
14-
* @return \Magento\SalesRule\Model\Rule\Action\Discount\Data
21+
* @return Data
1522
*/
16-
public function calculate($rule, $item, $qty)
23+
public function calculate($rule, $item, $qty): Data
1724
{
18-
/** @var \Magento\SalesRule\Model\Rule\Action\Discount\Data $discountData */
1925
$discountData = $this->discountFactory->create();
2026

2127
$itemPrice = $this->validator->getItemPrice($item);

app/code/Magento/SalesRule/Model/Validator.php

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
namespace Magento\SalesRule\Model;
88

99
use Magento\Framework\App\ObjectManager;
10+
use Magento\Quote\Model\Quote;
1011
use Magento\Quote\Model\Quote\Address;
1112
use Magento\Quote\Model\Quote\Item\AbstractItem;
1213
use Magento\SalesRule\Helper\CartFixedDiscount;
@@ -319,7 +320,7 @@ public function processShippingAmount(Address $address)
319320
$quote = $address->getQuote();
320321
$appliedRuleIds = [];
321322
foreach ($this->_getRules($address) as $rule) {
322-
/* @var \Magento\SalesRule\Model\Rule $rule */
323+
/* @var Rule $rule */
323324
if (!$rule->getApplyToShipping() || !$this->validatorUtility->canProcessRule($rule, $address)) {
324325
continue;
325326
}
@@ -328,28 +329,28 @@ public function processShippingAmount(Address $address)
328329
$baseDiscountAmount = 0;
329330
$rulePercent = min(100, $rule->getDiscountAmount());
330331
switch ($rule->getSimpleAction()) {
331-
case \Magento\SalesRule\Model\Rule::TO_PERCENT_ACTION:
332+
case Rule::TO_PERCENT_ACTION:
332333
$rulePercent = max(0, 100 - $rule->getDiscountAmount());
333334
// break is intentionally omitted
334335
// no break
335-
case \Magento\SalesRule\Model\Rule::BY_PERCENT_ACTION:
336+
case Rule::BY_PERCENT_ACTION:
336337
$discountAmount = ($shippingAmount - $address->getShippingDiscountAmount()) * $rulePercent / 100;
337338
$baseDiscountAmount = ($baseShippingAmount -
338339
$address->getBaseShippingDiscountAmount()) * $rulePercent / 100;
339340
$discountPercent = min(100, $address->getShippingDiscountPercent() + $rulePercent);
340341
$address->setShippingDiscountPercent($discountPercent);
341342
break;
342-
case \Magento\SalesRule\Model\Rule::TO_FIXED_ACTION:
343+
case Rule::TO_FIXED_ACTION:
343344
$quoteAmount = $this->priceCurrency->convert($rule->getDiscountAmount(), $quote->getStore());
344345
$discountAmount = $shippingAmount - $quoteAmount;
345346
$baseDiscountAmount = $baseShippingAmount - $rule->getDiscountAmount();
346347
break;
347-
case \Magento\SalesRule\Model\Rule::BY_FIXED_ACTION:
348+
case Rule::BY_FIXED_ACTION:
348349
$quoteAmount = $this->priceCurrency->convert($rule->getDiscountAmount(), $quote->getStore());
349350
$discountAmount = $quoteAmount;
350351
$baseDiscountAmount = $rule->getDiscountAmount();
351352
break;
352-
case \Magento\SalesRule\Model\Rule::CART_FIXED_ACTION:
353+
case Rule::CART_FIXED_ACTION:
353354
$cartRules = $address->getCartFixedRules();
354355
$quoteAmount = $this->priceCurrency->convert($rule->getDiscountAmount(), $quote->getStore());
355356
$isAppliedToShipping = (int) $rule->getApplyToShipping();
@@ -385,6 +386,12 @@ public function processShippingAmount(Address $address)
385386
}
386387
$address->setCartFixedRules($cartRules);
387388
break;
389+
case Rule::BUY_X_GET_Y_ACTION:
390+
$allQtyDiscount = $this->getDiscountQtyAllItemsBuyXGetYAction($quote, $rule);
391+
$quoteAmount = $address->getBaseShippingAmount() / $quote->getItemsQty() * $allQtyDiscount;
392+
$discountAmount = $this->priceCurrency->convert($quoteAmount, $quote->getStore());
393+
$baseDiscountAmount = $quoteAmount;
394+
break;
388395
}
389396

390397
$discountAmount = min($address->getShippingDiscountAmount() + $discountAmount, $shippingAmount);
@@ -426,9 +433,9 @@ public function initTotals($items, Address $address)
426433
return $this;
427434
}
428435

429-
/** @var \Magento\SalesRule\Model\Rule $rule */
436+
/** @var Rule $rule */
430437
foreach ($this->_getRules($address) as $rule) {
431-
if (\Magento\SalesRule\Model\Rule::CART_FIXED_ACTION == $rule->getSimpleAction()
438+
if (Rule::CART_FIXED_ACTION == $rule->getSimpleAction()
432439
&& $this->validatorUtility->canProcessRule($rule, $address)
433440
) {
434441
$ruleTotalItemsPrice = 0;
@@ -481,6 +488,40 @@ private function isValidItemForRule(AbstractItem $item, Rule $rule)
481488
return true;
482489
}
483490

491+
/**
492+
* Return discount Qty for all items at Buy_X_Get_Y_Action
493+
*
494+
* @param Quote $quote
495+
* @param Rule $rule
496+
* @return float
497+
*/
498+
private function getDiscountQtyAllItemsBuyXGetYAction(Quote $quote, Rule $rule): float
499+
{
500+
$discountAllQty = 0;
501+
foreach ($quote->getItems() as $item) {
502+
$qty = $item->getQty();
503+
504+
$discountStep = $rule->getDiscountStep();
505+
$discountAmount = $rule->getDiscountAmount();
506+
if (!$discountStep || $discountAmount > $discountStep) {
507+
continue;
508+
}
509+
$buyAndDiscountQty = $discountStep + $discountAmount;
510+
511+
$fullRuleQtyPeriod = floor($qty / $buyAndDiscountQty);
512+
$freeQty = $qty - $fullRuleQtyPeriod * $buyAndDiscountQty;
513+
514+
$discountQty = $fullRuleQtyPeriod * $discountAmount;
515+
if ($freeQty > $discountStep) {
516+
$discountQty += $freeQty - $discountStep;
517+
}
518+
519+
$discountAllQty += $discountQty;
520+
}
521+
522+
return $discountAllQty;
523+
}
524+
484525
/**
485526
* Return item price
486527
*
@@ -564,7 +605,7 @@ public function prepareDescription($address, $separator = ', ')
564605
public function sortItemsByPriority($items, Address $address = null)
565606
{
566607
$itemsSorted = [];
567-
/** @var $rule \Magento\SalesRule\Model\Rule */
608+
/** @var $rule Rule */
568609
foreach ($this->_getRules($address) as $rule) {
569610
foreach ($items as $itemKey => $itemValue) {
570611
if ($rule->getActions()->validate($itemValue)) {
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
/**
4+
* Copyright © Magento, Inc. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
-->
8+
9+
<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
10+
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd">
11+
<test name="AdminCreateBuyXGetYFreeWithApplyShippingAmountTest" extends="AdminCreateBuyXGetYFreeTest">
12+
<annotations>
13+
<title value="Admin should be able to create a cart price rule of type Buy X get Y free enable 'Apply to Shipping Amount' "/>
14+
<description value="Use cart price rule of type Buy X get Y free with enable 'Apply to Shipping Amount'"/>
15+
<group value="SalesRule"/>
16+
</annotations>
17+
18+
<remove keyForRemoval="verifyStorefront"/>
19+
<click selector="{{AdminCartPriceRulesFormSection.applyDiscountToShippingLabel}}" stepKey="enabledApplyDiscountToShipping" after="fillDiscountStep"/>
20+
<actionGroup ref="VerifyDiscountAmountActionGroup" stepKey="verifyStorefrontDiscount" after="fillProductFieldsInAdmin">
21+
<argument name="productUrl" value="{{_defaultProduct.urlKey}}.html"/>
22+
<argument name="quantity" value="2"/>
23+
<argument name="expectedDiscount" value="-$128.00"/>
24+
</actionGroup>
25+
</test>
26+
</tests>

0 commit comments

Comments
 (0)