Custom free shipping calculated rate cost in Woocommerce 3 - php

I am having an issue with applying this filter at checkout. the below code works as expected on the cart page, the labels for the shipping methods each update if the specified criteria (in this case "free_shipping") are met.
add_filter( 'woocommerce_package_rates', 'my_custom_shipping', 100, 2 );
function my_custom_shipping( $rates, $package ) {
$percentage = 0.01;
foreach($rates as $key => $rate ) {
$test_method_id = $rates[$key]->method_id;
if ( $test_method_id === "free_shipping" ){
$surcharge = ( wc()->cart->cart_contents_total + $rates[$key]->cost ) * $percentage;
$rates[$key]->label .= " Fee: {$surcharge}";
$rates[$key]->cost += $surcharge;
}
}
return $rates;
}// Function END
while this works fine on the cart page (but only with debug mode active, nothing loads when debug mode is turned off which is very frustrating), on the checkout page the changes do load briefly ( i can scroll down and see them ) but they are quickly overwritten with the original labels. I cannot seem to figure out why, I thought setting the filter to call later might help but it does not seem to.
all the data is there and I can output and see it but it simply does not apply in a permanent way at checkout, why would this be happening?

You can't really get an initial cost for "free shipping" rate as there is not any cost field for "Free shipping" method in backend.
But you can add a calculated cost and change the label (differently):
add_filter( 'woocommerce_package_rates', 'custom_shipping_rates', 100, 2 );
function custom_shipping_rates( $rates, $package ) {
$cc_total = WC()->cart->cart_contents_total;
$percentage = 0.01; // 1%
foreach( $rates as $rate_key => $rate ) {
if ( 'free_shipping' === $rate->method_id ){
// Calculation
$surcharge = $cc_total * $percentage;
// Set the new Label name
$rates[$rate_key]->label .= ' ' . __("Fee", "woocommerce");
// Set Custom rate cost
$rates[$rate_key]->cost = round($surcharge, 2);
}
}
return $rates;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works
You should need to refresh the shipping caches:
1) First this code is already saved on your function.php file.
2) In Shipping settings, enter in a Shipping Zone and disable a Shipping Method and "save". Then re-enable that Shipping Method and "save". You are done.
Update: (Force clearing shipping methods from WC_Session):
add_filter( 'woocommerce_package_rates', 'custom_shipping_rates', 100, 2 );
function custom_shipping_rates( $rates, $package ) {
// Reset session
WC()->session->set('shipping_for_package_0', array('package_hash' => ''));
$cc_total = WC()->cart->cart_contents_total;
$percentage = 0.01; // 1%
foreach( $rates as $rate_key => $rate ) {
if ( 'free_shipping' === $rate->method_id ){
// Calculation
$surcharge = $cc_total * $percentage;
// Set the new Label name
$rates[$rate_key]->label .= ' ' . __("Fee", "woocommerce");
// Set Custom rate cost
$rates[$rate_key]->cost = round($surcharge, 2);
}
}
return $rates;
}
Code goes in function.php file of your active child theme (or active theme). It could works…

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.

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.

WooCommerce free shipping to members results in error

I have this issue where I want for members to receive free delivery. I have figured out how to do that, but now I get an error on the page.
The error is:
The WC_Cart->taxes function is deprecated since version 3.2. Replace
with getters (WC_Cart::get_cart_contents_taxes()) and setters
(WC_Cart::set_cart_contents_taxes())., referer:
This is the code that generates the problem:
add_filter('woocommerce_package_rates','test_overwrite_fedex', 100, 2);
function test_overwrite_fedex($rates,$package)
{
$memberships = wc_memberships_get_user_active_memberships();
if (WC()->customer->get_shipping_country() === 'DK' && !empty($memberships))
{
foreach ($rates as $rate)
{
//Set the TAX
$rate->taxes[1] = 0;
}
}
return $rates;
}
I have tried with:
$rate->set_shipping_total('0');
WC()->cart->set_shipping_total('0');
$rate = WC()->cart->get_shipping_total();
And still no luck.
The Taxes for shipping methods rates are set in a more complex multidimensional array, so your code make an error. Also you just forgot to null the rate cost.
You may have to "Enable debug mode" in general shipping settings under "Shipping options" tab, to disable temporarily shipping caches.
Try the following code that will null any shipping method cost for a specific country (DK) and for active members:
add_filter('woocommerce_package_rates', 'conditionally_remove_shipping_rates_cost', 25, 2);
function conditionally_remove_shipping_rates_cost( $rates, $package ){
$memberships = wc_memberships_get_user_active_memberships();
if ( WC()->customer->get_shipping_country() === 'DK' && !empty($memberships) ) {
// Loop through the shipping taxes array
foreach ( $rates as $rate_key => $rate ){
$has_taxes = false;
// Not for free shipping
if( 'free_shippping' !== $rate->method_id ){
// Taxes rate cost (if enabled)
$taxes = [];
// Null the shippin cost
$rates[$rate_key]->cost = 0;
// 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 ){
// Null tax cost
$taxes[$key] = 0;
$has_taxes = true;
}
}
if( $has_taxes )
$rates[$rate_key]->taxes = $taxes;
}
}
}
return $rates;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works (without the membership function).
Don't forget to enable back shipping cache.

Change Flat rate shipping method cost if a specific coupon is applied in Woocommerce

I have a specific coupon which is special50. When someone applied this coupon on the store then a new shipping method need to add. When current shipping method price is $50 (flat rate) and after applying coupon new shipping method, pricing will be $25. In a word, if you apply this coupon you will receive 50% OFF on products(which WooCommerce has already provided to us) and 50% OFF on shipping(which really I need).
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';
$new_rate['label'] = 'Shipping and handling';
global $woocommerce, $wpdb;
$coupon = "SELECT post_title FROM {$wpdb->posts} WHERE post_title='special50' AND post_type ='shop_coupon' AND post_status ='publish'";
if(in_array($coupon_id, $woocommerce->cart->applied_coupons)){
$cost = 25;
}
$new_rate['cost'] = $cost;
$method->add_rate( $new_rate );
}
This can be done using the following custom function hooked in woocommerce_package_rates filter hook, without any need of creating an additional discounted flat rate. The following code will change the "flat rate" shipping method cost when 'special50' coupon is applied.
You should first "Enable debug mode" in Woocommerce settings > shipping > Shipping options.
The code:
add_filter('woocommerce_package_rates', 'coupon_discount_on_flat_rate', 10, 2);
function coupon_discount_on_flat_rate( $rates, $package ){
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return $rates;
// Checking for 'special50' in applied coupons
if( in_array( 'special50', WC()->cart->get_applied_coupons() ) ){
foreach ( $rates as $rate_key => $rate ){
$has_taxes = false;
// Targeting "flat rate" shipping method
if( $rate->method_id === 'flat_rate' ){
// Set 50% of the cost
$rates[$rate_key]->cost = $rates[$rate_key]->cost / 2;
// Taxes rate cost (if enabled)
foreach ($rates[$rate_key]->taxes as $key => $tax){
if( $rates[$rate_key]->taxes[$key] > 0 ){
$has_taxes = true;
// set 50% of the cost
$taxes[$key] = $rates[$rate_key]->taxes[$key] / 2;
}
}
if( $has_taxes )
$rates[$rate_key]->taxes = $taxes;
}
}
}
return $rates;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
Dont forget to disable "Enable debug mode" once this has been tested and works.

Tiered Shipping based on cart subtotal in Woocommerce

In Woocommerce, I need to set up shipping costs based on cart subtotal as following:
if price is below 20€ the shipping cost is 5,90€,
if price start from 20€ and below 30€ the shipping cost 4,90€
if price start from 30€ and up the shipping cost 3,90€.
I have very basic PHP knowledge and have modified a code snippet that I have found.
This is my code:
add_filter('woocommerce_package_rates','bbloomer_woocommerce_tiered_shipping', 10, 2 );
function bbloomer_woocommerce_tiered_shipping( $rates, $package){
$threshold1 = 20;
$threshold2 = 30;
if ( WC()->cart->subtotal < $threshold1 ){
unset( $rates['flat_rate:5'] );
unset($rates['flat_rate:6']);
} elseif((WC()->cart->subtotal>$threshold1)and(WC()->cart->subtotal<$threshold2) ) {
unset( $rates['flat_rate:1'] );
unset( $rates['flat_rate:6'] );
} elseif((WC()->cart->subtotal > $threshold2) and (WC()->cart->subtotal > $threshold1) ) {
unset( $rates['flat_rate:1'] );
unset( $rates['flat_rate:5'] );
}
return $rates;
}
This seems to be working, but sometimes (and I have not figured out the trigger!) it doesn't and all three shipping methods show up in the cart. I think I noticed that it only happens when a user is logged in, but I am not 100% sure on this.
Is my function is correct? Do need to change or improve anything?
I have revisited your code a bit. You should try the following:
add_filter('woocommerce_package_rates','subtotal_based_tiered_shipping', 10, 2 );
function subtotal_based_tiered_shipping( $rates, $package ){
$threshold1 = 20;
$threshold2 = 30;
$subtotal = WC()->cart->subtotal;
if ( $subtotal < $threshold1 )
{ // Below 20 ('flat_rate:1' is enabled)
unset( $rates['flat_rate:5'] );
unset( $rates['flat_rate:6'] );
}
elseif ( $subtotal >= $threshold1 && $subtotal < $threshold2 )
{ // Starting from 20 and below 30 ('flat_rate:5' is enabled)
unset( $rates['flat_rate:1'] );
unset( $rates['flat_rate:6'] );
}
elseif ( $subtotal >= $threshold2 )
{ // Starting from 30 and up ('flat_rate:6' is enabled)
unset( $rates['flat_rate:1'] );
unset( $rates['flat_rate:5'] );
}
return $rates;
}
Code goes in function.php file of your active child theme (or active theme).
It should works now as intended.
To make it work with "Free shipping" methods too, you will have to disable "Free shipping requires..." option (set to "N/A").
Sometimes you should need to refresh shipping caches:
Once saved this code and emptied cart, go to your shipping settings. In your shipping zone, disable save and re-enable save your shipping methods.

Categories