Skip to content

Commit 720f05c

Browse files
committed
MAGETWO-97456: Cart's customer and address mismatch
1 parent 5b3adc5 commit 720f05c

File tree

9 files changed

+406
-33
lines changed

9 files changed

+406
-33
lines changed

app/code/Magento/Quote/Model/Quote/Address/BillingAddressPersister.php

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
use Magento\Quote\Model\QuoteAddressValidator;
1313
use Magento\Customer\Api\AddressRepositoryInterface;
1414

15+
/**
16+
* Saves billing address for quotes.
17+
*/
1518
class BillingAddressPersister
1619
{
1720
/**
@@ -37,17 +40,17 @@ public function __construct(
3740
}
3841

3942
/**
43+
* Save address for billing.
44+
*
4045
* @param CartInterface $quote
4146
* @param AddressInterface $address
4247
* @param bool $useForShipping
4348
* @return void
44-
* @throws NoSuchEntityException
45-
* @throws InputException
4649
*/
4750
public function save(CartInterface $quote, AddressInterface $address, $useForShipping = false)
4851
{
4952
/** @var \Magento\Quote\Model\Quote $quote */
50-
$this->addressValidator->validate($address);
53+
$this->addressValidator->validateForCart($quote, $address);
5154
$customerAddressId = $address->getCustomerAddressId();
5255
$shippingAddress = null;
5356
$addressData = [];

app/code/Magento/Quote/Model/QuoteAddressValidator.php

Lines changed: 52 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@
66
namespace Magento\Quote\Model;
77

88
use Magento\Framework\Exception\NoSuchEntityException;
9+
use Magento\Quote\Api\Data\AddressInterface;
10+
use Magento\Quote\Api\Data\CartInterface;
911

1012
/**
1113
* Quote shipping/billing address validator service.
1214
*
15+
* @SuppressWarnings(PHPMD.CookieAndSessionMisuse)
1316
*/
1417
class QuoteAddressValidator
1518
{
@@ -28,7 +31,7 @@ class QuoteAddressValidator
2831
protected $customerRepository;
2932

3033
/**
31-
* @var \Magento\Customer\Model\Session
34+
* @deprecated This class is not a part of HTML presentation layer and should not use sessions.
3235
*/
3336
protected $customerSession;
3437

@@ -50,44 +53,77 @@ public function __construct(
5053
}
5154

5255
/**
53-
* Validates the fields in a specified address data object.
56+
* Validate address.
5457
*
55-
* @param \Magento\Quote\Api\Data\AddressInterface $addressData The address data object.
56-
* @return bool
57-
* @throws \Magento\Framework\Exception\InputException The specified address belongs to another customer.
58+
* @param AddressInterface $address
59+
* @param int|null $customerId Cart belongs to
60+
* @return void
5861
* @throws \Magento\Framework\Exception\NoSuchEntityException The specified customer ID or address ID is not valid.
5962
*/
60-
public function validate(\Magento\Quote\Api\Data\AddressInterface $addressData)
63+
private function doValidate(AddressInterface $address, $customerId)
6164
{
6265
//validate customer id
63-
if ($addressData->getCustomerId()) {
64-
$customer = $this->customerRepository->getById($addressData->getCustomerId());
66+
if ($customerId) {
67+
$customer = $this->customerRepository->getById($customerId);
6568
if (!$customer->getId()) {
6669
throw new \Magento\Framework\Exception\NoSuchEntityException(
67-
__('Invalid customer id %1', $addressData->getCustomerId())
70+
__('Invalid customer id %1', $customerId)
6871
);
6972
}
7073
}
7174

72-
if ($addressData->getCustomerAddressId()) {
75+
if ($address->getCustomerAddressId()) {
76+
//Existing address cannot belong to a guest
77+
if (!$customerId) {
78+
throw new \Magento\Framework\Exception\NoSuchEntityException(
79+
__('Invalid customer address id %1', $address->getCustomerAddressId())
80+
);
81+
}
82+
//Validating address ID
7383
try {
74-
$this->addressRepository->getById($addressData->getCustomerAddressId());
84+
$this->addressRepository->getById($address->getCustomerAddressId());
7585
} catch (NoSuchEntityException $e) {
7686
throw new \Magento\Framework\Exception\NoSuchEntityException(
77-
__('Invalid address id %1', $addressData->getId())
87+
__('Invalid address id %1', $address->getId())
7888
);
7989
}
80-
90+
//Finding available customer's addresses
8191
$applicableAddressIds = array_map(function ($address) {
8292
/** @var \Magento\Customer\Api\Data\AddressInterface $address */
8393
return $address->getId();
84-
}, $this->customerRepository->getById($addressData->getCustomerId())->getAddresses());
85-
if (!in_array($addressData->getCustomerAddressId(), $applicableAddressIds)) {
94+
}, $this->customerRepository->getById($customerId)->getAddresses());
95+
if (!in_array($address->getCustomerAddressId(), $applicableAddressIds)) {
8696
throw new \Magento\Framework\Exception\NoSuchEntityException(
87-
__('Invalid customer address id %1', $addressData->getCustomerAddressId())
97+
__('Invalid customer address id %1', $address->getCustomerAddressId())
8898
);
8999
}
90100
}
101+
}
102+
103+
/**
104+
* Validates the fields in a specified address data object.
105+
*
106+
* @param \Magento\Quote\Api\Data\AddressInterface $addressData The address data object.
107+
* @return bool
108+
* @throws \Magento\Framework\Exception\NoSuchEntityException The specified customer ID or address ID is not valid.
109+
*/
110+
public function validate(AddressInterface $addressData): bool
111+
{
112+
$this->doValidate($addressData, $addressData->getCustomerId());
113+
91114
return true;
92115
}
116+
117+
/**
118+
* Validate address to be used for cart.
119+
*
120+
* @param CartInterface $cart
121+
* @param AddressInterface $address
122+
* @return void
123+
* @throws \Magento\Framework\Exception\NoSuchEntityException The specified customer ID or address ID is not valid.
124+
*/
125+
public function validateForCart(CartInterface $cart, AddressInterface $address)
126+
{
127+
$this->doValidate($address, $cart->getCustomerIsGuest() ? null : $cart->getCustomer()->getId());
128+
}
93129
}

app/code/Magento/Quote/Model/ShippingAddressManagement.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public function __construct(
7878
}
7979

8080
/**
81-
* {@inheritDoc}
81+
* @inheritDoc
8282
* @SuppressWarnings(PHPMD.NPathComplexity)
8383
*/
8484
public function assign($cartId, \Magento\Quote\Api\Data\AddressInterface $address)
@@ -94,7 +94,7 @@ public function assign($cartId, \Magento\Quote\Api\Data\AddressInterface $addres
9494
$saveInAddressBook = $address->getSaveInAddressBook() ? 1 : 0;
9595
$sameAsBilling = $address->getSameAsBilling() ? 1 : 0;
9696
$customerAddressId = $address->getCustomerAddressId();
97-
$this->addressValidator->validate($address);
97+
$this->addressValidator->validateForCart($quote, $address);
9898
$quote->setShippingAddress($address);
9999
$address = $quote->getShippingAddress();
100100

@@ -122,7 +122,7 @@ public function assign($cartId, \Magento\Quote\Api\Data\AddressInterface $addres
122122
}
123123

124124
/**
125-
* {@inheritDoc}
125+
* @inheritDoc
126126
*/
127127
public function get($cartId)
128128
{

app/code/Magento/Quote/Test/Unit/Model/QuoteAddressValidatorTest.php

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,17 +77,13 @@ public function testValidateInvalidCustomer()
7777

7878
/**
7979
* @expectedException \Magento\Framework\Exception\NoSuchEntityException
80-
* @expectedExceptionMessage Invalid address id 101
80+
* @expectedExceptionMessage Invalid customer address id 101
8181
*/
8282
public function testValidateInvalidAddress()
8383
{
8484
$address = $this->createMock(\Magento\Quote\Api\Data\AddressInterface::class);
8585
$this->customerRepositoryMock->expects($this->never())->method('getById');
86-
$address->expects($this->atLeastOnce())->method('getCustomerAddressId')->willReturn(101);
87-
$address->expects($this->once())->method('getId')->willReturn(101);
88-
89-
$this->addressRepositoryMock->expects($this->once())->method('getById')
90-
->willThrowException(new \Magento\Framework\Exception\NoSuchEntityException());
86+
$address->expects($this->exactly(2))->method('getCustomerAddressId')->willReturn(101);
9187

9288
$this->model->validate($address);
9389
}

app/code/Magento/Quote/Test/Unit/Model/ShippingAddressManagementTest.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ public function testSetAddressValidationFailed()
110110
->with('cart654')
111111
->will($this->returnValue($quoteMock));
112112

113-
$this->validatorMock->expects($this->once())->method('validate')
113+
$this->validatorMock->expects($this->once())->method('validateForCart')
114114
->will($this->throwException(new \Magento\Framework\Exception\NoSuchEntityException(__('error345'))));
115115

116116
$this->service->assign('cart654', $this->quoteAddressMock);
@@ -143,8 +143,8 @@ public function testSetAddress()
143143
->with($customerAddressId)
144144
->willReturn($customerAddressMock);
145145

146-
$this->validatorMock->expects($this->once())->method('validate')
147-
->with($this->quoteAddressMock)
146+
$this->validatorMock->expects($this->once())->method('validateForCart')
147+
->with($quoteMock, $this->quoteAddressMock)
148148
->willReturn(true);
149149

150150
$quoteMock->expects($this->exactly(3))->method('getShippingAddress')->willReturn($this->quoteAddressMock);
@@ -218,8 +218,8 @@ public function testSetAddressWithInabilityToSaveQuote()
218218
->with($customerAddressId)
219219
->willReturn($customerAddressMock);
220220

221-
$this->validatorMock->expects($this->once())->method('validate')
222-
->with($this->quoteAddressMock)
221+
$this->validatorMock->expects($this->once())->method('validateForCart')
222+
->with($quoteMock, $this->quoteAddressMock)
223223
->willReturn(true);
224224

225225
$this->quoteAddressMock->expects($this->once())->method('getSaveInAddressBook')->willReturn(1);
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
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\Api;
9+
10+
use Magento\Checkout\Api\Data\ShippingInformationInterface;
11+
use Magento\Checkout\Api\Data\ShippingInformationInterfaceFactory;
12+
use Magento\Customer\Api\CustomerRepositoryInterface;
13+
use Magento\Framework\Api\SearchCriteriaBuilder;
14+
use Magento\Quote\Api\CartRepositoryInterface;
15+
use Magento\Quote\Api\Data\ShippingAssignmentInterface;
16+
use Magento\TestFramework\Helper\Bootstrap;
17+
use PHPUnit\Framework\TestCase;
18+
use Magento\Quote\Model\QuoteIdMask;
19+
use Magento\Quote\Model\QuoteIdMaskFactory;
20+
21+
/**
22+
* Test GuestShippingInformationManagement API.
23+
*/
24+
class GuestShippingInformationManagementTest extends TestCase
25+
{
26+
/**
27+
* @var GuestShippingInformationManagementInterface
28+
*/
29+
private $management;
30+
31+
/**
32+
* @var CartRepositoryInterface
33+
*/
34+
private $cartRepo;
35+
36+
/**
37+
* @var CustomerRepositoryInterface
38+
*/
39+
private $customerRepo;
40+
41+
/**
42+
* @var ShippingInformationInterfaceFactory
43+
*/
44+
private $shippingFactory;
45+
46+
/**
47+
* @var SearchCriteriaBuilder
48+
*/
49+
private $searchCriteria;
50+
51+
/**
52+
* @var QuoteIdMaskFactory
53+
*/
54+
private $maskFactory;
55+
56+
/**
57+
* @inheritdoc
58+
*/
59+
protected function setUp()
60+
{
61+
$objectManager = Bootstrap::getObjectManager();
62+
$this->management = $objectManager->get(GuestShippingInformationManagementInterface::class);
63+
$this->cartRepo = $objectManager->get(CartRepositoryInterface::class);
64+
$this->customerRepo = $objectManager->get(CustomerRepositoryInterface::class);
65+
$this->shippingFactory = $objectManager->get(ShippingInformationInterfaceFactory::class);
66+
$this->searchCriteria = $objectManager->get(SearchCriteriaBuilder::class);
67+
$this->maskFactory = $objectManager->get(QuoteIdMaskFactory::class);
68+
}
69+
70+
/**
71+
* Test using another address for quote.
72+
*
73+
* @param bool $swapShipping Whether to swap shipping or billing addresses.
74+
* @return void
75+
*
76+
* @magentoDataFixture Magento/Sales/_files/quote.php
77+
* @magentoDataFixture Magento/Customer/_files/customer_with_addresses.php
78+
* @dataProvider differentAddressesDataProvider
79+
* @expectedException \Magento\Framework\Exception\InputException
80+
* @expectedExceptionMessage Unable to save shipping information. Please check input data.
81+
*/
82+
public function testDifferentAddresses(bool $swapShipping)
83+
{
84+
$carts = $this->cartRepo->getList(
85+
$this->searchCriteria->addFilter('reserved_order_id', 'test01')->create()
86+
)->getItems();
87+
$cart = array_pop($carts);
88+
$otherCustomer = $this->customerRepo->get('customer_with_addresses@test.com');
89+
$otherAddresses = $otherCustomer->getAddresses();
90+
$otherAddress = array_pop($otherAddresses);
91+
92+
//Setting invalid IDs.
93+
/** @var ShippingAssignmentInterface $shippingAssignment */
94+
$shippingAssignment = $cart->getExtensionAttributes()->getShippingAssignments()[0];
95+
$shippingAddress = $shippingAssignment->getShipping()->getAddress();
96+
$billingAddress = $cart->getBillingAddress();
97+
if ($swapShipping) {
98+
$address = $shippingAddress;
99+
} else {
100+
$address = $billingAddress;
101+
}
102+
$address->setCustomerAddressId($otherAddress->getId());
103+
$address->setCustomerId($otherCustomer->getId());
104+
$address->setId(null);
105+
/** @var ShippingInformationInterface $shippingInformation */
106+
$shippingInformation = $this->shippingFactory->create();
107+
$shippingInformation->setBillingAddress($billingAddress);
108+
$shippingInformation->setShippingAddress($shippingAddress);
109+
$shippingInformation->setShippingMethodCode('flatrate');
110+
/** @var QuoteIdMask $idMask */
111+
$idMask = $this->maskFactory->create();
112+
$idMask->load($cart->getId(), 'quote_id');
113+
$this->management->saveAddressInformation($idMask->getMaskedId(), $shippingInformation);
114+
}
115+
116+
/**
117+
* @return array
118+
*/
119+
public function differentAddressesDataProvider(): array
120+
{
121+
return [
122+
'Shipping address swap' => [true],
123+
'Billing address swap' => [false],
124+
];
125+
}
126+
}

0 commit comments

Comments
 (0)