Ross
Ross

Reputation: 534

How to subtract subtotal from total in WooCommerce?

In WooCommerce, I'm wondering is there a simple solution to subtract the subtotal from the total without adding another fee? And have this appear in all places such as checkout, orders (Woocommerce), my orders (my-account/view-order/) etc.

The reason for this is that I have added custom fees / surcharges to products which is 10% reservation fee of subtotal, and I only want the surcharges to be paid first and rest offline (like collection).

For example

So, Is there a simple way to remove the subtotal from total on complete order table as well as cart / checkout? Thanks

add_filter( 'woocommerce_calculated_total', 'custom_cart_total', 20, 2 );
function custom_cart_total( $total, $cart ) {
    return $total - $subtotal;
}

Upvotes: 1

Views: 1478

Answers (1)

7uc1f3r
7uc1f3r

Reputation: 29614

The woocommerce_calculated_total filter hook can indeed be used, only $subtotal is undefined in your current code.

So you get:

// Allow plugins to filter the grand total, and sum the cart totals in case of modifications.
function filter_woocommerce_calculated_total( $total, $cart ) { 
    // Get subtotal
    $subtotal = $cart->get_subtotal();
    
    return $total - $subtotal;
}
add_filter( 'woocommerce_calculated_total', 'filter_woocommerce_calculated_total', 10, 2 );

Update:

In first instance this hook works and the new total is displayed in order details tables, WooCommerce email notifications, etc.. however, when changes are made to the order, everything is recalculated.

To get around this, we can manipulate the total when the recalculations take place.

Step 1: Add specific meta data if applicable to the current order after WooCommerce checkout

/**
 * Action hook fired after an order is created used to add custom meta to the order.
 *
 * @since 3.0.0
 */
function action_woocommerce_checkout_update_order_meta( $order_id, $data ) {    
    // Get an instance of the WC_Order object
    $order = wc_get_order( $order_id );
    
    // Is a WC_Order
    if ( is_a( $order, 'WC_Order' ) ) {     
        // Get subtotal
        $subtotal = $order->get_subtotal();
        
        // Get total
        $total = $order->get_total();
        
        // Total is less than subtotal
        if ( $total < $subtotal ) {
            // Save the order data and meta data
            $order->update_meta_data( '_is_recalculated_order_id', $order_id );
            $order->save();
        }
    }
}   
add_action( 'woocommerce_checkout_update_order_meta', 'action_woocommerce_checkout_update_order_meta', 10, 2 );

Step 2: manipulate the total, when changes are made for this specific order

function filter_woocommerce_order_get_total( $total, $order ) {
    global $pagenow;
    
    // Only on order edit page
    if ( $pagenow != 'post.php' || get_post_type( $_GET['post'] ) != 'shop_order' ) return $total;
    
    // Get meta
    $is_recalculated_order_id = $order->get_meta( '_is_recalculated_order_id' );
    
    // NOT empty and meta value is equal to current order ID
    if ( ! empty ( $is_recalculated_order_id ) && $is_recalculated_order_id == $order->get_id() ) {     
        // Get subtotal
        $subtotal = $order->get_subtotal();
        
        // Subtotal is less than total
        if ( $subtotal < $total ) {         
            // Manipulate
            $total = $total - $subtotal;
        }
    }
    
    return $total;
}
add_filter( 'woocommerce_order_get_total', 'filter_woocommerce_order_get_total', 10, 2 );

The downside of this is that it will only be applied for changes made via the order edit page. To also apply this from the WooCommerce admin orders list you could remove the if condition, but then the change will not only be applied to the current order but also to all previous orders (if applicable).

In short: far from the ideal solution

Upvotes: 2

Related Questions