A Magento integration can take many shapes and forms. ERP integrations especially can get very complex. In this short post we’ll take a look at a small aspect of an inventory integration's implementation, namely dealing with stock deduction, keeping inventory levels accurate at all times and otherwise dealing with overselling or underselling.

The requirements of the example that we'll look at are simple; Magento's inventory, or more accurately, Magento MSI sources, are synced with warehouses inventory levels that come from an ERP. Whenever stock levels change in the ERP, Magento's sources are updated via the API — a one way sync from the ERP to Magento, keeping inventory levels accurate at all times.

When an order is placed on Magento, the ERP deducts stock within itself (The order is synced via another mechanism that we won’t get into.) The ERP stock level change triggers the ERP to notify Magento.

As stock deduction is done on ERP side (and the ERP being the source of truth for inventory levels), we will need to disable Magento’s stock deduction on shipment. Otherwise, we would end up with double the stock deduction.

To disable stock deduction on the Magento side, we can create a preference for Magento\InventorySourceDeductionApi\Model\SourceDeductionServiceInterface, and override the execute method, making it do nothing:

In your di.xml:

<preference
    for="Magento\InventorySourceDeductionApi\Model\SourceDeductionServiceInterface"
    type="Company\ErpIntegration\Model\SourceDeductionService"/>

And the plugin:

<?php

declare(strict_types=1);

namespace Company\ErpIntegration\Model;

use Magento\InventorySourceDeductionApi\Model\SourceDeductionRequestInterface;
use Magento\InventorySourceDeductionApi\Model\SourceDeductionServiceInterface;

class SourceDeductionService implements SourceDeductionServiceInterface
{

    /**
     * Dummy method as source deduction is done by ERP, and is not Magento's responsibility
     * at this point.
     *
     * @phpcs:disable Magento2.CodeAnalysis.EmptyBlock
     * @param SourceDeductionRequestInterface $sourceDeductionRequest
     */
    public function execute(SourceDeductionRequestInterface $sourceDeductionRequest): void
    {
        // Do nothing
    }
}

(If you have a better implementation for disabling stock deduction on shipment, please let me know in the comments!)

So far so good. Our Magento stock levels should match the ERP levels fairly accurately.

Let's take a look at an example of a product sale, including Magento MSI's stock reservation system that takes care of the saleable quantity before the actual source deduction takes place.

Event Qty calculation Saleable qty
Initial stock levels 5 5
Customer places an order 5 - 1 (reservation) 4
Order is processed in ERP and API call made 4 - 1 (reservation) 3
Shipment is created in Magento 4 - 1 + 1 (compensation reservation) 4
Reservations cleanup 4 4

Through this example we can see that there is an instance where the saleable quantity drops to 3, even though we've only sold one product. However, the saleable quantity jumps back to 4 as soon as the order is shipped and Magento creates the compensation reservation.

This might be OK as long as the shipment is created at the exact time that the order is processed by the ERP.

In the case that there is a delay between ERP's stock deduction and the shipment creation, the business now has to decide whether it prefers underselling or overselling.

If you prefer to undersell (saying something is out of stock when in reality it isn't), the above scenario would work.

If you prefer to oversell, you can turn off Magento’s stock reservation system entirely, allowing customers to buy quantities of a SKU that's potentially not in stock.

Reservations are enabled or disabled by the setting in Stores -> Configuration -> Catalog -> Inventory -> "Decrease Stock When Order is Placed".

When stock reservation is disabled, the above example would look something like this:

Event Qty calculation Saleable qty
Initial stock levels 5 5
Customer places an order 5 5
Order is processed in ERP and API call made 4 4
Shipment is created in Magento 4 4

We can see that stock levels never jump below 4, as intended. However, there's now a brief period where a stock can be oversold.

I hope this sheds some light on a small aspect of an inventory integration! This might be especially useful if the business values accuracy of stock levels.

As always, if there's a better way to do this, don't hesitate to reach out.

magento2 integrations msi catalog