3
3
* Copyright © Magento, Inc. All rights reserved.
4
4
* See COPYING.txt for license details.
5
5
*/
6
+
6
7
namespace Magento \CatalogImportExport \Model \Import ;
7
8
8
9
use Magento \Catalog \Model \Config as CatalogConfig ;
9
10
use Magento \Catalog \Model \Product \Visibility ;
10
11
use Magento \CatalogImportExport \Model \Import \Product \MediaGalleryProcessor ;
11
12
use Magento \CatalogImportExport \Model \Import \Product \RowValidatorInterface as ValidatorInterface ;
13
+ use Magento \CatalogImportExport \Model \StockItemImporterInterface ;
12
14
use Magento \CatalogInventory \Api \Data \StockItemInterface ;
13
15
use Magento \Framework \App \Filesystem \DirectoryList ;
14
16
use Magento \Framework \Exception \LocalizedException ;
@@ -638,6 +640,13 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
638
640
*/
639
641
private $ _logger ;
640
642
643
+ /**
644
+ * "Duplicate multiselect values" error array key
645
+ *
646
+ * @var string
647
+ */
648
+ private static $ errorDuplicateMultiselectValues = 'duplicatedMultiselectValues ' ;
649
+
641
650
/**
642
651
* {@inheritdoc}
643
652
*/
@@ -767,7 +776,6 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
767
776
* @param CatalogConfig $catalogConfig
768
777
* @param MediaGalleryProcessor $mediaProcessor
769
778
* @param ProductRepositoryInterface|null $productRepository
770
- * @throws \Magento\Framework\Exception\LocalizedException
771
779
*
772
780
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
773
781
*/
@@ -857,18 +865,18 @@ public function __construct(
857
865
$ string ,
858
866
$ errorAggregator
859
867
);
860
- $ this ->_optionEntity = isset (
861
- $ data ['option_entity ' ]
862
- ) ? $ data ['option_entity ' ] : $ optionFactory ->create (
863
- ['data ' => ['product_entity ' => $ this ]]
864
- );
868
+ $ this ->_optionEntity = $ data ['option_entity ' ]
869
+ ?? $ optionFactory ->create (['data ' => ['product_entity ' => $ this ]]);
865
870
866
871
$ this ->_initAttributeSets ()
867
872
->_initTypeModels ()
868
873
->_initSkus ();
869
874
$ this ->validator ->init ($ this );
870
875
$ this ->productRepository = $ productRepository ?? \Magento \Framework \App \ObjectManager::getInstance ()
871
876
->get (ProductRepositoryInterface::class);
877
+
878
+ $ errorMessageText = __ ('Value for multiselect attribute %s contains duplicated values ' );
879
+ $ this ->_messageTemplates [self ::$ errorDuplicateMultiselectValues ] = $ errorMessageText ;
872
880
}
873
881
874
882
/**
@@ -884,7 +892,7 @@ public function isAttributeValid($attrCode, array $attrParams, array $rowData, $
884
892
{
885
893
if (!$ this ->validator ->isAttributeValid ($ attrCode , $ attrParams , $ rowData )) {
886
894
foreach ($ this ->validator ->getMessages () as $ message ) {
887
- $ this ->addRowError ( $ message , $ rowNum , $ attrCode );
895
+ $ this ->skipRow ( $ rowNum , $ message , ProcessingError:: ERROR_LEVEL_NOT_CRITICAL , $ attrCode );
888
896
}
889
897
return false ;
890
898
}
@@ -1592,7 +1600,7 @@ protected function _saveProducts()
1592
1600
if (!empty ($ rowData [self ::URL_KEY ])) {
1593
1601
// If url_key column and its value were in the CSV file
1594
1602
$ rowData [self ::URL_KEY ] = $ urlKey ;
1595
- } else if ($ this ->isNeedToChangeUrlKey ($ rowData )) {
1603
+ } elseif ($ this ->isNeedToChangeUrlKey ($ rowData )) {
1596
1604
// If url_key column was empty or even not declared in the CSV file but by the rules it is need to
1597
1605
// be setteed. In case when url_key is generating from name column we have to ensure that the bunch
1598
1606
// of products will pass for the event with url_key column.
@@ -1604,7 +1612,9 @@ protected function _saveProducts()
1604
1612
if (null === $ rowSku ) {
1605
1613
$ this ->getErrorAggregator ()->addRowToSkip ($ rowNum );
1606
1614
continue ;
1607
- } elseif (self ::SCOPE_STORE == $ rowScope ) {
1615
+ }
1616
+
1617
+ if (self ::SCOPE_STORE == $ rowScope ) {
1608
1618
// set necessary data from SCOPE_DEFAULT row
1609
1619
$ rowData [self ::COL_TYPE ] = $ this ->skuProcessor ->getNewSku ($ rowSku )['type_id ' ];
1610
1620
$ rowData ['attribute_set_id ' ] = $ this ->skuProcessor ->getNewSku ($ rowSku )['attr_set_id ' ];
@@ -1740,13 +1750,7 @@ protected function _saveProducts()
1740
1750
$ uploadedImages [$ columnImage ] = $ uploadedFile ;
1741
1751
} else {
1742
1752
unset($ rowData [$ column ]);
1743
- $ this ->addRowError (
1744
- ValidatorInterface::ERROR_MEDIA_URL_NOT_ACCESSIBLE ,
1745
- $ rowNum ,
1746
- null ,
1747
- null ,
1748
- ProcessingError::ERROR_LEVEL_NOT_CRITICAL
1749
- );
1753
+ $ this ->skipRow ($ rowNum , ValidatorInterface::ERROR_MEDIA_URL_NOT_ACCESSIBLE );
1750
1754
}
1751
1755
} else {
1752
1756
$ uploadedFile = $ uploadedImages [$ columnImage ];
@@ -2380,32 +2384,35 @@ public function validateRow(array $rowData, $rowNum)
2380
2384
// BEHAVIOR_DELETE and BEHAVIOR_REPLACE use specific validation logic
2381
2385
if (Import::BEHAVIOR_REPLACE == $ this ->getBehavior ()) {
2382
2386
if (self ::SCOPE_DEFAULT == $ rowScope && !$ this ->isSkuExist ($ sku )) {
2383
- $ this ->addRowError ( ValidatorInterface::ERROR_SKU_NOT_FOUND_FOR_DELETE , $ rowNum );
2387
+ $ this ->skipRow ( $ rowNum , ValidatorInterface::ERROR_SKU_NOT_FOUND_FOR_DELETE );
2384
2388
return false ;
2385
2389
}
2386
2390
}
2387
2391
if (Import::BEHAVIOR_DELETE == $ this ->getBehavior ()) {
2388
2392
if (self ::SCOPE_DEFAULT == $ rowScope && !$ this ->isSkuExist ($ sku )) {
2389
- $ this ->addRowError ( ValidatorInterface::ERROR_SKU_NOT_FOUND_FOR_DELETE , $ rowNum );
2393
+ $ this ->skipRow ( $ rowNum , ValidatorInterface::ERROR_SKU_NOT_FOUND_FOR_DELETE );
2390
2394
return false ;
2391
2395
}
2392
2396
return true ;
2393
2397
}
2394
2398
2399
+ // if product doesn't exist, need to throw critical error else all errors should be not critical.
2400
+ $ errorLevel = $ this ->getValidationErrorLevel ($ sku );
2401
+
2395
2402
if (!$ this ->validator ->isValid ($ rowData )) {
2396
2403
foreach ($ this ->validator ->getMessages () as $ message ) {
2397
- $ this ->addRowError ( $ message , $ rowNum , $ this ->validator ->getInvalidAttribute ());
2404
+ $ this ->skipRow ( $ rowNum , $ message , $ errorLevel , $ this ->validator ->getInvalidAttribute ());
2398
2405
}
2399
2406
}
2400
2407
2401
2408
if (null === $ sku ) {
2402
- $ this ->addRowError ( ValidatorInterface::ERROR_SKU_IS_EMPTY , $ rowNum );
2409
+ $ this ->skipRow ( $ rowNum , ValidatorInterface::ERROR_SKU_IS_EMPTY , $ errorLevel );
2403
2410
} elseif (false === $ sku ) {
2404
- $ this ->addRowError ( ValidatorInterface::ERROR_ROW_IS_ORPHAN , $ rowNum );
2411
+ $ this ->skipRow ( $ rowNum , ValidatorInterface::ERROR_ROW_IS_ORPHAN , $ errorLevel );
2405
2412
} elseif (self ::SCOPE_STORE == $ rowScope
2406
2413
&& !$ this ->storeResolver ->getStoreCodeToId ($ rowData [self ::COL_STORE ])
2407
2414
) {
2408
- $ this ->addRowError ( ValidatorInterface::ERROR_INVALID_STORE , $ rowNum );
2415
+ $ this ->skipRow ( $ rowNum , ValidatorInterface::ERROR_INVALID_STORE , $ errorLevel );
2409
2416
}
2410
2417
2411
2418
// SKU is specified, row is SCOPE_DEFAULT, new product block begins
@@ -2420,19 +2427,15 @@ public function validateRow(array $rowData, $rowNum)
2420
2427
$ this ->prepareNewSkuData ($ sku )
2421
2428
);
2422
2429
} else {
2423
- $ this ->addRowError ( ValidatorInterface::ERROR_TYPE_UNSUPPORTED , $ rowNum );
2430
+ $ this ->skipRow ( $ rowNum , ValidatorInterface::ERROR_TYPE_UNSUPPORTED , $ errorLevel );
2424
2431
}
2425
2432
} else {
2426
2433
// validate new product type and attribute set
2427
- if (!isset ($ rowData [self ::COL_TYPE ]) || !isset ($ this ->_productTypeModels [$ rowData [self ::COL_TYPE ]])) {
2428
- $ this ->addRowError (ValidatorInterface::ERROR_INVALID_TYPE , $ rowNum );
2429
- } elseif (!isset (
2430
- $ rowData [self ::COL_ATTR_SET ]
2431
- ) || !isset (
2432
- $ this ->_attrSetNameToId [$ rowData [self ::COL_ATTR_SET ]]
2433
- )
2434
+ if (!isset ($ rowData [self ::COL_TYPE ], $ this ->_productTypeModels [$ rowData [self ::COL_TYPE ]])) {
2435
+ $ this ->skipRow ($ rowNum , ValidatorInterface::ERROR_INVALID_TYPE , $ errorLevel );
2436
+ } elseif (!isset ($ rowData [self ::COL_ATTR_SET ], $ this ->_attrSetNameToId [$ rowData [self ::COL_ATTR_SET ]])
2434
2437
) {
2435
- $ this ->addRowError ( ValidatorInterface::ERROR_INVALID_ATTR_SET , $ rowNum );
2438
+ $ this ->skipRow ( $ rowNum , ValidatorInterface::ERROR_INVALID_ATTR_SET , $ errorLevel );
2436
2439
} elseif (is_null ($ this ->skuProcessor ->getNewSku ($ sku ))) {
2437
2440
$ this ->skuProcessor ->addNewSku (
2438
2441
$ sku ,
@@ -2488,8 +2491,11 @@ public function validateRow(array $rowData, $rowNum)
2488
2491
ValidatorInterface::ERROR_DUPLICATE_URL_KEY ,
2489
2492
$ rowNum ,
2490
2493
$ rowData [self ::COL_NAME ],
2491
- $ message
2492
- );
2494
+ $ message ,
2495
+ $ errorLevel
2496
+ )
2497
+ ->getErrorAggregator ()
2498
+ ->addRowToSkip ($ rowNum );
2493
2499
}
2494
2500
}
2495
2501
}
@@ -2499,9 +2505,10 @@ public function validateRow(array $rowData, $rowNum)
2499
2505
$ newFromTimestamp = strtotime ($ this ->dateTime ->formatDate ($ rowData [self ::COL_NEW_FROM_DATE ], false ));
2500
2506
$ newToTimestamp = strtotime ($ this ->dateTime ->formatDate ($ rowData [self ::COL_NEW_TO_DATE ], false ));
2501
2507
if ($ newFromTimestamp > $ newToTimestamp ) {
2502
- $ this ->addRowError (
2503
- ValidatorInterface::ERROR_NEW_TO_DATE ,
2508
+ $ this ->skipRow (
2504
2509
$ rowNum ,
2510
+ ValidatorInterface::ERROR_NEW_TO_DATE ,
2511
+ $ errorLevel ,
2505
2512
$ rowData [self ::COL_NEW_TO_DATE ]
2506
2513
);
2507
2514
}
@@ -3029,4 +3036,39 @@ private function retrieveProductBySku(string $sku)
3029
3036
3030
3037
return $ product ;
3031
3038
}
3039
+
3040
+ /**
3041
+ * Add row as skipped
3042
+ *
3043
+ * @param int $rowNum
3044
+ * @param string $errorCode Error code or simply column name
3045
+ * @param string $errorLevel error level
3046
+ * @param string|null $colName optional column name
3047
+ * @return $this
3048
+ */
3049
+ private function skipRow (
3050
+ int $ rowNum ,
3051
+ string $ errorCode ,
3052
+ string $ errorLevel = ProcessingError::ERROR_LEVEL_NOT_CRITICAL ,
3053
+ $ colName = null
3054
+ ): self {
3055
+ $ this ->addRowError ($ errorCode , $ rowNum , $ colName , null , $ errorLevel );
3056
+ $ this ->getErrorAggregator ()
3057
+ ->addRowToSkip ($ rowNum );
3058
+
3059
+ return $ this ;
3060
+ }
3061
+
3062
+ /**
3063
+ * Returns errorLevel for validation
3064
+ *
3065
+ * @param string|bool|null $sku
3066
+ * @return string
3067
+ */
3068
+ private function getValidationErrorLevel ($ sku ): string
3069
+ {
3070
+ return (!$ this ->isSkuExist ($ sku ) && Import::BEHAVIOR_REPLACE !== $ this ->getBehavior ())
3071
+ ? ProcessingError::ERROR_LEVEL_CRITICAL
3072
+ : ProcessingError::ERROR_LEVEL_NOT_CRITICAL ;
3073
+ }
3032
3074
}
0 commit comments