Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Anyway to disable stock reservation? #2269

Closed
asim-vax opened this issue May 22, 2019 · 70 comments · Fixed by AmpersandHQ/magento2-disable-stock-reservation#11
Closed

Anyway to disable stock reservation? #2269

asim-vax opened this issue May 22, 2019 · 70 comments · Fixed by AmpersandHQ/magento2-disable-stock-reservation#11
Projects

Comments

@asim-vax
Copy link

Summary (*)

We are noticing an issue where the default behaviour of MSI (no sources or stock set up) is that as an item is sold, it adds a record to inventory_reservation. And salable quantity is reduced.

We manage our stock in another system, and update the quantity field as orders come in. This is causing issues as suddenly the salable quantities are lowering, and never reset.

Is there a way to reset this salable quantity?
Is there a way to disable this in store -> config, or via config.php by disabling modules?

Examples (*)

Screenshot from 2019-05-22 15-01-02
As you can see here, some salable quantity is the same as quantity, but some are 0, and some are lower. As we manage stock elsewhere, and update magento, the quantity is the field we rely on. It should reduce by 1 as an order is placed, like prior default magento behaviour.

Proposed solution

A way to reset MSI, or to not fill the reservation table so our salable quantity is artificially lower than it should be.

@csdougliss
Copy link

csdougliss commented May 22, 2019

MSI has dependencies on reservation code.

I've added the following plugin override:

/**
 * Prevent append reservation if manage_reservations is set to 0
 */
class PreventAppendReservationOnNotManageItemsInStockPlugin extends \Magento\InventorySales\Plugin\InventoryReservationsApi\PreventAppendReservationOnNotManageItemsInStockPlugin
{
    /**
     * @var GetStockItemConfigurationInterface
     */
    private $getStockItemConfiguration;

    /**
     * @var StockConfigurationInterface
     */
    private $stockConfiguration;

    /**
     * @param GetStockItemConfigurationInterface $getStockItemConfiguration
     * @param StockConfigurationInterface $stockConfiguration
     */
    public function __construct(
        GetStockItemConfigurationInterface $getStockItemConfiguration,
        StockConfigurationInterface $stockConfiguration
    ) {
        $this->getStockItemConfiguration = $getStockItemConfiguration;
        $this->stockConfiguration = $stockConfiguration;
    }

    /**
     * @param AppendReservationsInterface $subject
     * @param \Closure $proceed
     * @param ReservationInterface[] $reservations
     *
     * @throws LocalizedException
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
     */
    public function aroundExecute(AppendReservationsInterface $subject, \Closure $proceed, array $reservations)
    {
        if (!(bool)$this->stockConfiguration->getDefaultConfigValue('manage_reservations')) {
            return;
        }

        return parent::aroundExecute($subject, $proceed, $reservations);
    }
}

And added a new back end option Manage Reservations.

Will this cause any other issues?? e.g. when the item is shipped for example?

Have to add the variables again as they are private 👎

@ishakhsuvarov
Copy link
Contributor

Generally we do not advise to disable reservations mechanism as it might cause undesired effects in multiple scenarios across the system.

@asim-vax Scenario you are describing should work fine when you have Manage Stock = False on the product level, this way you would tell Magento Inventory to stop trying to manage stock for you. Is there any specific reason you decided not to use it?

@okorshenko okorshenko added this to Ready for Grooming in Backlog May 28, 2019
@ishakhsuvarov ishakhsuvarov added this to the MSI Part III milestone May 29, 2019
@okorshenko okorshenko removed this from the MSI Part III milestone May 30, 2019
@ishakhsuvarov ishakhsuvarov added this to the Bugfixing milestone May 30, 2019
@Swahjak
Copy link

Swahjak commented Jun 6, 2019

We are using an external system that (by default) communicates the salable stock to Magento. I'm going to go ahead and assume that every Magento 1 webshop with an external inventory worked this way, since Magento 1 didn't have a reservation logic (and a wms / erp usually provides this functionality).

Now both Magento 2 and the external system will have their own 'truth' about what the available stock level is. The reservation logic is great if no external systems are used, but if an external system is used that system should be the source of truth, not Magento. One solution could be to revert to the legacy inventory, but I don't really like working with things that are going to be legacy. Besides, there are a lot of great features in MSI that I do want to use.

@csdougliss
Copy link

@ishakhsuvarov If you have say, 10 in stock, you want manage stock to be enabled as you only want to sell what you have and for magento to reduce the count.

So you cannot turn off Manage stock even if you have an external system.

@Swahjak
Copy link

Swahjak commented Jun 6, 2019

I'm not talking about turning off 'Manage stock' but a simple decrease would suffice since we rely on an external system to actually 'compute' what we want to happen to the stock. If a refund is created, the external system will decide whether or not the item should be returned to stock.

Take the following scenario;

  • We have a stock of 2 Product A
  • User buys product A
  • Magento decreases salable quantity by 1 resulting in 1 salable unit
  • External system decreases salable quantity by 1 resulting in 1 salable unit
  • External system pushes stock level back to Magento
  • Product A is no longer salable since the quantity is 1, reservation is 1, and salable quantity is 0

