Skip to content

Commit ca01449

Browse files
author
Alexander Akimov
authored
Merge pull request #1210 from magento-folks/bugfix
[Folks] Bugfix
2 parents a45e591 + 4c72450 commit ca01449

File tree

19 files changed

+882
-26
lines changed

19 files changed

+882
-26
lines changed

app/code/Magento/Bundle/Model/CartItemProcessor.php

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -84,26 +84,28 @@ public function processOptions(CartItemInterface $cartItem)
8484
$productOptions = [];
8585
$bundleOptions = $cartItem->getBuyRequest()->getBundleOption();
8686
$bundleOptionsQty = $cartItem->getBuyRequest()->getBundleOptionQty();
87-
foreach ($bundleOptions as $optionId => $optionSelections) {
88-
if (empty($optionSelections)) {
89-
continue;
90-
}
91-
$optionSelections = is_array($optionSelections) ? $optionSelections : [$optionSelections];
92-
$optionQty = isset($bundleOptionsQty[$optionId]) ? $bundleOptionsQty[$optionId] : 1;
87+
if (is_array($bundleOptions)) {
88+
foreach ($bundleOptions as $optionId => $optionSelections) {
89+
if (empty($optionSelections)) {
90+
continue;
91+
}
92+
$optionSelections = is_array($optionSelections) ? $optionSelections : [$optionSelections];
93+
$optionQty = isset($bundleOptionsQty[$optionId]) ? $bundleOptionsQty[$optionId] : 1;
9394

94-
/** @var \Magento\Bundle\Api\Data\BundleOptionInterface $productOption */
95-
$productOption = $this->bundleOptionFactory->create();
96-
$productOption->setOptionId($optionId);
97-
$productOption->setOptionSelections($optionSelections);
98-
$productOption->setOptionQty($optionQty);
99-
$productOptions[] = $productOption;
100-
}
95+
/** @var \Magento\Bundle\Api\Data\BundleOptionInterface $productOption */
96+
$productOption = $this->bundleOptionFactory->create();
97+
$productOption->setOptionId($optionId);
98+
$productOption->setOptionSelections($optionSelections);
99+
$productOption->setOptionQty($optionQty);
100+
$productOptions[] = $productOption;
101+
}
101102

102-
$extension = $this->productOptionExtensionFactory->create()->setBundleOptions($productOptions);
103-
if (!$cartItem->getProductOption()) {
104-
$cartItem->setProductOption($this->productOptionFactory->create());
103+
$extension = $this->productOptionExtensionFactory->create()->setBundleOptions($productOptions);
104+
if (!$cartItem->getProductOption()) {
105+
$cartItem->setProductOption($this->productOptionFactory->create());
106+
}
107+
$cartItem->getProductOption()->setExtensionAttributes($extension);
105108
}
106-
$cartItem->getProductOption()->setExtensionAttributes($extension);
107109
return $cartItem;
108110
}
109111
}

