Woocommerce - Change order status automatically and send email to a selected address

122 Views Asked by At

I'm customizing woocommerce to create a food reservation system. I've successfully removed all unnecessary checkout fields and I've disabled the payments gateway because I just need to register the order. I'm now adding some custom fields to the checkout page using elementor and a custom plugin I'm writing. I have this code ath the moment and I need to customize the checkout, when a client place a reservation (order), I will need to send an email to some custom email adresses that can be selected from the user using a custom checkout field I've added. Is possible to automatically change the order status to completed and send the email, and what is the hook to use?

class CustomCheckout {        
    
    #
    public function __construct()
    {
        # 
        add_action( 'woocommerce_checkout_fields', array( $this, 'remove_default_checkout_fields') );
        # 
        add_action( 'woocommerce_after_checkout_billing_form', array( $this, 'add_custom_checkout_fields') );
        # 
        add_action( 'phpmailer_init', array( $this, 'mailtrap') );
        # 
        add_filter( 'woocommerce_cart_needs_payment', '__return_false' );
    }

    # debug email woocommerce
    public function mailtrap( $phpmailer ) 
    {
        $phpmailer->isSMTP();
        $phpmailer->Host = 'sandbox.smtp.mailtrap.io';
        $phpmailer->SMTPAuth = true;
        $phpmailer->Port = 2525;
        $phpmailer->Username = '****';
        $phpmailer->Password = '***';
    }
        
    #
    public function remove_default_checkout_fields( $fields )
    {
        //unset( $fields['billing']['first_name'] );
        //unset( $fields['billing']['last_name'] );
        unset( $fields['billing']['billing_company'] );
        unset( $fields['billing']['billing_country'] );
        unset( $fields['billing']['billing_address_1'] );
        unset( $fields['billing']['billing_address_2'] );
        unset( $fields['billing']['billing_city'] );
        unset( $fields['billing']['billing_state'] );
        unset( $fields['billing']['billing_postcode'] );
        # Rimuovo campi spedizione
        unset( $fields['shipping']['shipping_first_name'] );
        unset( $fields['shipping']['shipping_last_name'] );
        unset( $fields['shipping']['shipping_company'] );
        unset( $fields['shipping']['shipping_country'] );
        unset( $fields['shipping']['shipping_address_1'] );
        unset( $fields['shipping']['shipping_addredd_2'] );
        unset( $fields['shipping']['shipping_city'] );
        unset( $fields['shipping']['shipping_state'] );
        unset( $fields['shipping']['shipping_postcode'] );

        #
        return $fields;
    }
    
    #
    public function add_custom_checkout_fields( $checkout )
    {
        # 
        woocommerce_form_field( 
            'pdvselector', 
            array(
                'type' => 'select',
                'required' => true,
                'class' => array('form-row-first'),
                'label' => 'Punto di ritiro',
                //'label_class'
                'options' => array(
                    '' => 'Select a store',
                    '[email protected]' => 'Value 1',
                    '[email protected]' => 'Value 2',
                    '[email protected]' => 'Value 3'
                )
            ),
            $checkout->get_value( 'pdvselector' )
        );

        #
        woocommerce_form_field( 
            'dateselector', 
            array(
                'type' => 'date',
                'required' => true,
                'class' => array('form-row-last'),
                'label' => 'Reservation day'
            ), 
            $checkout->get_value( 'dateselector' ) 
        );
    
        #
        woocommerce_form_field(
            'hourselector',
            array(
                'type' => 'time',
                'required' => true,
                'class' => array('form-row-last'),
                'label' => 'Reservation hour'
            ),
            $checkout->get_value( 'hourselector' )
        );
    }
  
    #
    public function send_order_status_change_email( $order_id )
    {
        #
        $order = wc_get_order( $order_id );
        //$order->update_status( '' );
        #
    }
}

$checkout = new CustomCheckout();

2

There are 2 best solutions below

3
talha2k On BEST ANSWER

1. Hook into the woocommerce_thankyou action:

This action fires when an order is placed successfully, making it ideal for handling post-order tasks like status changes and email notifications.

