Skip to content

Commit 0be3b51

Browse files
committed
MC-40772: Incorrect billing address for PickUp In Store after refreshing the page
- Fix empty billing address after refreshing payment page with store pickup
1 parent 2e2af0a commit 0be3b51

File tree

6 files changed

+508
-49
lines changed

6 files changed

+508
-49
lines changed

app/code/Magento/Checkout/Model/DefaultConfigProvider.php

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use Magento\Checkout\Model\Session as CheckoutSession;
1212
use Magento\Customer\Api\AddressMetadataInterface;
1313
use Magento\Customer\Api\CustomerRepositoryInterface as CustomerRepository;
14+
use Magento\Customer\Api\Data\CustomerInterface;
1415
use Magento\Customer\Model\Address\CustomerAddressDataProvider;
1516
use Magento\Customer\Model\Context as CustomerContext;
1617
use Magento\Customer\Model\Session as CustomerSession;
@@ -311,14 +312,11 @@ public function getConfig()
311312
$output['isCustomerLoggedIn'] = $this->isCustomerLoggedIn();
312313
$output['selectedShippingMethod'] = $this->getSelectedShippingMethod();
313314
if ($email && !$this->isCustomerLoggedIn()) {
314-
$shippingAddressFromData = $this->getAddressFromData($quote->getShippingAddress());
315-
$billingAddressFromData = $this->getAddressFromData($quote->getBillingAddress());
316-
$output['shippingAddressFromData'] = $shippingAddressFromData;
317-
if ($shippingAddressFromData != $billingAddressFromData) {
318-
$output['billingAddressFromData'] = $billingAddressFromData;
319-
}
320315
$output['validatedEmailValue'] = $email;
321316
}
317+
if (!$this->isCustomerLoggedIn() || !$this->getCustomer()->getAddresses()) {
318+
$output = array_merge($output, $this->getQuoteAddressData());
319+
}
322320
$output['storeCode'] = $this->getStoreCode();
323321
$output['isGuestCheckoutAllowed'] = $this->isGuestCheckoutAllowed();
324322
$output['isCustomerLoginRequired'] = $this->isCustomerLoginRequired();
@@ -387,8 +385,7 @@ private function getCustomerData(): array
387385
{
388386
$customerData = [];
389387
if ($this->isCustomerLoggedIn()) {
390-
/** @var \Magento\Customer\Api\Data\CustomerInterface $customer */
391-
$customer = $this->customerRepository->getById($this->customerSession->getCustomerId());
388+
$customer = $this->getCustomer();
392389
$customerData = $customer->__toArray();
393390
$customerData['addresses'] = $this->customerAddressData->getAddressDataByCustomer($customer);
394391
}
@@ -731,4 +728,43 @@ private function getQuoteItemsMessages(array $quoteItemData): array
731728

732729
return $quoteItemsMessages;
733730
}
731+
732+
/**
733+
* Get quote address data for checkout
734+
*
735+
* @return array
736+
*/
737+
private function getQuoteAddressData(): array
738+
{
739+
$output = [];
740+
$quote = $this->checkoutSession->getQuote();
741+
$shippingAddressFromData = [];
742+
if ($quote->getShippingAddress()->getEmail()) {
743+
$shippingAddressFromData = $this->getAddressFromData($quote->getShippingAddress());
744+
if ($shippingAddressFromData) {
745+
$output['isShippingAddressFromDataValid'] = $quote->getShippingAddress()->validate() === true;
746+
$output['shippingAddressFromData'] = $shippingAddressFromData;
747+
}
748+
}
749+
750+
if ($quote->getBillingAddress()->getEmail()) {
751+
$billingAddressFromData = $this->getAddressFromData($quote->getBillingAddress());
752+
if ($billingAddressFromData && $shippingAddressFromData != $billingAddressFromData) {
753+
$output['isBillingAddressFromDataValid'] = $quote->getBillingAddress()->validate() === true;
754+
$output['billingAddressFromData'] = $billingAddressFromData;
755+
}
756+
}
757+
758+
return $output;
759+
}
760+
761+
/**
762+
* Get logged-in customer
763+
*
764+
* @return CustomerInterface
765+
*/
766+
private function getCustomer(): CustomerInterface
767+
{
768+
return $this->customerRepository->getById($this->customerSession->getCustomerId());
769+
}
734770
}
Lines changed: 298 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
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+
namespace Magento\Checkout\Test\Unit\Model;
9+
10+
use Magento\Captcha\Api\CaptchaConfigPostProcessorInterface;
11+
use Magento\Catalog\Helper\Image;
12+
use Magento\Catalog\Helper\Product\ConfigurationPool;
13+
use Magento\Checkout\Helper\Data as CheckoutHelper;
14+
use Magento\Checkout\Model\Cart\ImageProvider;
15+
use Magento\Checkout\Model\DefaultConfigProvider;
16+
use Magento\Checkout\Model\Session as CheckoutSession;
17+
use Magento\Customer\Api\AddressMetadataInterface;
18+
use Magento\Customer\Api\CustomerRepositoryInterface as CustomerRepository;
19+
use Magento\Customer\Api\Data\AttributeMetadataInterface;
20+
use Magento\Customer\Model\Address\CustomerAddressDataProvider;
21+
use Magento\Customer\Model\Address\Mapper;
22+
use Magento\Customer\Model\Session as CustomerSession;
23+
use Magento\Customer\Model\Url as CustomerUrlManager;
24+
use Magento\Directory\Helper\Data;
25+
use Magento\Directory\Model\Country\Postcode\ConfigInterface;
26+
use Magento\Eav\Api\AttributeOptionManagementInterface;
27+
use Magento\Framework\App\Config\ScopeConfigInterface;
28+
use Magento\Framework\App\Http\Context as HttpContext;
29+
use Magento\Framework\Data\Form\FormKey;
30+
use Magento\Framework\Locale\FormatInterface as LocaleFormat;
31+
use Magento\Framework\UrlInterface;
32+
use Magento\Quote\Api\CartItemRepositoryInterface as QuoteItemRepository;
33+
use Magento\Quote\Api\CartRepositoryInterface;
34+
use Magento\Quote\Api\CartTotalRepositoryInterface;
35+
use Magento\Quote\Api\Data\TotalsInterface;
36+
use Magento\Quote\Api\PaymentMethodManagementInterface;
37+
use Magento\Quote\Api\ShippingMethodManagementInterface as ShippingMethodManager;
38+
use Magento\Quote\Model\Quote;
39+
use Magento\Quote\Model\Quote\Address;
40+
use Magento\Quote\Model\QuoteIdMaskFactory;
41+
use Magento\Shipping\Model\Config;
42+
use Magento\Store\Model\Store;
43+
use Magento\Store\Model\StoreManagerInterface;
44+
use PHPUnit\Framework\MockObject\MockObject;
45+
use PHPUnit\Framework\TestCase;
46+
47+
/**
48+
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
49+
*/
50+
class DefaultConfigProviderTest extends TestCase
51+
{
52+
/**
53+
* @var DefaultConfigProvider
54+
*/
55+
private $model;
56+
57+
/**
58+
* @var CheckoutSession|MockObject
59+
*/
60+
private $checkoutSession;
61+
62+
/**
63+
* @var ShippingMethodManager|MockObject
64+
*/
65+
private $shippingMethodManager;
66+
67+
/**
68+
* @var AddressMetadataInterface|MockObject
69+
*/
70+
private $addressMetadata;
71+
72+
/**
73+
* @var CartTotalRepositoryInterface|MockObject
74+
*/
75+
private $cartTotalRepository;
76+
77+
/**
78+
* @var Config|MockObject
79+
*/
80+
private $shippingMethodConfig;
81+
82+
/**
83+
* @var CaptchaConfigPostProcessorInterface|MockObject
84+
*/
85+
private $configPostProcessor;
86+
87+
/**
88+
* @inheritdoc
89+
*/
90+
protected function setUp(): void
91+
{
92+
parent::setUp();
93+
$checkoutHelper = $this->createMock(CheckoutHelper::class);
94+
$this->checkoutSession = $this->createMock(CheckoutSession::class);
95+
$customerRepository = $this->createMock(CustomerRepository::class);
96+
$customerSession = $this->createMock(CustomerSession::class);
97+
$customerUrlManager = $this->createMock(CustomerUrlManager::class);
98+
$httpContext = $this->createMock(HttpContext::class);
99+
$quoteRepository = $this->createMock(CartRepositoryInterface::class);
100+
$quoteItemRepository = $this->createMock(QuoteItemRepository::class);
101+
$this->shippingMethodManager = $this->getMockBuilder(ShippingMethodManager::class)
102+
->addMethods(['get'])
103+
->getMockForAbstractClass();
104+
$configurationPool = $this->createMock(ConfigurationPool::class);
105+
$quoteIdMaskFactory = $this->createMock(QuoteIdMaskFactory::class);
106+
$localeFormat = $this->createMock(LocaleFormat::class);
107+
$addressMapper = $this->createMock(Mapper::class);
108+
$addressConfig = $this->createMock(\Magento\Customer\Model\Address\Config::class);
109+
$formKey = $this->createMock(FormKey::class);
110+
$imageHelper = $this->createMock(Image::class);
111+
$viewConfig = $this->createMock(\Magento\Framework\View\ConfigInterface::class);
112+
$postCodesConfig = $this->createMock(ConfigInterface::class);
113+
$imageProvider = $this->createMock(ImageProvider::class);
114+
$directoryHelper = $this->createMock(Data::class);
115+
$this->cartTotalRepository = $this->createMock(CartTotalRepositoryInterface::class);
116+
$scopeConfig = $this->createMock(ScopeConfigInterface::class);
117+
$this->shippingMethodConfig = $this->createMock(Config::class);
118+
$storeManager = $this->createMock(StoreManagerInterface::class);
119+
$paymentMethodManagement = $this->createMock(PaymentMethodManagementInterface::class);
120+
$urlBuilder = $this->createMock(UrlInterface::class);
121+
$this->configPostProcessor = $this->createMock(CaptchaConfigPostProcessorInterface::class);
122+
$this->addressMetadata = $this->createMock(AddressMetadataInterface::class);
123+
$attributeOptionManager = $this->createMock(AttributeOptionManagementInterface::class);
124+
$customerAddressData = $this->createMock(CustomerAddressDataProvider::class);
125+
$this->model = new DefaultConfigProvider(
126+
$checkoutHelper,
127+
$this->checkoutSession,
128+
$customerRepository,
129+
$customerSession,
130+
$customerUrlManager,
131+
$httpContext,
132+
$quoteRepository,
133+
$quoteItemRepository,
134+
$this->shippingMethodManager,
135+
$configurationPool,
136+
$quoteIdMaskFactory,
137+
$localeFormat,
138+
$addressMapper,
139+
$addressConfig,
140+
$formKey,
141+
$imageHelper,
142+
$viewConfig,
143+
$postCodesConfig,
144+
$imageProvider,
145+
$directoryHelper,
146+
$this->cartTotalRepository,
147+
$scopeConfig,
148+
$this->shippingMethodConfig,
149+
$storeManager,
150+
$paymentMethodManagement,
151+
$urlBuilder,
152+
$this->configPostProcessor,
153+
$this->addressMetadata,
154+
$attributeOptionManager,
155+
$customerAddressData
156+
);
157+
}
158+
159+
/**
160+
* @param array $shippingAddressData
161+
* @param array $billingAddressData
162+
* @param array $expected
163+
* @dataProvider getConfigQuoteAddressDataDataProvider
164+
*/
165+
public function testGetConfigQuoteAddressData(
166+
array $shippingAddressData,
167+
array $billingAddressData,
168+
array $expected
169+
): void {
170+
$shippingAddressData['email'] = '[email protected]';
171+
$billingAddressData['email'] = '[email protected]';
172+
$keys = [
173+
'isShippingAddressFromDataValid',
174+
'shippingAddressFromData',
175+
'isBillingAddressFromDataValid',
176+
'billingAddressFromData',
177+
];
178+
$quote = $this->createMock(Quote::class);
179+
$shippingAddress = $this->getMockBuilder(Address::class)
180+
->disableOriginalConstructor()
181+
->onlyMethods(['validate'])
182+
->getMockForAbstractClass();
183+
$shippingAddress->addData($shippingAddressData);
184+
$shippingAddress->method('validate')
185+
->willReturn(!empty($shippingAddress['firstname']));
186+
$billingAddress = $this->getMockBuilder(Address::class)
187+
->disableOriginalConstructor()
188+
->onlyMethods(['validate'])
189+
->getMockForAbstractClass();
190+
$billingAddress->addData($billingAddressData);
191+
$billingAddress->method('validate')
192+
->willReturn(!empty($shippingAddress['firstname']));
193+
$quote->method('getShippingAddress')
194+
->willReturn($shippingAddress);
195+
$quote->method('getBillingAddress')
196+
->willReturn($billingAddress);
197+
$quote->method('getStore')
198+
->willReturn($this->createMock(Store::class));
199+
$this->checkoutSession->expects($this->atLeast(1))
200+
->method('getQuote')
201+
->willReturn($quote);
202+
203+
$attributeMetadata1 = $this->createMock(AttributeMetadataInterface::class);
204+
$attributeMetadata1->method('isVisible')
205+
->willReturn(true);
206+
$attributeMetadata1->method('getAttributeCode')
207+
->willReturn('firstname');
208+
209+
$attributeMetadata2 = $this->createMock(AttributeMetadataInterface::class);
210+
$attributeMetadata2->method('isVisible')
211+
->willReturn(true);
212+
$attributeMetadata2->method('getAttributeCode')
213+
->willReturn('lastname');
214+
215+
$this->addressMetadata->method('getAllAttributesMetadata')
216+
->willReturn([$attributeMetadata1, $attributeMetadata2]);
217+
218+
$totals = $this->getMockBuilder(TotalsInterface::class)
219+
->addMethods(['toArray'])
220+
->getMockForAbstractClass();
221+
$totals->method('getItems')
222+
->willReturn([]);
223+
$totals->method('getTotalSegments')
224+
->willReturn([]);
225+
$this->cartTotalRepository->method('get')
226+
->willReturn($totals);
227+
$this->shippingMethodConfig->method('getActiveCarriers')
228+
->willReturn([]);
229+
$this->configPostProcessor->method('process')
230+
->willReturnArgument(0);
231+
$actual = array_intersect_key($this->model->getConfig(), array_flip($keys));
232+
$this->assertEquals($expected, $actual);
233+
}
234+
235+
/**
236+
* @return array
237+
*/
238+
public function getConfigQuoteAddressDataDataProvider(): array
239+
{
240+
return [
241+
[
242+
[],
243+
[],
244+
[]
245+
],
246+
[
247+
[
248+
'firstname' => 'John'
249+
],
250+
[
251+
'firstname' => 'Jack'
252+
],
253+
[
254+
'isShippingAddressFromDataValid' => true,
255+
'shippingAddressFromData' => [
256+
'firstname' => 'John'
257+
],
258+
'isBillingAddressFromDataValid' => true,
259+
'billingAddressFromData' => [
260+
'firstname' => 'Jack'
261+
]
262+
]
263+
],
264+
[
265+
[
266+
'lastname' => 'John'
267+
],
268+
[
269+
'lastname' => 'Jack'
270+
],
271+
[
272+
'isShippingAddressFromDataValid' => false,
273+
'shippingAddressFromData' => [
274+
'lastname' => 'John'
275+
],
276+
'isBillingAddressFromDataValid' => false,
277+
'billingAddressFromData' => [
278+
'lastname' => 'Jack'
279+
]
280+
]
281+
],
282+
[
283+
[
284+
'firstname' => 'John'
285+
],
286+
[
287+
'firstname' => 'John'
288+
],
289+
[
290+
'isShippingAddressFromDataValid' => true,
291+
'shippingAddressFromData' => [
292+
'firstname' => 'John'
293+
],
294+
]
295+
],
296+
];
297+
}
298+
}

0 commit comments

Comments
 (0)