How to make shipping method based on weight for specific city?
For example, I want to take the postage for city "A" for $1 per kg.
Here is the code of plugin I made:
public function calculate_shipping( $package ) {
$rate = array(
'id' => $this->id,
'label' => $this->title,
'cost' => '10.99',
'calc_tax' => 'per_item'
);
// Register the rate
$this->add_rate( $rate );
}
Full code:
<?php
/*
Plugin Name: Trucking shipping
Plugin URI: http://abdhannan.com
Description: Trucking shipping dengan JNE dsb
Version: 1.0.0
Author: Abd Hannan
Author URI: http://abdhannan.com
*/
/**
* Check if WooCommerce is active
*/
if ( in_array( 'woocommerce/woocommerce.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) ) {
function trucking_shipping_init() {
if ( ! class_exists( 'WC_Your_Shipping_Method' ) ) {
class WC_trucking_shipping_Method extends WC_Shipping_Method {
/**
* Constructor for your shipping class
*
* #access public
* #return void
*/
public function __construct() {
$this->id = 'trucking_shipping_method'; // Id for your shipping method. Should be uunique.
$this->method_title = __( 'JNE TRUCKING' ); // Title shown in admin
$this->method_description = __( 'Pengiriman dengan truck JNE' ); // Description shown in admin
$this->enabled = "yes"; // This can be added as an setting but for this example its forced enabled
$this->title = "JNE TRUCKING"; // This can be added as an setting but for this example its forced.
$this->init();
}
/**
* Init your settings
*
* #access public
* #return void
*/
function init() {
// Load the settings API
$this->init_form_fields(); // This is part of the settings API. Override the method to add your own settings
$this->init_settings(); // This is part of the settings API. Loads settings you previously init.
// Save settings in admin if you have any defined
add_action( 'woocommerce_update_options_shipping_' . $this->id, array( $this, 'process_admin_options' ) );
}
/**
* calculate_shipping function.
*
* #access public
* #param mixed $package
* #return void
*/
public function calculate_shipping( $package ) {
$rate = array(
'id' => $this->id,
'label' => $this->title,
'cost' => '10.99',
'calc_tax' => 'per_item'
);
// Register the rate
$this->add_rate( $rate );
}
}
}
}
add_action( 'woocommerce_shipping_init', 'trucking_shipping_init' );
function add_trucking_shipping_method( $methods ) {
$methods['trucking_shipping_method'] = 'WC_trucking_shipping_Method';
return $methods;
}
add_filter( 'woocommerce_shipping_methods', 'add_trucking_shipping_method' );
}
The problem is in calculate_shipping, I don't know how to do what I want.
This is not really a code question, rather a 'how do I use WooCommerce' question. What you are looking for is functionality Automattic deliberately removed from WooCommerce so that they could charge for it, look here for the plugin.
If you are frugal and reduce your product weights down to a specific set, then all you really need is flat rate shipping assigned to postal code defined shipping zones, mixed with shipping classes and boom no programming ever needed.
Koda
Related
I'm working on a custom plugin that processes order data & sends it to another Web application.
I hook in to order processing using WooCommerce's woocommerce_checkout_order_processed hook, so, I get the order id as an argument & hence can easily obtain WC_Order.
The problem I got stuck with is the following:
I want to calculate some data based on the shipping method chosen by the customer, but I need more data than just the shipping method name in order to perform the calculation.
A simplified version of what I want to achieve is essentially something very similar to:
$order = wc_get_order($order_id);
$is_express_shipping = strpos($order->get_shipping_method(), 'Express') !== false;
But the whole problem is that the shipping method name does not contain (and in fact, it should not) any hint regarding whether it is an express shipping method or not, so, the only option is to pass some additional information about the shipping method through.
I've tried to add a custom shipping method as described in this question & in its accepted answer, so, I added the desired data into shipping method settings like this:
But it looks like WooCommerce doesn't expect custom shipping classes to register custom fields or at least, I didn't figure out how to get the Launch Simple Shipping's "Shipping Mode" property from within the order object (it does not appear anywhere within $order->get_shipping_methods() output).
Here is the full code of the custom shipping class in case if needed. It's a copy-paste from the previously mentioned question + a new field.
function launch_shipping_method() {
if (!class_exists('Launch_Shipping_Method')) {
class Launch_Shipping_Method extends WC_Shipping_Method {
public function __construct( $instance_id = 0 ) {
$this->id = 'launch_shipping';
$this->instance_id = absint( $instance_id );
$this->method_title = __('Launch Simple Shipping', 'launch_shipping');
$this->method_description = __('Custom Simple Shipping Method', 'launch_shipping');
$this->supports = array(
'shipping-zones',
'instance-settings',
'instance-settings-modal',
);
$this->init();
}
/**
* Initialize Launch Simple Shipping.
*/
public function init() {
// Load the settings.
$this->init_form_fields();
$this->init_settings();
// Define user set variables.
$this->title = isset($this->settings['title']) ? $this->settings['title'] : __('Launch Shipping', 'launch_shipping');
$this->shipping_mode = isset($this->settings['shipping_mode']) ? $this->settings['shipping_mode'] : __('Launch Shipping', 'launch_shipping');
add_action('woocommerce_update_options_shipping_' . $this->id, array($this, 'process_admin_options'));
}
/**
* Init form fields.
*/
public function init_form_fields() {
$this->form_fields = array(
'title' => array(
'title' => __( 'Title', 'launch_shipping' ),
'type' => 'text',
'description' => __( 'This controls the title which the user sees during checkout.', 'launch_shipping' ),
'default' => $this->method_title,
'desc_tip' => true,
),
'shipping_mode' => array(
'title' => __( 'Shipping Mode', 'launch_shipping' ),
'type' => 'select',
'default' => 'Standard',
'options' => [
'Standard' => 'Standard',
'Express' => 'Express',
],
)
);
}
/**
* Get setting form fields for instances of this shipping method within zones.
*
* #return array
*/
public function get_instance_form_fields() {
return parent::get_instance_form_fields();
}
/**
* Always return shipping method is available
*
* #param array $package Shipping package.
* #return bool
*/
public function is_available( $package ) {
$is_available = true;
return apply_filters( 'woocommerce_shipping_' . $this->id . '_is_available', $is_available, $package, $this );
}
/**
* Free shipping rate applied for this method.
*
* #uses WC_Shipping_Method::add_rate()
*
* #param array $package Shipping package.
*/
public function calculate_shipping( $package = array() ) {
$this->add_rate(
array(
'label' => $this->title,
'cost' => 0,
'taxes' => false,
'package' => $package,
'shipping_mode' => $this->shipping_mode,
)
);
}
}
}
}
add_action('woocommerce_shipping_init', 'Launch_Shipping_Method');
function add_launch_shipping_method($methods) {
$methods['launch_shipping'] = 'launch_shipping_method';
return $methods;
}
add_filter('woocommerce_shipping_methods', 'add_launch_shipping_method');
Is there a way to somehow pass additional data about the chosen shipping method into WC_Order object or somehow obtain it having WC_Order at hand?
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.
EDITED - Progress made on original post
I have created a simple custom shipping method plugin stub (see code below).
The plugin is registered and now appears in the shipping method dropdown when I'm creating a shipping zone. However when selected the custom field doesn't appear for the shipping zone (see gif)
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
if (in_array('woocommerce/woocommerce.php', apply_filters('active_plugins', get_option('active_plugins')))) {
function launch_shipping_method() {
if (!class_exists('Launch_Shipping_Method')) {
class Launch_Shipping_Method extends WC_Shipping_Method {
public function __construct( $instance_id = 0 ) {
$this->id = 'launch_shipping';
$this->instance_id = absint( $instance_id );
$this->method_title = __('Launch Simple Shipping', 'launch_shipping');
$this->method_description = __('Custom Simple Shipping Method', 'launch_shipping');
$this->supports = array(
'shipping-zones',
'instance-settings',
'instance-settings-modal',
);
$this->init();
}
/**
* Initialize Launch Simple Shipping.
*/
public function init() {
// Load the settings.
$this->init_form_fields();
$this->init_settings();
// Define user set variables.
$this->title = isset($this->settings['title']) ? $this->settings['title'] : __('Launch Shipping', 'launch_shipping');
add_action('woocommerce_update_options_shipping_' . $this->id, array($this, 'process_admin_options'));
}
/**
* Init form fields.
*/
public function init_form_fields() {
$this->form_fields = array(
'title' => array(
'title' => __( 'Title', 'launch_shipping' ),
'type' => 'text',
'description' => __( 'This controls the title which the user sees during checkout.', 'launch_shipping' ),
'default' => $this->method_title,
'desc_tip' => true,
)
);
}
/**
* Get setting form fields for instances of this shipping method within zones.
*
* #return array
*/
public function get_instance_form_fields() {
return parent::get_instance_form_fields();
}
/**
* Always return shipping method is available
*
* #param array $package Shipping package.
* #return bool
*/
public function is_available( $package ) {
$is_available = true;
return apply_filters( 'woocommerce_shipping_' . $this->id . '_is_available', $is_available, $package, $this );
}
/**
* Free shipping rate applied for this method.
*
* #uses WC_Shipping_Method::add_rate()
*
* #param array $package Shipping package.
*/
public function calculate_shipping( $package = array() ) {
$this->add_rate(
array(
'label' => $this->title,
'cost' => 0,
'taxes' => false,
'package' => $package,
)
);
}
}
}
}
add_action('woocommerce_shipping_init', 'Launch_Shipping_Method');
function add_launch_shipping_method($methods) {
$methods[] = 'launch_shipping_method';
return $methods;
}
add_filter('woocommerce_shipping_methods', 'add_launch_shipping_method');
}
Try this:
function add_launch_shipping_method($methods) {
$methods['launch_shipping'] = 'launch_shipping_method';
return $methods;
}
It should fix things. If you need to debug it or want to understand more have a look at:
wp-content/plugins/woocommerce/includes/class-wc-shipping-zone.php
look at
function add_shipping_method($type)
Briefly: the id you register the class with in your woocommerce_shipping_init hook must match the class id property (the one you set with $this->id = 'launch_shipping';)
I have successfully created a new shipping method and given it support for shipping zones. However when I come to select the method from the dropdown to add it to the zone it does not appear in the 'selected methods list'.
I recorded a screencast gif to demonstrate:
I can't for the life of me figure out why it's not working. It works fine if I select one of the standard methods (Screencast GIF)
Anyone know what's going on here and how to get it to work?
Here's the code that I have from this official thread: Shipping Method API:
if ( in_array( 'woocommerce/woocommerce.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) ) {
function request_a_shipping_quote_init() {
if ( ! class_exists( 'WC_Request_Shipping_Quote_Method' ) ) {
class WC_Request_Shipping_Quote_Method extends WC_Shipping_Method {
/**
* Constructor for your shipping class
*
* #access public
* #return void
*/
public function __construct() {
$this->id = 'request_a_shipping_quote'; // Id for your shipping method. Should be uunique.
$this->method_title = __( 'Request a Shipping Quote' ); // Title shown in admin
$this->method_description = __( 'Shipping method to be used where the exact shipping amount needs to be quoted' ); // Description shown in admin
$this->title = "Request a Shipping Quote"; // This can be added as an setting but for this example its forced.
$this->supports = array(
'shipping-zones'
);
$this->init();
}
/**
* Init your settings
*
* #access public
* #return void
*/
function init() {
// Load the settings API
$this->init_form_fields(); // This is part of the settings API. Override the method to add your own settings
$this->init_settings(); // This is part of the settings API. Loads settings you previously init.
// Save settings in admin if you have any defined
add_action( 'woocommerce_update_options_shipping_' . $this->id, array( $this, 'process_admin_options' ) );
}
function init_form_fields() {
$this->form_fields = array(
'enabled' => array(
'title' => __( 'Enable', 'dc_raq' ),
'type' => 'checkbox',
'description' => __( 'Enable this shipping method.', 'dc_raq' ),
'default' => 'yes'
),
'title' => array(
'title' => __( 'Title', 'dc_raq' ),
'type' => 'text',
'description' => __( 'Title to be displayed on site', 'dc_raq' ),
'default' => __( 'Request a Quote', 'dc_raq' )
),
);
}
/**
* calculate_shipping function.
*
* #access public
*
* #param mixed $package
*
* #return void
*/
public function calculate_shipping( $packages = array() ) {
$rate = array(
'id' => $this->id,
'label' => $this->title,
'cost' => '0.00',
'calc_tax' => 'per_item'
);
// Register the rate
$this->add_rate( $rate );
}
}
}
}
add_action( 'woocommerce_shipping_init', 'request_a_shipping_quote_init' );
function request_shipping_quote_shipping_method( $methods ) {
$methods['request_shipping_quote_shipping_method'] = 'WC_Request_Shipping_Quote_Method';
return $methods;
}
add_filter( 'woocommerce_shipping_methods', 'request_shipping_quote_shipping_method' );
}
The method key on "woocommerce_shipping_methods" should match the shipping method id
In your case:
You should change
function request_shipping_quote_shipping_method( $methods ) {
$methods['request_shipping_quote_shipping_method'] = 'WC_Request_Shipping_Quote_Method';
return $methods;
}
add_filter( 'woocommerce_shipping_methods', 'request_shipping_quote_shipping_method' );
To:
function request_shipping_quote_shipping_method( $methods ) {
$methods['request_a_shipping_quote'] = 'WC_Request_Shipping_Quote_Method';
return $methods;
}
add_filter( 'woocommerce_shipping_methods', 'request_shipping_quote_shipping_method' );
Change this line
public function calculate_shipping( $package ) {
to this line
public function calculate_shipping( $package = array() ) {
After I trying to use code in question and fix all errors that I found in comments to these post I still had some problems with it. For example I can't edit shipping method after even I successfully added it to shipping zone.
Finally I got desired code that working for me after edit standard free shipping woocoommerce method. Hope it will save time for someone.
function request_a_shipping_quote_init() {
if ( ! class_exists( 'Imp_WC_Shipping_Local_Pickup' ) ) {
class Imp_WC_Pickup_Shipping_Method extends WC_Shipping_Method {
/**
* Constructor.
*
* #param int $instance_id
*/
public function __construct( $instance_id = 0 ) {
$this->id = 'imp_pickup_shipping_method';
$this->instance_id = absint( $instance_id );
$this->method_title = __( "Самовывоз из точки выдачи ( MO г. Дзержинский )", 'imp' );
$this->supports = array(
'shipping-zones',
'instance-settings',
'instance-settings-modal',
);
$this->init();
}
/**
* Initialize custom shiping method.
*/
public function init() {
// Load the settings.
$this->init_form_fields();
$this->init_settings();
// Define user set variables
$this->title = $this->get_option( 'title' );
// Actions
add_action( 'woocommerce_update_options_shipping_' . $this->id, array( $this, 'process_admin_options' ) );
}
/**
* Calculate custom shipping method.
*
* #param array $package
*
* #return void
*/
public function calculate_shipping( $package = array() ) {
$this->add_rate( array(
'label' => $this->title,
'package' => $package,
) );
}
/**
* Init form fields.
*/
public function init_form_fields() {
$this->instance_form_fields = array(
'title' => array(
'title' => __( 'Самовывоз из точки выдачи ( MO г. Дзержинский )', 'imp' ),
'type' => 'text',
'description' => __( 'This controls the title which the user sees during checkout.', 'woocommerce' ),
'default' => __( 'Самовывоз из точки выдачи ( MO г. Дзержинский )', 'imp' ),
'desc_tip' => true,
),
);
}
}
}
}
add_action( 'woocommerce_shipping_init', 'request_a_shipping_quote_init' );
function request_shipping_quote_shipping_method( $methods ) {
$methods['imp_pickup_shipping_method'] = 'Imp_WC_Pickup_Shipping_Method';
return $methods;
}
add_filter( 'woocommerce_shipping_methods', 'request_shipping_quote_shipping_method' );
WC_Custom_Shipping_Method is an abstract class and you are trying to change its inherited method calculate_shipping which abstract classes don't allow.
Try doing it like this.
<?php
if ( in_array( 'woocommerce/woocommerce.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) ) {
function request_a_shipping_quote_init() {
class Abs_Custom_Shipping extends WC_Shipping_Method{}
if ( ! class_exists( 'WC_Request_Shipping_Quote_Method' ) ) {
class WC_Request_Shipping_Quote_Method extends Abs_Custom_Shipping {
/**
* Constructor for your shipping class
*
* #access public
* #return void
*/
public function __construct() {
$this->id = 'request_a_shipping_quote'; // Id for your shipping method. Should be uunique.
$this->method_title = __( 'Request a Shipping Quote' ); // Title shown in admin
$this->method_description = __( 'Shipping method to be used where the exact shipping amount needs to be quoted' ); // Description shown in admin
$this->title = "Request a Shipping Quote"; // This can be added as an setting but for this example its forced.
$this->supports = array(
'shipping-zones'
);
$this->init();
}
/**
* Init your settings
*
* #access public
* #return void
*/
function init() {
// Load the settings API
$this->init_form_fields(); // This is part of the settings API. Override the method to add your own settings
$this->init_settings(); // This is part of the settings API. Loads settings you previously init.
// Save settings in admin if you have any defined
add_action( 'woocommerce_update_options_shipping_' . $this->id, array( $this, 'process_admin_options' ) );
}
function init_form_fields() {
$this->form_fields = array(
'enabled' => array(
'title' => __( 'Enable', 'dc_raq' ),
'type' => 'checkbox',
'description' => __( 'Enable this shipping method.', 'dc_raq' ),
'default' => 'yes'
),
'title' => array(
'title' => __( 'Title', 'dc_raq' ),
'type' => 'text',
'description' => __( 'Title to be displayed on site', 'dc_raq' ),
'default' => __( 'Request a Quote', 'dc_raq' )
),
);
}
/**
* calculate_shipping function.
*
* #access public
*
* #param mixed $package
*
* #return void
*/
public function calculate_shipping( $packages = array() ) {
$rate = array(
'id' => $this->id,
'label' => $this->title,
'cost' => '0.00',
'calc_tax' => 'per_item'
);
// Register the rate
$this->add_rate( $rate );
}
}
}
}
add_action( 'woocommerce_shipping_init', 'request_a_shipping_quote_init' );
function request_shipping_quote_shipping_method( $methods ) {
$methods['request_shipping_quote_shipping_method'] = 'WC_Request_Shipping_Quote_Method';
return $methods;
}
add_filter( 'woocommerce_shipping_methods', 'request_shipping_quote_shipping_method' );
}
( extend the shipping method into a child class, then extend the child class into a grandchild class where you can modify the calculate_shipping method ).
Hope it makes sense.
Regards
I had this issue and it was driving me crazy for a few days until, while looking through the Woocommerce code to understand what was happening, I discovered that when setting up the filter method for the woocommerce_shipping_methods, I needed to make the index of the entry that I was adding to this array the same as the ID property in my shipping method class. Once I did this, it added the shipping method fine and it showed it correctly for the zone. Previously, I had been adding the entry to the array in the filter method with no index, which works fine as far as WC seeing the method which was why it seemed ok. However, the code that saves the settings, uses the ID as the index to identify the shipping method. From other comments, I would imagine that this specific index was added in WC version 3. Hope this helps.
If your shipping method still seems not to work, you have to ensure that
instance_id must be defined in constructor, as in this snippet
public function __construct($instance_id = 0)
{
$this->instance_id = absint($instance_id);
// other lines follow
}
there is no stale data: remove transient data and clients' data (WooCommerce Settings > Status > Tools)
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' ) );