4
votes

Awhile ago I posted a question regarding a problem, I needed to send out different emails depending on what storage (custom field) the ordered products belonged to. So, if only a product belonging to Storage1 was ordered, only Email1 was supposed to be sent. If one product belonging to Storage1 AND one product belonging to Storage2 was ordered, email1 would be sent out containing product1 and email2 would contain product2. Link to original question: Custom order emails depending on product meta data

This all works now, but for some reason I get double emails. If I order one product from Storage1 I get TWO Email1 sent to my inbox... It does not matter if I order 5, 10, or 20 products, I only get ONE extra email, but that is enough.

So I would like to see if anyone experienced the same thing, and how they fixed it?

I'll add the code here again, for clarity.

I copied the class-wc-new-order and created two new classes exactly the same as the original. I changed ID and class names to storage 1 and storage 2 respectively.

I load the classes by doing the following:

//Add our custom class to WC email classes
add_filter( 'woocommerce_email_classes', [ $this, 'custom_order_email_add_email_classes' ], 10, 1 );

function custom_order_email_add_email_classes( $email_classes ) {

     require( CUSTOM_ORDER_EMAIL_PLUGIN_DIR . 'classes/class-wc-email-new-order-storage1.php' );
     require( CUSTOM_ORDER_EMAIL_PLUGIN_DIR . 'classes/class-wc-email-new-order-storage2.php' );

     $email_classes['WC_Email_New_Order_Storage1']  = new WC_Email_New_Order_Storage1(); 
     $email_classes['WC_Email_New_Order_Storage2']  = new WC_Email_New_Order_Storage2();

     return $email_classes;
}

I then edited the trigger()-function in both email classes. Notice that I did not change anything with the trigger actions, except for testing but I still get double emails every time, no matter what trigger actions are active. If I deactivate all trigger actions I do not get any emails at all, of course.

public function trigger( $order_id, $order = false ) {

    $trigger = false;

    if ( $order_id && ! is_a( $order, 'WC_Order' ) ) {
        $order = wc_get_order( $order_id );
    }

    if ( is_a( $order, 'WC_Order' ) ) {
        $this->object                  = $order;
        $this->find['order-date']      = '{order_date}';
        $this->find['order-number']    = '{order_number}';
        $this->replace['order-date']   = wc_format_datetime( $this->object->get_date_created() );
        $this->replace['order-number'] = $this->object->get_order_number();

        $items = $order->get_items();

        foreach ( $items as $item_id => $item ) {

            $product = $item->get_product();

            if ( $product->get_meta( '_product_storage' ) == 'storage2' ) {//storage1 in the other email class
                $trigger = true;
            }
        }
    }

    if ( ! $this->is_enabled() || ! $this->get_recipient() ) {
        return;
    }

    if( $trigger === true) {
        $this->send( $this->get_recipient(), $this->get_subject(), $this->get_content(), $this->get_headers(), $this->get_attachments() );
    }
    else {
        return;
    }
}

In order to filter out Storage2 products from Email1 I had to override email-order-details.php. This code is entered just below <tbody> in the template, and I took the code from the order-email-items.php which is used to display the products by default.

Edited email-order-details.php:

if ( $email->id == 'new_order_storage1' ) {

        $items = $order->get_items();
        foreach( $items as $item_id => $item ) {

            $product = $item->get_product();

            if ( $product->get_meta( '_product_storage' ) == 'storage1' ) {
        ?>
                <tr class="<?php echo esc_attr( apply_filters( 'woocommerce_order_item_class', 'order_item', $item, $order ) ); ?>">
                    <td class="td" style="text-align:<?php echo $text_align; ?>; vertical-align:middle; border: 1px solid #eee; font-family: 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif; word-wrap:break-word;"><?php

                        // Show title/image etc
                        if ( $show_image ) {
                            echo apply_filters( 'woocommerce_order_item_thumbnail', '<div style="margin-bottom: 5px"><img src="' . ( $product->get_image_id() ? current( wp_get_attachment_image_src( $product->get_image_id(), 'thumbnail' ) ) : wc_placeholder_img_src() ) . '" alt="' . esc_attr__( 'Product image', 'woocommerce' ) . '" height="' . esc_attr( $image_size[1] ) . '" width="' . esc_attr( $image_size[0] ) . '" style="vertical-align:middle; margin-' . ( is_rtl() ? 'left' : 'right' ) . ': 10px;" /></div>', $item );
                        }

                        // Product name
                        echo apply_filters( 'woocommerce_order_item_name', $item->get_name(), $item, false );

                        // SKU
                        if ( $show_sku && is_object( $product ) && $product->get_sku() ) {
                            echo ' (#' . $product->get_sku() . ')';
                        }

                        // allow other plugins to add additional product information here
                        do_action( 'woocommerce_order_item_meta_start', $item_id, $item, $order, $plain_text );

                        wc_display_item_meta( $item );

                        if ( $show_download_links ) {
                            wc_display_item_downloads( $item );
                        }

                        // allow other plugins to add additional product information here
                        do_action( 'woocommerce_order_item_meta_end', $item_id, $item, $order, $plain_text );

                    ?></td>
                    <td class="td" style="text-align:<?php echo $text_align; ?>; vertical-align:middle; border: 1px solid #eee; font-family: 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif;"><?php echo apply_filters( 'woocommerce_email_order_item_quantity', $item->get_quantity(), $item ); ?></td>
                    <td class="td" style="text-align:<?php echo $text_align; ?>; vertical-align:middle; border: 1px solid #eee; font-family: 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif;"><?php echo $order->get_formatted_line_subtotal( $item ); ?></td>
                </tr>
            <?php
            }
        }
    }
    else if ( $email->id == 'new_order_storage2' ) {
        $items = $order->get_items();
        foreach( $items as $item_id => $item ) {

            $product = $item->get_product();

            if ( $product->get_meta( '_product_storage' ) == 'storage2' ) {
        ?>
                <tr class="<?php echo esc_attr( apply_filters( 'woocommerce_order_item_class', 'order_item', $item, $order ) ); ?>">
                    <td class="td" style="text-align:<?php echo $text_align; ?>; vertical-align:middle; border: 1px solid #eee; font-family: 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif; word-wrap:break-word;"><?php

                        // Show title/image etc
                        if ( $show_image ) {
                            echo apply_filters( 'woocommerce_order_item_thumbnail', '<div style="margin-bottom: 5px"><img src="' . ( $product->get_image_id() ? current( wp_get_attachment_image_src( $product->get_image_id(), 'thumbnail' ) ) : wc_placeholder_img_src() ) . '" alt="' . esc_attr__( 'Product image', 'woocommerce' ) . '" height="' . esc_attr( $image_size[1] ) . '" width="' . esc_attr( $image_size[0] ) . '" style="vertical-align:middle; margin-' . ( is_rtl() ? 'left' : 'right' ) . ': 10px;" /></div>', $item );
                        }

                        // Product name
                        echo apply_filters( 'woocommerce_order_item_name', $item->get_name(), $item, false );

                        // SKU
                        if ( $show_sku && is_object( $product ) && $product->get_sku() ) {
                            echo ' (#' . $product->get_sku() . ')';
                        }

                        // allow other plugins to add additional product information here
                        do_action( 'woocommerce_order_item_meta_start', $item_id, $item, $order, $plain_text );

                        wc_display_item_meta( $item );

                        if ( $show_download_links ) {
                            wc_display_item_downloads( $item );
                        }

                        // allow other plugins to add additional product information here
                        do_action( 'woocommerce_order_item_meta_end', $item_id, $item, $order, $plain_text );

                    ?></td>
                    <td class="td" style="text-align:<?php echo $text_align; ?>; vertical-align:middle; border: 1px solid #eee; font-family: 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif;"><?php echo apply_filters( 'woocommerce_email_order_item_quantity', $item->get_quantity(), $item ); ?></td>
                    <td class="td" style="text-align:<?php echo $text_align; ?>; vertical-align:middle; border: 1px solid #eee; font-family: 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif;"><?php echo $order->get_formatted_line_subtotal( $item ); ?></td>
                </tr>
            <?php
            }
        }
    }
    else {
     echo wc_get_email_order_items( $order, array(
        'show_sku'      => $sent_to_admin,
        'show_image'    => false,
        'image_size'    => array( 32, 32 ),
        'plain_text'    => $plain_text,
        'sent_to_admin' => $sent_to_admin,
    ) ); 
    }

I would be very greatful if any of you have any answers or suggestions, also if there are any better ways to do this I am all ears.

Regards,

2

2 Answers

4
votes

I followed the same SkyVerge tutorial and found that my duplicate emails were caused by the line "new WC_Emails();" in the tutorial.

The WC_Emails class should not be instantiated directly and instead should be called by the static function "instance()".

At the time of this writing the custom-email-manager.php file in the tutorial has the following function defined:

public function custom_trigger_email_action( $order_id, $posted ) {
    // add an action for our email trigger if the order id is valid
    if ( isset( $order_id ) && 0 != $order_id ) {
        $order = new WC_order( $order_id );
        new WC_Emails();
        do_action( 'custom_pending_email_notification', $order_id );
    }
}

This should look like this instead:

public function custom_trigger_email_action( $order_id, $posted ) {
     // add an action for our email trigger if the order id is valid
    if ( isset( $order_id ) && 0 != $order_id ) {
        $order = new WC_order( $order_id );
        WC_Emails::instance();
        do_action( 'custom_pending_email_notification', $order_id );
    }
}

If you create a new instance of WC_Emails then it will duplicate the emails.

3
votes

The tutorial at SkyVerge I used loaded the email classes the wrong (old) way. Updated and working code, credits to @bhamrick @ wordpress.org:

add_filter( 'woocommerce_email_classes', [ $this, 'custom_order_email_add_email_classes' ], 10, 1 );

    function custom_order_email_add_email_classes( $email_classes ) {

         $email_classes['WC_Email_New_Order_Storage1']  = include( CUSTOM_ORDER_EMAIL_PLUGIN_DIR . 'classes/class-wc-email-new-order-storage1.php' );
         $email_classes['WC_Email_New_Order_Storage2']  = include( CUSTOM_ORDER_EMAIL_PLUGIN_DIR . 'classes/class-wc-email-new-order-storage2.php' );

     return $email_classes;
}