Skip to content

Commit da0579a

Browse files
committed
Merge remote-tracking branch 'origin/MAGETWO-90789' into 2.3-develop-pr20
2 parents 2a3b27c + ea26c0d commit da0579a

File tree

3 files changed

+133
-74
lines changed

3 files changed

+133
-74
lines changed

app/code/Magento/CatalogImportExport/Model/Import/Product/Option.php

Lines changed: 78 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1223,6 +1223,7 @@ protected function _importData()
12231223
$typeTitles = [];
12241224
$parentCount = [];
12251225
$childCount = [];
1226+
$optionsToRemove = [];
12261227

12271228
foreach ($bunch as $rowNumber => $rowData) {
12281229
if (isset($optionId, $valueId) && empty($rowData[PRODUCT::COL_STORE_VIEW_CODE])) {
@@ -1232,14 +1233,18 @@ protected function _importData()
12321233
$optionId = $nextOptionId;
12331234
$valueId = $nextValueId;
12341235
$multiRowData = $this->_getMultiRowFormat($rowData);
1235-
1236+
if (!empty($rowData[self::COLUMN_SKU]) && isset($this->_productsSkuToId[$rowData[self::COLUMN_SKU]])) {
1237+
$this->_rowProductId = $this->_productsSkuToId[$rowData[self::COLUMN_SKU]];
1238+
if (array_key_exists('custom_options', $rowData) && trim($rowData['custom_options']) === '') {
1239+
$optionsToRemove[] = $this->_rowProductId;
1240+
}
1241+
}
12361242
foreach ($multiRowData as $optionData) {
12371243
$combinedData = array_merge($rowData, $optionData);
12381244

1239-
if (!$this->isRowAllowedToImport($combinedData, $rowNumber)) {
1240-
continue;
1241-
}
1242-
if (!$this->_parseRequiredData($combinedData)) {
1245+
if (!$this->isRowAllowedToImport($combinedData, $rowNumber)
1246+
|| !$this->_parseRequiredData($combinedData)
1247+
) {
12431248
continue;
12441249
}
12451250
$optionData = $this->_collectOptionMainData(
@@ -1266,38 +1271,45 @@ protected function _importData()
12661271
}
12671272
}
12681273

1269-
// Save prepared custom options data !!!
1270-
if ($this->getBehavior() != \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND) {
1271-
$this->_deleteEntities(array_keys($products));
1272-
}
1273-
1274-
if ($this->_isReadyForSaving($options, $titles, $typeValues)) {
1275-
if ($this->getBehavior() == \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND) {
1276-
$this->_compareOptionsWithExisting($options, $titles, $prices, $typeValues);
1277-
$this->restoreOriginalOptionTypeIds($typeValues, $typePrices, $typeTitles);
1278-
}
1274+
$this->removeExistingOptions($products, $optionsToRemove);
12791275

1280-
$this->_saveOptions(
1281-
$options
1282-
)->_saveTitles(
1283-
$titles
1284-
)->_savePrices(
1285-
$prices
1286-
)->_saveSpecificTypeValues(
1287-
$typeValues
1288-
)->_saveSpecificTypePrices(
1289-
$typePrices
1290-
)->_saveSpecificTypeTitles(
1291-
$typeTitles
1292-
)->_updateProducts(
1293-
$products
1294-
);
1295-
}
1276+
$types = [
1277+
'values' => $typeValues,
1278+
'prices' => $typePrices,
1279+
'titles' => $typeTitles,
1280+
];
1281+
//Save prepared custom options data.
1282+
$this->savePreparedCustomOptions(
1283+
$products,
1284+
$options,
1285+
$titles,
1286+
$prices,
1287+
$types
1288+
);
12961289
}
12971290

12981291
return true;
12991292
}
13001293

1294+
/**
1295+
* Remove all existing options if import behaviour is APPEND
1296+
* in other case remove options for products with empty "custom_options" row only.
1297+
*
1298+
* @param array $products
1299+
* @param array $optionsToRemove
1300+
*
1301+
* @return void
1302+
*/
1303+
private function removeExistingOptions(array $products, array $optionsToRemove): void
1304+
{
1305+
if ($this->getBehavior() != \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND) {
1306+
$this->_deleteEntities(array_keys($products));
1307+
} elseif (!empty($optionsToRemove)) {
1308+
// Remove options for products with empty "custom_options" row
1309+
$this->_deleteEntities($optionsToRemove);
1310+
}
1311+
}
1312+
13011313
/**
13021314
* Load data of existed products
13031315
*
@@ -1537,9 +1549,7 @@ private function getExistingOptionTypeId($optionId, $storeId, $optionTypeTitle)
15371549
*/
15381550
protected function _parseRequiredData(array $rowData)
15391551
{
1540-
if ($rowData[self::COLUMN_SKU] != '' && isset($this->_productsSkuToId[$rowData[self::COLUMN_SKU]])) {
1541-
$this->_rowProductId = $this->_productsSkuToId[$rowData[self::COLUMN_SKU]];
1542-
} elseif (!isset($this->_rowProductId)) {
1552+
if ($this->_rowProductId === null) {
15431553
return false;
15441554
}
15451555

@@ -1991,4 +2001,38 @@ private function getProductIdentifierField()
19912001
}
19922002
return $this->productEntityIdentifierField;
19932003
}
2004+
2005+
/**
2006+
* Save prepared custom options.
2007+
*
2008+
* @param array $products
2009+
* @param array $options
2010+
* @param array $titles
2011+
* @param array $prices
2012+
* @param array $types
2013+
*
2014+
* @return void
2015+
*/
2016+
private function savePreparedCustomOptions(
2017+
array $products,
2018+
array $options,
2019+
array $titles,
2020+
array $prices,
2021+
array $types
2022+
): void {
2023+
if ($this->_isReadyForSaving($options, $titles, $types['values'])) {
2024+
if ($this->getBehavior() == \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND) {
2025+
$this->_compareOptionsWithExisting($options, $titles, $prices, $types['values']);
2026+
$this->restoreOriginalOptionTypeIds($types['values'], $types['prices'], $types['titles']);
2027+
}
2028+
2029+
$this->_saveOptions($options)
2030+
->_saveTitles($titles)
2031+
->_savePrices($prices)
2032+
->_saveSpecificTypeValues($types['values'])
2033+
->_saveSpecificTypePrices($types['prices'])
2034+
->_saveSpecificTypeTitles($types['titles'])
2035+
->_updateProducts($products);
2036+
}
2037+
}
19942038
}

dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php

Lines changed: 53 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -271,15 +271,18 @@ public function testStockState()
271271
}
272272

273273
/**
274-
* Tests adding of custom options with existing and new product
274+
* Tests adding of custom options with existing and new product.
275275
*
276276
* @magentoDataFixture Magento/Catalog/_files/product_simple.php
277277
* @dataProvider getBehaviorDataProvider
278278
* @param string $importFile
279279
* @param string $sku
280+
* @param int $expectedOptionsQty
280281
* @magentoAppIsolation enabled
282+
*
283+
* @return void
281284
*/
282-
public function testSaveCustomOptions($importFile, $sku)
285+
public function testSaveCustomOptions(string $importFile, string $sku, int $expectedOptionsQty): void
283286
{
284287
$pathToFile = __DIR__ . '/_files/' . $importFile;
285288
$importModel = $this->createImportModel($pathToFile);
@@ -312,6 +315,7 @@ public function testSaveCustomOptions($importFile, $sku)
312315
// assert of options data
313316
$this->assertCount(count($expectedData['data']), $actualData['data']);
314317
$this->assertCount(count($expectedData['values']), $actualData['values']);
318+
$this->assertCount($expectedOptionsQty, $actualData['options']);
315319
foreach ($expectedData['options'] as $expectedId => $expectedOption) {
316320
$elementExist = false;
317321
// find value in actual options and values
@@ -411,17 +415,24 @@ public function testSaveCustomOptionsWithMultipleStoreViews()
411415
/**
412416
* @return array
413417
*/
414-
public function getBehaviorDataProvider()
418+
public function getBehaviorDataProvider(): array
415419
{
416420
return [
417421
'Append behavior with existing product' => [
418-
'$importFile' => 'product_with_custom_options.csv',
419-
'$sku' => 'simple',
422+
'importFile' => 'product_with_custom_options.csv',
423+
'sku' => 'simple',
424+
'expectedOptionsQty' => 6,
425+
],
426+
'Append behavior with existing product and without options in import file' => [
427+
'importFile' => 'product_without_custom_options.csv',
428+
'sku' => 'simple',
429+
'expectedOptionsQty' => 0,
420430
],
421431
'Append behavior with new product' => [
422-
'$importFile' => 'product_with_custom_options_new.csv',
423-
'$sku' => 'simple_new',
424-
]
432+
'importFile' => 'product_with_custom_options_new.csv',
433+
'sku' => 'simple_new',
434+
'expectedOptionsQty' => 4,
435+
],
425436
];
426437
}
427438

@@ -571,43 +582,45 @@ protected function getExpectedOptionsData(string $pathToFile, string $storeCode
571582
break;
572583
}
573584
}
574-
foreach (explode('|', $productData['data'][$storeRowId]['custom_options']) as $optionData) {
575-
$option = array_values(
576-
array_map(
577-
function ($input) {
578-
$data = explode('=', $input);
579-
return [$data[0] => $data[1]];
580-
},
581-
explode(',', $optionData)
582-
)
583-
);
584-
$option = array_merge(...$option);
585-
586-
if (!empty($option['type']) && !empty($option['name'])) {
587-
$lastOptionKey = $option['type'] . '|' . $option['name'];
588-
if (!isset($expectedOptions[$expectedOptionId])
589-
|| $expectedOptions[$expectedOptionId] != $lastOptionKey) {
590-
$expectedOptionId++;
591-
$expectedOptions[$expectedOptionId] = $lastOptionKey;
592-
$expectedData[$expectedOptionId] = [];
593-
foreach ($this->_assertOptions as $assertKey => $assertFieldName) {
594-
if (array_key_exists($assertFieldName, $option)
595-
&& !(($assertFieldName == 'price' || $assertFieldName == 'sku')
596-
&& in_array($option['type'], $this->specificTypes))
597-
) {
598-
$expectedData[$expectedOptionId][$assertKey] = $option[$assertFieldName];
585+
if (!empty($productData['data'][$storeRowId]['custom_options'])) {
586+
foreach (explode('|', $productData['data'][$storeRowId]['custom_options']) as $optionData) {
587+
$option = array_values(
588+
array_map(
589+
function ($input) {
590+
$data = explode('=', $input);
591+
return [$data[0] => $data[1]];
592+
},
593+
explode(',', $optionData)
594+
)
595+
);
596+
$option = array_merge(...$option);
597+
598+
if (!empty($option['type']) && !empty($option['name'])) {
599+
$lastOptionKey = $option['type'] . '|' . $option['name'];
600+
if (!isset($expectedOptions[$expectedOptionId])
601+
|| $expectedOptions[$expectedOptionId] != $lastOptionKey) {
602+
$expectedOptionId++;
603+
$expectedOptions[$expectedOptionId] = $lastOptionKey;
604+
$expectedData[$expectedOptionId] = [];
605+
foreach ($this->_assertOptions as $assertKey => $assertFieldName) {
606+
if (array_key_exists($assertFieldName, $option)
607+
&& !(($assertFieldName == 'price' || $assertFieldName == 'sku')
608+
&& in_array($option['type'], $this->specificTypes))
609+
) {
610+
$expectedData[$expectedOptionId][$assertKey] = $option[$assertFieldName];
611+
}
599612
}
600613
}
601614
}
602-
}
603-
$optionValue = [];
604-
if (!empty($option['name']) && !empty($option['option_title'])) {
605-
foreach ($this->_assertOptionValues as $assertKey => $assertFieldName) {
606-
if (isset($option[$assertFieldName])) {
607-
$optionValue[$assertKey] = $option[$assertFieldName];
615+
$optionValue = [];
616+
if (!empty($option['name']) && !empty($option['option_title'])) {
617+
foreach ($this->_assertOptionValues as $assertKey => $assertFieldName) {
618+
if (isset($option[$assertFieldName])) {
619+
$optionValue[$assertKey] = $option[$assertFieldName];
620+
}
608621
}
622+
$expectedValues[$expectedOptionId][] = $optionValue;
609623
}
610-
$expectedValues[$expectedOptionId][] = $optionValue;
611624
}
612625
}
613626

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
sku,website_code,store_view_code,attribute_set_code,product_type,name,description,short_description,weight,product_online,visibility,product_websites,categories,price,special_price,special_price_from_date,special_price_to_date,tax_class_name,url_key,meta_title,meta_keywords,meta_description,base_image,base_image_label,small_image,small_image_label,thumbnail_image,thumbnail_image_label,additional_images,additional_image_labels,configurable_variation_labels,configurable_variations,bundle_price_type,bundle_sku_type,bundle_weight_type,bundle_values,downloadble_samples,downloadble_links,associated_skus,related_skus,crosssell_skus,upsell_skus,custom_options,additional_attributes,manage_stock,is_in_stock,qty,out_of_stock_qty,is_qty_decimal,allow_backorders,min_cart_qty,max_cart_qty,notify_on_stock_below,qty_increments,enable_qty_increments,is_decimal_divided,new_from_date,new_to_date,gift_message_available,created_at,updated_at,custom_design,custom_design_from,custom_design_to,custom_layout_update,page_layout,product_options_container,msrp_price,msrp_display_actual_price_type,map_enabled
2+
simple,base,,Default,simple,New Product,,,9,1,"Catalog, Search",base,,10,,,,Taxable Goods,new-product,,,,,,,,,,,,,,,,,,,,,,,,,,1,1,999,0,0,0,1,10000,1,1,0,0,,,,,,,,,,,Block after Info Column,,,

0 commit comments

Comments
 (0)