WooCommerce custom order state

Posted on

WooCommerce custom order state

Before diving into the code, consider this:

No code is better than no code.

So what is a good use-case for custom WooCommerce order state? More often than not, the product you sell will involve some kind of operations that you want to track transparently from customer, for example ordering the materials from a supplier, or send it off to a custom shop. WooCommerce is so matured it is possible to implement complex order processing pipeline just utilizing its note system. I mean obviously you can add a note every time you need to order from a supplier. The down side of this is the staff might forgot the details, or the note can get inconsistent over time. Moreover since the note is just text it is hard to keep track and filter the order of various state. Custom order state can really stream line the whole operation.

Adding custom order state to WooCommerce

Let’s say you’re selling leather goods, and allow customers to have their name engrave into the tag. You have a partner that do this for you and want to track the items you sent them. Here’s the code:

define('ENGRAVING_ORDERED_STATUS', 'wc-engraving_ordered');
define('ENGRAVING_FINISHED_STATUS', 'wc-engraving_finished');

add_filter('wc_order_statuses', 'enable_order_status');

function enable_order_status($_statuses) {
  $statuses = array();
  foreach ($_statuses as $key => $status) {
    $statuses[$key] = $status;
    if ($key === 'wc-processing') {
      $statuses[ENGRAVING_ORDERED_STATUS] = __('Engraving ordered', 'text-domain');
      $statuses[ENGRAVING_FINISHED_STATUS] = __('Engraving finished', 'text-domain');
  return $statuses;

Some note about the names:

  • The status have wc prefix just for simplicity. There are some internal WooCommerce thing involve this.
  • They are Wordpress post_status, so they must not be longer than 20 characters. This cost me 4 hour digging :(

Add custom order process actions

For better user experience, we can also add a couple of custom order actions. In my case these action also helps send the order information to our API.

function order_actions($actions) {
  global $theorder;
  $status = 'wc-' .  $theorder->get_status();

  if ($status !== ENGRAVING_ORDERED_STATUS &&
      $status !== ENGRAVING_FINISHED_STATUS) {
    $actions[ENGRAVE_ORDERED_STATUS] = __('Order engraving', 'text-domain');

  if ($status === ENGRAVING_ORDERED_STATUS) {
    $actions[ENGRAVING_FINISHED_STATUS] = __('Engraving finished', 'text-domain');

  return $actions;
add_action('woocommerce_order_actions', 'order_actions');

public function engraving_ordered_action($order) {
  $note = "Engrave order sent, customer name: {$order->billing_first_name} {$order->billing_last_name}";
  $order->update_status(ENGRAVING_ORDERED_STATUS, $note);
add_action("woocommerce_order_action_{ENGRAVE_ORDERED_STATUS}", 'engraving_ordered_action');

public function engraving_finished_action($order) {
add_action("woocommerce_order_action_{ENGRAVING_FINISHED_STATUS}", 'engraving_finished_action');

Bonus: Display order status column in the order list

With some more code we can display the order status nicely in the admin interface:

function shop_order_status_column($columns) {
  $columns[’status’] = __(’Status’, ’text-domain’);
  return $columns;
add_filter( 'manage_edit-shop_order_columns', 'shop_order_status_column', 11);

function orders_list_status_column_content($column, $post_id) {
  if ($column === ‘status’) {
    echo wc_get_order($post_id)->get_status();
add_action('manage_shop_order_posts_custom_column' , 'orders_list_status_column_content', 10, 2);


Overall, the process is not challenging at all, but the information is so scattered I decided to put together a tutorial. Hope you find it helpful.