Round late when rounding at subtotal to reduce rounding error.

We were earlier rounding different tax rate values while they are merged, even if rounding at subtotal setting is enabled. This increases the rounding error, especially when prices are inclusive of taxes, and thus there is a chance that the total will be slightly different from when add the original values. For egs: https://github.com/woocommerce/woocommerce/issues/23917 .

This commit changes this behavior to round *after* we have summed all the precise unround values. Similar for items prices, we now round as late as possible, if rounding at subtotal is enabled.
This commit is contained in:
vedanshujain 2019-06-27 20:23:59 +05:30
parent a26f38f3a1
commit 8ab6cb2b5a
2 changed files with 76 additions and 4 deletions

View File

@ -591,7 +591,6 @@ final class WC_Cart_Totals {
$taxes[ $rate_id ] += $this->round_line_tax( $rate );
}
}
$taxes = $this->round_merged_taxes( $taxes );
return $in_cents ? $taxes : wc_remove_number_precision_deep( $taxes );
}
@ -682,7 +681,13 @@ final class WC_Cart_Totals {
$this->cart->cart_contents[ $item_key ]['line_tax'] = wc_remove_number_precision( $item->total_tax );
}
$this->set_total( 'items_total', array_sum( array_map( 'round', array_values( wp_list_pluck( $this->items, 'total' ) ) ) ) );
$items_total = array_sum(
array_map(
array( $this, 'round_item_subtotal' ),
array_values( wp_list_pluck( $this->items, 'total' ) )
)
);
$this->set_total( 'items_total', round( $items_total ) );
$this->set_total( 'items_total_tax', array_sum( array_values( wp_list_pluck( $this->items, 'total_tax' ) ) ) );
$this->cart->set_cart_contents_total( $this->get_total( 'items_total' ) );
@ -740,8 +745,14 @@ final class WC_Cart_Totals {
$this->cart->cart_contents[ $item_key ]['line_subtotal_tax'] = wc_remove_number_precision( $item->subtotal_tax );
}
$this->set_total( 'items_subtotal', array_sum( array_map( 'round', array_values( wp_list_pluck( $this->items, 'subtotal' ) ) ) ) );
$this->set_total( 'items_subtotal_tax', array_sum( $this->round_merged_taxes( $merged_subtotal_taxes ) ) );
$items_subtotal = array_sum(
array_map(
array( $this, 'round_item_subtotal' ),
array_values( wp_list_pluck( $this->items, 'subtotal' ) )
)
);
$this->set_total( 'items_subtotal', round( $items_subtotal ) );
$this->set_total( 'items_subtotal_tax', wc_round_tax_total( array_sum( $merged_subtotal_taxes ), 0 ) );
$this->cart->set_subtotal( $this->get_total( 'items_subtotal' ) );
$this->cart->set_subtotal_tax( $this->get_total( 'items_subtotal_tax' ) );
@ -876,4 +887,18 @@ final class WC_Cart_Totals {
}
return $value;
}
/**
* Apply rounding to item subtotal before summing.
*
* @since 3.7.0
* @param float $value Item subtotal value.
* @return float
*/
protected function round_item_subtotal( $value ) {
if ( ! $this->round_at_subtotal() ) {
$value = round( $value );
}
return $value;
}
}

View File

@ -187,6 +187,53 @@ class WC_Tests_Cart extends WC_Unit_Test_Case {
WC_Tax::_delete_tax_rate( $tax_rate_5 );
}
/**
* Test for subtotal when multiple tax slabs are present and round at subtotal is enabled.
*
* Ticket: @link https://github.com/woocommerce/woocommerce/issues/23917
*/
public function test_cart_calculate_total_rounding_23917() {
update_option( 'woocommerce_prices_include_tax', 'yes' );
update_option( 'woocommerce_calc_taxes', 'yes' );
update_option( 'woocommerce_tax_round_at_subtotal', 'yes' );
$tax_rate = array(
'tax_rate_country' => '',
'tax_rate_state' => '',
'tax_rate' => '24.0000',
'tax_rate_name' => 'CGST',
'tax_rate_priority' => '1',
'tax_rate_compound' => '0',
'tax_rate_shipping' => '0',
'tax_rate_order' => '1',
'tax_rate_class' => 'tax_1',
);
WC_Tax::_insert_tax_rate( $tax_rate );
$tax_rate = array(
'tax_rate_country' => '',
'tax_rate_state' => '',
'tax_rate' => '24.0000',
'tax_rate_name' => 'SGST',
'tax_rate_priority' => '2',
'tax_rate_compound' => '0',
'tax_rate_shipping' => '0',
'tax_rate_order' => '1',
'tax_rate_class' => 'tax_2',
);
WC_Tax::_insert_tax_rate( $tax_rate );
// Create a product with price 599.
$product = WC_Helper_Product::create_simple_product();
$product->set_regular_price( 599 );
$product->save();
WC()->cart->add_to_cart( $product->get_id(), 1 );
WC()->cart->calculate_totals();
$this->assertEquals( 599, WC()->cart->subtotal );
$this->assertEquals( 599, WC()->cart->total );
}
/**
* Test some discount logic which has caused issues in the past.
* Ticket: