Skip to content

Commit 153c567

Browse files
committed
ACP2E-3302: [Cloud] Incorrect Calculations in Coupon Usage Report
1 parent b071d6a commit 153c567

File tree

2 files changed

+265
-29
lines changed

2 files changed

+265
-29
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
/**
3+
* Copyright 2024 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Quote\Test\Fixture;
9+
10+
use Magento\Framework\DataObject;
11+
use Magento\Framework\Exception\NoSuchEntityException;
12+
use Magento\Quote\Api\CartRepositoryInterface;
13+
use Magento\TestFramework\Fixture\DataFixtureInterface;
14+
15+
class ApplyCoupon implements DataFixtureInterface
16+
{
17+
/**
18+
* @var CartRepositoryInterface
19+
*/
20+
public CartRepositoryInterface $quoteRepository;
21+
22+
/**
23+
* @param CartRepositoryInterface $quoteRepository
24+
*/
25+
public function __construct(
26+
CartRepositoryInterface $quoteRepository
27+
) {
28+
$this->quoteRepository = $quoteRepository;
29+
}
30+
31+
/**
32+
* {@inheritdoc}
33+
*
34+
* @param array $data Parameters
35+
* <pre>
36+
* $data = [
37+
* 'cart_id' => (string) Cart ID. Required.
38+
* 'coupon_codes' => (array) Coupon Codes. Required.
39+
* ]
40+
* </pre>
41+
* @throws NoSuchEntityException
42+
*/
43+
public function apply(array $data = []): ?DataObject
44+
{
45+
if (empty($data['cart_id']) || empty($data['coupon_codes'])) {
46+
throw new \InvalidArgumentException('cart_id or coupon_codes is missing!');
47+
}
48+
$quote = $this->quoteRepository->getActive($data['cart_id']);
49+
$quote->setCouponCode(reset($data['coupon_codes']));
50+
$this->quoteRepository->save($quote->collectTotals());
51+
return $quote;
52+
}
53+
}

dev/tests/integration/testsuite/Magento/SalesRule/Model/ResourceModel/Report/Rule/CreatedatTest.php

Lines changed: 212 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,83 @@
55
*/
66
namespace Magento\SalesRule\Model\ResourceModel\Report\Rule;
77

