Add a Woocommerce fee based on shipping class and item quantity - wordpress

In Woocommerce I am trying to add a shipping fee if a cart item has a specific shipping class assigned to the related product. I would like this shipping fee to be multiplied by the cart item quantity…
I have this working when a product is added to the cart and the quantity is increased and the additional shipping fee is increased also. However if I add another product with the same shipping class and increase the quantity the additional fee does not increase.
This is my code:
// Add additional fees based on shipping class
function woocommerce_fee_based_on_shipping_class( $cart_object ) {
global $woocommerce;
// Setup an array of shipping classes which correspond to those created in Woocommerce
$shippingclass_dry_ice_array = array( 'dry-ice-shipping' );
$dry_ice_shipping_fee = 70;
// then we loop through the cart, checking the shipping classes
foreach ( $cart_object->cart_contents as $key => $value ) {
$shipping_class = get_the_terms( $value['product_id'], 'product_shipping_class' );
$quantity = $value['quantity'];
if ( isset( $shipping_class[0]->slug ) && in_array( $shipping_class[0]->slug, $shippingclass_dry_ice_array ) ) {
$woocommerce->cart->add_fee( __('Dry Ice Shipping Fee', 'woocommerce'), $quantity * $dry_ice_shipping_fee ); // each of these adds the appropriate fee
}
}
}
add_action( 'woocommerce_cart_calculate_fees', 'woocommerce_fee_based_on_shipping_class' ); // make it all happen when Woocommerce tallies up the fees
How can I make it work for additional cart items too?

Your code is a bit outdated and there is some mistakes. To add a fee based on product shipping class and cart item quantity use the following:
// Add a fee based on shipping class and cart item quantity
add_action( 'woocommerce_cart_calculate_fees', 'shipping_class_and_item_quantity_fee', 10, 1 );
function shipping_class_and_item_quantity_fee( $cart ) {
## -------------- YOUR SETTINGS BELOW ------------ ##
$shipping_class = 'dry-ice-shipping'; // Targeted Shipping class slug
$base_fee_rate = 70; // Base rate for the fee
## ----------------------------------------------- ##
$total_quantity = 0; // Initializing
// Loop through cart items
foreach( $cart->get_cart() as $cart_item ) {
// Get the instance of the WC_Product Object
$product = $cart_item['data'];
// Check for product shipping class
if( $product->get_shipping_class() == $shipping_class ) {
$total_quantity += $cart_item['quantity']; // Add item quantity
}
}
if ( $total_quantity > 0 ) {
$fee_text = __('Dry Ice Shipping Fee', 'woocommerce');
$fee_amount = $base_fee_rate * $total_quantity; // Calculate fee amount
// Add the fee
$cart->add_fee( $fee_text, $fee_amount );
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.

If there is one shipping class in cart there should be no fees, but if there is more than one shipping class, then should be additional 1 Eur handling fee and increase for every another shipping class.
Situation 1: Product with shipping class (Warehouse 1) added to cart = No extra fees
Situation 2: Product with shipping class (Warehouse 1) added to cart, Product with another shipping class (Warehouse 2) added to cart = 1x Handling fee added to cart
Subtotals:
Products 2x - 10 Eur
Shipping - Free
Handling fee 1x - 1Eur
Total - 11 Eur
Situation 3: Product with shipping class (Warehouse 1) added to cart, Product with shipping class (Warehouse 2) added to cart, Product with shipping class (Warehouse 3) added to cart = 2x Handling fee added to cart
Subtotals:
Products 3x - 15 Eur
Shipping - Free
Handling fee 2x - 1Eur
Total - 12 Eur

Related

Update cart fee on shipping method change in WooCommerce

I need a way to offer shipping discount up to a maximum of $25. I'm using the Fee API to do this since there are no coupon codes for shipping. Here's my code:
add_action( 'woocommerce_cart_calculate_fees', 'conditional_shipping_discount' );
function conditional_shipping_discount() {
$max_shipping_discount = 25;
$cart_subtotal = WC()->cart->subtotal;
$current_shipping_method = WC()->session->get( 'chosen_shipping_methods' );
$current_shipping_method_cost = WC()->session->get('cart_totals')['shipping_total'];
// If our cart is > $99 AND shipping is not already free
if ( ($cart_subtotal >= 99) && ($current_shipping_method_cost > 0) ) {
// Calculate 25% of cart subtotal
$calculated_shipping_discount = $cart_subtotal * 0.25;
// $shipping_discount = lowest value
$shipping_discount = min( $current_shipping_method_cost, $calculated_shipping_discount, $max_shipping_discount );
WC()->cart->add_fee( 'Shipping Discount', -1 * abs($shipping_discount) );
}
}
This works great for the most part, except when the customer selects a different shipping method. The fee only shows the previously selected shipping discount. This GIF shows what I mean:
The fee is corrected when the page is reloaded, or when the update_checkout event is triggered by changing item quantities or updating the checkout fields.
How can I have the cart fee (shipping discount) to update immediately when the shipping method is selected? Thank you in advance.
As I am checking your function, we always got previous shipping costs if I switch/change shipping methods. there is only one issue with $current_shipping_method_cost.
So if you change 1 line
$current_shipping_method_cost = WC()->session->get('cart_totals')['shipping_total'];
to
$current_shipping_method_cost = WC()->cart->get_shipping_total();
You always getting current/active shipping costs.
full code is :
add_action( 'woocommerce_cart_calculate_fees', 'conditional_shipping_discount' );
function conditional_shipping_discount() {
$max_shipping_discount = 25;
$cart_subtotal = WC()->cart->subtotal;
//$current_shipping_method = WC()->session->get( 'chosen_shipping_methods' );
$current_shipping_method_cost = WC()->cart->get_shipping_total();
// If our cart is > $99 AND shipping is not already free
if ( ($cart_subtotal >= 99) && ($current_shipping_method_cost > 0) )
{
// Calculate 25% of cart subtotal
$calculated_shipping_discount = $cart_subtotal * 0.25;
// $shipping_discount = lowest value
$shipping_discount = min( $current_shipping_method_cost, $calculated_shipping_discount, $max_shipping_discount );
WC()->cart->add_fee( 'Shipping Discount', -1 * abs($shipping_discount) );
}
}

Different shipping classes

I have a website where I need to charge different delivery fees for different products.
The standard delivery fee is $50 per order irrelevant of how many items.
However, I have some bulky orders where I will need to charge $150 for them and up to 2 items. So let's say if you buy 2 bulky items, the delivery charge would be $150 but if you buy 3 it would go up to $300 and so on.
When the client puts items in the cart, they will be charged for bulky items and regular items separately, so if the customer puts 1 bulky item and 1 normal item, they need to be charged $150 + $50 which makes the delivery fee $200 in total.
I tried to create a shipping class for the bulky items and in the settings, I have configured it as below:
I also have this logic in my functions file which takes care of charging in multiples in case of heavy products:
add_filter( 'woocommerce_package_rates', 'different_rates_based_on_quantity_steps', 10, 2 );
function different_rates_based_on_quantity_steps ( $rates, $package ){
$slug_class_shipping = 'test'; // Slug of the shipping class
$step_change = 2; // Steps for item
$item_count = 0;
// Checking in cart items
foreach( WC()->cart->get_cart() as $cart_item ){
// If we find the shipping class
if( $cart_item['data']->get_shipping_class() == $slug_class_shipping ){
$item_count += $cart_item['quantity']; // Cart item count
$rate_operand = ceil( $item_count / $step_change ); // Operand increase each # items here
foreach ( $rates as $rate_key => $rate ){
// Targetting "Flat rate"
if( 'flat_rate' === $rate->method_id ) {
$has_taxes = false;
// Set the new cost
$rates[$rate_key]->cost = $rate->cost * $rate_operand;
// Taxes rate cost (if enabled)
foreach ($rates[$rate_key]->taxes as $key => $tax){
if( $tax > 0 ){
// New tax calculated cost
$taxes[$key] = $tax * $rate_operand;
$has_taxes = true;
}
}
// Set new taxes cost
if( $has_taxes )
$rates[$rate_key]->taxes = $taxes;
}
}
return $rates;
}
break;
}
}
That said, unfortunately, this is not working as expected.
If I add 2 bulky items, this is calculating the delivery fee at $200 (instead of $150), and if I add regular items the delivery fee doesn't change and remains at $200.
If I add 3 bulky items, delivery is totalling $400 (instead of $300) and if I add 5 it is going up to $600 (instead of $450). It is like always adding the base fee in case of bulky items, but not adding it in case of regular ones.
Can someone help me with what I'm doing wrong?

WooCommerce shipping cost calculated by weight of product

I need to organize a system for calculating shipping on a WooCommerce project. Here's example:
If the product weighs less than 19 kilograms shipping cost: 36$
If the product weighs more than 19 kilograms shipping cost: 300$
Plus, I need to create an additional shipping class (free shipping). So that the administrator of
the store can determine which product to shipping for free.
How I tried to solve this problem:
At first in the WooCommerce -> Settings -> Shipping -> Shipping zones I created new shipping zones (Israel – Shipping by Weight) and in this zone I create three different shipping methods:
Orders Below 19kg (flat_rate:21)
Orders Above 20kg (flat_rate:22)
Free Shipping (flat_rate:24)
Then I placed in the functions.php file this chunk of code:
add_filter( 'woocommerce_package_rates', 'custom_tiered_shipping_rates', 9999, 2 );
function custom_tiered_shipping_rates( $rates, $package ) {
if ( WC()->cart->get_cart_contents_weight() < 19 ) {
if ( isset( $rates['flat_rate:21'] ) ) unset( $rates['flat_rate:22'], $rates['flat_rate:24'] );
} elseif ( WC()->cart->get_cart_contents_weight() > 20 ) {
if ( isset( $rates['flat_rate:21'] ) ) unset( $rates['flat_rate:21'], $rates['flat_rate:24'] );
} else {
if ( isset( $rates['flat_rate:21'] ) ) unset( $rates['flat_rate:21'], $rates['flat_rate:22'] );
}
return $rates;
}
The source of code with the details description I got from here: WooCommerce: Shipping by Weight (Without a Plugin!)
And everything seems to work. Only the free shipping method does not work. When I give a product the free_shipping class. The shipping cost is calculated not by the availability of this class, but by the weight of the product.Please help to fix it. I understand that I have confused something in the conditions. But the more I experiment the more I get confused now.
PS: If there is at least one item with paid shipping in the cart, then even if there is an item with free shipping, the shipping cost should be guided by paid shipping.
Try the following, that will check on cart items if there are only items from "Free shipping" shipping class. If it's the case and if "Free shipping" shipping method is enabled, Free shipping method will be set.
On other cases a Flat rate based on weight will be applied.
Be sure to set the correct shipping class slug, in the code below, for "Free shipping" shipping class.
add_filter( 'woocommerce_package_rates', 'custom_tiered_shipping_rates', 9999, 2 );
function custom_tiered_shipping_rates( $rates, $package ) {
// HERE below set the correct shipping class slug for "Free shipping" items.
$free_shipping_class = 'free-shipping';
$free_shipping_only = true;
$non_free_items_weight = 0;
// Check items shipping class for the current shipping package
foreach( $package['contents'] as $cart_item ) {
// For non "Free shipping items" flag them and get the calculated weight.
if( $cart_item['data']->get_shipping_class() !== $free_shipping_class ) {
$free_shipping_only = false;
$non_free_items_weight += $cart_item['data']->get_weight() * $cart_item['quantity'];
}
}
// Free shipping
if ( $free_shipping_only && isset($rates['flat_rate:24']) ) {
unset($rates['flat_rate:21'], $rates['flat_rate:22']);
}
// Other rates (Flat rates)
else {
if ( $non_free_items_weight < 20 && isset($rates['flat_rate:21']) )
unset( $rates['flat_rate:22'], $rates['flat_rate:24'] );
} elseif ( $non_free_items_weight >= 20 && isset($rates['flat_rate:22']) )
unset( $rates['flat_rate:21'], $rates['flat_rate:24'] );
}
}
return $rates;
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
Don't forget to empty your cart to refresh shipping caches.
Now instead of using a "Flat rate" for Free shipping, you should better use WooCommerce "Free shipping" method instead (with no restrictions) and replace flat_rate:24in your code by the correct "Free shipping" method rate ID…

exclude a category from woocommerce discount

The following working code is a discount for cart with minimum 3 items not including 'sale' products Now I need to ignore categoty 'gift' (if it is in the cart) from this discount:
//10% discount at minimum 3 items in cart not for sale items
add_action('woocommerce_cart_calculate_fees' , 'custom_cart_discount', 20, 1);
function custom_cart_discount( $cart ){
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
// Limitations: Only when there is 5 or more non on sale items in cart
$starting_limit = 3;
// Initialising variables
$not_on_sale_subtotal = $discount = $items_count = 0;
// Iterating through each item in cart
foreach( $cart->get_cart() as $cart_item ){
// For cart items is not on sale
if( ! $cart_item['data']->is_on_sale() ){
$not_on_sale_subtotal += (float) $cart_item['line_subtotal'];
$items_count += $cart_item['quantity'];
}
}
// Discount calculation
$discount = $not_on_sale_subtotal * 0.1;
// Applied discount only cart items that are not on sale
if( $discount && $items_count >= $starting_limit )
$cart->add_fee( 'הנחת כמות 10%', -$discount );
I would recommend to work with a different approach. WooCommerce Coupons gives you a full control for what you want to achieve.
First, create a coupon in WC admin, with the 10% discount. Then, use the coupon settings to apply it on the cart or just on specific items/categories. You can see in the coupon limitations how exactly to limit the discount to specific categories (with include / exclude ), or to apply the discount only on items that are not on sale etc...
Finally, all you have to do, is just to ask or encourage the customer to apply the coupon, or automatically apply it yourself behind the scenes with:
WC()->cart->apply_coupon('YouCouponCodeGoesHere');
בהצלחה!

Woocommerce - Extra fee based on product quantity

I found this answer in a earlier post here (below), but I want to know of there is any way to add an extra fee based on quantity if the product quantity changes?
Lets say there is 100 items in one package. (the problem is also that there is not they same amount of item in all packages, some can be 100, some can be 150, 200, or 500)
Example:
1-99 = 1$.
100 = no fee.
101 - 199 1$
200 = no fee
201 - 299 = 1$ and so on..
Total will always be 1$ per product but the total can be more if they order several products that have these breaks. The total can be 4$ if there is 4 products with break-cost.
(Also, not sure where to put the code)
Thank you!
The code I found here: Add additional costs based on quantity in Woocommerce
// Hook before adding fees
add_action('woocommerce_cart_calculate_fees' , 'add_custom_fees');
/**
* Add custom fee on article specifics
* #param WC_Cart $cart
*/
function add_custom_fees( WC_Cart $cart ){
$fees = 0;
foreach( $cart->get_cart() as $item ){
// Check if odds and if it's the right item
if( $item[ 'quantity' ] % 2 == 1 && get_post_meta( $item[ 'product_id' ], 'custom_fee_for_supplier_name', true) ){
// You can also put a custom price in each produt with get_post_meta
$fees += 10;
}
}
if( $fees != 0 ){
// You can customize the descriptions here
$cart->add_fee( 'Custom fee (odds paquets)', $fees);
}
}
The woocommerce_after_cart_item_quantity_update fires right after a quantity is updated. If you modify your function a little bit (to use WC()->cart to access the cart object) you can run the same function on both hooks. I thought it might keep adding additional fees, but in my testing it seems to just recalculate the right cost for the same fee.
add_action('woocommerce_cart_calculate_fees' , 'add_custom_fees');
add_action( 'woocommerce_after_cart_item_quantity_update', 'add_custom_fees' );
/**
* Add custom fee on article specifics
* #param WC_Cart $cart
*/
function add_custom_fees(){
$fees = 0;
foreach( WC()->cart->get_cart() as $item ){
// Check if odds and if it's the right item
if( $item[ 'quantity' ] % 2 == 1 && get_post_meta( $item[ 'product_id' ], '_custom_fee_for_odds', true ) ){
// You can also put a custom price in each produt with get_post_meta
$fees += 10;
}
}
if( $fees > 0 ){
// You can customize the descriptions here
WC()->cart->add_fee( 'Custom fee (odds paquets)', $fees);
}
}

Resources