Merge pull request #16569 from woocommerce/update/fees-api-class
WC_Cart_fees and negative fee calculations
This commit is contained in:
commit
22813663d7
|
@ -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 );
|
||||
}
|
||||
}
|
|
@ -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' ) ) ) );
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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' ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() );
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue