1

I'm using Magento 2.4.3-p1 and I'm trying to decrease stock quantity with custom observer while placing an order, but salable quantity is decreasing twice, my events and observer file are this:

etc/events.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <event name="sales_order_place_after">
        <observer name="update_qty_with_saleable_qty" instance="Vendor\Module\Observer\AfterPlaceOrderUpdateQty" />
    </event>
</config>

Vendor\Module\Observer\AfterPlaceOrderUpdateQty

<?php

declare(strict_types=1);

namespace Vendor\Module\Observer;

use Magento\Catalog\Model\Product as Product;
use Magento\CatalogInventory\Model\Stock\StockItemRepository;
use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;
use Magento\Framework\Exception\CouldNotSaveException;
use Magento\Framework\Exception\InputException;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\Validation\ValidationException;
use Magento\InventoryApi\Api\Data\SourceItemInterface;
use Magento\InventoryApi\Api\SourceItemsSaveInterface;

/**
 * After Place Order Update Stock Quantity and Salable Quantity
 */
class AfterPlaceOrderUpdateQty implements ObserverInterface
{

    /**
     * @var SourceItemInterface
     */
    protected SourceItemInterface $sourceItemInterface;
    /**
     * @var SourceItemsSaveInterface
     */
    protected SourceItemsSaveInterface $sourceItemsSave;
    /**
     * @var Product
     */
    protected Product $product;
    /**
     * @var StockItemRepository
     */
    protected StockItemRepository $stockRepository;

    /**
     * @param SourceItemsSaveInterface $sourceItemsSave
     * @param SourceItemInterface $sourceItemInterface
     * @param StockItemRepository $stockRepository
     * @param Product $product
     */
    public function __construct(
        SourceItemsSaveInterface $sourceItemsSave,
        SourceItemInterface $sourceItemInterface,
        StockItemRepository $stockRepository,
        Product $product
    ) {
        $this->sourceItemsSave = $sourceItemsSave;
        $this->sourceItemInterface = $sourceItemInterface;
        $this->product = $product;
        $this->stockRepository = $stockRepository;
    }

    /**
     * @param $sku
     * @param $qty
     * @return AfterPlaceOrderUpdateQty
     * @throws NoSuchEntityException
     * @throws CouldNotSaveException
     * @throws InputException
     * @throws ValidationException
     */
    public function setQtyToProduct($sku, $qty): AfterPlaceOrderUpdateQty
    {
        $product = $this->product->getIdBySku($sku);
        $productStock = $this->stockRepository->get($product);
        $productQty = $productStock->getQty();
        $totalQty = $productQty - $qty;
        $this->sourceItemInterface->setSku($sku);
        $this->sourceItemInterface->setQuantity($totalQty);
        $this->sourceItemInterface->setStatus(1);
        $this->sourceItemInterface->setSourceCode('default');
        $this->sourceItemsSave->execute([$this->sourceItemInterface]);
        return $this;
    }

    /**
     * @param Observer $observer
     * @return void
     * @throws NoSuchEntityException
     * @throws CouldNotSaveException
     * @throws InputException
     * @throws ValidationException
     */
    public function execute(Observer $observer)
    {
        $order = $observer->getEvent()->getOrder();
        foreach ($order->getAllVisibleItems() as $item) {
            $productSku = $item->getSku();
            $productQty = $item->getQtyOrdered();
            $this->setQtyToProduct($productSku, $productQty);
        }
    }
}
3
  • check with Log, may be your event objerver class would be execute multiple times. if so then try with "sales_order_save_after" event.
    – S.P
    Commented Apr 21, 2022 at 9:52
  • you should not use "sales_order_place_after" event, that event do execute on every save statements Ex. $order->save().
    – S.P
    Commented Apr 21, 2022 at 10:08
  • Check if you have any other module or code saving the order, if so, the observer will be trigger a second time. Normally when I have modules that I do not know I use a variable or a field in the database to check if the code already have passed for my function... is not the better solution, but solves and I do not lose to much time... just saying :) Commented Sep 13, 2022 at 10:43

3 Answers 3

1

Go to Admin Panel's Configuration and disable Decrease Stock When Order Is Placed in the Stock Options

enter image description here

Stores->Configuration->Catalog->Inventory->Stock Options->Decrease Stock When Order Is Placed "NO"

1
  • 1
    I want to do this programtically, not through admin configuration. Commented Apr 21, 2022 at 8:26
1

you should not use sales_order_place_after event for quantity update, that event will fire on every save statements Ex. $order->save().

try with "checkout_submit_all_after" or "sales_order_save_after"

your events.xml file would be with global scope app/code/{{vendor}}/{{module}}/etc/events.xml.

-1

Are you using Multi Source Inventory feature in magento? If yes, it would automatically reduce the saleable quantity of an item when the order is placed.

Magento uses reservations to calculate and keep track of the salable quantity of each product assigned to a stock. When a customer places an order, Magento checks whether the quantity requested for each item is available for sale. If yes, Magento creates a reservation as an inventory request for each item, thereby reducing the salable quantity available for purchase.

https://devdocs.magento.com/guides/v2.4/inventory/reservations.html

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.