Merge pull request #18008 from woocommerce/update/rounding-17970-17863
Rounding fixes/line item rounding
This commit is contained in:
commit
7c06e7d63d
|
@ -1366,9 +1366,14 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
|
|||
$this->add_item( $item );
|
||||
}
|
||||
|
||||
// Save tax totals.
|
||||
$this->set_shipping_tax( WC_Tax::round( array_sum( $shipping_taxes ) ) );
|
||||
$this->set_cart_tax( WC_Tax::round( array_sum( $cart_taxes ) ) );
|
||||
if ( 'yes' !== get_option( 'woocommerce_tax_round_at_subtotal' ) ) {
|
||||
$this->set_shipping_tax( wc_round_tax_total( array_sum( array_map( 'wc_round_tax_total', $shipping_taxes ) ) ) );
|
||||
$this->set_cart_tax( wc_round_tax_total( array_sum( array_map( 'wc_round_tax_total', $cart_taxes ) ) ) );
|
||||
} else {
|
||||
$this->set_shipping_tax( wc_round_tax_total( array_sum( $shipping_taxes ) ) );
|
||||
$this->set_cart_tax( wc_round_tax_total( array_sum( $cart_taxes ) ) );
|
||||
}
|
||||
|
||||
$this->save();
|
||||
}
|
||||
|
||||
|
@ -1599,7 +1604,7 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
|
|||
public function get_subtotal_to_display( $compound = false, $tax_display = '' ) {
|
||||
$tax_display = $tax_display ? $tax_display : get_option( 'woocommerce_tax_display_cart' );
|
||||
$subtotal = 0;
|
||||
|
||||
|
||||
if ( ! $compound ) {
|
||||
foreach ( $this->get_items() as $item ) {
|
||||
$subtotal += $item->get_subtotal();
|
||||
|
|
|
@ -5,6 +5,10 @@
|
|||
* Methods are protected and class is final to keep this as an internal API.
|
||||
* May be opened in the future once structure is stable.
|
||||
*
|
||||
* Rounding guide:
|
||||
* - if something is being stored e.g. item total, store unrounded. This is so taxes can be recalculated later accurately.
|
||||
* - if calculating a total, round (if settings allow).
|
||||
*
|
||||
* @author Automattic
|
||||
* @package WooCommerce/Classes
|
||||
*/
|
||||
|
@ -315,12 +319,7 @@ final class WC_Cart_Totals {
|
|||
}
|
||||
|
||||
$fee->taxes = apply_filters( 'woocommerce_cart_totals_get_fees_from_cart_taxes', $fee->taxes, $fee, $this );
|
||||
|
||||
$fee->total_tax = array_sum( $fee->taxes );
|
||||
|
||||
if ( ! $this->round_at_subtotal() ) {
|
||||
$fee->total_tax = wc_round_tax_total( $fee->total_tax, wc_get_rounding_precision() );
|
||||
}
|
||||
$fee->total_tax = array_sum( array_map( array( $this, 'round_line_tax' ), $fee->taxes ) );
|
||||
|
||||
// Set totals within object.
|
||||
$fee->object->total = wc_remove_number_precision_deep( $fee->total );
|
||||
|
@ -350,11 +349,7 @@ final class WC_Cart_Totals {
|
|||
$shipping_line->taxable = true;
|
||||
$shipping_line->total = wc_add_number_precision_deep( $shipping_object->cost );
|
||||
$shipping_line->taxes = wc_add_number_precision_deep( $shipping_object->taxes, false );
|
||||
$shipping_line->total_tax = wc_add_number_precision_deep( array_sum( $shipping_object->taxes ), false );
|
||||
|
||||
if ( ! $this->round_at_subtotal() ) {
|
||||
$shipping_line->total_tax = wc_round_tax_total( $shipping_line->total_tax, wc_get_rounding_precision() );
|
||||
}
|
||||
$shipping_line->total_tax = array_sum( array_map( array( $this, 'round_line_tax' ), $shipping_line->taxes ) );
|
||||
|
||||
$this->shipping[ $key ] = $shipping_line;
|
||||
}
|
||||
|
@ -427,7 +422,7 @@ final class WC_Cart_Totals {
|
|||
$base_tax_rates = WC_Tax::get_base_tax_rates( $item->product->get_tax_class( 'unfiltered' ) );
|
||||
|
||||
// Work out a new base price without the shop's base tax.
|
||||
$taxes = WC_Tax::calc_tax( $item->price, $base_tax_rates, true, true );
|
||||
$taxes = WC_Tax::calc_tax( $item->price, $base_tax_rates, true );
|
||||
|
||||
// Now we have a new item price (excluding TAX).
|
||||
$item->price = absint( $item->price - array_sum( $taxes ) );
|
||||
|
@ -454,8 +449,8 @@ final class WC_Cart_Totals {
|
|||
|
||||
if ( $item->tax_rates !== $base_tax_rates ) {
|
||||
// Work out a new base price without the shop's base tax.
|
||||
$taxes = WC_Tax::calc_tax( $item->price, $base_tax_rates, true, true );
|
||||
$new_taxes = WC_Tax::calc_tax( $item->price - array_sum( $taxes ), $item->tax_rates, false, true );
|
||||
$taxes = WC_Tax::calc_tax( $item->price, $base_tax_rates, true );
|
||||
$new_taxes = WC_Tax::calc_tax( $item->price - array_sum( $taxes ), $item->tax_rates, false );
|
||||
|
||||
// Now we have a new item price.
|
||||
$item->price = $item->price - array_sum( $taxes ) + array_sum( $new_taxes );
|
||||
|
@ -575,7 +570,7 @@ final class WC_Cart_Totals {
|
|||
if ( ! isset( $taxes[ $rate_id ] ) ) {
|
||||
$taxes[ $rate_id ] = 0;
|
||||
}
|
||||
$taxes[ $rate_id ] += $rate;
|
||||
$taxes[ $rate_id ] += $this->round_line_tax( $rate );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -636,15 +631,13 @@ final class WC_Cart_Totals {
|
|||
}
|
||||
|
||||
if ( $this->calculate_tax && $item->product->is_taxable() ) {
|
||||
$item->taxes = WC_Tax::calc_tax( $item->total, $item->tax_rates, $item->price_includes_tax );
|
||||
$item->total_tax = array_sum( $item->taxes );
|
||||
|
||||
if ( ! $this->round_at_subtotal() ) {
|
||||
$item->total_tax = wc_round_tax_total( $item->total_tax, wc_get_rounding_precision() );
|
||||
}
|
||||
$total_taxes = WC_Tax::calc_tax( $item->total, $item->tax_rates, $item->price_includes_tax );
|
||||
$item->taxes = $total_taxes;
|
||||
$item->total_tax = array_sum( array_map( array( $this, 'round_line_tax' ), $item->taxes ) );
|
||||
|
||||
if ( $item->price_includes_tax ) {
|
||||
$item->total = $item->total - $item->total_tax;
|
||||
// Use unrounded taxes so we can re-calculate from the orders screen accurately later.
|
||||
$item->total = $item->total - array_sum( $item->taxes );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -653,7 +646,7 @@ 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_values( wp_list_pluck( $this->items, 'total' ) ) ) );
|
||||
$this->set_total( 'items_total', array_sum( array_map( 'round', array_values( wp_list_pluck( $this->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' ) );
|
||||
|
@ -690,14 +683,11 @@ final class WC_Cart_Totals {
|
|||
|
||||
if ( $this->calculate_tax && $item->product->is_taxable() ) {
|
||||
$subtotal_taxes = WC_Tax::calc_tax( $item->subtotal, $item->tax_rates, $item->price_includes_tax );
|
||||
$item->subtotal_tax = array_sum( $subtotal_taxes );
|
||||
|
||||
if ( ! $this->round_at_subtotal() ) {
|
||||
$item->subtotal_tax = wc_round_tax_total( $item->subtotal_tax, wc_get_rounding_precision() );
|
||||
}
|
||||
$item->subtotal_tax = array_sum( array_map( array( $this, 'round_line_tax' ), $subtotal_taxes ) );
|
||||
|
||||
if ( $item->price_includes_tax ) {
|
||||
$item->subtotal = $item->subtotal - $item->subtotal_tax;
|
||||
// Use unrounded taxes so we can re-calculate from the orders screen accurately later.
|
||||
$item->subtotal = $item->subtotal - array_sum( $subtotal_taxes );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -705,7 +695,7 @@ final class WC_Cart_Totals {
|
|||
$this->cart->cart_contents[ $item_key ]['line_subtotal'] = wc_remove_number_precision( $item->subtotal );
|
||||
$this->cart->cart_contents[ $item_key ]['line_subtotal_tax'] = wc_remove_number_precision( $item->subtotal_tax );
|
||||
}
|
||||
$this->set_total( 'items_subtotal', array_sum( array_values( wp_list_pluck( $this->items, 'subtotal' ) ) ) );
|
||||
$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( array_values( wp_list_pluck( $this->items, 'subtotal_tax' ) ) ) );
|
||||
|
||||
$this->cart->set_subtotal( $this->get_total( 'items_subtotal' ) );
|
||||
|
@ -816,7 +806,7 @@ final class WC_Cart_Totals {
|
|||
* @since 3.2.0
|
||||
*/
|
||||
protected function calculate_totals() {
|
||||
$this->set_total( 'total', round( $this->get_total( 'items_total', true ) + $this->get_total( 'fees_total', true ) + $this->get_total( 'shipping_total', true ) + array_sum( $this->get_merged_taxes( true ) ) ) );
|
||||
$this->set_total( 'total', round( $this->get_total( 'items_total', true ) + $this->get_total( 'fees_total', true ) + $this->get_total( 'shipping_total', true ) + array_sum( $this->get_merged_taxes( true ) ), 0 ) );
|
||||
$this->cart->set_total_tax( array_sum( $this->get_merged_taxes( false ) ) );
|
||||
|
||||
// Allow plugins to hook and alter totals before final total is calculated.
|
||||
|
@ -827,4 +817,18 @@ final class WC_Cart_Totals {
|
|||
// Allow plugins to filter the grand total, and sum the cart totals in case of modifications.
|
||||
$this->cart->set_total( max( 0, apply_filters( 'woocommerce_calculated_total', $this->get_total( 'total' ), $this->cart ) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply rounding to an array of taxes before summing. Rounds to store DP setting, ignoring precision.
|
||||
*
|
||||
* @since 3.2.6
|
||||
* @param float $value Tax value.
|
||||
* @return float
|
||||
*/
|
||||
protected function round_line_tax( $value ) {
|
||||
if ( ! $this->round_at_subtotal() ) {
|
||||
$value = wc_round_tax_total( $value, 0 );
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -430,7 +430,7 @@ class WC_Cart extends WC_Legacy_Cart {
|
|||
* @param string $value Value to set.
|
||||
*/
|
||||
public function set_subtotal( $value ) {
|
||||
$this->totals['subtotal'] = wc_format_decimal( $value );
|
||||
$this->totals['subtotal'] = wc_format_decimal( $value, wc_get_price_decimals() );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -460,7 +460,7 @@ class WC_Cart extends WC_Legacy_Cart {
|
|||
* @param string $value Value to set.
|
||||
*/
|
||||
public function set_discount_tax( $value ) {
|
||||
$this->totals['discount_tax'] = wc_format_decimal( $value );
|
||||
$this->totals['discount_tax'] = wc_round_tax_total( $value );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -470,7 +470,7 @@ class WC_Cart extends WC_Legacy_Cart {
|
|||
* @param string $value Value to set.
|
||||
*/
|
||||
public function set_shipping_total( $value ) {
|
||||
$this->totals['shipping_total'] = wc_format_decimal( $value );
|
||||
$this->totals['shipping_total'] = wc_format_decimal( $value, wc_get_price_decimals() );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -490,7 +490,7 @@ class WC_Cart extends WC_Legacy_Cart {
|
|||
* @param string $value Value to set.
|
||||
*/
|
||||
public function set_cart_contents_total( $value ) {
|
||||
$this->totals['cart_contents_total'] = wc_format_decimal( $value );
|
||||
$this->totals['cart_contents_total'] = wc_format_decimal( $value, wc_get_price_decimals() );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -510,7 +510,7 @@ class WC_Cart extends WC_Legacy_Cart {
|
|||
* @param string $value Value to set.
|
||||
*/
|
||||
public function set_total( $value ) {
|
||||
$this->totals['total'] = wc_format_decimal( $value );
|
||||
$this->totals['total'] = wc_format_decimal( $value, wc_get_price_decimals() );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -530,7 +530,7 @@ class WC_Cart extends WC_Legacy_Cart {
|
|||
* @param string $value Value to set.
|
||||
*/
|
||||
public function set_fee_total( $value ) {
|
||||
$this->totals['fee_total'] = wc_format_decimal( $value );
|
||||
$this->totals['fee_total'] = wc_format_decimal( $value, wc_get_price_decimals() );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -85,7 +85,7 @@ class WC_Discounts {
|
|||
$item->object = $cart_item;
|
||||
$item->product = $cart_item['data'];
|
||||
$item->quantity = $cart_item['quantity'];
|
||||
$item->price = wc_add_number_precision_deep( $item->product->get_price() ) * $item->quantity;
|
||||
$item->price = wc_add_number_precision_deep( $item->product->get_price() * $item->quantity );
|
||||
$this->items[ $key ] = $item;
|
||||
}
|
||||
|
||||
|
@ -370,7 +370,7 @@ class WC_Discounts {
|
|||
}
|
||||
}
|
||||
|
||||
$discount = min( $discounted_price, $discount );
|
||||
$discount = wc_cart_round_discount( min( $discounted_price, $discount ), 0 );
|
||||
$cart_total = $cart_total + $price_to_discount;
|
||||
$total_discount = $total_discount + $discount;
|
||||
$applied_count = $applied_count + $apply_quantity;
|
||||
|
|
|
@ -27,7 +27,7 @@ class WC_Tax {
|
|||
*
|
||||
* @var bool
|
||||
*/
|
||||
public static $round_at_subtotal;
|
||||
public static $round_at_subtotal = false;
|
||||
|
||||
/**
|
||||
* Load options.
|
||||
|
@ -64,32 +64,20 @@ class WC_Tax {
|
|||
|
||||
/**
|
||||
* Calculate tax for a line.
|
||||
* @param float $price Price to calc tax on
|
||||
* @param array $rates Rates to apply
|
||||
* @param boolean $price_includes_tax Whether the passed price has taxes included
|
||||
* @param boolean $suppress_rounding Whether to suppress any rounding from taking place
|
||||
* @return array Array of rates + prices after tax
|
||||
*
|
||||
* @param float $price Price to calc tax on.
|
||||
* @param array $rates Rates to apply.
|
||||
* @param boolean $price_includes_tax Whether the passed price has taxes included.
|
||||
* @param boolean $deprecated Whether to suppress any rounding from taking place. No longer used here.
|
||||
* @return array Array of rates + prices after tax.
|
||||
*/
|
||||
public static function calc_tax( $price, $rates, $price_includes_tax = false, $suppress_rounding = false ) {
|
||||
// Work in pence to X precision
|
||||
$price = self::precision( $price );
|
||||
|
||||
public static function calc_tax( $price, $rates, $price_includes_tax = false, $deprecated = false ) {
|
||||
if ( $price_includes_tax ) {
|
||||
$taxes = self::calc_inclusive_tax( $price, $rates );
|
||||
} else {
|
||||
$taxes = self::calc_exclusive_tax( $price, $rates );
|
||||
}
|
||||
|
||||
// Round to precision
|
||||
if ( ! self::$round_at_subtotal && ! $suppress_rounding ) {
|
||||
$taxes = array_map( 'round', $taxes ); // Round to precision
|
||||
}
|
||||
|
||||
// Remove precision
|
||||
$price = self::remove_precision( $price );
|
||||
$taxes = array_map( array( __CLASS__, 'remove_precision' ), $taxes );
|
||||
|
||||
return apply_filters( 'woocommerce_calc_tax', $taxes, $price, $rates, $price_includes_tax, $suppress_rounding );
|
||||
return apply_filters( 'woocommerce_calc_tax', $taxes, $price, $rates, $price_includes_tax, $deprecated );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -104,24 +92,6 @@ class WC_Tax {
|
|||
return apply_filters( 'woocommerce_calc_shipping_tax', $taxes, $price, $rates );
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiply cost by pow precision.
|
||||
* @param float $price
|
||||
* @return float
|
||||
*/
|
||||
private static function precision( $price ) {
|
||||
return $price * ( pow( 10, self::$precision ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Divide cost by pow precision.
|
||||
* @param float $price
|
||||
* @return float
|
||||
*/
|
||||
private static function remove_precision( $price ) {
|
||||
return $price / ( pow( 10, self::$precision ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Round to precision.
|
||||
*
|
||||
|
@ -137,7 +107,7 @@ class WC_Tax {
|
|||
* @return float
|
||||
*/
|
||||
public static function round( $in ) {
|
||||
return apply_filters( 'woocommerce_tax_round', round( $in, self::$precision ), $in );
|
||||
return apply_filters( 'woocommerce_tax_round', round( $in, wc_get_rounding_precision() ), $in );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -362,32 +362,23 @@ function wc_cart_totals_shipping_method_label( $method ) {
|
|||
/**
|
||||
* Round discount.
|
||||
*
|
||||
* @param float $value
|
||||
* @param int $precision
|
||||
* @param double $value Amount to round.
|
||||
* @param int $precision DP to round.
|
||||
* @return float
|
||||
*/
|
||||
function wc_cart_round_discount( $value, $precision ) {
|
||||
if ( version_compare( PHP_VERSION, '5.3.0', '>=' ) ) {
|
||||
return round( $value, $precision, WC_DISCOUNT_ROUNDING_MODE );
|
||||
} elseif ( 2 === WC_DISCOUNT_ROUNDING_MODE ) {
|
||||
return wc_legacy_round_half_down( $value, $precision );
|
||||
} else {
|
||||
// Fake it in PHP 5.2.
|
||||
if ( 2 === WC_DISCOUNT_ROUNDING_MODE && strstr( $value, '.' ) ) {
|
||||
$value = (string) $value;
|
||||
$value = explode( '.', $value );
|
||||
$value[1] = substr( $value[1], 0, $precision + 1 );
|
||||
$value = implode( '.', $value );
|
||||
|
||||
if ( substr( $value, -1 ) === '5' ) {
|
||||
$value = substr( $value, 0, -1 ) . '4';
|
||||
}
|
||||
$value = floatval( $value );
|
||||
}
|
||||
return round( $value, $precision );
|
||||
return round( $value, $precision );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets chosen shipping method IDs from chosen_shipping_methods session, without instance IDs.
|
||||
*
|
||||
* @since 2.6.2
|
||||
* @return string[]
|
||||
*/
|
||||
|
|
|
@ -1496,17 +1496,17 @@ function wc_get_rounding_precision() {
|
|||
}
|
||||
|
||||
/**
|
||||
* Add precision to a number and return an int.
|
||||
* Add precision to a number and return a number.
|
||||
*
|
||||
* @since 3.2.0
|
||||
* @param float $value Number to add precision to.
|
||||
* @param bool $round Should we round after adding precision?
|
||||
* @return int|float
|
||||
* @return float
|
||||
*/
|
||||
function wc_add_number_precision( $value, $round = true ) {
|
||||
$precision = pow( 10, wc_get_price_decimals() );
|
||||
$value = $value * $precision;
|
||||
return $round ? intval( round( $value ) ) : $value;
|
||||
$cent_precision = pow( 10, wc_get_price_decimals() );
|
||||
$value = $value * $cent_precision;
|
||||
return $round ? round( $value, wc_get_rounding_precision() - wc_get_price_decimals() ) : $value;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1517,8 +1517,8 @@ function wc_add_number_precision( $value, $round = true ) {
|
|||
* @return float
|
||||
*/
|
||||
function wc_remove_number_precision( $value ) {
|
||||
$precision = pow( 10, wc_get_price_decimals() );
|
||||
return $value / $precision;
|
||||
$cent_precision = pow( 10, wc_get_price_decimals() );
|
||||
return $value / $cent_precision;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -226,32 +226,46 @@ function wc_trim_zeros( $price ) {
|
|||
*
|
||||
* @param double $value Amount to round.
|
||||
* @param int $precision DP to round. Defaults to wc_get_price_decimals.
|
||||
* @return double
|
||||
* @return float
|
||||
*/
|
||||
function wc_round_tax_total( $value, $precision = null ) {
|
||||
$precision = is_null( $precision ) ? wc_get_price_decimals() : absint( $precision );
|
||||
$precision = is_null( $precision ) ? wc_get_price_decimals() : intval( $precision );
|
||||
|
||||
if ( version_compare( PHP_VERSION, '5.3.0', '>=' ) ) {
|
||||
$rounded_tax = round( $value, $precision, wc_get_tax_rounding_mode() );
|
||||
} elseif ( 2 === wc_get_tax_rounding_mode() ) {
|
||||
$rounded_tax = wc_legacy_round_half_down( $value, $precision );
|
||||
} else {
|
||||
// Fake it in PHP 5.2.
|
||||
if ( 2 === wc_get_tax_rounding_mode() && strstr( $value, '.' ) ) {
|
||||
$value = (string) $value;
|
||||
$value = explode( '.', $value );
|
||||
$value[1] = substr( $value[1], 0, $precision + 1 );
|
||||
$value = implode( '.', $value );
|
||||
|
||||
if ( substr( $value, -1 ) === '5' ) {
|
||||
$value = substr( $value, 0, -1 ) . '4';
|
||||
}
|
||||
$value = floatval( $value );
|
||||
}
|
||||
$rounded_tax = round( $value, $precision );
|
||||
}
|
||||
|
||||
return apply_filters( 'wc_round_tax_total', $rounded_tax, $value, $precision, WC_TAX_ROUNDING_MODE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Round half down in PHP 5.2.
|
||||
*
|
||||
* @since 3.2.6
|
||||
* @param float $value Value to round.
|
||||
* @param int $precision Precision to round down to.
|
||||
* @return float
|
||||
*/
|
||||
function wc_legacy_round_half_down( $value, $precision ) {
|
||||
$value = wc_float_to_string( $value );
|
||||
|
||||
if ( false !== strstr( $value, '.' ) ) {
|
||||
$value = explode( '.', $value );
|
||||
|
||||
if ( strlen( $value[1] ) > $precision && substr( $value[1], -1 ) === '5' ) {
|
||||
$value[1] = substr( $value[1], 0, -1 ) . '4';
|
||||
}
|
||||
|
||||
$value = implode( '.', $value );
|
||||
}
|
||||
|
||||
return round( floatval( $value ), $precision );
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a refund total negative.
|
||||
*
|
||||
|
|
|
@ -301,7 +301,7 @@ class WC_Tests_Cart extends WC_Unit_Test_Case {
|
|||
WC()->cart->calculate_totals();
|
||||
$this->assertEquals( '8.33', wc_format_decimal( WC()->cart->get_subtotal(), 2 ) );
|
||||
$this->assertEquals( '0.83', wc_format_decimal( WC()->cart->get_discount_total(), 2 ) );
|
||||
$this->assertEquals( '1.50', wc_format_decimal( WC()->cart->get_total_tax(), 2 ) );
|
||||
$this->assertEquals( '1.50', wc_format_decimal( WC()->cart->get_total_tax(), 2 ), WC()->cart->get_total_tax() );
|
||||
$this->assertEquals( '8.99', wc_format_decimal( WC()->cart->get_total( 'edit' ), 2 ) );
|
||||
WC()->cart->remove_coupons();
|
||||
|
||||
|
@ -396,6 +396,213 @@ class WC_Tests_Cart extends WC_Unit_Test_Case {
|
|||
return 'US';
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a rounding issue on prices that are entered inclusive tax and shipping is used.
|
||||
* See: #17970.
|
||||
*
|
||||
* @since 3.2.6
|
||||
*/
|
||||
public function test_inclusive_tax_rounding() {
|
||||
global $wpdb;
|
||||
|
||||
// Store is set to enter product prices inclusive tax.
|
||||
update_option( 'woocommerce_prices_include_tax', 'yes' );
|
||||
update_option( 'woocommerce_calc_taxes', 'yes' );
|
||||
|
||||
// 19% tax.
|
||||
$tax_rate = array(
|
||||
'tax_rate_country' => '',
|
||||
'tax_rate_state' => '',
|
||||
'tax_rate' => '19.0000',
|
||||
'tax_rate_name' => 'VAT',
|
||||
'tax_rate_priority' => '1',
|
||||
'tax_rate_compound' => '0',
|
||||
'tax_rate_shipping' => '1',
|
||||
'tax_rate_order' => '1',
|
||||
'tax_rate_class' => '',
|
||||
);
|
||||
WC_Tax::_insert_tax_rate( $tax_rate );
|
||||
|
||||
// Create a flat rate method.
|
||||
$flat_rate_settings = array(
|
||||
'enabled' => 'yes',
|
||||
'title' => 'Flat rate',
|
||||
'availability' => 'all',
|
||||
'countries' => '',
|
||||
'tax_status' => 'taxable',
|
||||
'cost' => '4.12',
|
||||
);
|
||||
update_option( 'woocommerce_flat_rate_settings', $flat_rate_settings );
|
||||
update_option( 'woocommerce_flat_rate', array() );
|
||||
WC_Cache_Helper::get_transient_version( 'shipping', true );
|
||||
WC()->shipping->load_shipping_methods();
|
||||
|
||||
// We need this to have the calculate_totals() method calculate totals.
|
||||
if ( ! defined( 'WOOCOMMERCE_CHECKOUT' ) ) {
|
||||
define( 'WOOCOMMERCE_CHECKOUT', true );
|
||||
}
|
||||
|
||||
// Create the product and add it to the cart.
|
||||
$product = new WC_Product_Simple;
|
||||
$product->set_regular_price( '149.14' );
|
||||
$product->save();
|
||||
WC()->cart->add_to_cart( $product->get_id(), 1 );
|
||||
|
||||
// Set the flat_rate shipping method
|
||||
WC()->session->set( 'chosen_shipping_methods', array( 'flat_rate' ) );
|
||||
|
||||
WC()->cart->calculate_totals();
|
||||
$this->assertEquals( '154.04', wc_format_decimal( WC()->cart->get_total( 'edit' ), 2 ) );
|
||||
$this->assertEquals( '24.59', wc_format_decimal( WC()->cart->get_total_tax(), 2 ) );
|
||||
|
||||
// Clean up.
|
||||
WC()->cart->empty_cart();
|
||||
WC_Helper_Product::delete_product( $product->get_id() );
|
||||
$wpdb->query( "DELETE FROM {$wpdb->prefix}woocommerce_tax_rates" );
|
||||
$wpdb->query( "DELETE FROM {$wpdb->prefix}woocommerce_tax_rate_locations" );
|
||||
update_option( 'woocommerce_prices_include_tax', 'no' );
|
||||
update_option( 'woocommerce_calc_taxes', 'no' );
|
||||
|
||||
// Delete the flat rate method
|
||||
WC()->session->set( 'chosen_shipping_methods', array() );
|
||||
delete_option( 'woocommerce_flat_rate_settings' );
|
||||
delete_option( 'woocommerce_flat_rate' );
|
||||
WC_Cache_Helper::get_transient_version( 'shipping', true );
|
||||
WC()->shipping->unregister_shipping_methods();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a rounding issue on prices that are entered exclusive tax.
|
||||
* See: #17970.
|
||||
*
|
||||
* @since 3.2.6
|
||||
*/
|
||||
public function test_exclusive_tax_rounding() {
|
||||
global $wpdb;
|
||||
|
||||
// todo remove this line when previous test stops failing.
|
||||
WC()->cart->empty_cart();
|
||||
|
||||
// Store is set to enter product prices excluding tax.
|
||||
update_option( 'woocommerce_prices_include_tax', 'no' );
|
||||
update_option( 'woocommerce_calc_taxes', 'yes' );
|
||||
|
||||
// 20% tax.
|
||||
$tax_rate = array(
|
||||
'tax_rate_country' => '',
|
||||
'tax_rate_state' => '',
|
||||
'tax_rate' => '20.0000',
|
||||
'tax_rate_name' => 'VAT',
|
||||
'tax_rate_priority' => '1',
|
||||
'tax_rate_compound' => '0',
|
||||
'tax_rate_shipping' => '0',
|
||||
'tax_rate_order' => '1',
|
||||
'tax_rate_class' => '',
|
||||
);
|
||||
WC_Tax::_insert_tax_rate( $tax_rate );
|
||||
|
||||
// Add 2 products whose retail prices (inc tax) are: £65, £50.
|
||||
// Their net prices are therefore: £54.1666667 and £41.6666667.
|
||||
$product1 = new WC_Product_Simple;
|
||||
$product1->set_regular_price( '54.1666667' );
|
||||
$product1->save();
|
||||
|
||||
$product2 = new WC_Product_Simple;
|
||||
$product2->set_regular_price( '41.6666667' );
|
||||
$product2->save();
|
||||
|
||||
WC()->cart->add_to_cart( $product1->get_id(), 1 );
|
||||
WC()->cart->add_to_cart( $product2->get_id(), 1 );
|
||||
|
||||
WC()->cart->calculate_totals();
|
||||
$this->assertEquals( '115.00', wc_format_decimal( WC()->cart->get_total( 'edit' ), 2 ) );
|
||||
|
||||
// Clean up.
|
||||
WC()->cart->empty_cart();
|
||||
WC_Helper_Product::delete_product( $product1->get_id() );
|
||||
WC_Helper_Product::delete_product( $product2->get_id() );
|
||||
$wpdb->query( "DELETE FROM {$wpdb->prefix}woocommerce_tax_rates" );
|
||||
$wpdb->query( "DELETE FROM {$wpdb->prefix}woocommerce_tax_rate_locations" );
|
||||
update_option( 'woocommerce_prices_include_tax', 'no' );
|
||||
update_option( 'woocommerce_calc_taxes', 'no' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a rounding issue on prices and totals that are entered exclusive tax.
|
||||
* See: #17647.
|
||||
*
|
||||
* @since 3.2.6
|
||||
*/
|
||||
public function test_exclusive_tax_rounding_and_totals() {
|
||||
global $wpdb;
|
||||
|
||||
WC()->cart->empty_cart();
|
||||
WC()->cart->remove_coupons();
|
||||
$wpdb->query( "DELETE FROM {$wpdb->prefix}woocommerce_tax_rates" );
|
||||
$wpdb->query( "DELETE FROM {$wpdb->prefix}woocommerce_tax_rate_locations" );
|
||||
|
||||
$product_data = array(
|
||||
// price, quantity.
|
||||
array( 2.13, 1 ),
|
||||
array( 2.55, 0.5 ),
|
||||
array( 39, 1 ),
|
||||
array( 1.76, 1 ),
|
||||
);
|
||||
|
||||
foreach ( $product_data as $data ) {
|
||||
$product = new WC_Product_Simple();
|
||||
$product->set_regular_price( $data[0] );
|
||||
$product->save();
|
||||
$products[] = array( $product, $data[1] );
|
||||
}
|
||||
|
||||
$tax_rate = array(
|
||||
'tax_rate_country' => '',
|
||||
'tax_rate_state' => '',
|
||||
'tax_rate' => '5.5',
|
||||
'tax_rate_name' => 'TAX',
|
||||
'tax_rate_priority' => '1',
|
||||
'tax_rate_compound' => '0',
|
||||
'tax_rate_shipping' => '1',
|
||||
'tax_rate_order' => '1',
|
||||
'tax_rate_class' => '',
|
||||
);
|
||||
|
||||
WC_Tax::_insert_tax_rate( $tax_rate );
|
||||
|
||||
update_option( 'woocommerce_prices_include_tax', 'no' );
|
||||
update_option( 'woocommerce_calc_taxes', 'yes' );
|
||||
update_option( 'woocommerce_tax_round_at_subtotal', 'no' );
|
||||
|
||||
foreach ( $products as $data ) {
|
||||
WC()->cart->add_to_cart( $data[0]->get_id(), $data[1] );
|
||||
}
|
||||
|
||||
// We need this to have the calculate_totals() method calculate totals.
|
||||
if ( ! defined( 'WOOCOMMERCE_CHECKOUT' ) ) {
|
||||
define( 'WOOCOMMERCE_CHECKOUT', true );
|
||||
}
|
||||
|
||||
WC()->cart->calculate_totals();
|
||||
|
||||
$cart_totals = WC()->cart->get_totals();
|
||||
|
||||
$this->assertEquals( '2.44', wc_format_decimal( $cart_totals['total_tax'], 2 ) );
|
||||
$this->assertEquals( '2.44', wc_format_decimal( $cart_totals['cart_contents_tax'], 2 ) );
|
||||
$this->assertEquals( '44.17', wc_format_decimal( $cart_totals['cart_contents_total'], 2 ) );
|
||||
$this->assertEquals( '46.61', wc_format_decimal( $cart_totals['total'], 2 ) );
|
||||
|
||||
// Clean up.
|
||||
WC()->cart->empty_cart();
|
||||
foreach ( $products as $data ) {
|
||||
WC_Helper_Product::delete_product( $data[0]->get_id() );
|
||||
}
|
||||
$wpdb->query( "DELETE FROM {$wpdb->prefix}woocommerce_tax_rates" );
|
||||
$wpdb->query( "DELETE FROM {$wpdb->prefix}woocommerce_tax_rate_locations" );
|
||||
update_option( 'woocommerce_prices_include_tax', 'no' );
|
||||
update_option( 'woocommerce_calc_taxes', 'no' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test get_remove_url.
|
||||
*
|
||||
|
|
|
@ -221,15 +221,29 @@ class WC_Tests_Formatting_Functions extends WC_Unit_Test_Case {
|
|||
/**
|
||||
* Test wc_round_tax_total().
|
||||
*
|
||||
* Note the PHP 5.2 section of wc_round_tax_total() is excluded from test.
|
||||
* coverage.
|
||||
*
|
||||
* @since 2.2
|
||||
*/
|
||||
public function test_wc_round_tax_total() {
|
||||
update_option( 'woocommerce_prices_include_tax', 'no' );
|
||||
$this->assertEquals( 1.25, wc_round_tax_total( 1.246 ) );
|
||||
$this->assertEquals( 20, wc_round_tax_total( 19.9997 ) );
|
||||
$this->assertEquals( 19.99, wc_round_tax_total( 19.99 ) );
|
||||
$this->assertEquals( 19.99, wc_round_tax_total( 19.99 ) );
|
||||
$this->assertEquals( 83, wc_round_tax_total( 82.5, 0 ) );
|
||||
$this->assertEquals( 83, wc_round_tax_total( 82.54, 0 ) );
|
||||
$this->assertEquals( 83, wc_round_tax_total( 82.546, 0 ) );
|
||||
|
||||
update_option( 'woocommerce_prices_include_tax', 'yes' );
|
||||
$this->assertEquals( 1.25, wc_round_tax_total( 1.246 ) );
|
||||
$this->assertEquals( 20, wc_round_tax_total( 19.9997 ) );
|
||||
$this->assertEquals( 19.99, wc_round_tax_total( 19.99 ) );
|
||||
$this->assertEquals( 19.99, wc_round_tax_total( 19.99 ) );
|
||||
$this->assertEquals( 82, wc_round_tax_total( 82.5, 0 ) );
|
||||
$this->assertEquals( 83, wc_round_tax_total( 82.54, 0 ) );
|
||||
$this->assertEquals( 83, wc_round_tax_total( 82.546, 0 ) );
|
||||
|
||||
// Default.
|
||||
update_option( 'woocommerce_prices_include_tax', 'no' );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -272,7 +272,8 @@ class WC_Tests_Tax extends WC_Unit_Test_Case {
|
|||
* Next tax would be calced on 100 - 7.8341 = 92.1659.
|
||||
* 92.1659 - ( 92.1659 / 1.05 ) = 4.38885.
|
||||
*/
|
||||
$this->assertEquals( $calced_tax, array( $tax_rate_1_id => 4.3889, $tax_rate_2_id => 7.8341 ) );
|
||||
$this->assertEquals( round( $calced_tax[ $tax_rate_1_id ], 4 ), 4.3889 );
|
||||
$this->assertEquals( round( $calced_tax[ $tax_rate_2_id ], 4 ), 7.8341 );
|
||||
|
||||
WC_Tax::_delete_tax_rate( $tax_rate_1_id );
|
||||
WC_Tax::_delete_tax_rate( $tax_rate_2_id );
|
||||
|
|
Loading…
Reference in New Issue