phpwordpresswoocommercediscountfee

WooCommerce discount: add negative fees without taxes


I am adding a WooCommerce cart fee like so :

  add_action('woocommerce_cart_calculate_fees', function($cart) { 
      $cart->add_fee(__('Popust za vjernost'), -10, 0);
  });

I thought adding a 0 as the third argument would set the tax to 0 but WooCommerce still adds tax to the fee.

How can I set the tax to 0?


Solution

  • Overwriting cores files is not a solution, as it can affect other processes and you will lose your changes each time you will update WooCommerce.

    Using WC_Cart add_fee() method with a negative amount, is a tweak to add a discount. In that case, the "taxable" optional 3rd argument doesn't work, and taxes are always applied. This has been reported to WooCommerce many times and the response was that WC_Cart add_fee() method was not made for discounts.

    1) Reminder about WC_Cart add_fee() method 4 available arguments:

    /**
     * Add additional fee to the cart.
     *
     * This method should be called on a callback attached to the
     * woocommerce_cart_calculate_fees action during cart/checkout. Fees do not
     * persist.
     *
     * @uses WC_Cart_Fees::add_fee
     * @param string $name      Unique name for the fee. Multiple fees of the same name cannot be added.
     * @param float  $amount    Fee amount (do not enter negative amounts).
     * @param bool   $taxable   Is the fee taxable? (default: false).
     * @param string $tax_class The tax class for the fee if taxable. A blank string is standard tax class. (default: '').
     */
    WC()->cart->add_fee( $name, $amount, $taxable, $tax_class );
    

    2) Make the "taxable" 3rd argument working with negative amounts:

    The following code will enable "taxable" when using a negative amount, allowing to add a discount without taxes when the "taxable" argument is set to false:

    // Adjust cart total amount
    add_filter( 'woocommerce_cart_get_total', 'filter_cart_get_total', 10, 1 );
    function filter_cart_get_total( $total ) {
        $tax_amount = 0;
    
        foreach( WC()->cart->get_fees() as $fee ) {
            if( ! $fee->taxable && $fee->tax < 0 ) {
                $tax_amount -= $fee->tax;
            }
        }
    
        if( $tax_amount != 0 ) {
            $total += $tax_amount;
        }
        return $total;
    }
    
    // Adjust Fee taxes (array of tax totals)
    add_filter( 'woocommerce_cart_get_fee_taxes', 'filter_cart_get_fee_taxes', 10, 1 );
    function filter_cart_get_fee_taxes( $fee_taxes ) {
        $fee_taxes = array();
        
        foreach( WC()->cart->get_fees() as $fee ) {
            if( $fee->taxable ) {
                foreach( $fee->tax_data as $tax_key => $tax_amount ) {
                    if( isset($fee_taxes[$tax_key]) ) {
                        $fee_taxes[$tax_key] += $tax_amount;
                    } else {
                        $fee_taxes[$tax_key] = $tax_amount;
                    }
                }
            }
        }
        return $fee_taxes;
    }
    
    // Displayed fees: Remove taxes from non taxable fees with negative amount
    add_filter( 'woocommerce_cart_totals_fee_html', 'filter_cart_totals_fee_html', 10, 2 );
    function filter_cart_totals_fee_html( $fee_html, $fee ) {
        if( ! $fee->taxable && $fee->tax < 0 ) {
            return wc_price( $fee->total );
        }
        return $fee_html;
    }
    
    // Adjust Order fee item(s) for negative non taxable fees
    add_action( 'woocommerce_checkout_create_order_fee_item', 'alter_checkout_create_order_fee_item', 10, 4 );
    function alter_checkout_create_order_fee_item( $item, $fee_key, $fee, $order ) {
        if ( ! $fee->taxable && $fee->tax < 0 ) {
            $item->set_taxes(['total' => []]);
            $item->set_total_tax(0);
        }
    }
    

    Code goes in functions.php file of your child theme (or in a plugin).


    3) Testing using Add_fee():

    Below, we add a discount without taxes using a negative amount:

    add_action( 'woocommerce_cart_calculate_fees', 'add_discount_without_tax' );
    function add_discount_without_tax( $cart ) { 
        if ( is_admin() && ! defined( 'DOING_AJAX' ) )
            return;
        
        // Add a NON taxable DISCOUNT (negative amount, "taxable" argument set to "false")
        $cart->add_fee(__('Discount (no tax)'), -10, false); 
    };
    

    This time it works, taxes are not applied and when order is submitted, same thing.

    Code goes in functions.php file of your child theme (or in a plugin).