Skip to content

Commit d1ba944

Browse files
authored
Merge pull request #7002 from magento-l3/PR_20210725
L3 Bugfix delivery
2 parents ea236e7 + 943cd75 commit d1ba944

File tree

58 files changed

+1437
-355
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+1437
-355
lines changed

app/code/Magento/AsynchronousOperations/Model/BulkManagement.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,13 +119,14 @@ public function scheduleBulk($bulkUuid, array $operations, $description, $userId
119119
$bulkSummary->setOperationCount((int)$bulkSummary->getOperationCount() + count($operations));
120120
$this->entityManager->save($bulkSummary);
121121

122+
$this->publishOperations($operations);
123+
122124
$connection->commit();
123125
} catch (\Exception $exception) {
124126
$connection->rollBack();
125127
$this->logger->critical($exception->getMessage());
126128
return false;
127129
}
128-
$this->publishOperations($operations);
129130

130131
return true;
131132
}

app/code/Magento/AsynchronousOperations/Model/MassSchedule.php

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -166,12 +166,17 @@ public function publishMass($topicName, array $entitiesArray, $groupId = null, $
166166
}
167167
}
168168

169-
$this->saveMultipleOperations->execute($operations);
170169
if (!$this->bulkManagement->scheduleBulk($groupId, $operations, $bulkDescription, $userId)) {
171-
throw new LocalizedException(
172-
__('Something went wrong while processing the request.')
173-
);
170+
try {
171+
$this->bulkManagement->deleteBulk($groupId);
172+
} finally {
173+
throw new LocalizedException(
174+
__('Something went wrong while processing the request.')
175+
);
176+
}
174177
}
178+
$this->saveMultipleOperations->execute($operations);
179+
175180
/** @var AsyncResponseInterface $asyncResponse */
176181
$asyncResponse = $this->asyncResponseFactory->create();
177182
$asyncResponse->setBulkUuid($groupId);

app/code/Magento/AsynchronousOperations/Test/Unit/Model/BulkManagementTest.php

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,85 @@ public function testScheduleBulkWithException()
186186
$this->assertFalse($this->bulkManagement->scheduleBulk($bulkUuid, [$operation], $description, $userId));
187187
}
188188

