From d30c770c58438c9e9d6b62a05ee6b58b44812625 Mon Sep 17 00:00:00 2001 From: pradeep1819 <pradeep05.pro@gmail.com> Date: Tue, 7 Nov 2023 12:12:56 +0530 Subject: [PATCH 01/15] ACP2E-2261: [Cloud] Product import fails with custom separator value --- .../Model/Import/Product.php | 5 +- .../Import/Product/Type/AbstractType.php | 14 ++++- .../Model/Import/Product/Validator.php | 26 +++++++-- .../Model/Import/Product/ValidatorTest.php | 43 +++++++++++---- .../Test/Unit/Model/Import/ProductTest.php | 53 +++++++++++++++++++ .../ProductTest/ProductValidationTest.php | 2 +- ...th_custom_multiselect_values_separator.csv | 2 +- 7 files changed, 126 insertions(+), 19 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index 4c0e54b5c5a05..718bc654fc971 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -53,7 +53,6 @@ class Product extends AbstractEntity { private const COL_NAME_FORMAT = '/[\x00-\x1F\x7F]/'; - private const DEFAULT_GLOBAL_MULTIPLE_VALUE_SEPARATOR = ','; public const CONFIG_KEY_PRODUCT_TYPES = 'global/importexport/import_product_types'; /** @@ -2949,10 +2948,10 @@ private function parseAttributesWithWrappedValues($attributesData) * @return array * @since 100.1.2 */ - public function parseMultiselectValues($values, $delimiter = self::PSEUDO_MULTI_LINE_SEPARATOR) + public function parseMultiselectValues($values, $delimiter = '') { if (empty($this->_parameters[Import::FIELDS_ENCLOSURE])) { - if ($this->getMultipleValueSeparator() !== self::DEFAULT_GLOBAL_MULTIPLE_VALUE_SEPARATOR) { + if (!$delimiter) { $delimiter = $this->getMultipleValueSeparator(); } diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php index 862cd89e3bda9..bde681768b846 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php @@ -633,7 +633,19 @@ public function prepareAttributesWithDefaultValueForSave(array $rowData, $withDe $resultAttrs[$attrCode] = $attrParams['options'][strtolower($rowData[$attrCode])]; } elseif ('multiselect' == $attrParams['type']) { $resultAttrs[$attrCode] = []; - foreach ($this->_entityModel->parseMultiselectValues($rowData[$attrCode]) as $value) { + $delimiter = ''; + if (is_string($rowData[$attrCode]) + && str_contains($rowData[$attrCode], Product::PSEUDO_MULTI_LINE_SEPARATOR)) { + if (!empty($rowData['additional_attributes']) + && str_contains( + $rowData['additional_attributes'], + $attrCode . Product::PAIR_NAME_VALUE_SEPARATOR + ) + ) { + $delimiter = Product::PSEUDO_MULTI_LINE_SEPARATOR; + } + } + foreach ($this->_entityModel->parseMultiselectValues($rowData[$attrCode], $delimiter) as $value) { $resultAttrs[$attrCode][] = $attrParams['options'][strtolower($value)]; } $resultAttrs[$attrCode] = implode(',', $resultAttrs[$attrCode]); diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php index f74886069d501..4116cd894b1d7 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php @@ -8,6 +8,7 @@ use Magento\CatalogImportExport\Model\Import\Product; use Magento\Framework\Validator\AbstractValidator; use Magento\Catalog\Model\Product\Attribute\Backend\Sku; +use Magento\ImportExport\Model\Import; /** * Product import model validator @@ -254,11 +255,23 @@ public function isAttributeValid($attrCode, array $attrParams, array $rowData) */ private function validateByAttributeType(string $attrCode, array $attrParams, array $rowData): bool { + $delimiter = ''; + if (is_string($rowData[$attrCode]) && str_contains($rowData[$attrCode], Product::PSEUDO_MULTI_LINE_SEPARATOR)) { + if (!empty($rowData['additional_attributes']) + && str_contains($rowData['additional_attributes'], $attrCode . Product::PAIR_NAME_VALUE_SEPARATOR)) { + $delimiter = Product::PSEUDO_MULTI_LINE_SEPARATOR; + } + } return match ($attrParams['type']) { 'varchar', 'text' => $this->textValidation($attrCode, $attrParams['type']), 'decimal', 'int' => $this->numericValidation($attrCode, $attrParams['type']), 'select', 'boolean' => $this->validateOption($attrCode, $attrParams['options'], $rowData[$attrCode]), - 'multiselect' => $this->validateMultiselect($attrCode, $attrParams['options'], $rowData[$attrCode]), + 'multiselect' => $this->validateMultiselect( + $attrCode, + $attrParams['options'], + $rowData[$attrCode], + $delimiter + ), 'datetime' => $this->validateDateTime($rowData[$attrCode]), default => true, }; @@ -270,13 +283,18 @@ private function validateByAttributeType(string $attrCode, array $attrParams, ar * @param string $attrCode * @param array $options * @param array|string $rowData + * @param string $delimiter * @return bool */ - private function validateMultiselect(string $attrCode, array $options, array|string $rowData): bool - { + private function validateMultiselect( + string $attrCode, + array $options, + array|string $rowData, + string $delimiter = '' + ): bool { $valid = true; - $values = $this->context->parseMultiselectValues($rowData); + $values = $this->context->parseMultiselectValues($rowData, $delimiter); foreach ($values as $value) { $valid = $this->validateOption($attrCode, $options, $value); if (!$valid) { diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/ValidatorTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/ValidatorTest.php index d708850fe0d2f..0a46bf26d440c 100644 --- a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/ValidatorTest.php +++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/ValidatorTest.php @@ -169,33 +169,58 @@ public function attributeValidationProvider() [ Import::BEHAVIOR_APPEND, ['is_required' => true, 'type' => 'multiselect', 'options' => ['option 1' => 0, 'option 2' => 1]], - ['product_type' => 'any', 'attribute_code' => 'Option 1|Option 2|Option 3'], + [ + 'product_type' => 'any', + 'attribute_code' => 'Option 1|Option 2|Option 3', + 'additional_attributes' => 'test_attribute=any,attribute_code=Option 1|Option 2|Option 3' + ], false ], [ Import::BEHAVIOR_APPEND, ['is_required' => true, 'type' => 'multiselect', 'options' => ['option 1' => 0, 'option 2' => 1]], - ['product_type' => 'any', 'attribute_code' => 'Option 1|Option 2'], + [ + 'product_type' => 'any', + 'attribute_code' => 'Option 1|Option 2', + 'additional_attributes' => 'test_attribute=any,attribute_code=Option 1|Option 2' + ], true ], [ Import::BEHAVIOR_APPEND, - ['is_required' => true, 'type' => 'multiselect', - 'options' => ['option 1' => 0, 'option 2' => 1, 'option 3']], - ['product_type' => 'any', 'attribute_code' => 'Option 1|Option 2|Option 1'], + [ + 'is_required' => true, + 'type' => 'multiselect', + 'options' => ['option 1' => 0, 'option 2' => 1, 'option 3'] + ], + [ + 'product_type' => 'any', + 'attribute_code' => 'Option 1|Option 2|Option 1', + 'additional_attributes' => 'test_attribute=any,attribute_code=Option 1|Option 2|Option 1' + ], false ], [ Import::BEHAVIOR_APPEND, - ['is_required' => true, 'type' => 'multiselect', - 'options' => ['option 1' => 0, 'option 2' => 1, 'option 3']], - ['product_type' => 'any', 'attribute_code' => 'Option 3|Option 3|Option 3|Option 1'], + [ + 'is_required' => true, 'type' => 'multiselect', + 'options' => ['option 1' => 0, 'option 2' => 1, 'option 3'] + ], + [ + 'product_type' => 'any', + 'attribute_code' => 'Option 3|Option 3|Option 3|Option 1', + 'additional_attributes' => 'test_attribute=any,attribute_code=Option 3|Option 3|Option 3|Option 1' + ], false ], [ Import::BEHAVIOR_APPEND, ['is_required' => true, 'type' => 'multiselect', 'options' => ['option 1' => 0]], - ['product_type' => 'any', 'attribute_code' => 'Option 1|Option 1|Option 1|Option 1'], + [ + 'product_type' => 'any', + 'attribute_code' => 'Option 1|Option 1|Option 1|Option 1', + 'additional_attributes' => 'test_attribute=any,attribute_code=Option 1|Option 1|Option 1|Option 1' + ], false ], [ diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php index 730f736685028..a35d76a8bee1e 100644 --- a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php +++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php @@ -2191,4 +2191,57 @@ protected function createModelMockWithErrorAggregator( return $importProduct; } + + /** + * @dataProvider valuesDataProvider + */ + public function testParseMultiselectValues($value, $fieldSeparator, $valueSeparator) + { + $this->importProduct->setParameters( + [ + Import::FIELD_FIELD_SEPARATOR => $fieldSeparator, + Import::FIELD_FIELD_MULTIPLE_VALUE_SEPARATOR => $valueSeparator + ] + ); + $this->assertEquals(explode($valueSeparator, $value), $this->importProduct->parseMultiselectValues($value)); + } + + /** + * @return array + */ + public function valuesDataProvider(): array + { + return [ + 'pipeWithCustomFieldSeparator' => [ + 'value' => 'L|C|D|T|H', + 'fieldSeparator' => ';', + 'valueSeparator' => '|' + ], + 'commaWithCustomFieldSeparator' => [ + 'value' => 'L,C,D,T,H', + 'fieldSeparator' => ';', + 'valueSeparator' => ',' + ], + 'pipeWithDefaultFieldSeparator' => [ + 'value' => 'L|C|D|T|H', + 'fieldSeparator' => ',', + 'valueSeparator' => '|' + ], + 'commaWithDefaultFieldSeparator' => [ + 'value' => 'L,C,D,T,H', + 'fieldSeparator' => ',', + 'valueSeparator' => ',' + ], + 'anonymousValueSeparatorWithDefaultFieldSeparator' => [ + 'value' => 'L+C+D+T+H', + 'fieldSeparator' => ',', + 'valueSeparator' => '+' + ], + 'anonymousValueSeparatorWithDefaultFieldSeparatorAndSingleValue' => [ + 'value' => 'L', + 'fieldSeparator' => ',', + 'valueSeparator' => '*' + ] + ]; + } } diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest/ProductValidationTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest/ProductValidationTest.php index 2a235ab8587c8..b7798853f2c90 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest/ProductValidationTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest/ProductValidationTest.php @@ -392,7 +392,7 @@ public function testValidateMultiselectValuesWithCustomSeparator(): void $params = [ 'behavior' => Import::BEHAVIOR_ADD_UPDATE, 'entity' => 'catalog_product', - Import::FIELD_FIELD_MULTIPLE_VALUE_SEPARATOR => '|||' + Import::FIELD_FIELD_MULTIPLE_VALUE_SEPARATOR => '###' ]; $errors = $this->_model->setParameters($params) diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_with_custom_multiselect_values_separator.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_with_custom_multiselect_values_separator.csv index 9a0549efcb860..e14bd5ce2ea71 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_with_custom_multiselect_values_separator.csv +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_with_custom_multiselect_values_separator.csv @@ -1,2 +1,2 @@ sku,store_view_code,product_type,name,price,additional_attributes -simple_ms_2,,simple,"With Multiselect 2",10,"multiselect_attribute=Option 2|||Option 3" +simple_ms_2,,simple,"With Multiselect 2",10,"multiselect_attribute=Option 2###Option 3" From aebc470bb675276e296ecf72013655fe1778b863 Mon Sep 17 00:00:00 2001 From: pradeep1819 <pradeep05.pro@gmail.com> Date: Thu, 16 Nov 2023 22:00:21 +0530 Subject: [PATCH 02/15] ACP2E-2261: [Cloud] Product import fails with custom separator value --- .../Model/Import/Product.php | 31 ++++++++++++++----- .../Import/Product/Type/AbstractType.php | 14 +-------- .../Model/Import/Product/Validator.php | 20 ++---------- 3 files changed, 27 insertions(+), 38 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index 718bc654fc971..5d65fa3cc4ea8 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -2836,7 +2836,7 @@ private function prepareNewSkuData($sku) * * @return array */ - private function _parseAdditionalAttributes($rowData) + private function _parseAdditionalAttributes(array $rowData): array { if (empty($rowData['additional_attributes'])) { return $rowData; @@ -2846,7 +2846,7 @@ private function _parseAdditionalAttributes($rowData) $rowData[mb_strtolower($key)] = $value; } } else { - $rowData = array_merge($rowData, $this->getAdditionalAttributes($rowData['additional_attributes'])); + $rowData = array_merge($rowData, $this->getAdditionalAttributes($rowData)); } return $rowData; } @@ -2860,14 +2860,14 @@ private function _parseAdditionalAttributes($rowData) * codeN => valueN * ] * - * @param string $additionalAttributes Attributes data that will be parsed + * @param array $rowData * @return array */ - private function getAdditionalAttributes($additionalAttributes) + private function getAdditionalAttributes(array $rowData): array { return empty($this->_parameters[Import::FIELDS_ENCLOSURE]) - ? $this->parseAttributesWithoutWrappedValues($additionalAttributes) - : $this->parseAttributesWithWrappedValues($additionalAttributes); + ? $this->parseAttributesWithoutWrappedValues($rowData['additional_attributes'], $rowData['product_type']) + : $this->parseAttributesWithWrappedValues($rowData['additional_attributes']); } /** @@ -2881,9 +2881,10 @@ private function getAdditionalAttributes($additionalAttributes) * * @param string $attributesData Attributes data that will be parsed. It keeps data in format: * code=value,code2=value2...,codeN=valueN + * @param string $productType * @return array */ - private function parseAttributesWithoutWrappedValues($attributesData) + private function parseAttributesWithoutWrappedValues(string $attributesData, string $productType): array { $attributeNameValuePairs = explode($this->getMultipleValueSeparator(), $attributesData); $preparedAttributes = []; @@ -2899,7 +2900,21 @@ private function parseAttributesWithoutWrappedValues($attributesData) } list($code, $value) = explode(self::PAIR_NAME_VALUE_SEPARATOR, $attributeData, 2); $code = mb_strtolower($code); - $preparedAttributes[$code] = $value; + + $entityTypeModel = $this->retrieveProductTypeByName($productType); + if ($entityTypeModel) { + $attrParams = $entityTypeModel->retrieveAttributeFromCache($code); + if (!empty($attrParams) && $attrParams['type'] == 'multiselect') { + $attributeValue = $this->parseMultiselectValues($value, Product::PSEUDO_MULTI_LINE_SEPARATOR); + if (count($attributeValue) > 1) { + $preparedAttributes[$code] = $attributeValue; + } else { + $preparedAttributes[$code] = $value; + } + } + } else { + $preparedAttributes[$code] = $value; + } } return $preparedAttributes; } diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php index bde681768b846..862cd89e3bda9 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php @@ -633,19 +633,7 @@ public function prepareAttributesWithDefaultValueForSave(array $rowData, $withDe $resultAttrs[$attrCode] = $attrParams['options'][strtolower($rowData[$attrCode])]; } elseif ('multiselect' == $attrParams['type']) { $resultAttrs[$attrCode] = []; - $delimiter = ''; - if (is_string($rowData[$attrCode]) - && str_contains($rowData[$attrCode], Product::PSEUDO_MULTI_LINE_SEPARATOR)) { - if (!empty($rowData['additional_attributes']) - && str_contains( - $rowData['additional_attributes'], - $attrCode . Product::PAIR_NAME_VALUE_SEPARATOR - ) - ) { - $delimiter = Product::PSEUDO_MULTI_LINE_SEPARATOR; - } - } - foreach ($this->_entityModel->parseMultiselectValues($rowData[$attrCode], $delimiter) as $value) { + foreach ($this->_entityModel->parseMultiselectValues($rowData[$attrCode]) as $value) { $resultAttrs[$attrCode][] = $attrParams['options'][strtolower($value)]; } $resultAttrs[$attrCode] = implode(',', $resultAttrs[$attrCode]); diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php index 4116cd894b1d7..b4fae5f821ae4 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php @@ -255,23 +255,11 @@ public function isAttributeValid($attrCode, array $attrParams, array $rowData) */ private function validateByAttributeType(string $attrCode, array $attrParams, array $rowData): bool { - $delimiter = ''; - if (is_string($rowData[$attrCode]) && str_contains($rowData[$attrCode], Product::PSEUDO_MULTI_LINE_SEPARATOR)) { - if (!empty($rowData['additional_attributes']) - && str_contains($rowData['additional_attributes'], $attrCode . Product::PAIR_NAME_VALUE_SEPARATOR)) { - $delimiter = Product::PSEUDO_MULTI_LINE_SEPARATOR; - } - } return match ($attrParams['type']) { 'varchar', 'text' => $this->textValidation($attrCode, $attrParams['type']), 'decimal', 'int' => $this->numericValidation($attrCode, $attrParams['type']), 'select', 'boolean' => $this->validateOption($attrCode, $attrParams['options'], $rowData[$attrCode]), - 'multiselect' => $this->validateMultiselect( - $attrCode, - $attrParams['options'], - $rowData[$attrCode], - $delimiter - ), + 'multiselect' => $this->validateMultiselect($attrCode, $attrParams['options'], $rowData[$attrCode]), 'datetime' => $this->validateDateTime($rowData[$attrCode]), default => true, }; @@ -283,18 +271,16 @@ private function validateByAttributeType(string $attrCode, array $attrParams, ar * @param string $attrCode * @param array $options * @param array|string $rowData - * @param string $delimiter * @return bool */ private function validateMultiselect( string $attrCode, array $options, - array|string $rowData, - string $delimiter = '' + array|string $rowData ): bool { $valid = true; - $values = $this->context->parseMultiselectValues($rowData, $delimiter); + $values = $this->context->parseMultiselectValues($rowData); foreach ($values as $value) { $valid = $this->validateOption($attrCode, $options, $value); if (!$valid) { From e7b05df55da1aa3a76ffa60ef7760f6d2e7cec7e Mon Sep 17 00:00:00 2001 From: "Chhandak.Barua" <chhandak.barua@BLR1-LMC-N73490.local> Date: Mon, 20 Nov 2023 17:49:36 +0530 Subject: [PATCH 03/15] ACP2E-2515: Cart Price Rule stops working properly after adding Bundle Product to the cart with Dynamic Price attribute disabled --- app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php b/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php index dddaa693c896e..49031691630c6 100644 --- a/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php +++ b/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php @@ -132,7 +132,7 @@ public function collectRates(RateRequest $request) if ($item->getHasChildren() && $item->isShipSeparately()) { foreach ($item->getChildren() as $child) { if ($child->getFreeShipping() && !$child->getProduct()->isVirtual()) { - $freeShipping = is_numeric($child->getFreeShipping()) ? $child->getFreeShipping() : 0; + $freeShipping = is_numeric((int)$child->getFreeShipping()) ? (int)$child->getFreeShipping() : 0; $freeQty += $item->getQty() * ($child->getQty() - $freeShipping); } } @@ -142,7 +142,7 @@ public function collectRates(RateRequest $request) ) { $freeShipping = $item->getFreeShipping() ? $item->getFreeShipping() : $item->getAddress()->getFreeShipping(); - $freeShipping = is_numeric($freeShipping) ? $freeShipping : 0; + $freeShipping = is_numeric((int)$freeShipping) ? (int)$freeShipping : 0; $freeQty += $item->getQty() - $freeShipping; $freePackageValue += $item->getBaseRowTotal(); } From 37b686868a67890829c8b9c74edb67e0e5fea5a1 Mon Sep 17 00:00:00 2001 From: "Chhandak.Barua" <chhandak.barua@BLR1-LMC-N73490.local> Date: Tue, 21 Nov 2023 17:51:17 +0530 Subject: [PATCH 04/15] ACP2E-2515: Cart Price Rule stops working properly after adding Bundle Product to the cart with Dynamic Price attribute disabled --- app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php b/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php index 49031691630c6..80dd9f18c174a 100644 --- a/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php +++ b/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php @@ -142,7 +142,7 @@ public function collectRates(RateRequest $request) ) { $freeShipping = $item->getFreeShipping() ? $item->getFreeShipping() : $item->getAddress()->getFreeShipping(); - $freeShipping = is_numeric((int)$freeShipping) ? (int)$freeShipping : 0; + $freeShipping = is_numeric($freeShipping) ? $freeShipping : 0; $freeQty += $item->getQty() - $freeShipping; $freePackageValue += $item->getBaseRowTotal(); } From 3ad466762322ce819ad4ff990737050eb92b2df6 Mon Sep 17 00:00:00 2001 From: "Chhandak.Barua" <chhandak.barua@BLR1-LMC-N73490.local> Date: Tue, 21 Nov 2023 22:52:26 +0530 Subject: [PATCH 05/15] ACP2E-2515: Cart Price Rule stops working properly after adding Bundle Product to the cart with Dynamic Price attribute disabled --- .../Magento/OfflineShipping/Model/Carrier/Tablerate.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php b/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php index 80dd9f18c174a..092766bdedfd3 100644 --- a/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php +++ b/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php @@ -132,14 +132,17 @@ public function collectRates(RateRequest $request) if ($item->getHasChildren() && $item->isShipSeparately()) { foreach ($item->getChildren() as $child) { if ($child->getFreeShipping() && !$child->getProduct()->isVirtual()) { - $freeShipping = is_numeric((int)$child->getFreeShipping()) ? (int)$child->getFreeShipping() : 0; + $freeShipping = is_numeric((int)$child->getFreeShipping()) + ? (int)$child->getFreeShipping() + : 0; $freeQty += $item->getQty() * ($child->getQty() - $freeShipping); } } } elseif (($item->getFreeShipping() || $item->getAddress()->getFreeShipping()) && ($item->getFreeShippingMethod() == null || $item->getFreeShippingMethod() && $item->getFreeShippingMethod() == 'tablerate_bestway') - ) { + ) + { $freeShipping = $item->getFreeShipping() ? $item->getFreeShipping() : $item->getAddress()->getFreeShipping(); $freeShipping = is_numeric($freeShipping) ? $freeShipping : 0; From be2ce3ac27ddd8783d4338cba8b11dc0fd376e46 Mon Sep 17 00:00:00 2001 From: "Chhandak.Barua" <chhandak.barua@BLR1-LMC-N73490.local> Date: Wed, 22 Nov 2023 11:51:19 +0530 Subject: [PATCH 06/15] ACP2E-2515: Cart Price Rule stops working properly after adding Bundle Product to the cart with Dynamic Price attribute disabled --- .../Magento/OfflineShipping/Model/Carrier/Tablerate.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php b/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php index 092766bdedfd3..9b6785ac31275 100644 --- a/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php +++ b/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php @@ -133,16 +133,14 @@ public function collectRates(RateRequest $request) foreach ($item->getChildren() as $child) { if ($child->getFreeShipping() && !$child->getProduct()->isVirtual()) { $freeShipping = is_numeric((int)$child->getFreeShipping()) - ? (int)$child->getFreeShipping() - : 0; + ? (int)$child->getFreeShipping() : 0; $freeQty += $item->getQty() * ($child->getQty() - $freeShipping); } } } elseif (($item->getFreeShipping() || $item->getAddress()->getFreeShipping()) && ($item->getFreeShippingMethod() == null || $item->getFreeShippingMethod() && $item->getFreeShippingMethod() == 'tablerate_bestway') - ) - { + ) { $freeShipping = $item->getFreeShipping() ? $item->getFreeShipping() : $item->getAddress()->getFreeShipping(); $freeShipping = is_numeric($freeShipping) ? $freeShipping : 0; From 037c514bae8d3d79293c257c088c5f26aa516bdb Mon Sep 17 00:00:00 2001 From: "Chhandak.Barua" <chhandak.barua@BLR1-LMC-N73490.local> Date: Thu, 23 Nov 2023 22:14:24 +0530 Subject: [PATCH 07/15] ACP2E-2515: Cart Price Rule stops working properly after adding Bundle Product to the cart with Dynamic Price attribute disabled --- .../Test/Unit/Model/Carrier/TablerateTest.php | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/OfflineShipping/Test/Unit/Model/Carrier/TablerateTest.php b/app/code/Magento/OfflineShipping/Test/Unit/Model/Carrier/TablerateTest.php index 7b99a8d354d9e..b2790565cf9d0 100644 --- a/app/code/Magento/OfflineShipping/Test/Unit/Model/Carrier/TablerateTest.php +++ b/app/code/Magento/OfflineShipping/Test/Unit/Model/Carrier/TablerateTest.php @@ -118,10 +118,11 @@ protected function setUp(): void /** * @param bool $freeshipping + * @param bool $isShipSeparately * @dataProvider collectRatesWithGlobalFreeShippingDataProvider * @return void */ - public function testCollectRatesWithGlobalFreeShipping($freeshipping) + public function testCollectRatesWithGlobalFreeShipping($freeshipping, $isShipSeparately) { $rate = [ 'price' => 15, @@ -177,11 +178,17 @@ public function testCollectRatesWithGlobalFreeShipping($freeshipping) $this->resultFactoryMock->expects($this->once())->method('create')->willReturn($result); $product->expects($this->any())->method('isVirtual')->willReturn(false); - $item->expects($this->any())->method('getProduct')->willReturn($product); - $item->expects($this->any())->method('getFreeShipping')->willReturn(1); $item->expects($this->any())->method('getQty')->willReturn(1); - + if ($isShipSeparately) { + $freeShippingReturnValue = true; + $item->expects($this->any())->method('getHasChildren')->willReturn(1); + $item->expects($this->any())->method('isShipSeparately')->willReturn(1); + $item->expects($this->any())->method('getChildren')->willReturn([$item]); + } else { + $freeShippingReturnValue = 1; + } + $item->expects($this->any())->method('getFreeShipping')->willReturn($freeShippingReturnValue); $request->expects($this->any())->method('getAllItems')->willReturn([$item]); $request->expects($this->any())->method('getPackageQty')->willReturn(1); @@ -225,8 +232,9 @@ private function captureArg(&$captureVar) public function collectRatesWithGlobalFreeShippingDataProvider() { return [ - ['freeshipping' => true], - ['freeshipping' => false] + ['freeshipping' => true, 'isShipSeparately' => false], + ['freeshipping' => false, 'isShipSeparately' => false], + ['freeshipping' => true, 'isShipSeparately' => true] ]; } } From 7db18d4359a498d6825d69b6d4f5f60519ca06c3 Mon Sep 17 00:00:00 2001 From: pradeep1819 <pradeep05.pro@gmail.com> Date: Fri, 24 Nov 2023 13:39:01 +0530 Subject: [PATCH 08/15] ACP2E-2261: [Cloud] Product import fails with custom separator value --- .../Model/Import/Product.php | 32 ++++--------- .../Model/Import/Product/Validator.php | 8 +--- .../Model/Import/Product/ValidatorTest.php | 46 +++++-------------- .../Test/Unit/Model/Import/ProductTest.php | 5 +- .../ProductTest/ProductValidationTest.php | 2 +- ...th_custom_multiselect_values_separator.csv | 2 +- 6 files changed, 28 insertions(+), 67 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index 5d65fa3cc4ea8..1493448ae3b37 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -2836,7 +2836,7 @@ private function prepareNewSkuData($sku) * * @return array */ - private function _parseAdditionalAttributes(array $rowData): array + private function _parseAdditionalAttributes($rowData) { if (empty($rowData['additional_attributes'])) { return $rowData; @@ -2846,7 +2846,7 @@ private function _parseAdditionalAttributes(array $rowData): array $rowData[mb_strtolower($key)] = $value; } } else { - $rowData = array_merge($rowData, $this->getAdditionalAttributes($rowData)); + $rowData = array_merge($rowData, $this->getAdditionalAttributes($rowData['additional_attributes'])); } return $rowData; } @@ -2860,14 +2860,14 @@ private function _parseAdditionalAttributes(array $rowData): array * codeN => valueN * ] * - * @param array $rowData + * @param string $additionalAttributes Attributes data that will be parsed * @return array */ - private function getAdditionalAttributes(array $rowData): array + private function getAdditionalAttributes($additionalAttributes) { return empty($this->_parameters[Import::FIELDS_ENCLOSURE]) - ? $this->parseAttributesWithoutWrappedValues($rowData['additional_attributes'], $rowData['product_type']) - : $this->parseAttributesWithWrappedValues($rowData['additional_attributes']); + ? $this->parseAttributesWithoutWrappedValues($additionalAttributes) + : $this->parseAttributesWithWrappedValues($additionalAttributes); } /** @@ -2881,10 +2881,9 @@ private function getAdditionalAttributes(array $rowData): array * * @param string $attributesData Attributes data that will be parsed. It keeps data in format: * code=value,code2=value2...,codeN=valueN - * @param string $productType * @return array */ - private function parseAttributesWithoutWrappedValues(string $attributesData, string $productType): array + private function parseAttributesWithoutWrappedValues($attributesData) { $attributeNameValuePairs = explode($this->getMultipleValueSeparator(), $attributesData); $preparedAttributes = []; @@ -2900,21 +2899,10 @@ private function parseAttributesWithoutWrappedValues(string $attributesData, str } list($code, $value) = explode(self::PAIR_NAME_VALUE_SEPARATOR, $attributeData, 2); $code = mb_strtolower($code); - - $entityTypeModel = $this->retrieveProductTypeByName($productType); - if ($entityTypeModel) { - $attrParams = $entityTypeModel->retrieveAttributeFromCache($code); - if (!empty($attrParams) && $attrParams['type'] == 'multiselect') { - $attributeValue = $this->parseMultiselectValues($value, Product::PSEUDO_MULTI_LINE_SEPARATOR); - if (count($attributeValue) > 1) { - $preparedAttributes[$code] = $attributeValue; - } else { - $preparedAttributes[$code] = $value; - } - } - } else { - $preparedAttributes[$code] = $value; + if (str_contains($value, self::PSEUDO_MULTI_LINE_SEPARATOR)) { + $value = $this->parseMultiselectValues($value, self::PSEUDO_MULTI_LINE_SEPARATOR); } + $preparedAttributes[$code] = $value; } return $preparedAttributes; } diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php index b4fae5f821ae4..f74886069d501 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php @@ -8,7 +8,6 @@ use Magento\CatalogImportExport\Model\Import\Product; use Magento\Framework\Validator\AbstractValidator; use Magento\Catalog\Model\Product\Attribute\Backend\Sku; -use Magento\ImportExport\Model\Import; /** * Product import model validator @@ -273,11 +272,8 @@ private function validateByAttributeType(string $attrCode, array $attrParams, ar * @param array|string $rowData * @return bool */ - private function validateMultiselect( - string $attrCode, - array $options, - array|string $rowData - ): bool { + private function validateMultiselect(string $attrCode, array $options, array|string $rowData): bool + { $valid = true; $values = $this->context->parseMultiselectValues($rowData); diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/ValidatorTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/ValidatorTest.php index 0a46bf26d440c..68b75e71f1897 100644 --- a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/ValidatorTest.php +++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/ValidatorTest.php @@ -47,7 +47,7 @@ protected function setUp(): void $entityTypeModel->expects($this->any())->method('retrieveAttributeFromCache')->willReturn([]); $this->context = $this->createPartialMock( Product::class, - ['retrieveProductTypeByName', 'retrieveMessageTemplate', 'getBehavior'] + ['retrieveProductTypeByName', 'retrieveMessageTemplate', 'getBehavior', 'getMultipleValueSeparator'] ); $this->context->expects($this->any())->method('retrieveProductTypeByName')->willReturn($entityTypeModel); $this->context->expects($this->any())->method('retrieveMessageTemplate')->willReturn('error message'); @@ -83,6 +83,7 @@ protected function setUp(): void */ public function testAttributeValidation($behavior, $attrParams, $rowData, $isValid, $attrCode = 'attribute_code') { + $this->context->method('getMultipleValueSeparator')->willReturn(Product::PSEUDO_MULTI_LINE_SEPARATOR); $this->context->expects($this->any())->method('getBehavior')->willReturn($behavior); $result = $this->validator->isAttributeValid( $attrCode, @@ -169,58 +170,33 @@ public function attributeValidationProvider() [ Import::BEHAVIOR_APPEND, ['is_required' => true, 'type' => 'multiselect', 'options' => ['option 1' => 0, 'option 2' => 1]], - [ - 'product_type' => 'any', - 'attribute_code' => 'Option 1|Option 2|Option 3', - 'additional_attributes' => 'test_attribute=any,attribute_code=Option 1|Option 2|Option 3' - ], + ['product_type' => 'any', 'attribute_code' => 'Option 1|Option 2|Option 3'], false ], [ Import::BEHAVIOR_APPEND, ['is_required' => true, 'type' => 'multiselect', 'options' => ['option 1' => 0, 'option 2' => 1]], - [ - 'product_type' => 'any', - 'attribute_code' => 'Option 1|Option 2', - 'additional_attributes' => 'test_attribute=any,attribute_code=Option 1|Option 2' - ], + ['product_type' => 'any', 'attribute_code' => 'Option 1|Option 2'], true ], [ Import::BEHAVIOR_APPEND, - [ - 'is_required' => true, - 'type' => 'multiselect', - 'options' => ['option 1' => 0, 'option 2' => 1, 'option 3'] - ], - [ - 'product_type' => 'any', - 'attribute_code' => 'Option 1|Option 2|Option 1', - 'additional_attributes' => 'test_attribute=any,attribute_code=Option 1|Option 2|Option 1' - ], + ['is_required' => true, 'type' => 'multiselect', + 'options' => ['option 1' => 0, 'option 2' => 1, 'option 3']], + ['product_type' => 'any', 'attribute_code' => 'Option 1|Option 2|Option 1'], false ], [ Import::BEHAVIOR_APPEND, - [ - 'is_required' => true, 'type' => 'multiselect', - 'options' => ['option 1' => 0, 'option 2' => 1, 'option 3'] - ], - [ - 'product_type' => 'any', - 'attribute_code' => 'Option 3|Option 3|Option 3|Option 1', - 'additional_attributes' => 'test_attribute=any,attribute_code=Option 3|Option 3|Option 3|Option 1' - ], + ['is_required' => true, 'type' => 'multiselect', + 'options' => ['option 1' => 0, 'option 2' => 1, 'option 3']], + ['product_type' => 'any', 'attribute_code' => 'Option 3|Option 3|Option 3|Option 1'], false ], [ Import::BEHAVIOR_APPEND, ['is_required' => true, 'type' => 'multiselect', 'options' => ['option 1' => 0]], - [ - 'product_type' => 'any', - 'attribute_code' => 'Option 1|Option 1|Option 1|Option 1', - 'additional_attributes' => 'test_attribute=any,attribute_code=Option 1|Option 1|Option 1|Option 1' - ], + ['product_type' => 'any', 'attribute_code' => 'Option 1|Option 1|Option 1|Option 1'], false ], [ diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php index a35d76a8bee1e..758792f6610f9 100644 --- a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php +++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php @@ -1471,7 +1471,7 @@ public function testGetImagesFromRow($rowData, $expectedResult): void */ public function testParseAttributesWithoutWrappedValuesWillReturnsLowercasedAttributeCodes(): void { - $attributesData = 'PARAM1=value1,param2=value2'; + $attributesData = 'PARAM1=value1,param2=value2|value3'; $preparedAttributes = $this->invokeMethod( $this->importProduct, 'parseAttributesWithoutWrappedValues', @@ -1482,7 +1482,8 @@ public function testParseAttributesWithoutWrappedValuesWillReturnsLowercasedAttr $this->assertEquals('value1', $preparedAttributes['param1']); $this->assertArrayHasKey('param2', $preparedAttributes); - $this->assertEquals('value2', $preparedAttributes['param2']); + $this->assertTrue(in_array('value2', $preparedAttributes['param2'])); + $this->assertTrue(in_array('value3', $preparedAttributes['param2'])); $this->assertArrayNotHasKey('PARAM1', $preparedAttributes); } diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest/ProductValidationTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest/ProductValidationTest.php index b7798853f2c90..2a235ab8587c8 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest/ProductValidationTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest/ProductValidationTest.php @@ -392,7 +392,7 @@ public function testValidateMultiselectValuesWithCustomSeparator(): void $params = [ 'behavior' => Import::BEHAVIOR_ADD_UPDATE, 'entity' => 'catalog_product', - Import::FIELD_FIELD_MULTIPLE_VALUE_SEPARATOR => '###' + Import::FIELD_FIELD_MULTIPLE_VALUE_SEPARATOR => '|||' ]; $errors = $this->_model->setParameters($params) diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_with_custom_multiselect_values_separator.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_with_custom_multiselect_values_separator.csv index e14bd5ce2ea71..9a0549efcb860 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_with_custom_multiselect_values_separator.csv +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_with_custom_multiselect_values_separator.csv @@ -1,2 +1,2 @@ sku,store_view_code,product_type,name,price,additional_attributes -simple_ms_2,,simple,"With Multiselect 2",10,"multiselect_attribute=Option 2###Option 3" +simple_ms_2,,simple,"With Multiselect 2",10,"multiselect_attribute=Option 2|||Option 3" From a394ae15d3165facba08993259340da6e9e9072d Mon Sep 17 00:00:00 2001 From: pradeep1819 <pradeep05.pro@gmail.com> Date: Fri, 24 Nov 2023 17:38:02 +0530 Subject: [PATCH 09/15] ACP2E-2261: [Cloud] Product import fails with custom separator value --- .../Magento/CatalogImportExport/Model/Import/Product.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index 1493448ae3b37..fb3ae4cc10068 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -2894,6 +2894,14 @@ private function parseAttributesWithoutWrappedValues($attributesData) if (!$code) { continue; } + //concatenate attribute values with last used separator in case of array + if (is_array($preparedAttributes[$code]) + && str_contains($attributesData, self::PSEUDO_MULTI_LINE_SEPARATOR)) { + $preparedAttributes[$code] = implode( + self::PSEUDO_MULTI_LINE_SEPARATOR, + $preparedAttributes[$code] + ); + } $preparedAttributes[$code] .= $this->getMultipleValueSeparator() . $attributeData; continue; } From 5ca94916ef8280bf2134c1bc14b7b5c36b6d4b60 Mon Sep 17 00:00:00 2001 From: pradeep1819 <pradeep05.pro@gmail.com> Date: Fri, 24 Nov 2023 19:40:42 +0530 Subject: [PATCH 10/15] ACP2E-2261: [Cloud] Product import fails with custom separator value --- app/code/Magento/CatalogImportExport/Model/Import/Product.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index fb3ae4cc10068..4140a7e1aa147 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -2962,6 +2962,9 @@ private function parseAttributesWithWrappedValues($attributesData) public function parseMultiselectValues($values, $delimiter = '') { if (empty($this->_parameters[Import::FIELDS_ENCLOSURE])) { + if (is_array($values)) { + return $values; + } if (!$delimiter) { $delimiter = $this->getMultipleValueSeparator(); } From 66563fb3c61f8969ce42e614c607898dab92c8d5 Mon Sep 17 00:00:00 2001 From: "Chhandak.Barua" <chhandak.barua@BLR1-LMC-N73490.local> Date: Mon, 27 Nov 2023 12:27:22 +0530 Subject: [PATCH 11/15] ACP2E-2515: Cart Price Rule stops working properly after adding Bundle Product to the cart with Dynamic Price attribute disabled --- .../OfflineShipping/Test/Unit/Model/Carrier/TablerateTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/OfflineShipping/Test/Unit/Model/Carrier/TablerateTest.php b/app/code/Magento/OfflineShipping/Test/Unit/Model/Carrier/TablerateTest.php index b2790565cf9d0..8814d8d45b773 100644 --- a/app/code/Magento/OfflineShipping/Test/Unit/Model/Carrier/TablerateTest.php +++ b/app/code/Magento/OfflineShipping/Test/Unit/Model/Carrier/TablerateTest.php @@ -186,7 +186,7 @@ public function testCollectRatesWithGlobalFreeShipping($freeshipping, $isShipSep $item->expects($this->any())->method('isShipSeparately')->willReturn(1); $item->expects($this->any())->method('getChildren')->willReturn([$item]); } else { - $freeShippingReturnValue = 1; + $freeShippingReturnValue = "1"; } $item->expects($this->any())->method('getFreeShipping')->willReturn($freeShippingReturnValue); $request->expects($this->any())->method('getAllItems')->willReturn([$item]); From 5e9ba20225c4b167b018cb208a9fc468e59a01c8 Mon Sep 17 00:00:00 2001 From: "Chhandak.Barua" <chhandak.barua@BLR1-LMC-N73490.local> Date: Fri, 1 Dec 2023 16:28:19 +0530 Subject: [PATCH 12/15] ACP2E-2515: Cart Price Rule stops working properly after adding Bundle Product to the cart with Dynamic Price attribute disabled --- app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php b/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php index 9b6785ac31275..69af732ccc5a8 100644 --- a/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php +++ b/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php @@ -132,7 +132,7 @@ public function collectRates(RateRequest $request) if ($item->getHasChildren() && $item->isShipSeparately()) { foreach ($item->getChildren() as $child) { if ($child->getFreeShipping() && !$child->getProduct()->isVirtual()) { - $freeShipping = is_numeric((int)$child->getFreeShipping()) + $freeShipping = (int)$child->getFreeShipping() ? (int)$child->getFreeShipping() : 0; $freeQty += $item->getQty() * ($child->getQty() - $freeShipping); } From d301755c38ebd91644a62735f04e5f05381dbb58 Mon Sep 17 00:00:00 2001 From: pradeep1819 <pradeep05.pro@gmail.com> Date: Sun, 3 Dec 2023 20:28:06 +0530 Subject: [PATCH 13/15] ACP2E-2261: [Cloud] Product import fails with custom separator value --- .../Model/Import/Product.php | 43 ++++++++++--------- .../Test/Unit/Model/Import/ProductTest.php | 24 ++++++++--- 2 files changed, 41 insertions(+), 26 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index 4140a7e1aa147..bd0bf17487e58 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -938,7 +938,7 @@ public function __construct( $this->_optionEntity = $data['option_entity'] ?? $optionFactory->create(['data' => ['product_entity' => $this]]); $this->skuStorage = $skuStorage ?? ObjectManager::getInstance() - ->get(SkuStorage::class); + ->get(SkuStorage::class); $this->_initAttributeSets() ->_initTypeModels() ->_initSkus() @@ -948,7 +948,7 @@ public function __construct( $this->productRepository = $productRepository ?? ObjectManager::getInstance() ->get(ProductRepositoryInterface::class); $this->stockItemProcessor = $stockItemProcessor ?? ObjectManager::getInstance() - ->get(StockItemProcessorInterface::class); + ->get(StockItemProcessorInterface::class); } /** @@ -2002,8 +2002,8 @@ private function saveProductMediaGalleryPhase( private function saveProductAttributesPhase( array $rowData, int $rowScope, - &$previousType, - &$prevAttributeSet, + &$previousType, + &$prevAttributeSet, array &$attributes ) : void { $rowSku = $rowData[self::COL_SKU]; @@ -2836,7 +2836,7 @@ private function prepareNewSkuData($sku) * * @return array */ - private function _parseAdditionalAttributes($rowData) + private function _parseAdditionalAttributes(array $rowData): array { if (empty($rowData['additional_attributes'])) { return $rowData; @@ -2846,7 +2846,7 @@ private function _parseAdditionalAttributes($rowData) $rowData[mb_strtolower($key)] = $value; } } else { - $rowData = array_merge($rowData, $this->getAdditionalAttributes($rowData['additional_attributes'])); + $rowData = array_merge($rowData, $this->getAdditionalAttributes($rowData)); } return $rowData; } @@ -2860,14 +2860,14 @@ private function _parseAdditionalAttributes($rowData) * codeN => valueN * ] * - * @param string $additionalAttributes Attributes data that will be parsed + * @param array $rowData * @return array */ - private function getAdditionalAttributes($additionalAttributes) + private function getAdditionalAttributes(array $rowData): array { return empty($this->_parameters[Import::FIELDS_ENCLOSURE]) - ? $this->parseAttributesWithoutWrappedValues($additionalAttributes) - : $this->parseAttributesWithWrappedValues($additionalAttributes); + ? $this->parseAttributesWithoutWrappedValues($rowData['additional_attributes'], $rowData['product_type']) + : $this->parseAttributesWithWrappedValues($rowData['additional_attributes']); } /** @@ -2881,9 +2881,10 @@ private function getAdditionalAttributes($additionalAttributes) * * @param string $attributesData Attributes data that will be parsed. It keeps data in format: * code=value,code2=value2...,codeN=valueN + * @param string $productType * @return array */ - private function parseAttributesWithoutWrappedValues($attributesData) + private function parseAttributesWithoutWrappedValues(string $attributesData, string $productType): array { $attributeNameValuePairs = explode($this->getMultipleValueSeparator(), $attributesData); $preparedAttributes = []; @@ -2894,21 +2895,21 @@ private function parseAttributesWithoutWrappedValues($attributesData) if (!$code) { continue; } - //concatenate attribute values with last used separator in case of array - if (is_array($preparedAttributes[$code]) - && str_contains($attributesData, self::PSEUDO_MULTI_LINE_SEPARATOR)) { - $preparedAttributes[$code] = implode( - self::PSEUDO_MULTI_LINE_SEPARATOR, - $preparedAttributes[$code] - ); - } $preparedAttributes[$code] .= $this->getMultipleValueSeparator() . $attributeData; continue; } list($code, $value) = explode(self::PAIR_NAME_VALUE_SEPARATOR, $attributeData, 2); $code = mb_strtolower($code); - if (str_contains($value, self::PSEUDO_MULTI_LINE_SEPARATOR)) { - $value = $this->parseMultiselectValues($value, self::PSEUDO_MULTI_LINE_SEPARATOR); + + $entityTypeModel = $this->retrieveProductTypeByName($productType); + if ($entityTypeModel) { + $attrParams = $entityTypeModel->retrieveAttributeFromCache($code); + if (!empty($attrParams) && $attrParams['type'] == 'multiselect') { + $parsedValue = $this->parseMultiselectValues($value, self::PSEUDO_MULTI_LINE_SEPARATOR); + if (count($parsedValue) > 1) { + $value = $parsedValue; + } + } } $preparedAttributes[$code] = $value; } diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php index 758792f6610f9..f43179cc58948 100644 --- a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php +++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php @@ -25,6 +25,7 @@ use Magento\CatalogInventory\Api\StockConfigurationInterface; use Magento\CatalogInventory\Api\StockRegistryInterface; use Magento\CatalogInventory\Model\Spi\StockStateProviderInterface; +use Magento\ConfigurableImportExport\Model\Import\Product\Type\Configurable; use Magento\Eav\Model\Config; use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; use Magento\Eav\Model\Entity\Attribute\Set; @@ -1471,19 +1472,32 @@ public function testGetImagesFromRow($rowData, $expectedResult): void */ public function testParseAttributesWithoutWrappedValuesWillReturnsLowercasedAttributeCodes(): void { + $entityTypeModel = $this->createPartialMock( + Configurable::class, + ['retrieveAttributeFromCache'] + ); + $entityTypeModel->expects($this->exactly(2))->method('retrieveAttributeFromCache')->willReturn([ + 'type' => 'multiselect' + ]); + $importProduct = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->onlyMethods(['retrieveProductTypeByName']) + ->getMock(); + $importProduct->expects($this->exactly(2))->method('retrieveProductTypeByName')->willReturn($entityTypeModel); + $attributesData = 'PARAM1=value1,param2=value2|value3'; $preparedAttributes = $this->invokeMethod( - $this->importProduct, + $importProduct, 'parseAttributesWithoutWrappedValues', - [$attributesData] + [$attributesData, 'configurable'] ); $this->assertArrayHasKey('param1', $preparedAttributes); $this->assertEquals('value1', $preparedAttributes['param1']); $this->assertArrayHasKey('param2', $preparedAttributes); - $this->assertTrue(in_array('value2', $preparedAttributes['param2'])); - $this->assertTrue(in_array('value3', $preparedAttributes['param2'])); + $this->assertEquals('value2', $preparedAttributes['param2'][0]); + $this->assertEquals('value3', $preparedAttributes['param2'][1]); $this->assertArrayNotHasKey('PARAM1', $preparedAttributes); } @@ -1685,7 +1699,7 @@ public function productCategoriesDataProvider() ], 'catalog_category_product', [ - [2, 5], + [2, 5], [ [ 'product_id' => 2, From cfc1dafa77d823b11626cc06492c9e5a904eb30b Mon Sep 17 00:00:00 2001 From: pradeep1819 <pradeep05.pro@gmail.com> Date: Mon, 4 Dec 2023 10:23:08 +0530 Subject: [PATCH 14/15] ACP2E-2261: [Cloud] Product import fails with custom separator value --- app/code/Magento/CatalogImportExport/Model/Import/Product.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index bd0bf17487e58..39a9b60ff76d7 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -2002,8 +2002,8 @@ private function saveProductMediaGalleryPhase( private function saveProductAttributesPhase( array $rowData, int $rowScope, - &$previousType, - &$prevAttributeSet, + &$previousType, + &$prevAttributeSet, array &$attributes ) : void { $rowSku = $rowData[self::COL_SKU]; From 8bfadccfa68b736c90d03cb7f0ae447b8f6f154f Mon Sep 17 00:00:00 2001 From: "Chhandak.Barua" <chhandak.barua@BLR1-LMC-N73490.local> Date: Wed, 6 Dec 2023 17:12:41 +0530 Subject: [PATCH 15/15] ACP2E-2515: Cart Price Rule stops working properly after adding Bundle Product to the cart with Dynamic Price attribute disabled --- app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php b/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php index 69af732ccc5a8..01328e0df63a1 100644 --- a/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php +++ b/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php @@ -132,8 +132,7 @@ public function collectRates(RateRequest $request) if ($item->getHasChildren() && $item->isShipSeparately()) { foreach ($item->getChildren() as $child) { if ($child->getFreeShipping() && !$child->getProduct()->isVirtual()) { - $freeShipping = (int)$child->getFreeShipping() - ? (int)$child->getFreeShipping() : 0; + $freeShipping = (int)$child->getFreeShipping(); $freeQty += $item->getQty() * ($child->getQty() - $freeShipping); } }