This comes from an interesting question we got from Helmar:

Is it possible to send an “order received” email to a second recipient only if the order contained a tangible product?

Background: If I receive orders with tangible products, my shipping company gets a CC of that order in order to fulfill it. If the order contained no tangible product, there’s no need for them to receive a copy of the order.


This is definitely an interesting question, and it’s not quite straightforward at first glance to add WooCommerce email recipients.

WooCommerce doesn’t include filters for email recipients within each email class. Instead, the general WC_Email class has a filter that will be of use to us here:

woocommerce_email_recipient_{email_id}

This lets you adjust the email recipient, and the filter accepts the email ID as part of the filter name so you can target the recipients of a particular email.

In our case, the email id we want to target is new_order, as this is the admin “New Order” / order received email. When we insert this, the filter name to use will become: woocommerce_email_recipient_new_order

However, filtering the email recipient isn’t useful in itself, as we can do this in the email settings. We want to base the recipient on order items, and we’re in luck here. The object passed into the email recipient filter is the order this email pertains to, so we can use the order data to conditionally change our recipient ?

From here, we’ll need to check our order for physical / non-virtual products, and then if the order contains one, add our extra recipient email.

Considerations

First of all, for some reason this filter is run on the WooCommerce > Emails settings page. However, in this context, the order object for the email is not set (since it’s obviously not being run as part of an order). No bueno.

We’ll get an error that basically kills our email settings page, which we absolutely don’t want, and that’s why this isn’t quite straight-forward (and why you should always test your snippets!).

We’ll need to avoid this with a preliminary pre-flight check. I’ll $_GET the page we’re on, and bail out if we’re on any WooCommerce settings page, so we can avoid errors to keep our settings intact.

$page = $_GET['page'] = isset( $_GET['page'] ) ? $_GET['page'] : '';
if ( 'wc-settings' === $page ) {
    return $recipient; 
}

My second sanity check that the order we’re passed in is an instance of WC_Order probably isn’t necessary, but it’s used just in case a custom order type triggers this email as well.

Next, we’ll get the items in the order, get the products from the line item, and if the line item is a product, we’ll check it to see if it’s a non-virtual, shippable product.

Fortunately, the WC_Product class has the exact method I’ll need here — $product->needs_shipping() will check if I have a virtual product (no shipping) or not, so we can use this to base our recipient addition on.

If we do have a product that requires shipping, we’ll add our recipient and bail out of our loop checking order items (after all, once we know we have at least one shipped product, the recipient will be added and we don’t need to add it again — we’re done).

Return your additional recipient like: , new@email.com — the filter expects a comma-separated list of emails, so you should add your recipient to the end of the list with an additional comma (another pre-flight check you need to be aware of — know what you should be returning!).

Finally, don’t forget to return the original recipient in case our order doesn’t contain a shippable product, as we want to return the original recipient list back.

? For the admin “New Order” email, it will be sent to the email in our settings, and if a product that requires shipping is present in the order, the additional recipient will be added to this email. If not, then it will only be sent to the recipient(s) in our settings.

Published by Beka Rice

Beka leads product direction for SkyVerge, focusing on new features for our plugins and Jilt. She spends a lot of time on research and interviews, but likes to write so she has an excuse to spend more time jamming out to anything from The Clash to Lady Gaga.

