In this tutorial we'll discover how to add our own custom order statuses, and leverage them correctly.
WooCommerce is one of the most popular e-Commerce solutions for WordPress-based websites. In this tutorial we’ll discover how to add our own custom order statuses, and leverage them correctly.
Note: For the following actions and filters, you’ll probably want to wrap them in your own custom plugin so everything is neat and tidy.
Since order statuses in WooCommerce are simply Post Statuses rebranded, we can leverage the register_post_status function to add our new order status. Let’s create an ‘init’ hook and add our new custom Order Status:
add_action( 'init', function() { $slug = 'wc-custom-status'; $label = 'My Custom Status'; register_post_status( $slug, [ 'label' => $label, 'public' => true, 'exclude_from_search' => false, 'show_in_admin_all_list' => true, 'show_in_admin_status_list' => true, 'label_count' => _n_noop( $label . ' <span class="count">(%s)</span>', $label . ' <span class="count">(%s)</span>' ) ]); }
Now that we’ve registered our statuses, let’s go ahead and make sure WooCommerce recognizes them as Order Statuses. For that we’ll use the wc_order_status filter.
add_filter( 'wc_order_statuses', function( $order_statuses ) { $slug = 'wc-custom-status'; $label = 'My Custom Status'; $new_order_statuses = [ $slug => $label ]; return array_merge( $new_order_statuses, $order_statuses ); } );
Alright, we’re looking good! But, some order statuses in WooCommerce have an effect on the stock levels of the products in the order. For example, when an order is Pending Payment no stock is reduced. But when an order’s status changes to say, Processing, On Hold, or Completed, then that triggers WooCommerce to reduce stock. Conversely if an order is set to Cancelled or Pending, stock is returned back to the products.
If you wanted to add this functionality, I find that two snippets are required.
Reducing Stock:
(note that we’re excluding the ‘wc-‘ prefix for our slugs here)
add_action( 'woocommerce_order_status_custom-status', 'wc_maybe_reduce_stock_levels' );
And
add_filter('woocommerce_order_is_paid_statuses', function( $statuses ){ return array_merge($statuses, ['custom-status']); });
Returning Stock:
(note that we’re excluding the ‘wc-‘ prefix for our slugs here)
add_action( 'woocommerce_order_status_custom-status', 'wc_maybe_increase_stock_levels' );
And
add_filter('woocommerce_order_is_pending_statuses', function( $statuses ){ return array_merge($statuses, ['custom-status']); });
Sometimes it can be handy to have these new status in the WooCommerce Order’s bulk actions dropdown. WooCommerce has some by default, namely the ‘Mark as ….’ options in the dropdown. To create this ourselves, we’ll need to add our own options to this dropdown, and add a handler to process the bulk-update request.
Let’s add our options first:
add_filter( 'bulk_actions-edit-shop_order', function ( $bulk_actions ) { $slug = 'wc-custom-status'; $action = "mark_" . $slug; $label = 'My Custom Status'; $bulk_actions[ $action ] = $label; return $bulk_actions; });
Now we’ll have to write a handler to actually make these bulk changes. Note that it’s the $action variable from before that is used in our action hook below:
add_action( 'admin_action_mark_wc-custom-status', function() { $slug = 'wc-custom-status'; // if an array with order IDs is not present, abort if( !isset( $_REQUEST['post'] ) && !is_array( $_REQUEST['post'] ) ) return; // Loop through the Post Ids and update each of them to the new status. foreach( $_REQUEST['post'] as $order_id ) { $order = new WC_Order( $order_id ); $order_note = 'This orders status was changed by bulk edit:'; $order->update_status( $slug, $order_note, true ); } // And it's usually best to redirect the user back to the main order page, with some confirmation variables we can use in our notice: $location = add_query_arg( array( 'post_type' => 'shop_order', $slug => 1, // We'll use this as confirmation 'changed' => count( $post_ids ), // number of changed orders 'ids' => join( $post_ids, ',' ), // list of ids 'post_status' => 'all' ), 'edit.php' ); wp_redirect( admin_url( $location ) ); exit; } ); // Now let's add our Admin Notice! add_action('admin_notices', function() { global $pagenow, $typenow; $status = false; $listeningStatuses = [ 'wc-custom-status' ]; foreach( $listeningStatuses as $listeningStatus ) { if( isset($_REQUEST[ $listeningStatus ]) && $_REQUEST[ $listeningStatus ] == 1 ) { $status = $listeningStatus; } } if( $typenow == 'shop_order' && $pagenow == 'edit.php' && $status && isset( $_REQUEST['changed'] ) ) { $message = sprintf( _n( 'Order status changed.', '%s order statuses changed.', $_REQUEST['changed'] ), number_format_i18n( $_REQUEST['changed'] ) ); echo "<div class=\"notice notice-success updated\"><p>{$message}</p></div>"; } });
And that just about wraps it up!
You’re now able to add your own custom statuses, and apply them to individual or bulk orders. Plus, you can now use these statuses to take or return stock from your products. Some interesting order statuses might look like:
Are there any custom order statuses that you might find useful?