Merge pull request #16303 from woocommerce/feature/discount-cart-integration

Integrate WC_Cart_Totals with Cart
This commit is contained in:
Claudiu Lodromanean 2017-08-08 07:34:36 -07:00 committed by GitHub
commit 591dbd5a82
8 changed files with 476 additions and 878 deletions

View File

@ -1,218 +0,0 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* WC_Cart_Item
*
* Class object which represents an item in the cart.
*
* @version 3.2.0
* @package WooCommerce/Classes
* @category Class
* @author Automattic
*/
class WC_Cart_Item implements ArrayAccess {
/**
* Cart Data array.
*
* @var array
*/
protected $data = array(
'product_id' => 0,
'quantity' => 0,
'variation' => array(),
);
/**
* Product this item represents.
*
* @var WC_Product
*/
protected $product = null;
/**
* Constructor.
*
* @param array $data
*/
public function __construct( $data = array() ) {
$this->set_data( $data );
}
/**
* Gets price of the product.
* @return float
*/
public function get_price() {
return $this->get_product() ? $this->get_product()->get_price() : 0;
}
/**
* Gets weight of the product.
* @return float
*/
public function get_weight() {
return $this->get_product() ? $this->get_product()->get_weight() : 0;
}
/**
* Gets tax class of the product.
* @return float
*/
public function get_tax_class() {
return $this->get_product() ? $this->get_product()->get_tax_class() : '';
}
/**
* Set product.
* @param int $value
*/
public function set_product( $value ) {
$this->product = $value;
$this->data['product_id'] = is_callable( array( $this->product, 'get_variation_id' ) ) ? $this->product->get_variation_id() : $this->product->get_id();
}
/**
* Get product object.
*
* @return WC_Product
*/
public function get_product() {
return ! is_null( $this->product ) ? $this->product : ( $this->product = wc_get_product( $this->get_product_id() ) );
}
/**
* Get all item data.
* @return array
*/
public function get_data() {
return $this->data;
}
/**
* Product or variation ID this item represents.
* @return int
*/
public function get_product_id() {
return $this->data['product_id'];
}
/**
* Get quantity in cart.
* @return int
*/
public function get_quantity() {
return $this->data['quantity'];
}
/**
* Get variation data.
* @return array
*/
public function get_variation() {
return $this->data['variation'];
}
/**
* Set product ID.
* @param int $value
*/
public function set_product_id( $value ) {
$this->data['product_id'] = absint( $value );
$this->product = null;
}
/**
* Set Quantity.
* @param int $value
*/
public function set_quantity( $value ) {
$this->data['quantity'] = wc_stock_amount( $value );
}
/**
* Set variation data.
* @param array $value
*/
public function set_variation( $value ) {
$this->data['variation'] = (array) $value;
}
/**
* Set all data.
* @param array $value
*/
public function set_data( $values ) {
if ( is_a( $values, 'WC_Cart_Item' ) ) {
$values = $values->get_data();
}
foreach ( $values as $key => $value ) {
if ( in_array( $key, array( 'quantity', 'product_id', 'variation', 'product' ) ) ) {
$this->{ "set_$key" }( $value );
} else {
$this->data[ $key ] = $value;
}
}
}
/**
* ArrayAccess/Backwards compatibility.
*
* @param string $offset
* @return mixed
*/
public function offsetGet( $offset ) {
switch ( $offset ) {
case 'data' :
return $this->get_product();
case 'variation_id' :
return is_callable( array( $this, 'get_variation_id' ) ) ? $this->get_product()->get_variation_id() : 0;
}
return isset( $this->data[ $offset ] ) ? $this->data[ $offset ] : '';
}
/**
* ArrayAccess/Backwards compatibility.
*
* @param string $offset
* @param mixed $value
*/
public function offsetSet( $offset, $value ) {
switch ( $offset ) {
case 'data' :
$this->set_product( $value );
break;
case 'variation_id' :
$this->set_product( wc_get_product( $value ) );
break;
default :
$this->data[ $offset ] = $value;
break;
}
}
/**
* ArrayAccess/Backwards compatibility.
*
* @param string $offset
* @return bool
*/
public function offsetExists( $offset ) {
if ( in_array( $offset, array( 'data' ) ) || isset( $this->data[ $offset ] ) ) {
return true;
}
return false;
}
/**
* ArrayAccess/Backwards compatibility.
*
* @param string $offset
*/
public function offsetUnset( $offset ) {
unset( $this->data[ $offset ] );
}
}

