Merge pull request #16569 from woocommerce/update/fees-api-class

WC_Cart_fees and negative fee calculations
This commit is contained in:
Mike Jolley 2017-08-23 10:59:19 +01:00 committed by GitHub
commit 22813663d7
8 changed files with 582 additions and 75 deletions

View File

@ -0,0 +1,142 @@
<?php
/**
* Cart fees API.
*
* Developers can add fees to the cart via WC()->cart->fees_api() which will reference this class.
*
* We suggest using the action woocommerce_cart_calculate_fees hook for adding fees.
*
* @author Automattic
* @package WooCommerce/Classes
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* WC_Cart_Fees class.
*
* @since 3.2.0
*/
final class WC_Cart_Fees {
/**
* An array of fee objects.
*
* @var object[]
*/
private $fees = array();
/**
* Reference to cart object.
*
* @since 3.2.0
* @var array
*/
private $cart;
/**
* New fees are made out of these props.
*
* @var array
*/
private $default_fee_props = array(
'id' => '',
'name' => '',
'tax_class' => '',
'taxable' => false,
'amount' => 0,
);
/**
* Constructor. Reference to the cart.
*
* @since 3.2.0
* @param object $cart Cart object.
*/
public function __construct( &$cart = null ) {
$this->cart = $cart;
add_action( 'woocommerce_cart_emptied', array( $this, 'remove_all_fees' ) );
add_action( 'woocommerce_cart_reset', array( $this, 'remove_all_fees' ) );
}
/**
* Add a fee. Fee IDs must be unique.
*
* @since 3.2.0
* @param array $args Array of fee properties.
* @return object Either a fee object if added, or a WP_Error if it failed.
*/
public function add_fee( $args = array() ) {
$fee_props = (object) wp_parse_args( $args, $this->default_fee_props );
$fee_props->name = $fee_props->name ? $fee_props->name : __( 'Fee', 'woocommerce' );
$fee_props->tax_class = in_array( $fee_props->tax_class, WC_Tax::get_tax_classes(), true ) ? $fee_props->tax_class: '';
$fee_props->taxable = wc_string_to_bool( $fee_props->taxable );
$fee_props->amount = wc_format_decimal( $fee_props->amount );
if ( empty( $fee_props->id ) ) {
$fee_props->id = $this->generate_id( $fee_props );
}
if ( array_key_exists( $fee_props->id, $this->fees ) ) {
return new WP_Error( 'fee_exists', __( 'Fee has already been added.', 'woocommerce' ) );
}
return $this->fees[ $fee_props->id ] = $fee_props;
}
/**
* Get fees.
*
* @return array
*/
public function get_fees() {
uasort( $this->fees, array( $this, 'sort_fees_callback' ) );
return $this->fees;
}
/**
* Set fees.
*
* @param object[] $raw_fees Array of fees.
*/
public function set_fees( $raw_fees = array() ) {
$this->fees = array();
foreach ( $raw_fees as $raw_fee ) {
$this->add_fee( $raw_fee );
}
}
/**
* Remove all fees.
*
* @since 3.2.0
*/
public function remove_all_fees() {
$this->set_fees();
}
/**
* Sort fees by amount.
*
* @param WC_Coupon $a Coupon object.
* @param WC_Coupon $b Coupon object.
* @return int
*/
protected function sort_fees_callback( $a, $b ) {
return ( $a->amount > $b->amount ) ? -1 : 1;
}
/**
* Generate a unique ID for the fee being added.
*
* @param string $fee Fee object.
* @return string fee key.
*/
private function generate_id( $fee ) {
return sanitize_title( $fee->name );
}
}

View File

