Shipping rate applied only if a certain coupon is used in WooCommerce - php

I need to create 2000 coupons to sell, but I would like the customers who will use them to always pay for shipping. Currently the threshold for getting free shipping is set above 69€. I tried to use the code below (taken from here: Applied coupons disable Free shipping conditionally in Woocommerce).
It applies to all coupons though, and I'd like to apply it only on coupons with the prefix 'pd'.
add_filter( 'woocommerce_package_rates', 'coupons_removes_free_shipping', 10, 2 );
function coupons_removes_free_shipping( $rates, $package ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return $rates;
$applied_coupons = WC()->cart->get_applied_coupons();
if( sizeof($applied_coupons) > 0 ) {
// Loop through shipping rates
foreach ( $rates as $rate_key => $rate ) {
// Targeting "Free shipping" only
if( 'free_shipping' === $rate->method_id ) {
unset($rates[$rate_key]); // Removing current method
}
}
}
return $rates;
}

Related

Extra fee on WooCommerce checkout page not added to subtotal

I need to add Handling fee in checkout page for some state. For this I am using woocommerce add_fee option. But my problem is on checkout page the handling fee is showing but not add to subtotal. Here is my code
add_action( 'woocommerce_cart_calculate_fees','xa_custom_surcharge' );
function xa_custom_surcharge() {
global $woocommerce;
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
$state= array('MH');
$surcharge = 10;
if ( in_array( WC()->customer->shipping_state, $state ) ) {
$woocommerce->cart->add_fee( 'Additional Charge', $surcharge, true, '' );
}
}
Can anyone please help me.
Your code is outdated since WooCommerce 3:
Properties can't not be accessed anymore on CRUD objects, so you should use instead methods like get_shipping_state() in your case.
global $woocommerce and $woocommerce->cart are outdated & replaced directly by WC()->cart
The WC_Cart Object $cart variable is available in the hooked function as an argument.
The correct code is:
add_action( 'woocommerce_cart_calculate_fees','add_custom_surcharge', 10, 1 );
function add_custom_surcharge( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
$state = array('MH');
$surcharge = 10;
if ( in_array( WC()->customer->get_shipping_state(), $state ) ) {
$cart->add_fee( 'Additional Charge', $surcharge, true );
}
}
Now when using the Fee API, the fee amount is displayed as a total and added to gran total at the end, but NOT to the subtotal:
The subtotal in WooCommerce is made only from cart items subtotals…

Keep highest flat rate shipping cost and local pickup in Woocommerce

I'm trying to show the highest shipping costs in the cart. I've found a nice little snippet for that:
function only_show_most_expensive_shipping_rate( $rates, $package ) {
$most_expensive_method = '';
$new_rates = array();
// Loop through shipping rates
if ( is_array( $rates ) ) {
foreach ( $rates as $key => $rate ) {
// Set variables when the rate is more expensive than the one saved
if ( empty( $most_expensive_method ) || $rate->cost > $most_expensive_method->cost ){
$most_expensive_method = $rate;
}
}
}
// Return the most expensive rate when possible
if ( ! empty( $most_expensive_method ) ){
/**
** Keep local pickup if it's present.
**/
foreach ( $rates as $rate_id => $rate ) {
if ('local_pickup' === $rate->method_id ) {
$new_rates[ $rate_id ] = $rate;
break;
}
}
return array( $most_expensive_method->id => $most_expensive_method );
}
return $rates;
}
add_action('woocommerce_package_rates', 'only_show_most_expensive_shipping_rate', 10, 2);
However this snippet also hides the "local pickup" shipping method.
Why does the above method doesn't work? Right now it only shows the highest shipping class/price and hide all others including the pickup method.
Is it because of the two arrays? I don't see any errors popping up.
Any help greatly appreciated!
The following will keep the highest shipping Flat rate cost and the Local pickup shipping method:
add_action('woocommerce_package_rates', 'keep_highest_flat_rate_cost', 10, 2);
function keep_highest_flat_rate_cost( $rates, $package ) {
$flat_rate_costs = [];
// Loop through shipping methods rates
foreach ( $rates as $key_rate => $rate ) {
// Targeting only "Flat rate" type shipping methods
if ( ! in_array( $rate->method_id, ['local_pickup', 'free_shipping'] ) ) {
// Store the Rate ID keys with corresponding costs in an indexed array
$flat_rate_costs[$key_rate] = $rate->cost;
}
}
// Sorting "Flat rate" costs in DESC order
arsort($flat_rate_costs);
// Remove the highest cost from the array
array_shift($flat_rate_costs);
// Loop through remaining "Flat rate" shipping methods to remove them all
foreach ( $flat_rate_costs as $key_rate => $cost){
unset($rates[$key_rate]);
}
return $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 ensure that the code is already saved on your function.php file.
2) In Shipping settings, enter in a Shipping Zone: Disable any Shipping Method and "save", then re-enable it and "save". You are done.

Shipping cost discount based on a shipping classes in Woocommerce

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.

Shipping options depending on cart weight and shipping class

I am working on a WordPress site that uses WooCommerce. I found this piece of code that allows me to set a freight shipping option based on weight. It worked great until the client also wanted the same freight shipping option if a freight shipping class is selected on a product.
add_filter( 'woocommerce_package_rates', 'bbloomer_woocommerce_tiered_shipping', 10, 2 );
function bbloomer_woocommerce_tiered_shipping( $rates, $package ) {
if ( WC()->cart->cart_contents_weight < 300 ) {
if ( isset( $rates['fedex:PRIORITY_OVERNIGHT'], $rates['fedex:FEDEX_2_DAY'], $rates['fedex:FEDEX_GROUND'] ) );
} else {
if ( isset( $rates['flat_rate:10'] ) ) unset( $rates['fedex:PRIORITY_OVERNIGHT'], $rates['fedex:FEDEX_2_DAY'],$rates['fedex:FEDEX_GROUND'] );
}
return $rates;
}
Then I found another piece of code that could set a shipping class to a shipping option. The code I found was mean to unset shipping options that are available but I want to isset the freight shipping since we unset it via the cart weight. However, I am running into an issue where the freight shipping class can no longer be set to isset without causing major issues. Below is what I tried to do for the freight shipping.
add_filter( 'woocommerce_package_rates', 'freight_shipping_class_only', 10, 2 );
function freight_shipping_class_only( $rates, $package ) {
$shipping_class_target = 193;
$in_cart = false;
foreach( WC()->cart->cart_contents as $key => $values ) {
if( $values[ 'data' ]->get_shipping_class_id() == $shipping_class_target ) {
$in_cart = true;
break;
}
}
if( $in_cart ) {
unset( $rates['fedex:PRIORITY_OVERNIGHT'], $rates['fedex:FEDEX_2_DAY'],$rates['fedex:FEDEX_GROUND'] ); // shipping method with ID (to find it, see screenshot below)
isset( $rates['flat_rate:10'] );
}
return $rates;
}
Is there a way to set both the cart weight and the freight shipping class in one function? It would be ideal since I want them both to do the exact same thing by unset all FedEx shipping option and isset the freight shipping option.
Let me know if you need me to be clearer or if you have any tips.
Thanks!
As you are using 2 times the same hook, you can simply merge your functions in one.
Now with php [isset()][1] you need to use it in as a condition in an IF statement to check if a variable is set (exist), but it has no effect alone outside your IF statement in your function.
So in your first function the following doesn't has any effect:
if ( isset( $rates['fedex:PRIORITY_OVERNIGHT'], $rates['fedex:FEDEX_2_DAY'], $rates['fedex:FEDEX_GROUND'] ) );
So you can try this compact and efficient code instead:
add_filter( 'woocommerce_package_rates', 'freight_shipping_class_only', 20, 2 );
function freight_shipping_class_only( $rates, $package ) {
// HERE the targeted shipping class ID
$targeted_shipping_class = 193;
// Loop through cart items and checking
$found = false;
foreach( $package['contents'] as $item ) {
if( $item['data']->get_shipping_class_id() == $targeted_shipping_class ){
$found = true;
break;
}
}
// The condition
if ( isset( $rates['flat_rate:10'] ) && ( WC()->cart->get_cart_contents_weight() >= 300 || $found ) ){
unset( $rates['fedex:PRIORITY_OVERNIGHT'], $rates['fedex:FEDEX_2_DAY'], $rates['fedex:FEDEX_GROUND'] );
}
return $rates;
}
Code goes in function.php file of the active child theme (or active theme). It should work.
I found a solution, it isn't exactly what I wanted but it works. What I did was change the cart weight to anything over 300 pounds unset the FedEx shipping options. Then unset FedEx shipping options for the freight shipping class. Which still left the freight shipping option visible on non-freight items, which I hid using CSS.
I am still open for a better way to do this. So if you have suggestions please send them my way.
Below is what I ended up doing.
add_filter( 'woocommerce_package_rates', 'bbloomer_woocommerce_tiered_shipping', 10, 2 );
function bbloomer_woocommerce_tiered_shipping( $rates, $package ) {
if ( WC()->cart->cart_contents_weight < 300 ) {
if ( isset( $rates['fedex:PRIORITY_OVERNIGHT'], $rates['fedex:FEDEX_2_DAY'], $rates['fedex:FEDEX_GROUND'] ) );
} else {
if ( isset( $rates['flat_rate:10'] ) ) unset( $rates['fedex:PRIORITY_OVERNIGHT'], $rates['fedex:FEDEX_2_DAY'],$rates['fedex:FEDEX_GROUND'] );
}
return $rates;
}
/** Freight Shipping Class Only **/
add_filter( 'woocommerce_package_rates', 'freight_shipping_class_only', 10, 2 );
function freight_shipping_class_only( $rates, $package ) {
$shipping_class_target = 193;
$in_cart = false;
foreach( WC()->cart->cart_contents as $key => $values ) {
if( $values[ 'data' ]->get_shipping_class_id() == $shipping_class_target ) {
$in_cart = true;
break;
}
}
if( $in_cart ) {
unset( $rates['fedex:PRIORITY_OVERNIGHT'], $rates['fedex:FEDEX_2_DAY'],$rates['fedex:FEDEX_GROUND'] );
}
return $rates;
}

Show only free shipping when over x amount issue in Woocommerce

I am trying to hide "flat-rate" if free shipping is available.
add_filter( 'woocommerce_package_rates', 'hide_other_shipping_when_free_is_available', 100, 2 );
function hide_other_shipping_when_free_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;
}
I found the following snippet and added to my functions.php, but it does not hide the flat rate shipping option.
This code still perfectly works for woocommerce versions 2.6+ (so also 3.2.x)
The missing part is once you have saved your code in your function.php file, you need to refresh the shipping cached data:
Disable, save and enable, save related shipping methods for the current shipping zone, in woocommerce shipping settings.

Categories