17

In the following code, I am able to store all the information with the exclusion of the stock data. Has something changed in Magento 2?

public function __construct(
    ScopeConfigInterface $scopeConfig, CollectionFactory $product,
    Magento\Catalog\Api\ProductRepositoryInterface $productRepository
) {
    $this->scopeConfig = $scopeConfig;
    $this->product = $product;
    $this->productRepository = $productRepository;
}

public function update(\XXXXXX\XXXXXX\Api\Data\InventoryCollectionInterface $data) {
    foreach ($data['list'] as $d) {
        $product = $this->productRepository->getById($d['entity_id']);
        $product->setStatus(($d['quantity'] > 0 ? 1 : 0));
        $product->setUpc($d['upc']);
        $product->setStockData(array(
            'qty' => $d['quantity'],
            'is_in_stock' => ($d['quantity'] > 0 ? 1 : 0)
        ));

        $this->productRepository->save($product);
    }

    return "Done";
}
1

12 Answers 12

40

This works for me:

$item = ['qty' => 11]; // For example
$product->setStockData(['qty' => $item['qty'], 'is_in_stock' => $item['qty'] > 0]);
$product->save();

Edit:

This is no longer the correct way to handle this, because $product->save() is deprecated since Magento 2.1. The correct way to do this is by using the StockRegistryInterface:

/**
 * @var StockRegistryInterface
 */
protected $stockRegistry;

/**
 * Inventory constructor.
 * @param StockRegistryInterface $stockRegistry
 */
public function __construct(
    StockRegistryInterface $stockRegistry
)
{
    $this->stockRegistry = $stockRegistry;
    parent::__construct();
}

With the above code, you can use the following:

$sku = 'ABC123';
$qty = 10;
$stockItem = $this->stockRegistry->getStockItemBySku($sku);
$stockItem->setQty($qty);
$this->stockRegistry->updateStockItemBySku($sku, $stockItem);

Use Managers as much as possible. Keep your modules decoupled from each other.

It's The Magento Way™

7
  • setStockData is what is required. There is no need to call ->setQuantityAndStockStatuses, though it seems to do no harm. Commented Nov 15, 2016 at 16:50
  • 15
    It works great for one product. However, when you reading a CSV file with 5k SKUs the performance is pretty bad. Anyone having the same issue?
    – medina
    Commented Feb 23, 2017 at 5:45
  • Really appreciate the Updated Method! Magento is getting better for all of us because of community members like you doing posts like this! Way to go
    – JustinP
    Commented Jan 10, 2019 at 17:30
  • can you help me on this question? magento.stackexchange.com/questions/270215/… Commented Apr 15, 2019 at 21:03
  • Can you help me for this question ? magento.stackexchange.com/questions/279823/…
    – Anil
    Commented Jun 27, 2019 at 9:17
22

If you are using the @giel-berkers solution, you may need to set isInStock also, because it will not be automatically sets. So, the following code was useful for me:

public function __construct(
    \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry
)
{
    $this->stockRegistry = $stockRegistry;
    parent::__construct();
}

public function yourMethod() {
    $sku = 'ABC123';
    $qty = 10;
    $stockItem = $this->stockRegistry->getStockItemBySku($sku);
    $stockItem->setQty($qty);
    $stockItem->setIsInStock((bool)$qty); // this line
    $this->stockRegistry->updateStockItemBySku($sku, $stockItem);
}
3
  • how can i use this answer to update the qty store wise in magento 2?
    – Mujahidh
    Commented Mar 22, 2019 at 11:01
  • @Mujahidh you can try to pass scopeId as a second parameter to the getStockItemBySku() method
    – spiilmusic
    Commented Mar 23, 2019 at 19:14
  • 1
    thanks, qty is updating but not store wise, for both store updating the same qty.
    – Mujahidh
    Commented Mar 25, 2019 at 6:38
8

I struggled with this same issue. While debugging I found that the product data has a quantity_and_stock_status array, so I tried setting it with:

$product->setQuantityAndStockStatus(['qty' => $quantity, 'is_in_stock' => 1]);

and it started working for me. I still am setting the $product->setStockData too if you edit a product and inspect element on the fields you'll see it has both, one is on the general tab, the other on the advanced inventory fields. I have not fully investigated why there are 2 of them.

3
  • sort and simple , very good solution !!! works for me +1 Commented Sep 14, 2017 at 6:49
  • Preferred solution, as this works and takes way less editing than the others +1
    – leedch
    Commented Jan 4, 2018 at 10:48
  • When product is Disable then quantity not update; else in enable status working properly. Can you help me for it. My site is in 2.1.9
    – Anil
    Commented Jun 27, 2019 at 9:38
8

One thing the other answers missed is that if you setQty($qty), it will apply the exact value you provide. But if a sale was made for that product a moment before your save, the original qty could have changed. So what you really want to do is tell Magento the difference you want to apply to the qty.

Fortunately, Magento 2 provides a nice mechanism for this. Take a look at Magento\CatalogInventory\Model\ResourceModel\Stock\Item:

protected function _prepareDataForTable(\Magento\Framework\DataObject $object, $table)
{
    $data = parent::_prepareDataForTable($object, $table);
    $ifNullSql = $this->getConnection()->getIfNullSql('qty');
    if (!$object->isObjectNew() && $object->getQtyCorrection()) {
        if ($object->getQty() === null) {
            $data['qty'] = null;
        } elseif ($object->getQtyCorrection() < 0) {
            $data['qty'] = new \Zend_Db_Expr($ifNullSql . '-' . abs($object->getQtyCorrection()));
        } else {
            $data['qty'] = new \Zend_Db_Expr($ifNullSql . '+' . $object->getQtyCorrection());
        }
    }
    return $data;
}

Here we see that if you set the qty_correction value, it will apply the difference incrementally instead of applying an exact amount.

So my suggestion for a safer qty save is this:

/**
 * @var \Magento\CatalogInventory\Api\StockRegistryInterface
 */
protected $stockRegistry;

public function __construct(StockRegistryInterface $stockRegistry)
{
    $this->stockRegistry = $stockRegistry;
}

/**
* Set the quantity in stock for a product
*
*/
public function applyNewQty($sku, $newQty)
{
    $stockItem = $this->stockRegistry->getStockItemBySku($sku);
    $origQty = $stockItem->getQty();
    $difference = $newQty - $origQty;
    $stockItem->setQtyCorrection($difference);
    $this->stockRegistry->updateStockItemBySku($sku, $stockItem);

    // note that at this point, $stockItem->getQty() is incorrect, so you'll need to reload if you need that value
}
4
$objectManager = $bootstrap->getObjectManager();
$stockRegistry = $objectManager->create('Magento\CatalogInventory\Api\StockRegistryInterface');

$stockItem = $stockRegistry->getStockItemBySku($sku);
$stockItem->setQty($qty);
$stockItem->setIsInStock((bool)$qty);
$stockRegistry->updateStockItemBySku($sku, $stockItem);
3

Magento2 also come up with Multi stock feature so to update on particular stock source you can follow this solution

https://magento.stackexchange.com/questions/272296/how-to-set-qty-to-product-on-msi-magento-2-3
1

Below code is working fine for me to update product qty,

public function __construct(
    \Magento\Catalog\Model\ProductFactory $productFactory
) {
    $this->productFactory = $productFactory;
}

public function updateQty(){
    $sku = '24-mb01';
    $product = $this->productFactory->create();
    $productId = $product->getIdBySku($sku);
    if($productId){
        $product->load($productId);
    }

    $product->setStockData(
        array(
            'use_config_manage_stock' => 0,
            'manage_stock' => 1,
            'is_in_stock' => 1,
            'qty' => 10
        )
    );

    try {
        $product->save(); 
        echo $sku.' updated. '; 
    } catch (Exception $e) {
        echo $e->getException();
    }
}
3
  • When product status is disable then quantity not update in my case. Can you help me for solve it.
    – Anil
    Commented Jun 27, 2019 at 9:42
  • 1
    Calling save on an entity (e,g, product) has been deprecated for a while now. Use a repository or resource model instead. Commented May 28, 2021 at 13:03
  • You can use resource model to resolve this
    – bashid cb
    Commented Sep 13, 2023 at 6:53
1

If you're planning to do this as a mass update for a list of skus and qtys, everyone here has missed on key aspect to setProcessIndexEvents(false);

Code to prevent "The stock item was unable to be saved. Please try again" error would be as follows

$allrecords = []; // Your array of records that need to be updated

foreach ($allrecords as $record) {

  $sku = $record['sku'];
  $qty = $record['qty'];

    try {
            $stockItem = $this->stockRegistry->getStockItemBySku($sku);
        } catch (\Magento\Framework\Exception\NoSuchEntityException $e){
            $stockItem = false;
        }

   if(!$stockItem) continue;

        /* SET STOCK LEVEL */
   $stockItem->setQty($qty);
   /* PREVENT The stock item was unable to be saved. Please try again */
   $stockItem->setProcessIndexEvents(false);  
   $stockItem->setIsInStock((bool)$qty);
   $this->stockRegistry->updateStockItemBySku($sku, $stockItem);
}

Doing this prevents the need to turn your indexes to scheduled only and can be left on update on save.

1

you can use product repository to update:

use Magento\Catalog\Api\ProductRepositoryInterface as ProductRepositoryInterface;

$product = $this->productRepository->getById($productId);

$availableQty = $product->getExtensionAttributes()->getStockItem->setQty($updateQty)->setIsInStock(true);

$this->productRepository->save($product);
0

Try to set StoreId to $product before all, and maybe replace:

$product->setStockData(...) for $product->setData('stock_data', '...') // A Paranoid Recommendation

BTW If you look the Save ActionController at the backend M2 uses a filter for prepare stock_data you could find that filter in:

Magento\Catalog\Controller\Adminhtml\Product\Initialization\StockDataFilter

1
  • I appreciate the response, but that option did not work. Thanks for the help. Commented Jan 9, 2016 at 0:51
0

Try this, while saving the product in admin, they saved inventory data by using event catalog_product_save_after in Magento_CatalogInventory module observer

Magento\CatalogInventory\Observer\SaveInventoryDataObserver

0

I had same issue for magento 2.0.9 and following code is working in my case

$productStock = $this->_productRepository->getById($item->getMageproductId());
$productStock->setQuantityAndStockStatus(['qty' => 12, 'is_in_stock' => 1]);
$res = $productStock->save();

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.