189+
/**
190+
* Test for scheduleBulk method with exception during publishing.
191+
*
192+
* @return void
193+
*/
194+
public function testScheduleBulkWithExceptionDuringPublishing()
195+
{
196+
$bulkUuid = 'bulk-001';
197+
$description = 'Bulk summary description...';
198+
$userId = 1;
199+
$userType = UserContextInterface::USER_TYPE_ADMIN;
200+
$connectionName = 'default';
201+
$exceptionMessage = 'Exception message';
202+
$operation = $this->createMock(OperationInterface::class);
203+
$metadata = $this->createMock(EntityMetadataInterface::class);
204+
$this->metadataPool->expects($this->once())
205+
->method('getMetadata')
206+
->with(BulkSummaryInterface::class)
207+
->willReturn($metadata);
208+
$metadata->expects($this->once())
209+
->method('getEntityConnectionName')
210+
->willReturn($connectionName);
211+
$connection = $this->createMock(AdapterInterface::class);
212+
$this->resourceConnection->expects($this->once())
213+
->method('getConnectionByName')
214+
->with($connectionName)
215+
->willReturn($connection);
216+
$connection->expects($this->once())
217+
->method('beginTransaction')
218+
->willReturnSelf();
219+
$bulkSummary = $this->createMock(BulkSummaryInterface::class);
220+
$this->bulkSummaryFactory->expects($this->once())
221+
->method('create')
222+
->willReturn($bulkSummary);
223+
$this->entityManager->expects($this->once())
224+
->method('load')
225+
->with($bulkSummary, $bulkUuid)
226+
->willReturn($bulkSummary);
227+
$bulkSummary->expects($this->once())
228+
->method('setBulkId')
229+
->with($bulkUuid)
230+
->willReturnSelf();
231+
$bulkSummary->expects($this->once())
232+
->method('setDescription')
233+
->with($description)
234+
->willReturnSelf();
235+
$bulkSummary->expects($this->once())
236+
->method('setUserId')
237+
->with($userId)
238+
->willReturnSelf();
239+
$bulkSummary->expects($this->once())
240+
->method('setUserType')
241+
->with($userType)
242+
->willReturnSelf();
243+
$bulkSummary->expects($this->once())
244+
->method('getOperationCount')
245+
->willReturn(1);
246+
$bulkSummary->expects($this->once())
247+
->method('setOperationCount')
248+
->with(2)
249+
->willReturnSelf();
250+
$this->entityManager->expects($this->once())
251+
->method('save')
252+
->with($bulkSummary)
253+
->willReturn($bulkSummary);
254+
$this->publisher->expects($this->once())
255+
->method('publish')
256+
->willThrowException(new \Exception($exceptionMessage));
257+
$connection->expects($this->never())
258+
->method('commit');
259+
$connection->expects($this->once())
260+
->method('rollBack')
261+
->willReturnSelf();
262+
$this->logger->expects($this->once())
263+
->method('critical')
264+
->with($exceptionMessage);
265+
$this->assertFalse($this->bulkManagement->scheduleBulk($bulkUuid, [$operation], $description, $userId));
266+
}
267+
189268
/**
190269
* Test for retryBulk method.
191270
*

app/code/Magento/Bundle/Test/Mftf/Section/StorefrontBundledSection.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,7 @@
4848
<element name="currency" type="select" selector="//a[text()='{{arg}}']" parameterized="true"/>
4949
<element name="multiSelectOption" type="select" selector="//div[@class='field option required']//select"/>
5050
<element name="validationMessageBox" type="block" selector="#validation-message-box"/>
51+
<element name="dropDownQuantityValidation" type="input" selector="//span[contains(text(), '{{productName}}')]/../..//input/following-sibling::div[@class='mage-error']" parameterized="true"/>
52+
<element name="radioButtonQuantityValidation" type="input" selector="//label//span[contains(text(), '{{productName}}')]/../..//div[@class='control']//div[@class='field qty qty-holder']//input/following-sibling::div[@class='mage-error']" parameterized="true"/>
5153
</section>
5254
</sections>

app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,23 +104,23 @@
104104
<selectOption selector="{{StorefrontBundledSection.dropDownOptionOneProducts('Option One')}}" userInput="$$simpleProduct1.name$$ +$$$simpleProduct1.price$$.00" stepKey="selectOption0Product0"/>
105105
<seeOptionIsSelected selector="{{StorefrontBundledSection.dropDownOptionOneProducts('Option One')}}" userInput="$$simpleProduct1.name$$ +$$$simpleProduct1.price$$.00" stepKey="checkOption0Product0"/>
106106
<fillField selector="{{StorefrontBundledSection.dropDownOptionOneQuantity('Option One')}}" userInput="3" stepKey="fillQuantity00"/>
107-
<seeInField selector="{{StorefrontBundledSection.dropDownOptionOneQuantity('Option One')}}" userInput="03" stepKey="checkQuantity00"/>
107+
<seeInField selector="{{StorefrontBundledSection.dropDownOptionOneQuantity('Option One')}}" userInput="3" stepKey="checkQuantity00"/>
108108

109109
<selectOption selector="{{StorefrontBundledSection.dropDownOptionOneProducts('Option One')}}" userInput="$$simpleProduct2.name$$ +$$$simpleProduct2.price$$.00" stepKey="selectOption0Product1"/>
110110
<seeOptionIsSelected selector="{{StorefrontBundledSection.dropDownOptionOneProducts('Option One')}}" userInput="$$simpleProduct2.name$$ +$$$simpleProduct2.price$$.00" stepKey="checkOption0Product1"/>
111111
<fillField selector="{{StorefrontBundledSection.dropDownOptionOneQuantity('Option One')}}" userInput="3" stepKey="fillQuantity01"/>
112-
<seeInField selector="{{StorefrontBundledSection.dropDownOptionOneQuantity('Option One')}}" userInput="03" stepKey="checkQuantity01"/>
112+
<seeInField selector="{{StorefrontBundledSection.dropDownOptionOneQuantity('Option One')}}" userInput="3" stepKey="checkQuantity01"/>
113113

114114
<!--"Radio Buttons" type option-->
115115
<checkOption selector="{{StorefrontBundledSection.radioButtonOptionTwoProducts('Option Two', '1')}}" stepKey="selectOption1Product0"/>
116116
<seeCheckboxIsChecked selector="{{StorefrontBundledSection.radioButtonOptionTwoProducts('Option Two', '1')}}" stepKey="checkOption1Product0"/>
117117
<fillField selector="{{StorefrontBundledSection.radioButtonOptionTwoQuantity('Option Two')}}" userInput="3" stepKey="fillQuantity10"/>
118-
<seeInField selector="{{StorefrontBundledSection.radioButtonOptionTwoQuantity('Option Two')}}" userInput="03" stepKey="checkQuantity10"/>
118+
<seeInField selector="{{StorefrontBundledSection.radioButtonOptionTwoQuantity('Option Two')}}" userInput="3" stepKey="checkQuantity10"/>
119119

120120
<checkOption selector="{{StorefrontBundledSection.radioButtonOptionTwoProducts('Option Two', '2')}}" stepKey="selectOption1Product1"/>
121121
<seeCheckboxIsChecked selector="{{StorefrontBundledSection.radioButtonOptionTwoProducts('Option Two', '2')}}" stepKey="checkOption1Product1"/>
122122
<fillField selector="{{StorefrontBundledSection.radioButtonOptionTwoQuantity('Option Two')}}" userInput="3" stepKey="fillQuantity11"/>
123-
<seeInField selector="{{StorefrontBundledSection.radioButtonOptionTwoQuantity('Option Two')}}" userInput="03" stepKey="checkQuantity11"/>
123+
<seeInField selector="{{StorefrontBundledSection.radioButtonOptionTwoQuantity('Option Two')}}" userInput="3" stepKey="checkQuantity11"/>
124124

125125
<!--"Checkbox" type option-->
126126
<!--This option does not support user defined quantities-->
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
/**
4+
* Copyright © Magento, Inc. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
-->
8+
<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
9+
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd">
10+
<test name="StorefrontValidateQuantityBundleProductsTest">
11+
<annotations>
12+
<features value="Bundle"/>
13+
<stories value="Bundle product details page"/>
14+
<title value="Validation for negative quantity on bundle products."/>
15+
<description value="Customer should not be able to add the products to the cart if the quantity is negative value"/>
16+
<severity value="MINOR"/>
17+
<testCaseId value="MC-42765"/>
18+
<group value="Bundle"/>
19+
</annotations>
20+
<before>
21+
<actionGroup ref="AdminLoginActionGroup" stepKey="login"/>
22+
<createData entity="SimpleProduct2" stepKey="createProduct1"/>
23+
<createData entity="SimpleProduct2" stepKey="createProduct2"/>
24+
<magentoCron stepKey="runCronIndex" groups="index"/>
25+
</before>
26+
<after>
27+
<!-- Delete the bundled product -->
28+
<actionGroup stepKey="deleteBundle" ref="DeleteProductUsingProductGridActionGroup">
29+
<argument name="product" value="BundleProduct"/>
30+
</actionGroup>
31+
<actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
32+
<deleteData createDataKey="createProduct1" stepKey="deleteSimpleProduct1"/>
33+
<deleteData createDataKey="createProduct2" stepKey="deleteSimpleProduct2"/>
34+
</after>
35+
36+
<!-- Start creating a bundle product -->
37+
<actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="goToProductList"/>
38+
<actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProduct">
39+
<argument name="product" value="BundleProduct"/>
40+
</actionGroup>
41+
<actionGroup ref="FillProductNameAndSkuInProductFormActionGroup" stepKey="fillNameAndSku">
42+
<argument name="product" value="BundleProduct"/>
43+
</actionGroup>
44+
45+
<!-- Add Option One, a "Drop-down" type option -->
46+
<actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addBundleOptionWithTwoProducts1">
47+
<argument name="x" value="0"/>
48+
<argument name="n" value="1"/>
49+
<argument name="prodOneSku" value="$$createProduct1.sku$$"/>
50+
<argument name="prodTwoSku" value="$$createProduct2.sku$$"/>
51+
<argument name="optionTitle" value="Option One"/>
52+
<argument name="inputType" value="select"/>
53+
</actionGroup>
54+
<checkOption selector="{{AdminProductFormBundleSection.userDefinedQuantity('0', '0')}}" stepKey="userDefinedQuantitiyOption0Product0"/>
55+
<checkOption selector="{{AdminProductFormBundleSection.userDefinedQuantity('0', '1')}}" stepKey="userDefinedQuantitiyOption0Product1"/>
56+
57+
<!-- Add Option Two, a "Radio Buttons" type option -->
58+
<actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addBundleOptionWithTwoProducts2">
59+
<argument name="x" value="1"/>
60+
<argument name="n" value="2"/>
61+
<argument name="prodOneSku" value="$$createProduct1.sku$$"/>
62+
<argument name="prodTwoSku" value="$$createProduct2.sku$$"/>
63+
<argument name="optionTitle" value="Option Two"/>
64+
<argument name="inputType" value="radio"/>
65+
</actionGroup>
66+
<checkOption selector="{{AdminProductFormBundleSection.userDefinedQuantity('1', '0')}}" stepKey="userDefinedQuantitiyOption1Product0"/>
67+
<checkOption selector="{{AdminProductFormBundleSection.userDefinedQuantity('1', '1')}}" stepKey="userDefinedQuantitiyOption1Product1"/>
68+
69+
<!-- Save product and go to storefront -->
70+
<actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/>
71+
72+
<wait stepKey="waitBeforeIndexerAfterBundle" time="60"/>
73+
<magentoCLI command="cron:run --group=index" stepKey="runCronIndexerAfterBundle"/>
74+
75+
<amOnPage url="{{BundleProduct.sku}}.html" stepKey="goToStorefront"/>
76+
<waitForPageLoad stepKey="waitForStorefront"/>
77+
<click selector="{{StorefrontBundledSection.addToCart}}" stepKey="clickCustomize"/>
78+
79+
<!--"Drop-down" type option-->
80+
<selectOption selector="{{StorefrontBundledSection.dropDownOptionOneProducts('Option One')}}" userInput="$$createProduct1.name$$ +$$$createProduct1.price$$.00" stepKey="selectOption0Product0"/>
81+
<seeOptionIsSelected selector="{{StorefrontBundledSection.dropDownOptionOneProducts('Option One')}}" userInput="$$createProduct1.name$$ +$$$createProduct1.price$$.00" stepKey="checkOption0Product0"/>
82+
<fillField selector="{{StorefrontBundledSection.dropDownOptionOneQuantity('Option One')}}" userInput="-1" stepKey="fillQuantity00"/>
83+
<seeInField selector="{{StorefrontBundledSection.dropDownOptionOneQuantity('Option One')}}" userInput="-1" stepKey="checkQuantity00"/>
84+
<click selector="{{StorefrontBundledSection.addToCartConfigured}}" stepKey="clickAddToCartDropDown"/>
85+
<see selector="{{StorefrontBundledSection.dropDownQuantityValidation('Option One')}}" userInput="Please enter a quantity greater than 0." stepKey="seeQuantityNegativeErrorMessageDropDown"/>
86+
87+
<!--"Radio Buttons" type option-->
88+
<checkOption selector="{{StorefrontBundledSection.radioButtonOptionTwoProducts('Option Two', '1')}}" stepKey="selectOption1Product0"/>
89+
<seeCheckboxIsChecked selector="{{StorefrontBundledSection.radioButtonOptionTwoProducts('Option Two', '1')}}" stepKey="checkOption1Product0"/>
90+
<fillField selector="{{StorefrontBundledSection.radioButtonOptionTwoQuantity('Option Two')}}" userInput="-1" stepKey="fillQuantity10"/>
91+
<seeInField selector="{{StorefrontBundledSection.radioButtonOptionTwoQuantity('Option Two')}}" userInput="-1" stepKey="checkQuantity10"/>
92+
<click selector="{{StorefrontBundledSection.addToCartConfigured}}" stepKey="clickAddToCartRadioButton"/>
93+
<see selector="{{StorefrontBundledSection.radioButtonQuantityValidation('Option Two')}}" userInput="Please enter a quantity greater than 0." stepKey="seeQuantityNegativeErrorMessageRadioButton"/>
94+
</test>
95+
</tests>
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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\Bundle\ViewModel;
9+
10+
use Magento\Framework\Serialize\Serializer\Json;
11+
use Magento\Framework\View\Element\Block\ArgumentInterface;
12+
use Magento\Catalog\Block\Product\View as ProductView;
13+
14+
/**
15+
* ViewModel for Bundle Option Block
16+
*/
17+
class ValidateQuantity implements ArgumentInterface
18+
{
19+
/**
20+
* @var Json
21+
*/
22+
private $serializer;
23+
24+
/**
25+
* @var ProductView
26+
*/
27+
private $productView;
28+
29+
/**
30+
* @param Json $serializer
31+
* @param ProductView $productView
32+
*/
33+
public function __construct(
34+
Json $serializer,
35+
ProductView $productView
36+
) {
37+
$this->serializer = $serializer;
38+
$this->productView = $productView;
39+
}
40+
41+
public function getQuantityValidators(): string
42+
{
43+
return $this->serializer->serialize(
44+
$this->productView->getQuantityValidators()
45+
);
46+
}
47+
}

app/code/Magento/Bundle/view/base/web/js/price-bundle.js

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ define([
2222
priceBoxSelector: '.price-box',
2323
optionHandlers: {},
2424
optionTemplate: '<%- data.label %>' +
25-
'<% if (data.finalPrice.value) { %>' +
26-
' +<%- data.finalPrice.formatted %>' +
27-
'<% } %>',
25+
'<% if (data.finalPrice.value) { %>' +
26+
' +<%- data.finalPrice.formatted %>' +
27+
'<% } %>',
2828
controlContainer: 'dd', // should be eliminated
2929
priceFormat: {},
3030
isFixedPrice: false,
@@ -107,12 +107,15 @@ define([
107107
changes = defaultGetOptionValue(bundleOption, this.options.optionConfig);//eslint-disable-line
108108
}
109109

110-
if (changes) {
111-
priceBox.trigger('updatePrice', changes);
112-
}
110+
// eslint-disable-next-line no-use-before-define
111+
if (isValidQty(bundleOption)) {
112+
if (changes) {
113+
priceBox.trigger('updatePrice', changes);
114+
}
113115

114-
this._displayTierPriceBlock(bundleOption);
115-
this.updateProductSummary();
116+
this._displayTierPriceBlock(bundleOption);
117+
this.updateProductSummary();
118+
}
116119
},
117120

118121
/**
@@ -132,7 +135,10 @@ define([
132135
.selections[field.data('optionValueId')];
133136
optionConfig.qty = field.val();
134137

135-
optionInstance.trigger('change');
138+
// eslint-disable-next-line no-use-before-define
139+
if (isValidQty(optionInstance)) {
140+
optionInstance.trigger('change');
141+
}
136142
}
137143
},
138144

@@ -373,6 +379,29 @@ define([
373379
return changes;
374380
}
375381

382+
/**
383+
* Check the quantity field if negative value occurs.
384+
*
385+
* @param {Object} bundleOption
386+
*/
387+
function isValidQty(bundleOption) {
388+
var isValid = true,
389+
qtyElem = bundleOption.data('qtyField'),
390+
bundleOptionType = bundleOption.prop('type'),
391+
qtyValidator = qtyElem.data('validate') &&
392+
typeof qtyElem.data('validate')['validate-item-quantity'] === 'object' ?
393+
qtyElem.data('validate')['validate-item-quantity'] : null;
394+
395+
if (['radio', 'select-one'].includes(bundleOptionType) &&
396+
qtyValidator &&
397+
(qtyElem.val() < qtyValidator.minAllowed || qtyElem.val() > qtyValidator.maxAllowed)
398+
) {
399+
isValid = false;
400+
}
401+
402+
return isValid;
403+
}
404+
376405
/**
377406
* Helper to toggle qty field
378407
* @param {jQuery} element

0 commit comments

Comments
 (0)