Skip to content

Commit 407f985

Browse files
author
Bomko, Alex(abomko)
committed
Merge pull request #291 from magento-nord/develop
[NORD+TROLL] Bugfixes
2 parents e02c113 + d4016fe commit 407f985

File tree

46 files changed

+1077
-395
lines changed

Some content is hidden

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

46 files changed

+1077
-395
lines changed

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

Lines changed: 123 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,11 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
137137
*/
138138
const INVENTORY_USE_CONFIG_PREFIX = 'use_config_';
139139

140+
/**
141+
* Url key attribute code
142+
*/
143+
const URL_KEY = 'url_key';
144+
140145
/**
141146
* Attribute cache
142147
*
@@ -233,6 +238,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
233238
ValidatorInterface::ERROR_MEDIA_PATH_NOT_ACCESSIBLE => 'Imported resource (image) does not exist in the local media storage',
234239
ValidatorInterface::ERROR_MEDIA_URL_NOT_ACCESSIBLE => 'Imported resource (image) could not be downloaded from external resource due to timeout or access permissions',
235240
ValidatorInterface::ERROR_INVALID_WEIGHT => 'Product weight is invalid',
241+
ValidatorInterface::ERROR_DUPLICATE_URL_KEY => 'Specified url key is already exist',
236242
];
237243

238244
/**
@@ -502,12 +508,24 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
502508
*/
503509
protected $categoryProcessor;
504510

511+
/** @var \Magento\Framework\App\Config\ScopeConfigInterface */
512+
protected $scopeConfig;
513+
514+
/** @var \Magento\Catalog\Model\Product\Url */
515+
protected $productUrl;
516+
505517
/** @var array */
506518
protected $websitesCache = [];
507519

508520
/** @var array */
509521
protected $categoriesCache = [];
510522

523+
/** @var array */
524+
protected $productUrlSuffix = [];
525+
526+
/** @var array */
527+
protected $productUrlKeys = [];
528+
511529
/**
512530
* Instance of product tax class processor.
513531
*
@@ -561,6 +579,12 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
561579
*/
562580
protected $cachedImages = null;
563581

