Skip to content

Extension Point of Product Import Export

Josh Carter edited this page Nov 24, 2017 · 3 revisions

MSI Product Import/Export

One story to consider with MSI is how an admin user imports source item data via the Product Import/Export. To achieve this, we have added an extension point to the Magento Catalog Import Export.

\Magento\CatalogImportExport\Model\Import\Product::_saveStockItem()

/**
 * Stock item saving.
 *
 * @return $this
 */
protected function _saveStockItem()
{
    /** @var $stockResource \Magento\CatalogInventory\Model\ResourceModel\Stock\Item */
    $stockResource = $this->_stockResItemFac->create();
    $entityTable = $stockResource->getMainTable();
    while ($bunch = $this->_dataSourceModel->getNextBunch()) {
        $stockData = [];
        $productIdsToReindex = [];
        // Format bunch to stock data rows
        foreach ($bunch as $rowNum => $rowData) {
            if (!$this->isRowAllowedToImport($rowData, $rowNum)) {
                continue;
            }

            $row = [];
            $sku = $rowData[self::COL_SKU];
            if ($this->skuProcessor->getNewSku($sku) !== null) {
                $row['product_id'] = $this->skuProcessor->getNewSku($sku)['entity_id'];
                $productIdsToReindex[] = $row['product_id'];

                $row['website_id'] = $this->stockConfiguration->getDefaultScopeId();
                $row['stock_id'] = $this->stockRegistry->getStock($row['website_id'])->getStockId();

                $stockItemDo = $this->stockRegistry->getStockItem($row['product_id'], $row['website_id']);
                $existStockData = $stockItemDo->getData();

                $row = array_merge(
                    $this->defaultStockData,
                    array_intersect_key($existStockData, $this->defaultStockData),
                    array_intersect_key($rowData, $this->defaultStockData),
                    $row
                );

                if ($this->stockConfiguration->isQty(
                    $this->skuProcessor->getNewSku($sku)['type_id']
                )
                ) {
                    $stockItemDo->setData($row);
                    $row['is_in_stock'] = $this->stockStateProvider->verifyStock($stockItemDo);
                    if ($this->stockStateProvider->verifyNotification($stockItemDo)) {
                        $row['low_stock_date'] = $this->dateTime->gmDate(
                            'Y-m-d H:i:s',
                            (new \DateTime())->getTimestamp()
                        );
                    }
                    $row['stock_status_changed_auto'] =
                        (int)!$this->stockStateProvider->verifyStock($stockItemDo);
                } else {
                    $row['qty'] = 0;
                }
            }

            if (!isset($stockData[$sku])) {
                $stockData[$sku] = $row;
            }
        }

        // Insert rows
        if (!empty($stockData)) {
            $this->_connection->insertOnDuplicate($entityTable, array_values($stockData));
            $this->stockItemImporter->import($bunch); // Extension Point for MSI Source Item Importing
        }

        $this->reindexProducts($productIdsToReindex);
    }
    return $this;
}

Our extension point is:

$this->stockItemImporter->import($bunch); // Extension Point for MSI Source Item Importing

Where stockItemImporter is an instance of \Magento\CatalogImportExport\Model\StockItemImporterInterface

We than have a preference inside Mageno_InventoryImportExport:

<preference for="\Magento\CatalogImportExport\Model\StockItemImporterInterface" 
type="\Magento\InventoryImportExport\Model\StockItemImporter" />

So we inject the new MSI class, \Magento\InventoryImportExport\Model\StockItemImporter and move the operations to the MSI APIs. For example, we use the following to process the qty for source item now:

/**
 * Handle Import of Stock Item Data
 *
 * @param array $stockData
 * @return void
 */
public function import(array $stockData)
{
    $sourceItems = [];
    foreach ($stockData as $stockDatum) {
        if (isset($stockDatum[Product::COL_SKU])) {
            $inStock = (isset($stockDatum['is_in_stock'])) ? $stockDatum['is_in_stock'] : 0;
            $qty = (isset($stockDatum['qty'])) ? $stockDatum['qty'] : 0;
            $sourceItem = $this->sourceItemFactory->create();
            $sourceItem->setSku($stockDatum[Product::COL_SKU]);
            $sourceItem->setSourceId($this->defaultSource->getId());
            $sourceItem->setQuantity($qty);
            $sourceItem->setStatus($inStock);
            $sourceItems[] = $sourceItem;
        }
    }
    if (count($sourceItems) > 0) {
        /** Magento\Inventory\Model\SourceItem[] $sourceItems */
        $this->sourceItemsSave->execute($sourceItems);
    }
}

Where $this->sourceItemsSave is an instance of MSI's \Magento\InventoryApi\Api\SourceItemsSaveInterface.

Future Changes

The idea is that eventually we can use this extension point to convert the whole _saveStockItem() function to here for the new MSI functionality.

At the time of writing this, we have only just started the story, the importing of MSI Source items has now been implemented as part of the Product Import/Export, but only for the Default Source. Next steps are to handle the different potential formatting of the qty column to be able to import multiple source's items at one. See the relevant story for more information on the different proposed quantity column formats - Update Product import export to include inventory source

MSI Documentation:

  1. Technical Vision. Catalog Inventory
  2. Installation Guide
  3. List of Inventory APIs and their legacy analogs
  4. MSI Roadmap
  5. Known Issues in Order Lifecycle
  6. MSI User Guide
  7. DevDocs Documentation
  8. User Stories
  9. User Scenarios:
  10. Technical Designs:
  11. Admin UI
  12. MFTF Extension Tests
  13. Weekly MSI Demos
  14. Tutorials
Clone this wiki locally