Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Defer WooCommerce emails for a few minutes - Defer WooCommerce emails for a specified time after the normal delivery time. https://www.damiencarbery.com/2020/04/defer-woocommerce-emails-for-a-few-minutes/
<?php
/*
Plugin Name: Defer WooCommerce emails for a few minutes
Plugin URI: https://www.damiencarbery.com/2020/04/defer-woocommerce-emails-for-a-few-minutes/
Description: Defer WooCommerce emails for a specified time after the normal delivery time.
Author: Damien Carbery
Author URI: https://www.damiencarbery.com
Version: 0.5
*/
class DeferSendingWooCommerceEmails {
private static $instance;
private $default_defer_time;
// An associative array to match $email_id with email class, to allow for the deferring of different emails.
private $email_id_to_class;
// Returns an instance of this class.
public static function get_instance() {
if ( null == self::$instance ) {
self::$instance = new DeferSendingWooCommerceEmails();
}
return self::$instance;
}
// Initialize the plugin variables.
public function __construct() {
$this->default_defer_time = 600; // Defer for 600 seconds (10 minutes).
// Can add other email IDs and their associated email class names.
// You can uncomment the add_action() with 'order_status_changed' function to find out the before and after status.
// This is also the list of emails that will be deferred.
$this->email_id_to_class = array(
'woocommerce_order_status_completed' => array( 'WC_Email_Customer_Completed_Order', $this->default_defer_time ),
// Probably don't want to defer these emails but they are shown
// here as a demo of a different defer time.
//'woocommerce_order_status_pending_to_on-hold' => array( 'WC_Email_New_Order', $this->default_defer_time ), // New order
//'woocommerce_order_status_pending_to_processing' => array( 'WC_Email_New_Order', $this->default_defer_time ),
// Additional transitition-to-email class info from @djm56
//'woocommerce_order_status_on-hold' => array('WC_Email_Customer_On_Hold_Order', $this->default_defer_time), // Order on hold.
//'woocommerce_order_status_pending_to_on-hold' => array('WC_Email_Customer_On_Hold_Order', $this->default_defer_time), // Order on hold.
//'woocommerce_order_status_cancelled_to_on-hold' => array('WC_Email_Customer_On_Hold_Order', $this->default_defer_time), // Order on hold.
//'woocommerce_order_status_pending_to_on-hold' => array('WC_Email_Customer_On_Hold_Order', $this->default_defer_time), // Order on hold.
);
$this->init();
}
// Set up WordPress specfic actions.
public function init() {
// Set all WooCommerce emails to be deferred.
add_filter( 'woocommerce_defer_transactional_emails', '__return_true' );
// Allow most emails to be sent as normal but prevent emails listed in $this->email_id_to_class. Schedule them for a time in the future.
add_filter( 'woocommerce_allow_send_queued_transactional_email', array( $this, 'whether_send_queued_wc_email' ), 10, 3 );
// This is the scheduled function that will send the email.
add_action( 'send_deferred_woocommerce_email', array( $this, 'send_deferred_woocommerce_email' ), 10, 2 );
// DEBUG: Add the order modification time and current time to prove that the email was intentionally delayed.
add_action( 'woocommerce_email_order_details', array( $this, 'add_defer_length_info_to_order_email' ), 5, 4 );
// Uncomment this to log the before and after statuses so you know what ones
// to add to the $this->email_id_to_class array.
//add_action('woocommerce_order_status_changed', array( $this, 'order_status_changed' ), 10, 4);
}
// Log the before and after status to discover which one to add to $this->email_id_to_class array.
public function order_status_changed( $id, $from, $to, $order ) {
error_log( 'Order ID: ' . $id );
error_log( 'Status from: ' . $from );
error_log( 'Status to: ' . $to );
}
private function get_email_class( $email_id ) {
if ( array_key_exists( $email_id, $this->email_id_to_class ) ) {
return $this->email_id_to_class[ $email_id ][ 0 ];
}
else {
return null;
}
}
private function get_email_defer_time( $filter ) {
if ( array_key_exists( $filter, $this->email_id_to_class ) ) {
return $this->email_id_to_class[ $filter ][ 1 ];
}
else {
return $this->default_defer_time;
}
}
public function whether_send_queued_wc_email( $true, $filter, $args ) {
//error_log( 'woocommerce_allow_send_queued_transactional_email $filter: ' . var_export( $filter, true ) );
//error_log( 'woocommerce_allow_send_queued_transactional_email order_number: ' . var_export( $args[ 0 ], true ) );
if ( array_key_exists( $filter, $this->email_id_to_class ) ) {
// TODO: Consider verifying that $args[0] is a valid order number.
//$order = wc_get_order( $args[ 0 ] );
$order_num = $args[ 0 ];
wp_schedule_single_event( time() + $this->get_email_defer_time( $filter ), 'send_deferred_woocommerce_email', array( $order_num, $filter ) );
//error_log( sprintf( 'woocommerce_allow_send_queued_transactional_email: Defer a %s email for order: %s for %d seconds.', $filter, $order_num, $this->get_email_defer_time( $filter ) ) );
return false;
}
//error_log( 'woocommerce_allow_send_queued_transactional_email: Ok to send email.' );
return $true;
}
// Send the deferred email for order $order_id.
function send_deferred_woocommerce_email( $order_id, $email_id ) {
//error_log( 'send_deferred_woocommerce_email for order: ' . $order_id );
WC()->mailer()->get_emails()[ $this->get_email_class( $email_id ) ]->trigger( $order_id );
}
// This is an experimental function to add the date/time the order was modified and
// the date/time the email was sent into email - to demonstrate that the deferring code worked.
public function add_defer_length_info_to_order_email( $order, $sent_to_admin, $plain_text, $email ) {
if ( 'customer_completed_order' == $email->id ) {
if ( $plain_text ) {
printf( '%sThe order was modified at %s and email sent at %s.', "\n", $order->get_date_modified(), current_time( 'mysql' ) );
}
else {
printf( '<p>The order was modified at <strong>%s</strong> and email sent at <strong>%s</strong>.</p>', $order->get_date_modified(), current_time( 'mysql' ) );
}
}
}
}
$DeferSendingWooCommerceEmails = new DeferSendingWooCommerceEmails();
@djm56

This comment has been minimized.

Copy link

@djm56 djm56 commented May 19, 2020

Thanks for this code I was desperately looking for something like this you saved my life. I needed it for on-hold mails. Here is the code I used so it pulls the correct email.
'woocommerce_order_status_on-hold' => array('WC_Email_Customer_On_Hold_Order', $this->default_defer_time), 'woocommerce_order_status_pending_to_on-hold' => array('WC_Email_Customer_On_Hold_Order', $this->default_defer_time), // New order 'woocommerce_order_status_cancelled_to_on-hold' => array('WC_Email_Customer_On_Hold_Order', $this->default_defer_time), // New order 'woocommerce_order_status_pending_to_on-hold' => array('WC_Email_Customer_On_Hold_Order', $this->default_defer_time), // New order

@damiencarbery

This comment has been minimized.

Copy link
Owner Author

@damiencarbery damiencarbery commented May 19, 2020

@djm56 - Thanks for the additional code. I have updated the gist to include these. I'm delighted that you found it useful - it took me a long while to figure it all out.

@KitKatCarson

This comment has been minimized.

Copy link

@KitKatCarson KitKatCarson commented May 24, 2020

Thanks for sharing your gist! This helped me a ton!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.