I need to hide WooCommerce shipping zone_id=1 & shipping zone_id=3 on Sundays.
I've stumbled upon this answer, and come up with this code:
// Hide $15 & $25 shipping rates on Sunday
add_filter('woocommerce_package_rates', 'amano_hide_shipping_method_on_sunday', 10, 2);
function amano_hide_shipping_method_on_sunday($available_shipping_methods, $package) {
$date = date("1");
$is_sunday = date('l', $date) == 'Sunday';
if($is_sunday) {
$hide_when_shipping_class_exist = array(
42 => array(
'free_shipping'
)
);
$hide_when_shipping_class_not_exist = array(
42 => array(
'wf_shipping_ups:03',
'wf_shipping_ups:02',
'wf_shipping_ups:01'
),
43 => array(
'free_shipping'
)
);
$shipping_class_in_cart = array();
foreach(WC()->cart->cart_contents as $key => $values) {
$shipping_class_in_cart[] = $values['data']->get_shipping_class_id();
}
foreach($hide_when_shipping_class_exist as $class_id => $methods) {
if(in_array($class_id, $shipping_class_in_cart)){
foreach($methods as & $current_method) {
unset($available_shipping_methods[$current_method]);
}
}
}
foreach($hide_when_shipping_class_not_exist as $class_id => $methods) {
if(!in_array($class_id, $shipping_class_in_cart)){
foreach($methods as & $current_method) {
unset($available_shipping_methods[$current_method]);
}
}
}
return $available_shipping_methods;
}
}
However, the bit I don't know how to customize is:
$hide_when_shipping_class_exist = array(
42 => array(
'free_shipping'
)
);
$hide_when_shipping_class_not_exist = array(
42 => array(
'wf_shipping_ups:03',
'wf_shipping_ups:02',
'wf_shipping_ups:01'
),
43 => array(
'free_shipping'
)
);
I don’t have any Shipping Classes, and don’t understand what they are. More code will probably need to be replaced to substitute hiding shipping classes for shipping zones, but I don't know what that code is.
Help appreciated please.
Code contains
On a specific day
Unset $rates[$rate_key] for certain zone IDs
Comment with explanation added to the code
function filter_woocommerce_package_rates( $rates, $package ) {
/* SETTINGS */
// Zone ID's
$hide_zones = array ( 1, 3 );
$on_day = 'Sunday';
/* END SETTINGS */
// Shipping zone
$shipping_zone = wc_get_shipping_zone( $package );
// Get zone ID
$zone_id = $shipping_zone->get_id();
// set the default timezone to use. Available since PHP 5.1
date_default_timezone_set('UTC');
// l - A full textual representation of the day of the week
$day_of_the_week = date( 'l' );
// True
if( $day_of_the_week == $on_day ) {
// In array
if ( in_array( $zone_id, $hide_zones ) ) {
// Loop
foreach ( $rates as $rate_key => $rate ) {
// Unset
unset( $rates[$rate_key] );
}
}
}
return $rates;
}
add_filter( 'woocommerce_package_rates', 'filter_woocommerce_package_rates', 10, 2 );
Related
I am trying to enable free shipping for specific shipping zones (array) on selected days.
I'm using therefore this code:
add_filter( 'woocommerce_shipping_free_shipping_is_available', 'enable_free_shipping_on_selected_days', 10, 3 );
function enable_free_shipping_on_selected_days( $is_available, $package, $shipping_method ) {
$shipping_zones = array('US, Europe');
if ( array_key_exists( $shipping_zones, $rates ) && in_array( date( 'w' ), [ 6, 7 ] ) ) {
return true;
}
return $is_available;
}
But I am getting an error: "Uncaught TypeError: array_key_exists(): Argument #2 ($array) must be of type array, null given in.."
Any advice on how to make free shipping available for certain shipping zones on specific days of the week?
$rates is not set in your code, hence the error message
To make free shipping available based on shipping zones and specific days of the week, you can use:
function filter_woocommerce_shipping_free_shipping_is_available( $available, $package, $shipping_method ) {
// The targeted shipping zones. Multiple can be entered, separated by a comma
$shipping_zones = array( 'US', 'Europe', 'België' );
// The targeted day numbers. 0 = Sunday, 1 = Monday.. to 6 for Saturday. Multiple can be entered, separated by a comma
$day_numbers = array( 0, 1, 6 );
// Message
$notice = __( 'free shipping available', 'woocommerce' );
/* END settings */
// Default
$available = false;
// Get shipping zone
$shipping_zone = wc_get_shipping_zone( $package );
// Get the zone name
$zone_name = $shipping_zone->get_zone_name();
// Checks if a value exists in an array (zone name)
if ( in_array( $zone_name, $shipping_zones ) ) {
// Set the default timezone to use.
date_default_timezone_set( 'UTC' );
// Day number
$day_number = date( 'w' );
// Checks if a value exists in an array (day number)
if ( in_array( $day_number, $day_numbers ) ) {
// Clear all other notices
wc_clear_notices();
// Display notice
wc_add_notice( $notice, 'notice' );
// True
$available = true;
}
}
// Return
return $available;
}
add_filter( 'woocommerce_shipping_free_shipping_is_available', 'filter_woocommerce_shipping_free_shipping_is_available', 10, 3 );
// Optional: Hide other shipping methods when free shipping is available
function filter_woocommerce_package_rates( $rates, $package ) {
// Empty array
$free = array();
// Loop trough
foreach ( $rates as $rate_id => $rate ) {
if ( $rate->method_id === 'free_shipping' ) {
$free[ $rate_id ] = $rate;
// Break loop
break;
}
}
return ! empty( $free ) ? $free : $rates;
}
add_filter( 'woocommerce_package_rates', 'filter_woocommerce_package_rates', 10, 2 );
Based on:
Make free shipping available on specific days of the week in WooCommerce
Enable free shipping for products on sale in WooCommerce
How to restrict WooCommerce shipping zone from certain days of the week
I have several products in the store, however one product (flammable) can only be shipped via a specific shipping company; Unfortunately that company can't reach the whole country. So in case the customer buys the flammable product and it's outside the coverage of the only company that can ship the product, he should not see any other shipping option except local pickup.
So far I have this code (courtesy of different StackOverFlow answers):
function filter_woocommerce_package_rates( $rates, $package ) {
// Shipping zone
//echo 'entrando';
$shipping_zone = wc_get_shipping_zone( $package );
$product_ids = array( 2267 ); // HERE set the product IDs in the array
$method_id = 'weight_based_shipping:38'; // HERE set the shipping method ID that I want to hide
$found = false;
// Get zone ID
$zone_id = $shipping_zone->get_id();
//echo $shipping_zone;
//echo $zone_id;
// NOT equal
if ( $zone_id != 8 ) {
// Unset a single rate/method for a specific product
foreach( $package['contents'] as $cart_item ) {
if ( in_array( $cart_item['product_id'], $product_ids ) ){
$found = true;
break;
}
}
if ( $found )
unset( $rates[$method_id] );
}
return $rates;
}
add_filter( 'woocommerce_package_rates', 'filter_woocommerce_package_rates', 10, 2 );
however I don't know why is not working. Even the 'echo' is not working.
Updated - Try following instead (code is commented):
// SETTINGS BELOW: Custom function that handle your settings
function custom_shipping_settings() {
return array(
'product_ids' => array(2267), // Define the products that need to be in a separated shipping package (local pickup)
'allowed_zones_ids' => array(8), // Define the allowed zones IDs
);
}
// Splitting cart items into 2 shipping packages
add_filter( 'woocommerce_cart_shipping_packages', 'split_shipping_packages' );
function split_shipping_packages( $packages ) {
extract(custom_shipping_settings()); // Load and extract settings
$customer = WC()->customer;
$destination = array(
'country' => $customer->get_shipping_country(),
'state' => $customer->get_shipping_state(),
'postcode' => $customer->get_shipping_postcode(),
'city' => $customer->get_shipping_city(),
'address' => $customer->get_shipping_address(),
'address_2' => $customer->get_shipping_address_2()
);
$package_dest = array( 'destination' => $destination );
$zone = wc_get_shipping_zone( $package_dest );
if ( ! in_array( $zone->get_id(), $allowed_zones_ids ) ) {
// Reset packages and initialize variables
$packages = $splitted_cart_items = array();
// Loop through cart items
foreach ( WC()->cart->get_cart() as $item_key => $item ) {
if ( is_a($item['data'], 'WC_Product') && $item['data']->needs_shipping() ) {
if ( ! array_intersect( array($item['product_id'], $item['variation_id']), $product_ids ) ) {
$splitted_cart_items[0][$item_key] = $item; // Regular items
} else {
$splitted_cart_items[1][$item_key] = $item; // Special separated items
}
}
}
if ( count($splitted_cart_items) < 2 )
return $packages;
// Loop through spitted cart items
foreach ( $splitted_cart_items as $key => $items ) {
// Set each cart items group in a shipping package
$packages[$key] = array(
'contents' => $items,
'contents_cost' => array_sum( wp_list_pluck( $items, 'line_total' ) ),
'applied_coupons' => WC()->cart->get_applied_coupons(),
'user' => array( 'ID' => get_current_user_id() ),
'destination' => $destination,
);
}
}
return $packages;
}
// Force local pickup for specific splitted shipping package (for the defined products)
add_filter( 'woocommerce_package_rates', 'filter_wc_package_rates', 10, 2 );
function filter_wc_package_rates( $rates, $package ) {
extract(custom_shipping_settings()); // Load and extract settings
$zone = wc_get_shipping_zone( $package ); // Get current shipping zone
$found = false;
// For all others shipping zones than allowed zones
if ( ! in_array( $zone->get_id(), $allowed_zones_ids ) ) {
// Loop through cart items for current shipping package
foreach( $package['contents'] as $item ) {
// Look for defined specific product Ids
if ( array_intersect( array($item['product_id'], $item['variation_id']), $product_ids ) ) {
$found = true; // Flag as found
break; // Stop the loop
}
}
// If any defined product is in cart
if ( $found ) {
// Loop through shipping rates
foreach ( $rates as $rate_key => $rate ) {
// Hide all shipping methods keeping "Local pickup"
if ( 'local_pickup' !== $rate->method_id ) {
unset( $rates[$rate_key] );
}
}
}
}
return $rates;
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.
Refresh the shipping caches:
This code is already saved on your functions.php file.
In a shipping zone settings, disable / save any shipping method, then enable back / save.
You are done and you can test it.
I need to sort fees by name rather than the default by price in WooCommerce orders.
Using Reordering multiple fees differently in Woocommerce cart and checkout pages answer code, I have managed to sort fees by name in cart and checkout pages. But it doesn't work for the order confirmation and emails that get sent out.
Thank you for your help.
Updated
To sort fees by name on customer orders and email notifications, you will use the following:
// Custom function that sort displayed fees by name on order items
function wc_get_sorted_order_item_totals_fee_rows( &$total_rows, $tax_display, $order ) {
$fees = $order->get_fees();
if ( $fees ) {
$fee_names = []; // initializing
// First Loop
foreach ( $fees as $fee_id => $fee ) {
$fee_names[$fee_id] = $fee->get_name();
}
asort($fee_names); // Sorting by name
// 2nd Loop
foreach ( $fee_names as $fee_id => $fee_name ) {
$fee = $fees[$fee_id];
if ( apply_filters( 'woocommerce_get_order_item_totals_excl_free_fees', empty( $fee['line_total'] ) && empty( $fee['line_tax'] ), $fee_id ) ) {
continue;
}
$total_rows[ 'fee_' . $fee->get_id() ] = array(
'label' => $fee->get_name() . ':',
'value' => wc_price( 'excl' === $tax_display ? $fee->get_total() : $fee->get_total() + $fee->get_total_tax(), array( 'currency' => $order->get_currency() ) ),
);
}
}
}
// Display sorted fees by name everywhere on orders and emails
add_filter( 'woocommerce_get_order_item_totals', 'display_date_custom_field_value_on_order_item_totals', 10, 3 );
function display_date_custom_field_value_on_order_item_totals( $total_rows, $order, $tax_display ){
// Initializing variables
$new_total_rows = $item_fees = [];
// 1st Loop - Check for fees
foreach ( $total_rows as $key => $values ) {
if( strpos($key, 'fee_') !== false ) {
$item_fees[] = $key;
}
}
if( count($item_fees) > 1 ) {
// 2nd Loop - Remove item fees total lines
foreach ( $item_fees as $key ) {
unset($total_rows[$key]);
}
$key_start = isset($total_rows['shipping']) ? 'shipping' : ( isset($total_rows['discount']) ? 'discount' : 'cart_subtotal' );
// 3rd Loop - loop through order total rows
foreach( $total_rows as $key => $values ) {
$new_total_rows[$key] = $values;
// Re-inserting sorted fees
if( $key === $key_start ) {
wc_get_sorted_order_item_totals_fee_rows( $new_total_rows, $tax_display, $order );
}
}
return $new_total_rows;
}
return $total_rows;
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.
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 );
}
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
));
}
}