Based on this answer (code below), I successfully can hide flat rate from particular product category and local delivery option is available. This is working perfect.
The problem: local pickup option is NOT available for that particular category.
How can I make the local pickup option available to this special category?
This is the code that I use:
function custom_shipping_methods( $rates ){
// Define/replace here your correct category slug (!)
$cat_slug = 'your_category_slug';
$prod_cat = false;
// Going through each item in cart to see if there is anyone of your category
foreach ( WC()->cart->get_cart() as $values ) {
$item = $values['data'];
if ( has_term( $cat_slug, 'product_cat', $item->id ) )
$prod_cat = true;
}
$rates_arr = array();
if ( $prod_cat ) {
foreach($rates as $key => $rate) {
if ('free_shipping' === $rate->method_id || 'local_pickup' === $rate->method_id || 'local_delivery' === $rate->method_id) {
$rates_arr[ $rate_id ] = $rate;
break;
}
}
}
return !empty( $rates_arr ) ? $rates_arr : $rates;
}
add_filter( 'woocommerce_package_rates', 'custom_shipping_methods', 100);
One more thing: Is it possible to show local delivery and local pickup for that special category depending on location?
Currently in my store local Pickup or Delivery is setup only for one location.
Advice: ONLY for WooCommerce version 2.6.x (added compatibility for WC 3+)
After many tests… You will need to change 2 little things in your code:
add_filter( 'woocommerce_package_rates', 'custom_shipping_methods', 100, 2 );
function custom_shipping_methods( $rates, $package ){
// Define/replace here your correct category slug (!)
$cat_slug = 'posters';
$prod_cat = false;
// Going through each item in cart to see if there is anyone of your category
foreach ( WC()->cart->get_cart() as $values ) {
$product = $values['data'];
// compatibility with WC +3
$product_id = method_exists( $product, 'get_id' ) ? $product->get_id() : $product->id;
if ( has_term( $cat_slug, 'product_cat', $product_id ) )
$prod_cat = true;
}
$rates_arr = array();
if ( $prod_cat ) {
foreach($rates as $rate_id => $rate) { // <== There was a mistake here
if ('free_shipping' === $rate->method_id || 'local_pickup' === $rate->method_id || 'local_delivery' === $rate->method_id) {
$rates_arr[ $rate_id ] = $rate;
// break; // <========= Removed this to avoid stoping the loop
}
}
}
return !empty( $rates_arr ) ? $rates_arr : $rates;
}
There was 2 mistakes:
One in the foreach loop with a bad variable name that I have replace it.
Removed break; avoiding stoping the foreach loop when one condition match.
This code goes on function.php file of your active child theme or theme.
This code is tested and fully functional (it will work if you have correctly set your shipping zones).
You will need to refresh shipping cached data: disable, save and enable, save related shipping methods for the current shipping zone, in woocommerce shipping settings.
References:
Hide other shipping methods when FREE SHIPPING is available
WooCommerce - Hide other shipping methods when FREE SHIPPING is available
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 trying to add code like the one below into functions.php file. The overall function is to check products in cart and their custom fields (post_meta) called auto_delivery_default.
If its certain text in the custom field then display free shipping only, if all other text then show all other shipping methods.
Here's what I've gotten so far but I'm overlooking something making it not function right;
function show_free_ship_to_autodelivery ( $autodelivery_rate ) {
$autodelivery_free = array();
foreach( WC()->cart->get_cart() as $cart_item ){
$product = $cart_item['data'];
$product_id = $product->get_id(); // get the product ID
$autodelivery = get_post_meta( $product->get_id(), 'auto_delivery_default', true );
if( $autodelivery == "90 Days" ) {
$autodeliveryfree = array();
foreach ( $rates as $rate_id => $rate ) {
if ( 'free_shipping' === $rate->method_id ) {
$autodelivery_free[ $rate_id ] = $rate;
break;
}
}
return ! empty( $autodelivery_free ) ? $autodelivery_free : $autodelivery_rate;
}
}
}
add_filter( 'woocommerce_package_rates', 'show_free_ship_to_autodelivery', 10);
There is some errors and mistakes in your code… Instead, try the following that will hide other shipping methods when free shipping is available and when a cart item with a custom field auto_delivery_default has a value of 90 Days:
add_filter( 'woocommerce_package_rates', 'show_only_free_shipping_for_autodelivery', 100, 2 );
function show_only_free_shipping_for_autodelivery ( $rates, $package ) {
// Loop through cart items
foreach( $package['contents'] as $cart_item ){
if( $cart_item['data']->get_meta('auto_delivery_default') == '90 Days' ) {
$found = true;
break; // Stop the loop
}
}
if( ! ( isset($found) && $found ) )
return $rates; // Exit
$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.
I am using this official woocommerce hook and snippet to Hide other shipping methods when “Free Shipping” is triggered by a coupon. Which works fine.
Currnently, it just displays FREE SHIPPING. I am trying to add in a text line where it displays FREE SHIPPING to say "-£3.95" in order to let the user know that they have saved money off their shipping.
I have tried adding a dummy "fee" inline to the IF statement, but it wont display.
$cart->add_fee( 'You saved -£3.95');
Code is as follows:
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 );
e.g. like the following example:
There is 2 ways:
1) In your existing code (best way) - It will append your formatted saved price on free shipping:
add_filter( 'woocommerce_package_rates', 'hide_other_shipping_when_free_is_available', 100, 1 );
function hide_other_shipping_when_free_is_available( $rates ) {
// Here set your saved amount
$labeleld_price = -3.95;
$free = array();
foreach ( $rates as $rate_id => $rate ) {
if ( 'free_shipping' === $rate->method_id ) {
$free[ $rate_id ] = $rate;
// Here we append the labbelled saved price formated display
$free[ $rate_id ]->label .= ' (' . strip_tags( wc_price( $labeleld_price ) ) . ')';
break; // stop the loop
}
}
return ! empty( $free ) ? $free : $rates;
}
Code goes in function.php file of your active child theme (or active theme). tested and works.
2) Using settings - Renaming the "Free shipping" label method:
This is an extension of this question: Remove shipping Flat Rate method for particular Category in WooCommerce 2.6 and 3+
Used the Woo Smart Coupons plugin for a Gift Card product. This HAS to be set to Variation, as we have multiple tiers to select from. (this rules out virtual products) The Gift Card has it's own category for distinction. We have two shipping options set up: Flat Rate + Local Pickup. It's pretty silly to have shipping options for a gift card that gets sent to your Inbox, so I used the following snippet found in the link above:
add_filter( 'woocommerce_package_rates', 'conditional_hide_shipping_methods', 100, 2 );
function conditional_hide_shipping_methods( $rates, $package ){
// Define/replace here your correct category slug (!)
$product_category = 'coupons-gift-cards';
$prod_cat = false;
foreach ( WC()->cart->get_cart() as $cart_item ) {
$product_id = $cart_item['product_id'];
if ( has_term( $product_category, 'product_cat', $product_id ) ){
$prod_cat = true;
}
}
$rates_arr = array();
if ( $prod_cat ) {
foreach($rates as $key => $rate) {
if ('free_shipping' === $rate->method_id || 'local_pickup' === $rate->method_id || 'local_delivery' === $rate->method_id) {
$rates_arr[ $rate_id ] = $rate;
break;
}
}
}
return !empty( $rates_arr ) ? $rates_arr : $rates;
}
Works like a charm... until you add a product that ISN'T from that category. If someone decides that they want a Gift Card AND a normal product, then the regular shipping options need to be back in place.
EDIT: The checked answer works perfectly! If you want to change the Pickup Label for items like the above situation so they say something like "Download" instead of "Pickup", then add this line after the IF statement that checks which products are matching the categories
foreach( $rates as $rate_key => $rate ) {
//change local_pickup:1 to your shipping method
if ( 'local_pickup:1' == $rate_key){
//set the text for the label
$rates[$rate_key]->label = __( 'Download', 'woocommerce' );
}
}
Here is the correct way to make it work for a unique and exclusive product category, that will hide Flat rate exclusively if there is no others products remaining to this product category:
add_filter( 'woocommerce_package_rates', 'hide_shipping_flat_rate_conditionaly', 90, 2 );
function hide_shipping_flat_rate_conditionaly( $rates, $package ){
// HERE Define your product category (ID, slug or name or an array of values)
$term = 'coupons-gift-cards';
$others = $found = false;
foreach ( WC()->cart->get_cart() as $cart_item ) {
$product_id = $cart_item['product_id'];
if ( has_term( $term, 'product_cat', $cart_item['product_id'] ) )
$found = true;
else
$others = true;
}
if ( $found && ! $others ) {
foreach($rates as $rate_id => $rate) {
if ('flat_rate' === $rate->method_id )
unset($rates[ $rate_id ]);
}
}
return $rates;
}
Code goes on function.php file of your active child theme (or active theme).
This code is tested and fully functional (it will work if you have correctly set your shipping zones).
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.
With Woocommerce, I would like to hide all shipping methods except "Local pickup" when a defined products category is in cart…
The code below does that for other product types, except variable products:
add_filter( 'woocommerce_package_rates', 'custom_shipping_methods', 100, 2 );
function custom_shipping_methods( $rates, $package ){
// Define/replace here your correct category slug (!)
$cat_slug = 'my_category_slug';
$prod_cat = false;
// Going through each item in cart to see if there is anyone of your category
foreach ( WC()->cart->get_cart() as $values ) {
$product = $values['data'];
// compatibility with WC +3
$product_id = method_exists( $product, 'get_id' ) ? $product->get_id() : $product->id;
if ( has_term( $cat_slug, 'product_cat', $product_id ) )
$prod_cat = true;
}
$rates_arr = array();
if ( $prod_cat ) {
foreach($rates as $rate_id => $rate) { // <== There was a mistake here
if ('local_pickup' === $rate->method_id) {
$rates_arr[ $rate_id ] = $rate;
}
}
}
return !empty( $rates_arr ) ? $rates_arr : $rates;
}
What can I do to make it work for variable products too? Any help is appreciated.
I have revisited your code and here is correct way to make it work for variable products too:
add_filter( 'woocommerce_package_rates', 'product_category_hide_shipping_methods', 90, 2 );
function product_category_hide_shipping_methods( $rates, $package ){
// HERE set your product categories in the array (IDs, slugs or names)
$categories = array( 'clothing');
$found = false;
// Loop through each cart item Checking for the defined product categories
foreach( $package['contents'] as $cart_item ) {
if ( has_term( $categories, 'product_cat', $cart_item['product_id'] ) ){
$found = true;
break;
}
}
$rates_arr = array();
if ( $found ) {
foreach($rates as $rate_id => $rate) {
if ('local_pickup' === $rate->method_id) {
$rates_arr[ $rate_id ] = $rate;
}
}
}
return !empty( $rates_arr ) ? $rates_arr : $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.