Now we could argue that the external system should be pushing the quantity, not the salable quantity. But what if I have some other logic in my external system? Let's say a POS that can also reserve stock in my external system. Now my external system communicates a stock level of 2 while only 1 is available.

Point being; My external system is the source of truth, not Magento. Magento is 'just' my storefront. Something which worked perfectly before MSI. (Not bashing on MSI, just stating the facts).

@franckgarnier21
Copy link

Totally agree with @Swahjak.

The logic reservation can be complex and it has to be handle by the erp.

I have the same issue.

I have two solutions.

1/ Provide a view of Magento reservations state to the erp.
2/ override reservations logic to deduct source qty and not create reservations.

Thanks,

@maghamed
Copy link
Contributor

maghamed commented Jun 6, 2019

hey guys @Swahjak @franckgarnier21 @craigcarnell ,

I totally agree with your business reasoning, and that for many merchants Magento is "just" a storefront while the real decision-making system is periodically sync-up with Magento providing the reliable data for transactional operations processing, being a source of truth.
Moreover internally we deal with quite the same situation while integrating Magento Order Management (MOM) with MSI, where MOM system in our case is a source of truth, which is in charge for calculation of an aggregation (stock) and periodically sync up with Magento updating the Inventory data we have there.

MSI was designed taking in mind all the possible integration strategies with 3rd party systems.

That's why we introduced \Magento\InventorySalesApi\Api\PlaceReservationsForSalesEventInterface interface which is an entry point for all reservations created in Magento. You can look through the usage of the interface, it's called for Order Placement, Returns, Order cancellation, and Shipment handling both negative and positive (compensational) reservations will go through this entry point.

The trickiest part for us is that we can't predict what particular integration set up for your case, as there could be different options which impact the Quantity calculation.
For example,

  1. Magento used as a storefront, and there is an external 3rd party system which is a source of truth for Inventory data.
  2. Order placed in Magento.
  3. Magento creates a corresponding Stock reservation. But the order is not handled yet, order status is Pending.
  4. External system sync-up with Magento providing "correct" Inventory data from that system

The question is what to do with an open reservation in Magento created as a result of order placement? Whether the order is already counted and Quantities are adjusted accordingly in the external system or not?
We don't know the answers to these questions, thus there could not be generic handling. But you as an extension developer know how both systems work and whether the Order already adjusted quantities coming from external ERP or not. Thus, you are in charge of customizing Magento behavior via pluginization or replacement of PlaceReservationsForSalesEventInterface service.

You no need to switch off Manage Stock in Magento in this case, moreover doing so you will lose an explicit extension point provided exactly for this customization, but you need to add your behavior which will impact on how Reservations are created.

For example, for MSI and MOM integration we will handle just the initial negative reservation created at the time of Order placement, all other reservations (on shipment and refund) are swallowed, as they are managed by MOM. The order placement reservation publishes a message to a Queue (in RabbitMQ) which is then handled by a subscriber on the MOM side. Thus, we always consider that Stock Quantities coming from MOM always include all the Orders placed in Magento.

Here you can watch a presentation by @vadimjustus where he is describing different integration strategies with Magento + MSI based on TechDivision projects - https://www.youtube.com/watch?v=RoIEaoVm4mU

@micwallace
Copy link

Rather than disabling the reservation in favor of deduction I propose the following so that stock is reserved until the order hits the ERP system:

  1. Order is placed and reservation entry is added.
  2. The order is synced with the ERP system. An additional reservation entry is added to cancel out the order_placed reservation and bring the total to 0. Stock can be deducted from the source quantity at that time too, depending on how often your ERP stock gets synced.
  3. A plugin prevents default reservation logic after shipment, invoice or cancellation if the reservation total is already 0.

This solution has the benefit that orders that aren't yet synced still count towards the salable quantity.

I'll be doing this for our implementation on Monday and will post the code once I'm done.

@gavinhoman
Copy link

We are experiencing the same issues for a project and need to remove the 'reservation' feature completely and allow 'saleable stock' to be the true source of inventory.

Has anybody come up with a solution to this yet?

@franckgarnier21
Copy link

You can play with the observers and plugin in order to remove the reservation additional.
At your own risk.
On my side I succeed to decrease the physical stock at invoice creation and not during shipment.

@darrenbrooksnm
Copy link

We are experiencing the same issues for a project and need to remove the 'reservation' feature completely and allow 'saleable stock' to be the true source of inventory.

Has anybody come up with a solution to this yet?

You can turn it off but disabling these modules and then running the usual setup:upgrade... etc.

