I'm trying to write a function that will offer free shipping only (remove all other options) if the order is over 5 pounds (80 ounces), but it doesn't work although the code seems correct.
This is what I have:
// Hide ALL shipping options but FREe when over 80 ounches (5 pounds)
add_filter( 'woocommerce_available_shipping_methods', 'ship_free_if_over_five_pounds' , 10, 1 );
/**
* Hide ALL Shipping option but free if over 5 pounds
*
* #param array $available_methods
*/
function ship_free_if_over_five_pounds( $available_methods ) {
global $woocommerce;
$whats_the_weight = $woocommerce->cart->cart_contents_weight;
if($whats_the_weight != 80) :
// Get Free Shipping array into a new array
$freeshipping = array();
$freeshipping = $available_methods['free_shipping'];
// Empty the $available_methods array
unset( $available_methods );
// Add Free Shipping back into $avaialble_methods
$available_methods = array();
$available_methods[] = $freeshipping;
endif;
return $available_methods;
}
Any thoughts?
The code is based on example #19 on this site: My 25 Best WooCommerce Snippets For WordPress Part 2
I know that this is an old question, but I have this recent alternative…
First, in your code "if the order is over 5 pounds (80 ounces)" your if statement should be if($whats_the_weight > 80) instead of !=… But i think that your code is a little outdated if you use WooCommerce 2.6+.
After instead using global $woocommerce; with $woocommerce->cart->cart_contents_weight; you could use: WC()->cart->cart_contents_weight;
I have this much more recent code snippet based on this official thread for WooCommerce 2.6+. You should try it:
add_filter( 'woocommerce_package_rates', 'my_hide_shipping_when_free_is_available', 100 );
function my_hide_shipping_when_free_is_available( $rates ) {
$cart_weight = WC()->cart->cart_contents_weight; // Cart total weight
$free = array();
foreach ( $rates as $rate_id => $rate ) {
if ( 'free_shipping' === $rate->method_id && $cart_weight > 80 ) { // <= your weight condition
$free[ $rate_id ] = $rate;
break;
}
}
return ! empty( $free ) ? $free : $rates;
}
For WooCommerce 2.5, You should try this:
add_filter( 'woocommerce_package_rates', 'hide_shipping_when_free_is_available', 10, 2 );
function hide_shipping_when_free_is_available( $rates, $package ) {
$cart_weight = WC()->cart->cart_contents_weight; // Cart total weight
// Only modify rates if free_shipping is present
if ( isset( $rates['free_shipping'] ) && $cart_weight > 80 ) { // Here your weight condition
// 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've made an plugin where you can set the maximum weight for free shipping!
Take an look at: http://wordpress.org/plugins/woocommerce-advanced-free-shipping/
Related
I'm working on hiding free shipping when there is a fixed price shipping amount based on certain products. This code is working and after certain sum is also disabled. But I want this to work with flat shipping conditional, not post class conditional.
I want it: if ( isset( $rates['flat_rate:1'] ) )
But it works: if ( $values[ 'data' ]->get_shipping_class_id() == $shipping_class_target )
add_filter( 'woocommerce_package_rates', 'bbloomer_hide_free_shipping_for_shipping_class', 9999, 2 );
function bbloomer_hide_free_shipping_for_shipping_class( $rates, $package ) {
$shipping_class_target = 261; // shipping class ID (to find it, see screenshot below)
$subtotal = WC()->cart->get_subtotal();
$in_cart = false;
foreach ( WC()->cart->get_cart_contents() as $key => $values ) {
if ( $values[ 'data' ]->get_shipping_class_id() == $shipping_class_target && $subtotal <= 299 ) {
$in_cart = true;
break;
}
}
if ( $in_cart ) {
unset( $rates['free_shipping:2'] ); // shipping method with ID (to find it, see screenshot below)
}
return $rates;
}
I'm looking for a way to give free shipping on all orders based on weekday of the order. So if someone places an order (let's say on Monday) it will give him free shipping no matter what type of order quantity/amount/etc.
I've tried to hook up something from different other tutorials but I can't seem to get the part where I change the free_shipping limit + not sure it works since it's incomplete.
function free_shipping_day( $rates, $package ) {
// valid days
$valid_days = array('Mon');
// current day
$today = date ( 'D' );
// check if it's valid day
if( in_array($today, $valid_day) ){
// clear other shipping methods allow only free
foreach ( $rates as $rate_id => $rate ) {
if ( 'free_shipping' === $rate->method_id ) {
$free[ $rate_id ] = $rate;
}
}
// set free_shipping limit to 0
// show notice
wc_add_notice( __( "Shipping is free today!" ), 'notice');
}
}
add_action('woocommerce_package_rates', 'free_shipping_day');
Any help is very appreciated since I'm kind of stuck with this.
To make free shipping available on specific days of the week, it requires a different hook to bypass the free shipping limitations (if they exist).
We use date() function with "w" parameter that gives an integer from 0 (Sunday) to 6 (Saturday):
// Enable free shipping on specific days of the week
add_filter( 'woocommerce_shipping_free_shipping_is_available', 'enable_free_shipping_for_specific_week_days', 10, 3 );
function enable_free_shipping_for_specific_week_days( $is_available, $package, $shipping_method ) {
// Free shipping is available on mondays and wednesdays for example
if( in_array( date('w'), [ 1, 3 ] ) ) {
return true;
}
return $is_available;
}
To hide other shipping methods when free shipping is available, you can use additionally the following:
// Hide other shipping methods when free shipping is available
add_filter( 'woocommerce_package_rates', 'hide_other_shipping_methods_when_free_shipping_is_available', 100, 2 );
function hide_other_shipping_methods_when_free_shipping_is_available( $rates, $package ) {
$free = array();
foreach ( $rates as $rate_id => $rate ) {
if ( 'free_shipping' === $rate->method_id ) {
$free[ $rate_id ] = $rate;
break;
}
}
return ! empty( $free ) ? $free : $rates;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
Add the follows code snippet in your active theme's functions.php -
function enable_free_shipping_for_days( $rates ) {
$free = array();
// valid days
$valid_days = array('Mon');
if( !in_array( date('D'), $valid_days ) ) return $rates;
foreach ( $rates as $rate_id => $rate ) {
if ( 'free_shipping' !== $rate->method_id ) continue;
$free[ $rate_id ] = $rate;
}
if( $free ) wc_add_notice( __( "Shipping is free today!" ), 'notice');
return ( $free ) ? $free : $rates;
}
add_filter( 'woocommerce_package_rates', 'enable_free_shipping_for_days', 99 );
I have three subscription products, yearly, half-yearly and monthly. By default, prices are shown per year, per every 6 months and per month on my Shop page.
Based on "Change product prices via a hook in WooCommerce 3" answer code, I am trying to display all the prices per month, so to change the price display based on the subscription term:
// Utility function to change the prices with a multiplier (number)
function get_price_multiplier($var_product) {
switch($var_product) {
case 111:
// Annual
return 12;
break;
case 222:
// Semiannual
return 6;
break;
case 333:
// Month
return 1;
break;
default:
return 1;
break;
}
}
add_filter('woocommerce_product_variation_get_price', 'custom_price', 99, 2 );
function custom_price( $price, $product ) {
$var_product = $product->get_id();
return $price / get_price_multiplier($var_product);
}
This worked but when a product is added to the cart, the price is not the regular price but the modified price from the function above.
Based on "Set programmatically product sale price and cart item prices in Woocommerce 3" answer code, I have been able to fix that:
add_action( 'woocommerce_before_calculate_totals', 'set_cart_item_sale_price', 20, 1 );
function set_cart_item_sale_price( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// Iterate through each cart item
foreach( $cart->get_cart() as $cart_item ) {
$regular_price = $cart_item['data']->get_regular_price();
$variation_id = $cart_item['variation_id'];
$cart_item['data']->set_price( $regular_price * get_price_multiplier($variation_id) );
}
}
This reset the cart price and it seems to work although one of the prices is off by a penny.
Is this all convoluted and prone to problems or is it a solid way to achieve what I am after: to show the price per subscription term on the shop page?
You are doing it in the right way… There is 2 ways (the last is the best one):
Note: The calculation and the price change is not needed when the multiplier is equal to 1.
1) First alternative: Improved code version that is very similar to yours.
Here is my commented code:
// Utility function that increase conditionally the variation price with a multiplier (int)
function get_variation_calculated_price( $variation_id, $price, $multiplier = true ) {
switch( $variation_id ) {
case 111: // Annual
$rate = 12;
break;
case 222: // Semi-annual
$rate = 6;
break;
default: // Month (and others)
$rate = 1;
break;
}
// Return calculated price (or false when multiplier is 1, as calculation is not needed)
return $rate !== 1 ? ( $multiplier ? $price * $rate : $price / $rate ) : false;
}
// Change variations calculated prices
add_filter('woocommerce_product_variation_get_price', 'custom_price', 99, 2 );
function custom_price( $price, $variation ) {
if( $new_price = get_variation_calculated_price( $variation->get_id(), $price, false ) )
return $new_price;
return $price;
}
// Customizing cart item prices
add_action( 'woocommerce_before_calculate_totals', 'set_cart_item_sale_price', 20, 1 );
function set_cart_item_sale_price( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
// Required since Woocommerce version 3.2 for cart items properties changes
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// Loop through cart items
foreach( $cart->get_cart() as $cart_item ) {
// Only for variations
if( $cart_item['variation_id'] > 0 ) {
if( $new_price = get_variation_calculated_price( $cart_item['variation_id'], $cart_item['data']->get_price() ) ) {
$cart_item['data']->set_price( $new_price );
}
}
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
2) Second Alternative: much more accurate and lighter, restricting the product price avoiding price change on cart, checkout and backend.
Note: You will need to avoid displaying Cross-sells products in cart page (as the prices will not be changed like in shop page).
So the code will be more light, compact and efficient:
// Utility function that increase conditionally the variation price with a multiplier (int)
function get_variation_calculated_price( $variation_id, $price ) {
switch( $variation_id ) {
case 111: // Annual
$rate = 12;
break;
case 939: // Semi-annual
$rate = 6;
break;
default: // Month (and others)
$rate = 1;
break;
}
// Return calculated price (or false when multiplier is 1, as calculation is not needed)
return $rate !== 1 ? $price / $rate : false;
}
// Change variations calculated prices
add_filter('woocommerce_product_variation_get_price', 'custom_price', 99, 2 );
function custom_price( $price, $variation ) {
// Not in cart, checkout and admin
if( is_cart() || is_checkout() || is_admin() )
return $price;
if( $new_price = get_variation_calculated_price( $variation->get_id(), $price ) )
return $new_price;
return $price;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
I need a way to achieve the following: If Free Shipping is available, AND the order is being shipped to a specific zone, hide all other shipping methods.
I found this snippet:
function 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', 'hide_shipping_when_free_is_available', 100 );
How would I add a conditional into it to only apply it to orders going to one zone?
The following code will hide all other shipping methods when free shipping is available for a specific Zone (you will define in the function the targeted zone ID or Zone name):
add_filter( 'woocommerce_package_rates', 'free_shipping_hide_others_by_zone', 100, 2 );
function free_shipping_hide_others_by_zone( $rates, $package ) {
// HERE define your shipping zone ID OR the shipping zone name
$defined_zone_id = '';
$defined_zone_name = 'Europe';
// Get The current WC_Shipping_Zone Object
$zone = WC_Shipping_Zones::get_zone_matching_package( $package );
$zone_id = $zone->get_id(); // The zone ID
$zone_name = $zone->get_zone_name(); // The zone name
$free = array(); // Initializing
// Loop through shipping rates
foreach ( $rates as $rate_id => $rate ) {
if ( 'free_shipping' === $rate->method_id && ( $zone_id == $defined_zone_id || $zone_name == $defined_zone_name ) ) {
$free[ $rate_id ] = $rate;
break;
}
}
return ! empty( $free ) ? $free : $rates;
}
Code goes in function.php file of the active child theme (or active theme). Tested and work.
/**
* 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 );
I need to remove my UPS shipping methods on the cart and checkout pages after the cart weight surpasses 150lbs. This is along the lines of what I'm thinking...
function is_it_over_onefifty($available_methods){
global $woocommerce;
if ($woocommerce->cart->cart_contents_weight > 150){
//diable shipping
unset( $available_methods['ups'] );
}
else{
//do nothing
}
return $available_methods;
}
add_filter( 'woocommerce_available_shipping_methods', 'is_it_over_onefifty', 10, 1);
add_filter( 'woocommerce_package_rates', 'define_default_shipping_method', 10, 2 );
function define_default_shipping_method( $rates, $package ) {
// Only unset rates if free_shipping is available
if ( isset( $rates['free_shipping:8'] ) ) {
unset( $rates['flat_rate:4'] );
}
return $rates;
}
I had started with a comment, but it became too large.
Below is some solid example code that will get you going where you need to. Note the idea is taken from here: http://www.bolderelements.net/support/knowledgebase/hide-shipping-method-based-on-weight/
add_filter( 'woocommerce_package_rates', 'hide_shipping_weight_based', 10, 2 );
function hide_shipping_weight_based( $rates, $package ) {
// if you don't know what the rates are, you can uncomment below to see all the rates that are available
// var_dump( $rates );
// Set weight variable
$cart_weight = 0;
// Shipping rate to be excluded
$shipping_type = 'ups_';
// Calculate cart's total
foreach( WC()->cart->cart_contents as $key => $value) {
$cart_weight += $value['data']->weight * $value['quantity'];
}
// only if the weight is over 150lbs find / remove specific carrier
if( $cart_weight > 150 ) {
// loop through all of the available rates
foreach( $rates AS $id => $data ) {
// if the rate id starts with "ups_"
if ( 0 === stripos( $id, $shipping_type ) ) {
unset( $rates[ $id ] ); // remove it
}
}
}
return $rates;
}