Stocks are key for any e-commerce. They tell you how many items you can actually offer. But sometimes you want customers to buy, even if stocks are gone. So you can admit backorders.
Woocommerce, like many other shop scripts, allow managers to admit backorders from customers. All you need to do is to click in a checkbox, and done – customers now can buy even if stocks are empty.
Now, a problem: what if sales boom, and customers buy thousands of products you don’t have to deliver? Can backorders be complied at any level? Or there is a reasonable limit for that?
Well, I believe there is always a limit for anything. So I wanted WC’s backorders to be controlled as well. However, the native WC config doesn’t allow us to establish any numeric limit for backorders. Once we allow it, the only way to stop selling is by changing back the checkbox.
Thankfully, we always can code a solution.
Where should I hook?
That is always the main question while developing for WP or Woocommerce. Sometimes you know exactly what you should do – but then you need to find where the procedures must be executed, or when.
As we are talking about a product with no stocks, and still being sold, the best hooking option seems to be in the cart. Ideally, so I thought from the beginning, some mechanism should be preventing customer sto check-out for orders in which backorders exceed a certain limit. However, I want to give this same customer the possibility of ordering a bit less – and not abandon the cart.
So, my personal research led to a pocketful of possible filters and actions. By the end of my studies, I found this hook to be the optimal choice: woocommerce_add_to_cart_validation
.
Well, checking for backorder limits during the validation process makes sense. And the arguments passed through this filter give us exactly what we need to find the “negative inventories”, the ordered quantity and the respective product ID that we need to gather all this data.
How the hook works?
The filter woocommerce_add_to_cart_validation
works in a very straightforward way. The function or method hooked to it must return a boolean value. It is based on three different variables: $has_stock, $product_id and $quantity. Let’s consider using it inside a class:
class Backorder
{
public function __construct() {
add_filter('woocommerce_add_to_cart_validation', [$this, 'backorder_limit'], 10, 3);
}
public function backorder_limit($has_stock, $product_id, $quantity): bool {
return false;
}
}
This prevents any product to be added to the cart. Explaining: by defaut, $has_stock returns true. When we apply the filter and return false
, that means any product is treated as out of stock.
Brilliant. So all we need to do now is to check whether the product is out of stock and on backorders or not. Then we set a limit – when we have 300 items backordered, we stop that and prevent any new orders to come.
Before burning the brains trying to remember functions or hooks on backorders, let’s remind a simple WC behaviour: backordered purchases still reduce inventories anyway. So if we get the product’s stock level after a backorder, we’ll get a negative number. So the limit – whatever that can be – is actually the “absolute” of the current stock level.
class Backorder
{
public function __construct() {
add_filter('woocommerce_add_to_cart_validation', [$this, 'backorder_limit'], 10, 3);
}
public function backorder_limit(
bool $has_stock,
int $product_id,
int $quantity) {
$limit = 300; // Backorders should never exceed this quantity overall
$product = wc_get_product($product_id); // Let's use the passed ID to get the product's object
$stocks = $product->get_stock_quantity(); // Retrives inventories for the product
// Conditional - if product is in backorder
if($stocks <= 0) {
if($quantity > ($limit - abs($stocks)) {
return false; // Quantity exceeds the limit, so the backorder is denied
}
}
return true;
}
}
Of course we can set new conditions or use an option for leveraging the limit – but the very important thing is: it is quick and easy to set a limit for backorders, without any plugin. By default, WC still doesn’t do that, but you can easily solve this problems with a few lines of code.
This is a very good feature that should be made basic function in woocommerce. I have tried your code but it keeps giving me error as I have limited knowledge in coding. Can you help provide simpler codes filter that can be used in functions.php?
Thank you so much for your post!