phpwordpresswoocommercehook-woocommercecomposite

When and where woocommerce_order_status_completed_notification hook is executed in WooCommerce?


I have WooCommerce 8.0.3 installed on my e-shop. In short, let's say I have an order with ID 123 in status processing. In a specific situation, I'm running this code:

// Make sure e-mails are initialized.
WC()->mailer();

// Set order status to completed.
$order = wc_get_order( 123 );
$order->update_status( "completed", "", true );

I need a customer_completed_order e-mail to be sent. Sometimes it works, but sometimes it doesn't. I have the e-mail enabled in WooCommerce admin.

I found an e-mail is triggered on woocommerce_order_status_completed_notification hook, so I tried to modify the code:

WC()->mailer();
$order = wc_get_order( 123 );
$order->update_status( "completed", "", true );

var_dump( did_action( "woocommerce_order_status_completed" ) );
var_dump( did_action( "woocommerce_order_status_completed_notification" ) );

Result:

int(1)
int(0)

Which means woocommerce_order_status_completed hook was executed, but woocommerce_order_status_completed_notification wasn't.

My questions are:

  1. Where in WooCommerce code are hooks like woocommerce_order_status_completed_notification executed?
  2. What can be the reason that hook woocommerce_order_status_completed_notification isn't executed when woocommerce_order_status_completed is?

Any help is appreciated.


Solution

  • The hook woocommerce_order_status_completed_notification is a multi composite hook that allows to trigger the email sent to the customer when order status is changed to "completed".

    It's triggered in WC_Emails send_transactional_email() method on line 170:

    do_action_ref_array( current_filter() . '_notification', $args );
    

    where current_filter() refer to woocommerce_email_actions filter hook arguments:

    array(
        'woocommerce_low_stock',
        'woocommerce_no_stock',
        'woocommerce_product_on_backorder',
        'woocommerce_order_status_pending_to_processing',
        'woocommerce_order_status_pending_to_completed',
        'woocommerce_order_status_processing_to_cancelled',
        'woocommerce_order_status_pending_to_failed',
        'woocommerce_order_status_pending_to_on-hold',
        'woocommerce_order_status_failed_to_processing',
        'woocommerce_order_status_failed_to_completed',
        'woocommerce_order_status_failed_to_on-hold',
        'woocommerce_order_status_cancelled_to_processing',
        'woocommerce_order_status_cancelled_to_completed',
        'woocommerce_order_status_cancelled_to_on-hold',
        'woocommerce_order_status_on-hold_to_processing',
        'woocommerce_order_status_on-hold_to_cancelled',
        'woocommerce_order_status_on-hold_to_failed',
        'woocommerce_order_status_completed', // <=== <=== <=== HERE
        'woocommerce_order_fully_refunded',
        'woocommerce_order_partially_refunded',
        'woocommerce_new_customer_note',
        'woocommerce_created_customer',
    )
    

    The hook is hooked in WC_Email_Customer_Completed_Order in the constructor function line 42, for the WC_Email_Customer_Completed_Order method trigger():

    // Triggers for this email.
    add_action( 'woocommerce_order_status_completed_notification', array( $this, 'trigger' ), 10, 2 );
    

    So you can only use this hook with remove_action() as showed in this code snippet, to remove the email notification sent to the customer when order status is "completed".


    Note that WC()->mailer() is just an instance object of the WC_Emails Class, that load the mailer class, similar to new WC_Emails().