Skip to content

Commit f06b479

Browse files
authored
Merge pull request #7954 from magento-gl/Arrows_BugFixDelivery
Arrows bug fix delivery
2 parents 9c42b97 + 07ecd4a commit f06b479

File tree

5 files changed

+162
-18
lines changed

5 files changed

+162
-18
lines changed

app/code/Magento/Checkout/view/frontend/web/js/view/checkout/placeOrderCaptcha.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ function (defaultCaptcha, captchaList, _, placeOrderHooks) {
2020

2121
this._super();
2222
currentCaptcha = captchaList.getCaptchaByFormId(this.formId);
23-
2423
if (currentCaptcha != null) {
2524
currentCaptcha.setIsVisible(true);
2625
this.setCurrentCaptcha(currentCaptcha);
@@ -29,9 +28,11 @@ function (defaultCaptcha, captchaList, _, placeOrderHooks) {
2928
headers['X-Captcha'] = self.captchaValue()();
3029
}
3130
});
32-
placeOrderHooks.afterRequestListeners.push(function () {
33-
self.refresh();
34-
});
31+
if (self.isRequired()) {
32+
placeOrderHooks.afterRequestListeners.push(function () {
33+
self.refresh();
34+
});
35+
}
3536
}
3637
}
3738
});

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

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
use Magento\Framework\Exception\NoSuchEntityException;
2525
use Magento\Framework\Exception\StateException;
2626
use Magento\Framework\HTTP\PhpEnvironment\RemoteAddress;
27+
use Magento\Framework\Lock\LockManagerInterface;
2728
use Magento\Framework\Model\AbstractExtensibleModel;
2829
use Magento\Framework\Validator\Exception as ValidatorException;
2930
use Magento\Payment\Model\Method\AbstractMethod;
@@ -51,6 +52,10 @@
5152
*/
5253
class QuoteManagement implements CartManagementInterface
5354
{
55+
private const LOCK_PREFIX = 'PLACE_ORDER_';
56+
57+
private const LOCK_TIMEOUT = 10;
58+
5459
/**
5560
* @var EventManager
5661
*/
@@ -151,6 +156,11 @@ class QuoteManagement implements CartManagementInterface
151156
*/
152157
protected $quoteFactory;
153158

159+
/**
160+
* @var LockManagerInterface
161+
*/
162+
private $lockManager;
163+
154164
/**
155165
* @var QuoteIdMaskFactory
156166
*/
@@ -201,6 +211,7 @@ class QuoteManagement implements CartManagementInterface
201211
* @param AddressRepositoryInterface|null $addressRepository
202212
* @param RequestInterface|null $request
203213
* @param RemoteAddress $remoteAddress
214+
* @param LockManagerInterface $lockManager
204215
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
205216
*/
206217
public function __construct(
@@ -227,7 +238,8 @@ public function __construct(
227238
QuoteIdMaskFactory $quoteIdMaskFactory = null,
228239
AddressRepositoryInterface $addressRepository = null,
229240
RequestInterface $request = null,
230-
RemoteAddress $remoteAddress = null
241+
RemoteAddress $remoteAddress = null,
242+
LockManagerInterface $lockManager = null
231243
) {
232244
$this->eventManager = $eventManager;
233245
$this->submitQuoteValidator = $submitQuoteValidator;
@@ -257,6 +269,8 @@ public function __construct(
257269
->get(RequestInterface::class);
258270
$this->remoteAddress = $remoteAddress ?: ObjectManager::getInstance()
259271
->get(RemoteAddress::class);
272+
$this->lockManager = $lockManager ?: ObjectManager::getInstance()
273+
->get(LockManagerInterface::class);
260274
}
261275

262276
/**
@@ -584,25 +598,29 @@ protected function submitQuote(QuoteEntity $quote, $orderData = [])
584598
$order->setCustomerFirstname($quote->getCustomerFirstname());
585599
$order->setCustomerMiddlename($quote->getCustomerMiddlename());
586600
$order->setCustomerLastname($quote->getCustomerLastname());
587-
588601
if ($quote->getOrigOrderId()) {
589602
$order->setEntityId($quote->getOrigOrderId());
590603
}
591-
592604
if ($quote->getReservedOrderId()) {
593605
$order->setIncrementId($quote->getReservedOrderId());
594606
}
595-
596607
$this->submitQuoteValidator->validateOrder($order);
597-
598608
$this->eventManager->dispatch(
599609
'sales_model_service_quote_submit_before',
600610
[
601611
'order' => $order,
602612
'quote' => $quote
603613
]
604614
);
615+
616+
$lockedName = self::LOCK_PREFIX . $quote->getId();
617+
if ($this->lockManager->isLocked($lockedName)) {
618+
throw new LocalizedException(__(
619+
'A server error stopped your order from being placed. Please try to place your order again.'
620+
));
621+
}
605622
try {
623+
$this->lockManager->lock($lockedName, self::LOCK_TIMEOUT);
606624
$order = $this->orderManagement->place($order);
607625
$quote->setIsActive(false);
608626
$this->eventManager->dispatch(
@@ -613,7 +631,9 @@ protected function submitQuote(QuoteEntity $quote, $orderData = [])
613631
]
614632
);
615633
$this->quoteRepository->save($quote);
634+
$this->lockManager->unlock($lockedName);
616635
} catch (\Exception $e) {
636+
$this->lockManager->unlock($lockedName);
617637
$this->rollbackAddresses($quote, $order, $e);
618638
throw $e;
619639
}

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

Lines changed: 130 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
use Magento\Framework\Exception\NoSuchEntityException;
2626
use Magento\Framework\Exception\StateException;
2727
use Magento\Framework\HTTP\PhpEnvironment\RemoteAddress;
28+
use Magento\Framework\Lock\LockManagerInterface;
2829
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
2930
use Magento\Quote\Api\CartRepositoryInterface;
3031
use Magento\Quote\Model\CustomerManagement;
@@ -196,6 +197,11 @@ class QuoteManagementTest extends TestCase
196197
*/
197198
private $quoteIdMaskFactoryMock;
198199

200+
/**
201+
* @var LockManagerInterface|MockObject
202+
*/
203+
private $lockManagerMock;
204+
199205
/**
200206
* @inheriDoc
201207
*
@@ -292,6 +298,9 @@ protected function setUp(): void
292298
$this->addressRepositoryMock = $this->getMockBuilder(AddressRepositoryInterface::class)
293299
->getMockForAbstractClass();
294300

301+
$this->lockManagerMock = $this->getMockBuilder(LockManagerInterface::class)
302+
->getMockForAbstractClass();
303+
295304
$this->model = $objectManager->getObject(
296305
QuoteManagement::class,
297306
[
@@ -315,7 +324,8 @@ protected function setUp(): void
315324
'customerSession' => $this->customerSessionMock,
316325
'accountManagement' => $this->accountManagementMock,
317326
'quoteFactory' => $this->quoteFactoryMock,
318-
'addressRepository' => $this->addressRepositoryMock
327+
'addressRepository' => $this->addressRepositoryMock,
328+
'lockManager' => $this->lockManagerMock
319329
]
320330
);
321331

@@ -761,7 +771,8 @@ public function testSubmit(): void
761771
$customerId,
762772
$quoteId,
763773
$quoteItems,
764-
$shippingAddress
774+
$shippingAddress,
775+
false
765776
);
766777

767778
$this->submitQuoteValidator->expects($this->once())
@@ -826,6 +837,7 @@ public function testSubmit(): void
826837
['order' => $order, 'quote' => $quote]
827838
]
828839
);
840+
$this->lockManagerMock->method('isLocked')->willReturn(false);
829841
$this->quoteRepositoryMock->expects($this->once())->method('save')->with($quote);
830842
$this->assertEquals($order, $this->model->submit($quote, $orderData));
831843
}
@@ -1063,7 +1075,8 @@ protected function getQuote(
10631075
int $customerId,
10641076
int $id,
10651077
array $quoteItems,
1066-
Address $shippingAddress = null
1078+
Address $shippingAddress = null,
1079+
bool $setIsActive
10671080
): MockObject {
10681081
$quote = $this->getMockBuilder(Quote::class)
10691082
->addMethods(['getCustomerEmail', 'getCustomerId'])
@@ -1085,9 +1098,11 @@ protected function getQuote(
10851098
)
10861099
->disableOriginalConstructor()
10871100
->getMock();
1088-
$quote->expects($this->once())
1089-
->method('setIsActive')
1090-
->with(false);
1101+
if ($setIsActive) {
1102+
$quote->expects($this->once())
1103+
->method('setIsActive')
1104+
->with(false);
1105+
}
10911106
$quote->expects($this->any())
10921107
->method('getAllVisibleItems')
10931108
->willReturn($quoteItems);
@@ -1129,7 +1144,7 @@ protected function getQuote(
11291144
$quote->expects($this->any())
11301145
->method('getCustomer')
11311146
->willReturn($customer);
1132-
$quote->expects($this->once())
1147+
$quote->expects($this->exactly(2))
11331148
->method('getId')
11341149
->willReturn($id);
11351150
$this->customerRepositoryMock->expects($this->any())->method('getById')->willReturn($customer);
@@ -1292,7 +1307,8 @@ public function testSubmitForCustomer(): void
12921307
$customerId,
12931308
$quoteId,
12941309
$quoteItems,
1295-
$shippingAddress
1310+
$shippingAddress,
1311+
false
12961312
);
12971313

12981314
$this->submitQuoteValidator->method('validateQuote')
@@ -1386,4 +1402,110 @@ private function createPartialMockForAbstractClass(string $className, array $met
13861402
$methods
13871403
);
13881404
}
1405+
1406+
/**
1407+
* @return void
1408+
*
1409+
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
1410+
*/
1411+
public function testSubmitWithLockException(): void
1412+
{
1413+
$orderData = [];
1414+
$isGuest = true;
1415+
$isVirtual = false;
1416+
$customerId = 1;
1417+
$quoteId = 1;
1418+
$quoteItem = $this->createMock(Item::class);
1419+
$billingAddress = $this->createMock(Address::class);
1420+
$shippingAddress = $this->getMockBuilder(Address::class)
1421+
->addMethods(['getQuoteId'])
1422+
->onlyMethods(['getShippingMethod', 'getId'])
1423+
->disableOriginalConstructor()
1424+
->getMock();
1425+
$payment = $this->createMock(Payment::class);
1426+
$baseOrder = $this->getMockForAbstractClass(OrderInterface::class);
1427+
$convertedBilling = $this->createPartialMockForAbstractClass(OrderAddressInterface::class, ['setData']);
1428+
$convertedShipping = $this->createPartialMockForAbstractClass(OrderAddressInterface::class, ['setData']);
1429+
$convertedPayment = $this->getMockForAbstractClass(OrderPaymentInterface::class);
1430+
$convertedQuoteItem = $this->getMockForAbstractClass(OrderItemInterface::class);
1431+
$addresses = [$convertedShipping, $convertedBilling];
1432+
$quoteItems = [$quoteItem];
1433+
$convertedItems = [$convertedQuoteItem];
1434+
$quote = $this->getQuote(
1435+
$isGuest,
1436+
$isVirtual,
1437+
$billingAddress,
1438+
$payment,
1439+
$customerId,
1440+
$quoteId,
1441+
$quoteItems,
1442+
$shippingAddress,
1443+
false
1444+
);
1445+
1446+
$this->submitQuoteValidator->expects($this->once())
1447+
->method('validateQuote')
1448+
->with($quote);
1449+
$this->quoteAddressToOrder->expects($this->once())
1450+
->method('convert')
1451+
->with($shippingAddress, $orderData)
1452+
->willReturn($baseOrder);
1453+
$this->quoteAddressToOrderAddress
1454+
->method('convert')
1455+
->withConsecutive(
1456+
[
1457+
$shippingAddress,
1458+
[
1459+
'address_type' => 'shipping',
1460+
'email' => 'customer@example.com'
1461+
]
1462+
],
1463+
[
1464+
$billingAddress,
1465+
[
1466+
'address_type' => 'billing',
1467+
'email' => 'customer@example.com'
1468+
]
1469+
]
1470+
)->willReturnOnConsecutiveCalls($convertedShipping, $convertedBilling);
1471+
$billingAddress->expects($this->once())->method('getId')->willReturn(4);
1472+
$convertedBilling->expects($this->once())->method('setData')->with('quote_address_id', 4);
1473+
1474+
$this->quoteItemToOrderItem->expects($this->once())->method('convert')
1475+
->with($quoteItem, ['parent_item' => null])
1476+
->willReturn($convertedQuoteItem);
1477+
$this->quotePaymentToOrderPayment->expects($this->once())->method('convert')->with($payment)
1478+
->willReturn($convertedPayment);
1479+
$shippingAddress->expects($this->once())->method('getShippingMethod')->willReturn('free');
1480+
$shippingAddress->expects($this->once())->method('getId')->willReturn(5);
1481+
$convertedShipping->expects($this->once())->method('setData')->with('quote_address_id', 5);
1482+
$order = $this->prepareOrderFactory(
1483+
$baseOrder,
1484+
$convertedBilling,
1485+
$addresses,
1486+
$convertedPayment,
1487+
$convertedItems,
1488+
$quoteId,
1489+
$convertedShipping
1490+
);
1491+
1492+
$this->eventManager
1493+
->method('dispatch')
1494+
->withConsecutive(
1495+
[
1496+
'sales_model_service_quote_submit_before',
1497+
['order' => $order, 'quote' => $quote]
1498+
],
1499+
[
1500+
'sales_model_service_quote_submit_success',
1501+
['order' => $order, 'quote' => $quote]
1502+
]
1503+
);
1504+
$this->lockManagerMock->method('isLocked')->willReturn(true);
1505+
1506+
$this->expectExceptionMessage(
1507+
'A server error stopped your order from being placed. Please try to place your order again.'
1508+
);
1509+
$this->assertEquals($order, $this->model->submit($quote, $orderData));
1510+
}
13891511
}

app/code/Magento/Webapi/Model/ServiceMetadata.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ public function getServiceMetadata($serviceName)
201201
{
202202
$servicesConfig = $this->getServicesConfig();
203203
if (!isset($servicesConfig[$serviceName]) || !is_array($servicesConfig[$serviceName])) {
204-
throw new RuntimeException(__('Requested service is not available: "%1"', $serviceName)->render());
204+
throw new RuntimeException((string)__('Requested service is not available: "%1"', $serviceName)->render());
205205
}
206206
return $servicesConfig[$serviceName];
207207
}

dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQlAbstract.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ protected function runTest()
263263
$resetConfig = $this->resetAddedSection($initialConfig, $currentConfig, $cacheSaltPathChunks);
264264
$resetFileContents = $this->formatter->format($resetConfig);
265265
$directoryWrite = $this->filesystem->getDirectoryWrite(DirectoryList::CONFIG);
266+
sleep(4);
266267
$directoryWrite->writeFile($this->envConfigPath, $resetFileContents);
267268
}
268269
}

0 commit comments

Comments
 (0)