Magento Product Import Problem – Stock Update Issue – Resolved

November 9th, 2012

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.

<br />foreach ($bunch as $rowNum => $rowData) {<br />    if (!$this->isRowAllowedToImport($rowData, $rowNum)) {<br />    continue;<br />    }<br />    // only SCOPE_DEFAULT can contain stock data<br />    if (self::SCOPE_DEFAULT != $this->getRowScope($rowData)) {<br />    continue;<br />    }<br /><br />    $row['product_id'] = $this->_newSku[$rowData[self::COL_SKU]]['entity_id'];<br />    $row['stock_id'] = 1;<br /><br />    /** @var $stockItem Mage_CatalogInventory_Model_Stock_Item */<br />    $stockItem = Mage::getModel('cataloginventory/stock_item');<br />    $stockItem->loadByProduct($row['product_id']);<br />    $existStockData = $stockItem->getData();<br /><br />    $row = array_merge(<br />           $defaultStockData,<br />           array_intersect_key($existStockData, $defaultStockData),<br />           array_intersect_key($rowData, $defaultStockData),<br />           $row<br />           );<br /><br />    $stockItem->setData($row);<br /><br />    if ($helper->isQty($this->_newSku[$rowData[self::COL_SKU]]['type_id'])) {<br />        if ($stockItem->verifyNotification()) {<br />            $stockItem->setLowStockDate(Mage::app()->getLocale()<br />            ->date(null, null, null, false)<br />            ->toString(Varien_Date::DATETIME_INTERNAL_FORMAT)<br />            );<br />        }<br />        $stockItem->setStockStatusChangedAutomatically((int) !$stockItem->verifyStock());<br />    } else {<br />        $stockItem->setQty(0);<br />    }<br />    $stockData[] = $stockItem->unsetOldData()->getData();<br />}<br />

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.

<br />foreach ($bunch as $rowNum => $rowData) {<br />    if (!$this->isRowAllowedToImport($rowData, $rowNum)) {<br />    continue;<br />    }<br />    // only SCOPE_DEFAULT can contain stock data<br />    if (self::SCOPE_DEFAULT != $this->getRowScope($rowData)) {<br />    continue;<br />    }<br /><br />    // Instantiate $row variable correctly.<br />    $row = array();<br /><br />    $row['product_id'] = $this->_newSku[$rowData[self::COL_SKU]]['entity_id'];<br />    $row['stock_id'] = 1;<br /><br />    /** @var $stockItem Mage_CatalogInventory_Model_Stock_Item */<br />    $stockItem = Mage::getModel('cataloginventory/stock_item');<br />    $stockItem->loadByProduct($row['product_id']);<br />    $existStockData = $stockItem->getData();<br /><br />    $row = array_merge(<br />           $defaultStockData,<br />           array_intersect_key($existStockData, $defaultStockData),<br />           array_intersect_key($rowData, $defaultStockData),<br />           $row<br />           );<br /><br />    $stockItem->setData($row);<br /><br />    if ($helper->isQty($this->_newSku[$rowData[self::COL_SKU]]['type_id'])) {<br />        if ($stockItem->verifyNotification()) {<br />            $stockItem->setLowStockDate(Mage::app()->getLocale()<br />            ->date(null, null, null, false)<br />            ->toString(Varien_Date::DATETIME_INTERNAL_FORMAT)<br />            );<br />        }<br />        $stockItem->setStockStatusChangedAutomatically((int) !$stockItem->verifyStock());<br />    } else {<br />        $stockItem->setQty(0);<br />    }<br />    $stockData[] = $stockItem->unsetOldData()->getData();<br />}<br />

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.

admin

About admin

  • jcdr

    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.

  • Sean

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

  • Nic

    Thanks, saved me a lot of time!

  • Stan

    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!

  • Tom

    Thanks, saved me a lot of time, too!

  • http://gawebdev.com Mark

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

  • Rishabh Srivastava

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

  • Pingback: Проблема с импортом наличия товаров в Magento | Magento fast

  • http://www.magefast.com Magefast.com

    Thank you for resolve this BUG.

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

    Thaks you!

    • http://www.logicspot.com/about-us/team/mark/ Mark

      Fantastic! Glad this has been useful to you :-)

  • http://fashion4you.in Rishabh Srivastava

    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

    • http://fashion4you.in Rishabh Srivastava

      please short out this.

  • Victoria

    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

  • shashank

    Great solution. It works like butter.

  • Tyler

    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”

    • Tyler

      To clarify, this happens when i attempt to import a CSV that previously worked except for the stock qty.

  • http://www.iggone.fr iggOne

    Hi, I upload your fix files but it does not fix the problem.

  • Jayson CAin

    Hello, I also uploaded the fix files, but it still doesn’t update the inventory