Search code examples
phpwordpresswoocommercehook-woocommerceorders

WooCommerce won't trigger "woocommerce_order_status_changed" hook for custom order statuses


I began to have problems with action woocommerce_order_status_changed and it never triggered for any of my custom statuses.

I found the hook is used in class-wc-order.php only when $status_transition is not false.

if ( $status_transition ) {
    try {
        do_action( 'woocommerce_order_status_' . $status_transition['to'], $this->get_id(), $this );

        if ( ! empty( $status_transition['from'] ) ) {
            /* translators: 1: old order status 2: new order status */
            $transition_note = sprintf( __( 'Order status changed from %1$s to %2$s.', 'woocommerce' ), wc_get_order_status_name( $status_transition['from'] ), wc_get_order_status_name( $status_transition['to'] ) );

            // Note the transition occurred.
            $this->add_status_transition_note( $transition_note, $status_transition );

            do_action( 'woocommerce_order_status_' . $status_transition['from'] . '_to_' . $status_transition['to'], $this->get_id(), $this );
            do_action( 'woocommerce_order_status_changed', $this->get_id(), $status_transition['from'], $status_transition['to'], $this );
            // Etc...

All the custom statuses I made works and are shown on WooCommerce admin page. For adding the status I use:

function register_statuses()
    {
        register_post_status('wc-payment-await', array(
            'label'                     => 'Awaiting payment',
            'public'                    => true,
            'exclude_from_search'       => false,
            'show_in_admin_all_list'    => true,
            'show_in_admin_status_list' => true,
            'label_count'               => _n_noop('Awaiting payment <span class="count">(%s)</span>', 'Awaiting payment <span class="count">(%s)</span>', 'woocommerce')
        )); 
// more statuses here

}
add_action('init', 'register_statuses');

I found out that $status_transition is always false whenever I save order with new status. This feature worked until I have recently upgraded WooCommerce. Also I tried all other hooks for status change or transition none of which worked. Any advice is appreciated.


Solution

  • To add a custom order status I rewritten your code because:

    • The init hook has been replaced with woocommerce_register_shop_order_post_statuses to register custom order statuses.
    • wc_order_statuses is added to show the order status in the dropdown @ single order
    • bulk_actions-edit-shop_order is added to show order status in the dropdown @ bulk actions
    // Register order status
    function filter_woocommerce_register_shop_order_post_statuses( $order_statuses ) {
        // Status must start with "wc-"
        $order_statuses['wc-payment-await'] = array(
            'label'                     => _x( 'Awaiting payment', 'Order status', 'woocommerce' ),
            'public'                    => false,
            'exclude_from_search'       => false,
            'show_in_admin_all_list'    => true,
            'show_in_admin_status_list' => true,
            /* translators: %s: number of orders */
            'label_count'               => _n_noop( 'Awaiting payment <span class="count">(%s)</span>', 'Awaiting payment <span class="count">(%s)</span>', 'woocommerce' ),   
        );
        
        return $order_statuses;
    }
    add_filter( 'woocommerce_register_shop_order_post_statuses', 'filter_woocommerce_register_shop_order_post_statuses', 10, 1 );
    
    // Show order status in the dropdown @ single order
    function filter_wc_order_statuses( $order_statuses ) {  
        $new_order_statuses = array();
    
        // Add new order status after processing
        foreach ( $order_statuses as $key => $status ) {
    
            $new_order_statuses[ $key ] = $status;
    
            if ( 'wc-processing' === $key ) {
                // Status must start with "wc-"
                $new_order_statuses['wc-payment-await'] = _x( 'Awaiting payment', 'Order status', 'woocommerce' );
            }
        }
    
        return $new_order_statuses;
    }
    add_filter( 'wc_order_statuses', 'filter_wc_order_statuses', 10, 1 );
    
    // Show order status in the dropdown @ bulk actions
    function filter_bulk_actions_edit_shop_order( $bulk_actions ) {
        // Note: "mark_" must be there instead of "wc"
        $bulk_actions['mark_payment-await'] = __( 'Awaiting payment', 'woocommerce' );
        return $bulk_actions;
    }
    add_filter( 'bulk_actions-edit-shop_order', 'filter_bulk_actions_edit_shop_order', 10, 1 );
    

    Regarding your question: "woocommerce_order_status_changed is never triggered for any of my custom order statuses."

    If you use the following code you will see as result, that the $new_status variable contains your custom order status and so you know that the hook is triggered

    function action_woocommerce_order_status_changed( $order_id, $old_status, $new_status, $order ) {   
        var_dump( $old_status );
        var_dump( $new_status );
        die();
    }
    add_action( 'woocommerce_order_status_changed', 'action_woocommerce_order_status_changed', 10, 4 );