Magento Product Import Problem – Stock Update Issue – Resolved

The Magento product import is a handy tool used to update and create products from a CSV file. A problem exists however with this import that can be quite damaging to the stock data for all the products referenced in the import file. To clarify the import process in question, it is accessed through the following menu item in the Magento admin panel.


The problem:

When an import is run, the stock data for the product on the first line of the CSV is replicated for all other products in the file, regardless of what inventory data you set in the import file itself. The obvious issue is that this can cause the quantity’s of products to change, the stock status and more, which can be very damaging to business activity.

This issue is evident in all community editions and enterprise version 1.12. It is thought that it is fixed in later editions and also in Magento2. We can see the fix for this issue in place for Magento2 if we look at the code on GitHub here.

Lets pretend we don’t know the fix (as seen on Magento2) and take a look at the code.

File: app/code/core/Mage/ImportExport/Model/Import/Entity/Product.php
Class: Mage_ImportExport_Model_Import_Entity_Product

Navigate to the file above in your Magento install, then search for the following function:

protected function _saveStockItem()

This function is used to save the stock data for items in the import. The part we are interested in is the point that the stock data is saved. You will notice a foreach loop, this is looping through all the products in the import and saving the stock data, so the problem must be inside here.

foreach ($bunch as $rowNum => $rowData) {
    if (!$this->isRowAllowedToImport($rowData, $rowNum)) {
    continue;
    }
    // only SCOPE_DEFAULT can contain stock data
    if (self::SCOPE_DEFAULT != $this->getRowScope($rowData)) {
    continue;
    }

    $row['product_id'] = $this->_newSku[$rowData[self::COL_SKU]]['entity_id'];
    $row['stock_id'] = 1;

    /** @var $stockItem Mage_CatalogInventory_Model_Stock_Item */
    $stockItem = Mage::getModel('cataloginventory/stock_item');
    $stockItem->loadByProduct($row['product_id']);
    $existStockData = $stockItem->getData();

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

    $stockItem->setData($row);

    if ($helper->isQty($this->_newSku[$rowData[self::COL_SKU]]['type_id'])) {
        if ($stockItem->verifyNotification()) {
            $stockItem->setLowStockDate(Mage::app()->getLocale()
            ->date(null, null, null, false)
            ->toString(Varien_Date::DATETIME_INTERNAL_FORMAT)
            );
        }
        $stockItem->setStockStatusChangedAutomatically((int) !$stockItem->verifyStock());
    } else {
        $stockItem->setQty(0);
    }
    $stockData[] = $stockItem->unsetOldData()->getData();
}

The first thing to notice is the $row variable, with values being set on it without it being instantiated. This is not usual practise with arrays so lets investigate this variable further.

The $row variable is next used in an array_merge where default stock data is merged / overwritten by existing stock data, which is overwritten with stockdata found in the import and then finally overwritten by $row again.

Heres the problem, after the first cycle, $row is set as a full stock data array, and we can see that its going to overwrite all future stock data no matter what is set for that product.

So now we have found the problem, lets build a solution. The required fix for this issue is to properly instatiate the $row variable inside the foreach loop. This will ensure that we are not carrying any old stock data for previous products.

foreach ($bunch as $rowNum => $rowData) {
    if (!$this->isRowAllowedToImport($rowData, $rowNum)) {
    continue;
    }
    // only SCOPE_DEFAULT can contain stock data
    if (self::SCOPE_DEFAULT != $this->getRowScope($rowData)) {
    continue;
    }

    // Instantiate $row variable correctly.
    $row = array();

    $row['product_id'] = $this->_newSku[$rowData[self::COL_SKU]]['entity_id'];
    $row['stock_id'] = 1;

    /** @var $stockItem Mage_CatalogInventory_Model_Stock_Item */
    $stockItem = Mage::getModel('cataloginventory/stock_item');
    $stockItem->loadByProduct($row['product_id']);
    $existStockData = $stockItem->getData();

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

    $stockItem->setData($row);

    if ($helper->isQty($this->_newSku[$rowData[self::COL_SKU]]['type_id'])) {
        if ($stockItem->verifyNotification()) {
            $stockItem->setLowStockDate(Mage::app()->getLocale()
            ->date(null, null, null, false)
            ->toString(Varien_Date::DATETIME_INTERNAL_FORMAT)
            );
        }
        $stockItem->setStockStatusChangedAutomatically((int) !$stockItem->verifyStock());
    } else {
        $stockItem->setQty(0);
    }
    $stockData[] = $stockItem->unsetOldData()->getData();
}

Now we have the fix in place. The Logic Spot team have put together a module which you can download and paste on your Magento installation which will solve this problem.

Download the fix here.

Simon

About Simon

Simon has over 7 years web development experience. Along the way he has become a master of photoshop and highly skilled in web programming

November 9th, 2012 / Tags: / Comments:

16 Responses to “Magento Product Import Problem – Stock Update Issue – Resolved”

  1. jcdr says:

    Brilliant!
    I’d been struggling for hours before stumbling on your post. Can’t believe this bug has not been fixed yet in Magento 1.7.

  2. Sean says:

    Excellent fix! Saved a lot of frustration – many thanks!

  3. Nic says:

    Thanks, saved me a lot of time!

  4. Stan says:

    Thank you for the very logical explanation for this (and kudos for digging through the code). I found a cheap fix elsewhere before by just adding the line:

    $row['qty'] = $rowData['qty'];

    which I suspected wouldn’t work long run. Sure enough when you import without qty column, Magento got angry and threw red error messages at me. Decided to try isset() and tested it again – and all my products’ qty got overwritten by 0.

    This simple line from you actually stamps out the bug properly since it looks at the big picture, so once again thank you!

  5. Tom says:

    Thanks, saved me a lot of time, too!

  6. Mark says:

    Thanks a million. Applied the fix to Magento ver. 1.7.0.2 & works like a charm!

  7. Rishabh Srivastava says:

    Waoooooooo……….Really Brilliant.This is very helpful for me.

  8. […] Скачать модуль LogicSpot_ImportExport и почитать о решении можно по ссылке — http://www.logicspot.com/magento/product-import-problem-stock-update-issue/ […]

  9. Magefast.com says:

    Thank you for resolve this BUG.

    Added post on russian, about solution
    http://www.magefast.com/problem-import-qty-magento/

    Thaks you!

  10. how we assigned customer into a new group according their email’s domain at the time of registration in magento?
    means i already created a customer group named ‘xxx’
    and now if customer enter his email address ‘jhon@xxx.com’ at the time of registration or create a new account,then he automatically assigned into ‘xxx’ group according his email’s domain name

  11. Victoria says:

    Would really appreciate if someone could answer this:

    I work as a developer and we have had a shop live for more than a month now. We’ve probably imported more than 2000 products through CSV and never encountered this issue until a week ago. We have not updated anything or changed any code what so ever.

    Does anyone have a clue why this bug first appeared now?

    We use version 1.7

  12. shashank says:

    Great solution. It works like butter.

  13. Tyler says:

    Thank you for posting this – I was sure it would resolve my issues. After extracting the ZIP provided I get the following error:

    “Entity adapter object must be an instance of Mage_ImportExport_Model_Import_Entity_Abstract”

Leave a Reply

Current day month ye@r *