I've been endlessly trying to add a shipping method to the following code but with no luck. Could a good samaritan please offer some assistance.
I just need to add a shipping method.
Basically what the code does is add the fields to the shipping zones section in woo commerce setting | Shipping Zones.
I'd like to add a [flat rate to the zone that is created]
I've searched everywhere but with no luck. :-(
<?php
//Creating the Zone.
function DW_shipping_zone_init() {
// Getting the Shipping object
$available_zones = WC_Shipping_Zones::get_zones();
// Get all WC Countries
$all_countries = WC()->countries->get_countries();
//Array to store available names
$available_zones_names = array();
// Add each existing zone name into our array
foreach ($available_zones as $zone ) {
if( !in_array( $zone['zone_name'], $available_zones_names ) ) {
$available_zones_names[] = $zone['zone_name'];
}
}
// Check if our zone 'C' is already there
if( ! in_array( 'C', $available_zones_names ) ){
// Create an empty object
$zone_za = new stdClass();
//Add null attributes
$zone_za->zone_id = null;
$zone_za->zone_order =null;
// Add shipping zone name
$zone_za->zone_name = 'C';
// Instantiate a new shipping zone with our object
$new_zone_za = new WC_Shipping_Zone( $zone_za );
// Add South Africa as location
$new_zone_za->add_location( 'ZA', 'country' );
// Save the zone, if non existent it will create a new zone
$new_zone_za->save();
// Add our shipping method to that zone
$new_zone_za->add_shipping_method( 'some_shipping_method' );
}
add_action( 'init', 'DW_shipping_zone_init' );
You can create plugin for this:
Create one folder inside wp-content/pluigins/ with name "woocommerce-fast-delivery-system"
Inside "woocommerce-fast-delivery-system" folder, create two files as displayed below:
1. fast-delivery-shipping-method.php
<?php
/*
Plugin Name: WooCommerce Fast Delivery Shipping Method
*/
/**
* Check if WooCommerce is active
*/
$active_plugins = apply_filters( 'active_plugins', get_option( 'active_plugins' ) );
if ( in_array( 'woocommerce/woocommerce.php', $active_plugins) ) {
add_filter( 'woocommerce_shipping_methods', 'add_fast_delivery_shipping_method' );
function add_fast_delivery_shipping_method( $methods ) {
$methods[] = 'WC_Fast_Delivery_Shipping_Method';
return $methods;
}
add_action( 'woocommerce_shipping_init', 'fast_delivery_shipping_method_init' );
function fast_delivery_shipping_method_init(){
require_once 'class-fast-delivery-shipping-method.php';
}
}
2. class-fast-delivery-shipping-method.php
<?php
class WC_Fast_Delivery_Shipping_Method extends WC_Shipping_Method{
public function __construct(){
$this->id = 'fast_delivery_shipping_method';
$this->method_title = __( 'Fast Delivery Shipping Method', 'woocommerce' );
// Load the settings.
$this->init_form_fields();
$this->init_settings();
// Define user set variables
$this->enabled = $this->get_option( 'enabled' );
$this->title = $this->get_option( 'title' );
add_action( 'woocommerce_update_options_shipping_' . $this->id, array( $this, 'process_admin_options' ) );
}
public function init_form_fields(){
$this->form_fields = array(
'enabled' => array(
'title' => __( 'Enable/Disable', 'woocommerce' ),
'type' => 'checkbox',
'label' => __( 'Enable Fast Delivery Shipping', 'woocommerce' ),
'default' => 'yes'
),
'title' => array(
'title' => __( 'Method Tittle', 'woocommerce' ),
'type' => 'text',
'description' => __( 'This controls the title which the user sees during checkout.', 'woocommerce' ),
'default' => __( 'Fast Delivery Shipping', 'woocommerce' ),
)
);
}
public function is_available( $package ){
foreach ( $package['contents'] as $item_id => $values ) {
$_product = $values['data'];
$weight = $_product->get_weight();
if($weight > 10){
return false;
}
}
return true;
}
public function calculate_shipping($package){
//get the total weight and dimensions
$weight = 0;
$dimensions = 0;
foreach ( $package['contents'] as $item_id => $values ) {
$_product = $values['data'];
$weight = $weight + $_product->get_weight() * $values['quantity'];
$dimensions = $dimensions + (($_product->length * $values['quantity']) * $_product->width * $_product->height);
}
//calculate the cost according to the table
switch ($weight) {
case ($weight < 1):
switch ($dimensions) {
case ($dimensions <= 1000):
$cost = 3;
break;
case ($dimensions > 1000):
$cost = 4;
break;
}
break;
case ($weight >= 1 && $weight < 3 ):
switch ($dimensions) {
case ($dimensions <= 3000):
$cost = 10;
break;
}
break;
case ($weight >= 3 && $weight < 10):
switch ($dimensions) {
case ($dimensions <= 5000):
$cost = 25;
break;
case ($dimensions > 5000):
$cost = 50;
break;
}
break;
}
// send the final rate to the user.
$c= $this->add_rate( array(
'id' => $this->id,
'label' => $this->title,
'cost' => $cost
));
}
}
Related
I want to create several shipping rates in WooCommerce, based on a different fixed cost per product, which I'm saving as meta values. I'd like to offer a different selection of rates for each of my shipping zones: for example, in the UK I'd like to have UK First and UK Second, each of which has a different cost for each product.
I've created the first of my shipping methods, and I can add it to a shipping zone. But when I try to check out in that zone, the cart says No shipping options were found. Can anyone spot what I'm doing wrong?
I've turned on debug mode and confirmed that I really am checking out in the UK zone.
I've also tried just adding the rate and returning from the top of calculate_shipping:
$this->add_rate(
$rate = array(
'id' => $this->id,
'label' => $this->title,
'cost' => 101,
)
);
if ( in_array( 'woocommerce/woocommerce.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) ) {
function shipping_method_uk_first_init() {
class Shipping_Method_UK_First extends \WC_Shipping_Method {
private string $meta_key = 'shipping_uk_1st';
public function __construct() {
$this->id = 'shipping_method_uk_first';
$this->method_title = __( 'UK First' );
$this->method_description = __( 'Royal Mail first class' );
$this->enabled = 'yes';
$this->title = 'UK First Class';
$this->supports = array(
'shipping-zones',
'instance-settings',
);
$this->init();
}
function init() {
$this->init_form_fields();
$this->init_settings();
add_action(
'woocommerce_update_options_shipping_' . $this->id,
array(
$this,
'process_admin_options',
)
);
}
public function calculate_shipping( $package = array() ) {
$cost = 0;
foreach ( $package['contents'] as $item_id => $values ) {
$product = $values['data'];
$product_shipping_method_cost = $product->get_meta( $this->meta_key );
$cost += floatval( $product_shipping_method_cost );
}
$rate = array(
'id' => $this->id,
'label' => $this->title,
'cost' => $cost,
);
$this->add_rate( $rate );
}
}
}
add_action( 'woocommerce_shipping_init', 'shipping_method_uk_first_init' );
function add_shipping_method_uk_first( $methods ) {
$methods['shipping_method_uk_first'] = 'Shipping_Method_UK_First';
return $methods;
}
add_filter( 'woocommerce_shipping_methods', 'add_shipping_method_uk_first' );
}
There are some mistakes and missing things. I have also added a product custom field to product shipping options. Try the following:
if ( in_array( 'woocommerce/woocommerce.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) ) {
function shipping_method_uk_first_init() {
class Shipping_Method_UK_First extends \WC_Shipping_Method {
public function __construct( $instance_id = 0 ) {
$this->id = 'shipping_method_uk_first';
$this->instance_id = absint( $instance_id );
$this->method_title = __( 'UK First' );
$this->method_description = __( 'Royal Mail first class' );
$this->enabled = 'yes';
$this->meta_key = 'shipping_uk_1st'; // <= HERE define the related product meta key
$this->title = __('UK First Class' );
$this->supports = array(
'shipping-zones',
'instance-settings',
);
$this->init();
}
function init() {
$this->init_form_fields();
$this->init_settings();
add_action( 'woocommerce_update_options_shipping_' . $this->id, array( $this, 'process_admin_options' ) );
}
public function calculate_shipping( $package = array() ) {
$cost = 0; // Initializing
foreach ( $package['contents'] as $item_key => $item ) {
// Get the parent variable product for product variation items
$product = $item['variation_id'] > 0 ? wc_get_product( $item['product_id']) : $item['data'];
$cost += floatval( $product->get_meta( $this->meta_key ) );
}
$rate = array(
'id' => $this->id,
'label' => $this->title,
'cost' => $cost,
);
$this->add_rate( $rate );
}
}
}
add_action( 'woocommerce_shipping_init', 'shipping_method_uk_first_init' );
function add_shipping_method_uk_first( $methods ) {
$methods['shipping_method_uk_first'] = 'Shipping_Method_UK_First';
return $methods;
}
add_filter( 'woocommerce_shipping_methods', 'add_shipping_method_uk_first' );
// Add a custom field to product shipping options
function add_custom_field_product_options_shipping() {
global $product_object;
echo '</div><div class="options_group">'; // New option group
woocommerce_wp_text_input( array(
'id' => 'shipping_uk_1st',
'label' => __( 'UK First shipping cost', 'woocommerce' ),
'placeholder' => '',
'desc_tip' => 'true',
'description' => __( 'Enter the UK First shipping cost value here.', 'woocommerce' ),
'value' => (float) $product_object->get_meta( 'shipping_uk_1st' ),
) );
}
// Save product custom field shipping option value
function save_custom_field_product_options_shipping( $product ) {
if ( isset($_POST['shipping_uk_1st']) ) {
$product->update_meta_data( 'shipping_uk_1st', (float) sanitize_text_field($_POST['shipping_uk_1st']) );
}
}
add_action( 'woocommerce_product_options_shipping', 'add_custom_field_product_options_shipping', 5 );
add_action( 'woocommerce_admin_process_product_object', 'save_custom_field_product_options_shipping' );
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
I am creating a WooCommerce plugin and I want to display dynamically Sub Areas according to chosen customer city in checkout page.
Here is my code attempt:
add_filter( 'woocommerce_checkout_fields', 'dvs_city_list' );
function dvs_city_list( $fields ) {
$fields["billing"]["billing_city"]["type"] = 'select';
$fields["billing"]["billing_city"]["input_class"] = array(
'state_select' => 'state_select'
);
$fields["billing"]["billing_city"]["options"] = array(
'Lahore' => 'Lahore',
'Karachi' => 'Karachi'
),
return $fields;
}
add_filter( 'woocommerce_checkout_fields', 'dvs_area_list' );
function dvs_area_list( $fields ) {
$fields['billing']['billing_area']['label'] = 'Area';
$fields['billing']['billing_area']['required'] = 'True';
$fields["billing"]["billing_area"]["type"] = 'select';
$fields["billing"]["billing_area"]["class"][0] = 'form-row-last';
$fields['billing']['billing_area']['priority'] = 50;
$fields["billing"]["billing_area"]["input_class"] = array(
'state_select' => 'state_select'
);
$city = $_REQUEST['billing_city'];
if ($city == 'Lahore') {
$fields["billing"]["billing_area"]["options"] = array(
'Naval Town' => 'Naval Town',
'Bahria Town' => 'Bahria Town',
'Faisal Town' => 'Faisal Town'
);
}
else ($city == 'Karachi') {
$fields["billing"]["billing_area"]["options"] = array(
'Walton Road' => 'Walton Road',
'Zest Road' => 'Zest Road'
);
}
return $fields;
}
Here is the screenshot
But I am getting this error
Notice:
Undefined index: billing_city in …wp-content/plugins/custom-plugin/index.php on line 35
How to fixed this error? What I am doing wrong?
To synch a custom checkout select field from another select field, it requires to use jQuery.
Also you can merge both functions as they use the same hook.
Below in the first function, we keep your cities / areas settings that we can call everywhere. The last function enable dynamic options changes on the "Billing areas" dropdown depending on the chosen city:
function cities_areas_settings() {
$text_domain = 'woocommerce';
return array(
__('Lahore', $text_domain) => array(
__('Naval Town', $text_domain),
__('Bahria Town', $text_domain),
__('Faisal Town', $text_domain),
),
__('Karachi', $text_domain) => array(
__('Walton Road', $text_domain),
__('Zest Road', $text_domain),
)
);
}
add_filter( 'woocommerce_checkout_fields', 'custom_checkout_fields' );
function custom_checkout_fields( $fields ) {
// Initializing
$text_domain = 'woocommerce';
$option_cities = array();
$lahore_areas = array( '' => __('Choose your area', $text_domain) );
// Load settings and prepare options arrays
foreach( cities_areas_settings() as $city => $areas ) {
$option_cities[$city] = $city;
if( $city === 'Lahore' ) {
foreach( $areas as $area ) {
$lahore_areas[$area] = $area;
}
}
}
// 1. Billing City field
$fields['billing']['billing_city']['type'] = 'select';
$fields['billing']['billing_city']['class'] = array('form-row-first');
$fields['billing']['billing_city']['input_class'] = array('state_select');
$fields['billing']['billing_city']['options'] = $option_cities;
// 2. Billing Area Field
$fields['billing']['billing_area'] = array(
'type' => 'select',
'label' => __('Area', $text_domain),
'class' => array('form-row-last'),
'input_class' => array('state_select'),
'options' => $lahore_areas,
'required' => true,
'default' => '',
'priority' => 50,
);
return $fields;
}
add_action('wp_footer', 'custom_checkout_js_script');
function custom_checkout_js_script() {
if( is_checkout() && ! is_wc_endpoint_url() ) :
// Initializing
$text_domain = 'woocommerce';
$karachi_areas = array( '' => __('Choose your area', $text_domain) );
$settings = cities_areas_settings(); // Load settings
// Prepare 'Karachi' options dropdown
foreach( cities_areas_settings()['Karachi'] as $area ) {
$karachi_areas[$area] = $area;
}
?>
<script language="javascript">
jQuery( function($){
var a = 'select[name="billing_city"]',
b = 'select[name="billing_area"]',
o = <?php echo json_encode($karachi_areas); ?>,
s = $(b).html();
// Utility function to fill dynamically the select field options
function dynamicSelectOptions( opt ){
var options = '';
$.each( opt, function( key, value ){
options += '<option value="'+key+'">'+value+'</option>';
});
$(b).html(options);
}
// On Start (once DOM is loaded)
if ( $(a).val() === 'Karachi' ) {
dynamicSelectOptions( o );
}
console.log($(a).val());
// On billing city change live event
$('form.woocommerce-checkout').on('change', a, function() {
console.log($(this).val());
if ( $(this).val() === 'Karachi' ) {
dynamicSelectOptions( o );
} else {
$(b).html(s);
}
});
});
</script>
<?php
endif;
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.
Related: Dynamic synched custom checkout select fields in WooCommerce
I have the following sum
$item_shipping_cost += (float) $this->get_fee( $this->fee, $item_shipping_cost ) * (int) $product_data['quantity'];
This adds up like the this:
Product Price + Item Shipping Cost + Fee * Qty
So if the product price is set at £1.00 and Item Shipping is at £1.00 and the fee is £0.50 and I want 2 items the cost will be a total of £4.00. However, I would like to subtract the value of 'fee' as I only want the fee to be charge on addition products. How would I do this?
This line of code belongs to a WooCommerce Shipping Per Product plugin. The full code is:
class WC_Shipping_Per_Product extends WC_Shipping_Method {
const METHOD_ID = 'per_product';
/**
* Default product shipping cost.
*
* #var int
*/
private $cost;
/**
* Handling fee applied to entire order.
*
* #var int
*/
private $order_fee;
/**
* Constructor.
*
* #param int $instance_id Instance Id for method in zone config.
*/
public function __construct( $instance_id = 0 ) {
parent::__construct( $instance_id );
$this->id = self::METHOD_ID;
$this->method_title = __( 'Per-product', 'woocommerce-shipping-per-product' );
$this->method_description = __( 'Per product shipping allows you to define different shipping costs for products, based on customer location. These costs will be displayed and charged separately from any other shipping methods.', 'woocommerce-shipping-per-product' );
$this->supports = array(
'shipping-zones',
'instance-settings',
'instance-settings-modal',
);
// Load the form fields.
$this->init_form_fields();
// Define user set variables.
$this->title = $this->get_option( 'title' );
$this->tax_status = $this->get_option( 'tax_status' );
$this->cost = $this->get_option( 'cost' );
$this->fee = $this->get_option( 'fee' );
$this->order_fee = $this->get_option( 'order_fee' );
// Actions.
add_action( 'woocommerce_update_options_shipping_' . $this->id, array( $this, 'process_admin_options' ) );
}
/**
* Initialise Gateway Settings Form Fields.
*/
public function init_form_fields() {
$this->instance_form_fields = array(
'title' => array(
'title' => __( 'Method Title', 'woocommerce-shipping-per-product' ),
'type' => 'text',
'description' => __( 'This controls the title which the user sees during checkout.', 'woocommerce-shipping-per-product' ),
'default' => __( 'Product Shipping', 'woocommerce-shipping-per-product' ),
'desc_tip' => true,
),
'tax_status' => array(
'title' => __( 'Tax Status', 'woocommerce-shipping-per-product' ),
'type' => 'select',
'description' => '',
'default' => 'taxable',
'options' => array(
'taxable' => __( 'Taxable', 'woocommerce-shipping-per-product' ),
'none' => __( 'None', 'woocommerce-shipping-per-product' ),
),
),
'cost' => array(
'title' => __( 'Default Product Cost', 'woocommerce-shipping-per-product' ),
'type' => 'text',
'description' => __( 'Cost excluding tax (per product) for products without defined costs. Enter an amount, e.g. 2.50. Entering an amount here will apply a global shipping cost for all products, effectively disabling all other shipping methods', 'woocommerce-shipping-per-product' ),
'default' => '',
'placeholder' => __( 'Disabled, Enter an amount, e.g. 2.50.', 'woocommerce-shipping-per-product' ),
'desc_tip' => true,
),
'fee' => array(
'title' => __( 'Handling Fee (per product)', 'woocommerce-shipping-per-product' ),
'type' => 'text',
'description' => __( 'Fee excluding tax. Enter an amount, e.g. 2.50, or a percentage, e.g. 5%. Leave blank to disable.', 'woocommerce-shipping-per-product' ),
'default' => '',
'placeholder' => __( 'Disabled, Enter an amount, e.g. 2.50, or a percentage, e.g. 5%.', 'woocommerce-shipping-per-product' ),
'desc_tip' => true,
),
'order_fee' => array(
'title' => __( 'Handling Fee (per order)', 'woocommerce-shipping-per-product' ),
'type' => 'text',
'description' => __( 'Fee excluding tax. Enter an amount, e.g. 2.50, or a percentage, e.g. 5%. Leave blank to disable.', 'woocommerce-shipping-per-product' ),
'default' => '',
'placeholder' => __( 'Disabled, Enter an amount, e.g. 2.50, or a percentage, e.g. 5%.', 'woocommerce-shipping-per-product' ),
'desc_tip' => true,
),
);
}
/**
* Check is per product shipping is enabled for the product.
*
* #param array $product_data The product data form the package array.
* #param array $package Shipping package array.
*
* #return bool
*/
public function is_per_product_shipping_product( array $product_data, array $package ) {
if ( $product_data['quantity'] > 0 ) {
if ( $product_data['data']->needs_shipping() ) {
if ( false !== $this->calculate_product_shipping_cost( $product_data, $package ) ) {
return true;
}
}
}
return false;
}
/**
* Calculate the per product shipping cost if enabled for the product.
*
* #param array $product_data The product data form the package array.
* #param array $package Shipping package array.
*
* #return float|bool
*/
private function calculate_product_shipping_cost( array $product_data, array $package ) {
$rule = false;
$item_shipping_cost = 0;
if ( $product_data['variation_id'] ) {
$rule = woocommerce_per_product_shipping_get_matching_rule( $product_data['variation_id'], $package );
}
if ( false === $rule ) {
$rule = woocommerce_per_product_shipping_get_matching_rule( $product_data['product_id'], $package );
}
if ( $rule ) {
$item_shipping_cost += (float) $rule->rule_item_cost * (int) $product_data['quantity'];
$item_shipping_cost += (float) $rule->rule_cost;
} elseif ( '0' === $this->cost || $this->cost > 0 ) {
// Use default shipping cost.
$item_shipping_cost += (float) $this->cost * (int) $product_data['quantity'];
} else {
// NO default and nothing found - abort.
return false;
}
// Fee.
$item_shipping_cost += (float) $this->get_fee( $this->fee, $item_shipping_cost ) * (int) $product_data['quantity'];
return $item_shipping_cost;
}
/**
* Calculate shipping when this method is used standalone.
*
* #param array $package information.
*/
public function calculate_shipping( $package = array() ) {
$_tax = new WC_Tax();
$taxes = array();
$shipping_cost = 0;
if ( empty( $package['ship_via'] ) || ! in_array( $this->id, $package['ship_via'], true ) ) {
return; // must be a package mark as per product shipping in split_shipping_packages_per_product.
}
// This shipping method loops through products, adding up the cost.
if ( count( $package['contents'] ) > 0 ) {
foreach ( $package['contents'] as $item_id => $values ) {
if ( $values['quantity'] > 0 ) {
if ( $values['data']->needs_shipping() ) {
$item_shipping_cost = $this->calculate_product_shipping_cost( $values, $package );
$shipping_cost += $item_shipping_cost;
if ( 'yes' === get_option( 'woocommerce_calc_taxes' ) && 'taxable' === $this->tax_status ) {
$rates = $_tax->get_shipping_tax_rates( $values['data']->get_tax_class() );
$item_taxes = $_tax->calc_shipping_tax( $item_shipping_cost, $rates );
// Sum the item taxes.
foreach ( array_keys( $taxes + $item_taxes ) as $key ) {
$taxes[ $key ] = ( isset( $item_taxes[ $key ] ) ? $item_taxes[ $key ] : 0 ) + ( isset( $taxes[ $key ] ) ? $taxes[ $key ] : 0 );
}
}
}
}
}
}
// Add order shipping cost + tax.
if ( $this->order_fee ) {
$order_fee = (float) $this->get_fee( $this->order_fee, $shipping_cost );
$shipping_cost += $order_fee;
if ( 'yes' === get_option( 'woocommerce_calc_taxes' ) && 'taxable' === $this->tax_status ) {
$rates = $_tax->get_shipping_tax_rates();
$item_taxes = $_tax->calc_shipping_tax( $order_fee, $rates );
// Sum the item taxes.
foreach ( array_keys( $taxes + $item_taxes ) as $key ) {
$taxes[ $key ] = ( isset( $item_taxes[ $key ] ) ? $item_taxes[ $key ] : 0 ) + ( isset( $taxes[ $key ] ) ? $taxes[ $key ] : 0 );
}
}
}
// Add rate.
$this->add_rate(
array(
'id' => $this->id,
'label' => $this->title,
'cost' => $shipping_cost,
'taxes' => $taxes, // We calc tax in the method.
)
);
}
}
I suppose you mean:
$item_shipping_cost -= ($product_data['quantity'] - 1) * $this->fee;
This will remove fees from all products except one.
Add my line of code after your line:
$item_shipping_cost += (float) $this->get_fee( $this->fee, $item_shipping_cost ) * (int) $product_data['quantity'];
When there is only 1 product, then no fee will be subtracted.
I've built my first shipping method plugin based on flat rate with a few extra fields.
I have done the following:
1. Installed and activated the plugin
2. Added 2 instances of the shipping method to the UK zone
I can see in the top sub menu in the shipping section there appears to be some kind of "default" instance of the shipping plugin in a menu labelled "UK Flat Rate"
I was wondering if there's a way to remove this and ONLY have the plugin work in the shipping zones section.
The reason I ask is that then in checkout if I enter a UK address I see the 2 UK methods defined and then underneath them both there is also a radio button for UK Flat Rate which I'm trying to get rid of. It shows the default values based on the values entered in the sub-section.
if (!defined('ABSPATH')) {
exit;
}
if (in_array('woocommerce/woocommerce.php', apply_filters('active_plugins', get_option('active_plugins')))) {
add_action( 'woocommerce_shipping_init', 'uk_shipping_method');
function uk_shipping_method() {
if (!class_exists('UK_WC_Shipping_Flat_Rate')) {
class UK_WC_Shipping_Flat_Rate extends WC_Shipping_Method {
/** #var string cost passed to [fee] shortcode */
protected $fee_cost = '';
/**
* Constructor.
*
* #param int $instance_id
*/
public function __construct($instance_id = 1) {
$this->id = 'uk_flat_rate';
$this->instance_id = absint($instance_id);
$this->enabled = "yes"; // This can be added as an setting but for this example its forced enabled
$this->method_title = __('UK Flat Rate');
$this->title = 'UK Flat Rate';
$this->method_description = __('Lets you charge a fixed rate for shipping but flags for UK Status Update.');
$this->supports = array(
'shipping-zones',
'instance-settings',
'instance-settings-modal',
);
$this->init();
add_action('woocommerce_update_options_shipping_' . $this->id, array( $this, 'process_admin_options'));
}
/**
* init user set variables.
*/
public function init() {
$this->instance_form_fields = include('includes/settings-flat-rate.php');
$this->title = $this->get_option( 'title' );
$this->tax_status = $this->get_option( 'tax_status' );
$this->cost = $this->get_option( 'cost' );
$this->type = $this->get_option( 'type', 'class' );
}
/**
* Evaluate a cost from a sum/string.
* #param string $sum
* #param array $args
* #return string
*/
protected function evaluate_cost( $sum, $args = array() ) {
include_once( WC()->plugin_path() . '/includes/libraries/class-wc-eval-math.php' );
// Allow 3rd parties to process shipping cost arguments
$args = apply_filters( 'woocommerce_evaluate_shipping_cost_args', $args, $sum, $this );
$locale = localeconv();
$decimals = array( wc_get_price_decimal_separator(), $locale['decimal_point'], $locale['mon_decimal_point'], ',' );
$this->fee_cost = $args['cost'];
// Expand shortcodes
add_shortcode( 'fee', array( $this, 'fee' ) );
$sum = do_shortcode( str_replace(
array(
'[qty]',
'[cost]',
),
array(
$args['qty'],
$args['cost'],
),
$sum
) );
remove_shortcode( 'fee', array( $this, 'fee' ) );
// Remove whitespace from string
$sum = preg_replace( '/\s+/', '', $sum );
// Remove locale from string
$sum = str_replace( $decimals, '.', $sum );
// Trim invalid start/end characters
$sum = rtrim( ltrim( $sum, "\t\n\r\0\x0B+*/" ), "\t\n\r\0\x0B+-*/" );
// Do the math
return $sum ? WC_Eval_Math::evaluate( $sum ) : 0;
}
/**
* Work out fee (shortcode).
* #param array $atts
* #return string
*/
public function fee( $atts ) {
$atts = shortcode_atts( array(
'percent' => '',
'min_fee' => '',
'max_fee' => '',
), $atts, 'fee' );
$calculated_fee = 0;
if ( $atts['percent'] ) {
$calculated_fee = $this->fee_cost * ( floatval( $atts['percent'] ) / 100 );
}
if ( $atts['min_fee'] && $calculated_fee < $atts['min_fee'] ) {
$calculated_fee = $atts['min_fee'];
}
if ( $atts['max_fee'] && $calculated_fee > $atts['max_fee'] ) {
$calculated_fee = $atts['max_fee'];
}
return $calculated_fee;
}
/**
* calculate_shipping function.
*
* #param array $package (default: array())
*/
public function calculate_shipping( $package = array() ) {
$rate = array(
'id' => $this->get_rate_id(),
'label' => $this->title,
'cost' => 0,
'package' => $package,
);
// Calculate the costs
$has_costs = false; // True when a cost is set. False if all costs are blank strings.
$cost = $this->get_option('cost');
if ( '' !== $cost ) {
$has_costs = true;
$rate['cost'] = $this->evaluate_cost( $cost, array(
'qty' => $this->get_package_item_qty( $package ),
'cost' => $package['contents_cost'],
) );
}
// Add shipping class costs.
$shipping_classes = WC()->shipping->get_shipping_classes();
if ( ! empty( $shipping_classes ) ) {
$found_shipping_classes = $this->find_shipping_classes( $package );
$highest_class_cost = 0;
foreach ( $found_shipping_classes as $shipping_class => $products ) {
// Also handles BW compatibility when slugs were used instead of ids
$shipping_class_term = get_term_by( 'slug', $shipping_class, 'product_shipping_class' );
$class_cost_string = $shipping_class_term && $shipping_class_term->term_id ? $this->get_option( 'class_cost_' . $shipping_class_term->term_id, $this->get_option( 'class_cost_' . $shipping_class, '' ) ) : $this->get_option( 'no_class_cost', '' );
if ( '' === $class_cost_string ) {
continue;
}
$has_costs = true;
$class_cost = $this->evaluate_cost( $class_cost_string, array(
'qty' => array_sum( wp_list_pluck( $products, 'quantity' ) ),
'cost' => array_sum( wp_list_pluck( $products, 'line_total' ) ),
) );
if ( 'class' === $this->type ) {
$rate['cost'] += $class_cost;
} else {
$highest_class_cost = $class_cost > $highest_class_cost ? $class_cost : $highest_class_cost;
}
}
if ( 'order' === $this->type && $highest_class_cost ) {
$rate['cost'] += $highest_class_cost;
}
}
// Add the rate
if ( $has_costs ) {
$this->add_rate( $rate );
}
/**
* Developers can add additional flat rates based on this one via this action since #version 2.4.
*
* Previously there were (overly complex) options to add additional rates however this was not user.
* friendly and goes against what Flat Rate Shipping was originally intended for.
*
* This example shows how you can add an extra rate based on this flat rate via custom function:
*
* add_action( 'woocommerce_flat_rate_shipping_add_rate', 'add_another_custom_flat_rate', 10, 2 );
*
* function add_another_custom_flat_rate( $method, $rate ) {
* $new_rate = $rate;
* $new_rate['id'] .= ':' . 'custom_rate_name'; // Append a custom ID.
* $new_rate['label'] = 'Rushed Shipping'; // Rename to 'Rushed Shipping'.
* $new_rate['cost'] += 2; // Add $2 to the cost.
*
* // Add it to WC.
* $method->add_rate( $new_rate );
* }.
*/
do_action( 'woocommerce_' . $this->id . '_shipping_add_rate', $this, $rate );
}
/**
* Get items in package.
* #param array $package
* #return int
*/
public function get_package_item_qty( $package ) {
$total_quantity = 0;
foreach ( $package['contents'] as $item_id => $values ) {
if ( $values['quantity'] > 0 && $values['data']->needs_shipping() ) {
$total_quantity += $values['quantity'];
}
}
return $total_quantity;
}
/**
* Finds and returns shipping classes and the products with said class.
* #param mixed $package
* #return array
*/
public function find_shipping_classes( $package ) {
$found_shipping_classes = array();
foreach ( $package['contents'] as $item_id => $values ) {
if ( $values['data']->needs_shipping() ) {
$found_class = $values['data']->get_shipping_class();
if ( ! isset( $found_shipping_classes[ $found_class ] ) ) {
$found_shipping_classes[ $found_class ] = array();
}
$found_shipping_classes[ $found_class ][ $item_id ] = $values;
}
}
return $found_shipping_classes;
}
}
}
function add_uk_shipping_method( $methods ) {
$methods['uk_flat_rate'] = 'UK_WC_Shipping_Flat_Rate';
return $methods;
}
add_filter( 'woocommerce_shipping_methods', 'add_uk_shipping_method' );
}
}
Is there a setting in the plugin I'm missing to enforce the method is only zones based?
Turns out I needed to add filters to remove the section and the first instance of the method from shipping like so
woocommerce_shipping_option_remove( $section ) {
unset($section['uk_flat_rate']);
return $section;
}
add_filter( 'woocommerce_get_sections_shipping', 'woocommerce_shipping_option_remove' ,1 );
function woocommerce_shipping_remove_method( $rates )
{
unset($rates['uk_flat_rate:1']);
return $rates;
}
add_filter('woocommerce_package_rates','woocommerce_shipping_remove_method', 100 );
}
}
I create a custom shipping method.
(https://docs.woocommerce.com/document/shipping-method-api/)
In the calculate_shipping function i want to set the price of my shipping method based on the shipping zone:
If shipping zone is 'Zone 1' set price to 15
else set price to 20
So i want to know, how can i get the current shipping zone ?
I already try to see this doc: https://docs.woocommerce.com/wc-apidocs/class-WC_Shipping_Zone.html
Maybe i don't understand...
public function calculate_shipping( $package=Array()) {
global $woocommerce;
$cart_total=$woocommerce->cart->cart_contents_total;
$current_hour=date('His');
$start_hour='110000';
$end_hour='210000';
// CONDITIONAL CHECK
// OPEN HOUR
if($current_hour >= $start_hour && $current_hour <= $end_hour){
$is_open=true;
}else{
$is_open=false;
}
// price PER ZONE
$zone=""; // need to know how to get zone name or zone id
if($zone=='Zone 1' && $cart_total>='60'){
$min_order=true;
$cost = 6;
}elseif($zone=='Zone 2' && $cart_total>='120'){
$min_order=true;
$cost = 8;
}elseif($zone=='Zone 3' && $cart_total>='180'){
$min_order=true;
$cost = 9;
}else{
$min_order=false;
$cost = 0;
}
if($is_open==true && $min_order==true){
$allowed=true;
}else{
$allowed=false;
}
$rate = array(
'id' => $this->id,
'label' => 'Livraison Express T '.wc_get_shipping_zone().' T',
'cost' => $cost,
);
// Register the rate
if($allowed==true){
$this->add_rate( $rate );
}
}
I find a solution myself
Like that:
$shipping_zone = WC_Shipping_Zones::get_zone_matching_package( $package );
$zone=$shipping_zone->get_zone_name();
Complete function:
public function calculate_shipping( $package=Array()) {
global $woocommerce;
$shipping_zone = WC_Shipping_Zones::get_zone_matching_package( $package );
$zone=$shipping_zone->get_zone_name();
if($zone=='Zone 1'){
$cost = 6;
}else{
$cost=12;
}
$rate = array(
'id' => $this->id,
'label' => 'Delivery Name',
'cost' => $cost,
);
$this->add_rate( $rate );
}