php bin/magento module:disable -f Magento_Inventory Magento_InventoryAdminUi Magento_InventoryApi Magento_InventoryBundleProduct Magento_InventoryBundleProductAdminUi Magento_InventoryCatalog Magento_InventorySales Magento_InventoryCatalogAdminUi Magento_InventoryCatalogApi Magento_InventoryCatalogSearch Magento_InventoryConfigurableProduct Magento_InventoryConfigurableProductAdminUi Magento_InventoryConfigurableProductIndexer Magento_InventoryConfiguration Magento_InventoryConfigurationApi Magento_InventoryGroupedProduct Magento_InventoryGroupedProductAdminUi Magento_InventoryGroupedProductIndexer Magento_InventoryImportExport Magento_InventoryIndexer Magento_InventoryLowQuantityNotification Magento_InventoryLowQuantityNotificationAdminUi Magento_InventoryLowQuantityNotificationApi Magento_InventoryMultiDimensionalIndexerApi Magento_InventoryProductAlert Magento_InventoryReservations Magento_InventoryReservationsApi Magento_InventoryCache Magento_InventorySalesAdminUi Magento_InventorySalesApi Magento_InventorySalesFrontendUi Magento_InventoryShipping Magento_InventorySourceDeductionApi Magento_InventorySourceSelection Magento_InventorySourceSelectionApi Magento_InventoryShippingAdminUi Magento_InventoryDistanceBasedSourceSelectionAdminUi Magento_InventoryDistanceBasedSourceSelectionApi Magento_InventoryElasticsearch Magento_InventoryExportStockApi Magento_InventoryReservationCli Magento_InventoryExportStock Magento_CatalogInventoryGraphQl Magento_InventorySetupFixtureGenerator

@gavinhoman
Copy link

@darrenbrooksnm thanks for the reply. This looks like ALL inventory management would be disabled? We still want to manage inventory so that 'saleable' stock is the same as 'quantity' so just do away with the reservation feature in M2.

So if an item has a quantity of 10 then a maximum of 10 units can be purchased by a customer.

@darrenbrooksnm
Copy link

@gavinhoman It does not, it disables the reservation feature along with a few other bits. It is almost like reverting back to pre2.3, do it on a test site and you will see.

@brnquester
Copy link

brnquester commented Sep 10, 2019

@asim-vax Hey, did you find any solution for that? We're facing the exactly same problem and we are desperate because of multiple stock issues.

@gavinhoman What about you? Any solution on your side?

Any help is very appreciated.

@Swahjak
Copy link

Swahjak commented Sep 11, 2019

@brngyn you might want to take a look at Magento\InventorySourceDeductionApi\Model\SourceDeductionServiceInterface which is doing the actual deduction from the stock if you don't want Magento to control the stock position (this will still deduct the salable quantity). You can create an 'around' plugin that will just do nothing and leave the actual stock deduction to an external service. It isn't the pretiest solution, but it works for us at the moment.

/**
 * Class SourceDeductionService
 */
class SourceDeductionServicePlugin
{
    /**
     * @param \Magento\InventorySourceDeductionApi\Model\SourceDeductionServiceInterface $subject
     * @param callable                                                                   $proceed
     * @param \Magento\InventorySourceDeductionApi\Model\SourceDeductionRequestInterface $sourceDeductionRequest
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
     */
    public function aroundExecute(
        SourceDeductionServiceInterface $subject,
        callable $proceed,
        SourceDeductionRequestInterface $sourceDeductionRequest
    ): void {
        // do nothing
    }
}

@asim-vax
Copy link
Author

@brngyn we disabled all MSI modules for the time being, it was the only way we could get patch our live website same day to stop this causing more issues

@texboy
Copy link

texboy commented Sep 30, 2019

@franckgarnier21 Could you please explain how you managed to decrease the stock during the invoice instead of the shipment ?
@micwallace Were you able to do what you proposed?

@TheFrakes
Copy link

@franckgarnier21 Could you please explain how you managed to decrease the stock during the invoice instead of the shipment ?
@micwallace Were you able to do what you proposed?

I would like to know too.
Also I don't get whyt magento2 does not truncate all reserved stock for a specific product if i manually change the stock amount of said product. It's like completely against any logic. I liked the way they handled that in magento1.

@texboy
Copy link

texboy commented Oct 10, 2019

@TheFrakes I know right? it boggles my mind that there's not way to manage the reserved stock in any way.

@qrz-io
Copy link

qrz-io commented Oct 23, 2019

Is there any word from Magento on this? It's a pretty drastic change from previous typical inventory integrations.

To add to the commend from @Swahjak, the way for the "proper" inventory implementation would have to be something like:

  • We have a stock of 2 Product A
  • User buys 1 of product A
  • Magento reserves quantity of, 1 resulting in 1 saleable unit
  • External system also reserves quantity by 1 resulting in 1 saleable unit

At this point both Magento and WMS have stock=2 and reserved=1, so the actual saleable quantity is 1. Imagine now stock is sold somewhere else:

  • A second user buys 1 of product A through a separate mobile app
  • Warehouse now has qty=2 and reserved=2, so product should not be saleable
  • Magento is updated with stock qty=2, so it thinks the product is still saleable.

This means the inventory solution will also need to send updates to Magento for stock reserved elsewhere, as well as its status (shipped, cancelled, refunded).

It does feel like Magento is trying to do too much with Stock, and act as a WMS, when really it should just be "told" what stock it can sell or not and prevent oversells, like it used to.

@Timothy-V
Copy link

We're running into situation A described in the video by Vadimjustus. We have another system which is the source of truth. I can really see the added value of the multi inventory module, since we have multiple stocks in multiple databases. But right now I have to disable MSI altogether because of our situation.
What I really fail to understand is that something like this can be introduced, while it is probably well-known that a lot of merchants will run into this problematic situation and that no simple option is provided. Where is the out-of-the-box feature to disable the whole reservation logic, while still being able to use the other benefits of MSI??

@deepikajaniyani
Copy link

@micwallace did you implement the code for the solution you proposed?

