0

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.

LoicTheAztec
  • 229,944
  • 23
  • 356
  • 399
Mario83
  • 143
  • 15

1 Answers1

1

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:

enter image description here


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.

LoicTheAztec
  • 229,944
  • 23
  • 356
  • 399
  • Thank you for your help. This does indeed add a discount, however, it applies the discount to the sum of all shipping classes in a cart. It seems like defining a shipping class in the code `$shipping_class = 'large';` doesn't have any impact on the totals. How is that possible? – Mario83 Oct 20 '18 at 14:32
  • That is the weird part because I already had the shipping classes exactly as you suggested in the screenshots. 'Large' = $200; 'Small' = $150; Shipping per Class. But at the checkout, the shipping cost is $175 which is 50% discount of both classes summed up; 200+150 / 2 = 175. Looking at the code you provided, I also can't see where could be the problem. Any other ideas? Thank you so much! – Mario83 Oct 20 '18 at 15:43
  • @Mario83 For me it's working perfectly with the provided settings: Each items shipping class cost is added and makes a correct total cost… So there is something else that is making trouble in your installation and settings. – LoicTheAztec Oct 20 '18 at 16:10
  • I wish I could say the same. I tested this on 2 local and one live environment with basic WP installations - no luck. It keeps discounting the sum instead of a single shipping class. I'll dive into this deeper tomorrow and I'll share my progress and possible solution. – Mario83 Oct 20 '18 at 17:01
  • @Mario You don't need any code, just the settings, entering the discounted cost directly in your 'large' shipping class. So if 'smal' = $150 … 'large' (discounted) = $100 (50% of $200)… if both are in cart you will have 150 + 100 = $250. – LoicTheAztec Oct 20 '18 at 17:24
  • yep, that works indeed! However, this is only a piece of a more complex solution I need to implement. If I may share, I need this behavior: The product in cart with the most expensive shipping class should be charged 100% . All other shipping classes in user cart should be discounted by 50%. Finally, I have 5 products with unique shipping classes that should never be discounted, even if they are not the highest class in the cart. So my idea was to find a way to set a discount for any shipping class and work my way from there. Hope it makes sense. – Mario83 Oct 22 '18 at 07:54
  • Hi Loic. My editing feedback on this answer is that using quote blocks for anything other than quotes is semantically incorrect - there is a strong parallel in misusing a
    as a general highlighter. I am not confident that appealing it is a good use of a moderator's time, but please note my disagreement.
    – halfer Nov 01 '20 at 20:13
  • I tend to recommend against adding edit notes in reverse chronological order, too - asking readers to read the last part first is probably confusing. The only way I can see this being the better approach is if the original answer is rewritten as an alternative approach, or even a footnote - perhaps in such cases it is not important to make it clear the post has been edited at all. After all, there is an edit history that readers can see if they wish. – halfer Nov 01 '20 at 20:13
  • It absolutely is a reason for downvoting. To be fair, I was in two minds about my downvote, and tried to retract it, but the system froze it in. You have extremely high rep, so I suggest you just weather it as the natural consequence of throwing away patient and careful editing work without discussion. Needless to say, editors cannot ask before making edits - that would be entirely unscalable. – halfer Nov 01 '20 at 21:30
  • As I am fond of saying, my doors are open to civil feedback - I am always able to justify my edits, and have done so here, at length. If you disagree with my reasoning, then I am happy to learn why. In general, if someone edits your work, it's worth pinging them first before rolling back - that makes the conversation much easier. – halfer Nov 01 '20 at 21:31