I have done a vast amount of searching and although I have found users that have asked how to achieve the following no examples of working solutions to the best of my knowledge exist.
The question is regarding the very popular Wordpress plugin "Woocommerce". The plugin comes with an email system to make life easier for the ecommerce site owner and the customer. One issue is that there is no email that is sent when a shop manager changes the order status to "Refunded". Someone has said this is because it is a manual process. This is true it is a process that the shop owner would do via there merchant account or paypal account. But once this is done and the shop owner then logs into their wordpress admin panel and changes an order status to Refunded it would be beneficial for an email to be generated and sent the customer.
This is something I've seen requested.
So I decided to modify a tutorial over at
http://www.skyverge.com/blog/how-to-add-a-custom-woocommerce-email/#comment-553147
I am trying to have an email sent out when an orders order status is updated to "Refunded".
Here is the code to the initial plugin file
<?php
/**
* Plugin Name: WooCommerce Custom Expedited Order Email
* Plugin URI: http://www.skyverge.com/blog/how-to-add-a-custom-woocommerce-email/
* Description: Demo plugin for adding a custom WooCommerce email that sends admins an email when an order is received with expedited shipping
* Author: SkyVerge
* Author URI: http://www.skyverge.com
* Version: 0.1
*
* License: GNU General Public License v3.0
* License URI: http://www.gnu.org/licenses/gpl-3.0.html
*
*/
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
/**
* Add a custom email to the list of emails WooCommerce should load
*
* #since 0.1
* #param array $email_classes available email classes
* #return array filtered available email classes
*/
function add_expedited_order_woocommerce_email( $email_classes ) {
// include our custom email class
require( 'includes/class-wc-expedited-order-email.php' );
// add the email class to the list of email classes that WooCommerce loads
$email_classes['WC_Expedited_Order_Email'] = new WC_Expedited_Order_Email();
return $email_classes;
}
add_filter( 'woocommerce_email_classes', 'add_expedited_order_woocommerce_email' );
And here is a link to the code of my class
<?php
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
/**
* A custom Expedited Order WooCommerce Email class
*
* #since 0.1
* #extends \WC_Email
*/
class WC_Expedited_Order_Email extends WC_Email {
/**
* Set email defaults
*
* #since 0.1
*/
public function __construct() {
// set ID, this simply needs to be a unique name
$this->id = 'wc_expedited_order';
// this is the title in WooCommerce Email settings
$this->title = 'Refunded Order Email';
// this is the description in WooCommerce email settings
$this->description = 'Refunded Emails are sent when an order status has been changed to Refunded';
// these are the default heading and subject lines that can be overridden using the settings
$this->heading = 'Refunded Order';
$this->subject = 'Refunded Order';
// these define the locations of the templates that this email should use, we'll just use the new order template since this email is similar
$this->template_html = 'emails/admin-new-order.php';
$this->template_plain = 'emails/plain/admin-new-order.php';
// Trigger on new paid orders
add_action( 'woocommerce_order_status_pending_to_processing_notification', array( $this, 'trigger' ) );
add_action( 'woocommerce_order_status_failed_to_processing_notification', array( $this, 'trigger' ) );
// Call parent constructor to load any other defaults not explicity defined here
parent::__construct();
// this sets the recipient to the settings defined below in init_form_fields()
$this->recipient = $this->get_option( 'recipient' );
// if none was entered, just use the WP admin email as a fallback
if ( ! $this->recipient )
$this->recipient = get_option( 'admin_email' );
}
/**
* Determine if the email should actually be sent and setup email merge variables
*
* #since 0.1
* #param int $order_id
*/
public function trigger( $order_id ) {
// bail if no order ID is present
if ( ! $order_id )
return;
$order = new WC_Order( $order_id );
//bail if not a refunded order
if ( 'refunded' !== $order->status ) {
return;
}
// setup order object
$this->object = new WC_Order( $order_id );
// bail if shipping method is not expedited
//if ( ! in_array( $this->object->get_shipping_method(), array( 'Three Day Shipping', 'Next Day Shipping' ) ) )
//return;
// replace variables in the subject/headings
$this->find[] = '{order_date}';
$this->replace[] = date_i18n( woocommerce_date_format(), strtotime( $this->object->order_date ) );
$this->find[] = '{order_number}';
$this->replace[] = $this->object->get_order_number();
if ( ! $this->is_enabled() || ! $this->get_recipient() )
return;
// woohoo, send the email!
$this->send( $this->get_recipient(), $this->get_subject(), $this->get_content(), $this->get_headers(), $this->get_attachments() );
}
/**
* get_content_html function.
*
* #since 0.1
* #return string
*/
public function get_content_html() {
ob_start();
woocommerce_get_template( $this->template_html, array(
'order' => $this->object,
'email_heading' => $this->get_heading()
) );
return ob_get_clean();
}
/**
* get_content_plain function.
*
* #since 0.1
* #return string
*/
public function get_content_plain() {
ob_start();
woocommerce_get_template( $this->template_plain, array(
'order' => $this->object,
'email_heading' => $this->get_heading()
) );
return ob_get_clean();
}
/**
* Initialize Settings Form Fields
*
* #since 0.1
*/
public function init_form_fields() {
$this->form_fields = array(
'enabled' => array(
'title' => 'Enable/Disable',
'type' => 'checkbox',
'label' => 'Enable this email notification',
'default' => 'yes'
),
'recipient' => array(
'title' => 'Recipient(s)',
'type' => 'text',
'description' => sprintf( 'Enter recipients (comma separated) for this email. Defaults to <code>%s</code>.', esc_attr( get_option( 'admin_email' ) ) ),
'placeholder' => '',
'default' => ''
),
'subject' => array(
'title' => 'Subject',
'type' => 'text',
'description' => sprintf( 'This controls the email subject line. Leave blank to use the default subject: <code>%s</code>.', $this->subject ),
'placeholder' => '',
'default' => ''
),
'heading' => array(
'title' => 'Email Heading',
'type' => 'text',
'description' => sprintf( __( 'This controls the main heading contained within the email notification. Leave blank to use the default heading: <code>%s</code>.' ), $this->heading ),
'placeholder' => '',
'default' => ''
),
'email_type' => array(
'title' => 'Email type',
'type' => 'select',
'description' => 'Choose which format of email to send.',
'default' => 'html',
'class' => 'email_type',
'options' => array(
'plain' => 'Plain text',
'html' => 'HTML', 'woocommerce',
'multipart' => 'Multipart', 'woocommerce',
)
)
);
}
} // end \WC_Expedited_Order_Email class
These are the only 2 files in my plugin. I have activated it and it appears as an email in the list of emails in the woo commerce email tab. Unfortunately no email is sent when the order status is updated.
Can anyone advise why this is failing to work?
I have had some feedback of an individual who said the following
"the actions that you're adding the trigger to are for pending/failed to processing order status changes - http://cld.wthms.co/cZzw
You'd want these to be actions that are related to the refunded orders, like: add_action( 'woocommerce_order_status_refunded', array( $this, 'trigger' ) ); (for the exact one look around woocommerce's email classes)"
I am using Woocommerce 2.1.12
The main problem is that the woocommerce_order_status_refunded hook is not registered by default with the send_transactional_email callback, so you can't use the above method to send emails automatically when the order status is changed to Refunded.
You can change that with the following:
/**
* Register the "woocommerce_order_status_refunded" hook which is necessary to
* allow automatic email notifications when the order is changed to refunded.
*
* #see http://stackoverflow.com/a/26413223/2078474
*/
add_action( 'woocommerce_init', function() {
add_action(
'woocommerce_order_status_refunded',
array( WC(), 'send_transactional_email' ),
10,
10
);
});
Also make sure that you have enable it in the corresponding section in the Woo Settings -> Emails tab:
By default the following actions are registered for automatic email notifications:
woocommerce_low_stock
woocommerce_no_stock
woocommerce_product_on_backorder
woocommerce_order_status_pending_to_processing
woocommerce_order_status_pending_to_completed
woocommerce_order_status_pending_to_on-hold
woocommerce_order_status_failed_to_processing
woocommerce_order_status_failed_to_completed
woocommerce_order_status_completed
woocommerce_new_customer_note
woocommerce_created_customer
Update:
Good news, #helgatheviking just got her WooCommerce pull request merged (see the comments below).
This means we should be able to use the new woocommerce_email_actions filter:
add_filter( 'woocommerce_email_actions', function( $email_actions ) {
$email_actions[] = 'woocommerce_order_status_refunded';
return $email_actions;
});
in WooCommerce 2.3+.
Similar should work for other non-default email actions, like woocommerce_order_status_cancelled.
I think the problem is you are calling
add_action( 'woocommerce_order_status_pending_to_processing_notification', array( $this, 'trigger' ) );
Try calling:
add_action( 'woocommerce_order_status_refunded', array( $this, 'trigger' ) );
Related
I'm developing a plugin to integrate my WooCommerce store with a third-party right now, and as part of that integration I need to send the customer a new email with some license keys in it. The email sending functionality is fine, the class and its template are working as intended, but the email's sending twice, and I can't figure out why.
I've combed over the new email class and compared it with vanilla WooCommerce email classes, and can't tell where I'm going wrong. I've also tried using an if (did_action( 'woocommerce_order_status_completed_notification' ) === 1) {} check to limit the trigger function to one email, and even still it's sending two.
The code for my email class is here, which is where I'm assuming the problem to be as it doesn't interface with anything other than the default WooCommerce hooks that are working as expected with the vanilla emails. I've tested it with a number of different hooks (both vanilla WooCommerce ones and those I'm calling from my plugin's code), and I'm getting this same double email regardless. If anyone can see where I'm going wrong it'd be tremendously helpful!
<?php
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
if ( ! class_exists( 'Foundry_Code_Email', false ) ) :
class Foundry_Code_Email extends WC_Email {
/**
* Set email defaults
*/
public function __construct() {
$this->id = 'foundry_code_email'; // Unique ID for custom email
$this->customer_email = true; // Is a customer email
$this->title = __( 'Foundry Premium Content Email', 'woocommerce' ); // Title field in WooCommerce Email settings
$this->description = __( 'Foundry email is sent when customer purchases Foundry Premium Content', 'woocommerce' ); // Description field in WooCommerce email settings
$this->template_base = WP_PLUGIN_DIR . '/foundry-premium-content/templates/';
$this->template_html = 'emails/foundry-code-email.php';
$this->template_plain = 'emails/plain/foundry-code-email.php';
// $this->template_html = 'emails/customer-refunded-order.php';
$this->placeholders = array(
// '{site_title}' => $this->get_blogname(),
'{order_date}' => '',
'{order_number}' => '',
);
// Trigger email when woocommerce_order_status_completed_notification is called when payment is complete - double email debug
add_action( 'woocommerce_order_status_completed_notification', array( $this, 'trigger' ), 10, 2 );
// Call parent constructor to load any other defaults not explicitly defined here.
parent::__construct();
}
/**
* Prepares email content and triggers the email
*
* #param int $order_id
*/
public function trigger( $order_id, $order = false ) {
$this->setup_locale();
if ( $order_id && ! is_a( $order, 'WC_Order') ) {
$order = wc_get_order( $order_id );
}
if ( is_a( $order, 'WC_Order' ) ) {
$this->object = $order;
$this->recipient = $this->object->get_billing_email();
$this->placeholders['{order_date}'] = wc_format_datetime( $this->object->get_date_created() );
$this->placeholders['{order_number}'] = $this->object->get_order_number();
// Maybe include an additional check to make sure that stuff happened
}
if ( $this->is_enabled() && $this->get_recipient() ) {
// All well, send the email
$this->send ( $this->get_recipient(), $this->get_subject(), $this->get_content(), $this->get_headers(), $this->get_attachments() );
}
$this->restore_locale();
// Add order note about the same
$this->object->add_order_note( sprintf(__('%s email sent to the customer.', 'woocommerce'), $this->get_title() ) );
// Set order meta to indicate that the welcome email was sent
update_post_meta( $order_id, 'foundry_code_email_sent', 1);
}
/**
* Get email subject.
*
* #since 3.1.0
* #return string
*/
public function get_default_subject() {
return __( '{site_title}: Foundry VTT Premium Content Codes', 'woocommerce' );
}
/**
* Get email heading
*
* #since 3.1.0
* #return string
*/
public function get_default_heading() {
return __('Your Foundry Premium Content Codes', 'woocommerce' );
}
/**
* get_content_html function
*
* #return string
*/
public function get_content_html() {
return wc_get_template_html(
$this->template_html,
array(
'order' => $this->object,
'email_heading' => $this->get_heading(),
'additional_content' => $this->get_additional_content(),
'sent_to_admin' => false,
'plain_text' => false,
'email' => $this,
), '', $this->template_base
);
}
public function get_content_plain() {
return wc_get_template_html(
$this->template_plain,
array(
'order' => $this->object,
'email_heading' => $this->get_heading(),
'additional_content' => $this->get_additional_content(),
'sent_to_admin' => false,
'plain_text' => true,
'email' => $this,
), '', $this->template_base
);
}
public function get_default_additional_content() {
return __( 'Thanks for shopping with us.', 'woocommerce' );
}
/*
* Initialise settings form fields
*/
public function init_form_fields() {
$this->form_fields = array(
'enabled' => array(
'title' => __( 'Enable/Disable', 'woocommerce' ),
'type' => 'checkbox',
'label' => 'Enable this email notification',
'default' => 'yes'
),
'subject' => array(
'title' => __( 'Subject', 'woocommerce' ),
'type' => 'text',
'desc_tip' => true,
'description' => sprintf( 'This controls the email subject line. Leave blank to use the default subject: <code>%s</code>.', $this->get_subject() ),
'placeholder' => $this->get_default_subject(),
'default' => ''
),
'heading' => array(
'title' => __( 'Email Heading', 'woocommerce' ),
'type' => 'text',
'desc_tip' => true,
'description' => sprintf( __( 'This controls the main heading contained within the email notification. Leave blank to use the default heading: <code>%s</code>.' ), $this->get_heading() ),
'placeholder' => $this->get_default_heading(),
'default' => ''
),
'email_type' => array(
'title' => __( 'Email type', 'woocommerce'),
'type' => 'select',
'description' => __( 'Choose which format of email to send.', 'woocommerce' ),
'default' => 'html',
'class' => 'email_type wc-enhanced-select',
'options' => $this->get_email_type_options(),
'desc_tip' => true,
)
);
}
}
endif;
return new Foundry_Code_Email();
?>
I found the root issue with this was that I was returning the email class twice: once in the class file itself and once in my overall functions file.
This was my old code in that functions file:
function fpc_custom_woocommerce_emails( $email_classes ) {
// Custom email for Foundry Premium Content
$plugin_dir = dirname(__DIR__);
include_once( $plugin_dir . '/Includes/class-foundry-code-email.php' );
$email_classes['Foundry_Code_Email'] = new Foundry_Code_Email(); // Add to the list of email classes that WooCommerce loads.
return $email_classes;
}
And this is the revised code in that file now:
function fpc_custom_woocommerce_emails( $email_classes ) {
// Custom email for Foundry Premium Content
$plugin_dir = dirname(__DIR__);
$email_classes['Foundry_Code_Email'] = include_once( $plugin_dir . '/Includes/class-foundry-code-email.php' );
return $email_classes;
}
I need to change the Woocommerce payment gateway names, not the ones hat are displayed on the frontend(this can easily be achieved in the settings) but the inernatl titles Woo is using.
In class-wc-gateway-cheque.php for example I found this
$this->id = 'cheque';
but simply changing the name there did not work. How can I change the name Woocommerce is using internally for this payment method?
So what you can do instead is to copy the source code from WC_Gateway_Cheque Class to a plugin file as explained below:
To make a custom gateway based on an existing WooCommerce payment method as cheque, It's recommended to copy the source code from WC_Gateway_Cheque Class in a plugin (adapting the code for your needs).
You can copy the code to a php file that you will name for example wc-invoice-payments.php.
The code to copy:
<?php
/**
* Plugin Name: WooCommerce Invoice Gateway
* Plugin URI:
* Description: Clones the "Cheque" gateway to create another custom payment method.
* Author: Your name
* Author URI: http://www.something.tld/
* Version: 1.0.0
* Text Domain: wc-invoice-gateway
* Domain Path: /i18n/languages/
*
* Copyright: (c) 2016-2018
*
* License: GNU General Public License v3.0
* License URI: http://www.gnu.org/licenses/gpl-3.0.html
*
* #package wc-invoice-gateway
* #author Your name
* #category Admin
* #copyright Copyright (c) 2020
* #license http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License v3.0
*
* This "Invoice" gateway forks the WooCommerce core "Cheque" payment gateway to create another custom payment method.
*/
defined( 'ABSPATH' ) or exit;
// Make sure WooCommerce is active
if ( ! in_array( 'woocommerce/woocommerce.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) ) {
return;
}
/**
* Add the gateway to WC Available Gateways
*
* #since 1.0.0
* #param array $gateways all available WC gateways
* #return array $gateways all WC gateways + Custom gateway
*/
function wc_invoice_add_to_gateways( $gateways ) {
$gateways[] = 'WC_Invoice_Gateway';
return $gateways;
}
add_filter( 'woocommerce_payment_gateways', 'wc_invoice_add_to_gateways' );
/**
* Adds plugin page links
*
* #since 1.0.0
* #param array $links all plugin links
* #return array $links all plugin links + our custom links (i.e., "Settings")
*/
function wc_gateway_invoice_plugin_links( $links ) {
$plugin_links = array(
'' . __( 'Configure', 'wc-invoice-gateway' ) . ''
);
return array_merge( $plugin_links, $links );
}
add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), 'wc_gateway_invoice_plugin_links' );
/**
* Invoice Payment Gateway
*
* Provides an Custom Payment Gateway; mainly for testing purposes.
* We load it later to ensure WC is loaded first since we're extending it.
*
* #class WC_Invoice_Gateway
* #extends WC_Payment_Gateway
* #version 1.0.0
*/
add_action( 'plugins_loaded', 'wc_invoice_gateway_init', 11 );
function wc_invoice_gateway_init() {
class WC_Invoice_Gateway extends WC_Payment_Gateway {
/**
* Constructor for the gateway.
*/
public function __construct() {
$this->id = 'invoice';
$this->domain = 'wc-invoice-gateway';
$this->method_title = _x( 'Invoice payments', 'Invoice payment method', $this->domain );
$this->icon = apply_filters( 'woocommerce_invoice_icon', '' );
$this->has_fields = false;
$this->method_description = __( 'Take payments in person via Invoice. This offline gateway can also be useful to test purchases.', $this->domain );
// Load the settings.
$this->init_form_fields();
$this->init_settings();
// Define user set variables.
$this->title = $this->get_option( 'title' );
$this->description = $this->get_option( 'description' );
$this->instructions = $this->get_option( 'instructions' );
// Actions.
add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) );
add_action( 'woocommerce_thankyou_invoice', array( $this, 'thankyou_page' ) );
// Customer Emails.
add_action( 'woocommerce_email_before_order_table', array( $this, 'email_instructions' ), 10, 3 );
}
/**
* Initialize Gateway Settings Form Fields
*/
public function init_form_fields() {
$this->form_fields = array(
'enabled' => array(
'title' => __( 'Enable/Disable', $this->domain ),
'type' => 'checkbox',
'label' => __( 'Enable Invoice payments', $this->domain ),
'default' => 'no',
),
'title' => array(
'title' => __( 'Title', $this->domain ),
'type' => 'text',
'description' => __( 'This controls the title which the user sees during checkout.', $this->domain ),
'default' => _x( 'Invoice', 'Invoice payment method', $this->domain ),
'desc_tip' => true,
),
'description' => array(
'title' => __( 'Description', $this->domain ),
'type' => 'textarea',
'description' => __( 'Payment method description that the customer will see on your checkout.', $this->domain ),
'default' => __( 'Receive an invoice...', $this->domain ),
'desc_tip' => true,
),
'instructions' => array(
'title' => __( 'Instructions', $this->domain ),
'type' => 'textarea',
'description' => __( 'Instructions that will be added to the thank you page and emails.', $this->domain ),
'default' => '',
'desc_tip' => true,
),
);
}
/**
* Output for the order received page.
*/
public function thankyou_page() {
if ( $this->instructions ) {
echo wp_kses_post( wpautop( wptexturize( $this->instructions ) ) );
}
}
/**
* Add content to the WC emails.
*
* #access public
* #param WC_Order $order Order object.
* #param bool $sent_to_admin Sent to admin.
* #param bool $plain_text Email format: plain text or HTML.
*/
public function email_instructions( $order, $sent_to_admin, $plain_text = false ) {
if ( $this->instructions && ! $sent_to_admin && 'invoice' === $order->get_payment_method() && $order->has_status( 'on-hold' ) ) {
echo wp_kses_post( wpautop( wptexturize( $this->instructions ) ) . PHP_EOL );
}
}
/**
* Process the payment and return the result.
*
* #param int $order_id Order ID.
* #return array
*/
public function process_payment( $order_id ) {
$order = wc_get_order( $order_id );
if ( $order->get_total() > 0 ) {
// Mark as on-hold (we're awaiting the invoice).
$order->update_status( apply_filters( 'woocommerce_invoice_process_payment_order_status', 'on-hold', $order ), _x( 'Awaiting Invoice payment', 'Invoice payment method', $this->domain ) );
} else {
$order->payment_complete();
}
// Remove cart.
WC()->cart->empty_cart();
// Return thankyou redirect.
return array(
'result' => 'success',
'redirect' => $this->get_return_url( $order ),
);
}
} // end \WC_Invoice_Gateway class
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.
Then enable WooCommerce Invoice Gateway plugin in admin.
Now in WooCommerce settings, Payments section, you can enable this payment gateway.
You can unset / remove original Cheque payment gateway changing the 1st function like:
add_filter( 'woocommerce_payment_gateways', 'wc_invoice_add_to_gateways' );
function wc_invoice_add_to_gateways( $gateways ) {
$gateways[] = 'WC_Invoice_Gateway';
unset($gateways['WC_Gateway_Cheque']; // Remove Cheque gateway
return $gateways;
}
It should work as expected.
Related: Extending WooCommerce COD payment gateway in a plugin
Initial answer:
As all payment gateways extend WC_Payment_Gateway Class, if you look to get_title() method you will see that you can use the filter hook woocommerce_gateway_title.
So for "cheque" payment Id, you will use it as follow:
add_filter( 'woocommerce_gateway_title', 'change_cheque_payment_gateway_title', 100, 2 );
function change_cheque_payment_gateway_title( $title, $payment_id ){
if( $payment_id === 'cheque' ) {
$title = __("Something else", "woocommerce");
}
return $title;
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.
I have created a Woocommerce plugin and require it to do two things:
Send a notification message to a specific email address, based on which product variation is in the cart.
The email must contain only the relevant product, and not products that contain other attributes.
For example:
Product A has an Attribute named Chef, with chef-one and chef-two as variable Terms. The user may select Product A from chef-one or chef-two.
If the user selects Product A from chef-one, a notification email must be sent to chefone#email.com containing the name of the product ordered (as it would show up in a regular Woocommerce notification email).
If the user selects Product A from chef-one and Product B from chef-two, a notification email must be sent to chef-one containing only Product A, and a notification email must be sent to chef-two containing only Product B.
I have created the plugin using the tutorial found on https://www.skyverge.com/blog/how-to-add-a-custom-woocommerce-email/ and have adapted it to suit the above purpose.
I have also adapted code found the following solutions:
Adding a custom woocommerce email based on the product attribute
Woocommerce - Need to send email to specific address based on zip code
Here is the code from my plugin's class file:
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
/**
* A custom WAKIKI Order WooCommerce Email class
*
* #since 0.1
* #extends \WC_Email
*/
class WC_Wakiki_Order_Email extends WC_Email {
/**
* Set email defaults
*
* #since 0.1
*/
public function __construct() {
// set ID, this simply needs to be a unique name
$this->id = 'wc_wakiki_order';
// this is the title in WooCommerce Email settings
$this->title = 'WAKIKI Order';
// this is the description in WooCommerce email settings
$this->description = 'WAKIKI Order Notification emails are sent when a customer places an order on the website';
// these are the default heading and subject lines that can be overridden using the settings
$this->heading = 'WAKIKI Delivery Order';
$this->subject = 'WAKIKI Delivery Order';
// these define the locations of the templates that this email should use, we'll just use the new order template since this email is similar
$this->template_html = 'emails/admin-new-order.php';
$this->template_plain = 'emails/plain/admin-new-order.php';
// Trigger on new paid orders
add_action( 'woocommerce_order_status_pending_to_processing_notification', array( $this, 'trigger' ) );
add_action( 'woocommerce_order_status_failed_to_processing_notification', array( $this, 'trigger' ) );
// Call parent constructor to load any other defaults not explicity defined here
parent::__construct();
// this sets the recipient to the settings defined below in init_form_fields()
$this->recipient = $this->get_option( 'recipient' );
// if none was entered, just use the WP admin email as a fallback
if ( ! $this->recipient )
$this->recipient = get_option( 'admin_email' );
}
/**
* Determine if the email should actually be sent and setup email merge variables
*
* #since 0.1
* #param int $order_id
*/
public function trigger( $order_id ) {
// Bail if no order ID is present
if ( ! $order_id )
return;
$order = new WC_Order( $order_id );
// Find the product_id
$items = $order->get_items();
foreach ( $items as $item ) {
$product_id = $item['product_id'];
}
// From the product_id get the product attribute
$product = new WC_Product( $product_id ); // create an object of WC_Product class
$patt = $product->get_attributes(); // call get_attributes method
// Condition valid to send the email (if the attributes is chef)
if ( array_key_exists('pa_chef', $patt) )
// Determine which email address to send to, based on Product Attribute Term)
add_filter( 'new_order' , 'add_recipient', 20, 2 );
function add_recipient( $email, $order ) {
$additional_email = "info#email.com";
$terms = get_terms("pa_chef");
if( $order->$term->name == "pa_chef-one" ){
$email = explode( ',', $email );
array_push( $email, $additional_email );
}
return $email;
}
{
// Send the email
// Setup order object
$this->object = new WC_Order( $order_id );
$this->recipient = $this->object->billing_email;
// Replace variables in the subject/headings
$this->find[] = '{order_date}';
$this->replace[] = date_i18n( woocommerce_date_format(), strtotime( $this->object->order_date ) );
$this->find[] = '{order_number}';
$this->replace[] = $this->object->get_order_number();
if ( ! $this->is_enabled() || ! $this->get_recipient() )
return;
// Send the email!
$this->send( $this->get_recipient(), $this->get_subject(), $this->get_content(), $this->get_headers(), $this->get_attachments() );
}
else
{
return; //do nothing if there is not chef attribute
}
}
/**
* get_content_plain function
*
* #since 0.1
* #return string
*/
public function get_content_plain() {
ob_start();
woocommerce_get_template( $this->template_plain, array(
'order' => $this->object,
'email_heading' => $this->get_heading()
) );
return ob_get_clean();
}
/**
* Initialize Settings Form Fields
*
* #since 2.0
*/
public function init_form_fields() {
$this->form_fields = array(
'enabled' => array(
'title' => 'Enable/Disable',
'type' => 'checkbox',
'label' => 'Enable this email notification',
'default' => 'yes'
),
'recipient' => array(
'title' => 'Recipient(s)',
'type' => 'text',
'description' => sprintf( 'Enter recipients (comma separated) for this email. Defaults to <code>%s</code>.', esc_attr( get_option( 'admin_email' ) ) ),
'placeholder' => '',
'default' => ''
),
'subject' => array(
'title' => 'Subject',
'type' => 'text',
'description' => sprintf( 'This controls the email subject line. Leave blank to use the default subject: <code>%s</code>.', $this->subject ),
'placeholder' => '',
'default' => ''
),
'heading' => array(
'title' => 'Email Heading',
'type' => 'text',
'description' => sprintf( __( 'This controls the main heading contained within the email notification. Leave blank to use the default heading: <code>%s</code>.' ), $this->heading ),
'placeholder' => '',
'default' => ''
),
'email_type' => array(
'title' => 'Email type',
'type' => 'select',
'description' => 'Choose which format of email to send.',
'default' => 'html',
'class' => 'email_type',
'options' => array(
'plain' => __( 'Plain text', 'woocommerce' ),
'html' => __( 'HTML', 'woocommerce' ),
'multipart' => __( 'Multipart', 'woocommerce' ),
)
)
);
}
This is as close as I am able to get it, but it is not working. I suspect the problem lies somewhere around the line that says "Determine which email address to send to, based on Product Attribute Term". The plugin was loading until I added that section.
Is the function supposed to be in a separate plugin file?
I also need help in getting the email to contain only the information relevant to the vendor it is being sent to.
Any assistance in getting this plugin to work would be greatly appreciated.
The filter new_order doesn't exist in WooCommerce (or in your code)
The correct filter hook (located in WC_Email class core code, line 269) is this one:
$recipient = apply_filters( 'woocommerce_email_recipient_' . $this->id, $this->recipient, $this->object );
In this hook, $this->id is 'new_order' for you.
There is big errors in your code:
The term name should be something like "one" or "chef-one", but absolutely not "pa_chef-one", as "pa_chef" is the taxonomy slug for your attribute "Chef".
The multiple email recipients are not in an array, but in a coma separated string.
So the correct code should be something like:
add_filter( 'woocommerce_email_recipient_new_order', 'add_recipient', 10, 2 );
function add_recipient( $recipient, $order )
{
if ( ! is_a( $order, 'WC_Order' ) ) return $recipient;
// Additional email recipient
$additional_email = "info#email.com";
// The term name "pa_chef-one" is very strange … It should be "one" or "chef-one" (may be)
$term_slug = "one";
$has_term = false;
// Iterating through each order item
foreach ($order->get_items() as $item_id => $item_obj) {
$variation_id = $item_obj->get_variation_id();
$variation_obj = wc_get_product($variation_id);
$variation_attributes = $variation_obj->get_attributes();
foreach( $variation_attributes as $taxonomy_key => $term_value ){
if( $taxonomy_key == "pa_chef" && $term_value == $term_slug ){
$recipient .= ','. $additional_email;
$has_term = true;
break; // stop the 2nd loop
}
}
if( $has_term ) break; // stop the 1st loop
}
return $recipient;
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
Made for WooCommerce version 3+
Similar answers:
Add the New order email notification attachment to the vendor email
Woocommerce email notification recipient conditionally based on custom field
WooCommerce email notifications: different email recipient for different cities
I have tried to adapt the custom email plugin found on https://www.skyverge.com/blog/how-to-add-a-custom-woocommerce-email/
My intention is that if customer choose a product (it is a variable product) that has a specific attribute, a custom email is sent when the customer makes a new order (it has to be sent either it is pending or processing).
My atribute slug is "csr-dates". The custom plugin is composed (see the link above) by two files: woocommerce-csr-order-email.php and (stored in "includes" folder) class-wc-csr-order-email.php
I guess that the problem is in the class file, which I report here:
<?php
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
/**
* A custom Order WooCommerce Email class
*
* #since 0.1
* #extends \WC_Email
*/
class WC_CSR_Order_Email extends WC_Email {
/**
* Set email defaults
*
* #since 0.1
*/
public function __construct() {
// set ID, this simply needs to be a unique name
$this->id = 'customer_csr_order';
$this->customer_email = true;
// this is the title in WooCommerce Email settings
$this->title = 'CSR Cruise Order';
// this is the description in WooCommerce email settings
$this->description = 'CSR Cruise Order Notification emails are sent when a customer places an order for a CSR cruise';
// these are the default heading and subject lines that can be overridden using the settings
$this->heading = 'CSR Cruise Order';
$this->subject = 'CSR Cruise Order';
// these define the locations of the templates that this email should use, we'll just use the new order template since this email is similar
$this->template_html = 'emails/customer-processing-order-csr.php'; //qui posso duplicare il template e farne uno ad hoc per questo tipo di mail con i file attached
$this->template_plain = 'emails/plain/customer-processing-order.php';
// Trigger on new paid orders
add_action( 'woocommerce_order_status_pending_to_processing_notification', array( $this, 'trigger' ) );
add_action( 'woocommerce_order_status_failed_to_processing_notification', array( $this, 'trigger' ) );
add_action( 'woocommerce_order_status_pending_to_on-hold_notification', array( $this, 'trigger' ) );
add_action( 'woocommerce_order_status_failed_to_on-hold_notification', array( $this, 'trigger' ) );
// Call parent constructor to load any other defaults not explicity defined here
parent::__construct();
// if none was entered, just use the WP admin email as a fallback
if ( ! $this->recipient )
$this->recipient = get_option( 'admin_email' );
}
/**
* Determine if the email should actually be sent and setup email merge variables
*
* #since 0.1
* #param int $order_id
*/
public function trigger( $order_id ) {
// bail if no order ID is present
if ( ! $order_id )
return;
$order = new WC_Order( $order_id );
//step 1) find first the product_id
$items = $order->get_items();
foreach ( $items as $item ) {
$product_id = $item['product_id'];
}
//set 2) from the product_id get the product attribute
$product = new WC_Product( $product_id ); // create an object of WC_Product class
$patt = $product->get_attribute(); // call get_attribute method
//step 3) condition valid to send the email (if the attributes is csr-dates)
if ( $patt == 'pa_csr-dates' )
{
//send the email
// setup order object
$this->object = new WC_Order( $order_id );
$this->recipient = $this->object->billing_email;
// replace variables in the subject/headings
$this->find[] = '{order_date}';
$this->replace[] = date_i18n( woocommerce_date_format(), strtotime( $this->object->order_date ) );
$this->find[] = '{order_number}';
$this->replace[] = $this->object->get_order_number();
if ( ! $this->is_enabled() || ! $this->get_recipient() )
return;
// woohoo, send the email!
$this->send( $this->get_recipient(), $this->get_subject(), $this->get_content(), $this->get_headers(), $this->get_attachments() );
}
else
{
return; //do nothing if is not csr-dates attribute
}
}
/**
* get_content_html function.
*
* #since 0.1
* #return string
*/
public function get_content_html() {
ob_start();
woocommerce_get_template( $this->template_html, array(
'order' => $this->object,
'email_heading' => $this->get_heading(),
'sent_to_admin' => false,
'plain_text' => false,
'email' => $this
) );
return ob_get_clean();
}
/**
* get_content_plain function.
*
* #since 0.1
* #return string
*/
public function get_content_plain() {
ob_start();
woocommerce_get_template( $this->template_plain, array(
'order' => $this->object,
'email_heading' => $this->get_heading(),
'sent_to_admin' => false,
'plain_text' => true,
'email' => $this
) );
return ob_get_clean();
}
/**
* Initialize Settings Form Fields
*
* #since 2.0
*/
public function init_form_fields() {
$this->form_fields = array(
'enabled' => array(
'title' => 'Enable/Disable',
'type' => 'checkbox',
'label' => 'Enable this email notification',
'default' => 'yes'
),
'subject' => array(
'title' => 'Subject',
'type' => 'text',
'description' => sprintf( 'This controls the email subject line. Leave blank to use the default subject: <code>%s</code>.', $this->subject ),
'placeholder' => '',
'default' => ''
),
'heading' => array(
'title' => 'Email Heading',
'type' => 'text',
'description' => sprintf( __( 'This controls the main heading contained within the email notification. Leave blank to use the default heading: <code>%s</code>.' ), $this->heading ),
'placeholder' => '',
'default' => ''
),
'email_type' => array(
'title' => 'Email type',
'type' => 'select',
'description' => 'Choose which format of email to send.',
'default' => 'html',
'class' => 'email_type',
'options' => array(
'plain' => __( 'Plain text', 'woocommerce' ),
'html' => __( 'HTML', 'woocommerce' ),
'multipart' => __( 'Multipart', 'woocommerce' ),
)
)
);
}
} // end \WC_CSR_Order_Email class
I imagine that something is wrong in the code I wrote to get the attribute (step 1-2) and/or the condition to send the email (step 3).
Could somebody help me in solve this problem? Thank you
Instead of using get_attribute() function you should try to use get_attributes() this way:
//set 2) from the product_id get the product attribute
$product = new WC_Product( $product_id ); // create an object of WC_Product class
$patt = $product->get_attributes(); // call get_attributes method
//step 3) condition valid to send the email (if the attributes is csr-dates)
if ( array_key_exists('pa_csr-dates', $patt) )
{
Try it, this should work…
I added a custom status wc-order-confirmed:
// Register new status
function register_order_confirmed_order_status() {
register_post_status( 'wc-order-confirmed', array(
'label' => 'Potvrzení objednávky',
'public' => true,
'exclude_from_search' => false,
'show_in_admin_all_list' => true,
'show_in_admin_status_list' => true,
'label_count' => _n_noop( 'Potvrzení objednávky <span class="count">(%s)</span>', 'Potvrzení objednávky <span class="count">(%s)</span>' )
) );
}
add_action( 'init', 'register_order_confirmed_order_status' );
// Add to list of WC Order statuses
function add_order_confirmed_to_order_statuses( $order_statuses ) {
$new_order_statuses = array();
// add new order status after processing
foreach ( $order_statuses as $key => $status ) {
$new_order_statuses[ $key ] = $status;
if ( 'wc-processing' === $key ) {
$new_order_statuses['wc-order-confirmed'] = 'Potvrzení objednávky';
}
}
return $new_order_statuses;
}
add_filter( 'wc_order_statuses', 'add_order_confirmed_to_order_statuses' );
I added a custom email wc_confirmed_order:
/**
* A custom confirmed Order WooCommerce Email class
*
* #since 0.1
* #extends \WC_Email
*/
class WC_Confirmed_Order_Email extends WC_Email {
/**
* Set email defaults
*
* #since 0.1
*/
public function __construct() {
// set ID, this simply needs to be a unique name
$this->id = 'wc_confirmed_order';
// this is the title in WooCommerce Email settings
$this->title = 'Potvrzení objednávky';
// this is the description in WooCommerce email settings
$this->description = 'Confirmed Order Notification';
// these are the default heading and subject lines that can be overridden using the settings
$this->heading = 'Potvrzení objednávky';
$this->subject = 'Potvrzení objednávky';
// these define the locations of the templates that this email should use, we'll just use the new order template since this email is similar
$this->template_html = 'emails/customer-confirmed-order.php';
$this->template_plain = 'emails/plain/admin-new-order.php';
// Trigger on confirmed orders
add_action( 'woocommerce_order_status_pending_to_order_confirmed', array( $this, 'trigger' ) );
add_action( 'woocommerce_order_status_processing_to_order_confirmed', array( $this, 'trigger' ) );
// Call parent constructor to load any other defaults not explicity defined here
parent::__construct();
// this sets the recipient to the settings defined below in init_form_fields()
$this->recipient = $this->get_option( 'recipient' );
// if none was entered, just use the WP admin email as a fallback
if ( ! $this->recipient )
$this->recipient = get_option( 'admin_email' );
}
/**
* Determine if the email should actually be sent and setup email merge variables
*
* #since 0.1
* #param int $order_id
*/
public function trigger( $order_id ) {
// bail if no order ID is present
if ( ! $order_id )
return;
if ( $order_id ) {
$this->object = wc_get_order( $order_id );
$this->recipient = $this->object->billing_email;
$this->find['order-date'] = '{order_date}';
$this->find['order-number'] = '{order_number}';
$this->replace['order-date'] = date_i18n( wc_date_format(), strtotime( $this->object->order_date ) );
$this->replace['order-number'] = $this->object->get_order_number();
}
if ( ! $this->is_enabled() || ! $this->get_recipient() )
return;
// woohoo, send the email!
$this->send( $this->get_recipient(), $this->get_subject(), $this->get_content(), $this->get_headers(), $this->get_attachments() );
}
/**
* get_content_html function.
*
* #since 0.1
* #return string
*/
public function get_content_html() {
ob_start();
wc_get_template( $this->template_html, array(
'order' => $this->object,
'email_heading' => $this->get_heading()
) );
return ob_get_clean();
}
/**
* get_content_plain function.
*
* #since 0.1
* #return string
*/
public function get_content_plain() {
ob_start();
wc_get_template( $this->template_plain, array(
'order' => $this->object,
'email_heading' => $this->get_heading()
) );
return ob_get_clean();
}
/**
* Initialize Settings Form Fields
*
* #since 2.0
*/
public function init_form_fields() {
$this->form_fields = array(
'enabled' => array(
'title' => 'Enable/Disable',
'type' => 'checkbox',
'label' => 'Enable this email notification',
'default' => 'yes'
),
'recipient' => array(
'title' => 'Recipient(s)',
'type' => 'text',
'description' => sprintf( 'Enter recipients (comma separated) for this email. Defaults to <code>%s</code>.', esc_attr( get_option( 'admin_email' ) ) ),
'placeholder' => '',
'default' => ''
),
'subject' => array(
'title' => 'Subject',
'type' => 'text',
'description' => sprintf( 'This controls the email subject line. Leave blank to use the default subject: <code>%s</code>.', $this->subject ),
'placeholder' => '',
'default' => ''
),
'heading' => array(
'title' => 'Email Heading',
'type' => 'text',
'description' => sprintf( __( 'This controls the main heading contained within the email notification. Leave blank to use the default heading: <code>%s</code>.' ), $this->heading ),
'placeholder' => '',
'default' => ''
),
'email_type' => array(
'title' => 'Email type',
'type' => 'select',
'description' => 'Choose which format of email to send.',
'default' => 'html',
'class' => 'email_type',
'options' => array(
'plain' => __( 'Plain text', 'woocommerce' ),
'html' => __( 'HTML', 'woocommerce' ),
'multipart' => __( 'Multipart', 'woocommerce' ),
)
)
);
}
} // end \WC_confirmed_Order_Email class
I can see the email in the email settings, and the status in the order statuses dropdown. Now, I need to send my new email whenever the order status is changed to wc-order-confirmed. The transition hook seems to never be firing.
I also tried:
/**
* Register the "woocommerce_order_status_pending_to_quote" hook which is necessary to
* allow automatic email notifications when the order is changed to refunded.
*
* #modified from http://stackoverflow.com/a/26413223/2078474 to remove anonymous function
*/
add_action( 'woocommerce_init', 'so_25353766_register_email' );
function so_25353766_register_email(){
add_action( 'woocommerce_order_status_pending_to_order_confirmed', array( WC(), 'send_transactional_email' ), 10, 10 );
add_action( 'woocommerce_order_status_processing_to_order_confirmed', array( WC(), 'send_transactional_email' ), 10, 10 );
}
Which also doesn't seem to work at all... Any ideas, please?
The hook you need is:
woocommerce_order_status_changed
add_action("woocommerce_order_status_changed", "my_awesome_publication_notification");
function my_awesome_publication_notification($order_id, $checkout=null) {
global $woocommerce;
$order = new WC_Order( $order_id );
if($order->status === 'completed' ) {
// Create a mailer
$mailer = $woocommerce->mailer();
$message_body = __( 'Hello world!!!' );
$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 );
}
}
}
As Xcid's answer indicates, you need to register the email.
In WC 2.2+ I believe you can do this via the following:
add_action( 'woocommerce_order_status_wc-order-confirmed', array( WC(), 'send_transactional_email' ), 10, 10 );
I'd added a filter to WooCommerce 2.3, so when that comes out custom emails will be able to be added to the list of email actions that WooCommerce registers:
// As of WooCommerce 2.3
function so_27112461_woocommerce_email_actions( $actions ){
$actions[] = 'woocommerce_order_status_wc-order-confirmed';
return $actions;
}
add_filter( 'woocommerce_email_actions', 'so_27112461_woocommerce_email_actions' );
As you can see here :
https://github.com/woothemes/woocommerce/blob/f8a161c40673cb019eb96b04c04a774ca040a15a/includes/abstracts/abstract-wc-order.php#L2097
you can use this hook :
do_action( 'woocommerce_order_status_' . $new_status, $this->id );
with you custom status should give :
add_action( 'woocommerce_order_status_wc-order-confirmed' , array( $this, 'trigger' ) );
I imagine that you also add you custom email to the mailer, if not :
just add :
add_filter( 'woocommerce_email_classes', array($this,'edit_woocommerce_email_classes' ));
function edit_woocommerce_email_classes( $email_classes ) {
require_once( 'your-email-class.php' );
$email_classes[ 'WC_Confirmed_Order_Email' ] = new WC_Confirmed_Order_Email();
return $email_classes;
}
Edit :
You need to instanciate woocommerce emails before so you can add
add_action( 'init' , 'initiate_woocommerce_email' );
function initiate_woocommerce_email(){
// Just when you update the order_status on backoffice
if( isset($_POST['order_status']) ) {
WC()->mailer();
}
}
You can try to watch when the order status changed so put this into functions.php:
function confirmed_notifications($order_id, $checkout=null) {
global $woocommerce;
$order = new WC_Order( $order_id );
if( $order->status === 'order-confirmed' ) {
// Trigger transactional email to client
$email = $mailer->emails['WC_Confirmed_Order_Email'];
$email->trigger( $order_id );
}
}
add_action("woocommerce_order_status_changed", "confirmed_notifications");
This function will trigger your email and send it.