8+
use Magento\Catalog\Test\Fixture\Product as ProductFixture;
9+
use Magento\Checkout\Test\Fixture\PlaceOrder as PlaceOrderFixture;
10+
use Magento\Checkout\Test\Fixture\SetBillingAddress as SetBillingAddressFixture;
11+
use Magento\Checkout\Test\Fixture\SetDeliveryMethod as SetDeliveryMethodFixture;
12+
use Magento\Checkout\Test\Fixture\SetPaymentMethod as SetPaymentMethodFixture;
13+
use Magento\Checkout\Test\Fixture\SetShippingAddress as SetShippingAddressFixture;
14+
use Magento\Customer\Test\Fixture\Customer;
15+
use Magento\Framework\ObjectManagerInterface;
16+
use Magento\Quote\Test\Fixture\ApplyCoupon as ApplyCouponFixture;
17+
use Magento\Quote\Test\Fixture\AddProductToCart as AddProductToCartFixture;
18+
use Magento\Quote\Test\Fixture\CustomerCart;
19+
use Magento\Sales\Model\Order;
20+
use Magento\Sales\Test\Fixture\Invoice as InvoiceFixture;
21+
use Magento\Sales\Test\Fixture\Shipment as ShipmentFixture;
22+
use Magento\SalesRule\Model\ResourceModel\Report\Collection;
23+
use Magento\SalesRule\Model\ResourceModel\Report\Rule;
24+
use Magento\SalesRule\Model\Rule as SalesRule;
25+
use Magento\SalesRule\Test\Fixture\AddressCondition as AddressConditionFixture;
26+
use Magento\SalesRule\Test\Fixture\Rule as RuleFixture;
27+
use Magento\Store\Model\ScopeInterface;
28+
use Magento\Tax\Test\Fixture\TaxRate as TaxRateFixture;
29+
use Magento\Tax\Test\Fixture\TaxRule as TaxRuleFixture;
30+
use Magento\TestFramework\Fixture\AppArea;
31+
use Magento\TestFramework\Fixture\AppIsolation;
32+
use Magento\TestFramework\Fixture\Config as ConfigFixture;
33+
use Magento\TestFramework\Fixture\DataFixture;
34+
use Magento\TestFramework\Fixture\DataFixtureStorage;
35+
use Magento\TestFramework\Fixture\DataFixtureStorageManager;
36+
use Magento\TestFramework\Fixture\DbIsolation;
37+
use Magento\TestFramework\Helper\Bootstrap;
38+
use PHPUnit\Framework\TestCase;
39+
840
/**
941
* Createdat test for check report totals calculate
10-
*
11-
* @magentoDataFixture Magento/SalesRule/_files/order_with_coupon.php
1242
*/
13-
class CreatedatTest extends \PHPUnit\Framework\TestCase
43+
class CreatedatTest extends TestCase
1444
{
1545
/**
46+
* @var ObjectManagerInterface
47+
*/
48+
private $objectManager;
49+
50+
/**
51+
* @var DataFixtureStorage
52+
*/
53+
private $fixtures;
54+
55+
/**
56+
* @var Rule
57+
*/
58+
private $reportResource;
59+
60+
/**
61+
* @var Collection
62+
*/
63+
private $reportCollection;
64+
65+
/**
66+
* @inheirtDoc
67+
*/
68+
protected function setUp(): void
69+
{
70+
$this->objectManager = Bootstrap::getObjectManager();
71+
$this->fixtures = $this->objectManager->get(DataFixtureStorageManager::class)->getStorage();
72+
$this->reportResource = $this->objectManager->get(Rule::class);
73+
$this->reportCollection = $this->objectManager->get(Collection::class);
74+
}
75+
76+
/**
77+
* @magentoDataFixture Magento/SalesRule/_files/order_with_coupon.php
1678
* @dataProvider orderParamsDataProvider()
1779
* @param $orderParams
1880
*/
1981
public function testTotals($orderParams)
2082
{
21-
/** @var \Magento\Sales\Model\Order $order */
22-
$order = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Sales\Model\Order::class);
83+
/** @var Order $order */
84+
$order = $this->objectManager->create(Order::class);
2385
$order->loadByIncrementId('100000001')
2486
->setBaseGrandTotal($orderParams['base_subtotal'])
2587
->setSubtotal($orderParams['base_subtotal'])
@@ -35,50 +97,46 @@ public function testTotals($orderParams)
3597
->setCreatedAt('2014-10-25 10:10:10')
3698
->save();
3799
// refresh report statistics
38-
/** @var \Magento\SalesRule\Model\ResourceModel\Report\Rule $reportResource */
39-
$reportResource = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
40-
\Magento\SalesRule\Model\ResourceModel\Report\Rule::class
41-
);
42-
$reportResource->aggregate();
43-
/** @var \Magento\SalesRule\Model\ResourceModel\Report\Collection $reportCollection */
44-
$reportCollection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
45-
\Magento\SalesRule\Model\ResourceModel\Report\Collection::class
46-
);
47-
$salesRuleReportItem = $reportCollection->getFirstItem();
100+
$this->reportResource->aggregate();
101+
$salesRuleReportItem = $this->reportCollection->getFirstItem();
48102
$this->assertEquals($this->getTotalAmount($order), $salesRuleReportItem['total_amount']);
49103
$this->assertEquals($this->getTotalAmountActual($order), $salesRuleReportItem['total_amount_actual']);
50104
}
51105

52106
/**
53107
* Repeat sql formula from \Magento\SalesRule\Model\ResourceModel\Report\Rule\Createdat::_aggregateByOrder
54108
*
55-
* @param \Magento\Sales\Model\Order $order
109+
* @param Order $order
56110
* @return float
57111
*/
58-
private function getTotalAmount(\Magento\Sales\Model\Order $order)
112+
private function getTotalAmount(Order $order)
59113
{
60114
return (
61-
($order->getBaseSubtotal() - $order->getBaseSubtotalCanceled()
62-
+ ($order->getBaseShippingAmount() - $order->getBaseShippingCanceled()))
63-
- (abs((float) $order->getBaseDiscountAmount()) - abs((float) $order->getBaseDiscountCanceled()))
64-
+ ($order->getBaseTaxAmount() - $order->getBaseTaxCanceled())
65-
) * $order->getBaseToGlobalRate();
115+
($order->getBaseSubtotal() - $order->getBaseSubtotalCanceled()
116+
+ ($order->getBaseShippingAmount() - $order->getBaseShippingCanceled()))
117+
- (abs((float) $order->getBaseDiscountAmount()) - abs((float) $order->getBaseDiscountCanceled()))
118+
+ ($order->getBaseTaxAmount() - $order->getBaseTaxCanceled())
119+
+ ($order->getBaseDiscountTaxCompensationAmount() - $order->getBaseDiscountTaxCompensationRefunded())
120+
- abs((float) $order->getShippingDiscountTaxCompensationAmount())
121+
) * $order->getBaseToGlobalRate();
66122
}
67123