View File

@ -235,7 +235,7 @@ final class WC_Cart_Totals {
$fee->total_tax = array_sum( $fee->taxes );
if ( ! $this->round_at_subtotal() ) {
$fee->total_tax = wc_round_tax_total( $fee->total_tax, 0 );
$fee->total_tax = wc_round_tax_total( $fee->total_tax, wc_get_rounding_precision() );
}
}
@ -259,7 +259,7 @@ final class WC_Cart_Totals {
$shipping_line->total_tax = wc_add_number_precision_deep( array_sum( $shipping_object->taxes ) );
if ( ! $this->round_at_subtotal() ) {
$shipping_line->total_tax = wc_round_tax_total( $shipping_line->total_tax, 0 );
$shipping_line->total_tax = wc_round_tax_total( $shipping_line->total_tax, wc_get_rounding_precision() );
}
$this->shipping[ $key ] = $shipping_line;
@ -282,12 +282,14 @@ final class WC_Cart_Totals {
* If the customer is outside of the base location, this removes the base
* taxes. This is off by default unless the filter is used.
*
* Uses edit context so unfiltered tax class is returned.
*
* @since 3.2.0
* @param object $item Item to adjust the prices of.
* @return object
*/
protected function adjust_non_base_location_price( $item ) {
$base_tax_rates = WC_Tax::get_base_tax_rates( $item->product->tax_class );
$base_tax_rates = WC_Tax::get_base_tax_rates( $item->product->get_tax_class( 'edit' ) );
if ( $item->tax_rates !== $base_tax_rates ) {
// Work out a new base price without the shop's base tax.
@ -308,7 +310,13 @@ final class WC_Cart_Totals {
* @return int
*/
protected function get_discounted_price_in_cents( $item_key ) {
return $this->items[ $item_key ]->subtotal - $this->discount_totals[ $item_key ];
$item = $this->items[ $item_key ];
$price = $item->subtotal - $this->discount_totals[ $item_key ];
if ( $item->price_includes_tax ) {
$price += $item->subtotal_tax;
}
return $price;
}
/**
@ -424,7 +432,7 @@ final class WC_Cart_Totals {
$item->total_tax = array_sum( $item->taxes );
if ( ! $this->round_at_subtotal() ) {
$item->total_tax = wc_round_tax_total( $item->total_tax, 0 );
$item->total_tax = wc_round_tax_total( $item->total_tax, wc_get_rounding_precision() );
}
if ( $item->price_includes_tax ) {
@ -433,13 +441,13 @@ final class WC_Cart_Totals {
$item->total = $item->total;
}
}
$this->object->cart_contents[ $item_key ]['line_total'] = wc_remove_number_precision( $item->total );
$this->object->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_tax', array_sum( array_values( wp_list_pluck( $this->items, 'total_tax' ) ) ) );
$this->object->subtotal = $this->get_total( 'items_total' ) + $this->get_total( 'items_total_tax' );
$this->object->subtotal_ex_tax = $this->get_total( 'items_total' );
}
/**
@ -457,28 +465,32 @@ final class WC_Cart_Totals {
* @since 3.2.0
*/
protected function calculate_item_subtotals() {
foreach ( $this->items as $item ) {
foreach ( $this->items as $item_key => $item ) {
if ( $item->price_includes_tax && apply_filters( 'woocommerce_adjust_non_base_location_prices', true ) ) {
$item = $this->adjust_non_base_location_price( $item );
$item = $this->adjust_non_base_location_price( $item );
}
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, 0 );
$item->subtotal_tax = wc_round_tax_total( $item->subtotal_tax, wc_get_rounding_precision() );
}
if ( $item->price_includes_tax ) {
$item->subtotal = $item->subtotal - $item->subtotal_tax;
}
}
$this->object->cart_contents[ $item_key ]['line_subtotal'] = wc_remove_number_precision( $item->subtotal );
$this->object->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_tax', array_sum( array_values( wp_list_pluck( $this->items, 'subtotal_tax' ) ) ) );
$this->object->subtotal = $this->get_total( 'items_total' ) + $this->get_total( 'items_total_tax' );
$this->object->subtotal_ex_tax = $this->get_total( 'items_total' );
$this->object->subtotal = $this->get_total( 'items_subtotal' ) + $this->get_total( 'items_subtotal_tax' );
$this->object->subtotal_ex_tax = $this->get_total( 'items_subtotal' );
}
/**
@ -496,15 +508,11 @@ final class WC_Cart_Totals {
$discounts->apply_discount( $coupon );
}
$this->discount_totals = $discounts->get_discounts_by_item( true );
$this->totals['discounts_total'] = ! empty( $this->discount_totals ) ? array_sum( $this->discount_totals ) : 0;
$this->object->coupon_discount_amounts = $discounts->get_discounts_by_coupon();
$coupon_discount_amounts = $discounts->get_discounts_by_coupon( true );
$coupon_discount_tax_amounts = array();
// See how much tax was 'discounted' per item and per coupon.
if ( $this->calculate_tax ) {
$coupon_discount_tax_amounts = array();
$item_taxes = 0;
foreach ( $discounts->get_discounts( true ) as $coupon_code => $coupon_discounts ) {
$coupon_discount_tax_amounts[ $coupon_code ] = 0;
@ -512,16 +520,21 @@ final class WC_Cart_Totals {
$item = $this->items[ $item_key ];
if ( $item->product->is_taxable() ) {
$item_tax = array_sum( WC_Tax::calc_tax( $item_discount, $item->tax_rates, false ) );
$item_taxes += $item_tax;
$item_tax = array_sum( WC_Tax::calc_tax( $item_discount, $item->tax_rates, $item->price_includes_tax ) );
$coupon_discount_tax_amounts[ $coupon_code ] += $item_tax;
}
}
}
$this->totals['discounts_tax_total'] = $item_taxes;
$this->object->coupon_discount_tax_amounts = $coupon_discount_tax_amounts;
$coupon_discount_amounts[ $coupon_code ] -= $coupon_discount_tax_amounts[ $coupon_code ];
}
}
$this->discount_totals = $discounts->get_discounts_by_item( true );
$this->object->coupon_discount_amounts = wc_remove_number_precision_deep( $coupon_discount_amounts );
$this->object->coupon_discount_tax_amounts = wc_remove_number_precision_deep( $coupon_discount_tax_amounts );
$this->set_total( 'discounts_total', ! empty( $this->discount_totals ) ? array_sum( $this->discount_totals ) : 0 );
$this->set_total( 'discounts_tax_total', array_sum( $coupon_discount_tax_amounts ) );
}
/**
@ -583,10 +596,13 @@ final class WC_Cart_Totals {
$this->set_total( 'total', round( $this->get_total( 'items_total', true ) + $this->get_total( 'fees_total', true ) + $this->get_total( 'shipping_total', true ) + $this->get_total( 'tax_total', true ) + $this->get_total( 'shipping_tax_total', true ) ) );
// Add totals to cart object.
$this->object->taxes = wp_list_pluck( $this->get_total( 'taxes' ), 'shipping_tax_total' );
$this->object->shipping_taxes = wp_list_pluck( $this->get_total( 'taxes' ), 'tax_total' );
$this->object->tax_total = $this->get_total( 'tax_total' );
$this->object->total = $this->get_total( 'total' );
$this->object->taxes = wp_list_pluck( $this->get_total( 'taxes' ), 'shipping_tax_total' );
$this->object->shipping_taxes = wp_list_pluck( $this->get_total( 'taxes' ), 'tax_total' );
$this->object->cart_contents_total = $this->get_total( 'items_total' );
$this->object->tax_total = $this->get_total( 'tax_total' );
$this->object->total = $this->get_total( 'total' );
$this->object->discount_cart = $this->get_total( 'discounts_total' ) - $this->get_total( 'discounts_tax_total' );
$this->object->discount_cart_tax = $this->get_total( 'discounts_tax_total' );
// Allow plugins to hook and alter totals before final total is calculated.
if ( has_action( 'woocommerce_calculate_totals' ) ) {

File diff suppressed because it is too large Load Diff

View File

@ -174,10 +174,10 @@ class WC_Discounts {
*
* @since 3.2.0
* @param object $item Get data for this item.
* @return float
* @return int
*/
public function get_discounted_price_in_cents( $item ) {
return $item->price - $this->get_discount( $item->key, true );
return absint( $item->price - $this->get_discount( $item->key, true ) );
}
/**
@ -388,6 +388,7 @@ class WC_Discounts {
*/
protected function apply_coupon_percent( $coupon, $items_to_apply ) {
$total_discount = 0;
$cart_total = 0;
foreach ( $items_to_apply as $item ) {
// Find out how much price is available to discount for the item.
@ -396,14 +397,25 @@ class WC_Discounts {
// Get the price we actually want to discount, based on settings.
$price_to_discount = ( 'yes' === get_option( 'woocommerce_calc_discounts_sequentially', 'no' ) ) ? $item->price: $discounted_price;
// Total up.
$cart_total += $price_to_discount;
// Run coupon calculations.
$discount = $coupon->get_amount() * ( $price_to_discount / 100 );
$discount = floor( $price_to_discount * ( $coupon->get_amount() / 100 ) );
$discount = min( $discounted_price, apply_filters( 'woocommerce_coupon_get_discount_amount', $discount, $price_to_discount, $item->object, false, $coupon ) );
$total_discount += $discount;
// Store code and discount amount per item.
$this->discounts[ $coupon->get_code() ][ $item->key ] += $discount;
}
// Work out how much discount would have been given to the cart has a whole and compare to what was discounted on all line items.
$cart_total_discount = wc_cart_round_discount( $cart_total * ( $coupon->get_amount() / 100 ), 0 );
if ( $total_discount < $cart_total_discount ) {
$total_discount += $this->apply_coupon_remainder( $coupon, $items_to_apply, $cart_total_discount - $total_discount );
}
return $total_discount;
}
@ -449,26 +461,26 @@ class WC_Discounts {
*/
protected function apply_coupon_fixed_cart( $coupon, $items_to_apply, $amount = null ) {
$total_discount = 0;
$amount = $amount ? $amount: wc_add_number_precision( $coupon->get_amount() );
$amount = $amount ? $amount : wc_add_number_precision( $coupon->get_amount() );
$items_to_apply = array_filter( $items_to_apply, array( $this, 'filter_products_with_price' ) );
if ( ! $item_count = array_sum( wp_list_pluck( $items_to_apply, 'quantity' ) ) ) {
return $total_discount;
}
$per_item_discount = floor( $amount / $item_count ); // round it down to the nearest cent.
$per_item_discount = absint( $amount / $item_count ); // round it down to the nearest cent.
if ( $per_item_discount > 0 ) {
$total_discounted = $this->apply_coupon_fixed_product( $coupon, $items_to_apply, $per_item_discount );
$total_discount = $this->apply_coupon_fixed_product( $coupon, $items_to_apply, $per_item_discount );
/**
* If there is still discount remaining, repeat the process.
*/
if ( $total_discounted > 0 && $total_discounted < $amount ) {
$total_discounted = $total_discounted + $this->apply_coupon_fixed_cart( $coupon, $items_to_apply, $amount - $total_discounted );
if ( $total_discount > 0 && $total_discount < $amount ) {
$total_discount += $this->apply_coupon_fixed_cart( $coupon, $items_to_apply, $amount - $total_discount );
}
} elseif ( $amount > 0 ) {
$total_discounted = $this->apply_coupon_fixed_cart_remainder( $coupon, $items_to_apply, $amount );
$total_discount += $this->apply_coupon_remainder( $coupon, $items_to_apply, $amount );
}
return $total_discount;
}
@ -483,7 +495,7 @@ class WC_Discounts {
* @param int $amount Fixed discount amount to apply.
* @return int Total discounted.
*/
protected function apply_coupon_fixed_cart_remainder( $coupon, $items_to_apply, $amount ) {
protected function apply_coupon_remainder( $coupon, $items_to_apply, $amount ) {
$total_discount = 0;
foreach ( $items_to_apply as $item ) {

View File

@ -405,7 +405,6 @@ final class WooCommerce {
include_once( WC_ABSPATH . 'includes/class-wc-frontend-scripts.php' ); // Frontend Scripts.
include_once( WC_ABSPATH . 'includes/class-wc-form-handler.php' ); // Form Handlers.
include_once( WC_ABSPATH . 'includes/class-wc-cart.php' ); // The main cart class.
include_once( WC_ABSPATH . 'includes/class-wc-cart-item.php' );
include_once( WC_ABSPATH . 'includes/class-wc-tax.php' ); // Tax class.
include_once( WC_ABSPATH . 'includes/class-wc-shipping-zones.php' ); // Shipping Zones class.
include_once( WC_ABSPATH . 'includes/class-wc-customer.php' ); // Customer class.

View File

@ -23,22 +23,128 @@ abstract class WC_Legacy_Cart {
/**
* Contains an array of coupon usage counts after they have been applied.
*
* @deprecated 3.2.0
* @var array
*/
public $coupon_applied_count = array();
/**
* Store how many times each coupon is applied to cart/items.
* Function to apply discounts to a product and get the discounted price (before tax is applied).
*
* @deprecated 3.2.0
* @access private
* @param string $code
* @param int $count
* @deprecated Calculation and coupon logic is handled in WC_Cart_Totals.
* @param mixed $values Cart item.
* @param mixed $price Price of item.
* @param bool $add_totals Legacy.
* @return float price
*/
protected function increase_coupon_applied_count( $code, $count = 1 ) {
if ( empty( $this->coupon_applied_count[ $code ] ) ) {
$this->coupon_applied_count[ $code ] = 0;
public function get_discounted_price( $values, $price, $add_totals = false ) {
wc_deprecated_function( 'WC_Cart::get_discounted_price', '3.2', '' );
$cart_item_key = $values['key'];
$cart_item = $this->cart_contents[ $cart_item_key ];
return $cart_item->get_line_total();
}
/**
* Gets the url to the cart page.
*
* @deprecated 2.5.0 in favor to wc_get_cart_url()
* @return string url to page
*/
public function get_cart_url() {
wc_deprecated_function( 'WC_Cart::get_cart_url', '2.5', 'wc_get_cart_url' );
return wc_get_cart_url();
}
/**
* Gets the url to the checkout page.
*
* @deprecated 2.5.0 in favor to wc_get_checkout_url()
* @return string url to page
*/
public function get_checkout_url() {
wc_deprecated_function( 'WC_Cart::get_checkout_url', '2.5', 'wc_get_checkout_url' );
return wc_get_checkout_url();
}
/**
* Sees if we need a shipping address.
*
* @deprecated 2.5.0 in favor to wc_ship_to_billing_address_only()
* @return bool
*/
public function ship_to_billing_address_only() {
wc_deprecated_function( 'WC_Cart::ship_to_billing_address_only', '2.5', 'wc_ship_to_billing_address_only' );
return wc_ship_to_billing_address_only();
}
/**
* Coupons enabled function. Filterable.
*
* @deprecated 2.5.0 in favor to wc_coupons_enabled()
* @return bool
*/
public function coupons_enabled() {
return wc_coupons_enabled();
}
/**
* Gets the total (product) discount amount - these are applied before tax.
*
* @deprecated Order discounts (after tax) removed in 2.3 so multiple methods for discounts are no longer required.
* @return mixed formatted price or false if there are none.
*/
public function get_discounts_before_tax() {
wc_deprecated_function( 'get_discounts_before_tax', '2.3', 'get_total_discount' );
if ( $this->get_cart_discount_total() ) {
$discounts_before_tax = wc_price( $this->get_cart_discount_total() );
} else {
$discounts_before_tax = false;
}
$this->coupon_applied_count[ $code ] += $count;
return apply_filters( 'woocommerce_cart_discounts_before_tax', $discounts_before_tax, $this );
}
/**
* Get the total of all order discounts (after tax discounts).
*
* @deprecated Order discounts (after tax) removed in 2.3.
* @return int
*/
public function get_order_discount_total() {
wc_deprecated_function( 'get_order_discount_total', '2.3' );
return 0;
}
/**
* Function to apply cart discounts after tax.
*
* @deprecated Coupons can not be applied after tax.
* @param $values
* @param $price
*/
public function apply_cart_discounts_after_tax( $values, $price ) {
wc_deprecated_function( 'apply_cart_discounts_after_tax', '2.3' );
}
/**
* Function to apply product discounts after tax.
*
* @deprecated Coupons can not be applied after tax.
*
* @param $values
* @param $price
*/
public function apply_product_discounts_after_tax( $values, $price ) {
wc_deprecated_function( 'apply_product_discounts_after_tax', '2.3' );
}
/**
* Gets the order discount amount - these are applied after tax.
*
* @deprecated Coupons can not be applied after tax.
*/
public function get_discounts_after_tax() {
wc_deprecated_function( 'get_discounts_after_tax', '2.3' );
}
}

View File

@ -150,6 +150,75 @@ class WC_Tests_Cart extends WC_Unit_Test_Case {
WC_Helper_Coupon::delete_coupon( $coupon->get_id() );
}
/**
* Test that calculation rounding is done correctly with and without taxes.
*
* @see https://github.com/woocommerce/woocommerce/issues/16305
* @since 3.2
*/
public function test_discount_cart_rounding() {
global $wpdb;
# Test with no taxes.
WC()->cart->empty_cart();
WC()->cart->remove_coupons();
$product = new WC_Product_Simple;
$product->set_regular_price( 51.86 );
$product->save();
$coupon = new WC_Coupon;
$coupon->set_code( 'testpercent' );
$coupon->set_discount_type( 'percent' );
$coupon->set_amount( 40 );
$coupon->save();
WC()->cart->add_to_cart( $product->get_id(), 1 );
WC()->cart->add_discount( $coupon->get_code() );
WC()->cart->calculate_totals();
$cart_item = current( WC()->cart->get_cart() );
$this->assertEquals( '31.12', number_format( $cart_item['line_total'], 2, '.', '' ) );
// Clean up.
WC()->cart->empty_cart();
WC()->cart->remove_coupons();
# Test with taxes.
update_option( 'woocommerce_prices_include_tax', 'no' );
update_option( 'woocommerce_calc_taxes', 'yes' );
$tax_rate = array(
'tax_rate_country' => '',
'tax_rate_state' => '',
'tax_rate' => '8.2500',
'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 );
WC()->cart->add_to_cart( $product->get_id(), 1 );
WC()->cart->add_discount( $coupon->get_code() );
WC()->cart->calculate_totals();
$cart_item = current( WC()->cart->get_cart() );
$this->assertEquals( '33.69', number_format( $cart_item['line_total'] + $cart_item['line_tax'], 2, '.', '' ) );
// Clean up.
WC()->cart->empty_cart();
WC()->cart->remove_coupons();
WC_Helper_Product::delete_product( $product->get_id() );
WC_Helper_Coupon::delete_coupon( $coupon->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.
*

View File

@ -147,10 +147,10 @@ class WC_Tests_Totals extends WC_Unit_Test_Case {
$this->assertEquals( 40.00, $cart->fee_total );
$this->assertEquals( 27.00, $cart->cart_contents_total );
$this->assertEquals( 90.40, $cart->total );
$this->assertEquals( 32.40, $cart->subtotal );
$this->assertEquals( 27.00, $cart->subtotal_ex_tax );
$this->assertEquals( 36.00, $cart->subtotal );
$this->assertEquals( 30.00, $cart->subtotal_ex_tax );
$this->assertEquals( 11.40, $cart->tax_total );
$this->assertEquals( 3.00, $cart->discount_cart );
$this->assertEquals( 2.40, $cart->discount_cart );
$this->assertEquals( 0.60, $cart->discount_cart_tax );
$this->assertEquals( 40.00, $cart->fee_total );
$this->assertEquals( 10, $cart->shipping_total );