0
votes

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.

  1. if price is above 20USD shipping is free, below Shipping cost : 3USD
  2. 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.

enter address to view shipping options

2
You need to give the shipping details settings (shipping zone, shipping methods) … Also if the shop is limited to one country (which country)… If not you will not be able to get the shipping method displayed directly on cart for customers that have not a defined shipping address. The provided information is not enough…LoicTheAztec

2 Answers

1
votes

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;
}
0
votes

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: