Here's my code below which works but it's not yet fully completed. Currently it just checks if an added product in the cart has tech shipping class then apply a static $5 discount. However, what I want to achieve is to add an additional requirement.
Most of my products on my website are with NO set shipping class and the rest are set with 'tech shipping class'. I want to do the following:
The discount of $5 should only be given if the customer add one or more (it doesn't matter) of products with no shipping class assigned to them. Discount should NOT be given if there are only tech shipping class items in the cart. If there are products with no set class and tech shipping class regardless of the quantity of the items then apply a static $5 discount.
It seems unclear to me how to check if cart has other items and more specifically items with NO set class + tech shipping class then to apply that discount..
Any help is greatly appreciated. Thanks!
add_filter( 'woocommerce_package_rates', 'adjustment_in_rates_of_product_with_shipping_class', 12, 2 );
function adjustment_in_rates_of_product_with_shipping_class( $available_shipping_methods, $package ) {
// Shipping class slug to be eligible for a discount
$shipping_class = array(
'tech',
);
// Discount
$discount = 5;
// Enter the Shipping Method Value
$shipping_services = array(
'flat_rate:2',
);
$shipping_class_exists = false;
foreach(WC()->cart->get_cart_contents() as $key => $values) {
if ( in_array($values['data']->get_shipping_class() , $shipping_class) ) {
$shipping_class_exists = true;
break;
}
}
if ($shipping_class_exists) {
foreach ($available_shipping_methods as $key => $value) {
if ( in_array($value->get_id() , $shipping_services) ) {
$available_shipping_methods[$key]->cost -= $discount;
}
}
}
return $available_shipping_methods;
}
Replace this
if ( in_array($values['data']->get_shipping_class() , $shipping_class) )
For
if ( $values['data']->get_shipping_class() == '' )
I think that with this modification your code will check if there is at least one product in the cart without shipping class and in this case apply the discount.
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++;
Real example: a customer had bought the following products in the cart:
product A, weight: 0.2kg, Qty: 2, shipping class : FREE shipping
product B, weight: 0.6kg, Qty: 3, shipping class: weight based shipping
product C, weight: 0.8kg, Qty: 1, shipping class: weight based shipping
My client is using a table rate shipping plugin, it can only calculate the shipping cost by using total cart contents weight, in this case it is 3.0kg.
But the real chargeable weight is only 2.6kg...
Had searched around and could not find any function to calculate subtotal of cart items weight for specific shipping class , so have just drafted the following function, but it seems not working. Can someone help to improve this function?
// calculate cart weight for certain shipping class only
if (! function_exists('get_cart_shipping_class_weight')) {
function get_cart_shipping_class_weight() {
$weight = 0;
foreach ( $this->get_cart() as $cart_item_key => $values ) {
if ( $value['data']->get_shipping_class() == 'shipping-from-XX' ) {
if ( $values['data']->has_weight() ) {
$weight += (float) $values['data']->get_weight() * $values['quantity'];
}
}
return apply_filters( 'woocommerce_cart_contents_weight', $weight );
}
}
}
// end of calculate cart weight for certain shipping class
Update (the typo error has been corrected).
To make it work you need to use the dedicated woocommerce_cart_contents_weight filter hook in a custom hooked function this way:
add_filter( 'woocommerce_cart_contents_weight', 'custom_cart_contents_weight', 10, 1 );
function custom_cart_contents_weight( $weight ) {
$weight = 0;
foreach ( WC()->cart->get_cart() as $cart_item ) {
$product = $cart_item['data'];
if ( $product->get_shipping_class() == 'shipping-from-XX' && $product->has_weight() ) {
$weight += (float) $product->get_weight() * $cart_item['quantity'];
}
}
return $weight;
}
Code goes in function.php file of your active child theme (or active theme). It should works now.
Thanks #Loic TheAztec, just have to remove the extra "->", perhaps your typo error, then everything works perfectly, the credit shall go to #LoicTheAztec! So the correct code should be as follows:
//Alter calculated cart items total weight for a specific shipping class
add_filter( 'woocommerce_cart_contents_weight', 'custom_cart_contents_weight', 10, 1 );
function custom_cart_contents_weight( $weight ) {
$weight = 0;
foreach ( WC()->cart->get_cart() as $cart_item ) {
$product = $cart_item['data'];
if ( $product->get_shipping_class() == 'shipping-from-xx' && $product->has_weight() ) {
// just remember to change this above shipping class name 'shipping-from-xx' to the one you want, use shipping slug
$weight += (float) $product->get_weight() * $cart_item['quantity'];
}
}
return $weight;
}
I'm using woocommerce as a platform for booking products for delivery, People pay only for the shipping costs but not for the products.
How do I get the total amount to reflect ONLY the shipping costs and not the product prices?
I know I can set the individual product price to zero but I prefer to leave the prices there so that people can sort the items by price. Hopefully it's just a simple hack.
Thanks!
Add shipping role and assign them to all.
Or create a function that remove the product cost from the total in function.php file.
This might help:
add_action( 'woocommerce_before_calculate_totals', 'add_custom_price' );
function add_custom_price( $cart_object ) {
$custom_price = $shipping; // Add the shipping cost
foreach ( $cart_object->cart_contents as $key => $value ) {
$value['data']->price = $custom_price;
}
}
A client has a very odd request: they would like for a set of coupon codes to add a fee to an the order. Is this possible?
The problem they are trying to solve is this:
Currently the store offers free shipping to the lower 48 United States, for which I have shipping classes and table rates properly configured. However, the coupons the customer has sold on a daily deal site are supposed to remove the free shipping benefit and add a fixed shipping fee.
How can achieve this?
Yes you can do it with that snippet code (for a set of coupons):
function coupon_add_cart_fee( $bookable_total = 0 ) {
$has_coupon = false;
// Set here your specials coupons slugs (one by line - last one have no coma)
$coupon_codes = array (
'your_coupon_code_slug1',
'your_coupon_code_slug2',
'your_coupon_code_slug3'
);
// Set here your fee amount or make fees calculation (see the links in reference)
$fee = 20;
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
// checking if that "special" coupon is applied to customer cart
foreach ($coupon_codes as $coupon_code) {
if ( WC()->cart->has_discount( $coupon_code ) ) {
// If yes apply the fee to the cart
if ( !$has_coupon ) {
$has_coupon = true;
break;
}
}
}
if ( $has_coupon ) {
WC()->cart->add_fee( 'Fees: ', $fee, false );
}
}
add_action( 'woocommerce_cart_calculate_fees','coupon_add_cart_fee' );
This code is tested and it works. It goes on function.php file of your active child theme or theme.
TAX OPTIONS with add_fee() method
IMPORTANT: The fact that TAX is working or not with add_fee() method depends first of your tax settings in woocommerce.
Class WC_Cart add_fee() method, adds additional fee to the cart.
add_fee( string $name, float $amount, boolean $taxable = false, string $tax_class = '' )> <!-- language: lang-css -->
Parameters:
$name Unique name for the fee. Multiple fees of the same name cannot be added.
$amount Fee amount.
$taxable (default: false) Is the fee taxable?
$tax_class (default: '') The tax class for the fee if taxable. A blank string is standard tax class.
Reference:
Add tax free fees to WooCommerce cart programmatically
Class WC_Cart - add_fee() method
Class WC_Cart - has_discount() method
The following code will allow free shipping for a specific product:
function wcs_my_free_shipping( $is_available ) {
global $woocommerce;
// set the product ids that are eligible
$eligible = array( '360' );
// get cart contents
$cart_items = $woocommerce->cart->get_cart();
// loop through the items looking for one in the eligible array
foreach ( $cart_items as $key => $item ) {
if( in_array( $item['product_id'], $eligible ) ) {
return true;
}
}
// nothing found return the default value
return $is_available;
}
add_filter( 'woocommerce_shipping_free_shipping_is_available', 'wcs_my_free_shipping', 20 );
What I would like to do is allow free shipping not for a product, but for a specific street and zip code combination in the delivery address. I found out how to check this for a logged-in user, but can't seem to find the right variables that have this information at checkout. Any help would be greatly appreciated.
Thanks in advance,
-Ben
There are Shipping zones available in Woocommerce already. For each specific zone you can set the shipping method either "Flat rate" or "Free Shipping". You can check it under Woocommerce->Settings. Find Shipping tab.
Screenshot for shipping tab
Yes you can, there is an extra parameter you can pass into this hook names $package.
for eg.
add_filter( 'woocommerce_shipping_free_shipping_is_available', 'wcs_my_free_shipping', 20,2 );
function wcs_my_free_shipping( $is_available, $package){
//Your code for free shipping for a selected address found in $package
return $is_available
}
$package contains the address you entered so you can use this to apply free shipping for selected zip codes or streets