How to Add Custom Stock Status to Products in Woocommerce 4+

How to add custom stock status to products in WooCommerce 4+

Last update: 04/22 - Tested in WordPress 5.9.2 & WooCommerce 6.3.1

Code goes in functions.php file of the active child theme (or active theme).



Use woocommerce_product_stock_status_options

instead of woocommerce_product_options_stock_status.

This way you can immediately add a status instead of replace the existing dropdown



Also use woocommerce_get_availability_text & woocommerce_get_availability_class
opposite woocommerce_get_availability.

This way you don't have to add the existing statuses again

// Add new stock status options
function filter_woocommerce_product_stock_status_options( $status ) {
// Add new statuses
$status['pre_order'] = __( 'Pre order', 'woocommerce' );
$status['contact_us'] = __( 'Contact us', 'woocommerce' );

return $status;
}
add_filter( 'woocommerce_product_stock_status_options', 'filter_woocommerce_product_stock_status_options', 10, 1 );

// Availability text
function filter_woocommerce_get_availability_text( $availability, $product ) {
// Get stock status
switch( $product->get_stock_status() ) {
case 'pre_order':
$availability = __( 'Pre order', 'woocommerce' );
break;
case 'contact_us':
$availability = __( 'Contact us', 'woocommerce' );
break;
}

return $availability;
}
add_filter( 'woocommerce_get_availability_text', 'filter_woocommerce_get_availability_text', 10, 2 );

// Availability CSS class
function filter_woocommerce_get_availability_class( $class, $product ) {
// Get stock status
switch( $product->get_stock_status() ) {
case 'pre_order':
$class = 'pre-order';
break;
case 'contact_us':
$class = 'contact-us';
break;
}

return $class;
}
add_filter( 'woocommerce_get_availability_class', 'filter_woocommerce_get_availability_class', 10, 2 );

Sample Image



Use woocommerce_admin_stock_html to display the new stock status on the admin products list table

// Admin stock html
function filter_woocommerce_admin_stock_html( $stock_html, $product ) {
// Simple
if ( $product->is_type( 'simple' ) ) {
// Get stock status
$product_stock_status = $product->get_stock_status();
// Variable
} elseif ( $product->is_type( 'variable' ) ) {
foreach( $product->get_visible_children() as $variation_id ) {
// Get product
$variation = wc_get_product( $variation_id );

// Get stock status
$product_stock_status = $variation->get_stock_status();

/*
Currently the status of the last variant in the loop will be displayed.

So from here you need to add your own logic, depending on what you expect from your custom stock status.

By default, for the existing statuses. The status displayed on the admin products list table for variable products is determined as:

- Product should be in stock if a child is in stock.
- Product should be out of stock if all children are out of stock.
- Product should be on backorder if all children are on backorder.
- Product should be on backorder if at least one child is on backorder and the rest are out of stock.
*/
}
}

// Stock status
switch( $product_stock_status ) {
case 'pre_order':
$stock_html = '<mark class="pre-order" style="background:transparent none;color:#33ccff;font-weight:700;line-height:1;">' . __( 'Pre order', 'woocommerce' ) . '</mark>';
break;
case 'contact_us':
$stock_html = '<mark class="contact-us" style="background:transparent none;color:#cc33ff;font-weight:700;line-height:1;">' . __( 'Contact us', 'woocommerce' ) . '</mark>';
break;
}

return $stock_html;
}
add_filter( 'woocommerce_admin_stock_html', 'filter_woocommerce_admin_stock_html', 10, 2 );

Sample Image





Optional: if desired, the custom stock status can be used in hooks where you already have access to the $product object or you can use global $product.

1) No access to the $product object, use global $product as is the case for example with the woocommerce_shop_loop_item_title or the woocommerce_single_product_summary hook

function woocommerce_my_callback() {
// An example based on global $product
// Get the global product object
global $product;

// Is a WC product
if ( is_a( $product, 'WC_Product' ) ) {
// Get stock status
$product_stock_status = $product->get_stock_status();

// Use this line during testing, delete afterwards!
echo '<p style="color:red;font-size:20px;">DEBUG INFORMATION = ' . $product_stock_status . '</p>';

// Compare
if ( $product_stock_status == 'My custom stock status' ) {
// Etc..
}
}
}
add_action( 'woocommerce_shop_loop_item_title', 'woocommerce_my_callback', 10 );
add_action( 'woocommerce_single_product_summary', 'woocommerce_my_callback', 10 );

