X

Not Enough In Stock

Is anybody experiencing a message on their shopping cart saying that there is not enough in stock for their products? This has been a pretty popular topic recently and I just wanted to shed some light on a possible, less obvious reason as to why this could be.

A couple people posted in Slack and Facebook looking for help trying to find out why they were not able to checkout. Even though their store says a product is in stock and available.

So this is new to me, may not be new to WooCommerce, but the checkout process is now checking not only the amount of products in stock, but the amount of stock tied to pending orders! So to give you a good idea on what I mean, here is a simple example to help explain.

Not Enough In Stock? Wait.. Lies!

I have Product A in stock with 10 available. I have 9 orders with the 1 of Product A on the order, these orders are waiting for payment. In this situation, I can absolutely checkout and purchase Product A x1. BUT if I wanted to purchase 2, I would get a big message on my cart saying that there is not enough in stock for me to purchase! Even when the product page says there are 10 available. Confusing right? Uhhhhhh…… wtfudge?

So I started debugging to figure it out, found this chunk of code within the WC_Cart->check_cart_item_stock()

/**
 * Finally consider any held stock, from pending orders.
 */
if ( get_option( 'woocommerce_hold_stock_minutes' ) > 0 && ! $product->backorders_allowed() ) {
	$order_id   = isset( WC()->session->order_awaiting_payment ) ? absint( WC()->session->order_awaiting_payment ) : 0;
	$held_stock = $wpdb->get_var(
		$wpdb->prepare( "
			SELECT SUM( order_item_meta.meta_value ) AS held_qty
			FROM {$wpdb->posts} AS posts
			LEFT JOIN {$wpdb->prefix}woocommerce_order_items as order_items ON posts.ID = order_items.order_id
			LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta as order_item_meta ON order_items.order_item_id = order_item_meta.order_item_id
			LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta as order_item_meta2 ON order_items.order_item_id = order_item_meta2.order_item_id
			WHERE 	order_item_meta.meta_key   = '_qty'
			AND 	order_item_meta2.meta_key  = %s AND order_item_meta2.meta_value  = %d
			AND 	posts.post_type            IN ( '" . implode( "','", wc_get_order_types() ) . "' )
			AND 	posts.post_status          = 'wc-pending'
			AND		posts.ID                   != %d;",
			'variation' === get_post_type( $product->get_stock_managed_by_id() ) ? '_variation_id' : '_product_id',
			$product->get_stock_managed_by_id(),
			$order_id
		)
	);

	if ( $product->get_stock_quantity() < ( $held_stock + $product_qty_in_cart[ $product->get_stock_managed_by_id() ] ) ) {
		/* translators: 1: product name 2: minutes */
		$error->add( 'out-of-stock', sprintf( __( 'Sorry, we do not have enough "%1$s" in stock to fulfill your order right now. Please try again in %2$d minutes or edit your cart and try again. We apologize for any inconvenience caused.', 'woocommerce' ), $product->get_name(), get_option( 'woocommerce_hold_stock_minutes' ) ) );
		return $error;
	}
}

What this section is doing is checking if you are holding onto orders for payment. This setting is found in WooCommerce > Settings > Products > Inventory

If this setting is blank, the above code will not fire. If this is set, what this function does is it will reserve the products for the orders that have not been paid for. Preventing orders from being processed and causing you to run out of inventory. Of course backorders must be disabled on the product if you do want to use this handy feature.

So out of the box – this should not be a long lasting issue, but in order for it to keep your system clean – you will need to make sure your WP Cron is running! The cron is scheduled to clean out unpaid for orders, ultimately freeing up products stock.

Resolution

Well, you have a few options – really depends on how you want to move forward and how you want your store to process.

  • Disable Hold Stock
  • Allow Back Orders on the products
  • Clean out Old Orders

Conclusion

This is a great feature, very handy! But without working knowledge of why this is happening, something completely unrelated can cause huge problems for your customers and headaches for your store manager. Nothing is broken, this was by design, high 5 to WC at the end of the day.

I hope this helps answer some questions, freak out moments and enables you to make your next move.

James Tedrow :Originally born in Florida, moved to Arizona and started programming at 13, now 29. Enjoying life with computers, sports, skateboarding and my family. I enjoy short walks on the beach (I get tired fast), loud music (hit me with that bass) and to help others succeed.

View Comments (4)