2. Modify your send_order_status_change_email method:

public function send_order_status_change_email( $order_id ) {
    $order = wc_get_order( $order_id );

    // Get the selected email address from the custom field
    $selected_email = $order->get_meta( 'pdvselector' );

    // Change the order status to completed
    $order->update_status( 'completed' );

    // Send the email notification to the selected address
    WC()->mailer()->send( 'customer_completed_order', $order_id, $selected_email );
}

3. Add the action hook in your CustomCheckout constructor:

public function __construct() {

    //add this line
    add_action( 'woocommerce_thankyou', array( $this, 'send_order_status_change_email' ), 10, 1 );
}

When a customer places an order, the send_order_status_change_email method will be triggered. It will retrieve the selected email address from the pdvselector custom field. The order status will be updated to "completed". The "customer_completed_order" email template will be sent to the selected email address, informing them of the reservation.

EDIT 1

You can avoid sending two emails by utilizing a different hook and making some adjustments to your code.

1. Hook into woocommerce_order_status_changed:

Instead of using woocommerce_thankyou, leverage the woocommerce_order_status_changed action. This fires whenever the order status changes, allowing you to capture the completion moment without relying on the checkout page completion.

2. Refine your send_order_status_change_email method:

Modify your existing method to check the new order status before sending the email.

public function send_order_status_change_email( $order_id, $old_status, $new_status ) {
    if ( $new_status !== 'completed' ) {
        return; // Do not send email if not completed
    }

    $order = wc_get_order( $order_id );

    // ... Get selected email and send email logic ...

}

3. Register the action hook:

Update your constructor's code to register the hook with the updated method:

public function __construct() {
    //add this line
    add_action( 'woocommerce_order_status_changed', array( $this, 'send_order_status_change_email' ), 10, 3 );
}

With this, the email will only be sent when the order status changes to "completed," effectively preventing duplicate emails and confirming the successful order completion to the selected email address.

0
LoicTheAztec On

The correct hook to be used is not woocommerce_thankyou but woocommerce_new_order to avoid multiple email notifications…

For email debugging, you can use WP Mail Catcher plugin…

In the code below, I have added some functions to validate and save the custom checkout field values… When the order is submitted, the order is auto-completed, and an email notification is sent to the selected store as CC…

class CustomCheckout {        
    
    #
    public function __construct()
    {
        # 
        add_action( 'woocommerce_checkout_fields', array( $this, 'remove_default_checkout_fields') );
        # 
        add_action( 'woocommerce_after_checkout_billing_form', array( $this, 'add_custom_checkout_fields') );
        # 
        add_filter( 'woocommerce_cart_needs_payment', '__return_false' );
        # 
        add_action( 'woocommerce_after_checkout_validation', array( $this, 'custom_checkout_fields_validation'), 10, 2 );
        # 
        add_action( 'woocommerce_checkout_create_order', array( $this, 'save_order_meta_checkout_fields_values'), 10, 2 );
        # 
        add_action( 'woocommerce_checkout_update_customer', array( $this, 'save_customer_meta_checkout_field_value'), 10, 2 );

        add_action( 'woocommerce_new_order', array( $this, 'auto_complete_new_order'), 10, 2 );

        add_filter( 'woocommerce_email_headers', array( $this, 'additional_cc_recipient'), 10, 3 );
    }
        
    #
    public function remove_default_checkout_fields( $fields )
    {
        //unset( $fields['billing']['first_name'] );
        //unset( $fields['billing']['last_name'] );
        unset( $fields['billing']['billing_company'] );
        unset( $fields['billing']['billing_country'] );
        unset( $fields['billing']['billing_address_1'] );
        unset( $fields['billing']['billing_address_2'] );
        unset( $fields['billing']['billing_city'] );
        unset( $fields['billing']['billing_state'] );
        unset( $fields['billing']['billing_postcode'] );
        # Rimuovo campi spedizione
        unset( $fields['shipping']['shipping_first_name'] );
        unset( $fields['shipping']['shipping_last_name'] );
        unset( $fields['shipping']['shipping_company'] );
        unset( $fields['shipping']['shipping_country'] );
        unset( $fields['shipping']['shipping_address_1'] );
        unset( $fields['shipping']['shipping_addredd_2'] );
        unset( $fields['shipping']['shipping_city'] );
        unset( $fields['shipping']['shipping_state'] );
        unset( $fields['shipping']['shipping_postcode'] );
        #
        return $fields;
    }
    
