variation custom stock status linking to parent [duplicate] - php

I am using the following code to add new stock statuses in WooCommerce 4+
The new statuses are:
Preorder
Contact us
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' => 'custom-stock-status', 'label' => __( 'Stock status', 'woocommerce' ), 'options' => array(
'instock' => __( 'Available', 'woocommerce' ), //changed the name
'outofstock' => __( 'Sold out', 'woocommerce' ), //changed the name
'onbackorder' => __( 'Preorder : Pending Distributor release', 'woocommerce' ), //changed the name
'contact' => __( 'Contact us for Availability', 'woocommerce' ), //added new one
'preorder' => __( 'On Preorder: Pending Distributor release', 'woocommerce' ), //added new one
), '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');
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);
function woo_add_custom_general_fields_save_two( $post_id ){
// Select
$woocommerce_select = $_POST['_stock_status'];
if( !empty( $woocommerce_select ) )
update_post_meta( $post_id, '_stock_status', esc_attr( $woocommerce_select ) );
else
update_post_meta( $post_id, '_stock_status', '' );
}
function woocommerce_get_custom_availability( $data, $product ) {
switch( $product->stock_status ) {
case 'instock':
$data = array( 'availability' => __( 'Available', 'woocommerce' ), 'class' => 'in-stock' ); //changed name
break;
case 'outofstock':
$data = array( 'availability' => __( 'Sold Out', 'woocommerce' ), 'class' => 'out-of-stock' ); //changed name
break;
case 'onbackorder':
$data = array( 'availability' => __( 'Preorder : Pending Distributor release', 'woocommerce' ), 'class' => 'onbackorder' ); //changed name
break;
case 'contact':
$data = array( 'availability' => __( 'Contact us for Availability', 'woocommerce' ), 'class' => 'contact' ); //added new one
break;
case 'preorder':
$data = array( 'availability' => __( 'On Preorder : Pending Distributor release', 'woocommerce' ), 'class' => 'preorder' ); //added new one
break;
}
return $data;
}
add_action('woocommerce_get_availability', 'woocommerce_get_custom_availability', 10, 4);
Works:
Backend: The new status is added in the dropdown menu, I can select the status I want
Does not work:
Front end: on single product page is not showing the correct status
Backend: Display the new status on the admin products list table
Someone who can assist me with this?

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 );
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 );
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 );

In addition to filters provided by 7uc1f3r woocommerce_product_export_product_column_stock_status filter is required to display custom stock status in exported products CSV file:
function add_custom_stock_csv_data( $_, $product ) {
$status = $product->get_stock_status( 'edit' );
switch( $status ) {
case 'pre_order':
case 'contact_us':
return $status;
case 'onbackorder':
return 'backorder';
case 'instock':
return 1;
default:
return 0;
}
}
add_filter( 'woocommerce_product_export_product_column_stock_status', 'add_custom_stock_csv_data', 10, 2 );

Related

Add custom stock status in WooCommerce treated as out of stock

