Skip to content

Commit 97ab938

Browse files
authored
LYNX-702: EstimateTotals: Discounts is null for virtual product types (#342)
1 parent 3db39f3 commit 97ab938

File tree

2 files changed

+203
-0
lines changed

2 files changed

+203
-0
lines changed

app/code/Magento/QuoteGraphQl/Model/GetDiscounts.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ class GetDiscounts
2222
*/
2323
public function execute(Quote $quote, array $discounts): ?array
2424
{
25+
$discounts = $discounts ?: $quote->getBillingAddress()->getExtensionAttributes()->getDiscounts() ?? [];
26+
2527
if (empty($discounts)) {
2628
return null;
2729
}
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\GraphQl\Quote;
9+
10+
use Exception;
11+
use Magento\Catalog\Test\Fixture\Product as ProductFixture;
12+
use Magento\Checkout\Test\Fixture\SetBillingAddress as SetBillingAddressFixture;
13+
use Magento\Customer\Test\Fixture\Customer as CustomerFixture;
14+
use Magento\Framework\Exception\AuthenticationException;
15+
use Magento\Framework\Exception\LocalizedException;
16+
use Magento\Integration\Api\CustomerTokenServiceInterface;
17+
use Magento\Quote\Test\Fixture\AddProductToCart as AddProductToCartFixture;
18+
use Magento\Quote\Test\Fixture\CustomerCart as CustomerCartFixture;
19+
use Magento\Quote\Test\Fixture\QuoteIdMask;
20+
use Magento\SalesRule\Model\Rule as SalesRule;
21+
use Magento\SalesRule\Test\Fixture\AddressCondition as AddressConditionFixture;
22+
use Magento\SalesRule\Test\Fixture\Rule as SalesRuleFixture;
23+
use Magento\Quote\Test\Fixture\ApplyCoupon as ApplyCouponFixture;
24+
use Magento\TestFramework\Fixture\DataFixture;
25+
use Magento\TestFramework\Fixture\DataFixtureStorage;
26+
use Magento\TestFramework\Fixture\DataFixtureStorageManager;
27+
use Magento\TestFramework\Helper\Bootstrap;
28+
use Magento\TestFramework\TestCase\GraphQlAbstract;
29+
30+
/**
31+
* Test getting estimateTotals schema with discounts fields for virtual products
32+
*/
33+
class EstimateTotalsWithVirtualProductDiscountsTest extends GraphQlAbstract
34+
{
35+
private const PRODUCT_PRICE = 100;
36+
private const DISCOUNT_PERCENTAGE = 20;
37+
private const TOTAL_QTY = 1;
38+
private const DISCOUNT_LABEL = 'Flat 20 percent off';
39+
private const COUPON_CODE = 'SALE20';
40+
41+
/**
42+
* @var CustomerTokenServiceInterface
43+
*/
44+
private $customerTokenService;
45+
46+
/**
47+
* @var DataFixtureStorage
48+
*/
49+
private $fixtures;
50+
51+
/**
52+
* @inheridoc
53+
*/
54+
protected function setUp(): void
55+
{
56+
$this->customerTokenService = Bootstrap::getObjectManager()->get(CustomerTokenServiceInterface::class);
57+
$this->fixtures = Bootstrap::getObjectManager()->get(DataFixtureStorageManager::class)->getStorage();
58+
}
59+
60+
/**
61+
* Test estimateTotals.discounts for virtual product
62+
*
63+
* @return void
64+
* @throws AuthenticationException|LocalizedException
65+
* @throws Exception
66+
*/
67+
#[
68+
DataFixture(
69+
AddressConditionFixture::class,
70+
[
71+
'attribute' => 'total_qty',
72+
'operator' => '>=',
73+
'value' => 1
74+
],
75+
'condition'
76+
),
77+
DataFixture(
78+
SalesRuleFixture::class,
79+
[
80+
'store_labels' => [1 => self::DISCOUNT_LABEL],
81+
'coupon_type' => SalesRule::COUPON_TYPE_SPECIFIC,
82+
'simple_action' => SalesRule::BY_PERCENT_ACTION,
83+
'discount_amount' => self::DISCOUNT_PERCENTAGE,
84+
'coupon_code' => self::COUPON_CODE,
85+
'conditions' => ['$condition$'],
86+
'uses_per_customer' => 1,
87+
'stop_rules_processing' => true
88+
],
89+
as: 'rule'
90+
),
91+
DataFixture(ProductFixture::class, ['price' => self::PRODUCT_PRICE, 'type_id' => 'virtual'], as: 'product'),
92+
DataFixture(CustomerFixture::class, as: 'customer'),
93+
DataFixture(CustomerCartFixture::class, ['customer_id' => '$customer.id$'], as: 'quote'),
94+
DataFixture(
95+
AddProductToCartFixture::class,
96+
[
97+
'cart_id' => '$quote.id$',
98+
'product_id' => '$product.id$',
99+
'qty' => self::TOTAL_QTY
100+
]
101+
),
102+
DataFixture(
103+
ApplyCouponFixture::class,
104+
[
105+
'cart_id' => '$quote.id$',
106+
'coupon_codes' => [self::COUPON_CODE]
107+
]
108+
),
109+
DataFixture(SetBillingAddressFixture::class, ['cart_id' => '$quote.id$']),
110+
DataFixture(QuoteIdMask::class, ['cart_id' => '$quote.id$'], 'quoteIdMask')
111+
]
112+
public function testEstimateTotalsWithDiscount(): void
113+
{
114+
self::assertEquals(
115+
[
116+
'estimateTotals' => [
117+
'cart' => [
118+
'prices' => [
119+
'discounts' => [
120+
0 => [
121+
'amount' => [
122+
'value' => 20,
123+
'currency' => "USD"
124+
],
125+
'label' => self::DISCOUNT_LABEL,
126+
'coupon' => [
127+
'code' => self::COUPON_CODE
128+
],
129+
'applied_to' => "ITEM"
130+
]
131+
]
132+
]
133+
]
134+
]
135+
],
136+
$this->graphQlMutation(
137+
$this->getEstimateTotalsMutation($this->fixtures->get('quoteIdMask')->getMaskedId()),
138+
[],
139+
'',
140+
$this->getCustomerAuthHeaders($this->fixtures->get('customer')->getEmail())
141+
)
142+
);
143+
}
144+
145+
/**
146+
* Generates GraphQl mutation for estimateTotal with discounts
147+
*
148+
* @param string $maskedQuoteId
149+
* @return string
150+
*/
151+
private function getEstimateTotalsMutation(string $maskedQuoteId): string
152+
{
153+
return <<<MUTATION
154+
mutation {
155+
estimateTotals(
156+
input: {
157+
cart_id: "$maskedQuoteId"
158+
address: {
159+
country_code: US
160+
postcode: "36104"
161+
region: { region_code: "AL" }
162+
}
163+
shipping_method: {
164+
carrier_code: "flatrate",
165+
method_code: "flatrate"
166+
}
167+
}
168+
) {
169+
cart {
170+
prices {
171+
discounts {
172+
amount {
173+
value
174+
currency
175+
}
176+
label
177+
coupon {
178+
code
179+
}
180+
applied_to
181+
}
182+
}
183+
}
184+
}
185+
}
186+
MUTATION;
187+
}
188+
189+
/**
190+
* Returns the header with customer token for GQL Mutation
191+
*
192+
* @param string $email
193+
* @return array
194+
* @throws AuthenticationException
195+
*/
196+
private function getCustomerAuthHeaders(string $email): array
197+
{
198+
$customerToken = $this->customerTokenService->createCustomerAccessToken($email, 'password');
199+
return ['Authorization' => 'Bearer ' . $customerToken];
200+
}
201+
}

0 commit comments

Comments
 (0)