How to Use Decimal Quantities with WooCommerce Stock

I was volunteering on the WordPress support forms and someone asked how they could use decimal quantities with WooCommerce stock.

This would take care of situations where you’re selling half of a sandwich, for example.

Let’s start off with some simple code: this will allow you to start inserting values as decimal quantities from the back end, removing the limit that is originally set which only permits you to add whole number quantities.

remove_filter('woocommerce_stock_amount','intval');
add_filter('woocommerce_stock_amount', 'floatval');

Now we’re going to enter in code that will adjust the minimum quantity price as well as the step value when a user selects a quantity when purchasing an item.

First, we’ll deal with the step value.

As you can see, customers can “step” through values by either using the up or down arrows on their keyboard or clicking on the up or down arrows

If the quantity is not a whole number then we want the customer to be able to increase or decrease a quantity by a decimal, otherwise we want to have the step value be a whole number:

add_filter('woocommerce_quantity_input_step','ian_sackofwits_woo_decimal_step', 10, 2);

function ian_sackofwits_woo_decimal_step($val, $product) {
	$qnt = $product->get_stock_quantity();
	
	$num_decimals = strlen(substr(strrchr($qnt, "."), 1));
	
	$step = is_float($qnt) ? "." . "0" * $num_decimals . "1" : '1';
	
	return $step;
}

Now, onto the minimum quantity that can be selected.

The minimum quantity should be dependent on the decimal value of the quantity. If the step value is .01, like in this case, the minimum value should also be .01

Now, we will use the same logic in determining the minimum value for the quantity:

add_filter('woocommerce_quantity_input_min','ian_sackofwits_woo_decimal_min', 10, 2);

function ian_sackofwits_woo_decimal_min($val, $product) {
	$qnt = $product->get_stock_quantity();
	
	$num_decimals = strlen(substr(strrchr($qnt, "."), 1));
	
	$step = is_float($qnt) ? "." . "0" * $num_decimals . "1" : '1';
	
	return $step;
}

And, finally, now we have to deal with an issue showing the unit price for processed orders. :

add_filter('woocommerce_order_amount_item_total', 'ian_sackofwits_unit_total_fix', 10, 5);

function ian_sackofwits_unit_total_fix($total, $order, $item, $inc_tax = false, $round = true) {
    $qnt = (!empty($item['qty']) && $item['qty'] != 0) ? $item['qty'] : 1;
    $total = $inc_tax ? $item['line_total'] + $item['line_tax'] / $qnt : $total = $item['line_total'] / $qnt;
    $total = $round ? round( $total, 2 ) : $total;
    return $total;
}

Here’s the full complete code:

remove_filter('woocommerce_stock_amount','intval');
add_filter('woocommerce_stock_amount', 'floatval');

add_filter('woocommerce_quantity_input_step','ian_sackofwits_woo_decimal_step', 10, 2);

function ian_sackofwits_woo_decimal_step($val, $product) {
	$qnt = $product->get_stock_quantity();
	
	$num_decimals = strlen(substr(strrchr($qnt, "."), 1));
	
	$step = is_float($qnt) ? "." . "0" * $num_decimals . "1" : '1';
	
	return $step;
}

add_filter('woocommerce_quantity_input_min','ian_sackofwits_woo_decimal_min', 10, 2);

function ian_sackofwits_woo_decimal_min($val, $product) {
	$qnt = $product->get_stock_quantity();
	
	$num_decimals = strlen(substr(strrchr($qnt, "."), 1));
	
	$step = is_float($qnt) ? "." . "0" * $num_decimals . "1" : '1';
	
	return $step;
}

add_filter('woocommerce_order_amount_item_total', 'ian_sackofwits_unit_total_fix', 10, 5);

function ian_sackofwits_unit_total_fix($total, $order, $item, $inc_tax = false, $round = true) {
    $qnt = (!empty($item['qty']) && $item['qty'] != 0) ? $item['qty'] : 1;
    $total = $inc_tax ? $item['line_total'] + $item['line_tax'] / $qnt : $total = $item['line_total'] / $qnt;
    $total = $round ? round( $total, 2 ) : $total;
    return $total;
}

Add this code to your theme’s functions.php file or use a plugin such as Code Snippets.

Leave a comment

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