I created 2 new custom stock statuses:
Out of stock (Permanent)
Out of stock (Supplier)
As you can guess, I want them both to be treated as out of stock.
I used some extra code to hide the products with these custom statuses from the front end but I can not hide them from the ajax search (live) and also someone can add the product in cart.
(I use the Flatsome theme and the live search is from there)
Based on: How to add custom stock status to products in WooCommerce 4+
& Hide all products with a specific stock status from WooCommerce catalog, this is my code attempt:
// Add new stock status options
function filter_woocommerce_product_stock_status_options( $status ) {
// Add new statuses
$status['permanent'] = __( 'Out of stock (Permanent)', 'woocommerce' );
$status['supplier'] = __( 'Out of stock (Supplier)', '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 'permanent':
$availability = __( 'Out of stock (Permanent)', 'woocommerce' );
break;
case 'supplier':
$availability = __( 'Out of stock (Supplier)', '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 'permanent':
$class = 'permanent';
break;
case 'supplier':
$class = 'supplier';
break;
}
return $class;
}
add_filter( 'woocommerce_get_availability_class', 'filter_woocommerce_get_availability_class', 10, 2 );
// 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();
}
}
// Stock status
switch( $product_stock_status ) {
case 'permanent':
$stock_html = '<mark class="permanent" style="background:transparent none;color:#33ccff;font-weight:700;line-height:1;">' . __( 'Out of stock (Permanent)', 'woocommerce' ) . '</mark>';
break;
case 'supplier':
$stock_html = '<mark class="supplier" style="background:transparent none;color:#cc33ff;font-weight:700;line-height:1;">' . __( 'Out of stock (Supplier)', 'woocommerce' ) . '</mark>';
break;
}
return $stock_html;
}
add_filter( 'woocommerce_admin_stock_html', 'filter_woocommerce_admin_stock_html', 10, 2 );
//hide specific stock status
add_action( 'woocommerce_product_query_meta_query', 'custom_product_query_meta_query', 1000 );
function custom_product_query_meta_query( $meta_query ) {
if ( ! is_admin() ) {
$meta_query[] = array(
'key' => '_stock_status',
'value' => 'permanent',
'compare' => '!=',
);
}
if ( ! is_admin() ) {
$meta_query[] = array(
'key' => '_stock_status',
'value' => 'supplier',
'compare' => '!=',
);
}
return $meta_query;
}
Any advice on how to handle both custom stock statuses 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:
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 custom field value after selected WooCommerce product variation price

We have added a select field to simple products, so that we can define the price unit and display the unit after the price on the single product page. This works fine.
Additionally we have added a select field for variable products to define the price unit of each variation. This price unit should be displayed after the variation price on the product page:
<?php
function mytheme_woo_add_custom_variation_fields( $loop, $variation_data, $variation ) {
echo '<div class="options_group form-row form-row-full">';
$value = get_post_meta( $post->ID, '_variable_select_field', true );
if( empty( $value ) ) $value = '';
// Select
woocommerce_wp_select(
array(
'id' => '_variable_select_field[' . $variation->ID . ']',
'label' => __( 'Mengeneinheit', 'woocommerce' ),
'value' => get_post_meta( $variation->ID, '_variable_select_field', true ),
'options' => array(
'' => __( 'ohne', 'woocommerce' ),
'Stück' => __( 'Stück', 'woocommerce' ),
'Paar' => __( 'Paar', 'woocommerce' ),
'Karton' => __( 'Karton', 'woocommerce' ),
'Packung' => __( 'Packung', 'woocommerce' ),
'Set' => __( 'Set', 'woocommerce' ),
'lfm' => __( 'Laufmeter', 'woocommerce' ),
'm²' => __( 'm2', 'woocommerce' ),
)
)
);
echo '</div>';
}
// Variations tab
add_action( 'woocommerce_variation_options_pricing', 'mytheme_woo_add_custom_variation_fields', 10, 3 );
// Add custom field for variations
function load_variation_settings_fields( $variations ) {
$variations['_variable_select_field'] = get_post_meta( $variations[ 'variation_id' ], '_variable_select_field', true );
return $variations;
}
add_filter( 'woocommerce_available_variation', 'load_variation_settings_fields' );
//Save variable product field
function mytheme_woo_add_custom_variation_fields_save( $post_id ){
$woocommerce_select_field = $_POST['_variable_select_field'][ $post_id ];
update_post_meta( $post_id, '_variable_select_field', esc_attr( $woocommerce_select_field ) );
}
add_action( 'woocommerce_save_product_variation', 'mytheme_woo_add_custom_variation_fields_save', 10, 2 );
/* Display the custom field after the price */
function mytheme_display_woo_custom_fields( $price ){
global $post, $product;
if ( $product->is_type( 'variable' ) ){
$variation_price_unit = get_post_meta( $post->ID, '_variable_select_field', true );
$price .= $variation_price_unit;
}
return $price;
}
add_action( 'woocommerce_get_price_html', 'mytheme_display_woo_custom_fields', 15 );
However, it does not work , I cannot find the right way to do that.
Can anyone let me know how to display the saved value from the product variation after the price?
Thank you!
This simple code snippet will display after selected variation price the related price unit as follows:
add_filter( 'woocommerce_available_variation', 'display_price_unit_after_variations_price_html', 10, 3) ;
function display_price_unit_after_variations_price_html( $data, $product, $variation ) {
if ( $mengeneinheit = $variation->get_meta('_variable_select_field') )
$data['price_html'] .= ' ' . $mengeneinheit;
return $data;
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
You should rename the key _variable_select_field everywhere with for example _mengeneinheit as it will be more explicit (or in english _price_unit).

Display a product custom field for variable products in WooCommerce email order items

I've added a Custom SKU in products to use for vendor SKU for order confirmation email. What I've created works only for simple products and not variable products.
Custom SKU,
I added this to the functions.php
function jk_add_custom_sku() {
$args = array(
'label' => __( 'Custom SKU', 'woocommerce' ),
'placeholder' => __( 'Enter custom SKU here', 'woocommerce' ),
'id' => 'jk_sku',
'desc_tip' => true,
'description' => __( 'This SKU is for internal use only.', 'woocommerce' ),
);
woocommerce_wp_text_input( $args );
}
add_action( 'woocommerce_product_options_sku', 'jk_add_custom_sku' );
function jk_save_custom_sku( $post_id ) {
// grab the custom SKU from $_POST
$custom_sku = isset( $_POST[ 'jk_sku' ] ) ? sanitize_text_field( $_POST[ 'jk_sku' ] ) : '';
// grab the product
$product = wc_get_product( $post_id );
// save the custom SKU using WooCommerce built-in functions
$product->update_meta_data( 'jk_sku', $custom_sku );
$product->save();
}
add_action( 'woocommerce_process_product_meta', 'jk_save_custom_sku' );
Next I modified the email template email-order-items.php adding to the SKU section to check if Custom SKU exists.
<?php
// Show title/image etc.
if ( $show_image ) {
echo wp_kses_post( apply_filters( 'woocommerce_order_item_thumbnail', $image, $item ) );
}
// Product name.
echo wp_kses_post( apply_filters( 'woocommerce_order_item_name', $item->get_name(), $item, false ) );
// SKU.
if ( $show_sku && $sku ) {
echo wp_kses_post( ' (#' . $sku . ')' );
// load the custom SKU
$custom_sku = get_post_meta( $product->get_id(), 'jk_sku', true );
if ( is_string( $custom_sku ) ) { // only show the custom SKU if it's set
echo "<br>" . wp_kses_post( "Custom SKU: $custom_sku" ); // change this line if needed
}
}
// allow other plugins to add additional product information here.
do_action( 'woocommerce_order_item_meta_start', $item_id, $item, $order, $plain_text );
wc_display_item_meta(
$item,
array(
'label_before' => '<strong class="wc-item-meta-label" style="float: ' . esc_attr( $text_align ) . '; margin-' . esc_attr( $margin_side ) . ': .25em; clear: both">',
)
);
// allow other plugins to add additional product information here.
do_action( 'woocommerce_order_item_meta_end', $item_id, $item, $order, $plain_text );
?>
</td>
The Custom SKU only shows up on the email for Simple products. I need it to work with Variable products. There is only one Custom SKU for each product not for each variable product.
I have revisited a bit your code and changed the meta key to _sku2 just like WooCommerce meta keys starting with an underscore:
add_action( 'woocommerce_product_options_sku', 'add_product_sku2_custom_field' );
function add_product_sku2_custom_field() {
$field_key = '_sku2';
woocommerce_wp_text_input( array(
'id' => $field_key,
'label' => __( 'Custom SKU', 'woocommerce' ),
'placeholder' => __( 'Enter custom SKU here', 'woocommerce' ),
'desc_tip' => true,
'description' => __( 'This SKU is for internal use only.', 'woocommerce' ),
) );
}
add_action( 'woocommerce_admin_process_product_object', 'save_product_sku2_custom_field_value' );
function save_product_sku2_custom_field_value( $product ) {
$field_key = '_sku2';
if ( isset($_POST[$field_key]) ) {
$product->update_meta_data( $field_key, sanitize_text_field($_POST[$field_key]) );
}
}
For product variations, you need to get the parent variable product to get a "Custom sku" displayed (see at the code at the end)
Now to enable an additional custom SKU to product variations of a variable product too, use the following:
add_action( 'woocommerce_variation_options_pricing', 'add_product_variation_sku2_custom_field', 10, 3 );
function add_product_variation_sku2_custom_field( $loop, $variation_data, $variation ){
$field_key = '_sku2';
woocommerce_wp_text_input( array(
'id' => $field_key.'['.$loop.']',
'label' => __( 'Custom SKU', 'woocommerce' ),
'placeholder' => __( 'Enter custom SKU here', 'woocommerce' ),
'desc_tip' => true,
'description' => __( 'This SKU is for internal use only.', 'woocommerce' ),
'value' => get_post_meta( $variation->ID, $field_key, true )
) );
}
add_action( 'woocommerce_save_product_variation', 'save_product_variation_sku2_custom_field_value', 10, 2 );
function save_product_variation_sku2_custom_field_value( $variation_id, $i ){
$field_key = '_sku2';
if( isset($_POST[$field_key][$i]) ){
update_post_meta( $variation_id, $field_key, sanitize_text_field($_POST[$field_key][$i]) );
}
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
And in your template custom code you will replace:
// SKU.
if ( $show_sku && $sku ) {
echo wp_kses_post( ' (#' . $sku . ')' );
// load the custom SKU
$custom_sku = get_post_meta( $product->get_id(), 'jk_sku', true );
if ( is_string( $custom_sku ) ) { // only show the custom SKU if it's set
echo "<br>" . wp_kses_post( "Custom SKU: $custom_sku" ); // change this line if needed
}
}
by the following (that will work for variable products or product variations):
// SKU (and SKU2)
if ( $show_sku && $sku ) {
echo wp_kses_post( ' (#' . $sku . ')' );
// load the custom SKU
$sku2 = $product->get_meta('_sku2');
// For product variations with empty SKU (get the parent variable product SKU)
if ( empty($sku2) && $product->is_type('variation') ) {
// Get parent variable product Id
$parent_product_id = $product->get_parent_id();
$parent_product = wc_get_product($parent_product_id);
$sku2 = $parent_product->get_meta('_sku2');
}
// only show the custom SKU if it's set (and product variation too)
if ( ! empty($sku2) ) {
echo "<br>" . wp_kses_post( sprintf( __("Custom SKU: %s", "woocommerce"), $sku2 ) ); // change this line if needed
}
}
For product variations, if there is not any "custom sku", it will try to get the parent variable product "custom sku".
It should work.

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

I am using the following code to add new stock statuses in WooCommerce 4+
The new statuses are:
Preorder
Contact us
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' => 'custom-stock-status', 'label' => __( 'Stock status', 'woocommerce' ), 'options' => array(
'instock' => __( 'Available', 'woocommerce' ), //changed the name
'outofstock' => __( 'Sold out', 'woocommerce' ), //changed the name
'onbackorder' => __( 'Preorder : Pending Distributor release', 'woocommerce' ), //changed the name
'contact' => __( 'Contact us for Availability', 'woocommerce' ), //added new one
'preorder' => __( 'On Preorder: Pending Distributor release', 'woocommerce' ), //added new one
), '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');
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);
function woo_add_custom_general_fields_save_two( $post_id ){
// Select
$woocommerce_select = $_POST['_stock_status'];
if( !empty( $woocommerce_select ) )
update_post_meta( $post_id, '_stock_status', esc_attr( $woocommerce_select ) );
else
update_post_meta( $post_id, '_stock_status', '' );
}
function woocommerce_get_custom_availability( $data, $product ) {
switch( $product->stock_status ) {
case 'instock':
$data = array( 'availability' => __( 'Available', 'woocommerce' ), 'class' => 'in-stock' ); //changed name
break;
case 'outofstock':
$data = array( 'availability' => __( 'Sold Out', 'woocommerce' ), 'class' => 'out-of-stock' ); //changed name
break;
case 'onbackorder':
$data = array( 'availability' => __( 'Preorder : Pending Distributor release', 'woocommerce' ), 'class' => 'onbackorder' ); //changed name
break;
case 'contact':
$data = array( 'availability' => __( 'Contact us for Availability', 'woocommerce' ), 'class' => 'contact' ); //added new one
break;
case 'preorder':
$data = array( 'availability' => __( 'On Preorder : Pending Distributor release', 'woocommerce' ), 'class' => 'preorder' ); //added new one
break;
}
return $data;
}
add_action('woocommerce_get_availability', 'woocommerce_get_custom_availability', 10, 4);
Works:
Backend: The new status is added in the dropdown menu, I can select the status I want
Does not work:
Front end: on single product page is not showing the correct status
Backend: Display the new status on the admin products list table
Someone who can assist me with this?
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 );
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 );
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 );
In addition to filters provided by 7uc1f3r woocommerce_product_export_product_column_stock_status filter is required to display custom stock status in exported products CSV file:
function add_custom_stock_csv_data( $_, $product ) {
$status = $product->get_stock_status( 'edit' );
switch( $status ) {
case 'pre_order':
case 'contact_us':
return $status;
case 'onbackorder':
return 'backorder';
case 'instock':
return 1;
default:
return 0;
}
}
add_filter( 'woocommerce_product_export_product_column_stock_status', 'add_custom_stock_csv_data', 10, 2 );

