Conditional free shipping based on shipping class and minimal amount in Woocommerce - php

In woocommerce regarding Shipping methods, I am trying to have the following:
Products A only in cart: set with "Free shipping"
Products B only in cart: set with:
Flat rate amount of 15 if Products B purchased amount is less than 200
Free shipping if Products B purchased amount reaches 200 or more..
Products A + Products B are in cart at the same time: "Free Shipping" without any amount restriction.
I have tried by using flat rate and shipping classes I am getting like if product A and product B is there then if the cart doesn't reach 200 it is taking 15 shipping charge.
Any help is appreciated.

Updated: To make it work first you will need:
To add a "Free" shipping class (first),
To enable 2 shipping methods: "Free shipping" and "Flat rate",
In your products with Free shipping will need to be set with the shipping class "Free",
In your other product will not have any defined shipping class.
For the "free shipping" method, you will not add any restrictions amount to it.
For the "Flat rate" shipping method, you will set it as in this screen shot:
The magic will be done by the following code that will make the rest:
add_filter('woocommerce_package_rates', 'conditional_free_shipping', 10, 2);
function conditional_free_shipping( $rates, $package ){
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return $rates;
## -- Your settings below -- ##
$shipping_class = 'free'; // "Free" shipping class products
$min_free_amount = 200; // Minimal Free shipping amount for normal products
## -- -- -- -- -- -- -- -- -- ##
$has_free = false; // Initializing
$products_total = 0; // Initializing
// Loop through cart items
foreach( $package['contents'] as $cart_item ) {
if( $cart_item['data']->get_shipping_class() == $shipping_class ) {
$has_free = true;
} else {
// Get the total purchased amount for normal product
$products_total += $cart_item['line_total'] + $cart_item['line_tax'];
}
}
foreach ( $rates as $rate_key => $rate ){
// 1. Only Free shipping products in cart OR both products kind in cart
if( $has_free ) {
if( 'flat_rate' === $rate->method_id )
unset( $rates[$rate_key] ); // Remove flat rate
}
// 2. Only normal products in cart
else {
// A. If it's under the min amount
if( 'free_shipping' === $rate->method_id && $products_total < $min_free_amount )
unset( $rates[$rate_key] ); // Remove Free shipping
// B. When min amount is reached
elseif( 'flat_rate' === $rate->method_id && $products_total >= $min_free_amount )
unset( $rates[$rate_key] ); // Remove flat rate
}
}
return $rates;
}
Code goes in function.php file of your active child theme (active theme). Tested and works.
You might need to refresh shipping cached data: disable, save and enable, save related shipping methods for the current shipping zone, in Woocommerce shipping settings.

I did some improvement in the code now it is working fine..
add_filter('woocommerce_package_rates', 'conditional_free_shipping', 10, 2);
function conditional_free_shipping( $rates, $package ){
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return $rates;
## -- Your settings bellow -- ##
$shipping_class = 'free'; // "Free" shipping class products
$min_free_amount = 200; // Minimal Free shipping amount for normal products
## -- -- -- -- -- -- -- -- -- ##
$has_normal = $has_free = false; // Initializing
$products_total = 0; // Initializing
// Loop through cart items
foreach( $package['contents'] as $cart_item ) {
if( $cart_item['data']->get_shipping_class() == $shipping_class ) {
$has_free = true;
} else {
$has_normal = true;
// Get the total purchased amount for normal product
$products_total += $cart_item['line_total'] + $cart_item['line_tax'];
}
}
foreach ( $rates as $rate_key => $rate ){
// 1. Only Free shipping products in cart
if( $has_free && ! $has_normal ) {
if( 'flat_rate' === $rate->method_id )
unset( $rates[$rate_key] ); // Remove flat rate
}
elseif(( $has_free && $has_normal )){
if( 'flat_rate' === $rate->method_id && $products_total <= $min_free_amount )
unset( $rates[$rate_key] );
}
// 2. Only normal products in cart OR Both products kind in cart
elseif( ( ! $has_free && $has_normal ) ) {
// A. If it's under the min amount
if( 'free_shipping' === $rate->method_id && $products_total < $min_free_amount )
unset( $rates[$rate_key] ); // Remove Free shipping
// B. When min amount is reached
elseif( 'flat_rate' === $rate->method_id && $products_total >= $min_free_amount )
unset( $rates[$rate_key] ); // Remove flat rate
}
}
return $rates;
}

Related

Enable free shipping for a min amount based on WooCommerce discounted subtotal