582+
/** @var array */
583+
protected $urlKeys = [];
584+
585+
/** @var array */
586+
protected $rowNumbers = [];
587+
564588
/**
565589
* @var \Magento\Framework\Model\Entity\MetadataPool
566590
*/
@@ -601,6 +625,8 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
601625
* @param ObjectRelationProcessor $objectRelationProcessor
602626
* @param TransactionManagerInterface $transactionManager
603627
* @param Product\TaxClassProcessor $taxClassProcessor
628+
* @param \Magento\Framework\Model\Entity\MetadataPool $metadataPool
629+
* @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
604630
* @param array $data
605631
* @throws \Magento\Framework\Exception\LocalizedException
606632
*
@@ -642,6 +668,8 @@ public function __construct(
642668
TransactionManagerInterface $transactionManager,
643669
Product\TaxClassProcessor $taxClassProcessor,
644670
\Magento\Framework\Model\Entity\MetadataPool $metadataPool,
671+
\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
672+
\Magento\Catalog\Model\Product\Url $productUrl,
645673
array $data = []
646674
) {
647675
$this->_eventManager = $eventManager;
@@ -670,6 +698,8 @@ public function __construct(
670698
$this->transactionManager = $transactionManager;
671699
$this->taxClassProcessor = $taxClassProcessor;
672700
$this->metadataPool = $metadataPool;
701+
$this->scopeConfig = $scopeConfig;
702+
$this->productUrl = $productUrl;
673703
parent::__construct(
674704
$jsonHelper,
675705
$importExportData,
@@ -1933,7 +1963,7 @@ protected function _saveStockItem()
19331963
$productIdsToReindex[] = $row['product_id'];
19341964

19351965
$row['website_id'] = $this->stockConfiguration->getDefaultScopeId();
1936-
$row['stock_id'] = $this->stockRegistry->getStock($row['website_id'])->getStockId();
1966+
$row['stock_id'] = $this->stockRegistry->getStock()->getStockId();
19371967

19381968
$stockItemDo = $this->stockRegistry->getStockItem($row['product_id'], $row['website_id']);
19391969
$existStockData = $stockItemDo->getData();
@@ -1986,11 +2016,8 @@ protected function _saveStockItem()
19862016
*/
19872017
public function retrieveAttributeByCode($attrCode)
19882018
{
1989-
if (!$this->_resource) {
1990-
$this->_resource = $this->_resourceFactory->create();
1991-
}
19922019
if (!isset($this->_attributeCache[$attrCode])) {
1993-
$this->_attributeCache[$attrCode] = $this->_resource->getAttribute($attrCode);
2020+
$this->_attributeCache[$attrCode] = $this->getResource()->getAttribute($attrCode);
19942021
}
19952022
return $this->_attributeCache[$attrCode];
19962023
}
@@ -2201,7 +2228,25 @@ public function validateRow(array $rowData, $rowNum)
22012228
}
22022229
// validate custom options
22032230
$this->getOptionEntity()->validateRow($rowData, $rowNum);
2204-
2231+
if (!empty($rowData[self::URL_KEY]) || !empty($rowData[self::COL_NAME])) {
2232+
$urlKey = $this->getUrlKey($rowData);
2233+
$storeCodes = empty($rowData[self::COL_STORE_VIEW_CODE])
2234+
? array_flip($this->storeResolver->getStoreCodeToId())
2235+
: explode($this->getMultipleValueSeparator(), $rowData[self::COL_STORE_VIEW_CODE]);
2236+
foreach ($storeCodes as $storeCode) {
2237+
$storeId = $this->storeResolver->getStoreCodeToId($storeCode);
2238+
$productUrlSuffix = $this->getProductUrlSuffix($storeId);
2239+
$urlPath = $urlKey . $productUrlSuffix;
2240+
if (empty($this->urlKeys[$storeId][$urlPath])
2241+
|| ($this->urlKeys[$storeId][$urlPath] == $rowData[self::COL_SKU])
2242+
) {
2243+
$this->urlKeys[$storeId][$urlPath] = $rowData[self::COL_SKU];
2244+
$this->rowNumbers[$storeId][$urlPath] = $rowNum;
2245+
} else {
2246+
$this->addRowError(ValidatorInterface::ERROR_DUPLICATE_URL_KEY, $rowNum);
2247+
}
2248+
}
2249+
}
22052250
return !$this->getErrorAggregator()->isRowInvalid($rowNum);
22062251
}
22072252

@@ -2306,7 +2351,79 @@ protected function _saveValidatedBunches()
23062351
$this->validateRow($rowData, $source->key());
23072352
$source->next();
23082353
}
2354+
$this->checkUrlKeyDuplicates();
23092355
$this->getOptionEntity()->validateAmbiguousData();
23102356
return parent::_saveValidatedBunches();
23112357
}
2358+
2359+
/**
2360+
* Check that url_keys are not assigned to other products in DB
2361+
*
2362+
* @return void
2363+
*/
2364+
protected function checkUrlKeyDuplicates()
2365+
{
2366+
$resource = $this->getResource();
2367+
foreach ($this->urlKeys as $storeId => $urlKeys) {
2368+
$urlKeyDuplicates = $this->_connection->fetchAssoc(
2369+
$this->_connection->select()->from(
2370+
['url_rewrite' => $resource->getTable('url_rewrite')],
2371+
['request_path', 'store_id']
2372+
)->joinLeft(
2373+
['cpe' => $resource->getTable('catalog_product_entity')],
2374+
"cpe.entity_id = url_rewrite.entity_id"
2375+
)->where('request_path IN (?)', array_keys($urlKeys))
2376+
->where('store_id IN (?)', $storeId)
2377+
->where('cpe.sku not in (?)', array_values($urlKeys))
2378+
);
2379+
foreach ($urlKeyDuplicates as $entityData) {
2380+
$rowNum = $this->rowNumbers[$entityData['store_id']][$entityData['request_path']];
2381+
$this->addRowError(ValidatorInterface::ERROR_DUPLICATE_URL_KEY, $rowNum);
2382+
}
2383+
}
2384+
}
2385+
2386+
/**
2387+
* Retrieve product rewrite suffix for store
2388+
*
2389+
* @param int $storeId
2390+
* @return string
2391+
*/
2392+
protected function getProductUrlSuffix($storeId = null)
2393+
{
2394+
if (!isset($this->productUrlSuffix[$storeId])) {
2395+
$this->productUrlSuffix[$storeId] = $this->scopeConfig->getValue(
2396+
\Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator::XML_PATH_PRODUCT_URL_SUFFIX,
2397+
\Magento\Store\Model\ScopeInterface::SCOPE_STORE,
2398+
$storeId
2399+
);
2400+
}
2401+
return $this->productUrlSuffix[$storeId];
2402+
}
2403+
2404+
/**
2405+
* @param array $rowData
2406+
* @return string
2407+
*/
2408+
protected function getUrlKey($rowData)
2409+
{
2410+
if (!empty($rowData[self::URL_KEY])) {
2411+
$this->productUrlKeys[$rowData[self::COL_SKU]] = $rowData[self::URL_KEY];
2412+
}
2413+
$urlKey = !empty($this->productUrlKeys[$rowData[self::COL_SKU]])
2414+
? $this->productUrlKeys[$rowData[self::COL_SKU]]
2415+
: $this->productUrl->formatUrlKey($rowData[self::COL_NAME]);
2416+
return $urlKey;
2417+
}
2418+
2419+
/**
2420+
* @return Proxy\Product\ResourceModel
2421+
*/
2422+
protected function getResource()
2423+
{
2424+
if (!$this->_resource) {
2425+
$this->_resource = $this->_resourceFactory->create();
2426+
}
2427+
return $this->_resource;
2428+
}
23122429
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ interface RowValidatorInterface extends \Magento\Framework\Validator\ValidatorIn
7575