WooCommerce - Custom stock status and variable products [duplicate]

This question already has answers here:
How to add custom stock status to products in WooCommerce 4+
(2 answers)
Closed 2 years ago.
On our site, we're using custom stock statuses for our products. We use the below code to achieve that, and it works, but there are some issues on variable products, where it changes back to the default status.
We want to use it on variable products, to display a main stock status for the whole product, but when we select our custom stock status, it will suddenly change it back to the standard stock status after some time. It saves the setting when we update the product, but it will eventually change it back after some time.
Here is our code, which is placed in our functions.php file. Hope you can help or point me in the right direction:
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' => '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', 'add_custom_stock_type');
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);
function woo_add_custom_general_fields_save_two( $post_id ){
// Select
$woocommerce_select = $_POST['_stock_status'];
if( !empty( $woocommerce_select ) )
update_post_meta( $post_id, '_stock_status', esc_attr( $woocommerce_select ) );
else
update_post_meta( $post_id, '_stock_status', '' );
}
function woocommerce_get_custom_availability( $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', 'woocommerce_get_custom_availability', 10, 2);
2019 Update:
In February 2019, WooCommerce added a filter to version 3.6.0 that allows users to add their own custom stock statuses.
Inside the function wc_get_product_stock_status_options, which defines the different stock statuses, there are 3 default built-in statuses: instock, outofstock, and onbackorder.
In order to add a custom status or remove a built-in one, you can simply use the new woocommerce_product_stock_status_options filter:
add_filter('woocommerce_product_stock_status_options', 'add_custom_stock_statuses');
function add_custom_stock_statuses($statuses) {
// Add a new status
$statuses['customstatus'] = __( 'My Custom Status', 'plugin-name' );
// Remove a built-in status
unset($statuses['onbackorder']);
return $statuses;
}
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.

Categories