68124
/**
69125
* Repeat sql formula from \Magento\SalesRule\Model\ResourceModel\Report\Rule\Createdat::_aggregateByOrder
70126
*
71-
* @param \Magento\Sales\Model\Order $order
127+
* @param Order $order
72128
* @return float
73129
*/
74-
private function getTotalAmountActual(\Magento\Sales\Model\Order $order)
130+
private function getTotalAmountActual(Order $order)
75131
{
76132
return (
77-
($order->getBaseSubtotalInvoiced() - $order->getSubtotalRefunded()
78-
+ ($order->getBaseShippingInvoiced() - $order->getBaseShippingRefunded()))
79-
- abs((float) $order->getBaseDiscountInvoiced()) - abs((float) $order->getBaseDiscountRefunded())
80-
+ $order->getBaseTaxInvoiced() - $order->getBaseTaxRefunded()
81-
) * $order->getBaseToGlobalRate();
133+
($order->getBaseSubtotalInvoiced() - $order->getSubtotalRefunded()
134+
+ ($order->getBaseShippingInvoiced() - $order->getBaseShippingRefunded()))
135+
- abs((float) $order->getBaseDiscountInvoiced()) - abs((float) $order->getBaseDiscountRefunded())
136+
+ $order->getBaseTaxInvoiced() - $order->getBaseTaxRefunded()
137+
+ ($order->getBaseDiscountTaxCompensationInvoiced() - $order->getBaseDiscountTaxCompensationRefunded())
138+
- abs((float) $order->getBaseShippingDiscountTaxCompensationAmnt())
139+
) * $order->getBaseToGlobalRate();
82140
}
83141

