I have a number of shipping classes with prices set in multiple shipping zones. Is it possible to detect if ANY product within the cart belongs to a specific shipping class, and if so set the shipping cost to 0 and display a message?
I would like to use this mechanism for products which require special handling and would be shipped via freight. So if a customer's order contains any of these products the shipping quote would be provided manually post-sale.
Thanks for the help.
The following code will set all shipping cost to zero, when a specific defined shipping method is found in cart items and will display a custom notice.
The second hooked function is optional and will display the custom notice in checkout page.
You will have to temporarily "Enable debug mode" in Shipping settings under Shipping options (tab) to disable / clear caching.
In the code below define in each function your shipping class slug and your custom notice:
// Null shipping costs for a specific shipping class and display a message
add_filter('woocommerce_package_rates', 'shipping_class_null_shipping_costs', 10, 2);
function shipping_class_null_shipping_costs( $rates, $package ){
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return $rates;
// HERE set your shipping class slug
$shipping_class_slug = 'extra';
$found = false;
// Loop through cart items and checking for the specific defined shipping class
foreach( $package['contents'] as $cart_item ) {
if( $cart_item['data']->get_shipping_class() == $shipping_class_slug )
$found = true;
}
// Set shipping costs to zero if shipping class is found
if( $found ){
foreach ( $rates as $rate_key => $rate ){
$has_taxes = false;
// Targetting "flat rate" and local pickup
if( 'flat_rate' === $rate->method_id || 'local_pickup' === $rate->method_id ){
// Set the cost to zero
$rates[$rate_key]->cost = 0;
// Taxes rate cost (if enabled)
foreach ($rates[$rate_key]->taxes as $key => $tax){
if( $rates[$rate_key]->taxes[$key] > 0 ){
$has_taxes = true;
// Set taxes cost to zero
$taxes[$key] = 0;
}
}
if( $has_taxes )
$rates[$rate_key]->taxes = $taxes;
}
}
// Clear duplicated notices
wc_clear_notices();
// Add a custom notice to be displayed
wc_add_notice( __('My custom shipping message here.', 'woocommerce'), 'notice' );
}
return $rates;
}
add_action( 'woocommerce_before_checkout_form', 'display_custom_shipping_message_in_checkout' );
function display_custom_shipping_message_in_checkout(){
// HERE set your shipping class slug
$shipping_class_slug = 'extra';
$found = false;
// Loop through cart items and checking for the specific defined shipping class
foreach( WC()->cart->get_cart() as $cart_item ) {
if( $cart_item['data']->get_shipping_class() == $shipping_class_slug )
$found = true;
}
if( $found ){
// Display a custom notice
wc_print_notice( __('My custom shipping message here.', 'woocommerce'), 'notice' );
}
}
Code goes in function.php file of your active child theme (or active theme). It should works.
Don't forget to disable "debug mode" in shipping settings once this has been tested once.
Set up a Shipping Class and Shipping Method for Free Shipping, assign that Class to the product you want to offer free shipping for, and then ensure that Free Shipping Method is set at the default shipping method.
Good Luck!
Related
I've used a Business Bloomer snippet and tweaked it a bit to unset some shipping methods depending on a shipping class of items in the cart.
It works fine with the 4 shipping methods I have tested with but in order to fully work on my website, I have to list all shipping methods values manually and I have A LOT ( 86 to unset in if and 60 in else.)
Therefore, I would like to edit the snipped so I could unset all shipping methods that contains the same term all at once rather than look for each value individually, but don't really know how to.
I have prints for sale and so I’ve set some shipping methods for orders including prints and some for orders whitout. Therefore all my shipping methods for prints have a value ending with “_print” when the others have a value ending with “_classique”, as you can see in the code bellow.
My goal is to disable all shipping methods ending with “_classique” when a product with the “print” shipping class in the cart and vice versa.
So far my code looks like this :
add_filter( 'woocommerce_package_rates', 'businessbloomer_hide_regular_shipping_method', 10, 2 );
function businessbloomer_hide_regular_shipping_method( $rates, $package ) {
$shipping_class_target = 35; // shipping class ID
$in_cart = false;
foreach( WC()->cart->get_cart_contents() as $key => $values ) {
if( $values[ 'data' ]->get_shipping_class_id() == $shipping_class_target ) {
$in_cart = true;
break;
}
}
if( $in_cart ) { // shipping method with value
unset( $rates['wbs:2:d6f790a0_colissimo_sans_signature_classique'] );
unset( $rates['wbs:2:d748dcd4_lettre_suivie_classique'] );
unset( $rates['wbs:2:f1058bc8_colissimo_avec_signature_classique'] );
}
else{
unset( $rates['wbs:2:1cdf4913_colissimo_sans_signature_print'] );
unset( $rates['wbs:2:fghla482_lettre_suivie_print'] );
unset( $rates['wbs:2:g27a1f56_colissimo_avec_signature_print'] );
}
return $rates;
}
Any help on how to achieve this would be very appreciated.
Thank you to anyone who'll take time to read this !
Updated
Using PHP strpos() will allow you to check if a word is contained in a string. Then you will need to make some changes in your code to make it work:
add_filter( 'woocommerce_package_rates', 'show_hide_shipping_methods', 10, 2 );
function show_hide_shipping_methods( $rates, $package ) {
$shipping_class_id = 35; // Targeted shipping class ID
$found = false;
// Loop through cart items for the current package
foreach ( $package['contents'] as $cart_item ) {
if( $cart_item['data']->get_shipping_class_id() == $shipping_class_id ) {
$found = true;
break;
}
}
// Loop through shipping rates
foreach ( $rates as $rate_key => $rate ) {
if ( $found && strpos($rate_key, '_classique') !== false ) {
unset($rates[$rate_key]);
}
elseif (! $found && strpos($rate_key, '_print') !== false ) {
unset($rates[$rate_key]);
}
}
return $rates;
}
Code goes in functions.php file of the active child theme (or active theme). It should works.
Clearing shipping caches:
You will need to empty your cart, to clear cached shipping data
Or In shipping settings, you can disable / save any shipping method, then enable back / save.
Related:
How can I check if a word is contained in another string using PHP?
Hide shipping methods for specific shipping class in WooCommerce
Filter Shipping method based on shipping class in Woocommerce 3
From Disable specific shipping method if a cart item uses a specific shipping class ID answer code, how if there is another item in cart which does not have that shipping class ID and want to show flat_rate:2 again according to product shipping class?
You will use the following instead:
add_filter( 'woocommerce_package_rates', 'custom_hide_shipping_methods', 10, 2 );
function custom_hide_shipping_methods( $rates, $package ) {
$found = $others = false; // Initializing
$shipping_class_id = 513; // <== ID OF YOUR SHIPPING_CLASS
$shipping_rate_id = 'flat_rate:2'; // <== Targeted shipping rate ID
// Checking cart items for current package
foreach( $package['contents'] as $key => $cart_item ) {
$product = $cart_item['data']; // The WC_Product Object
if( $product->get_shipping_class_id() == $shipping_class_id ) {
$found = true;
} else {
$others = true;
}
}
if( $found && ! $others && isset($rates[$shipping_rate_id]) ) {
unset($rates[$shipping_rate_id]); // Removing specific shipping method
}
return $rates;
}
Code goes in functions.php file of your active child theme (or active theme). 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.
In WooCommerce cart page I am using the code below to check if specific shipping method is enabled, but the condition on the if statement is always true, so it doesn't work.
global $woocommerce;
$shipping_methods = $woocommerce->shipping->load_shipping_methods();
if ( $shipping_methods['free_shipping']->enabled == "yes" ) {
}
How can I Check on cart page if specific shipping method is enabled?
The method enabled() will be yes if the "free_shipping" shipping method is enabled in WooCommerce shipping settings for the current shipping zone, that's why you always get "yes"…
To check that for WC_Cart object, you will use instead WC_Session related data. Here is an example for "Free shipping" shipping method:
// Get available shipping methods
$shipping_for_package_0 = WC()->session->get('shipping_for_package_0');
$found = false;
if( isset($shipping_for_package_0['rates']) && ! empty($shipping_for_package_0['rates']) ) {
// Loop through available shipping methods rate data
foreach( $shipping_for_package_0['rates'] as $rate ) {
// Targeting "Free Shipping"
if( 'free_shipping' === $rate->method_id ) {
$found = true;
break;
}
}
}
// Display availability for "Free shipping"
echo $found ? 'Free shipping is available.' : 'Free shipping is not available.';
But it will not get refreshed on cart page if the customer make changes on cart page.
I'm trying to apply a discount to one shipping class for products currently in a cart. This is applied on the checkout view.
In Woocommerce backend, the option is set to charge each shipping class individually. Also, I use only one shipping method named "flat rate".
Based on Override all shipping costs for a specific shipping class in Woocommerce, the following code that should apply the discount:
add_filter('woocommerce_package_rates', 'shipping_class_null_shipping_costs', 10, 2);
function shipping_class_null_shipping_costs( $rates, $package ){
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return $rates;
$shipping_class_slug = 'large'; // Your shipping class slug
$found = false;
// Loop through cart items and checking for the specific defined shipping class
foreach( $package['contents'] as $cart_item ) {
if( $cart_item['data']->get_shipping_class() == $shipping_class_slug )
$found = true;
}
$percentage = 50; // 50%
$subtotal = WC()->cart->get_cart_shipping_total();
// Set shipping costs to 50% discount if shipping class is found
if( $found ){
foreach ( $rates as $rate_key => $rate ){
$has_taxes = false;
// Targetting "flat rate"
if( 'flat_rate' === $rate->method_id ){
$rates[$rate_key]->cost = $subtotal;
}
}
}
return $rates;
}
But whatever I try, the calculated shipping result is $0.
What am I doing wrong here and what would be the correct way to apply a discount to shipping class?
Thank you.
Update (Just about settings)
To add a discount only for "large" shipping class on "Flat rate" shipping method, You will have to:
Set the discounted price directly on your shipping method cost.
Enable option "Per class: Charge shipping for each shipping class individually"
Like:
Original answer:
The following code will set the shipping cost of 50% for "Flat rate" shipping method, when a specific defined shipping method is found in cart items.
Testing: Temporary "Enable debug mode" in Shipping settings under Shipping options tab...
Shipping "Flat rate" settings: Your shipping classes costs should be defined.
In the code below define in each function your shipping class slug and your custom notice:
add_filter('woocommerce_package_rates', 'shipping_costs_discounted_based_on_shipping_class', 10, 2);
function shipping_costs_discounted_based_on_shipping_class( $rates, $package ){
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return $rates;
// Your settings below
$shipping_class = 'large'; // <=== Shipping class slug
$percentage = 50; // <=== Discount percentage
$discount_rate = $percentage / 100;
$is_found = false;
// Loop through cart items and checking for the specific defined shipping class
foreach( $package['contents'] as $cart_item ) {
if( $cart_item['data']->get_shipping_class() == $shipping_class )
$is_found = true;
}
// Set shipping costs to 50% if shipping class is found
if( $is_found ){
foreach ( $rates as $rate_key => $rate ){
$has_taxes = false;
// Targeting "flat rate"
if( 'flat_rate' === $rate->method_id ){
$rates[$rate_key]->cost = $rate->cost * $discount_rate;
// Taxes rate cost (if enabled)
foreach ($rates[$rate_key]->taxes as $key => $tax){
if( $tax > 0 ){
$has_taxes = true;
$taxes[$key] = $tax * $discount_rate;
}
}
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.
Don't forget to disable "debug mode" in shipping settings once this has been tested once.
I have been searching for code to filter out any shipping methods other than local pick up, on checkout, when a product that has a specific shipping class selected (Only pickup, ex.) is in the cart (among other products).
I only found code that was outdated and that doesn't work on WC3+.
Here is the way to filter out any shipping methods other than local pick up, when a product that has a specific shipping class enabled:
add_filter( 'woocommerce_package_rates', 'custom_shipping_rates', 100, 2 );
function custom_shipping_rates( $rates, $package ) {
$shipping_class = 64; // HERE set the shipping class ID
$found = false;
// Loop through cart items Checking for the defined shipping method
foreach( $package['contents'] as $cart_item ) {
if ( $cart_item['data']->get_shipping_class_id() == $shipping_class ){
$found = true;
break;
}
}
if ( ! $found ) return $rates; // If not found we exit
// Loop through shipping methods
foreach( $rates as $rate_key => $rate ) {
// all other shipping methods other than "Local Pickup"
if ( 'local_pickup' !== $rate->method_id && $found ){
// Your code here
}
}
return $rates;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works
Then in StackOverFlow searching for recent answer with woocommerce_package_rates will allow you to finish your code.