0

How to get stock qty after the product in saved via plugin?

With code bellow I allways get "stock qty" 0 no matter what.

di.xml:

    <type name="Magento\Catalog\Model\ResourceModel\Product">
        <plugin name="Vendor_Modul::CheckProductStockStatus" type="Vendor\Modul\Plugin\Model\ProductCheckStock" sortOrder="999"/>
    </type>

Vendor\Modul\Plugin\Model\ProductCheckStock:

    public function afterSave(ProductResourceModel $subject, ProductResourceModel $result, Product $product)
    {

        $stockQty = $this->stockState->getStockQty($product->getId());
        var_dump($stockQty, $product->getQty()); die();
       return $result;
    }

2 Answers 2

1

Intro

MSI stock works differently from the previous CatalogInventory stock. To explain how to get the stock properly i first need to explain how MSI works.

However i will state this beforehand, it is impossible to get a different stock based on storeviews. It is only possible to get it per website, as i will explain below:

In Magento 2.3 when using MSI you can have multiple "Stocks" which each have their own "Sources".

Stock explained

A "Stock" is pretty much your total inventory for one or more websites. Each "Stock" can be assigned to multiple websites, however a website can only have a single "Stock" as this encompasses all of your stock for that website, regardless of the "Source". Within a "Stock" you can have multiple "Sources".

Sources explained

A "Source" is pretty much a warehouse, with a location and it's own stock. Each "Source" can be configured per product. It is possible to leave sources empty, however it is not possible to change the stock on a storeview level, it is always global for the websites your source's stock is linked to.

Possible Solution

The comment of Prabhu M. was pretty close but did not explain why you should use this.

Magento\InventoryApi\Api\GetSourceItemsBySkuInterface

This code gets the "Source Items" for a product based on the SKU. It does so for all sources, so if lets say you have 2 sources (warehouses) for a specific website, it will retrieve both of them. It will also do the same for other websites.

Example:

Lets say you have 2 websites.

  1. website1.nl
  2. website2.com

For website 1 you have it linked to the "Stock": Stock NL

For "Stock NL" you have 3 sources linked to it:

  1. Warehouse Rotterdam (33 stock) [code: rotterdam]
  2. Warehouse Amsterdam (0 stock) [code: amsterdam]
  3. Warehouse Leiden (5 stock) [code: leiden]

For website 2 you have it linked to the "Stock": Stock EN

For "Stock EN" you have 1 sources linked to it:

  1. Warehouse New York (0 stock) [code: new_york]

To get the data above you can use the following code:

use Magento\Inventory\Model\SourceItem\Command\GetSourceItemsBySku;
...    
public function __construct(
    GetSourceItemsBySku $getSourceItemsBySku
) {
    $this->getSourceItemsBySku = $getSourceItemsBySku;
}

...
function doSomething($product) {
    $sourceItems = $this->getSourceItemsBySku->execute($product->getSku());

    foreach ($sourceItems as $sourceItemId => $sourceItem) {
        print $sourceItem->getSku() . ','; 
        print $sourceItem->getSourceCode() . ','; // The actual source code, e.g: warehouse_rotterdam
        print $sourceItem->getStatus() . ','; // wether it is sellable
        print $sourceItem->getQuantity() . '<br/>'; // the quantity for the current source
    }
}
...

This will return the follow data:

testproduct, rotterdam, 1 (true), 33

testproduct, amsterdam, 0 (false), 0

testproduct, leiden, 1 (true), 5

testproduct, new_york, 1 (false), 0

In case of Stock NL your total quantity would be 38 and it would be sellable.

In case of Stock EN your total quantity would be 0 and it would not be sellable. (as it has no sources with stock)

Do note that this does not provide you with any indication of the website it is linked to.

If you know which sources you want to get it for you could use the following to get the stock:

use Magento\InventorySourceDeductionApi\Model\GetSourceItemBySourceCodeAndSku;
...
public function __construct(
    GetSourceItemBySourceCodeAndSku $getSourceItemBySourceCodeAndSku
) {
    $this->getSourceItemBySourceCodeAndSku = $getSourceItemBySourceCodeAndSku;
}
...
public function doSomething($product, $source) {
    $sourceItem = $this->getSourceItemBySourceCodeAndSku->execute($sourceCode, $itemSku);

    print $sourceItem->getSku() . ',';
    print $sourceItem->getSourceCode() . ','; // The actual source code, e.g: warehouse_rotterdam
    print $sourceItem->getStatus() . ','; // wether it is salable
    print $sourceItem->getQuantity() . '<br/>'; // the quantity for the 
}
...
3
  • Thank you for answer. I am not using MSI stock and I think I will not use it in near future. Any idea how to do it with MSI stock disabled? Or at least what is wrong with my approach. Jun 13 at 13:58
  • 1
    still you need to achive through above code. Jun 14 at 8:23
  • Thank you. Helpful answer. Tnx for all explanation. Jun 15 at 8:28
0

With your logic and your place i think you can use something like

<?php
declare(strict_types=1);

namespace Acme\StackExchange\Plugin\Catalog\Model\ResourceModel;

use Magento\Catalog\Model\ResourceModel\Product;
use Magento\CatalogInventory\Api\Data\StockItemInterface;
use Magento\CatalogInventory\Api\StockConfigurationInterface;
use Magento\CatalogInventory\Model\Spi\StockRegistryProviderInterface;
use Magento\Framework\Model\AbstractModel;

class ProductPlugin
{
    protected StockConfigurationInterface    $stockConfiguration;
    protected StockRegistryProviderInterface $stockRegistryProvider;
    protected ?StockItemInterface            $stockItem = null;

    public function __construct(
        StockConfigurationInterface $stockConfiguration,
        StockRegistryProviderInterface $stockRegistryProvider
    ) {
        $this->stockConfiguration    = $stockConfiguration;
        $this->stockRegistryProvider = $stockRegistryProvider;
    }

    public function beforeSave(Product $subject, AbstractModel $object): array
    {
        $scopeId         = $this->stockConfiguration->getDefaultScopeId();
        $this->stockItem = clone $this->stockRegistryProvider->getStockItem($object->getId(), $scopeId);

        return [$object];
    }

    public function afterSave(Product $subject, Product $result, AbstractModel $object): Product
    {
        $scopeId   = $this->stockConfiguration->getDefaultScopeId();
        $stockItem = $this->stockRegistryProvider->getStockItem($object->getId(), $scopeId);

        // to be care that $this->stockItem->getQty() might be like 1.0000
        if ($stockItem->getQty() != $this->stockItem->getQty()) {
            // do something
        }

        return $result;
    }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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