84142
/**
@@ -99,4 +157,129 @@ public static function orderParamsDataProvider()
99157
]
100158
];
101159
}
160+
161+
#[
162+
DbIsolation(false),
163+
AppIsolation(true),
164+
AppArea('adminhtml'),
165+
ConfigFixture('tax/classes/shipping_tax_class', 2, ScopeInterface::SCOPE_STORE),
166+
ConfigFixture('tax/classes/wrapping_tax_class', 0, ScopeInterface::SCOPE_STORE),
167+
168+
ConfigFixture('tax/calculation/algorithm', 'TOTAL_BASE_CALCULATION', ScopeInterface::SCOPE_STORE),
169+
ConfigFixture('tax/calculation/based_on', 'shipping', ScopeInterface::SCOPE_STORE),
170+
ConfigFixture('tax/calculation/price_includes_tax', 1, ScopeInterface::SCOPE_STORE),
171+
ConfigFixture('tax/calculation/shipping_includes_tax', 1, ScopeInterface::SCOPE_STORE),
172+
ConfigFixture('tax/calculation/apply_after_discount', 1, ScopeInterface::SCOPE_STORE),
173+
ConfigFixture('tax/calculation/discount_tax', 1, ScopeInterface::SCOPE_STORE),
174+
ConfigFixture('tax/calculation/apply_tax_on', 0, ScopeInterface::SCOPE_STORE),
175+
ConfigFixture('tax/calculation/cross_border_trade_enabled', 1, ScopeInterface::SCOPE_STORE),
176+
177+
ConfigFixture('tax/notification/ignore_discount', 0, ScopeInterface::SCOPE_STORE),
178+
ConfigFixture('tax/notification/ignore_price_display', 0, ScopeInterface::SCOPE_STORE),
179+
ConfigFixture('tax/notification/ignore_apply_discount', 0, ScopeInterface::SCOPE_STORE),
180+
181+
ConfigFixture('tax/display/type', 2, ScopeInterface::SCOPE_STORE),
182+
ConfigFixture('tax/display/shipping', 2, ScopeInterface::SCOPE_STORE),
183+
184+
ConfigFixture('tax/cart_display/price', 2, ScopeInterface::SCOPE_STORE),
185+
ConfigFixture('tax/cart_display/subtotal', 2, ScopeInterface::SCOPE_STORE),
186+
ConfigFixture('tax/cart_display/shipping', 2, ScopeInterface::SCOPE_STORE),
187+
ConfigFixture('tax/cart_display/full_summary', 1, ScopeInterface::SCOPE_STORE),
188+
ConfigFixture('tax/cart_display/zero_tax', 1, ScopeInterface::SCOPE_STORE),
189+
190+
ConfigFixture('tax/sales_display/price', 2, ScopeInterface::SCOPE_STORE),
191+
ConfigFixture('tax/sales_display/subtotal', 2, ScopeInterface::SCOPE_STORE),
192+
ConfigFixture('tax/sales_display/shipping', 2, ScopeInterface::SCOPE_STORE),
193+
ConfigFixture('tax/sales_display/full_summary', 1, ScopeInterface::SCOPE_STORE),
194+
ConfigFixture('tax/sales_display/zero_tax', 1, ScopeInterface::SCOPE_STORE),
195+
196+
DataFixture(
197+
TaxRateFixture::class,
198+
[
199+
'code' => 'US 19%',
200+
'tax_country_id' => 'USA',
201+
'rate' => 19,
202+
],
203+
'taxRate'
204+
),
205+
DataFixture(
206+
TaxRuleFixture::class,
207+
[
208+
'customer_tax_class_ids' => [3],
209+
'product_tax_class_ids' => [2],
210+
'tax_rate_ids' => ['$taxRate.id$']
211+
],
212+
'taxRule'
213+
),
214+
DataFixture(
215+
AddressConditionFixture::class,
216+
['attribute' => 'base_subtotal_total_incl_tax', 'operator' => '>=', 'value' => 12],
217+
'condition'
218+
),
219+
DataFixture(
220+
RuleFixture::class,
221+
[
222+
'website_ids' => [1],
223+
'customer_group_ids' => [0, 1, 2, 3],
224+
'coupon_code' => 'COUPON1',
225+
'coupon_type' => SalesRule::COUPON_TYPE_SPECIFIC,
226+
'simple_action' => SalesRule::BY_PERCENT_ACTION,
227+
'uses_per_customer' => 10,
228+
'discount_amount' => 10,
229+
'stop_rules_processing' => true,
230+
'conditions' => ['$condition$']
231+
],
232+
'cartPriceRule'
233+
),
234+
DataFixture(
235+
Customer::class,
236+
[
237+
'email' => 'customer@example.com',
238+
'password' => 'password'
239+
],
240+
'customer'
241+
),
242+
DataFixture(ProductFixture::class, ['price' => 17000, 'qty' => 1, 'special_price' => 16730], as:'product'),
243+
DataFixture(CustomerCart::class, ['customer_id' => '$customer.id$'], as: 'cart'),
244+
DataFixture(
245+
AddProductToCartFixture::class,
246+
[
247+
'cart_id' => '$cart.id$',
248+
'product_id' => '$product.id$',
249+
'qty' => 1
250+
]
251+
),
252+
DataFixture(ApplyCouponFixture::class, ['cart_id' => '$cart.id$', 'coupon_codes' => ['COUPON1']]),
253+
DataFixture(SetBillingAddressFixture::class, ['cart_id' => '$cart.id$']),
254+
DataFixture(SetShippingAddressFixture::class, ['cart_id' => '$cart.id$']),
255+
DataFixture(SetDeliveryMethodFixture::class, ['cart_id' => '$cart.id$']),
256+
DataFixture(SetPaymentMethodFixture::class, ['cart_id' => '$cart.id$']),
257+
DataFixture(PlaceOrderFixture::class, ['cart_id' => '$cart.id$'], 'order'),
258+
DataFixture(InvoiceFixture::class, ['order_id' => '$order.id$'], 'invoice'),
259+
DataFixture(ShipmentFixture::class, ['order_id' => '$order.id$'], 'shipment')
260+
]
261+
public function testCouponsReport(): void
262+
{
263+
$order = $this->fixtures->get('order');
264+
$this->reportResource->aggregate();
265+
$salesRuleReportItem = $this->reportCollection->getFirstItem();
266+
267+
$this->assertEquals(
268+
round($order->getData('subtotal_incl_tax'), 2),
269+
round($salesRuleReportItem['subtotal_amount'], 2)
270+
);
271+
$this->assertEquals(
272+
round($order->getData('base_subtotal_incl_tax'), 2),
273+
round($salesRuleReportItem['subtotal_amount_actual'], 2)
274+
);
275+
276+
$this->assertEquals(
277+
round($this->getTotalAmount($order), 2),
278+
round($salesRuleReportItem['total_amount'], 2)
279+
);
280+
$this->assertEquals(
281+
round($this->getTotalAmountActual($order), 2),
282+
round($salesRuleReportItem['total_amount_actual'], 2)
283+
);
284+
}
102285
}

0 commit comments

Comments
 (0)