phpwordpresswoocommercedecimalhook-woocommerce

Use 2 decimals for discount amount in Woocommerce with 0 decimals settings


I am using 0 decimals under wc-settings&tab=general which is working fine for my usecase. But now adding a discount for a specific payment method which is set to 10 percent at the moment.

For an 12 usd item it rounds the discount value to 1 - which reuslt in an a total of 11 instead of 10.8 due to the 0 decimals settings mentioned before.

The closest i came up with was the following filter, but resultet in shipping cost going from 23 to 23.23 usd too. Any idea how to apply the 2 decimals only to the discount amount without interfering elsewhere?

// Change woo settings from 0 at the moment in backend to 2 decimals for wallet payment
add_filter( 'wc_get_price_decimals', 'custom_price_decimals_for_wallet_s0dgPHq2',10,1);
function custom_price_decimals_for_wallet_s0dgPHq2( $decimals ) {
    
    // Check if we are on the front-end and WooCommerce session is available
    if ( !is_admin() && WC()->session && isset(WC()->session->chosen_payment_method) ) {
        // Only change decimal places if the chosen payment method is wallet
        if ( WC()->session->chosen_payment_method === 'wallet' ) {                  
        
            // Set to 2 decimals for wallet payments
            $decimals = 2;
            return $decimals;
        }           
        // Return the default value for other cases
        return $decimals;
    }   
}

Here my discount code:

add_filter( 'woocommerce_cart_calculate_fees', 'discount_based_on_payment_method_e643e', 10, 1 );
function discount_based_on_payment_method_e643e( $cart ) {
    if ( is_admin() && ! defined( 'DOING_AJAX' ) )
        return;

    if ( ! ( is_checkout() && ! is_wc_endpoint_url() ) )
        return;
    
    $targeted_payment_method = 'wallet'; // Here define the desired payment method

    global $abc_options;
    $wallet_pay_discount_amount = $abc_options['wallet_payment_discount_amount_value'];
    $wallet_pay_discount_amount_formated = $wallet_pay_discount_amount / 100; // results in 0.10 for 10% and so on
    
    if( WC()->session->get('chosen_payment_method') === $targeted_payment_method ) {
        $cart_subtotal = WC()->cart->get_subtotal();
        $discount = $cart_subtotal * $wallet_pay_discount_amount_formated;

        $fee_description = '~ ' . $wallet_pay_discount_amount . '% ' . __('Discount Wallet Payment', 'nm-ffk');

        
        $cart->add_fee($fee_description, -$discount);
    }
}   

Solution

  • To change only the fee displayed amount decimals in WooCommerce checkout page, use the following code replacement:

    // Add a negative fee (discount) in Checkout page
    add_action( 'woocommerce_cart_calculate_fees', 'discount_based_on_payment_method_e643e', 10, 1 ); // Action hook!
    function discount_based_on_payment_method_e643e( $cart ) {
        if ( is_admin() && ! defined( 'DOING_AJAX' ) )
            return;
    
        if ( ! ( is_checkout() && ! is_wc_endpoint_url() ) )
            return;
        
        $payment_id = 'wallet'; // The targeted payment method ID
        
        if( WC()->session->get('chosen_payment_method') === $payment_id ) {
            global $abc_options;
    
            $discount_rate   = $abc_options['wallet_payment_discount_amount_value'];
            $discount_amount = WC()->cart->get_subtotal() * $discount_rate / 100;
            $fee_description = '~ ' . $discount_rate . '% ' . __('Discount Wallet Payment', 'nm-ffk');
    
            $cart->add_fee($fee_description, -$discount_amount);
        }
    } 
    
    // Update checkout on payment method change
    add_action( 'woocommerce_checkout_init', 'update_checkout_on_payment_method_change' );
    function update_checkout_on_payment_method_change() {
        wc_enqueue_js("$('form.checkout').on( 'change', 'input[name=payment_method]', function(){
            $(document.body).trigger('update_checkout');
        });");
    }
    
    // Display 2 decimals for fees amounts on Checkout page
    add_filter( 'woocommerce_cart_totals_fee_html', 'filter_woo_cart_totals_fee_html', 10, 2 );
    function filter_woo_cart_totals_fee_html( $fee_html, $fee ){
        $args = array( 'decimals' => 2 );
        return WC()->cart->display_prices_including_tax() ? wc_price( $fee->total + $fee->tax, $args ) : wc_price( $fee->total, $args );
    }
    

    Code goes in functions.php file of your child theme (or in a plugin). Tested and works with WooCommerce Checkout Legacy (shortcode). Untested with new Checkout Block.


    Addition:

    For customer orders and email notifications (not possible in admin order):

    // Display 2 decimals for fees amounts on Customer orders and email notifications
    add_filter( 'woocommerce_get_order_item_totals', 'filter_woo_order_item_fee_totals', 10, 3 );
    function filter_woo_order_item_fee_totals( $total_rows, $order, $tax_display ){
        // Loop through total rows
        foreach ( $total_rows as $key => $data ) {
            // Loop through order fees
            foreach ( $order->get_fees() as $id => $fee ) {
                if( $key === 'fee_'.$id ) {
                    $args = array( 'currency' => $order->get_currency(), 'decimals' => 2 );
                    $total_rows[$key]['value'] = wc_price( 'excl' === $tax_display ? (float) $fee->get_total() : (float) $fee->get_total() + (float) $fee->get_total_tax(), $args );
                }
            }
        }
        return $total_rows;
    }
    

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