Allow backorders on a website scope

Allow backorders on a website scope

Related Inchoo Services

We’re all aware of the fact that Magento handles inventory of products in a (fairly) straightforward fashion. There is only one “warehouse”, one inventory, one “number” in the database that is responsible for a final say – how much of it is in stock. A lot of other functionality is dependent on the fact that stock is global. If we check “Advanced inventory” configuration from the administration of a product, we notice that all of the options are global: Out of stock threshold, Minimum and maximum qty allowed in Shopping Cart, backorders, notifications etc.

While this is perfectly normal – having a global quantity (stock), requires most of the functionality related to it, to have global setting as well, a situation may arise, in which we need some of the functionality to work on a different scope (e.g. website or store view scope). Example of this would be backordering. A simple functionality – allow customer to purchase something even if it’s not in stock. This decreases quantity further into negative values, e.g. -5.

Can we extend this functionality beyond just global scope, and have it work on, say, website level? It turns out we can, and it’s not that big of a change.

To investigate this first, we’d like to add to cart more than we have. By doing this, we end up in (omitting a few steps for brevity) vendor/magento/module-catalog-inventory/Model/StockStateProvider.php

     * Check quantity
     * @param StockItemInterface $stockItem
     * @param int|float $qty
     * @exception \Magento\Framework\Exception\LocalizedException
     * @return bool
    public function checkQty(StockItemInterface $stockItem, $qty)
        if (!$this->qtyCheckApplicable) {
            return true;
        if (!$stockItem->getManageStock()) {
            return true;
        if ($stockItem->getQty() - $stockItem->getMinQty() - $qty < 0) {
            switch ($stockItem->getBackorders()) {
                case \Magento\CatalogInventory\Model\Stock::BACKORDERS_YES_NONOTIFY:
                case \Magento\CatalogInventory\Model\Stock::BACKORDERS_YES_NOTIFY:
                    return false;
        return true;

What this does is check if qty we want to purchase is available. If it’s not, it performs a checks for the backorder functionality, which leads us further, to vendor/magento/module-catalog-inventory/Model/Configuration.php for retrieving the backorder information from database (core_config_data).

     * Retrieve backorders status
     * @param null|string|bool|int|\Magento\Store\Model\Store $store
     * @return int
    public function getBackorders($store = null)
        return (int) $this->scopeConfig->getValue(

Interesting thing to notice is the $store variable which is passed when fetching the configuration details. So, Magento is looking for a scoped value of backorder setting in database, but since there is none, a global is returned. What if we change scope for this configuration from global to website? Let’s try.

  1. Make a simple Magento 2 module (like this one, for example).
  2. Create a file at app\code\Your_Vendor\Your_Module\etc\adminhtml\system.xml

with the following content:

<?xml version="1.0"?>
 * Copyright © 2013-2017 Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
<config xmlns:xsi="" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
        <section id="cataloginventory" translate="label" type="text" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="1">
            <group id="item_options" translate="label comment" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
                <field id="backorders" translate="label" type="select" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1">
                    <comment>Changing can take some time due to processing whole catalog.</comment>

We’ve just copied over core configuration for backorders located at vendor/magento/module-catalog-inventory/etc/adminhtml/system.xml and updated showInWebsite from 0 to 1. Now we have configuration for backorders on a website level.

Couple of implications arise from this change:

  1. obviously, we can set backorder functionality on a website level
  2. since there is still only 1 inventory, we need to make sure that default scope for backorders is turned on, since cataloginventory_stockstatus indexer uses this to properly set status
  3. if default config is enabled (as point 2 suggests), then all products would be “Available” on frontend, which is not something we want for <b>all</b> websites (“Add to cart” button would be visible everywhere)

Thankfully, Magento offers a handy events which we can observe in order to “fix” stock status for website that does not have bacorders available.

vendor/magento/module-catalog/Model/Product.php has isSalable() method, called from frontend (when deciding whether add-to-cart button should be displayed). It is dispatching catalog_product_is_salable_before and catalog_product_is_salable_after which we can use, and display products as “not salable” if they are in the store that does not provide backorder functionality.

     * Check is product available for sale
     * @return bool
    public function isSalable()
        // ...
        <strong>$this->_eventManager->dispatch('catalog_product_is_salable_before', ['product' => $this]);</strong>
        $salable = $this->isAvailable();
        $object = new \Magento\Framework\DataObject(['product' => $this, 'is_salable' => $salable]);
            ['product' => $this, 'salable' => $object]
        // ...

What we can do here is create a custom observer that would listen to one of these events, and set product as not salable ($product->setIsSalable(false)) when a qty is <= 0 and store is the one where we don’t have backorders.

That’s it – a small change in the configuration, and a custom observer to make sure all data is properly displayed, gives us the ability to have backorder functionality on a website scope.

You made it all the way down here so you must enjoyed this post! You may also like:

How to improve usability for Magento 2 add to cart process Feb 05, 2018

How to improve usability for Magento 2 add to cart process

/ Leave a comment
Apr 26, 2013

Product Stock Alerts (not) working

/ Leave a comment
Lifestyle Out of Stock Nov 25, 2008

User friendly product availability message in Magento

/ Leave a comment

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <blockquote cite=""> <code> <del datetime=""> <em> <s> <strike> <strong>. You may use following syntax for source code: <pre><code>$current = "Inchoo";</code></pre>.