Skip to content

Commit bcd75f2

Browse files
authored
ENGCOM-4718: [Backport] Fix negative subtotal when full discount applied with tax calculation #10790 #22227
2 parents 51fcebd + 65669bb commit bcd75f2

File tree

4 files changed

+175
-0
lines changed

4 files changed

+175
-0
lines changed

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,8 @@ public function deltaRoundingFix(
189189
) {
190190
$discountAmount = $discountData->getAmount();
191191
$baseDiscountAmount = $discountData->getBaseAmount();
192+
$rowTotalInclTax = $item->getRowTotalInclTax();
193+
$baseRowTotalInclTax = $item->getBaseRowTotalInclTax();
192194

193195
//TODO Seems \Magento\Quote\Model\Quote\Item\AbstractItem::getDiscountPercent() returns float value
194196
//that can not be used as array index
@@ -205,6 +207,23 @@ public function deltaRoundingFix(
205207
- $this->priceCurrency->round($baseDiscountAmount);
206208
}
207209

210+
/**
211+
* When we have 100% discount check if totals will not be negative
212+
*/
213+
214+
if ($percentKey == 100) {
215+
$discountDelta = $rowTotalInclTax - $discountAmount;
216+
$baseDiscountDelta = $baseRowTotalInclTax - $baseDiscountAmount;
217+
218+
if ($discountDelta < 0) {
219+
$discountAmount += $discountDelta;
220+
}
221+
222+
if ($baseDiscountDelta < 0) {
223+
$baseDiscountAmount += $baseDiscountDelta;
224+
}
225+
}
226+
208227
$discountData->setAmount($this->priceCurrency->round($discountAmount));
209228
$discountData->setBaseAmount($this->priceCurrency->round($baseDiscountAmount));
210229

app/code/Magento/Tax/Model/Calculation/AbstractAggregateCalculator.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ protected function calculateWithTaxNotInPrice(QuoteDetailsItemInterface $item, $
156156
$rowTaxBeforeDiscount = array_sum($rowTaxesBeforeDiscount);
157157
$rowTotalInclTax = $rowTotal + $rowTaxBeforeDiscount;
158158
$priceInclTax = $rowTotalInclTax / $quantity;
159+
159160
if ($round) {
160161
$priceInclTax = $this->calculationTool->round($priceInclTax);
161162
}

dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
require_once __DIR__ . '/SetupUtil.php';
1313
require_once __DIR__ . '/../../../../_files/tax_calculation_data_aggregated.php';
14+
require_once __DIR__ . '/../../../../_files/full_discount_with_tax.php';
1415

1516
/**
1617
* Class TaxTest
@@ -124,6 +125,40 @@ public function testCollect()
124125
);
125126
}
126127

128+
/**
129+
* Test taxes collection with full discount for quote.
130+
*
131+
* Test tax calculation and price when the discount may be bigger than total
132+
* This method will test the collector through $quote->collectTotals() method
133+
*
134+
* @see \Magento\SalesRule\Model\Utility::deltaRoundingFix
135+
* @magentoDataFixture Magento/Tax/_files/full_discount_with_tax.php
136+
* @magentoDbIsolation enabled
137+
* @magentoAppIsolation enabled
138+
*/
139+
public function testFullDiscountWithDeltaRoundingFix()
140+
{
141+
global $fullDiscountIncTax;
142+
$configData = $fullDiscountIncTax['config_data'];
143+
$quoteData = $fullDiscountIncTax['quote_data'];
144+
$expectedResults = $fullDiscountIncTax['expected_result'];
145+
146+
/** @var \Magento\Framework\ObjectManagerInterface $objectManager */
147+
$objectManager = Bootstrap::getObjectManager();
148+
149+
//Setup tax configurations
150+
$this->setupUtil = new SetupUtil($objectManager);
151+
$this->setupUtil->setupTax($configData);
152+
153+
$quote = $this->setupUtil->setupQuote($quoteData);
154+
155+
$quote->collectTotals();
156+
157+
$quoteAddress = $quote->getShippingAddress();
158+
159+
$this->verifyResult($quoteAddress, $expectedResults);
160+
}
161+
127162
/**
128163
* Verify fields in quote item
129164
*
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
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+
use Magento\Tax\Model\Config;
9+
use Magento\Tax\Model\Sales\Total\Quote\SetupUtil;
10+
11+
$fullDiscountIncTax = [
12+
'config_data' => [
13+
'config_overrides' => [
14+
Config::CONFIG_XML_PATH_APPLY_AFTER_DISCOUNT => 0,
15+
Config::CONFIG_XML_PATH_DISCOUNT_TAX => 1,
16+
Config::XML_PATH_ALGORITHM => 'ROW_BASE_CALCULATION',
17+
Config::CONFIG_XML_PATH_SHIPPING_TAX_CLASS => SetupUtil::SHIPPING_TAX_CLASS,
18+
],
19+
'tax_rate_overrides' => [
20+
SetupUtil::TAX_RATE_TX => 18,
21+
SetupUtil::TAX_RATE_SHIPPING => 0,
22+
],
23+
'tax_rule_overrides' => [
24+
[
25+
'code' => 'Product Tax Rule',
26+
'product_tax_class_ids' => [
27+
SetupUtil::PRODUCT_TAX_CLASS_1
28+
],
29+
],
30+
[
31+
'code' => 'Shipping Tax Rule',
32+
'product_tax_class_ids' => [
33+
SetupUtil::SHIPPING_TAX_CLASS
34+
],
35+
'tax_rate_ids' => [
36+
SetupUtil::TAX_RATE_SHIPPING,
37+
],
38+
],
39+
],
40+
],
41+
'quote_data' => [
42+
'billing_address' => [
43+
'region_id' => SetupUtil::REGION_TX,
44+
],
45+
'shipping_address' => [
46+
'region_id' => SetupUtil::REGION_TX,
47+
],
48+
'items' => [
49+
[
50+
'sku' => 'simple1',
51+
'price' => 2542.37,
52+
'qty' => 2,
53+
]
54+
],
55+
'shipping_method' => 'free',
56+
'shopping_cart_rules' => [
57+
[
58+
'discount_amount' => 100
59+
],
60+
],
61+
],
62+
'expected_result' => [
63+
'address_data' => [
64+
'subtotal' => 5084.74,
65+
'base_subtotal' => 5084.74,
66+
'subtotal_incl_tax' => 5999.99,
67+
'base_subtotal_incl_tax' => 5999.99,
68+
'tax_amount' => 915.25,
69+
'base_tax_amount' => 915.25,
70+
'shipping_amount' => 0,
71+
'base_shipping_amount' => 0,
72+
'shipping_incl_tax' => 0,
73+
'base_shipping_incl_tax' => 0,
74+
'shipping_tax_amount' => 0,
75+
'base_shipping_tax_amount' => 0,
76+
'discount_amount' => -5999.99,
77+
'base_discount_amount' => -5999.99,
78+
'discount_tax_compensation_amount' => 0,
79+
'base_discount_tax_compensation_amount' => 0,
80+
'shipping_discount_tax_compensation_amount' => 0,
81+
'base_shipping_discount_tax_compensation_amount' => 0,
82+
'grand_total' => 0,
83+
'base_grand_total' => 0,
84+
'applied_taxes' => [
85+
SetupUtil::TAX_RATE_TX => [
86+
'percent' => 18,
87+
'amount' => 915.25,
88+
'base_amount' => 915.25,
89+
'rates' => [
90+
[
91+
'code' => SetupUtil::TAX_RATE_TX,
92+
'title' => SetupUtil::TAX_RATE_TX,
93+
'percent' => 18,
94+
],
95+
],
96+
]
97+
],
98+
],
99+
'items_data' => [
100+
'simple1' => [
101+
'row_total' => 5084.74,
102+
'base_row_total' => 5084.74,
103+
'tax_percent' => 18,
104+
'price' => 2542.37,
105+
'base_price' => 2542.37,
106+
'price_incl_tax' => 3000,
107+
'base_price_incl_tax' => 3000,
108+
'row_total_incl_tax' => 5999.99,
109+
'base_row_total_incl_tax' => 5999.99,
110+
'tax_amount' => 915.25,
111+
'base_tax_amount' => 915.25,
112+
'discount_amount' => 5999.99,
113+
'base_discount_amount' => 5999.99,
114+
'discount_percent' => 100,
115+
'discount_tax_compensation_amount' => 0,
116+
'base_discount_tax_compensation_amount' => 0,
117+
],
118+
],
119+
]
120+
];

0 commit comments

Comments
 (0)