How to add a custom working Shipping Method in WooCommerce 3 - php

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)

Related

Is there a way to get shipping method info from order in Woocommerce?

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?

Change specific payment gateway title in WooCommerce

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.

woocommerce custom shipping method not appearing in shipping zone

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';)

woocommerce shipping method with 3.0 api

Hi there, I'm trying to build a very simple plugin for woocommerce to show some information about delivery without taxes. The diference for the other methods is the extra HTML field.
I read a lot of the documentation but I think there is something miss on the configuration. The admin it seems working. But the method didn't appear on checkout screen. The code is below:
<?php
if ( ! defined( 'WPINC' ) ) {
die('security by preventing any direct access to your plugin file');
}
if (in_array('woocommerce/woocommerce.php', apply_filters('active_plugins', get_option('active_plugins')))) {
function shipping_delivery_info() {
if (!class_exists('shipping_delivery_info')) {
class shipping_delivery_info extends WC_Shipping_Method {
public function __construct( $instance_id = 0) {
$this->id = 'shipping_delivery_info';
$this->instance_id = absint( $instance_id );
$this->method_title = __('Shipping Delivery Info', 'shipping_delivery_info');
$this->method_description = __('A Woocommerce custom shipping method plugin, that shows ' .
'some shipping information to costumer, like free shipping but with HTML field.',
'shipping_delivery_info');
$this->supports = array(
'shipping-zones',
'instance-settings',
'instance-settings-modal',
);
$this->init();
}
/**
* Load the settings API
*/
function init() {
// Load the settings
$this->init_form_fields();
$this->init_settings();
$this->enabled = $this->get_option( 'enabled' );
$this->title = $this->get_option( 'title' );
$this->info = $this->get_option( 'info' );
add_action('woocommerce_update_options_shipping_' . $this->id, array($this, 'process_admin_options'));
}
function init_form_fields() {
$this->instance_form_fields = array(
'enabled' => array(
'title' => __( 'Enable/Disable', 'shipping_delivery_info'),
'type' => 'checkbox',
'label' => __( 'Enable this shipping method', 'shipping_delivery_info'),
'default' => 'yes',
),
'title' => array(
'title' => __('Title', 'shipping_delivery_info'),
'type' => 'text',
'description' => __( 'The title to be displayed during checkout.', 'shipping_delivery_info' ),
'default' => __('Shipping Information', 'shipping_delivery_info'),
),
'info' => array(
'title' => __('Information', 'shipping_delivery_info'),
'type' => 'text',
'description' => __( 'Information about delivery and its taxes.', 'shipping_delivery_info' ),
'default' => __('Insert here some HTML.'),
),
);
}
}
}
}
add_action('woocommerce_shipping_init', 'shipping_delivery_info');
function add_shipping_delivery_info($methods)
{
$methods['shipping_delivery_info'] = 'shipping_delivery_info';
return $methods;
}
add_filter('woocommerce_shipping_methods', 'add_shipping_delivery_info');
function shipping_delivery_info_message($posted)
{
$packages = WC()->shipping->get_packages();
$chosen_methods = WC()->session->get('chosen_shipping_methods');
if (is_array($chosen_methods) && in_array('shipping_delivery_info', $chosen_methods)) {
foreach ($packages as $i => $package) {
if ($chosen_methods[$i] != "shipping_delivery_info") {
continue;
}
$shipping_delivery_info = new shipping_delivery_info();
$message = $shipping_delivery_info->settings['info'];
return $message;
/*$messageType = "info";
wc_add_notice($message, $messageType);*/
}
}
}
add_action('woocommerce_review_order_before_cart_contents', 'shipping_delivery_info_message', 10);
add_action('woocommerce_after_checkout_validation', 'shipping_delivery_info_message', 10);
}
You need to add the calculate_shipping function to your class
/**
* function calculate_shipping.
*
* #access public
* #param mixed $package
* #return void
*/
public function calculate_shipping( $package = array() ) {
$rate = array(
'id' => 'My Method id',
'label' => 'New method',
'cost' => 0,
'calc_tax' => 'per_item'
);
$this->add_rate( $rate );
}
}
Anyway, i think you don't need to add a new shipping method just to show some info at the cart, instead you can use these actions:
add_action( 'woocommerce_review_order_before_submit', 'add_tracking_notification', 12 );
add_action('woocommerce_proceed_to_checkout', 'add_tracking_notification');
function add_tracking_notification() {
echo '<h5 style="margin-bottom:10px">This is a custom message</h5>';
}

Adding a custom woocommerce email based on the product attribute

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…

Categories