@ -230,6 +230,34 @@ final class WC_Cart_Totals {
}
}
/**
* Get item costs grouped by tax class.
*
* @since 3.2.0
* @return array
*/
protected function get_tax_class_costs() {
$item_tax_classes = wp_list_pluck( $this->items, 'tax_class' );
$shipping_tax_classes = wp_list_pluck( $this->shipping, 'tax_class' );
$fee_tax_classes = wp_list_pluck( $this->fees, 'tax_class' );
$costs = array_fill_keys( $item_tax_classes + $shipping_tax_classes + $fee_tax_classes, 0 );
$costs['non-taxable'] = 0;
foreach ( $this->items + $this->fees + $this->shipping as $item ) {
if ( 0 > $item->total ) {
continue;
}
if ( ! $item->taxable ) {
$costs['non-taxable'] += $item->total;
} elseif ( 'inherit' === $item->tax_class ) {
$costs[ reset( $item_tax_classes ) ] += $item->total;
} else {
$costs[ $item->tax_class ] += $item->total;
}
}
return array_filter( $costs );
}
/**
* Get fee objects from the cart. Normalises data
* into the same format for use by this class.
@ -247,15 +275,38 @@ final class WC_Cart_Totals {
$fee->taxable = $fee->object->taxable;
$fee->total = wc_add_number_precision_deep( $fee->object->amount );
if ( $this->calculate_tax && $fee->object->taxable ) {
$fee->taxes = WC_Tax::calc_tax( $fee->total, WC_Tax::get_rates( $fee->object->tax_class, $this->cart->get_customer() ), false );
$fee->total_tax = array_sum( $fee->taxes );
if ( $this->calculate_tax ) {
if ( 0 > $fee->total ) {
// Negative fees should have the taxes split between all items so it works as a true discount.
$tax_class_costs = $this->get_tax_class_costs();
$total_cost = array_sum( $tax_class_costs );
if ( ! $this->round_at_subtotal() ) {
$fee->total_tax = wc_round_tax_total( $fee->total_tax, wc_get_rounding_precision() );
if ( $total_cost ) {
foreach ( $tax_class_costs as $tax_class => $tax_class_cost ) {
if ( 'non-taxable' === $tax_class ) {
continue;
}
$proportion = $tax_class_cost / $total_cost;
$cart_discount_proportion = $fee->total * $proportion;
$fee->taxes = wc_array_merge_recursive_numeric( $fee->taxes, WC_Tax::calc_tax( $fee->total * $proportion, WC_Tax::get_rates( $tax_class ) ) );
}
}
} elseif ( $fee->object->taxable ) {
$fee->taxes = WC_Tax::calc_tax( $fee->total, WC_Tax::get_rates( $fee->tax_class, $this->cart->get_customer() ), false );
}
}
$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() );
}
// Set totals within object.
$fee->object->tax_data = wc_remove_number_precision_deep( $fee->taxes );
$fee->object->tax = wc_remove_number_precision_deep( $fee->total_tax );
$this->fees[ $fee_key ] = $fee;
}
}
@ -653,14 +704,11 @@ final class WC_Cart_Totals {
*/
protected function calculate_fee_totals() {
$this->get_fees_from_cart();
$this->set_total( 'fees_total', array_sum( wp_list_pluck( $this->fees, 'total' ) ) );
$this->set_total( 'fees_total_tax', array_sum( wp_list_pluck( $this->fees, 'total_tax' ) ) );
foreach ( $this->fees as $fee_key => $fee ) {
$this->cart->fees[ $fee_key ]->tax = wc_remove_number_precision_deep( $fee->total_tax );
$this->cart->fees[ $fee_key ]->tax_data = wc_remove_number_precision_deep( $fee->taxes );
}
$this->cart->fees_api()->set_fees( wp_list_pluck( $this->fees, 'object' ) );
$this->cart->set_fee_total( wc_remove_number_precision_deep( array_sum( wp_list_pluck( $this->fees, 'total' ) ) ) );
$this->cart->set_fee_tax( wc_remove_number_precision_deep( array_sum( wp_list_pluck( $this->fees, 'total_tax' ) ) ) );
$this->cart->set_fee_taxes( wc_remove_number_precision_deep( $this->combine_item_taxes( wp_list_pluck( $this->fees, 'taxes' ) ) ) );

View File

@ -44,13 +44,6 @@ class WC_Cart extends WC_Legacy_Cart {
*/
public $applied_coupons = array();
/**
* An array of fees.
*
* @var array
*/
public $fees = array();
/**
* Are prices in the cart displayed inc or excl tax.
*
@ -101,11 +94,19 @@ class WC_Cart extends WC_Legacy_Cart {
*/
protected $session;
/**
* Reference to the cart fees API class.
*
* @var WC_Cart_Fees
*/
protected $fees_api;
/**
* Constructor for the cart class. Loads options and hooks in the init method.
*/
public function __construct() {
$this->session = new WC_Cart_Session( $this );
$this->fees_api = new WC_Cart_Fees( $this );
$this->tax_display_cart = get_option( 'woocommerce_tax_display_cart' );
add_action( 'woocommerce_add_to_cart', array( $this, 'calculate_totals' ), 20, 0 );
@ -609,7 +610,6 @@ class WC_Cart extends WC_Legacy_Cart {
$this->coupon_discount_totals = array();
$this->coupon_discount_tax_totals = array();
$this->applied_coupons = array();
$this->fees = array();
$this->totals = $this->default_totals;
if ( $clear_persistent_cart ) {
@ -1695,61 +1695,54 @@ class WC_Cart extends WC_Legacy_Cart {
}
/**
* Add additional fee to the cart.
* Trigger an action so 3rd parties can add custom fees.
*
* Fee is an amount of money charged for a particular piece of work
* or for a particular right or service, and not supposed to be negative.
* @since 2.0.0
*/
public function calculate_fees() {
do_action( 'woocommerce_cart_calculate_fees', $this );
}
/**
* Return reference to fees API.
*
* @since 3.2.0
* @return WC_Cart_Fees
*/
public function fees_api() {
return $this->fees_api;
}
/**
* 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: '').
*/
public function add_fee( $name, $amount, $taxable = false, $tax_class = '' ) {
$new_fee_id = sanitize_title( $name );
// Only add each fee once.
foreach ( $this->fees as $fee ) {
if ( $fee->id === $new_fee_id ) {
return;
}
}
$new_fee = new stdClass();
$new_fee->id = $new_fee_id;
$new_fee->name = esc_attr( $name );
$new_fee->amount = (float) esc_attr( $amount );
$new_fee->tax_class = $tax_class;
$new_fee->taxable = $taxable ? true : false;
$new_fee->tax = 0;
$new_fee->tax_data = array();
$this->fees[] = $new_fee;
$this->fees_api()->add_fee( array(
'name' => $name,
'amount' => (float) $amount,
'taxable' => $taxable,
'tax_class' => $tax_class,
) );
}
/**
* Get fees.
* Return all added fees from the Fees API.
*
* @uses WC_Cart_Fees::get_fees
* @return array
*/
public function get_fees() {
return array_filter( (array) $this->fees );
}
/**
* Calculate fees.
*/
public function calculate_fees() {
$this->fees = array();
$this->set_fee_total( 0 );
$this->set_fee_tax( 0 );
$this->set_fee_taxes( array() );
// Fire an action where developers can add their fees.
do_action( 'woocommerce_cart_calculate_fees', $this );
return $this->fees_api()->get_fees();
}
/**

View File

@ -31,6 +31,70 @@ class WC_Order_Item_Fee extends WC_Order_Item {
),
);
/**
* Get item costs grouped by tax class.
*
* @since 3.2.0
* @param WC_Order $order Order object.
* @return array
*/
protected function get_tax_class_costs( $order ) {
$costs = array_fill_keys( $order->get_items_tax_classes(), 0 );
$costs['non-taxable'] = 0;
foreach ( $order->get_items( array( 'line_item', 'fee', 'shipping' ) ) as $item ) {
if ( 0 > $item->get_total() ) {
continue;
}
if ( 'taxable' !== $item->get_tax_status() ) {
$costs['non-taxable'] += $item->get_total();
} elseif ( 'inherit' === $item->get_tax_class() ) {
$costs[ reset( $order->get_items_tax_classes() ) ] += $item->get_total();
} else {
$costs[ $item->get_tax_class() ] += $item->get_total();
}
}
return array_filter( $costs );
}
/**
* Calculate item taxes.
*
* @since 3.2.0
* @param array $calculate_tax_for Location data to get taxes for. Required.
* @return bool True if taxes were calculated.
*/
public function calculate_taxes( $calculate_tax_for = array() ) {
if ( ! isset( $calculate_tax_for['country'], $calculate_tax_for['state'], $calculate_tax_for['postcode'], $calculate_tax_for['city'] ) ) {
return false;
}
// Use regular calculation unless the fee is negative.
if ( 0 <= $this->get_total() ) {
return parent::calculate_taxes();
}
if ( wc_tax_enabled() && ( $order = $this->get_order() ) ) {
// Apportion taxes to order items, shipping, and fees.
$order = $this->get_order();
$tax_class_costs = $this->get_tax_class_costs( $order );
$total_costs = array_sum( $tax_class_costs );
$discount_taxes = array();
if ( $total_costs ) {
foreach ( $tax_class_costs as $tax_class => $tax_class_cost ) {
if ( 'non-taxable' === $tax_class ) {
continue;
}
$proportion = $tax_class_cost / $total_costs;
$cart_discount_proportion = $this->get_total() * $proportion;
$discount_taxes = wc_array_merge_recursive_numeric( $discount_taxes, WC_Tax::calc_tax( $cart_discount_proportion, WC_Tax::get_rates( $tax_class ) ) );
}
}
$this->set_taxes( array( 'total' => $discount_taxes ) );
} else {
$this->set_taxes( false );
}
return true;
}
/*
|--------------------------------------------------------------------------
| Setters

View File

@ -58,7 +58,7 @@ abstract class WC_Legacy_Cart {
* @param mixed $value Value to set.
*/
public function __isset( $name ) {
if ( array_key_exists( $name, $cart_session_data ) ) {
if ( array_key_exists( $name, $cart_session_data ) || 'fees' === $name ) {
return true;
}
return false;
@ -114,6 +114,8 @@ abstract class WC_Legacy_Cart {
return $this->get_cart_contents_weight();
case 'cart_contents_count' :
return $this->get_cart_contents_count();
case 'fees' :
return $this->fees_api->get_fees();
case 'tax' :
wc_deprecated_argument( 'WC_Cart->tax', '2.3', 'Use WC_Tax:: directly' );
$this->tax = new WC_Tax();
@ -177,6 +179,9 @@ abstract class WC_Legacy_Cart {
case 'coupon_discount_tax_amounts' :
$this->set_coupon_discount_tax_totals( $value );
break;
case 'fees' :
$this->fees_api->set_fees( $value );
break;
default :
$this->$name = $value;
break;

View File

@ -21,21 +21,87 @@ class WC_Helper_Fee {
}
/**
* Add a cart simple fee without taxes.
* Note: need to be added before add any product in the cart.
* Create a cart fee with taxes.
*
* @since 2.3
* @since 3.2
*/
public static function add_cart_fee() {
add_action( 'woocommerce_cart_calculate_fees', array( __CLASS__, 'create_simple_fee' ) );
public static function create_taxed_fee() {
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) {
return;
}
WC()->cart->add_fee( 'Dummy Taxed Fee', 10, true );
}
/**
* Remove a cart simple fee without taxes.
* Create a negative cart fee without taxes.
*
* @since 3.2
*/
public static function create_negative_fee() {
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) {
return;
}
WC()->cart->add_fee( 'Dummy Negative Fee', -10 );
}
/**
* Create a negative cart fee with taxes.
*
* @since 3.2
*/
public static function create_negative_taxed_fee() {
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) {
return;
}
WC()->cart->add_fee( 'Dummy Negative Taxed Fee', -10, true );
}
/**
* Add a cart fee.
* Note: need to be added before add any product in the cart.
*
* @since 2.3
* @param string $fee Type of fee to add (Default: simple)
*/
public static function remove_cart_fee() {
remove_action( 'woocommerce_cart_calculate_fees', array( __CLASS__, 'create_simple_fee' ) );
public static function add_cart_fee( $fee = '' ) {
switch ( $fee ) {
case 'taxed':
add_action( 'woocommerce_cart_calculate_fees', array( __CLASS__, 'create_taxed_fee' ) );
break;
case 'negative':
add_action( 'woocommerce_cart_calculate_fees', array( __CLASS__, 'create_negative_fee' ) );
break;
case 'negative-taxed':
add_action( 'woocommerce_cart_calculate_fees', array( __CLASS__, 'create_negative_taxed_fee' ) );
break;
default:
add_action( 'woocommerce_cart_calculate_fees', array( __CLASS__, 'create_simple_fee' ) );
}
}
/**
* Remove a cart fee.
*
* @since 2.3
* @param string $fee Type of fee to remove (Default: simple)
*/
public static function remove_cart_fee( $fee = '' ) {
switch ( $fee ) {
case 'taxed':
remove_action( 'woocommerce_cart_calculate_fees', array( __CLASS__, 'create_taxed_fee' ) );
break;
case 'negative':
remove_action( 'woocommerce_cart_calculate_fees', array( __CLASS__, 'create_negative_fee' ) );
break;
case 'negative-taxed':
remove_action( 'woocommerce_cart_calculate_fees', array( __CLASS__, 'create_negative_taxed_fee' ) );
break;
default:
remove_action( 'woocommerce_cart_calculate_fees', array( __CLASS__, 'create_simple_fee' ) );
}
}
}

View File

@ -0,0 +1,59 @@
<?php
/**
* Class WC_Cart_Fees.
* @package WooCommerce\Tests\Cart
* @since 3.2.0
*/
class WC_Tests_WC_Cart_Fees extends WC_Unit_Test_Case {
/**
* Test the set/get/remove methods of WC_Cart_Fees.
*
* @since 3.2.0
*/
public function test_set_get_remove_fees() {
$cart_fees = new WC_Cart_Fees( wc()->cart );
// Test add_fee.
$args = array(
'name' => 'testfee',
'amount' => 10,
);
$cart_fees->add_fee( $args );
$applied_fees = $cart_fees->get_fees();
$this->assertEquals( 'testfee', $applied_fees['testfee']->name );
$this->assertEquals( 10, $applied_fees['testfee']->amount );
$this->assertEquals( 1, count( $applied_fees ) );
// Test remove_all_fees.
$cart_fees->remove_all_fees();
$this->assertEquals( array(), $cart_fees->get_fees() );
// Test set_fees.
$args = array(
array(
'name' => 'newfee',
'amount' => -5,
),
array(
'name' => 'newfee2',
'amount' => 10,
'tax_class' => 'Reduced rate',
'taxable' => true
),
);
$cart_fees->set_fees( $args );
$applied_fees = $cart_fees->get_fees();
$this->assertEquals( -5, $applied_fees['newfee']->amount );
$this->assertEquals( 'Reduced rate', $applied_fees['newfee2']->tax_class );
$this->assertEquals( 2, count( $applied_fees ) );
// Clean up.
WC()->cart->empty_cart();
// Test fees are removed when cart is emptied.
$this->assertEquals( array(), $cart_fees->get_fees() );
}
}

View File

@ -601,38 +601,168 @@ class WC_Tests_Cart extends WC_Unit_Test_Case {
* @since 2.3
*/
public function test_cart_fee() {
// Create product
// Create product.
$product = WC_Helper_Product::create_simple_product();
update_post_meta( $product->get_id(), '_price', '10' );
update_post_meta( $product->get_id(), '_regular_price', '10' );
// We need this to have the calculate_totals() method calculate totals
// We need this to have the calculate_totals() method calculate totals.
if ( ! defined( 'WOOCOMMERCE_CHECKOUT' ) ) {
define( 'WOOCOMMERCE_CHECKOUT', true );
}
// Add fee
// Add fee.
WC_Helper_Fee::add_cart_fee();
// Add product to cart
// Add product to cart.
WC()->cart->add_to_cart( $product->get_id(), 1 );
// Test if the cart total amount is equal 20
// Test if the cart total amount is equal 20.
$this->assertEquals( 20, WC()->cart->total );
// Clearing WC notices
// Clean up.
wc_clear_notices();
// Clean up the cart
WC()->cart->empty_cart();
// Remove fee
WC_Helper_Fee::remove_cart_fee();
// Delete product
WC_Helper_Product::delete_product( $product->get_id() );
}
/**
* Test cart fee with taxes.
*
* @since 3.2
*/
public function test_cart_fee_taxes() {
global $wpdb;
// Set up taxes.
update_option( 'woocommerce_calc_taxes', 'yes' );
$tax_rate = array(
'tax_rate_country' => '',
'tax_rate_state' => '',
'tax_rate' => '10.0000',
'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 );
// Create product.
$product = WC_Helper_Product::create_simple_product();
update_post_meta( $product->get_id(), '_price', '10' );
update_post_meta( $product->get_id(), '_regular_price', '10' );
// We need this to have the calculate_totals() method calculate totals.
if ( ! defined( 'WOOCOMMERCE_CHECKOUT' ) ) {
define( 'WOOCOMMERCE_CHECKOUT', true );
}
// Add fee.
WC_Helper_Fee::add_cart_fee( 'taxed' );
// Add product to cart.
WC()->cart->add_to_cart( $product->get_id(), 1 );
// Test if the cart total amount is equal 22 ($10 item + $10 fee + 10% taxes).
$this->assertEquals( 22, WC()->cart->total );
// Clean up.
wc_clear_notices();
WC()->cart->empty_cart();
WC_Helper_Fee::remove_cart_fee( 'taxed' );
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_calc_taxes', 'no' );
}
/**
* Test negative cart fee.
*
* @since 3.2
*/
public function test_cart_negative_fee() {
// Create product.
$product = WC_Helper_Product::create_simple_product();
update_post_meta( $product->get_id(), '_price', '15' );
update_post_meta( $product->get_id(), '_regular_price', '15' );
// We need this to have the calculate_totals() method calculate totals.
if ( ! defined( 'WOOCOMMERCE_CHECKOUT' ) ) {
define( 'WOOCOMMERCE_CHECKOUT', true );
}
// Add fee.
WC_Helper_Fee::add_cart_fee( 'negative' );
// Add product to cart.
WC()->cart->add_to_cart( $product->get_id(), 1 );
// Test if the cart total amount is equal 5.
$this->assertEquals( 5, WC()->cart->total );
// Clean up.
wc_clear_notices();
WC()->cart->empty_cart();
WC_Helper_Fee::remove_cart_fee( 'negative' );
WC_Helper_Product::delete_product( $product->get_id() );
}
/**
* Test negative cart fee with taxes.
*
* @since 3.2
*/
public function test_cart_negative_fee_taxes() {
global $wpdb;
// Set up taxes.
update_option( 'woocommerce_calc_taxes', 'yes' );
$tax_rate = array(
'tax_rate_country' => '',
'tax_rate_state' => '',
'tax_rate' => '10.0000',
'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 );
// Create product.
$product = WC_Helper_Product::create_simple_product();
update_post_meta( $product->get_id(), '_price', '15' );
update_post_meta( $product->get_id(), '_regular_price', '15' );
// We need this to have the calculate_totals() method calculate totals.
if ( ! defined( 'WOOCOMMERCE_CHECKOUT' ) ) {
define( 'WOOCOMMERCE_CHECKOUT', true );
}
// Add fee.
WC_Helper_Fee::add_cart_fee( 'negative-taxed' );
// Add product to cart.
WC()->cart->add_to_cart( $product->get_id(), 1 );
// Test if the cart total amount is equal 5.50 ($15 item - $10 negative fee + 10% tax).
$this->assertEquals( 5.50, WC()->cart->total );
// Clean up.
wc_clear_notices();
WC()->cart->empty_cart();
WC_Helper_Fee::remove_cart_fee( 'negative-taxed' );
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_calc_taxes', 'no' );
}
/**
* Test cart coupons.
*/