    #
    public function add_custom_checkout_fields( $checkout )
    {
        # 
        woocommerce_form_field( 'withdrawal_point', array(
            'type' => 'select',
            'required' => true,
            'class' => array('form-row-first'),
            'label' => __('Withdrawal point', 'woocommerce'),
            'options' => array(
                ''                 => __('Select a store', 'woocommerce'),
                '[email protected]' => __('Store one', 'woocommerce'),
                '[email protected]' => __('Store two', 'woocommerce'),
                '[email protected]' => __('Store three', 'woocommerce'),
            ) 
        ), $checkout->get_value( 'withdrawal_point' ) );

        #
        woocommerce_form_field( 'booking_day', array(
            'type' => 'date',
            'required' => true,
            'class' => array('form-row-last'),
            'label' => 'Booking day'
        ), $checkout->get_value('booking_day') );
    
        #
        woocommerce_form_field( 'booking_hour', array(
            'type' => 'time',
            'required' => true,
            'class' => array('form-row-last'),
            'label' => 'Booking hour'
        ), $checkout->get_value('booking_hour') );
    }

    # Custom checkout fields validation
    public function custom_checkout_fields_validation( $data, $errors ) {
        if ( isset($_POST['withdrawal_point']) && empty($_POST['withdrawal_point']) ) {
            $errors->add( 'pos_error', __( '<strong>Withdrawal point</strong> is a required field.', 'woocommerce' ), 'error' );
        }
        if ( isset($_POST['booking_day']) && empty($_POST['booking_day']) ) {
            $errors->add( 'pos_error', __( '<strong>Booking day</strong> is a required field.', 'woocommerce' ), 'error' );
        }
        if ( isset($_POST['booking_hour']) && empty($_POST['booking_hour']) ) {
            $errors->add( 'pos_error', __( '<strong>Booking hour</strong> is a required field.', 'woocommerce' ), 'error' );
        }
    }

    # Save custom checkout fields values as order metadata
    public function save_order_meta_checkout_fields_values( $order, $data ) {
        if ( isset($_POST['withdrawal_point']) && ! empty($_POST['withdrawal_point']) ) {
            $order->update_meta_data('withdrawal_point', sanitize_email($_POST['withdrawal_point']));
        }
        if ( isset($_POST['booking_day']) && ! empty($_POST['booking_day']) ) {
            $order->update_meta_data('booking_day', esc_attr($_POST['booking_day']));
        }
        if ( isset($_POST['booking_hour']) && ! empty($_POST['booking_hour']) ) {
            $order->update_meta_data('booking_hour', esc_attr($_POST['booking_hour']));
        }
    }

    # Save custom checkout fields values as customer metadata
    public function save_customer_meta_checkout_field_value( $customer, $data ) {
        if ( isset($_POST['withdrawal_point']) && ! empty($_POST['withdrawal_point']) ) {
            $customer->update_meta_data('withdrawal_point', sanitize_email($_POST['withdrawal_point']));
        }
    }

    # Change order status, send new order email to admin and selected store
    # Send email customer completed order email to customer
    public function auto_complete_new_order( $order_id, $order ) {
        $order->update_status('wc-completed');
    }

    # Add selected store as CC recipient to "new order email"
    public function additional_cc_recipient( $headers, $email_id, $order ) {
        if ( $email_id === 'new_order' && ( $pos_email = $order->get_meta('withdrawal_point') ) ) {
            $headers .= "Cc: " . $pos_email . "\r\n";
        }
        return $headers;
    }
}

$checkout = new CustomCheckout();

Code goes in functions.php file of your child theme (or in a plugin). Tested and works.