Skip to content

Commit 27076ca

Browse files
authored
Merge pull request #5230 from magento-tsg-csl3/2.4-develop-pr9
[TSG-CSL3] For 2.4 (pr9)
2 parents 420a8b6 + afc0623 commit 27076ca

File tree

40 files changed

+1151
-196
lines changed

40 files changed

+1151
-196
lines changed

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

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
use Magento\CatalogImportExport\Model\Import\Product\ImageTypeProcessor;
1414
use Magento\CatalogImportExport\Model\Import\Product\MediaGalleryProcessor;
1515
use Magento\CatalogImportExport\Model\Import\Product\RowValidatorInterface as ValidatorInterface;
16+
use Magento\CatalogImportExport\Model\Import\Product\StatusProcessor;
17+
use Magento\CatalogImportExport\Model\Import\Product\StockProcessor;
1618
use Magento\CatalogImportExport\Model\StockItemImporterInterface;
1719
use Magento\CatalogInventory\Api\Data\StockItemInterface;
1820
use Magento\Framework\App\Filesystem\DirectoryList;
@@ -746,6 +748,15 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
746748
*/
747749
private $productRepository;
748750

751+
/**
752+
* @var StatusProcessor
753+
*/
754+
private $statusProcessor;
755+
/**
756+
* @var StockProcessor
757+
*/
758+
private $stockProcessor;
759+
749760
/**
750761
* @param \Magento\Framework\Json\Helper\Data $jsonHelper
751762
* @param \Magento\ImportExport\Helper\Data $importExportData
@@ -791,6 +802,8 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
791802
* @param StockItemImporterInterface|null $stockItemImporter
792803
* @param DateTimeFactory $dateTimeFactory
793804
* @param ProductRepositoryInterface|null $productRepository
805+
* @param StatusProcessor|null $statusProcessor
806+
* @param StockProcessor|null $stockProcessor
794807
* @throws LocalizedException
795808
* @throws \Magento\Framework\Exception\FileSystemException
796809
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
@@ -840,7 +853,9 @@ public function __construct(
840853
MediaGalleryProcessor $mediaProcessor = null,
841854
StockItemImporterInterface $stockItemImporter = null,
842855
DateTimeFactory $dateTimeFactory = null,
843-
ProductRepositoryInterface $productRepository = null
856+
ProductRepositoryInterface $productRepository = null,
857+
StatusProcessor $statusProcessor = null,
858+
StockProcessor $stockProcessor = null
844859
) {
845860
$this->_eventManager = $eventManager;
846861
$this->stockRegistry = $stockRegistry;
@@ -876,6 +891,10 @@ public function __construct(
876891
$this->mediaProcessor = $mediaProcessor ?: ObjectManager::getInstance()->get(MediaGalleryProcessor::class);
877892
$this->stockItemImporter = $stockItemImporter ?: ObjectManager::getInstance()
878893
->get(StockItemImporterInterface::class);
894+
$this->statusProcessor = $statusProcessor ?: ObjectManager::getInstance()
895+
->get(StatusProcessor::class);
896+
$this->stockProcessor = $stockProcessor ?: ObjectManager::getInstance()
897+
->get(StockProcessor::class);
879898
parent::__construct(
880899
$jsonHelper,
881900
$importExportData,
@@ -1290,12 +1309,18 @@ protected function _saveLinks()
12901309
protected function _saveProductAttributes(array $attributesData)
12911310
{
12921311
$linkField = $this->getProductEntityLinkField();
1312+
$statusAttributeId = (int) $this->retrieveAttributeByCode('status')->getId();
12931313
foreach ($attributesData as $tableName => $skuData) {
1314+
$linkIdBySkuForStatusChanged = [];
12941315
$tableData = [];
12951316
foreach ($skuData as $sku => $attributes) {
12961317
$linkId = $this->_oldSku[strtolower($sku)][$linkField];
12971318
foreach ($attributes as $attributeId => $storeValues) {
12981319
foreach ($storeValues as $storeId => $storeValue) {
1320+
if ($attributeId === $statusAttributeId) {
1321+
$this->statusProcessor->setStatus($sku, $storeId, $storeValue);
1322+
$linkIdBySkuForStatusChanged[strtolower($sku)] = $linkId;
1323+
}
12991324
$tableData[] = [
13001325
$linkField => $linkId,
13011326
'attribute_id' => $attributeId,
@@ -1305,6 +1330,9 @@ protected function _saveProductAttributes(array $attributesData)
13051330
}
13061331
}
13071332
}
1333+
if ($linkIdBySkuForStatusChanged) {
1334+
$this->statusProcessor->loadOldStatus($linkIdBySkuForStatusChanged);
1335+
}
13081336
$this->_connection->insertOnDuplicate($tableName, $tableData, ['value']);
13091337
}
13101338

@@ -2188,6 +2216,7 @@ protected function _saveStockItem()
21882216
while ($bunch = $this->_dataSourceModel->getNextBunch()) {
21892217
$stockData = [];
21902218
$productIdsToReindex = [];
2219+
$stockChangedProductIds = [];
21912220
// Format bunch to stock data rows
21922221
foreach ($bunch as $rowNum => $rowData) {
21932222
if (!$this->isRowAllowedToImport($rowData, $rowNum)) {
@@ -2197,8 +2226,16 @@ protected function _saveStockItem()
21972226
$row = [];
21982227
$sku = $rowData[self::COL_SKU];
21992228
if ($this->skuProcessor->getNewSku($sku) !== null) {
2229+
$stockItem = $this->getRowExistingStockItem($rowData);
2230+
$existingStockItemData = $stockItem->getData();
22002231
$row = $this->formatStockDataForRow($rowData);
22012232
$productIdsToReindex[] = $row['product_id'];
2233+
$storeId = $this->getRowStoreId($rowData);
2234+
if (!empty(array_diff_assoc($row, $existingStockItemData))
2235+
|| $this->statusProcessor->isStatusChanged($sku, $storeId)
2236+
) {
2237+
$stockChangedProductIds[] = $row['product_id'];
2238+
}
22022239
}
22032240

22042241
if (!isset($stockData[$sku])) {
@@ -2211,11 +2248,24 @@ protected function _saveStockItem()
22112248
$this->stockItemImporter->import($stockData);
22122249
}
22132250

2251+
$this->reindexStockStatus($stockChangedProductIds);
22142252
$this->reindexProducts($productIdsToReindex);
22152253
}
22162254
return $this;
22172255
}
22182256

2257+
/**
2258+
* Reindex stock status for provided product IDs
2259+
*
2260+
* @param array $productIds
2261+
*/
2262+
private function reindexStockStatus(array $productIds): void
2263+
{
2264+
if ($productIds) {
2265+
$this->stockProcessor->reindexList($productIds);
2266+
}
2267+
}
2268+
22192269
/**
22202270
* Initiate product reindex by product ids
22212271
*
@@ -3259,4 +3309,30 @@ private function composeLinkKey(int $productId, int $linkedId, int $linkTypeId)
32593309
{
32603310
return "{$productId}-{$linkedId}-{$linkTypeId}";
32613311
}
3312+
3313+
/**
3314+
* Get row store ID
3315+
*
3316+
* @param array $rowData
3317+
* @return int
3318+
*/
3319+
private function getRowStoreId(array $rowData): int
3320+
{
3321+
return !empty($rowData[self::COL_STORE])
3322+
? (int) $this->getStoreIdByCode($rowData[self::COL_STORE])
3323+
: Store::DEFAULT_STORE_ID;
3324+
}
3325+
3326+
/**
3327+
* Get row stock item model
3328+
*
3329+
* @param array $rowData
3330+
* @return StockItemInterface
3331+
*/
3332+
private function getRowExistingStockItem(array $rowData): StockItemInterface
3333+
{
3334+
$productId = $this->skuProcessor->getNewSku($rowData[self::COL_SKU])['entity_id'];
3335+
$websiteId = $this->stockConfiguration->getDefaultScopeId();
3336+
return $this->stockRegistry->getStockItem($productId, $websiteId);
3337+
}
32623338
}
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\CatalogImportExport\Model\Import\Product;
9+
10+
use Magento\Catalog\Api\Data\ProductInterface;
11+
use Magento\CatalogImportExport\Model\Import\Proxy\Product\ResourceModelFactory;
12+
use Magento\Eav\Model\Entity\Attribute\AbstractAttribute;
13+
use Magento\Framework\App\ResourceConnection;
14+
use Magento\Framework\EntityManager\MetadataPool;
15+
16+
/**
17+
* Imported product status state manager
18+
*/
19+
class StatusProcessor
20+
{
21+
private const ATTRIBUTE_CODE = 'status';
22+
/**
23+
* @var array
24+
*/
25+
private $oldData;
26+
/**
27+
* @var array
28+
*/
29+
private $newData;
30+
/**
31+
* @var ResourceModelFactory
32+
*/
33+
private $resourceFactory;
34+
/**
35+
* @var ResourceConnection
36+
*/
37+
private $resourceConnection;
38+
/**
39+
* @var MetadataPool
40+
*/
41+
private $metadataPool;
42+
/**
43+
* @var string
44+
*/
45+
private $productEntityLinkField;
46+
/**
47+
* @var AbstractAttribute
48+
*/
49+
private $attribute;
50+
51+
/**
52+
* Initializes dependencies.
53+
*
54+
* @param MetadataPool $metadataPool
55+
* @param ResourceModelFactory $resourceFactory
56+
* @param ResourceConnection $resourceConnection
57+
*/
58+
public function __construct(
59+
MetadataPool $metadataPool,
60+
ResourceModelFactory $resourceFactory,
61+
ResourceConnection $resourceConnection
62+
) {
63+
$this->oldData = [];
64+
$this->newData = [];
65+
$this->resourceFactory = $resourceFactory;
66+
$this->resourceConnection = $resourceConnection;
67+
$this->metadataPool = $metadataPool;
68+
}
69+
70+
/**
71+
* Check if status has changed for given (sku, storeId)
72+
*
73+
* @param string $sku
74+
* @param int $storeId
75+
* @return bool
76+
*/
77+
public function isStatusChanged(string $sku, int $storeId): bool
78+
{
79+
$sku = strtolower($sku);
80+
if (!isset($this->newData[$sku][$storeId])) {
81+
$changed = false;
82+
} elseif (!isset($this->oldData[$sku][$storeId])) {
83+
$changed = true;
84+
} else {
85+
$oldStatus = (int) $this->oldData[$sku][$storeId];
86+
$newStatus = (int) $this->newData[$sku][$storeId];
87+
$changed = $oldStatus !== $newStatus;
88+
}
89+
return $changed;
90+
}
91+
92+
/**
93+
* Load old status data
94+
*
95+
* @param array $linkIdBySku
96+
*/
97+
public function loadOldStatus(array $linkIdBySku): void
98+
{
99+
$connection = $this->resourceConnection->getConnection();
100+
$linkId = $this->getProductEntityLinkField();
101+
$select = $connection->select()
102+
->from($this->getAttribute()->getBackend()->getTable())
103+
->columns([$linkId, 'store_id', 'value'])
104+
->where(sprintf('%s IN (?)', $linkId), array_values($linkIdBySku));
105+
$skuByLinkId = array_flip($linkIdBySku);
106+
107+
foreach ($connection->fetchAll($select) as $item) {
108+
if (isset($skuByLinkId[$item[$linkId]])) {
109+
$this->oldData[$skuByLinkId[$item[$linkId]]][$item['store_id']] = $item['value'];
110+
}
111+
}
112+
}
113+
114+
/**
115+
* Set SKU status for given storeId
116+
*
117+
* @param string $sku
118+
* @param string $storeId
119+
* @param int $value
120+
*/
121+
public function setStatus(string $sku, string $storeId, int $value): void
122+
{
123+
$sku = strtolower($sku);
124+
$this->newData[$sku][$storeId] = $value;
125+
}
126+
127+
/**
128+
* Get product entity link field.
129+
*
130+
* @return string
131+
*/
132+
private function getProductEntityLinkField()
133+
{
134+
if (!$this->productEntityLinkField) {
135+
$this->productEntityLinkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField();
136+
}
137+
138+
return $this->productEntityLinkField;
139+
}
140+
141+
/**
142+
* Get Attribute model
143+
*
144+
* @return AbstractAttribute
145+
*/
146+
private function getAttribute(): AbstractAttribute
147+
{
148+
if ($this->attribute === null) {
149+
$this->attribute = $this->resourceFactory->create()->getAttribute(self::ATTRIBUTE_CODE);
150+
}
151+
return $this->attribute;
152+
}
153+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\CatalogImportExport\Model\Import\Product;
9+
10+
use Magento\Framework\Indexer\IndexerRegistry;
11+
12+
/**
13+
* Imported product stock manager
14+
*/
15+
class StockProcessor
16+
{
17+
/**
18+
* @var IndexerRegistry
19+
*/
20+
private $indexerRegistry;
21+
/**
22+
* @var array
23+
*/
24+
private $indexers;
25+
26+
/**
27+
* Initializes dependencies.
28+
*
29+
* @param IndexerRegistry $indexerRegistry
30+
* @param array $indexers
31+
*/
32+
public function __construct(
33+
IndexerRegistry $indexerRegistry,
34+
array $indexers = []
35+
) {
36+
$this->indexerRegistry = $indexerRegistry;
37+
$this->indexers = array_filter($indexers);
38+
}
39+
40+
/**
41+
* Reindex products by ids
42+
*
43+
* @param array $ids
44+
* @return void
45+
*/
46+
public function reindexList(array $ids = []): void
47+
{
48+
if ($ids) {
49+
foreach ($this->indexers as $indexerName) {
50+
$indexer = $this->indexerRegistry->get($indexerName);
51+
if (!$indexer->isScheduled()) {
52+
$indexer->reindexList($ids);
53+
}
54+
}
55+
}
56+
}
57+
}

0 commit comments

Comments
 (0)