7676
const ERROR_MEDIA_PATH_NOT_ACCESSIBLE = 'mediaPathNotAvailable';
7777

78+
const ERROR_DUPLICATE_URL_KEY = 'duplicatedUrlKey';
79+
7880
/**
7981
* Value that means all entities (e.g. websites, groups etc.)
8082
*/

app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,12 @@ class ProductTest extends \Magento\ImportExport\Test\Unit\Model\Import\AbstractI
159159
*/
160160
protected $errorAggregator;
161161

162+
/** @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject*/
163+
protected $scopeConfig;
164+
165+
/** @var \Magento\Catalog\Model\Product\Url|\PHPUnit_Framework_MockObject_MockObject*/
166+
protected $productUrl;
167+
162168
/**
163169
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
164170
*/
@@ -328,6 +334,14 @@ protected function setUp()
328334
false
329335
);
330336

337+
$this->scopeConfig = $this->getMockBuilder('\Magento\Framework\App\Config\ScopeConfigInterface')
338+
->disableOriginalConstructor()
339+
->getMockForAbstractClass();
340+
341+
$this->productUrl = $this->getMockBuilder('\Magento\Catalog\Model\Product\Url')
342+
->disableOriginalConstructor()
343+
->getMock();
344+
331345
$this->errorAggregator = $this->getErrorAggregatorObject();
332346

333347
$this->data = [];
@@ -374,6 +388,8 @@ protected function setUp()
374388
$this->transactionManager,
375389
$this->taxClassProcessor,
376390
$this->metadataPool,
391+
$this->scopeConfig,
392+
$this->productUrl,
377393
$this->data
378394
);
379395
}

