I'm setting up a store in Woocommerce which has different shipping zones with different prices for delivery. I'd like to know if it is possible to define a flat rate that appears only when another shipping method is triggered? I explain:
3 different shipping zones, each one with its own fees for delivery.
Zone 1:
For orders up to 350, the methods available are 2:
a. Flat rate shipping 26,90
b. Flat rate insured shipping 31,90
After 350, the methods available become 3:
a. Flat rate shipping 26,90
b. Flat rate insured shipping 31,90
c. Free shipping
I want to hide a and b when free shipping is reached, but show another option d and obtain something like this (I have to this for each zone so there are several different ids to hide):
a. Free shipping
b. Flat rate insured shipping 6,00
I'm currently using this code to hide all other shipping methods when free shipping is available:
/**
* Hide shipping rates when free shipping is available.
* Updated to support WooCommerce 2.6 Shipping Zones.
*
* #param array $rates Array of rates found for the package.
* #return array
*/
function my_hide_shipping_when_free_is_available( $rates ) {
$free = array();
foreach ( $rates as $rate_id => $rate ) {
if ( 'free_shipping' === $rate->method_id ) {
$free[ $rate_id ] = $rate;
break;
}
}
return ! empty( $free ) ? $free : $rates;
}
add_filter( 'woocommerce_package_rates', 'my_hide_shipping_when_free_is_available', 100 );
Maybe there's a way to tweak it?
Related
Here's the scenario:
I ship trays of drinks using UPS. However, it becomes very difficult to pack 5 trays of drinks into boxes. So I would like to disable UPS shipping method and only display Flat Rate Shipping if a customer orders 5 or more trays of drinks. I have about 7 different drinks, but I can add these drinks to a shipping class to simplify the code.
I want to expand this code to include the quantity of products in a specific the class or perhaps the number of times a product in the shipping class appears. So if the cart has 5 or more of a product in this specific shipping class, it should remove the shipping methods I've specified under array.
How do I expand this code to also include quantity of products?
// If we find the shipping class & quantity of product in the shipping class is equal to or greater than 5.
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;
// Shipping Class To Find
$class = 182;
// Number Of Shipping Class Items In Cart
$amount = 5;
// Shipping Methods To Hide
$method_key_ids = array('wf_shipping_ups:07', 'wf_shipping_ups:08', 'wf_shipping_ups:11', 'wf_shipping_ups:54', 'wf_shipping_ups:65', 'wf_shipping_ups:70', 'wf_shipping_ups:74', 'free_shipping:2', 'request_shipping_quote');
// Checking In Cart Items
foreach( $package['contents'] as $item ) {
// If We Find The Shipping Class and Number of Items
if( $item['data']->get_shipping_class_id() == $class && count($package['contents']) >= $amount ){
foreach( $method_key_ids as $method_key_id ){
unset($rates[$method_key_id]); // Remove Targeted Methods
}
break; // Stop The Loop
}
}
return $rates;
}
Edit - Addition:
Since I purchased the "WooCommerce UPS Shipping Plugin with Print Label" from PluginHive, I have access to their "Manage Shipping Methods" plugin which allows me to do the following:
Set multiple rules to exclude various shipping methods from various shipping classes.
Break the sequence on first occurrence.
The rules I have set-up are as follows:
For Class 150 (Break on First Occurrence) - Unset:
wf_shipping_ups:07,
wf_shipping_ups:08,
wf_shipping_ups:11,
wf_shipping_ups:54,
wf_shipping_ups:65,
wf_shipping_ups:70,
wf_shipping_ups:74,
free_shipping:2,
request_shipping_quote.
For Class 151 - Unset:
flat_rate:20,
flat_rate:21.
I've created a third class 182 in the above code for the products I want to target. It should be treated as class 151 only if less than 5 items of the class are added to the cart.
But it should be treated as Class 150 if 5 or more items are added to the cart.
That's my dilemma.
Potential Solution - Addition:
I figured out how to solve my problem. The code #LoicTheAztec assisted me with lets me unset shipping methods for a given shipping class if the product quantity in the cart is 5 or more.
What I need to do now is unset two other shipping methods (flat_rate:20 and flat_rate:21) which is causing the conflict, for the same shipping class (182) but this time for a product quantity in the cart of 4 or less (=<).
Then I can use the existing plugin to create the following rules:
Break on First Occurrence (Check)
For Class 150 - Unset:
wf_shipping_ups:07,
wf_shipping_ups:08,
wf_shipping_ups:11,
wf_shipping_ups:54,
wf_shipping_ups:65,
wf_shipping_ups:70,
wf_shipping_ups:74,
free_shipping:2,
request_shipping_quote.
For Class 182 - Unset:
Nothing - Because Both Codes Will Create The Logic
For Class 151 - Unset:
flat_rate:20,
flat_rate:21.
This should resolve the conflict caused by the plugin.
The million dollar question is...can I somehow use #LoicTheAztec 's solution to set some sort of minimum quantity?
The following will hide specific defined shipping methods if total items from specific shipping class are 5 or more:
add_filter( 'woocommerce_package_rates', 'hide_shipping_method_based_on_shipping_class', 10, 2 );
function hide_shipping_method_based_on_shipping_class( $rates, $package ) {
$targeted_class_ids = array(182); // Shipping Class To Find
$allowed_max_qty = 4; // Max allowed quantity for the shipping class
$shipping_rates_ids = array( // Shipping Method rates Ids To Hide
'wf_shipping_ups:07',
'wf_shipping_ups:08',
'wf_shipping_ups:11',
'wf_shipping_ups:54',
'wf_shipping_ups:65',
'wf_shipping_ups:70',
'wf_shipping_ups:74',
'free_shipping:2',
'request_shipping_quote'
);
$related_total_qty = 0;
// Checking cart items for current package
foreach( $package['contents'] as $key => $cart_item ) {
if( in_array( $cart_item['data']->get_shipping_class_id(), $targeted_class_ids ) ){
$related_total_qty += $cart_item['quantity'];
}
}
// When total allowed quantity is more than allowed (for items from defined shipping classes)
if ( $related_total_qty > $allowed_max_qty ) {
// Hide related defined shipping methods
foreach( $shipping_rates_ids as $shipping_rate_id ) {
if( isset($rates[$shipping_rate_id]) ) {
unset($rates[$shipping_rate_id]); // Remove Targeted Methods
}
}
}
return $rates;
}
Code goes in functions.php file of your active child theme (or active theme). Untested it should 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.
Handling number of items instead of items cumulated quantity:
Replace:
$related_total_qty += $cart_item['quantity'];
by
$related_total_qty++;
I am using Hide specifics Flat Rates when Free Shipping is available in WooCommerce 3 lightly changed answer code to hide all shipping methods except one. The only method I want showing is a rate from the "Woocommerce Advanced Shipping" plugin.
I am using the correct rate ID etc...
Everything works fine except when a customer tries to click that shipping method, it won't stay selected. It just jumps back to free shipping.
I have tried debugging and also tried the code with a native woocommerce flat rate ID and it showed up/able to select it just fine.
add_filter( 'woocommerce_package_rates', 'conditionally_hide_shipping_methods', 100, 2 );
function conditionally_hide_shipping_methods( $rates, $package ) {
$flat_rates_express = array( '2588' );
$free = $flat2 = array();
foreach ( $rates as $rate_key => $rate ) {
// Updated Here To
if ( in_array( $rate->id, $flat_rates_express ) )
$flat2[ $rate_key ] = $rate;
if ( 'free_shipping:12' === $rate->id )
$free[ $rate_key ] = $rate;
}
return ! empty( $free ) ? array_merge( $free, $flat2 ) : $rates;
}
ID I want to Keep Shown: "2588" (Custom Shipping Rate From Plugin)
How can I disable the Flat rate shipping method when free shipping is available o and keep a custom shipping rate (from a plugin)?
As you have 3 shipping methods, 1 free shipping, 1 flat rate and 1 custom '2588', it's possible to hide the flat rate shipping method when free shipping is available instead:
add_filter( 'woocommerce_package_rates', 'free_shipping_disable_flat_rate', 1000, 2 );
function free_shipping_disable_flat_rate( $rates, $package ) {
// Here your free shipping rate Id
$free_shipping_rate_id = 'free_shipping:12';
// When your Free shipping method is available
if ( array_key_exists( $free_shipping_rate_id, $rates ) ) {
// Loop through shipping methods rates
foreach ( $rates as $rate_key => $rate ) {
// Removing "Flat rate" shipping method
if ( 'flat_rate' === $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 function.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 am trying to update the shipping cost after the address is entered. When the client starts typing the address, and autocomplete function helps the client find the street name, building, floor, and side.
Once the address is entered JavaScript calculates the distance and the price.
Then the price is via AJAX send to shipment.php
It is my goal to have shipment update the shipping cost accordingly.
I have tried:
include '../../../../wp-load.php';
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['cost'] = 50; // Update the cost to 50
$method->add_rate( $new_rate );
}
But without luck.
I have also following this guide and added another shipping method: Tuts
https://code.tutsplus.com/tutorials/create-a-custom-shipping-method-for-woocommerce--cms-26098
But once again, I cannot update the price once the address is entered.
I think that You don't need Query ajax to update the price directly for this calculated shipping cost and you don't need a custom shipping method as in your code or linked tutorial.
THERE IS THREE STEPS:
1) In your existing Ajax driven PHP function, you will set the calculated price in a custom WC_Session attribute where $shipping_cost variable will be the collected value of the calculated shipping cost passed via JS Ajax:
WC()->session->set( 'shipping_calculated_cost', $shipping_cost );
Then in jQuery Ajax "on success" event you will update checkout with the following:
$('body').trigger('update_checkout');
That will refresh checkout data…
2) In the custom function hooked in woocommerce_package_rates filer hook, you will get the session value of the calculated shipping cost…
IMPORTANT: In WooCommerce settings > shipping, you will set the "flat rate" cost to 1…
In The function code you will set the default price for this flat rate (that will be used when the shipping cost calculation doesn't exist yet):
add_filter('woocommerce_package_rates', 'update_shipping_costs_based_on_cart_session_custom_data', 10, 2);
function update_shipping_costs_based_on_cart_session_custom_data( $rates, $package ){
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return $rates;
// SET HERE the default cost (when "calculated cost" is not yet defined)
$cost = '50';
// Get Shipping rate calculated cost (if it exists)
$calculated_cost = WC()->session->get( 'shipping_calculated_cost');
// Iterating though Shipping Methods
foreach ( $rates as $rate_key => $rate_values ) {
$method_id = $rate_values->method_id;
$rate_id = $rate_values->id;
// For "Flat rate" Shipping" Method only
if ( 'flat_rate' === $method_id ) {
if( ! empty( $calculated_cost ) ) {
$cost = $calculated_cost;
}
// Set the rate cost
$rates[$rate_id]->cost = number_format($rates[$rate_id]->cost * $cost, 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] * $cost, 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 active theme) or in any plugin file.
3) Refresh the shipping caches (needed sometimes):
First empty your cart.
This code is already saved on your function.php file.
Go in a shipping zone settings and disable one "flat rate" (for example) and "save". Then re-enable that "flat rate" and "save". You are done and you can test it.
This should work for you,
If you have many different "flat rates" by shipping zones, you will need to make some changes in my code, if the default cost is different for each…
In WooCommerce 3, I have these shipping options (settings):
Free Shipping: free_shipping:1 - Minimum order amount is set at $50.
Normal Shipping flat_rate:3 - Amount $5.
Express Shipping flat_rate:5 - Amount $10.
I would like Express Shipping option to be always available (shown).
But when Free shipping is available (meaning that the customer has more than $50 in the cart) I would like to hide Normal Shipping only.
So when Free shipping is NOT available (and hidden), the available shipping rates will be Normal Shipping and Express Shipping.
Is that possible? How can I get this on WooCommerce 3?
Based on the official WooCommerce snippet code, making some light changes, you will be able to hide only your first flat rate when free shippings is available:
add_filter( 'woocommerce_package_rates', 'conditionally_hide_shipping_methods', 100, 2 );
function conditionally_hide_shipping_methods( $rates, $package ) {
// HERE yours 2nd flat rate "Express Shipping" (that you never hide) in the array:
$flat_rates_express = array( 'flat_rate:5', 'flat_rate:12', 'flat_rate:14' );
$free = $flat2 = array();
foreach ( $rates as $rate_key => $rate ) {
// Updated Here To
if ( in_array( $rate->id, $flat_rates_express ) )
$flat2[ $rate_key ] = $rate;
if ( 'free_shipping' === $rate->method_id )
$free[ $rate_key ] = $rate;
}
return ! empty( $free ) ? array_merge( $free, $flat2 ) : $rates;
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
Tested on WooCommerce 3 and works.
Refresh the shipping caches:
1) First empty your cart.
2) This code is already saved on your function.php file.
3) Go in a shipping zone settings and disable one "flat rate" (for example) and "save". Then re-enable that "flat rate" and "save". You are done and you can test it.
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;
}