通过自定义批量操作处理程序更新时,无需发送确认电子邮件即可将订单状态更改为已确认

Need a bulk status update to Orders to change from On Hold to Completed but without sending confirmation emails. However, still need to retain the email functionality. This would be a new custom bulk action in addition to the standard WooCommerce bulk action to update to Completed (which still would send the confirmation emails). I have the extra option added with no problem but can't find a method that will preclude the email notification or a way to temporarily disable email notifications (which doesn't sound like a good approach anyway).

So far code is as below. Everything is fine except the $order->update_status('completed') triggers the confirmation email.

Have tried using set_status() but that produces the same result (update_status calls set_status).

/*
 * Custom bulk action in dropdown - Change status to completed without sending Confirmation Email
 */
add_filter( 'bulk_actions-edit-shop_order', 'register_bulk_action' ); // edit-shop_order is the screen ID of the orders page

function register_bulk_action( $bulk_actions ) {

    $bulk_actions['complete_with_no_email'] = 'Change status to completed (no confirmation emails)'; 
    return $bulk_actions;

}

/*
 * Bulk action handler
 */
add_action( 'admin_action_complete_with_no_email', 'bulk_process_custom_status' ); // admin_action_{action name}

function bulk_process_custom_status() {

    // if an array with order IDs is not presented, exit the function
    if( !isset( $_REQUEST['post'] ) && !is_array( $_REQUEST['post'] ) )
        return;

    // New order emails
    foreach( $_REQUEST['post'] as $order_id ) {

        $order = new WC_Order( $order_id );
        $order_note = 'Changed Status to Completed via bulk edit (no confirmation email)';
        $order->update_status('completed', $order_note); //STILL SENDS EMAIL
    }

    // of course using add_query_arg() is not required, you can build your URL inline
    $location = add_query_arg( array(
        'post_type' => 'shop_order',
        'changed' => count( $_REQUEST['post'] ), // number of changed orders
        'ids' => join( $_REQUEST['post'], ',' ),
        'marked_fulfilled_no_emails' => 1,
        'post_status' => 'all'
    ), 'edit.php' );

    wp_redirect( admin_url( $location ) );
    exit;

}

/*
 * Notices for Bulk Action 
 */
add_action('admin_notices', 'custom_order_status_notices');

function custom_order_status_notices() {

    global $pagenow, $typenow;

    if( $typenow == 'shop_order' 
     && $pagenow == 'edit.php'
     && isset( $_REQUEST['marked_fulfilled_no_emails'] )
     && $_REQUEST['marked_fulfilled_no_emails'] == 1
     && isset( $_REQUEST['changed'] ) ) {

        $message = sprintf( _n( 'Order status changed.', '%s order statuses changed.', $_REQUEST['changed'] ), number_format_i18n( $_REQUEST['changed'] ) );
        echo "<div class=\"updated\"><p>{$message}</p></div>";

    }

}

Wanting to avoid triggering confirmation emails when using a custom bulk edit option from orders.

The best way I found to solve this was to add a flag using update_post_meta when looping through the selected orders flagging them to bypass the email confirmation.This, together with a function that hooks into woocommerce_email_recipient_customer_completed_order to return nothing for those that have been flagged and at the same time removing the flag so that all other functionality still triggers the email confirmation as normal afterwards. The relevant functions here:

Add the hook to add a new option for bulk editing:

/*
 * Custom bulk action in dropdown - Change status to completed without sending Confirmation Email
 */
add_filter( 'bulk_actions-edit-shop_order', 'register_bulk_action' ); // edit-shop_order is the screen ID of the orders page

function register_bulk_action( $bulk_actions ) {

    $bulk_actions['complete_with_no_email'] = 'Change status to completed (no confirmation emails)'; 
    return $bulk_actions;

}

Handle this new option here, making sure to flag each of the selected posts with update_user_meta so we can avoid emailing them

/*
 * Bulk action handler
 * admin_action_{action name}
 */
add_action( 'admin_action_complete_with_no_email', 'bulk_process_complete_no_email' );  
function bulk_process_complete_no_email() {

    // if an array with order IDs is not presented, exit the function
    if( !isset( $_REQUEST['post'] ) && !is_array( $_REQUEST['post'] ) )
        return;

    // Loop through selected posts 
    foreach( $_REQUEST['post'] as $order_id ) {

        // Use this flag later to avoid sending emails via hook woocommerce_email_recipient_customer_completed_order
        update_post_meta($order_id, 'bypass_email_confirmation', true);

        $order = new WC_Order( $order_id );
        $order_note = 'Changed Status to Completed via bulk edit (no confirmation email)';
        $order->update_status('completed', $order_note);
    }       

    // of course using add_query_arg() is not required, you can build your URL inline
    $location = add_query_arg( array(
        'post_type' => 'shop_order',
        'changed' => count( $_REQUEST['post'] ), // number of changed orders
        'ids' => join( $_REQUEST['post'], ',' ),
        'marked_fulfilled_no_emails' => 1,
        'post_status' => 'all'
    ), 'edit.php' );

    wp_redirect( admin_url( $location ) );
    exit;

}

This just adds a notice after the bulk action is completed

/*
 * Notices for the Bulk Action 
 * This just adds the notice after action has completed
 */
add_action('admin_notices', 'custom_order_status_notices');

function custom_order_status_notices() {

    global $pagenow, $typenow;

    if( $typenow == 'shop_order' 
     && $pagenow == 'edit.php'
     && isset( $_REQUEST['marked_fulfilled_no_emails'] )
     && $_REQUEST['marked_fulfilled_no_emails'] == 1
     && isset( $_REQUEST['changed'] ) ) {

        $message = sprintf( _n( 'Order status changed.', '%s order statuses changed.', $_REQUEST['changed'] ), number_format_i18n( $_REQUEST['changed'] ) );
        echo "<div class=\"updated\"><p>{$message}</p></div>";

    }

}

Finally, hook into every time the site is going to send a confirmation email and prevent it from doing so when the flag is present. Importantly, remove the flag here as well so only our bulk action above will prevent the confirmation email.

/* 
* Hook into every time the site is going to email customer and stop it if the flag is present
*/
add_filter('woocommerce_email_recipient_customer_completed_order','handle_email_recipient_completed_order', 10, 2);

function handle_email_recipient_completed_order($recipient, $order) {
    //if notifications are disabled (e.g. by bulk_process_complete_no_email)
    $notifications_disabled = get_post_meta($order->get_id(), 'bypass_email_confirmation', true);
    if ($notifications_disabled) {
        update_post_meta($order->get_id(), 'bypass_email_confirmation', false);
        return '';
    } else {
        return $recipient;
    }
}

Hopefully this helps someone, I spent quite a while trying to find an answer to this particular question and couldn't find it elsewhere.