app/code/Magento/Bundle/Test/Unit/Model/CartItemProcessorTest.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,4 +180,22 @@ public function testProcessProductOptionsInvalidType()
180180
$cartItemMock->expects($this->once())->method('getProductType')->willReturn(Type::TYPE_SIMPLE);
181181
$this->assertSame($cartItemMock, $this->model->processOptions($cartItemMock));
182182
}
183+
184+
public function testProcessProductOptionsifBundleOptionsNotExists()
185+
{
186+
$buyRequestMock = new \Magento\Framework\DataObject(
187+
[]
188+
);
189+
$methods = ['getProductType', 'getBuyRequest'];
190+
$cartItemMock = $this->getMock(
191+
\Magento\Quote\Model\Quote\Item::class,
192+
$methods,
193+
[],
194+
'',
195+
false
196+
);
197+
$cartItemMock->expects($this->once())->method('getProductType')->willReturn(Type::TYPE_BUNDLE);
198+
$cartItemMock->expects($this->exactly(2))->method('getBuyRequest')->willReturn($buyRequestMock);
199+
$this->assertSame($cartItemMock, $this->model->processOptions($cartItemMock));
200+
}
183201
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\CheckoutAgreements\Model\Checkout\Plugin;
7+
8+
use Magento\CheckoutAgreements\Model\AgreementsProvider;
9+
use Magento\Store\Model\ScopeInterface;
10+
11+
/**
12+
* Class GuestValidation
13+
*
14+
* Plugin that checks if checkout agreement enabled and validates all agreements.
15+
* Current plugin is duplicate from Magento\CheckoutAgreements\Model\Checkout\Plugin\Validation due to different
16+
* interfaces of payment information and makes check before processing of payment information.
17+
*/
18+
class GuestValidation
19+
{
20+
/**
21+
* @var \Magento\Framework\App\Config\ScopeConfigInterface
22+
*/
23+
private $scopeConfiguration;
24+
25+
/**
26+
* @var \Magento\CheckoutAgreements\Api\CheckoutAgreementsRepositoryInterface
27+
*/
28+
private $checkoutAgreementsRepository;
29+
30+
/**
31+
* @var \Magento\Checkout\Api\AgreementsValidatorInterface
32+
*/
33+
private $agreementsValidator;
34+
35+
/**
36+
* @param \Magento\Checkout\Api\AgreementsValidatorInterface $agreementsValidator
37+
* @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfiguration
38+
* @param \Magento\CheckoutAgreements\Api\CheckoutAgreementsRepositoryInterface $checkoutAgreementsRepository
39+
*/
40+
public function __construct(
41+
\Magento\Checkout\Api\AgreementsValidatorInterface $agreementsValidator,
42+
\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfiguration,
43+
\Magento\CheckoutAgreements\Api\CheckoutAgreementsRepositoryInterface $checkoutAgreementsRepository
44+
) {
45+
$this->agreementsValidator = $agreementsValidator;
46+
$this->scopeConfiguration = $scopeConfiguration;
47+
$this->checkoutAgreementsRepository = $checkoutAgreementsRepository;
48+
}
49+
50+
/**
51+
* @param \Magento\Checkout\Api\GuestPaymentInformationManagementInterface $subject
52+
* @param string $cartId
53+
* @param string $email
54+
* @param \Magento\Quote\Api\Data\PaymentInterface $paymentMethod
55+
* @param \Magento\Quote\Api\Data\AddressInterface|null $billingAddress
56+
* @return void
57+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
58+
*/
59+
public function beforeSavePaymentInformationAndPlaceOrder(
60+
\Magento\Checkout\Api\GuestPaymentInformationManagementInterface $subject,
61+
$cartId,
62+
$email,
63+
\Magento\Quote\Api\Data\PaymentInterface $paymentMethod,
64+
\Magento\Quote\Api\Data\AddressInterface $billingAddress = null
65+
) {
66+
if ($this->isAgreementEnabled()) {
67+
$this->validateAgreements($paymentMethod);
68+
}
69+
}
70+
71+
/**
72+
* @param \Magento\Checkout\Api\GuestPaymentInformationManagementInterface $subject
73+
* @param string $cartId
74+
* @param string $email
75+
* @param \Magento\Quote\Api\Data\PaymentInterface $paymentMethod
76+
* @param \Magento\Quote\Api\Data\AddressInterface|null $billingAddress
77+
* @return void
78+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
79+
*/
80+
public function beforeSavePaymentInformation(
81+
\Magento\Checkout\Api\GuestPaymentInformationManagementInterface $subject,
82+
$cartId,
83+
$email,
84+
\Magento\Quote\Api\Data\PaymentInterface $paymentMethod,
85+
\Magento\Quote\Api\Data\AddressInterface $billingAddress = null
86+
) {
87+
if ($this->isAgreementEnabled()) {
88+
$this->validateAgreements($paymentMethod);
89+
}
90+
}
91+
92+
/**
93+
* @param \Magento\Quote\Api\Data\PaymentInterface $paymentMethod
94+
* @throws \Magento\Framework\Exception\CouldNotSaveException
95+
* @return void
96+
*/
97+
private function validateAgreements(\Magento\Quote\Api\Data\PaymentInterface $paymentMethod)
98+
{
99+
$agreements = $paymentMethod->getExtensionAttributes() === null
100+
? []
101+
: $paymentMethod->getExtensionAttributes()->getAgreementIds();
102+
103+
if (!$this->agreementsValidator->isValid($agreements)) {
104+
throw new \Magento\Framework\Exception\CouldNotSaveException(
105+
__('Please agree to all the terms and conditions before placing the order.')
106+
);
107+
}
108+
}
109+
110+
/**
111+
* Verify if agreement validation needed
112+
* @return bool
113+
*/
114+
private function isAgreementEnabled()
115+
{
116+
$isAgreementsEnabled = $this->scopeConfiguration->isSetFlag(
117+
AgreementsProvider::PATH_ENABLED,
118+
ScopeInterface::SCOPE_STORE
119+
);
120+
$agreementsList = $isAgreementsEnabled ? $this->checkoutAgreementsRepository->getList() : [];
121+
return (bool)($isAgreementsEnabled && count($agreementsList) > 0);
122+
}
123+
}
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\CheckoutAgreements\Test\Unit\Model\Checkout\Plugin;
7+
8+
use Magento\CheckoutAgreements\Model\AgreementsProvider;
9+
use Magento\Store\Model\ScopeInterface;
10+
11+
class GuestValidationTest extends \PHPUnit_Framework_TestCase
12+
{
13+
/**
14+
* @var \Magento\CheckoutAgreements\Model\Checkout\Plugin\GuestValidation
15+
*/
16+
private $model;
17+
18+
/**
19+
* @var \PHPUnit_Framework_MockObject_MockObject
20+
*/
21+
private $agreementsValidatorMock;
22+
23+
/**
24+
* @var \PHPUnit_Framework_MockObject_MockObject
25+
*/
26+
private $subjectMock;
27+
28+
/**
29+
* @var \PHPUnit_Framework_MockObject_MockObject
30+
*/
31+
private $paymentMock;
32+
33+
/**
34+
* @var \PHPUnit_Framework_MockObject_MockObject
35+
*/
36+
private $addressMock;
37+
38+
/**
39+
* @var \PHPUnit_Framework_MockObject_MockObject
40+
*/
41+
private $extensionAttributesMock;
42+
43+
/**
44+
* @var \PHPUnit_Framework_MockObject_MockObject
45+
*/
46+
private $repositoryMock;
47+
48+
/**
49+
* @var \PHPUnit_Framework_MockObject_MockObject
50+
*/
51+
private $scopeConfigMock;
52+
53+
protected function setUp()
54+
{
55+
$this->agreementsValidatorMock = $this->getMock(\Magento\Checkout\Api\AgreementsValidatorInterface::class);
56+
$this->subjectMock = $this->getMock(\Magento\Checkout\Api\GuestPaymentInformationManagementInterface::class);
57+
$this->paymentMock = $this->getMock(\Magento\Quote\Api\Data\PaymentInterface::class);
58+
$this->addressMock = $this->getMock(\Magento\Quote\Api\Data\AddressInterface::class);
59+
$this->extensionAttributesMock = $this->getMock(
60+
\Magento\Quote\Api\Data\PaymentExtension::class,
61+
['getAgreementIds'],
62+
[],
63+
'',
64+
false
65+
);
66+
$this->scopeConfigMock = $this->getMock(\Magento\Framework\App\Config\ScopeConfigInterface::class);
67+
$this->repositoryMock = $this->getMock(
68+
\Magento\CheckoutAgreements\Api\CheckoutAgreementsRepositoryInterface::class
69+
);
70+
71+
$this->model = new \Magento\CheckoutAgreements\Model\Checkout\Plugin\GuestValidation(
72+
$this->agreementsValidatorMock,
73+
$this->scopeConfigMock,
74+
$this->repositoryMock
75+
);
76+
}
77+
78+
public function testBeforeSavePaymentInformationAndPlaceOrder()
79+
{
80+
$cartId = '100';
81+
$email = 'email@example.com';
82+
$agreements = [1, 2, 3];
83+
$this->scopeConfigMock
84+
->expects($this->once())
85+
->method('isSetFlag')
86+
->with(AgreementsProvider::PATH_ENABLED, ScopeInterface::SCOPE_STORE)
87+
->willReturn(true);
88+
$this->repositoryMock->expects($this->once())->method('getList')->willReturn([1]);
89+
$this->extensionAttributesMock->expects($this->once())->method('getAgreementIds')->willReturn($agreements);
90+
$this->agreementsValidatorMock->expects($this->once())->method('isValid')->with($agreements)->willReturn(true);
91+
$this->paymentMock->expects(static::atLeastOnce())
92+
->method('getExtensionAttributes')
93+
->willReturn($this->extensionAttributesMock);
94+
$this->model->beforeSavePaymentInformation(
95+
$this->subjectMock,
96+
$cartId,
97+
$email,
98+
$this->paymentMock,
99+
$this->addressMock
100+
);
101+
}
102+
103+
/**
104+
* @expectedException \Magento\Framework\Exception\CouldNotSaveException
105+
* @expectedExceptionMessage Please agree to all the terms and conditions before placing the order.
106+
*/
107+
public function testBeforeSavePaymentInformationAndPlaceOrderIfAgreementsNotValid()
108+
{
109+
$cartId = 100;
110+
$email = 'email@example.com';
111+
$agreements = [1, 2, 3];
112+
$this->scopeConfigMock
113+
->expects($this->once())
114+
->method('isSetFlag')
115+
->with(AgreementsProvider::PATH_ENABLED, ScopeInterface::SCOPE_STORE)
116+
->willReturn(true);
117+
$this->repositoryMock->expects($this->once())->method('getList')->willReturn([1]);
118+
$this->extensionAttributesMock->expects($this->once())->method('getAgreementIds')->willReturn($agreements);
119+
$this->agreementsValidatorMock->expects($this->once())->method('isValid')->with($agreements)->willReturn(false);
120+
$this->paymentMock->expects(static::atLeastOnce())
121+
->method('getExtensionAttributes')
122+
->willReturn($this->extensionAttributesMock);
123+
$this->model->beforeSavePaymentInformation(
124+
$this->subjectMock,
125+
$cartId,
126+
$email,
127+
$this->paymentMock,
128+
$this->addressMock
129+
);
130+
}
131+
132+
public function testBeforeSavePaymentInformation()
133+
{
134+
$cartId = 100;
135+
$email = 'email@example.com';
136+
$agreements = [1, 2, 3];
137+
$this->scopeConfigMock
138+
->expects($this->once())
139+
->method('isSetFlag')
140+
->with(AgreementsProvider::PATH_ENABLED, ScopeInterface::SCOPE_STORE)
141+
->willReturn(true);
142+
$this->repositoryMock->expects($this->once())->method('getList')->willReturn([1]);
143+
$this->extensionAttributesMock->expects($this->once())->method('getAgreementIds')->willReturn($agreements);
144+
$this->agreementsValidatorMock->expects($this->once())->method('isValid')->with($agreements)->willReturn(true);
145+
$this->paymentMock->expects(static::atLeastOnce())
146+
->method('getExtensionAttributes')
147+
->willReturn($this->extensionAttributesMock);
148+
$this->model->beforeSavePaymentInformation(
149+
$this->subjectMock,
150+
$cartId,
151+
$email,
152+
$this->paymentMock,
153+
$this->addressMock
154+
);
155+
}
156+
}