15 Comments

  1. […] WooCommerce sites, learn how to conditionally add recipients for admin order emails from […]

  2. Hi Beka,

    How would i use the same logic to get the billing_state from a new order?
    I need to send an email to different dealers based on the orders billing_state

    eg.
    If billing_state is “XX”, “YY”, “ZZ” send email to email1@address.com
    if billing_state is “AA”, “BB”, “CC” send email to email2@address.com

    Hope my question is clear 🙂
    Thanks in advance
    Jonathan.

    • Hi Jonathan, did you get any direction on this? I also require this feature. I have different order processing clerks for different regions and would like to send new order notifications only to the clerk associated with the specific region.

      Thanks in advance.

      • Hi Darryl,

        Although i ended up using Gravity Forms to tie all i needed together, yes i came up with a working solution for this by adding the following hook to my functions.php file (it’s quite self explanatory and probably could be simplified but it worked):

        In Italy we have provinces inside of regions, so i created a variable for every region and added an array of provinces for each.

        /**
        Check the user's Province to forward email to correct reseller
        **/
            function jb_add_recipient( $recipient, $order ) {
        
            global $woocommerce;
            global $current_user;
        
            get_currentuserinfo();
            $jb_user_roles = $current_user->roles;
            $jb_user_role = array_shift($jb_user_roles);
        
            $abruzzo = array('CH', 'AQ', 'PE', 'TE');
            $basilicata = array('MT', 'PZ');
            $calabria = array('CZ', 'CS', 'KR', 'RC', 'VV');
            $campania = array('AV', 'BN', 'CE', 'NA', 'SA');
            $emilia = array('BO', 'FE', 'FC', 'MO', 'PR', 'PC', 'RA', 'RE', 'RN');
            $friuli = array('GO', 'PN', 'TS', 'UD');
            $lazio = array('FR', 'LT', 'RI', 'RM', 'VT');
            $liguria = array('GE', 'IM', 'SP', 'SV');
            $lombardia = array('BG', 'BS', 'CO', 'CR', 'LC', 'LO', 'MN', 'MI', 'MB', 'PV', 'SO', 'VA');
            $marche = array('AN', 'AP', 'FM', 'MC', 'PU');
            $molise = array('CB', 'IS');
            $piemonte = array('AL', 'AT', 'BI', 'CN', 'NO', 'TO', 'VB', 'VC');
            $puglia = array('BA', 'BT', 'BR', 'FG', 'LE', 'TA');
            $sardegna = array('CA', 'CI', 'VS', 'NU', 'OG', 'OT', 'OR', 'SS');
            $sicilia = array('AG', 'CL', 'CT', 'EN', 'ME', 'PA', 'RG', 'SR', 'TP');
            $toscana = array('AR', 'FI', 'GR', 'LI', 'LU', 'MS', 'PI', 'PO', 'PT', 'SI');
            $trentino = array('BZ', 'TN');
            $umbria = array('PG', 'TR');
            $aosta = array('AO');
            $veneto = array('BL', 'PD', 'RO', 'TV', 'VE', 'VR', 'VI');
        
            $province =  $order->billing_state;
        
                if ( in_array( $province, $abruzzo)) {
                        $recipient .= ', email@email.com';
                    // $recipient .=  ',' . $email_one . ',' . $email_two;
                } elseif ( in_array( $province, $basilicata) ) {
                $recipient .= ', email@email.com';
                } elseif ( in_array( $province, $calabria) ) {
                        $recipient .= ', email@email.com';
                } elseif ( in_array( $province, $campania) ) {
                $recipient .= ', email@email.com';
                } elseif ( in_array( $province, $emilia) ) {
                        $recipient .= ', email@email.com';
                } elseif ( in_array( $province, $friuli) ) {
                $recipient .= ', email@email.com';
                } elseif ( in_array( $province, $lazio) ) {
                        $recipient .= ', email@email.com';
                } elseif ( in_array( $province, $liguria) ) {
                $recipient .= ', email@email.com';
                } elseif ( in_array( $province, $lombardia) && $jb_user_role !== 'customer' ) {
                        $recipient .= ', hello@hanzo.it';
                } elseif ( in_array( $province, $marche) ) {
                $recipient .= ', email@email.com';
                } elseif ( in_array( $province, $molise) ) {
                        $recipient .= ', email@email.com';
                } elseif ( in_array( $province, $piemonte) ) {
                $recipient .= ', email@email.com';
                } elseif ( in_array( $province, $puglia) ) {
                        $recipient .= ', email@email.com';
                } elseif ( in_array( $province, $sardegna) ) {
                $recipient .= ', email@email.com';
                } elseif ( in_array( $province, $sicilia) ) {
                        $recipient .= ', email@email.com';
                } elseif ( in_array( $province, $toscana) && $jb_user_role !== 'customer' ) {
                $recipient .= ', email@email.com';
                } elseif ( in_array( $province, $trentino) ) {
                        $recipient .= ', email@email.com';
                } elseif ( in_array( $province, $umbria) ) {
                        $recipient .= ', email@email.com';
                } elseif ( in_array( $province, $aosta) ) {
                $recipient .= ', email@email.com';
                } elseif ( in_array( $province, $veneto) ) {
                        $recipient .= ', email@email.com';
                }
                return $recipient;
        }
        add_filter( 'woocommerce_email_recipient_new_order', 'jb_add_recipient', 10, 2 );
        

        Hope it helps,
        Take care!
        Jonathan

  3. Hi. I need some help to add the function of “conditionally recipients” on our website.
    We have some “customer” users that are assigned/linked to other “representative” users.. that can view orders and various data of his customers (we install a plugin that made this work).

    When a customer place a New order we need an email notification is sent to his representative, to update him that an order was made by the customer.

    The plugin that assign/link customers to representatives use this code to retrieve the correct representative (sales rep) according to the customer (user):

    //get current Representative of a user
    $sales_rep_id = get_user_meta($user->ID , ‘sales-rep-id’, true);

    I tried for a long time to create a function to send a notification of New order to representative but without success.. You can help me with some advice please?

    Thanks!

    Marco.

    • Hey Marco, it’s not very simple because you only have the recipient email to work with from the beginning (not a WP user), but you could get the user data from there. The woocommerce_email_recipient_customer_invoice filter is what you’d need to add recipients, this lets you filter the recipient and gives you access to the WC_Email object — see details here.

      • Good morning Beka and thank a lot for the quick answer.
        You’re referring to this part of code?

        /**
        * Get valid recipients.
        * @return string
        */
        public function get_recipient() {
        $recipient = apply_filters( 'woocommerce_email_recipient_' . $this->id, $this->recipient, $this->object );
        $recipients = array_map( 'trim', explode( ',', $recipient ) );
        $recipients = array_filter( $recipients, 'is_email' );
        return implode( ', ', $recipients );
        }

        I’m not familiar with PHP language, but I understand that the suggested solution would be to retrieve the “WP user” starting from his email address. Then try to recover his correlated “representative ID”, and finally add the “representative” email address to the recipients.. I started working with this code:

        add_filter( ‘woocommerce_email_recipient_customer_invoice’, ‘woocommerce_email_customer_invoice_add_recipients’ );
        function woocommerce_email_customer_invoice_add_recipients( $recipient, $order ) {
        if ( ‘customer-A@test.com’ == $recipient ) {
        $recipient .= ‘, representative-A@test.com‘;
        }
        return $recipient;
        }

        But at the moment I don’t understand how to use all this to enable notification of new orders to the correct representative according to the related customer who placed the order. I understand that it is not an easy thing to do, but I have to find a way to notify the representatives when their customers make new orders. If you can give me some advice I would be very grateful. Thank you, have a good day.

        Marco.

        • Hey Marco, yes, that’s the filter I’m referencing. However, $this->object is the WC_Email, not the WC_Order as your snippet suggests, which is why I said it’s not quite straightforward since you don’t have the user ID from the order to work with 🙂

          Using the email recipient to get the WP_User, and if it exists, getting the usermeta to add new recipients that are tied to the original one should work as an approach (though of course, you don’t know 100% until you dig into it). I’m afraid I’ve not doing this so I don’t have any sample code to work from, so if this isn’t something you feel comfortable digging into, we typically recommend Codeable for these sort of customizations.

  4. Hi! I have code that adds a notice to the new status of the order. “fot”

    How to add a template?

       add_action(“woocommerce_order_status_fot”, “my_awesome_publication_notification”);
    function my_awesome_publication_notification($order_id, $checkout=null) {
    global $woocommerce;
    $order = new WC_Order( $order_id );
    if($order->status === ‘fot’ ) {
    // Create a mailer
    $mailer = $woocommerce->mailer();
     
    $message = $mailer->wrap_message(
    // Message head and message body.
    sprintf( __( ‘Order %s received’ ), $order->get_order_number() ), $message_body );
     
    // Cliente email, email subject and message.
    $mailer->send( $order->billing_email, sprintf( __( ‘Order %s received’ ), $order->get_order_number() ), $message );
    }
     
    } 
     
     get_order_number(); ?>
     
    email_order_items_table( array(
    ‘show_purchase_note’ => $show_purchase_note,
    ‘show_download_links’ => $show_download_links,
    ‘show_sku’ => false,
    ) );
    } else {
    echo $order->email_order_items_table( $show_download_links, false, $show_purchase_note );
    }
    ?>
     
    get_order_item_totals() ) {
    $i = 0;
    foreach ( $totals as $total ) {
    $i++;
    ?>
    <th scope="row" colspan="2" style="text-align:left; border: 1px solid #eee; “>
    <td style="text-align:left; border: 1px solid #eee; “>
     
    billing_email ) : ?>
    billing_email; ?>
     
    billing_phone ) : ?>
    billing_phone; ?>
     
    $order ) ); ?> 
  5. Hello Beka,
    I was digging for this info for the last few days. Even bought woo-commerce advance notification plugin but this plugin is failing to generate in the email it sends the information i need. That is a type and a shipping price of the product.
    With your approach looks like it should work but unfortunately i am not good at codes so my question is:
    1. Where this code should be written?
    2. If it is a new php file, where should it go on the hosting. Which folder?

  6. Hello

    Can this be adapted for the users role? We have several different customer roles, which need to go to different emails.

  7. This works great. Thanks! I was trying to use an old hook that apparently doesn’t exist anymore and have been frustrated for an hour now. What would you do to add a BCC? So the email address(es) of the other recipient(s) aren’t visible.

    • Figured it out using woocommerce_email_headers just check the mail ID in an if-statement and concate BCC: John Doe <joe@doe.com>\r\n.

  8. Hi, this is exactly what I’m looking for, almost…
    Could you possible help me to add another conditions.

    Not only do i want to send the “New Order Email” to the additional recipient when there is a tangible product in the cart, but also if the payment method is that the payment gateway MyGate.

    I edited your code as follows, and it seems to work; could you please verify if this is a good way?

    $payment_method = get_post_meta( $order->id, ‘_payment_method’, true );
    if ( ‘mygate’ == $payment_method ) {
    // add our extra recipient if there’s a shipped product – commas needed!
    // we can bail if we’ve found one, no need to add the recipient more than once
    if ( $product && $product->needs_shipping() ) {
    $recipient .= ‘, hello@thissideup.media‘;
    return $recipient;
    }
    }
    }

Hmm, looks like this article is quite old! Its content may be outdated, so comments are now closed.