Skip to content

Commit c11743e

Browse files
authored
LYNX-617 extend cart and customerCart GQL queries to know if the shipping addresses and billing address are the same or not
1 parent f5275f9 commit c11743e

File tree

3 files changed

+272
-3
lines changed

3 files changed

+272
-3
lines changed

app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?php
22
/**
3-
* Copyright © Magento, Inc. All rights reserved.
4-
* See COPYING.txt for license details.
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
55
*/
66
declare(strict_types=1);
77

@@ -83,6 +83,9 @@ public function execute(ContextInterface $context, CartInterface $cart, array $b
8383

8484
$this->validateBillingAddress($billingAddress);
8585
$this->assignBillingAddressToCart->execute($cart, $billingAddress, $useForShipping);
86+
if ($sameAsShipping) {
87+
$cart->getShippingAddress()->setSameAsBilling(1)->save();
88+
}
8689
}
8790

8891
/**
@@ -146,7 +149,7 @@ private function validateCanUseBillingForShipping(CartInterface $quote)
146149

147150
if (count($shippingAddresses) > 1) {
148151
throw new GraphQlInputException(
149-
__('Could not use the "use_for_shipping" option, because multiple shipping addresses have already been set.')
152+
__('Could not use "use_for_shipping" option, as multiple shipping addresses have already been set.')
150153
);
151154
}
152155
}

app/code/Magento/QuoteGraphQl/etc/schema.graphqls

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@ type ShippingCartAddress implements CartAddressInterface @doc(description: "Cont
318318
items_weight: Float @deprecated(reason: "This information should not be exposed on the frontend.")
319319
cart_items: [CartItemQuantity] @deprecated(reason: "Use `cart_items_v2` instead.")
320320
cart_items_v2: [CartItemInterface] @doc(description: "An array that lists the items in the cart.")
321+
same_as_billing: Boolean! @doc(description: "Indicates whether the shipping address is same as billing address.")
321322
}
322323

323324
type BillingCartAddress implements CartAddressInterface @doc(description: "Contains details about the billing address.") {
Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
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\Checkout\Test\Fixture\SetShippingAddress as SetShippingAddressFixture;
14+
use Magento\Customer\Test\Fixture\Customer as CustomerFixture;
15+
use Magento\Framework\Exception\AuthenticationException;
16+
use Magento\Framework\Exception\EmailNotConfirmedException;
17+
use Magento\Integration\Api\CustomerTokenServiceInterface;
18+
use Magento\Quote\Test\Fixture\AddProductToCart as AddProductToCartFixture;
19+
use Magento\Quote\Test\Fixture\CustomerCart as CustomerCartFixture;
20+
use Magento\Quote\Test\Fixture\QuoteIdMask;
21+
use Magento\TestFramework\Fixture\DataFixture;
22+
use Magento\TestFramework\Fixture\DataFixtureStorage;
23+
use Magento\TestFramework\Fixture\DataFixtureStorageManager;
24+
use Magento\TestFramework\Helper\Bootstrap;
25+
use Magento\TestFramework\TestCase\GraphQlAbstract;
26+
27+
/**
28+
* Test same_as_billing field in customerCart.shipping_addresses
29+
*/
30+
class ShippingAddressSameAsBillingTest extends GraphQlAbstract
31+
{
32+
/**
33+
* @var DataFixtureStorage
34+
*/
35+
private $fixtures;
36+
37+
/**
38+
* @var CustomerTokenServiceInterface
39+
*/
40+
private $customerTokenService;
41+
42+
/**
43+
* @inheritdoc
44+
*/
45+
protected function setUp(): void
46+
{
47+
$this->fixtures = Bootstrap::getObjectManager()->get(DataFixtureStorageManager::class)->getStorage();
48+
$this->customerTokenService = Bootstrap::getObjectManager()->get(CustomerTokenServiceInterface::class);
49+
}
50+
51+
/**
52+
* @throws Exception
53+
*/
54+
#[
55+
DataFixture(ProductFixture::class, as: 'product'),
56+
DataFixture(CustomerFixture::class, as: 'customer'),
57+
DataFixture(CustomerCartFixture::class, ['customer_id' => '$customer.id$'], as: 'quote'),
58+
DataFixture(AddProductToCartFixture::class, ['cart_id' => '$quote.id$', 'product_id' => '$product.id$']),
59+
DataFixture(SetShippingAddressFixture::class, ['cart_id' => '$quote.id$']),
60+
DataFixture(QuoteIdMask::class, ['cart_id' => '$quote.id$'], 'quoteIdMask'),
61+
]
62+
public function testSetSameAsShipping(): void
63+
{
64+
$maskedQuoteId = $this->fixtures->get('quoteIdMask')->getMaskedId();
65+
$headerMap = $this->getCustomerAuthHeaders($this->fixtures->get('customer')->getEmail());
66+
67+
$this->graphQlMutation(
68+
$this->getBillingAddressMutationSameAsShipping($maskedQuoteId),
69+
[],
70+
'',
71+
$headerMap
72+
);
73+
74+
$this->assertSameAsBillingField(
75+
$this->graphQlQuery(
76+
$this->getQuery($maskedQuoteId),
77+
[],
78+
'',
79+
$headerMap
80+
),
81+
true
82+
);
83+
}
84+
85+
/**
86+
* @throws Exception
87+
*/
88+
#[
89+
DataFixture(ProductFixture::class, as: 'product'),
90+
DataFixture(CustomerFixture::class, as: 'customer'),
91+
DataFixture(CustomerCartFixture::class, ['customer_id' => '$customer.id$'], as: 'quote'),
92+
DataFixture(AddProductToCartFixture::class, ['cart_id' => '$quote.id$', 'product_id' => '$product.id$']),
93+
DataFixture(QuoteIdMask::class, ['cart_id' => '$quote.id$'], 'quoteIdMask'),
94+
]
95+
public function testSetUseForShipping(): void
96+
{
97+
$maskedQuoteId = $this->fixtures->get('quoteIdMask')->getMaskedId();
98+
$headerMap = $this->getCustomerAuthHeaders(
99+
$this->fixtures->get('customer')->getEmail()
100+
);
101+
102+
$this->graphQlMutation(
103+
$this->getBillingAddressMutationUseForShipping($maskedQuoteId),
104+
[],
105+
'',
106+
$headerMap
107+
);
108+
109+
$this->assertSameAsBillingField(
110+
$this->graphQlQuery(
111+
$this->getQuery($maskedQuoteId),
112+
[],
113+
'',
114+
$headerMap
115+
),
116+
true
117+
);
118+
}
119+
120+
/**
121+
* @throws Exception
122+
*/
123+
#[
124+
DataFixture(ProductFixture::class, as: 'product'),
125+
DataFixture(CustomerFixture::class, as: 'customer'),
126+
DataFixture(CustomerCartFixture::class, ['customer_id' => '$customer.id$'], as: 'quote'),
127+
DataFixture(AddProductToCartFixture::class, ['cart_id' => '$quote.id$', 'product_id' => '$product.id$']),
128+
DataFixture(QuoteIdMask::class, ['cart_id' => '$quote.id$'], 'quoteIdMask'),
129+
DataFixture(SetBillingAddressFixture::class, ['cart_id' => '$quote.id$']),
130+
DataFixture(SetShippingAddressFixture::class, ['cart_id' => '$quote.id$']),
131+
]
132+
public function testShippingAndBillingAddressIsDifferent(): void
133+
{
134+
$this->assertSameAsBillingField(
135+
$this->graphQlQuery(
136+
$this->getQuery($this->fixtures->get('quoteIdMask')->getMaskedId()),
137+
[],
138+
'',
139+
$this->getCustomerAuthHeaders($this->fixtures->get('customer')->getEmail())
140+
),
141+
false
142+
);
143+
}
144+
145+
/**
146+
* Asserts the same_as_billing field in cart.shipping_addresses
147+
*
148+
* @param array $response
149+
* @param bool $sameAsBilling
150+
* @return void
151+
*/
152+
private function assertSameAsBillingField(array $response, bool $sameAsBilling): void
153+
{
154+
self::assertEquals(
155+
[
156+
'cart' => [
157+
'shipping_addresses' => [
158+
0 => [
159+
'same_as_billing' => $sameAsBilling
160+
]
161+
]
162+
]
163+
],
164+
$response
165+
);
166+
}
167+
168+
/**
169+
* Returns GraphQl mutation for (setBillingAddressOnCart) with same_as_shipping: true
170+
*
171+
* @param string $maskedQuoteId
172+
* @return string
173+
*/
174+
private function getBillingAddressMutationSameAsShipping(string $maskedQuoteId): string
175+
{
176+
return <<<MUTATION
177+
mutation {
178+
setBillingAddressOnCart(
179+
input: {
180+
cart_id: "{$maskedQuoteId}",
181+
billing_address: {
182+
same_as_shipping: true
183+
}
184+
}
185+
) {
186+
cart {
187+
id
188+
}
189+
}
190+
}
191+
MUTATION;
192+
}
193+
194+
/**
195+
* Returns GraphQl mutation for (setBillingAddressOnCart) with use_for_shipping: true
196+
*
197+
* @param string $maskedQuoteId
198+
* @return string
199+
*/
200+
private function getBillingAddressMutationUseForShipping(string $maskedQuoteId): string
201+
{
202+
return <<<MUTATION
203+
mutation {
204+
setBillingAddressOnCart(
205+
input: {
206+
cart_id: "{$maskedQuoteId}",
207+
billing_address: {
208+
address: {
209+
firstname: "test firstname"
210+
lastname: "test lastname"
211+
company: "test company"
212+
street: ["test street 1", "test street 2"]
213+
city: "test city"
214+
postcode: "887766"
215+
telephone: "88776655"
216+
region: "TX"
217+
country_code: "US"
218+
}
219+
use_for_shipping: true
220+
}
221+
}
222+
) {
223+
cart {
224+
id
225+
}
226+
}
227+
}
228+
MUTATION;
229+
}
230+
231+
/**
232+
* Returns GraphQl query with cart shipping_addresses.same_as_billing field
233+
*
234+
* @param string $maskedQuoteId
235+
* @return string
236+
*/
237+
private function getQuery(string $maskedQuoteId): string
238+
{
239+
return <<<QUERY
240+
{
241+
cart(cart_id: "$maskedQuoteId") {
242+
shipping_addresses {
243+
same_as_billing
244+
}
245+
}
246+
}
247+
QUERY;
248+
}
249+
250+
/**
251+
* Generates token for GQL and returns header with generated token
252+
*
253+
* @param string $customerEmail
254+
* @return array
255+
* @throws AuthenticationException
256+
* @throws EmailNotConfirmedException
257+
*/
258+
private function getCustomerAuthHeaders(string $customerEmail): array
259+
{
260+
return [
261+
'Authorization' => 'Bearer ' .
262+
$this->customerTokenService->createCustomerAccessToken($customerEmail, 'password')
263+
];
264+
}
265+
}

0 commit comments

Comments
 (0)