@micwallace
Copy link

micwallace commented Nov 18, 2019 via email

@andrewkett
Copy link

andrewkett commented Nov 19, 2019

We had similar issues recently and have implemented our own work around. We don't save any reservations to the db and reduce the source qty as soon as an order is invoiced. It isn't a one size fits all solution and I am sure there are caveats but it has worked well for our use case so far. https://github.com/8WireDigital/magento2-module-noreservations

@qrz-io
Copy link

qrz-io commented Nov 26, 2019

We also had a crack at this at Ampersand and dev'd a module to disable stock reservations: https://github.com/AmpersandHQ/magento2-disable-stock-reservation/

The main difference with @andrewkett's version is that we decrement stock on order placement, rather than invoice, because some payment methods have an in between state before invoices are created (payment_review) and if there's delays in the payment gateway, that might cause stock to oversell.

The module will force source selection at order placement, so that we know where stock needs to be picked from, and the correct source stock is decremented on order placement.

We also started the implementation to return stock in case of cancellations, however this needs to be updated to return stock to the correct source - at the moment it only works with single source inventory, but we plan on implementing this soon (see AmpersandHQ/magento2-disable-stock-reservation#4)

@oefterdal
Copy link

oefterdal commented Nov 28, 2019

I am not sure if this is good design, What if we use Double-entry bookkeeping approach on this issue and add the same amount we have negative in the stock reservation table as a positiv amount in a separated stock location. -4 + 4 = 0 so the effect of the reservation would be zeroed out and the sellable quantity would be correct.

Then we don't need to overwrite any core functionality.

@achleitner
Copy link

We had similar issues recently and have implemented our own work around. We don't save any reservations to the db and reduce the source qty as soon as an order is invoiced. It isn't a one size fits all solution and I am sure there are caveats but it has worked well for our use case so far. https://github.com/8WireDigital/magento2-module-noreservations

Hello, does this work with Magento 2.3.4? We are having trouble with Magento displaying out of stock products(salable quantitiy 0) as in stock in the front end for non logged in users. Thanks!

@elburro1887
Copy link

I am also setting the stock via my external warehouse management system and it seems MSI was rolled out without any regard for these situations, which seem to involve a lot of users.

There should at least be minimal pain when migrating by providing the following settings in the config, so that the installation of any 3rd party modules or fixes is not necessary.

  • Enable vs. disable inventory reservations
  • Trigger stock deductions on order placement vs. shipment

@moojet
Copy link

moojet commented May 13, 2020

I am also setting the stock via my external warehouse management system and it seems MSI was rolled out without any regard for these situations, which seem to involve a lot of users.

There should at least be minimal pain when migrating by providing the following settings in the config, so that the installation of any 3rd party modules or fixes is not necessary.

  • Enable vs. disable inventory reservations
  • Trigger stock deductions on order placement vs. shipment

We just made some changes to our StockEasy ext. which does this:

  • when importing stock via csv, it removes entries from the reservations table for those imported SKUs
  • others are left

Would this method work for you? I think this sidesteps your 2nd req, as the reservations system should still produce purchase limits until that external stock is updated.

@seansan
Copy link

seansan commented Jun 4, 2020

I'm not talking about turning off 'Manage stock' but a simple decrease would suffice since we rely on an external system to actually 'compute' what we want to happen to the stock. If a refund is created, the external system will decide whether or not the item should be returned to stock.

Take the following scenario;

  • We have a stock of 2 Product A
  • User buys product A
  • Magento decreases salable quantity by 1 resulting in 1 salable unit
  • External system decreases salable quantity by 1 resulting in 1 salable unit
  • External system pushes stock level back to Magento
  • Product A is no longer salable since the quantity is 1, reservation is 1, and salable quantity is 0

Now we could argue that the external system should be pushing the quantity, not the salable quantity. But what if I have some other logic in my external system? Let's say a POS that can also reserve stock in my external system. Now my external system communicates a stock level of 2 while only 1 is available.

Point being; My external system is the source of truth, not Magento. Magento is 'just' my storefront. Something which worked perfectly before MSI. (Not bashing on MSI, just stating the facts).

We have the same issue. Stock worked perfectly before reservations .. and is now leading to a lot of custom coding and uncertainty.

  1. How can we disable stock reservations?
  2. or how can we assign stock to a source location at quote/invoice moment instead of shipping?

@qrz-io
Copy link

qrz-io commented Jun 4, 2020

@seansan completely agree with your points, it's so frustrating that the maintainers are just shrugging and saying "nah it's fine".

Take a look at our module for a starting point, it forces stock selection at order invoice rather than shipment, and strongly relies on an external system to do stock replenishment in case of returns.

edit: link to the module: https://github.com/AmpersandHQ/magento2-disable-stock-reservation

@elburro1887
Copy link

I am also using the ampersand/magento2-disable-stock-reservation module, since the magento devs don't feel like this is an issue.

@seansan
Copy link

seansan commented Jun 4, 2020 via email

@seansan
Copy link

seansan commented Jun 4, 2020

I am also setting the stock via my external warehouse management system and it seems MSI was rolled out without any regard for these situations, which seem to involve a lot of users.

There should at least be minimal pain when migrating by providing the following settings in the config, so that the installation of any 3rd party modules or fixes is not necessary.

  • Enable vs. disable inventory reservations
  • Trigger stock deductions on order placement vs. shipment

Sorry missed that : Completely my point ...

THis change is leading to tremendous impact now

@alchats
Copy link

alchats commented Jun 5, 2020

We installed, configured, themed, and developed a 2.3.5 instance.
We also migrated about 30k products and put a lot of work in tweaking the content, before realising this MSI business and the catastrophic incompatibility with our ERP system and entire business model.
After considering AmpersandHQ's extension, I notice their warning about how both the inventory and cataloginventory_stock should be on the same mode (Update on Save or Schedule) for this module to work as expected.

There is no indication of any index named cataloginventory_stock on the Index Management.

Has anyone had any experience with overcoming this MSI problem on 2.3.5? Is the legacy inventory completely gone now with 2.3.5 and we have no solution?

@Swahjak
Copy link

Swahjak commented Jun 5, 2020

@alchats as far as I'm aware of you can actually completely disable MSI and just go with the legacy stock. To give you an idea, we run with the following modules as "replace" in the composer.json (and rely on the 'old' cataloginventory to manage the stock):

        "magento/module-admin-notification": "*",
        "magento/module-catalog-rule-configurable": "*",
        "magento/module-captcha": "*",
        "magento/module-paypal": "*",
        "magento/module-paypal-captcha": "*",
        "magento/module-paypal-recaptcha": "*",
        "magento/module-weee": "*",
        "magento/module-rss": "*",
        "magento/module-downloadable": "*",
        "magento/theme-frontend-luma": "*",
        "magento/inventory-composer-installer": "*",
        "magento/language-fr_fr": "*",
        "magento/language-pt_br": "*",
        "magento/language-zh_hans_cn": "*",
        "magento/language-es_es": "*",
        "magento/language-de_de": "*",
        "magento/module-adobe-ims": "*",
        "magento/module-adobe-ims-api": "*",
        "magento/module-adobe-stock-client-api": "*",
        "magento/module-adobe-stock-admin-ui": "*",
        "magento/module-adobe-stock-client": "*",
        "magento/module-adobe-stock-image-api": "*",
        "magento/module-adobe-stock-asset-api": "*",
        "magento/module-adobe-stock-image-admin-ui": "*",
        "magento/module-adobe-stock-image": "*",
        "magento/module-adobe-stock-asset": "*",
        "magento/adobe-stock-integration": "*",
        "magento/module-authorizenet-graph-ql":"*",
        "magento/module-bundle-graph-ql": "*",
        "magento/module-catalog-graph-ql": "*",
        "magento/module-catalog-cms-graph-ql": "*",
        "magento/module-catalog-customer-graph-ql": "*",
        "magento/module-catalog-inventory-graph-ql": "*",
        "magento/module-catalog-url-rewrite-graph-ql": "*",
        "magento/module-checkout-agreements-graph-ql":"*",
        "magento/module-cms-graph-ql": "*",
        "magento/module-cms-url-rewrite-graph-ql": "*",
        "magento/module-configurable-product-graph-ql": "*",
        "magento/module-customer-graph-ql": "*",
        "magento/module-customer-downloadable-graph-ql": "*",
        "magento/module-directory-graph-ql": "*",
        "magento/module-downloadable-graph-ql": "*",
        "magento/module-eav-graph-ql": "*",
        "magento/module-graph-ql": "*",
        "magento/module-graph-ql-cache": "*",
        "magento/module-grouped-product-graph-ql": "*",
        "magento/module-quote-graph-ql": "*",
        "magento/module-paypal-graph-ql": "*",
        "magento/module-related-product-graph-ql":"*",
        "magento/module-sales-graph-ql": "*",
        "magento/module-send-friend-graph-ql": "*",
        "magento/module-store-graph-ql": "*",
        "magento/module-swatches-graph-ql": "*",
        "magento/module-tax-graph-ql": "*",
        "magento/module-theme-graph-ql": "*",
        "magento/module-url-rewrite-graph-ql": "*",
        "magento/module-vault-graph-ql": "*",
        "magento/module-weee-graph-ql": "*",
        "magento/module-wishlist-graph-ql": "*",
        "magento/module-inventory": "*",
        "magento/module-inventory-admin-ui": "*",
        "magento/module-inventory-advanced-checkout": "*",
        "magento/module-inventory-requisition-list": "*",
        "magento/module-inventory-bundle-product": "*",
        "magento/module-inventory-bundle-product-admin-ui": "*",
        "magento/module-inventory-cache": "*",
        "magento/module-inventory-catalog": "*",
        "magento/module-inventory-catalog-admin-ui": "*",
        "magento/module-inventory-catalog-search": "*",
        "magento/module-inventory-configurable-product": "*",
        "magento/module-inventory-configurable-product-admin-ui": "*",
        "magento/module-inventory-configurable-product-indexer": "*",
        "magento/module-inventory-configuration": "*",
        "magento/module-inventory-distance-based-source-selection": "*",
        "magento/module-inventory-distance-based-source-selection-admin-ui": "*",
        "magento/module-inventory-distance-based-source-selection-api": "*",
        "magento/module-inventory-export-stock": "*",
        "magento/module-inventory-export-stock-api": "*",
        "magento/module-inventory-elasticsearch": "*",
        "magento/module-inventory-graph-ql": "*",
        "magento/module-inventory-grouped-product": "*",
        "magento/module-inventory-grouped-product-admin-ui": "*",
        "magento/module-inventory-grouped-product-indexer": "*",
        "magento/module-inventory-import-export": "*",
        "magento/module-inventory-low-quantity-notification": "*",
        "magento/module-inventory-low-quantity-notification-admin-ui": "*",
        "magento/module-inventory-low-quantity-notification-api": "*",
        "magento/module-inventory-product-alert": "*",
        "magento/module-inventory-reservations": "*",
        "magento/module-inventory-reservation-cli": "*",
        "magento/module-inventory-sales-admin-ui": "*",
        "magento/module-inventory-sales-frontend-ui": "*",
        "magento/module-inventory-setup-fixture-generator": "*",
        "magento/module-inventory-shipping": "*",
        "magento/module-inventory-shipping-admin-ui": "*",
        "magento/module-inventory-source-selection": "*",
        "magento/module-sales-inventory": "*",
        "magento/google-shopping-ads": "*",
        "magento/module-advanced-pricing-import-export": "*",
        "magento/module-analytics": "*",
        "magento/module-admin-analytics": "*",
        "magento/module-authorizenet": "*",
        "magento/module-authorizenet-acceptjs": "*",
        "magento/module-authorizenet-cardinal": "*",
        "magento/module-bundle-import-export": "*",
        "magento/module-catalog-analytics": "*",
        "magento/module-customer-analytics": "*",
        "magento/module-customer-import-export": "*",
        "magento/module-cybersource": "*",
        "magento/module-dhl": "*",
        "magento/module-downloadable-import-export": "*",
        "magento/module-eway": "*",
        "magento/module-fedex": "*",
        "magento/module-google-adwords": "*",
        "magento/module-google-optimizer": "*",
        "magento/module-grouped-import-export": "*",
        "magento/module-marketplace": "*",
        "magento/module-multishipping": "*",
        "magento/module-new-relic-reporting": "*",
        "magento/module-quote-analytics": "*",
        "magento/module-review-analytics": "*",
        "magento/module-sales-analytics": "*",
        "magento/module-sample-data": "*",
        "magento/module-send-friend": "*",
        "magento/module-signifyd": "*",
        "magento/module-swagger": "*",
        "magento/module-swagger-webapi": "*",
        "magento/module-swagger-webapi-async": "*",
        "magento/module-swatches-layered-navigation": "*",
        "magento/module-tax-import-export": "*",
        "magento/module-ups": "*",
        "magento/module-usps": "*",
        "magento/module-version": "*",
        "magento/module-wishlist-analytics": "*",
        "magento/module-worldpay": "*",
        "magento/module-braintree": "*",
        "magento/module-braintree-graph-ql": "*",
        "magento/module-cardinal-commerce": "*",

@alfonso-eusebio
Copy link

This might be a bit off-topic, but I think it's still relevant enough...

What I've always found odd is that there is a single "switch" to either have Magento handle stock levels and product display logic, or do nothing at all with stock information: the Manage Stock switch.

In fact, I think the name is misleading because managing the stock (i.e. increase/decrease quantities, etc) doesn't have anything to do with using stock information to decide if and how to display products.

In the type of scenarios that we are discussing here it might be enough to be able to tell Magento "don't touch my stock levels, I'll take care of that; but still use the stock information to decide if a product should be displayed or is available to purchase". I think this split makes perfect sense and shouldn't create any conflicts or inconsistencies.
The case that I'm dealing with at the moment would be fine just with that split of control: ERP updates stock, Magento displays products based on stock and options around it. But the way it is at the moment I can't have one without the other.

Cheers

@denniskopitz
Copy link

We also have an integration to an ERP that provides "source of truth" inventory qty updates. To be clear, this means that qty recieved from that system already respects reservations for the orders we have sent it. Our solution is:

Add a plugin that only allows "order_placed" events into the magento reservations table (this enables magento to continue to provide accurate saleable qty between erp qty updates).

<?php

namespace YOURNAMESPACE\YOURMODULE\Plugin\InventoryReservationsApi;

use Closure;
use Magento\Framework\Serialize\SerializerInterface;
use Magento\InventoryReservationsApi\Model\AppendReservationsInterface;
use Magento\InventoryReservationsApi\Model\ReservationInterface;

/**
 * Prevent append reservation if it's not an "order_placed" eventType
 */
class AllowOnlyOrderPlacedReservationsPlugin
{
    /**
     * @var SerializerInterface
     */
    private $serializer;

    /**
     * @param SerializerInterface $serializer
     */
    public function __construct(
        SerializerInterface $serializer
    ) {
        $this->serializer = $serializer;
    }

    /**
     * @param AppendReservationsInterface $subject
     * @param Closure $proceed
     * @param ReservationInterface[] $reservations
     *
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
     */
    public function aroundExecute(AppendReservationsInterface $subject, Closure $proceed, array $reservations)
    {

        $reservationToAppend = [];
        foreach ($reservations as $reservation) {

            $metaData = $this->serializer->unserialize($reservation->getMetadata());
            if (is_array($metaData) && $metaData['event_type'] == 'order_placed') {
                $reservationToAppend[] = $reservation;
            }
        }

        if (!empty($reservationToAppend)) {
            $proceed($reservationToAppend);
        }
    }
}

Add function in your integration to clear the magento reservations table by sku whenever we update qty from the erp integration for that sku

 /**
     * @param string $sku
     * @return int
     */
    public function cleanReservationsForSku(string $sku): int
    {
        $connection = $this->resourceConnection->getConnection();
        $tableName = $this->resourceConnection->getTableName('inventory_reservation');

        return $connection->delete($tableName, ['sku = ?' => $sku]);
    }

Add observer to disable magento's need to stop shipment processing if qty is not deemed available (for the occasion when things are not in perfect sync)

<?php

namespace YOURNAMESPACE\YOURMODULE\Observer;

use Magento\{
    Framework\Event\ObserverInterface,
    Framework\Event\Observer,
    Sales\Model\Order\Shipment,
    InventorySourceDeductionApi\Model\GetSourceItemBySourceCodeAndSku,
    InventoryCatalogApi\Model\IsSingleSourceModeInterface,
    InventoryCatalogApi\Api\DefaultSourceProviderInterface,
    InventoryApi\Api\SourceItemsSaveInterface,
    Framework\Registry
};

class SalesOrderShipmentSaveBefore implements ObserverInterface
{
    const ADJUSTED_INVENTORY_SHIPMENT_ITEM_SKUS = 'YOURNAMESPACE_YOURMODULE_adjusted_inventory_shipment_item_skus';

    /**
     * @var GetSourceItemBySourceCodeAndSku
     */
    protected $getSourceItemBySourceCodeAndSku;

    /**
     * @var IsSingleSourceModeInterface
     */
    protected $isSingleSourceMode;

    /**
     * @var DefaultSourceProviderInterface
     */
    protected $defaultSourceProvider;

    /**
     * @var SourceItemsSaveInterface
     */
    protected $sourceItemsSave;

    /**
     * @var Registry
     */
    protected $registry;

    /**
     * SalesOrderShipmentSaveBefore constructor.
     * @param GetSourceItemBySourceCodeAndSku $getSourceItemBySourceCodeAndSku
     * @param IsSingleSourceModeInterface $isSingleSourceMode
     * @param DefaultSourceProviderInterface $defaultSourceProvider
     * @param SourceItemsSaveInterface $sourceItemsSave
     * @param Registry $registry
     */
    public function __construct(
        GetSourceItemBySourceCodeAndSku $getSourceItemBySourceCodeAndSku,
        IsSingleSourceModeInterface $isSingleSourceMode,
        DefaultSourceProviderInterface $defaultSourceProvider,
        SourceItemsSaveInterface $sourceItemsSave,
        Registry $registry
    ) {
        $this->getSourceItemBySourceCodeAndSku = $getSourceItemBySourceCodeAndSku;
        $this->isSingleSourceMode = $isSingleSourceMode;
        $this->defaultSourceProvider = $defaultSourceProvider;
        $this->sourceItemsSave = $sourceItemsSave;
        $this->registry = $registry;
    }

    /**
     * @inheritdoc
     */
    public function execute(Observer $observer)
    {
        /**
         * @var Shipment $shipment
         */
        $shipment = $observer->getEvent()->getData('shipment');

        if ($shipment) {
            $shipmentItems = $shipment->getItems();

            if (is_array($shipmentItems) && count($shipmentItems) > 0) {
                $sourceCode = '';

                if (!empty($shipment->getExtensionAttributes())
                    && !empty($shipment->getExtensionAttributes()->getSourceCode())) {
                    $sourceCode = $shipment->getExtensionAttributes()->getSourceCode();
                } elseif ($this->isSingleSourceMode->execute()) {
                    $sourceCode = $this->defaultSourceProvider->getCode();
                }

                $adjustedItemSkus = [];

                foreach ($shipmentItems as $shipmentItem) {
                    $sourceItem = null;

                    try {
                        $sourceItem = $this->getSourceItemBySourceCodeAndSku->execute($sourceCode, $shipmentItem->getSku());
                    } catch (\Exception $e) {
                    }

                    if ($sourceItem && $sourceItem->getQuantity() == 0) {
                        $sourceItem->setQuantity($shipmentItem->getQty());
                        $this->sourceItemsSave->execute([$sourceItem]);
                        $adjustedItemSkus[] = $shipmentItem->getSku();
                    }
                }

                $this->registry->register(self::ADJUSTED_INVENTORY_SHIPMENT_ITEM_SKUS, $adjustedItemSkus, true);
            }
        }
    }
}

@regannoveske
Copy link

@denniskopitz Thank you, this appears elegant and capable of handling our specific situation. Thank you so much for sharing!!!! Looking forward to attempting to deploy & test.

@denniskopitz
Copy link

denniskopitz commented Jun 23, 2020 via email

@regannoveske
Copy link

@denniskopitz I'd like to explore this further. Can you please message me through https://www.linkedin.com/in/reganreneeengmanbell/

Thanks!

@maderlock
Copy link

I've been pretty surprised by this approach by MSI - you've considered multiple scenarios, really? How then was your conclusion that everyone should work with reservations?

We need multiple stock but without the two-versions-of-the-truth introduced by reservations. The Ampersand module looks like a way to save ourselves a lot of headaches!

@seansan
Copy link

seansan commented Jul 10, 2020 via email

@regannoveske
Copy link

I've been pretty surprised by this approach by MSI - you've considered multiple scenarios, really? How then was your conclusion that everyone should work with reservations?

We need multiple stock but without the two-versions-of-the-truth introduced by reservations. The Ampersand module looks like a way to save ourselves a lot of headaches!

I think reservations should be handled on a case-by-case basis depending on the client's configuration. In our case, since our single POT is synchronized once daily, we need stock to be reserved/managed between synchronizations and wholly ignored for shipping fulfillment. Each use case may have it's own solution.

For us however, the ampersand module is not a viable solution. We tested it, and for our use case it did not work.

@jorgb90
Copy link

jorgb90 commented Nov 17, 2020

@regannoveske Did you find a solution? We have the same situation..

@AAbitar
Copy link

AAbitar commented Jan 26, 2021

The whole Magento 2 reservation system is broken. We can not use this software anymore until the reservation system can be disabled.
Additional external API calls on order fulfilment does not work (no shipping number/order update) because the stock reservation system want to see stock on delivery day. So while the API tells Magento, that the goods has been physically dispatched, Magento tells the API that this is not possible.
As a workaround we will have to remove everything that has to do with the broken reservation system.

@mwgh401
Copy link

mwgh401 commented May 4, 2021

Hi my client is having the same issue as well. The 3rd party OMS syncs their version of Salable Qty to Magento's Qty every 5 minutes. Our current workaround is, when pushing OMS Salable Qty to Magento Qty, check to see if there is any Reserved Qty, if there is add to it first, and then update the Magento Qty.

However the Magento Product API does not seem to have the get method for Reserved Qty on an SKU level. Any idea how i can get it?

@Djohn12
Copy link

Djohn12 commented Oct 18, 2023

Hi there,

I'm not sure this is still relevant but I wanted to say we disabled magento stock reservations using a module for few clients who wanted to handle their stock by themselves without magento trying to handle reservation and stock balance: https://github.com/AmpersandHQ/magento2-disable-stock-reservation

It's working as expected for us but be sure to go through all your test cases.
Hope this helps

@MrPotatox
Copy link

I find it crazy that this is still an issue for sellers in 2024. We're also having troubles with this and was hoping someone could provide me some insight as to what implications simply turning off "Manage Stock" entails?

@Djohn12
Copy link

Djohn12 commented Feb 10, 2024

@MrPotatox You can find the Manage stock product option definition on this page https://docs.magento.com/user-guide/v2.3/catalog/inventory.html

From my understanding, setting the Manage Stock option to "no" on any product will prevent magento from caring about that product stock and movements (i.e. it won't record reservations nor decrease stock when customer order the product).

Having this option set to No, magento will simply check if product is flagged as In stock and has a quantity > 0 in order to display the product as inStock/OutOfStock

Hope that helps

@alchats
Copy link

alchats commented Feb 12, 2024

@MrPotatox You can find the Manage stock product option definition on this page https://docs.magento.com/user-guide/v2.3/catalog/inventory.html

From my understanding, setting the Manage Stock option to "no" on any product will prevent magento from caring about that product stock and movements (i.e. it won't record reservations nor decrease stock when customer order the product).

Having this option set to No, magento will simply check if product is flagged as In stock and has a quantity > 0 in order to display the product as inStock/OutOfStock

Hope that helps

This is not useful as we do care if there is 1 or 100 products to sell, something our stock control application will not be able to update if this setting is turned on.

@MrPotatox
Copy link

@MrPotatox You can find the Manage stock product option definition on this page https://docs.magento.com/user-guide/v2.3/catalog/inventory.html

From my understanding, setting the Manage Stock option to "no" on any product will prevent magento from caring about that product stock and movements (i.e. it won't record reservations nor decrease stock when customer order the product).

Having this option set to No, magento will simply check if product is flagged as In stock and has a quantity > 0 in order to display the product as inStock/OutOfStock

Hope that helps

Thank you, this is the response I was expecting, and as @alchats says isn't suitable because we need the stock to decrement when an order is placed.

@MrPotatox
Copy link

It appears the Magento documentation was updated 2 months ago (whether this bit was added then or not I am unsure) but it clearly states "To use Magento Order Management or third-party services such as ERP, disable Manage Stock."
https://docs.magento.com/user-guide/v2.3/catalog/inventory.html

And there is at least the option in the backend configuration to "Decrease Stock When Order is Placed" that appears disconnected from the Manage Stock option.
I am pretty sure we will disable the Manage Stock option within the next few days so I will see how this fares with our Order Management System.

For users with a single stock source/location like us, this may be the most straightforward answer. For everyone else that has multiple sources/locations I can't see a way around having at least some interaction with the Inventory Res.

@Djohn12
Copy link

Djohn12 commented Feb 22, 2024

Just so you know, I had a project using multiple websites et stock sources.

In that context, using the AmpersandHQ/magento2-disable-stock-reservation I pointed earlier in that thread did the trick nicely as well 😉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Backlog
  
Done
Development

Successfully merging a pull request may close this issue.