app/code/Magento/CheckoutAgreements/etc/di.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,7 @@
1919
<plugin name="validate-agreements" type="Magento\CheckoutAgreements\Model\Checkout\Plugin\Validation"/>
2020
</type>
2121
<preference for="Magento\Checkout\Api\AgreementsValidatorInterface" type="Magento\CheckoutAgreements\Model\AgreementsValidator" />
22+
<type name="Magento\Checkout\Api\GuestPaymentInformationManagementInterface">
23+
<plugin name="validate-guest-agreements" type="Magento\CheckoutAgreements\Model\Checkout\Plugin\GuestValidation"/>
24+
</type>
2225
</config>

app/code/Magento/CheckoutAgreements/etc/module.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@
66
*/
77
-->
88
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
9-
<module name="Magento_CheckoutAgreements" setup_version="2.0.1">
9+
<module name="Magento_CheckoutAgreements" setup_version="2.2.0">
1010
<sequence>
1111
<module name="Magento_Store"/>
12+
<module name="Magento_Checkout"/>
1213
</sequence>
1314
</module>
1415
</config>

app/code/Magento/CheckoutAgreements/view/frontend/web/js/model/agreement-validator.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,21 @@ define([
2020
* @returns {Boolean}
2121
*/
2222
validate: function () {
23+
var isValid = true;
24+
2325
if (!agreementsConfig.isEnabled || $(agreementsInputPath).length === 0) {
2426
return true;
2527
}
2628

27-
return $.validator.validateSingleElement(agreementsInputPath, {
28-
errorElement: 'div'
29+
$(agreementsInputPath).each(function (index, element) {
30+
if (!$.validator.validateSingleElement(element, {
31+
errorElement: 'div'
32+
})) {
33+
isValid = false;
34+
}
2935
});
36+
37+
return isValid;
3038
}
3139
};
3240
});

0 commit comments

Comments
 (0)