2) Access to the $product object, because it's passed by default to the callback function. As is the case for example with the woocommerce_get_price_html hook

function filter_woocommerce_get_price_html( $price, $product ) {
// Is a WC product
if ( is_a( $product, 'WC_Product' ) ) {
// Get stock status
$product_stock_status = $product->get_stock_status();

// Use this line during testing, delete afterwards!
echo '<p style="color:red;font-size:20px;">DEBUG INFORMATION = ' . $product_stock_status . '</p>';

// Compare
if ( $product_stock_status == 'My custom stock status' ) {
// Etc..
// $price .= ' my text';
}
}

return $price;
}
add_filter( 'woocommerce_get_price_html', 'filter_woocommerce_get_price_html', 10, 2 );

Add custom stock status in WooCommerce treated as out of stock

Since you indicate in your question that your custom stock statuses should contain the same functionality as the current 'outofstock' status, you can instead of rewrite/reuse all existing functionality of the 'outofstock' status for your custom stock statuses, use a workaround.

This will offer a simple solution, since otherwise you would have to overwrite some template files in addition to writing custom code.

The workaround can be applied as follows:

Step 1) add an extra custom field for your custom statuses, we will add this field under the existing stock status field:

// Add custom field
function action_woocommerce_product_options_stock_status() {
// Custom stock status
$options = array(
'empty' => __( 'N/A', 'woocommerce' ),
'permanent' => __( 'Permanent', 'woocommerce' ),
'supplier' => __( 'Supplier', 'woocommerce' ),
);

woocommerce_wp_select(
array(
'id' => '_custom_stock_status',
'wrapper_class' => 'stock_status_field hide_if_variable hide_if_external hide_if_grouped',
'label' => __( 'Custom stock status', 'woocommerce' ),
'options' => $options,
'desc_tip' => true,
'description' => __( 'Your description', 'woocommerce' ),
)
);
}
add_action( 'woocommerce_product_options_stock_status', 'action_woocommerce_product_options_stock_status', 10 );

// Save custom field
function action_woocommerce_admin_process_product_object( $product ) {
// Isset
if ( isset( $_POST['_custom_stock_status'] ) ) {
// Update
$product->update_meta_data( '_custom_stock_status', sanitize_text_field( $_POST['_custom_stock_status'] ) );
}
}
add_action( 'woocommerce_admin_process_product_object', 'action_woocommerce_admin_process_product_object', 10, 1 );

Result:

custom stock status


Step 2) when the product is 'out of stock', we will check whether a custom stock status has been set. If this is the case, we will visually change the text using the code below, so that the customer can see the new status. In the background/backend, however, the 'out of stock' status is still used and therefore automatically also the existing functionality:

// Availability text
function filter_woocommerce_get_availability_text( $availability, $product ) {
// Only for 'outofstock'
if ( $product->get_stock_status() == 'outofstock' ) {
// Get custom stock status
$custom_stock_status = $product->get_meta( '_custom_stock_status' );

// Compare and apply new text
if ( $custom_stock_status == 'permanent' ) {
$availability = __( 'Out of stock (Permanent)', 'woocommerce' );
} elseif ( $custom_stock_status == 'supplier' ) {
$availability = __( 'Out of stock (Supplier)', 'woocommerce' );
}
}

return $availability;
}
add_filter( 'woocommerce_get_availability_text', 'filter_woocommerce_get_availability_text', 10, 2 );

Add a custom stock status in WooCommerce

for anyone interested, here is complete solution, based on Laila's approach. Warning! My solution is intended to work only with WooCommerce "manage stock" option disabled! I am not working with exact amounts of items in stock. All code goes to functions.php, as usual.

Back-end part

Removing native stock status dropdown field. Adding CSS class to distinguish my new custom field. Dropdown has now new option "On Request".

