Skip to content

Commit fca9f6f

Browse files
authored
Merge branch '2.4-develop' into click-edit-link-for-customer-edit
2 parents 152c29b + 18da36b commit fca9f6f

File tree

574 files changed

+21286
-3437
lines changed

Some content is hidden

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

574 files changed

+21286
-3437
lines changed

app/autoload.php

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,36 +5,48 @@
55
* Copyright © Magento, Inc. All rights reserved.
66
* See COPYING.txt for license details.
77
*/
8+
declare(strict_types=1);
9+
810
use Magento\Framework\Autoload\AutoloaderRegistry;
911
use Magento\Framework\Autoload\ClassLoaderWrapper;
1012

1113
/**
1214
* Shortcut constant for the root directory
1315
*/
14-
define('BP', dirname(__DIR__));
16+
\define('BP', \dirname(__DIR__));
1517

16-
define('VENDOR_PATH', BP . '/app/etc/vendor_path.php');
18+
\define('VENDOR_PATH', BP . '/app/etc/vendor_path.php');
1719

18-
if (!file_exists(VENDOR_PATH)) {
20+
if (!\is_readable(VENDOR_PATH)) {
1921
throw new \Exception(
2022
'We can\'t read some files that are required to run the Magento application. '
2123
. 'This usually means file permissions are set incorrectly.'
2224
);
2325
}
2426

25-
$vendorDir = require VENDOR_PATH;
26-
$vendorAutoload = BP . "/{$vendorDir}/autoload.php";
27+
$vendorAutoload = (
28+
static function (): ?string {
29+
$vendorDir = require VENDOR_PATH;
30+
31+
$vendorAutoload = BP . "/{$vendorDir}/autoload.php";
32+
if (\is_readable($vendorAutoload)) {
33+
return $vendorAutoload;
34+
}
35+
36+
$vendorAutoload = "{$vendorDir}/autoload.php";
37+
if (\is_readable($vendorAutoload)) {
38+
return $vendorAutoload;
39+
}
40+
41+
return null;
42+
}
43+
)();
2744

28-
/* 'composer install' validation */
29-
if (file_exists($vendorAutoload)) {
30-
$composerAutoloader = include $vendorAutoload;
31-
} else if (file_exists("{$vendorDir}/autoload.php")) {
32-
$vendorAutoload = "{$vendorDir}/autoload.php";
33-
$composerAutoloader = include $vendorAutoload;
34-
} else {
45+
if ($vendorAutoload === null) {
3546
throw new \Exception(
3647
'Vendor autoload is not found. Please run \'composer install\' under application root directory.'
3748
);
3849
}
3950

51+
$composerAutoloader = include $vendorAutoload;
4052
AutoloaderRegistry::registerAutoloader(new ClassLoaderWrapper($composerAutoloader));

app/code/Magento/Backend/Block/Widget/Grid/Column/Filter/Price.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ protected function _getCurrencyList()
162162
/**
163163
* Retrieve filter value
164164
*
165-
* @param null $index
165+
* @param string|null $index
166166
* @return array|null
167167
*/
168168
public function getValue($index = null)
@@ -194,11 +194,11 @@ public function getCondition()
194194
$rate = $this->_getRate($displayCurrency, $this->_getColumnCurrencyCode());
195195

196196
if (isset($value['from'])) {
197-
$value['from'] *= $rate;
197+
$value['from'] = (float) $value['from'] * $rate;
198198
}
199199

200200
if (isset($value['to'])) {
201-
$value['to'] *= $rate;
201+
$value['to'] = (float) $value['to'] * $rate;
202202
}
203203

204204
$this->prepareRates($displayCurrency);

app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardWithChartsTest.xml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,7 @@
6464
<waitForLoadingMaskToDisappear stepKey="waitForLoadingCheckoutPageWithShippingMethod"/>
6565
<click selector="{{CheckoutShippingMethodsSection.firstShippingMethod}}" stepKey="selectFirstShippingMethod"/>
6666
<waitForLoadingMaskToDisappear stepKey="waitForLoadingMask1"/>
67-
<waitForElement selector="{{CheckoutShippingMethodsSection.next}}" time="30" stepKey="waitForNextButton"/>
68-
<click selector="{{CheckoutShippingMethodsSection.next}}" stepKey="clickNext"/>
67+
<actionGroup ref="StorefrontCheckoutClickNextButtonActionGroup" stepKey="clickNext"/>
6968
<!-- Checkout select Check/Money Order payment -->
7069
<comment userInput="Select Check/Money payment" stepKey="checkoutSelectCheckMoneyPayment"/>
7170
<actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/>
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
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\Backend\Test\Unit\Block\Widget\Grid\Column\Filter;
9+
10+
use Magento\Backend\Block\Context;
11+
use Magento\Backend\Block\Widget\Grid\Column;
12+
use Magento\Backend\Block\Widget\Grid\Column\Filter\Price;
13+
use Magento\Framework\App\RequestInterface;
14+
use Magento\Framework\DB\Helper;
15+
use Magento\Directory\Model\Currency;
16+
use Magento\Directory\Model\Currency\DefaultLocator;
17+
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
18+
use PHPUnit\Framework\MockObject\MockObject;
19+
use PHPUnit\Framework\TestCase;
20+
21+
class PriceTest extends TestCase
22+
{
23+
/** @var RequestInterface|MockObject */
24+
private $requestMock;
25+
26+
/** @var Context|MockObject */
27+
private $context;
28+
29+
/** @var Helper|MockObject */
30+
private $helper;
31+
32+
/** @var Currency|MockObject */
33+
private $currency;
34+
35+
/** @var DefaultLocator|MockObject */
36+
private $currencyLocator;
37+
38+
/** @var Column|MockObject */
39+
private $columnMock;
40+
41+
/** @var Price */
42+
private $blockPrice;
43+
44+
protected function setUp(): void
45+
{
46+
$this->requestMock = $this->getMockForAbstractClass(RequestInterface::class);
47+
48+
$this->context = $this->createMock(Context::class);
49+
$this->context->expects($this->any())->method('getRequest')->willReturn($this->requestMock);
50+
51+
$this->helper = $this->createMock(Helper::class);
52+
53+
$this->currency = $this->getMockBuilder(Currency::class)
54+
->disableOriginalConstructor()
55+
->setMethods(['getAnyRate'])
56+
->getMock();
57+
58+
$this->currencyLocator = $this->createMock(DefaultLocator::class);
59+
60+
$this->columnMock = $this->getMockBuilder(Column::class)
61+
->disableOriginalConstructor()
62+
->setMethods(['getCurrencyCode'])
63+
->getMock();
64+
65+
$helper = new ObjectManager($this);
66+
67+
$this->blockPrice = $helper->getObject(Price::class, [
68+
'context' => $this->context,
69+
'resourceHelper' => $this->helper,
70+
'currencyModel' => $this->currency,
71+
'currencyLocator' => $this->currencyLocator
72+
]);
73+
$this->blockPrice->setColumn($this->columnMock);
74+
}
75+
76+
public function testGetCondition()
77+
{
78+
$this->currencyLocator->expects(
79+
$this->any()
80+
)->method(
81+
'getDefaultCurrency'
82+
)->with(
83+
$this->requestMock
84+
)->willReturn(
85+
'defaultCurrency'
86+
);
87+
88+
$this->currency->expects($this->at(0))
89+
->method('getAnyRate')
90+
->with('defaultCurrency')
91+
->willReturn(1.0);
92+
93+
$testValue = [
94+
'value' => [
95+
'from' => '1234a',
96+
]
97+
];
98+
99+
$this->blockPrice->addData($testValue);
100+
$this->assertEquals(['from' => 1234], $this->blockPrice->getCondition());
101+
}
102+
}

app/code/Magento/Bundle/Model/Product/Type.php

Lines changed: 76 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
namespace Magento\Bundle\Model\Product;
88

9+
use Magento\Bundle\Model\Option;
10+
use Magento\Bundle\Model\ResourceModel\Option\Collection;
911
use Magento\Bundle\Model\ResourceModel\Selection\Collection as Selections;
1012
use Magento\Bundle\Model\ResourceModel\Selection\Collection\FilterApplier as SelectionCollectionFilterApplier;
1113
use Magento\Catalog\Api\ProductRepositoryInterface;
@@ -414,16 +416,13 @@ public function beforeSave($product)
414416
if ($product->getCanSaveBundleSelections()) {
415417
$product->canAffectOptions(true);
416418
$selections = $product->getBundleSelectionsData();
417-
if ($selections && !empty($selections)) {
418-
$options = $product->getBundleOptionsData();
419-
if ($options) {
420-
foreach ($options as $option) {
421-
if (empty($option['delete']) || 1 != (int)$option['delete']) {
422-
$product->setTypeHasOptions(true);
423-
if (1 == (int)$option['required']) {
424-
$product->setTypeHasRequiredOptions(true);
425-
break;
426-
}
419+
if (!empty($selections) && $options = $product->getBundleOptionsData()) {
420+
foreach ($options as $option) {
421+
if (empty($option['delete']) || 1 != (int)$option['delete']) {
422+
$product->setTypeHasOptions(true);
423+
if (1 == (int)$option['required']) {
424+
$product->setTypeHasRequiredOptions(true);
425+
break;
427426
}
428427
}
429428
}
@@ -464,7 +463,7 @@ public function getOptionsIds($product)
464463
public function getOptionsCollection($product)
465464
{
466465
if (!$product->hasData($this->_keyOptionsCollection)) {
467-
/** @var \Magento\Bundle\Model\ResourceModel\Option\Collection $optionsCollection */
466+
/** @var Collection $optionsCollection */
468467
$optionsCollection = $this->_bundleOption->create()
469468
->getResourceCollection();
470469
$optionsCollection->setProductIdFilter($product->getEntityId());
@@ -530,10 +529,10 @@ public function getSelectionsCollection($optionIds, $product)
530529
* Example: the catalog inventory validation of decimal qty can change qty to int,
531530
* so need to change quote item qty option value too.
532531
*
533-
* @param array $options
534-
* @param \Magento\Framework\DataObject $option
535-
* @param mixed $value
536-
* @param \Magento\Catalog\Model\Product $product
532+
* @param array $options
533+
* @param \Magento\Framework\DataObject $option
534+
* @param mixed $value
535+
* @param \Magento\Catalog\Model\Product $product
537536
* @return $this
538537
*/
539538
public function updateQtyOption($options, \Magento\Framework\DataObject $option, $value, $product)
@@ -682,6 +681,11 @@ protected function _prepareProduct(\Magento\Framework\DataObject $buyRequest, $p
682681
$options
683682
);
684683

684+
$this->validateRadioAndSelectOptions(
685+
$optionsCollection,
686+
$options
687+
);
688+
685689
$selectionIds = array_values($this->arrayUtility->flatten($options));
686690
// If product has not been configured yet then $selections array should be empty
687691
if (!empty($selectionIds)) {
@@ -1184,9 +1188,11 @@ public function canConfigure($product)
11841188
* @return void
11851189
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
11861190
*/
1191+
// @codingStandardsIgnoreStart
11871192
public function deleteTypeSpecificData(\Magento\Catalog\Model\Product $product)
11881193
{
11891194
}
1195+
// @codingStandardsIgnoreEnd
11901196

11911197
/**
11921198
* Return array of specific to type product entities
@@ -1196,18 +1202,19 @@ public function deleteTypeSpecificData(\Magento\Catalog\Model\Product $product)
11961202
*/
11971203
public function getIdentities(\Magento\Catalog\Model\Product $product)
11981204
{
1199-
$identities = parent::getIdentities($product);
1205+
$identities = [];
1206+
$identities[] = parent::getIdentities($product);
12001207
/** @var \Magento\Bundle\Model\Option $option */
12011208
foreach ($this->getOptions($product) as $option) {
12021209
if ($option->getSelections()) {
12031210
/** @var \Magento\Catalog\Model\Product $selection */
12041211
foreach ($option->getSelections() as $selection) {
1205-
$identities = array_merge($identities, $selection->getIdentities());
1212+
$identities[] = $selection->getIdentities();
12061213
}
12071214
}
12081215
}
12091216

1210-
return $identities;
1217+
return array_merge([], ...$identities);
12111218
}
12121219

12131220
/**
@@ -1272,6 +1279,53 @@ protected function checkIsAllRequiredOptions($product, $isStrictProcessMode, $op
12721279
}
12731280
}
12741281

1282+
/**
1283+
* Validate Options for Radio and Select input types
1284+
*
1285+
* @param Collection $optionsCollection
1286+
* @param int[] $options
1287+
* @return void
1288+
* @throws \Magento\Framework\Exception\LocalizedException
1289+
*/
1290+
private function validateRadioAndSelectOptions($optionsCollection, $options): void
1291+
{
1292+
$errorTypes = [];
1293+
1294+
if (is_array($optionsCollection->getItems())) {
1295+
foreach ($optionsCollection->getItems() as $option) {
1296+
if ($this->isSelectedOptionValid($option, $options)) {
1297+
$errorTypes[] = $option->getType();
1298+
}
1299+
}
1300+
}
1301+
1302+
if (!empty($errorTypes)) {
1303+
throw new \Magento\Framework\Exception\LocalizedException(
1304+
__(
1305+
'Option type (%types) should have only one element.',
1306+
['types' => implode(", ", $errorTypes)]
1307+
)
1308+
);
1309+
}
1310+
}
1311+
1312+
/**
1313+
* Check if selected option is valid
1314+
*
1315+
* @param Option $option
1316+
* @param array $options
1317+
* @return bool
1318+
*/
1319+
private function isSelectedOptionValid($option, $options): bool
1320+
{
1321+
return (
1322+
($option->getType() == 'radio' || $option->getType() == 'select') &&
1323+
isset($options[$option->getOptionId()]) &&
1324+
is_array($options[$option->getOptionId()]) &&
1325+
count($options[$option->getOptionId()]) > 1
1326+
);
1327+
}
1328+
12751329
/**
12761330
* Check if selection is salable
12771331
*
@@ -1333,16 +1387,18 @@ protected function checkIsResult($_result)
13331387
*/
13341388
protected function mergeSelectionsWithOptions($options, $selections)
13351389
{
1390+
$selections = [];
1391+
13361392
foreach ($options as $option) {
13371393
$optionSelections = $option->getSelections();
13381394
if ($option->getRequired() && is_array($optionSelections) && count($optionSelections) == 1) {
1339-
$selections = array_merge($selections, $optionSelections);
1395+
$selections[] = $optionSelections;
13401396
} else {
13411397
$selections = [];
13421398
break;
13431399
}
13441400
}
13451401

1346-
return $selections;
1402+
return array_merge([], ...$selections);
13471403
}
13481404
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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+
9+
<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
10+
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd">
11+
<actionGroup name="AdminClickAddProductToOptionActionGroup">
12+
<annotations>
13+
<description>Click AddProductToOption button for bundle product.</description>
14+
</annotations>
15+
16+
<waitForElementVisible selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="waitForAddProductsToBundle"/>
17+
<click selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="clickAddProductsToOption"/>
18+
<waitForPageLoad stepKey="waitForPageLoadAfterBundleProducts"/>
19+
</actionGroup>
20+
</actionGroups>

0 commit comments

Comments
 (0)