app/code/Magento/CatalogImportExport/composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"require": {
55
"php": "~5.5.0|~5.6.0|~7.0.0",
66
"magento/module-catalog": "100.0.*",
7+
"magento/module-catalog-url-rewrite": "100.0.*",
78
"magento/module-eav": "100.0.*",
89
"magento/module-import-export": "100.0.*",
910
"magento/module-store": "100.0.*",

app/code/Magento/CatalogInventory/Api/StockRegistryInterface.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@
1212
interface StockRegistryInterface
1313
{
1414
/**
15-
* @param int $scopeId
15+
* @param int|null $stockId
1616
* @return \Magento\CatalogInventory\Api\Data\StockInterface
1717
*/
18-
public function getStock($scopeId = null);
18+
public function getStock($stockId = null);
1919

2020
/**
2121
* @param int $productId

app/code/Magento/CatalogInventory/Api/StockStatusCriteriaInterface.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,31 +15,31 @@ interface StockStatusCriteriaInterface extends \Magento\Framework\Api\CriteriaIn
1515
* Add Criteria object
1616
*
1717
* @param \Magento\CatalogInventory\Api\StockStatusCriteriaInterface $criteria
18-
* @return bool
18+
* @return void
1919
*/
2020
public function addCriteria(\Magento\CatalogInventory\Api\StockStatusCriteriaInterface $criteria);
2121

2222
/**
2323
* Filter by scope(s)
2424
*
2525
* @param int $scope
26-
* @return bool
26+
* @return void
2727
*/
2828
public function setScopeFilter($scope);
2929

3030
/**
3131
* Add product(s) filter
3232
*
3333
* @param int $products
34-
* @return bool
34+
* @return void
3535
*/
3636
public function setProductsFilter($products);
3737

3838
/**
3939
* Add filter by quantity
4040
*
4141
* @param float $qty
42-
* @return bool
42+
* @return void
4343
*/
4444
public function setQtyFilter($qty);
4545
}

app/code/Magento/CatalogInventory/Helper/Stock.php

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
namespace Magento\CatalogInventory\Helper;
77

88
use Magento\CatalogInventory\Model\Spi\StockRegistryProviderInterface;
9+
use Magento\CatalogInventory\Model\Spi\StockResolverInterface;
910
use Magento\Store\Model\StoreManagerInterface;
1011
use Magento\Framework\App\Config\ScopeConfigInterface;
1112
use Magento\CatalogInventory\Model\ResourceModel\Stock\StatusFactory;
@@ -47,22 +48,30 @@ class Stock
4748
*/
4849
private $stockRegistryProvider;
4950

51+
/**
52+
* @var StockResolverInterface
53+
*/
54+
protected $stockResolver;
55+
5056
/**
5157
* @param StoreManagerInterface $storeManager
5258
* @param ScopeConfigInterface $scopeConfig
5359
* @param StatusFactory $stockStatusFactory
5460
* @param StockRegistryProviderInterface $stockRegistryProvider
61+
* @param StockResolverInterface $stockResolver
5562
*/
5663
public function __construct(
5764
StoreManagerInterface $storeManager,
5865
ScopeConfigInterface $scopeConfig,
5966
StatusFactory $stockStatusFactory,
60-
StockRegistryProviderInterface $stockRegistryProvider
67+
StockRegistryProviderInterface $stockRegistryProvider,
68+
StockResolverInterface $stockResolver
6169
) {
6270
$this->storeManager = $storeManager;
6371
$this->scopeConfig = $scopeConfig;
6472
$this->stockStatusFactory = $stockStatusFactory;
6573
$this->stockRegistryProvider = $stockRegistryProvider;
74+
$this->stockResolver = $stockResolver;
6675
}
6776

6877
/**
@@ -75,8 +84,9 @@ public function __construct(
7584
public function assignStatusToProduct(Product $product, $stockStatus = null)
7685
{
7786
if ($stockStatus === null) {
78-
$websiteId = $product->getStore()->getWebsiteId();
79-
$stockStatus = $this->stockRegistryProvider->getStockStatus($product->getId(), $websiteId);
87+
$productId = $product->getId();
88+
$stockId = $this->stockResolver->getStockId($productId, $product->getStore()->getWebsiteId());
89+
$stockStatus = $this->stockRegistryProvider->getStockStatus($productId, $stockId);
8090
$status = $stockStatus->getStockStatus();
8191
}
8292
$product->setIsSalable($status);
@@ -93,7 +103,8 @@ public function addStockStatusToProducts(AbstractCollection $productCollection)
93103
$websiteId = $this->storeManager->getStore($productCollection->getStoreId())->getWebsiteId();
94104
foreach ($productCollection as $product) {
95105
$productId = $product->getId();
96-
$stockStatus = $this->stockRegistryProvider->getStockStatus($productId, $websiteId);
106+
$stockId = $this->stockResolver->getStockId($productId, $websiteId);
107+
$stockStatus = $this->stockRegistryProvider->getStockStatus($productId, $stockId);
97108
$status = $stockStatus->getStockStatus();
98109
$product->setIsSalable($status);
99110
}

0 commit comments

Comments
 (0)