I'm looking for a way to make a custom reduction on shipping cost, based on cart total percentage, flat rate and quantity per type of product.
For 6 product : flat shipping rate 20€
Total cart is 120€ | Shipping cost = flat rate cost - 10% total cart | 20 - 12 = 8€
Total cart is 180€ | Shipping cost = flat rate cost - 10% total cart | 20 - 18 = 2€
Total cart is 220€ | Shipping cost = flat rate cost - 10% total cart | 20 - 22 = 0€
For 12 product : flat shipping rate 30€
Total cart is 120€ | Shipping cost = flat rate cost - 10% total cart | 30 - 12 = 18€
Total cart is 180€ | Shipping cost = flat rate cost - 10% total cart | 30 - 18 = 12€
Total cart is 220€ | Shipping cost = flat rate cost - 10% total cart | 30 - 22 = 8€
How can this be done?
This can be done without a plugin…
1) You will need first in WooCommerce shipping settings for each shipping zone to set an amount of 1 for "Flat rate" method:
This amount will be changed in our function to 20€ from 1 to 11 items and 30€for 12 and more items. This amount will be decreased by the 10% cart total amount.
2) Then using a custom function hooked in woocommerce_package_rates filter hook, you will be able to make a discount on shipping cost based on cart item count and on cart total.
Here is that code:
add_filter( 'woocommerce_package_rates', 'custom_package_rates', 10, 2 );
function custom_package_rates( $rates, $packages ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) return;
// Get some cart data and set variable values
$cart_count = WC()->cart->get_cart_contents_count();
$cart_total = WC()->cart->cart_contents_total;
$cart_10_percent = $cart_total * 0.1;
$flat_rate_value = 20; // Default "Flat rate" value
foreach($rates as $rate_key => $rate_values ) {
$method_id = $rate_values->method_id;
$rate_id = $rate_values->id;
if( $method_id == 'flat_rate' ){
if( $cart_count < 6 )
$cart_10_percent = 0; // No percent discount
elseif( $cart_count >= 12 )
$flat_rate_value = 30; // "Flat rate" value for 12 or more items
$rate_cost = $flat_rate_value > $cart_10_percent ? $flat_rate_value - $cart_10_percent : 0;
// Set the new calculated rate cost
$rates[$rate_id]->cost = number_format( $rates[$rate_id]->cost * $rate_cost, 2 );
// Taxes rate cost (if enabled)
$taxes = array();
foreach ($rates[$rate_id]->taxes as $key => $tax){
if( $tax > 0 ){ // set the new tax cost
// set the discounted tax cost
$taxes[$key] = number_format( $tax * $rate_cost, 2 );
}
}
$rates[$rate_id]->taxes = $taxes;
}
}
return $rates;
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
Tested on WooCommerce 3 and works.
Refresh the shipping caches (needed sometimes):
1) First empty your cart.
2) This code is already saved on your function.php file.
3) Go in a shipping zone settings and disable one "flat rate" (for example) and "save". Then re-enable that "flat rate" and "save". You are done and you can test it.
Related
There is the built in option for woocommerce shipping to set a fee for quantity of products in cart. There is also option for fee per shipping class. But I have thousands of products and need to charge a fee for each product(each product not qty of products).
For example 2 product "apples", and 23 product "oranges" in cart. I need to charge flat fee of $10 for any amount of apples and $10 for any amount of oranges. I dont seem to find solution to this in any of the available plugins. They all do fee per quantity but not this.
To get a cost by line item in cart, it requires the following:
1) In WooCommerce Settings > Shipping: set a cost of 10 for your "Flat rate" shipping methods (and save).
2) Add to functions.php file of your active child theme (or active theme), this code:
add_filter( 'woocommerce_package_rates', 'shipping_cost_based_on_number_of_items', 10, 2 );
function shipping_cost_based_on_number_of_items( $rates, $package ) {
$numer_of_items = (int) sizeof($package['contents']);
// Loop through shipping rates
foreach ( $rates as $rate_key => $rate ){
// Targetting "Flat rate" shipping method
if( 'flat_rate' === $rate->method_id ) {
$has_taxes = false;
// Set the new cost
$rates[$rate_key]->cost = $rate->cost * $numer_of_items;
// Taxes rate cost (if enabled)
foreach ($rates[$rate_key]->taxes as $key => $tax){
if( $tax > 0 ){
// New tax calculated cost
$taxes[$key] = $tax * $numer_of_items;
$has_taxes = true;
}
}
// Set new taxes cost
if( $has_taxes )
$rates[$rate_key]->taxes = $taxes;
}
}
return $rates;
}
Refresh the shipping caches: (required)
This code is already saved on your active theme's function.php file.
The cart is empty
In a shipping zone settings, disable / save any shipping method, then enable back / save.
Tested and work.
In Woocommerce, I would like to have a function that I can include within my theme that adds shipping rate based on price and weight.
if price is above 20USD shipping is free, below Shipping cost : 3USD
if weight is above 10kg shipping fee is 2USD extra
Based on "Shipping calculated on the items weight and cart amount" answer thread, I tried something like the beneath code:
//Adding a custom Shipping Fee to cart based conditionally on weight and cart amount
add_action('woocommerce_cart_calculate_fees', 'custom_conditional_shipping_fee', 10, 1);
function custom_conditional_shipping_fee( $cart_object ){
#### SETTINGS ####
// Your targeted "heavy" product weight
$target_weight = 1;
// Your targeted cart amount
$target_cart_amount = 20;
// Price by Kg;
$price_kg = 2;
// Amount set in 'flat rate' shipping method;
$flat_rate_price;
// Initializing variables
$fee = 0;
$calculated_weight = 0;
// For cart SUBTOTAL amount EXCLUDING TAXES
WC()->cart->subtotal_ex_tax >= $target_cart_amount ? $passed = true : $passed = false ;
// For cart SUBTOTAL amount INCLUDING TAXES (replace by this):
// WC()->cart->subtotal >= $target_cart_amount ? $passed = true : $passed = false ;
// Iterating through each cart items
foreach( $cart_object->get_cart() as $cart_item ){
// Item id ($product ID or variation ID)
if( $cart_item['variation_id'] > 0)
$item_id = $cart_item['variation_id'];
else
$item_id = $cart_item['product_id'];
// Getting the product weight
$product_weight = get_post_meta( $item_id , '_weight', true);
// Line item weight
$line_item_weight = $cart_item['quantity'] * $product_weight;
// When cart amount is up to 1kg, Adding weight of heavy items
if($passed && $product_weight < $target_weight)
$calculated_weight += $line_item_weight;
}
#### Making the fee calculation ####
// Cart is up to 250 with heavy items
if ( $passed && $calculated_weight != 0 ) {
// Fee is based on cumulated weight of heavy items
$fee = ($calculated_weight * $price_kg) - $flat_rate_price;
}
// Cart is below 250
elseif ( !$passed ) {
// Fee is based on cart total weight
$fee = ($cart_object->get_cart_contents_weight( ) * $price_kg) - $flat_rate_price;
}
#### APPLYING THE CALCULATED FEE ####
// When cart is below 250 or when there is heavy items
if ($fee > 0){
// Rounding the fee
$fee = round($fee);
// This shipping fee is taxable (You can have it not taxable changing last argument to false)
$cart_object->add_fee( __('Shipping weight fee', 'woocommerce'), $fee, true);
}
}
Edit:
And at the same time I want it to show this immediately on the cart page. Now it is showing "enter address to view shipping options". Basically just look at total of cart and show either a rate or free shipping based on the rules described for weight and price.
woocommerce_package_rates is the right filter to customize the shipping rate.
You can achieve this by following way.
Step-1: Create two shipping method, Free shipping and Flat rate with coast 3$
Step-2: Copy and paste below code snippet into functions.php
Config the flat rate and free shipping properly on the snippet.
add_filter( 'woocommerce_package_rates', 'modify_shipping_rate', 15, 2 );
function modify_shipping_rate( $available_shipping_methods, $package ){
global $woocmmerce;
$total_weight = WC()->cart->cart_contents_weight;
$total_coast = WC()->cart->get_cart_contents_total();
if( $total_coast >= 20 ){
unset($available_shipping_methods['flat_rate:1']); //Remove flat rate for coat abobe 20$
}elseif( $total_weight > 10 ){
unset($available_shipping_methods['free_shipping:1']); // remove free shipping for below 20$
$available_shipping_methods['flat_rate:1']->cost += 2; // add 2$ if weight exceeds 10KG
}else{
unset($available_shipping_methods['free_shipping:1']); // remove free shipping for below 20$
}
return $available_shipping_methods;
}
Use below snippet to change the default cart message.
add_filter( 'woocommerce_cart_no_shipping_available_html', 'change_msg_no_available_shipping_methods', 10, 1 );
add_filter( 'woocommerce_no_shipping_available_html', 'change_msg_no_available_shipping_methods', 10, 1 );
function change_msg_no_available_shipping_methods( $default_msg ) {
$custom_msg = "Enter address to view shipping options";
if( empty( $custom_msg ) ) {
return $default_msg;
}
return $custom_msg;
}
The following code isn't based on a custom fee, but on shipping methods customizations.It requires to set in shipping settings, for each shipping zone:
A "Flat Rate" with a defined cost ($3 for you)
A "Free Shipping" with no requirements (no restrictions).
The code will handle flat rate calculation cost based on the weight and also the tax calculations.
The code will work for any shipping zone without needing to define in the code the shipping method IDs.
Here is the code:
add_filter( 'woocommerce_package_rates', 'filter_package_rates_callback', 10, 2 );
function filter_package_rates_callback( $rates, $package ) {
## -------- Settings -------- ##
$targeted_total = 20; // The targeted cart amount
$weight_threshold = 10; // The cart weight threshold
$extra_for_10kg = 2; // 10 Kg addition extra cost;
$total_weight = WC()->cart->get_cart_contents_weight();
$cart_subtotal = WC()->cart->get_subtotal(); // Excluding taxes
// Set shipping costs based on weight
foreach ( $rates as $rate_key => $rate ){
$has_taxes = false;
if( $cart_subtotal < $targeted_total || $total_weight >= $weight_threshold ){
// Remove Free shipping Method
if( 'free_shipping' === $rate->method_id ) {
unset( $rates[$rate_key] );
}
// Flat rate calculation cost when 10 kg weight is reached
if( 'flat_rate' === $rate->method_id && $total_weight >= $weight_threshold ) {
// The default rate cost (set in the shipping method)
$default_cost = $rate->cost;
// The new calculated cost (up to 10 kg)
$new_cost = $default_cost + $extra_for_10kg;
// Tax rate conversion (for tax calculations)
$tax_rate_converion = $new_cost / $default_cost;
// Set the new cost
$rates[$rate_key]->cost = $new_cost;
// TAXES RATE COST (if enabled)
foreach ($rates[$rate_key]->taxes as $key => $tax){
if( $tax > 0 ){
// New tax calculated cost
$taxes[$key] = $tax * $tax_rate_converion;
$has_taxes = true;
}
}
// Set new taxes cost
if( $has_taxes )
$rates[$rate_key]->taxes = $taxes;
}
} else {
// Remove Flat Rate methods (keeping Free Shipping Method only)
if( 'flat_rate' === $rate->method_id ) {
unset( $rates[$rate_key] );
}
}
}
return $rates;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
Refresh the shipping caches: (required)
1) This code is already saved on your function.php file.
2) In a shipping zone settings, disable / save any shipping method, then enable back / save.
You are done and you can test it.
Displaying shipping methods directly (Regarding your question edit)
The provided information in your question is not enough to see how this can be managed.
If you are selling in one country, and you have a unique shipping zone, you can force the country for unlogged customers to get the shipping methods displayed, using the following:
add_action( 'template_redirect', 'allow_display_shipping_methods' );
function allow_display_shipping_methods() {
// HERE define the targeted country code
$country_code = 'GB';
// Set the shipping country if it doesn't exist
if( ! WC()->customer->get_shipping_country() )
WC()->customer->set_shipping_country('GB');
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
Now this is another question, in your initial question and should be asked as a new question.
Related answers:
Set a custom shipping rate cost calculated on cart total weight in WooCommerce
Free shipping depending on weight and on minimal cart amount
Shipping cost based on cart total weight in Woocommerce 3
Hide and unhide a specific shipping methods based on shipping class in WooCommerce
In my Woocommerce Webshop I do have different Products. I would like to have shipping cost calculated on total cart items weight:
from 0 to 6 Kilos the cost is 5 €,
from 6 to 12 Kilos the cost is 9 €
Actually if I have a Product which is 1 Kilo the shipping cost is 5 €, but if I have 10 items of this product in my basket, the shipping fee is still 5 € (and it should be 9 € instead).
How can I have a progressive shipping cost based on cart total weight?
Try the following function which "Flat Rate" cost will be changed based on cart total weight.
Using "Flate rate" shipping method, you will need to set a reference shipping cost with a simple initial cost instead of any formula. It can be for example 1. This cost will be replaced by my answer code, dynamically based on cart total weight.
You may have to "Enable debug mode" in general shipping settings under "Shipping options" tab, to disable temporarily shipping caches.
add_filter('woocommerce_package_rates', 'shipping_cost_based_on_weight', 12, 2);
function shipping_cost_based_on_weight( $rates, $package ){
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return $rates;
// HERE define the differents costs
$cost1 = 5; // Up to 6 Kg
$cost2 = 9; // Above 6 Kg and below 12 kg
$cost3 = 9; // Above 12 kg
// The cart total Weight
$total_weight = WC()->cart->get_cart_contents_weight();
// Loop through the shipping taxes array
foreach ( $rates as $rate_key => $rate ){
$has_taxes = false;
// Targetting "flat rate"
if( 'flat_rate' === $rate->method_id ){
// Get the initial cost
$initial_cost = $new_cost = $rates[$rate_key]->cost;
// Calculate new cost
if( $total_weight <= 6 ) { // Below 6 Kg
$new_cost = $cost1;
}
elseif( $total_weight > 6 && $total_weight <= 12 ) { // Between 6 and 12 Kg
$new_cost = $cost2;
}
else { // Above 12 Kg
$new_cost = $cost3;
}
// Set the new cost
$rates[$rate_key]->cost = $new_cost;
// Taxes rate cost (if enabled)
$taxes = [];
// Loop through the shipping taxes array (as they can be many)
foreach ($rates[$rate_key]->taxes as $key => $tax){
if( $rates[$rate_key]->taxes[$key] > 0 ){
// Get the initial tax cost
$initial_tax_cost = $new_tax_cost = $rates[$rate_key]->taxes[$key];
// Get the tax rate conversion
$tax_rate = $initial_tax_cost / $initial_cost;
// Set the new tax cost
$taxes[$key] = $new_cost * $tax_rate;
$has_taxes = true; // Enabling tax
}
}
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.
Once tested, don't forget to disable "Enable debug mode" option in shipping settings.
I'm trying to specify 2 different Flat rates shipping methods costs without using a plugin:
If there is a product in the cart from only one Vendor, the Flat rate shipping cost needs to be £19.
If there are multiple products in the cart from more than one Vendor, the Flat rate shipping cost needs to be £39.
I have tried various plugins, but they focus on shipping rates based on size, weight, quantity, location, category, but not Attributes or Terms.
I have an Attribute named Vendor with 8 Terms. Each Term is a different Vendor/supplier.
Here's the type of PHP logic I would like to implement:
if product attribute term quantity = 1
then flat rate = £19
else
if product attribute term quantity > 1
then flat rate = £39
How can I change this "Flat rate" shipping method cost when there is more than 1 attribute Vendor terms in cart?
This process needs 2 steps: some code and some settings…
1) CODE - You can use a custom function hooked in woocommerce_package_rates filter hook, targeting the "Flat rate" shipping method when cart items are from more than 1 vendor:
add_filter( 'woocommerce_package_rates', 'custom_flat_rate_cost_calculation', 10, 2 );
function custom_flat_rate_cost_calculation( $rates, $package )
{
// SET BELOW your attribute slug… always begins by "pa_"
$attribute_slug = 'pa_vendor'; // (like for "Color" attribute the slug is "pa_color")
// Iterating through each cart item to get the number of different vendors
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
// The attribute value for the current cart item
$attr_value = $cart_item[ 'data' ]->get_attribute( $attribute_slug );
// We store the values in an array: Each different value will be stored only one time
$attribute_values[ $attr_value ] = $attr_value;
}
// We count the "different" attribute values stored
$count = count($attribute_values);
// Iterating through each shipping rate
foreach($rates as $rate_key => $rate_values){
$method_id = $rate_values->method_id;
$rate_id = $rate_values->id;
// Targeting "Flat Rate" shipping method
if ( 'flat_rate' === $method_id ) {
// For more than 1 vendor (count)
if( $count > 1 ){
// Get the original rate cost
$orig_cost = $rates[$rate_id]->cost;
// Calculate the new rate cost
$new_cost = $orig_cost + 20; // 19 + 20 = 39
// Set the new rate cost
$rates[$rate_id]->cost = $new_cost;
// Calculate the conversion rate (for below taxes)
$conversion_rate = $new_cost / $orig_cost;
// Taxes rate cost (if enabled)
foreach ($rates[$rate_id]->taxes as $key => $tax){
if( $rates[$rate_id]->taxes[$key] > 0 ){
$new_tax_cost = number_format( $rates[$rate_id]->taxes[$key]*$conversion_rate, 2 );
$rates[$rate_id]->taxes[$key] = $new_tax_cost; // set the cost
}
}
}
}
}
return $rates;
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
This code is tested with woocommerce version 3+ and works
2) SETTINGS - Once the above code has been saved in the function.php file of your active theme, you will need to set (for all your shipping zones) the "Flat rate" shipping method cost to 19 (£19) (and save).
IMPORTANT: To refresh Shipping method caches you will need to disable "flat rate" then save, and enable back "flat rate" then save.
Now This should work for you as expected.
In Woocommerce I have installed a shipping plug in but it calculates shipping per item in cart instead of per order total.
What I need is to calculate the shipping cost this way:
- First kg is $5
- And $1 by kilo for all others.
Is it possible to do it?
With the plugin if there is 2 items in cart of 1 kg each, it calculates the shipping as 2 x $5 = $10
Instead what I should need is: $5 (first kilo) + $1 (additional kilo) = $6 (for 2 kg).
I have no response from plugin developer, so looking for work around.
UPDATED: You don't need any plugin (so you can remove it).
To get a shipping rate calculated on total cart weight, with:
an initial amount of $5 (for the first kilo)
a variable rate of $1 for each additional kilo
Do the following:
1) Settings in WooCommerce:
For all your "Flat rate" shipping method (in each shipping zone), set a cost of 1 (amount by kilo).
So the cost
(Once done disable/save and enable/save to refresh woocommerce transient cache)
2) Custom code (as the shipping unit cost is $1 by kilo, we add 4 kilos to total cart weight in the calculations to set the correct cost):
add_filter( 'woocommerce_package_rates', 'custom_delivery_flat_rate_cost_calculation', 10, 2 );
function custom_delivery_flat_rate_cost_calculation( $rates, $package )
{
// The total cart items weight
$cart_weight = WC()->cart->get_cart_contents_weight();
foreach($rates as $rate_key => $rate_values){
$method_id = $rate_values->method_id;
$rate_id = $rate_values->id;
if ( 'flat_rate' === $method_id ) {
## SHIPPING COSTS AND TAXES CALCULATIONS ##
$rates[$rate_id]->cost = $rates[$rate_id]->cost*($cart_weight+4);
foreach ($rates[$rate_id]->taxes as $key => $tax){
if( $rates[$rate_id]->taxes[$key] > 0 ){
$tax_cost = number_format( $rates[$rate_id]->taxes[$key]*($cart_weight+4));
$rates[$rate_id]->taxes[$key] = $tax_cost;
}
}
}
}
return $rates;
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
This code is tested and works on woocommerce versions 2.6.x and 3+