In WooCommerce we have set flat_rate shipping amount to 4.95€ and free_shipping shows up for a minimal total amount of 45€.
Now, if a customer has a cart with - let`s say 48€ - he does not have to pay shipping costs, as he has reached the order total amount to apply free_shipping.
If he does apply now a 10% coupon, he ends up having 43.20€ order total amount and therefore has to pay shipping fees again.
We would like to still offer free shipping to that customer, after he applied the coupon and "landed" below the free_shipping amount. Otherwise its not very attractive using a 10% coupon (4.80€ in our case) but must pay 4.95€ shipping again.
Based on Applied coupons disable Free shipping conditionally in Woocommerce answer code, here is my code attempt:
add_filter( 'woocommerce_package_rates', 'coupons_removes_free_shipping', 10, 2 );
function coupons_removes_free_shipping( $rates, $package ){
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return $rates;
$shipping_counrtry = WC()->customer->get_shipping_country();
if ($shipping_counrtry == 'DE') : $min_subtotal = 45;
endif;
$shipping_counrtry = WC()->customer->get_shipping_country();
if ($shipping_counrtry == 'AT') : $min_subtotal = 75;
endif;
// Get needed cart subtotals
$subtotal_excl_tax = WC()->cart->get_subtotal();
$subtotal_incl_tax = $subtotal_excl_tax + WC()->cart->get_subtotal_tax();
$discount_excl_tax = WC()->cart->get_discount_total();
$discount_incl_tax = $discount_total + WC()->cart->get_discount_tax();
// Calculating the discounted subtotal including taxes
$discounted_subtotal_incl_taxes = $subtotal_incl_tax - $discount_incl_tax;
$applied_coupons = WC()->cart->get_applied_coupons();
if( sizeof($applied_coupons) > 0 && $discounted_subtotal_incl_taxes > $min_subtotal ){
foreach ( $rates as $rate_key => $rate ){
// Targeting "Free shipping"
if( 'free_shipping' != $rate->method_id ){
// SET THE RATE HERE; but how
}
}
}
return $rates;
}
Updated
First in your shipping settings for Free shipping, you will need to set the minimal amount to 0 (zero). Then the following code will handle cart item non discounted subtotal for a "Free shipping" minimal amount (that will solve your issue):
add_filter( 'woocommerce_package_rates', 'conditional_free_shipping', 10, 2 );
function conditional_free_shipping( $rates, $package ){
$shipping_country = WC()->customer->get_shipping_country(); // Get shipping country
$free_shipping = $other_rates = array(); // Initializing
if ($shipping_country === 'DE') {
$min_subtotal = 45;
} elseif ($shipping_country === 'AT') {
$min_subtotal = 75;
}
// Get subtotal incl tax (non discounted) for the current shipping package
$items_subtotal = array_sum( wp_list_pluck( $package['contents'], 'line_subtotal' ) );
$items_subtotal += array_sum( wp_list_pluck( $package['contents'], 'line_subtotal_tax' ) );
// Loop through shipping rates for current shipping package
foreach ( $rates as $rate_key => $rate ){
if( 'free_shipping' === $rate->method_id ){
$free_shipping[$rate_key] = $rate;
} else
$other_rates[$rate_key] = $rate;
}
}
return isset($min_subtotal) && $items_subtotal >= $min_subtotal ? $free_shipping : $other_rates;
}
Code goes in functions.php file of the active child theme (or active theme). It should work.
Don't forget to empty your cart to refresh shipping cached data.

Hide Free shipping for specific applied coupon when discounted subtotal is below 75

I am trying to create a very specific type of coupon code.
If a customer enters "allthings30" they get 30% off.
However if the order is over £75, They get free shipping as well.
Now the website, already has free shipping in place, but I want those disabled if the order is below £75, only when this code is applied.
Using other questions on stackoverflow, I have managed to create the code, but it is being applied to every singe couple.
How do I apply this code to only the "allthings30" coupon. Any help is greatly appropriated.
add_filter( 'woocommerce_package_rates', 'coupons_removes_free_shipping', 33, 38 );
function coupons_removes_free_shipping( $rates, $package ){
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return $rates;
$min_subtotal = 75; // Minimal subtotal allowing free shipping
$coupon_code = 'allthings30'; // The required coupon code
// Get needed cart subtotals
$subtotal_excl_tax = WC()->cart->get_subtotal();
$subtotal_incl_tax = $subtotal_excl_tax + WC()->cart->get_subtotal_tax();
$discount_excl_tax = WC()->cart->get_discount_total();
$discount_incl_tax = $discount_total + WC()->cart->get_discount_tax();
// Calculating the discounted subtotal including taxes
$discounted_subtotal_incl_taxes = $subtotal_incl_tax - $discount_incl_tax;
$applied_coupons = in_array( strtolower($coupon_code), WC()->cart->get_applied_coupons() );
if( sizeof($applied_coupons) > 0 && $discounted_subtotal_incl_taxes < $min_subtotal ){
foreach ( $rates as $rate_key => $rate ){
// Targeting "Free shipping"
if( 'free_shipping' === $rate->method_id ){
unset($rates[$rate_key]);
}
}
}
return $rates;
}
You are very near… The following will do the job:
add_filter( 'woocommerce_package_rates', 'coupons_removes_free_shipping', 33, 38 );
function coupons_removes_free_shipping( $rates, $package ){
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return $rates;
$min_subtotal = 75; // Minimal subtotal allowing free shipping
$coupon_code = 'summer'; // The required coupon code
// Get cart subtotals and applied coupons
$cart = WC()->cart;
$subtotal_excl_tax = $cart->get_subtotal();
$subtotal_incl_tax = $subtotal_excl_tax + $cart->get_subtotal_tax();
$discount_excl_tax = $cart->get_discount_total();
$discount_incl_tax = $discount_excl_tax + $cart->get_discount_tax();
$applied_coupons = $cart->get_applied_coupons(); // Get applied coupons array
// Calculating the discounted subtotal including taxes
$disc_subtotal_incl_tax = $subtotal_incl_tax - $discount_incl_tax;
if( in_array( strtolower($coupon_code), $applied_coupons ) && $disc_subtotal_incl_tax < $min_subtotal ){
foreach ( $rates as $rate_key => $rate ){
// Targeting "Free shipping"
if( 'free_shipping' === $rate->method_id ){
unset($rates[$rate_key]);
}
}
}
return $rates;
}
Don't forget after saving that code to your theme's functions.php file to refresh your shipping rates in Admin shipping rates settings, disabling and save any shipping method and re-enable and save it back…

Applied coupons disable Free shipping conditionally in Woocommerce

I am trying to get it so that if a customer were to add a coupon code (Any of them) the Free Shipping option would go away and the flat rate fee would be implemented. - You would think this would be an easy thing to implement, there would be 100's of plugins and ways described to do this, but I have not found any. I do not want to pay $89 for the plugin to do this one thing
A side bonus would be if they are using a coupon but are spending over $249 they can still qualify for Free shipping. I read some where how to do this, but it requires me to get the POST ID, which with the latest WooCommerce is not possible like it was before, I do not know the shipping ID so I am at a lost Here is the code
add_filter( 'woocommerce_shipping_packages', function( $packages ) {
$applied_coupons = WC()->session->get( 'applied_coupons', array() );
if ( ! empty( $applied_coupons ) ) {
$free_shipping_id = 'free_shipping:2';
unset($packages[0]['rates'][ $free_shipping_id ]);
}
return $packages;
} );
Thanks
Edited
add_filter( 'woocommerce_package_rates', 'coupons_removes_free_shipping',
10, 2 );
function coupons_removes_free_shipping( $rates, $package ){
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return $rates;
$min_total = 250; // Minimal subtotal allowing free shipping
// Get needed cart totals
$total_excl_tax = WC()->cart->get_total();
$discount_excl_tax = WC()->cart->get_discount_total();
// Calculating the discounted subtotal including taxes
$discounted_subtotal_incl_taxes = $total_excl_tax - $discount_excl_tax;
$applied_coupons = WC()->cart->get_applied_coupons();
if( sizeof($applied_coupons) > 0 && $discounted_subtotal_incl_taxes < $min_total ) {
foreach ( $rates as $rate_key => $rate ){
// Targeting "Free shipping"
if( 'free_shipping' === $rate->method_id ){
unset($rates[$rate_key]);
}
}
}
return $rates;
}
The below code will enable "Free shipping" for applied coupons only if cart subtotal reaches a minimal amount (discounted including taxes):
add_filter( 'woocommerce_package_rates', 'coupons_removes_free_shipping', 10, 2 );
function coupons_removes_free_shipping( $rates, $package ){
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return $rates;
$min_subtotal = 250; // Minimal subtotal allowing free shipping
// Get needed cart subtotals
$subtotal_excl_tax = WC()->cart->get_subtotal();
$subtotal_incl_tax = $subtotal_excl_tax + WC()->cart->get_subtotal_tax();
$discount_excl_tax = WC()->cart->get_discount_total();
$discount_incl_tax = $discount_total + WC()->cart->get_discount_tax();
// Calculating the discounted subtotal including taxes
$discounted_subtotal_incl_taxes = $subtotal_incl_tax - $discount_incl_tax;
$applied_coupons = WC()->cart->get_applied_coupons();
if( sizeof($applied_coupons) > 0 && $discounted_subtotal_incl_taxes < $min_subtotal ){
foreach ( $rates as $rate_key => $rate ){
// Targeting "Free shipping"
if( 'free_shipping' === $rate->method_id ){
unset($rates[$rate_key]);
}
}
}
return $rates;
}
Code goes in function.php file of your active child theme (or active theme). Tested and work.
Original answer:
The below code will remove "Free shipping" shipping methods when any coupon is applied without any settings need. There is some mistakes in your actual code. Try the following:
add_filter( 'woocommerce_package_rates', 'coupons_removes_free_shipping', 10, 2 );
function coupons_removes_free_shipping( $rates, $package ){
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return $rates;
$applied_coupons = WC()->cart->get_applied_coupons();
if( sizeof($applied_coupons) > 0 ){
// Loop through shipping rates
foreach ( $rates as $rate_key => $rate ){
// Targeting "Free shipping" only
if( 'free_shipping' === $rate->method_id ){
unset($rates[$rate_key]); // Removing current method
}
}
}
return $rates;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
By default WooCommerce will still allow a free shipping method to be selected if the coupon code is filled in and active in the shopping cart. The following code can be added to your child’s functions.php page to hook in and hide any free shipping option if a coupon code is used.
add_filter( 'woocommerce_shipping_packages', function( $packages ) {
$applied_coupons = WC()->session->get( 'applied_coupons', array() );
if ( ! empty( $applied_coupons ) ) {
$free_shipping_id = 'free_shipping:2';
unset($packages[0]['rates'][ $free_shipping_id ]);
}
return $packages;
} );
Simply change the $free_shipping_id in the above code to the ID of your free shipping option. You may find your ID by going to WooCommerce > Settings > Shipping Options and then click on your Free Shipping option. The post ID will be in the URL of the page that displays the Free Shipping details/settings.
i.e www.yourdomain.com/wp-admin/post.php?post=40107&action=edit
Where 40107 is the shipping ID in this example. Your shipping ID will be different.

Set shipping cost conditionally based on a shipping class in Woocommerce

We're selling sample products on our Woocommerce site, which is just a variable product. The product has a unique shipping class that allows it to be delivered for 1.99.
Actually this cost is always set when if item belongs to that unique shipping class, even if there is other items.
I would like if possible to enable that shipping cost only if that specific item (from that unique shipping class) is alone in cart.
Any help is appreciated.
The following hooked function will set the shipping cost to 0 if an items with a specific shipping class are melted to other items:
add_filter('woocommerce_package_rates', 'conditional_shipping_class_cost', 15, 2);
function conditional_shipping_class_cost( $rates, $package ){
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return $rates;
// HERE define the targeted shipping method
$shipping_class = 'Extra';
// Initializing variables
$found = $others = false;
// Loop through cart items and checking for the specific product
foreach( $package['contents'] as $item ) {
if( $item['data']->get_shipping_class() == sanitize_title($shipping_class) ){
$found = true; // Has the shipping class
} else {
$others = true; // NOT the shipping class
}
}
// When items with the defined shipping are not alone in cart
if( $found && $others ){
// Loop through shipping rates
foreach ( $rates as $rate_key => $rate ){
// For Flat rate and Local pickup shipping methods
if( $rate->method_id == 'flat_rate' ) {
// Set the cost to zero
$rates[$rate_key]->cost = 0;
$rates[$rate_key]->label = 'f: '.$found.' | o: '.$others.' ';
// Initializing variables
$has_taxes = false;
$taxes = [];
// Loop through the shipping taxes array (as they can be many)
foreach ($rates[$rate_key]->taxes as $key => $tax){
if( $rates[$rate_key]->taxes[$key] > 0 ){
// Set the tax cost to zero
$taxes[$key] = 0;
$has_taxes = true;
}
}
// Set new taxes cost array
if( $has_taxes )
$rates[$rate_key]->taxes = $taxes;
}
}
}
return $rates;
}
This code goes on function.php file of your active child theme (or theme). Tested and works.

Charge local pickup costs per item quantity in WooCommerce

I calculate my flat rate shipping cost like this: x * [qty]
I also want to use this method for my local pickup methods but using the function like shown above, does not work.
How can I achieve a shipping cost calculation based on the item quantity?
WordPress: 4.8.4 / WooCommerce: 3.1.1
Link to the page: http://www.minimoto.me/
UPDATE:
After the first helpful answer, this is the code I'm using:
add_filter( 'woocommerce_package_rates', 'hide_shipping_method_based_on_shipping_class', 10, 2 );
function hide_shipping_method_based_on_shipping_class( $rates, $package )
{
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
## ----- 1. Hiding shipping methods based on shipping class 92 ----- ##
// HERE define your shipping class to find
$class = 92;
// HERE define the shipping methods you want to hide
$method_key_ids = array('local_pickup:8');
// Checking in cart items
foreach( WC()->cart->get_cart() as $cart_item ){
// If we find the shipping class
if( $cart_item['data']->get_shipping_class_id() == $class ){
foreach( $method_key_ids as $method_key_id ){
unset($rates[$method_key_id]); // Remove the targeted methods
}
break; // Stop the loop
}
}
## ----- 2. Hiding shipping methods based on shipping class 132 ----- ##
// HERE define your shipping class to find
$class = 132;
// HERE define the shipping methods you want to hide
$method_key_ids = array('local_pickup:2', 'local_pickup:3', 'local_pickup:4');
// Checking in cart items
foreach( WC()->cart->get_cart() as $cart_item ){
// If we find the shipping class
if( $cart_item['data']->get_shipping_class_id() == $class ){
foreach( $method_key_ids as $method_key_id ){
unset($rates[$method_key_id]); // Remove the targeted methods
}
break; // Stop the loop
}
}
## ------- 3. Charge local pickup costs per item quantity ------- ##
$cart_items_count = WC()->cart->get_cart_contents_count(); // Cart items count
// Iterating through Shipping Methods
foreach ( $rates as $rate_key => $rate ) {
$method_id = $rate_values->method_id;
$rate_id = $rate_values->id;
// For "Local pickup" Shipping" Method only
if ( 'local_pickup' === $method_id ) {
if( ! empty( $rates[$rate_id]->cost && $rates[$rate_id]->cost > 0 ) ) {
// Set the rate calculated cost based on cart items count
$rates[$rate_id]->cost = number_format($rates[$rate_id]->cost * $cart_items_count, 2);
// Taxes rate cost (if enabled)
foreach ($rates[$rate_id]->taxes as $key => $tax){
if( $rates[$rate_id]->taxes[$key] > 0 ){ // set the new tax cost
$taxes[$key] = number_format( $rates[$rate_id]->taxes[$key] * $cart_items_count, 2 );
$has_taxes = true;
} else {
$has_taxes = false;
}
}
if( $has_taxes )
$rates[$rate_id]->taxes = $taxes;
}
}
}
return $rates;
}
You can make this in your existing customized code from this answer, using the custom function hooked in woocommerce_package_rates action hook.
With the following code, Your local pickup shipping methods costs will be multiplied by the total cart items count:
add_filter( 'woocommerce_package_rates', 'customizing_shipping_methods', 10, 2 );
function customizing_shipping_methods( $rates, $package )
{
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
## ----- 1. Hiding shipping methods based on shipping class ----- ##
// HERE define your shipping class to find
$class = 92;
// HERE define the shipping methods you want to hide
$method_key_ids = array('flat_rate:7', 'local_pickup:3');
// Checking in cart items
foreach( WC()->cart->get_cart() as $cart_item ){
// If we find the shipping class
if( $cart_item['data']->get_shipping_class_id() == $class ){
foreach( $method_key_ids as $method_key_id ){
unset($rates[$method_key_id]); // Remove the targeted methods
}
break; // Stop the loop
}
}
## ------- 2. Charge local pickup costs per item quantity ------- ##
$cart_items_count = WC()->cart->get_cart_contents_count(); // Cart items count
// Iterating through Shipping Methods
foreach ( $rates as $rate_key => $rate_values ) {
$method_id = $rate_values->method_id;
$rate_id = $rate_values->id;
// For "Local pickup" Shipping" Method only
if ( 'local_pickup' === $method_id ) {
if( ! empty( $rates[$rate_id]->cost && $rates[$rate_id]->cost > 0 ) ) {
// Set the rate calculated cost based on cart items count
$rates[$rate_id]->cost = number_format($rates[$rate_id]->cost * $cart_items_count, 2);
// Taxes rate cost (if enabled)
foreach ($rates[$rate_id]->taxes as $key => $tax){
if( $rates[$rate_id]->taxes[$key] > 0 ){ // set the new tax cost
$taxes[$key] = number_format( $rates[$rate_id]->taxes[$key] * $cart_items_count, 2 );
$has_taxes = true;
} else {
$has_taxes = false;
}
}
if( $has_taxes )
$rates[$rate_id]->taxes = $taxes;
}
}
}
return $rates;
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
Tested and works
Sometimes, you should may be need to refresh shipping methods going to shipping areas, then disable / save and re-enable / save your "flat rate" and "local pickup" shipping methods.

Categories