function add_custom_stock_type() {
?>
<script type="text/javascript">
jQuery(function(){
jQuery('._stock_status_field').not('.custom-stock-status').remove();
});
</script>
<?php

woocommerce_wp_select( array( 'id' => '_stock_status', 'wrapper_class' => 'hide_if_variable custom-stock-status', 'label' => __( 'Stock status', 'woocommerce' ), 'options' => array(
'instock' => __( 'In stock', 'woocommerce' ),
'outofstock' => __( 'Out of stock', 'woocommerce' ),
'onrequest' => __( 'On Request', 'woocommerce' ), // The new option !!!
), 'desc_tip' => true, 'description' => __( 'Controls whether or not the product is listed as "in stock" or "out of stock" on the frontend.', 'woocommerce' ) ) );
}
add_action('woocommerce_product_options_stock_status', 'add_custom_stock_type');

Sadly, WooCommerce will save only "instock" or "outofstock" values with its native functions. So after all product data processing, I have to re-save my stock status again.

function save_custom_stock_status( $product_id ) {
update_post_meta( $product_id, '_stock_status', wc_clean( $_POST['_stock_status'] ) );
}
add_action('woocommerce_process_product_meta', 'save_custom_stock_status',99,1);

Template part

And the last thing - I have to alter data returned by product get_availability() function. When "managing stock" is off, WooCommerce only knows "instock" and "outofstock" values, again. So I have check stock status on my own.

function woocommerce_get_custom_availability( $data, $product ) {
switch( $product->stock_status ) {
case 'instock':
$data = array( 'availability' => __( 'In stock', 'woocommerce' ), 'class' => 'in-stock' );
break;
case 'outofstock':
$data = array( 'availability' => __( 'Out of stock', 'woocommerce' ), 'class' => 'out-of-stock' );
break;
case 'onrequest':
$data = array( 'availability' => __( 'On request', 'woocommerce' ), 'class' => 'on-request' );
break;
}
return $data;
}
add_action('woocommerce_get_availability', 'woocommerce_get_custom_availability', 10, 2);

Maybe it's not bulletproof solution ... I will update it, eventually.

Display custom stock status based on stock quantity in admin products list table

If you like to display the custom stock status on the admin products list table, based on the current stock quantity, you can just use:

// Admin stock html
function filter_woocommerce_admin_stock_html( $stock_html, $product ) {
if ( $product->get_stock_quantity() == 1 ) {
$stock_html = '<mark class="my-class" style="background:transparent none;color:#33ccff;font-weight:700;line-height:1;">' . __( 'Available soon', 'woocommerce' ) . '</mark>';
}

return $stock_html;
}
add_filter( 'woocommerce_admin_stock_html', 'filter_woocommerce_admin_stock_html', 10, 2 );

Related: How to add custom stock status to products in WooCommerce 4+

WooCommerce - Custom stock status and variable products

Unfortunately WooCommerce not supporting any custom stock status other than its own stock statuses - instock, outofstock & onbackorder. We can achieve the custom stock status by overriding the _stock_status meta in some cases but will not be successful in all cases (like variations update).

In the above functions, you have removed the onbackorder stock status and if particular product stock status changed to 'onbackorder', then it will not be shown. Note that this is the reason for showing 'instock' since its the first option in your select box.

If you are going to use the stock status purely for admin panel display purpose only, then you can achieve this by using the following functions.

/* add custom stock status */
function woocommerce_add_custom_stock_status() {
?>
<script type="text/javascript">
jQuery(function(){
jQuery('._stock_status_field').not('.custom-stock-status').remove();
});
</script>
<?php
/* update custom status if backorder if varations updated */
$real_stock_status = get_post_meta($_REQUEST['post'], '_stock_status', true );
if($real_stock_status=="onbackorder") {
$stock_status = get_post_meta($_REQUEST['post'], '_custom_stock_status', true ); //get status from custom meta
update_post_meta($_REQUEST['post'], '_stock_status', wc_clean( $stock_status ));
}

woocommerce_wp_select( array( 'id' => '_stock_status', 'wrapper_class' => 'custom-stock-status', 'label' => __( 'Stock status', 'woocommerce' ), 'options' => array(
'instock' => __( 'På lager/fjernlager', 'woocommerce' ),
'bestillingsvare' => __( 'Bestillingsvare', 'woocommerce' ), // The new option !!!
'outofstock' => __( 'Ikke på lager', 'woocommerce' ),
), 'desc_tip' => true, 'description' => __( 'Controls whether or not the product is listed as "in stock" or "out of stock" on the frontend.', 'woocommerce' ) ) );
}
add_action('woocommerce_product_options_stock_status', 'woocommerce_add_custom_stock_status');

