Customize Tax amount in "woocommerce_package_rates" hook

12.2k views Asked by At

I recently tried to modify all my shipping rates with hook to apply discount.

Here's my code :

add_filter( 'woocommerce_package_rates', 'woocommerce_package_rates' );
function woocommerce_package_rates( $rates ) {
    $user_id = get_current_user_id();
    if ( ! wc_memberships_is_user_active_member( $user_id, 'silver' ) ) { return $rates; }
    $discount_amount = 30; // 30%

    foreach($rates as $key => $rate ) {
        $rates[$key]->cost = $rates[$key]->cost - ( $rates[$key]->cost * ( $discount_amount/100 ) );
    }

    return $rates;
}

But one more step is the tax ! I got wrong tax.
For example I have my shipping rate who cost 3$. With the discount, it's now 2,10$.

I buy one item for 2$ and the shipping 2.10$. I got 1$ for the tax (as the 3$ shipping cost. look like he doesn't take the changes) and normally it's 0.82$.

What do I need to get the correct tax calculation?

3

There are 3 answers

6
LoicTheAztec On BEST ANSWER

Updated: add tax cost calculation, and removed some typos.

There is some little errors on your code and you have missed the tax calculation discount. I have revisited your code a bit, you should try this:

add_filter( 'woocommerce_package_rates', 'conditional_shipping_discount', 10, 2 );
function conditional_shipping_discount( $rates, $packages ) {

    $user_id = get_current_user_id();
    if ( ! wc_memberships_is_user_active_member( $user_id, 'silver' ) ) return $rates;

    $percent = 30; // 30%
    $discount = 1 - ($percent / 100);

    foreach($rates as $rate_key => $rate ) {
        // Get original cost
        $original_cost = $rates[$rate_id]->cost;
        // Calculate the discounted rate cost
        $new_cost = $original_cost * $discount;
        // Set the discounted rate cost
        $rates[$rate_key]->cost = number_format($new_cost, 2);
        // calculate the conversion rate (for taxes)
        $conversion_rate = $new_cost / $original_cost;

        // Taxes rate cost (if enabled)
        $taxes = array();
        foreach ($rate->taxes as $key => $tax){
            if( $tax > 0 ){ // set the new tax cost
                // set the new line tax cost in the taxes array
                $taxes[$key] = number_format( $tax * $conversion_rate, 2 );
            }
        }
        // Set the new taxes costs
        $rates[$rate_key]->taxes = $taxes
    }
    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.

You should need to refresh the shipping caches:

  1. First this code is already saved on your function.php file.
  2. In Shipping settings, enter in a Shipping Zone and disable a Shipping Method and "save". Then re-enable that Shipping Method and "save". You are done.
0
rafark On

The problem with the answers above is that you're calculating the tax based on the calculations already made for the original cost. What if, for example, a plugin is calculating the tax based on the shipping cost? For example, 10% if $100 or above, 20% if $200 or above, 0% if less than $100.

If the original cost was $150 and you're applying a $60 discount, the discounted cost would be $90 and no tax would apply in the aforementioned scenario. So we need a way to modify the cost before the tax is calculated. This way we don't have to recalculate the tax ourselves and we reduce bugs like the example in the first paragraph.

For one of my plugins I'm using the woocommerce_shipping_method_add_rate_args filter, which is called by \WC_Shipping_Method::add_rate(). This method is called everytime a \WC_Shipping_Method instance adds a rate.

This filter will accept an array as its first argument, which is the rate data. This array has a 'cost' entry which is what we want to modify. Keep in mind that this can be a scalar (apparently, a numeric string) or an array. Just check if it's an array when dealing with this:

add_filter('woocommerce_shipping_method_add_rate_args', function(array $rateArguments) : array {
     // Total up the cost. Taken from the WooCommerce source code. woocommerce/includes/abstracts/abstract-wc-shipping-method.php
     $totalCost = is_array( $rateArguments['cost'] ) ? array_sum( $rateArguments['cost'] ) : $rateArguments['cost'];

     $rateArguments['cost'] = 'here the final cost';
            
     return $rateArguments; 
});
0
Jankyz On

Below code @LoicTheAztec without mistakes:

add_filter( 'woocommerce_package_rates', 'conditional_shipping_discount', 10, 2 );
    function conditional_shipping_discount( $rates, $packages ) {

    $user_id = get_current_user_id();
    if ( ! wc_memberships_is_user_active_member( $user_id, 'silver' ) ) return $rates;

    $percent = 30; // 30%
    $discount = 1 - ($percent / 100);

    foreach($rates as $rate_key => $rate_values ) {
        // Get original cost
        $original_cost = $rates[$rate_key]->cost;
        // Calculate the discounted rate cost
        $new_cost = $original_cost * $discount;
        // Set the discounted rate cost
        $rates[$rate_key]->cost = number_format($new_cost, 2);
        // calculate the conversion rate (for taxes)
        $conversion_rate = $new_cost / $original_cost;

        // Taxes rate cost (if enabled)
        $taxes = array();
        foreach ($rates[$rate_key]->taxes as $key => $tax){
            if( $tax > 0 ){ // set the new tax cost
                // set the new line tax cost in the taxes array
                $taxes[$key] = number_format( $tax * $conversion_rate, 2 );
            }
        }
        // Set the new taxes costs
        $rates[$rate_key]->taxes = $taxes;
    }
    return $rates;
}