I am trying to get woocommerce to remove all shipping classes and charge for just the two highest charges found within the cart but my function is not working. It is removing all the shipping charges and setting the shipping cost to zero.
EDIT: There is default functionality within a shipping zone to charge shipping classes set as "Per order: Charge shipping for the most expensive shipping class" but I need this to be "Per order: Charge shipping for the two most expensive shipping classes"
I have commented each step to show my understanding.
function charge_for_two_highest_shipping_classes( $rates ) {
// Sort rates by cost, in ascending order
usort( $rates, function( $a, $b ) {
return $a->cost - $b->cost;
} );
// Remove all shipping rates except the two highest
$highest_rates = array_slice( $rates, -2 );
// Set all other rates to zero
foreach ( $rates as $rate ) {
if ( ! in_array( $rate, $highest_rates, true ) ) {
$shipping_class_id = 0;
foreach ( $rate->get_meta_data() as $meta ) {
if ( 'shipping_class_id' === $meta->key ) {
$shipping_class_id = (int) $meta->value;
break;
}
}
$rate->cost = 0;
$rate->taxes = array();
$rate->set_meta_data( 'shipping_class_id', $shipping_class_id );
}
}
return $highest_rates;
}
add_filter( 'woocommerce_package_rates', 'charge_for_two_highest_shipping_classes', 10, 1 );
What you are trying to achieve is not as simple as you think.
The woocommerce_package_rates hook is wrong because all shipping class calculations are already done at this point.
The WooCommerce code you are looking for is here:
wp-content/plugins/woocommerce/includes/shipping/flat-rate/class-wc-shipping-flat-rate.php
See in the calculate_shipping function and look for the $highest_class_cost.
You could patch this function, but I wouldn't recommend it. There is no hook for this, you could only create new flat rates programmatically and do your logic there.
To sorts the rates in descending order and takes the first two elements (the two highest rates) and removes all other rates from the array. you can use this modification, Hope it might helpful to you 🙂 Thanks
function charge_for_two_highest_shipping_classes( $rates ) {
// Sort rates by cost, in descending order
usort( $rates, function( $a, $b ) {
return $b->cost - $a->cost;
} );
// Remove all shipping rates except the two highest
$highest_rates = array_slice( $rates, 0, 2 );
foreach ( $rates as $key => $rate ) {
if ( ! in_array( $rate, $highest_rates, true ) ) {
unset( $rates[$key] );
}
}
return $highest_rates;
}
add_filter( 'woocommerce_package_rates', 'charge_for_two_highest_shipping_classes', 10, 1 );
Related
Currently, our store offers "Free Shipping up to $350", after thought on how to incorporate this into eCom - I believe this will be the simplest method. Fetch Shipping Cost --> IF Shipping Costs > $350 --> Apply "350-shipping" coupon code.
We use 2 different plugins to calculate shipping:
1.) WooCommerce FedEx Shipping
2.) WooCommerce WWE LTL Quotes
Looking to incorporate this inside of functions.php. Note, I am also working on a solution, so will apply here if I get to it sooner
Notice here the correct code for the Shipping Page: https://i.imgur.com/TndLiHT.png
So far I have the following:
// $350 Shipping Coupon
add_filter( 'woocommerce_package_rates', 'custom_shipping_rate_label_based_on_cost', 100, 2 );
function custom_shipping_rate_label_based_on_cost( $rates, $package ){
// Loop through available shipping rates
foreach ( $rates as $rate_key => $rate ) {
$rate_cost = $rate->cost;
if ( $rate_cost > 350 ) {
echo "<script type='text/javascript'>alert('350 Detected');</script>";
}
}
return $rates;
}
What is missing here is I need to say:
IF ALL Items in Foreach are greater than $350. Right now, if I run this script, I get an endless loop of "350 detected"
add_action( 'woocommerce_after_calculate_totals', 'apply_coupon_depending_on_shipping_total', 999, 1 );
function apply_coupon_depending_on_shipping_total( $cart ) {
$coupon_code = 'coupon_code';
if ( $cart->has_discount( $coupon_code ) ) {
return;
}
$shipping_total = $cart->get_shipping_total();
if ( $shipping_total > 350 ) {
$cart->apply_coupon( $coupon_code );
}
}
In the Woocommerce store there are 3 shipping options
free shipping
flat rate
pickup
Inspired by Free shipping depending on weight and on minimal cart amount I would like to require a minimum total cart weight when choosing an shipping option.
When customers order for 24kg or less, they only should be able to pickup their order.
If the total cart weight is between 25kg and 49kg they should only may choose between the flat rate method or pickup.
And if the total cart weight is 50 kg and above they may choose between free shipping and pickup.
On the internet I found WooCommerce: How to Setup Tiered Shipping Rates by Order Amount, which I customized to work properly on the store because we have 2 shipping zones (so multiple flat rates, local pickup, etc).
Unfortunately it does not work for my needs, while I think the code is correct? Any help will be much appreciated.
add_filter( 'woocommerce_package_rates', 'bbloomer_woocommerce_tiered_shipping', 9999, 2 );
function bbloomer_woocommerce_tiered_shipping( $rates, $package ) {
if ( WC()->cart->get_cart_contents_weight() < 25 ) {
if ( isset( $rates['local_pickup:5'], $rates['local_pickup:9'] ) ) unset( $rates['flat_rate:3'], $rates['flat_rate:6'], $rates['free_shipping:1'], $rates['free_shipping:8'] );
} elseif ( WC()->cart->get_cart_contents_weight() < 50 ) {
if ( isset( $rates['flat_rate:3'], $rates['flat_rate:6'], $rates['local_pickup:5'], $rates['local_pickup:9'] ) ) unset( $rates['free_shipping:1'], $rates['free_shipping:8'] );
} else {
if ( isset( $rates['free_shipping:1'], $rates['free_shipping:8'], $rates['local_pickup:5'], $rates['local_pickup:9'] ) ) unset( $rates['flat_rate:3'], $rates['flat_rate:6'] );
}
return $rates;
}
Note: you cannot add multiple conditions to an if condition in the way you apply it.
Add the shipping options you want to keep under a certain condition to the $available_shipping array
When customers order for 24kg or less = local pickup
If the total cart weight is between 25kg and 49kg = local pickup and flat rate
If the total cart weight is 50 kg and above = local pickup and free shipping
function filter_woocommerce_package_rates( $rates, $package ) {
// Get cart contents weight
$weight = WC()->cart->get_cart_contents_weight();
// Conditions
if ( $weight <= 24 ) {
// Set available shipping options
$available_shipping = array( 'local_pickup' );
} elseif ( $weight > 24 || $weight < 50 ) {
$available_shipping = array( 'local_pickup', 'flat_rate' );
} else {
$available_shipping = array( 'local_pickup', 'free_shipping' );
}
foreach ( $rates as $rate_key => $rate ) {
// Targeting, NOT in array
if ( ! in_array( $rate->method_id, $available_shipping ) ) {
unset( $rates[$rate_key] );
}
}
return $rates;
}
add_filter( 'woocommerce_package_rates', 'filter_woocommerce_package_rates', 10, 2 );
In Woocommerce I have the following situation: there are shipping methods before 100$, and after 100$ only one (free shipping) is available. So when a client buys a product 102$ and then apply the (10%) promo code, the price will be 91,80$. Because I unset the shipping methods after 100$ and the free shipping appears after 100$ only, for the client shows: "There are no shipping methods available..."
add_filter( 'woocommerce_package_rates', 'woocommerce_hide_shipping', 10, 2 );
function woocommerce_hide_shipping( $rates, $package ) {
$threshold = 100;
if ( WC()->cart->subtotal >= $threshold ) {
unset( $rates['flat_rate:45'] );
unset( $rates['flat_rate:75'] );
}
if ( WC()->cart->subtotal >= $threshold && !empty(WC()->cart->applied_coupons) ) {
//code here
}
return $rates;
}
Its possible to show the free shipping if the price was >100$ before promo code and a coupon was applied? I set up the free shipping to show a minimum order amount (100$) but there is a way to show, initialize? Other approaches are also welcomed.
The problem is.... Free Shipping that you have applied is bounded by min order and that is referring to order total (NOT SUBTOTAL).....
Whereas subtotal gives you the value without discount.
So,
Remove condition (min order total) from your Admin settings - Free shipping.... Now it will work for every order...
Then modify your code ---
add_filter( 'woocommerce_package_rates', 'woocommerce_hide_shipping', 10, 2 );
function woocommerce_hide_shipping( $rates, $package ) {
$threshold = 100;
if ( WC()->cart->subtotal >= $threshold ) {
unset( $rates['flat_rate:45'] );
unset( $rates['flat_rate:75'] );
}
if ( WC()->cart->subtotal < $threshold ) {
//code here
unset( $rates['free_shipping:45'] );
unset( $rates['free_shipping:75'] );
}
return $rates;
}
I have already tried editing the following code from woothemes site for hiding a shipping method which was this:
// woocommerce_package_rates is a 2.1+ hook
add_filter( 'woocommerce_package_rates', 'hide_shipping_when_free_is_available', 10, 2 );
// Hide shipping rates when free shipping is available
// #param array $rates Array of rates found for the package
// #param array $package The package array/object being shipped
// #return array of modified rates
//
function hide_shipping_when_free_is_available( $rates, $package ) {
// Only modify rates if free_shipping is present
if ( isset( $rates['free_shipping'] ) ) {
// To unset a single rate/method, do the following. This example unsets flat_rate shipping
unset( $rates['flat_rate'] );
// To unset all methods except for free_shipping, do the following
$free_shipping = $rates['free_shipping'];
$rates = array();
$rates['free_shipping'] = $free_shipping;
}
return $rates;
}
I editted the code from 'free_shipping' to 'ups' which I believed would be all I needed to do but alas nothing came of it.
I'm using Table Rate Shipping 2.9.0 by Mike Jolley
Woocommerce 2.4.6
and UPS shipping method 3.1.1 by woothemes
Any help would be appreciated.
What I am trying to accomplish is this:
Not all my products have dimensions. On the products that do have dimensions and can checkout via UPS I would like them to ONLY be able to checkout using UPS.
If there is a mixed cart or products without dimensions, I want it to use the Table Rate shipping.
What I specifically don't want is both UPS and table rate to be displayed at the same time.
The main problem with using that snippet is that UPS and Table Rate Shipping have multiple rates.... and therefore multiple rate IDs. So you can't effectively test for the presence of a particular rate with a simple isset($rates['ups']) nor unset another with a unset($rates['table_rate']);
Take a look at this var_dump of my sample shipping rates. I'm using USPS because that's what I have on hand, but I expect it to be pretty similar to UPS.
So as far as I can tell, to achieve your aim, we'll need to test the keys for the presence of the "ups" or "table_rate" strings in the array keys. Fortunately, that's pretty easy with strpos.
I tested this against USPS and it seemed to work. I used the WooCommerce tools to set the site into Shipping Debug mode. Otherwise, WooCommerce stores the rates in a transient for an hour. (see: admin.php?page=wc-status&tab=tools on your site)
Here's my final code:
// remove any table_rate rates if UPS rates are present
add_filter( 'woocommerce_package_rates', 'hide_table_rates_when_ups_available', 10, 2 );
function hide_table_rates_when_ups_available( $rates, $package ) {
// Only modify rates if ups is present
if ( is_ups_available( $rates ) ) {
foreach ( $rates as $key => $rate ){
$pos = strpos( $key, 'table_rate' );
if( false !== $pos ){
unset( $rates[$key] );
}
}
}
return $rates;
}
// loops through the rates looking for any UPS rates
function is_ups_available( $rates ){
$is_available = false;
foreach ( $rates as $key => $rate ){
$pos = strpos( $key, 'ups' );
if( false !== $pos ){
$is_available = true;
break;
}
}
return $is_available;
}
I run an online store with WooCommerce and sell normal products and books. I want to reach the following:
If somebody has a cart value of < 50 he should pay for shipping
If somebody has a cart value of > 50 shipping should be free
If somebody adds a book to the cart shipping is always free
I try this with the following code:
function custom_free_per_class( $return, $package ) {
// Setup an array of shipping classes that allow Flat Rate Shipping
$shippingclass_array = array( 'bookshipping');
// loop through the cart checking the shipping classes
foreach ( WC()->cart->get_cart() as $cart_item_key => $values ) {
$shipping_class = get_the_terms( $values['product_id'], 'product_shipping_class' );
if ( isset( $shipping_class[0]->slug ) && in_array( $shipping_class[0]->slug, $shippingclass_array ) ) {
return true;
break;
}//if
}//foreach
}//function
add_filter( 'woocommerce_shipping_free_shipping_is_available', 'custom_free_per_class', 10, 2 );
add_filter( 'woocommerce_package_rates', 'hide_shipping_when_free_is_available', 10, 2 );
/**
* Hide shipping rates when free shipping is available
*
* #param array $rates Array of rates found for the package
* #param array $package The package array/object being shipped
* #return array of modified rates
*/
function hide_shipping_when_free_is_available( $rates, $package ) {
// Only modify rates if free_shipping is present
if ( isset( $rates['free_shipping'] ) ) {
// To unset all methods except for free_shipping, do the following
$free_shipping = $rates['free_shipping'];
$rates = array();
$rates['free_shipping'] = $free_shipping;
}
return $rates;
}
I set it up in the WooCommerce backend that free shipping should be available after a cart value of 50 has been reached. But however this does not work. The above code works - so free shipping is guaranteed when somebody adds a book, but this seems to hinder the other intention (cart > 50) from working. If I remove the code
add_filter( 'woocommerce_shipping_free_shipping_is_available', 'custom_free_per_class', 10, 2 );
the functionality with adding free shipping when 50 is reached works, but surely books are not free anymore.
Des anybody has an idea what is going wrong here?
I would really appreciate it if anyone can help me.
Thanks!
I solved it by myself. The input parameter for the function is not ($return, $package) - it is just ($is_available).