/* save custom stock status */
function woocommerce_save_custom_stock_status( $product_id ) {
update_post_meta( $product_id, '_stock_status', wc_clean( $_POST['_stock_status'] ) );
update_post_meta( $product_id, '_custom_stock_status', wc_clean( $_POST['_stock_status'] ) ); //save another custom meta since '_stock_status' will be overridden
}
add_action('woocommerce_process_product_meta', 'woocommerce_save_custom_stock_status',99,1);

/* get custom stock status */
function get_custom_stock_status( $data, $product ) {
switch( $product->stock_status ) {
case 'instock':
$data = array( 'availability' => __( 'På lager/fjernlager', 'woocommerce' ), 'class' => 'in-stock' );
break;
case 'bestillingsvare':
$data = array( 'availability' => __( 'Bestillingsvare', 'woocommerce' ), 'class' => 'bestillings-vare' );
break;
case 'outofstock':
$data = array( 'availability' => __( 'Ikke på lager', 'woocommerce' ), 'class' => 'out-of-stock' );
break;
}
return $data;
}
add_action('woocommerce_get_availability', 'get_custom_stock_status', 10, 2);

/* change custom stock status after order completion */
function woocommerce_order_change_custom_stock_status( $order_id ){
if( ! $order_id ) return;

$order = wc_get_order( $order_id );
$items = $order->get_items();
foreach ( $items as $item ) {
$product_id = $item->get_product_id();

$real_stock_status = get_post_meta($product_id, '_stock_status', true );
if($real_stock_status=="onbackorder") {
$stock_status = get_post_meta($product_id, '_custom_stock_status', true ); //get status from custom meta
update_post_meta($product_id, '_stock_status', wc_clean( $stock_status ));
}
}
}
add_action( 'woocommerce_thankyou', 'woocommerce_order_change_custom_stock_status', 10, 1 );

Hope this helps.

Display product stock status on single product pages in WooCommerce when price field is empty

You will see that if the price is empty, besides not showing the product stock status, the price (the HTML code) for this will also not be displayed. Because the product is considered as not purchasable if the price is empty.

So in order to display the product stock status, you should make the product purchasable, if the price is empty. However, I do not believe this is your intention?

So instead of focusing on displaying the product stock status, it will be much simpler to display something else (HTML/text) where the price is normally displayed,
and that can be done by using the woocommerce_empty_price_html filter hook

  1. So either you drop the whole idea of ​​the product stock status and simply use:
function filter_woocommerce_empty_price_html( $html, $product ) {
// NOT true on a single product page, RETURN
if ( ! is_product() ) return $html;

// Add HTML
$html = '<p>My text</p>';

return $html;
}
add_filter( 'woocommerce_empty_price_html', 'filter_woocommerce_empty_price_html', 10, 2 );



  1. Or you build further on the product stock status and on that basis you get:
function filter_woocommerce_empty_price_html( $html, $product ) {
// NOT true on a single product page, RETURN
if ( ! is_product() ) return $html;

// Get stock status
$product_stock_status = $product->get_stock_status();

// Compare
if ( $product_stock_status == 'MY CUSTOM STATUS' ) {
// Add HTML
$html = '<p>My text based on product stock status</p>';
}

return $html;
}
add_filter( 'woocommerce_empty_price_html', 'filter_woocommerce_empty_price_html', 10, 2 );

The only downside to this solution(s) is that your new HTML/text will not be displayed where you normally see the product stock status, but where the price is normally displayed.

Disable add to cart button based on WooCommerce product custom stock status

You can use the following to disable add to cart button based on a custom stock status (where you will replace custom_status_slug by your custom status slug):

add_filter('woocommerce_is_purchasable', 'filter_is_purchasable_callback', 10, 2 );
add_filter('woocommerce_variation_is_purchasable', 'filter_is_purchasable_callback', 10, 2 );
function filter_is_purchasable_callback( $purchasable, $product ) {
if ( $product->get_stock_status() === 'custom_status_slug' ) {
return false;
}

return $purchasable;
}

Code goes in functions.php file of the active child theme (or active theme). Tested and works.



Related Topics



Leave a reply



Submit