Store fee amount and prevent totals going negative

This commit is contained in:
Mike Jolley 2017-08-23 12:15:06 +01:00
parent 372723dae1
commit 804feb9333
10 changed files with 78 additions and 17 deletions

View File

@ -1324,11 +1324,6 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
$cart_total += $item->get_total();
}
// Sum fee costs.
foreach ( $this->get_fees() as $item ) {
$fee_total += $item->get_total();
}
// Sum shipping costs.
foreach ( $this->get_shipping_methods() as $shipping ) {
$shipping_total += $shipping->get_total();
@ -1336,6 +1331,21 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
$this->set_shipping_total( $shipping_total );
// Sum fee costs.
foreach ( $this->get_fees() as $item ) {
$amount = $item->get_amount();
if ( 0 > $amount ) {
$max_discount = round( $cart_total + $fee_total + $shipping_total, wc_get_price_decimals() ) * -1;
if ( $item->get_total() < $max_discount ) {
$item->set_total( $max_discount );
}
}
$fee_total += $item->get_total();
}
// Calculate taxes for items, shipping, discounts.
if ( $and_taxes ) {
$this->calculate_taxes();

View File

@ -228,6 +228,10 @@ function wc_save_order_items( $order_id, $items ) {
),
) );
if ( 'fee' === $item->get_type() ) {
$item->set_amount( $item_data['line_total'] );
}
if ( isset( $items['meta_key'][ $item_id ], $items['meta_value'][ $item_id ] ) ) {
foreach ( $items['meta_key'][ $item_id ] as $meta_id => $meta_key ) {
$meta_value = isset( $items['meta_value'][ $item_id ][ $meta_id ] ) ? wp_unslash( $items['meta_value'][ $item_id ][ $meta_id ] ) : '';

View File

@ -827,6 +827,7 @@ class WC_AJAX {
}
$fee = new WC_Order_Item_Fee();
$fee->set_amount( $amount );
$fee->set_total( $amount );
$fee->set_name( sprintf( __( '%s fee', 'woocommerce' ), $formatted_amount ) );

View File

@ -47,6 +47,7 @@ final class WC_Cart_Fees {
'tax_class' => '',
'taxable' => false,
'amount' => 0,
'total' => 0,
);
/**

View File

@ -268,6 +268,8 @@ final class WC_Cart_Totals {
$this->fees = array();
$this->cart->calculate_fees();
$fee_running_total = 0;
foreach ( $this->cart->get_fees() as $fee_key => $fee_object ) {
$fee = $this->get_default_fee_props();
$fee->object = $fee_object;
@ -275,6 +277,17 @@ final class WC_Cart_Totals {
$fee->taxable = $fee->object->taxable;
$fee->total = wc_add_number_precision_deep( $fee->object->amount );
// Negative fees should not make the order total go negative.
if ( 0 > $fee->total ) {
$max_discount = round( $this->get_total( 'items_total', true ) + $fee_running_total + $this->get_total( 'shipping_total', true ) ) * -1;
if ( $fee->total < $max_discount ) {
$fee->total = $max_discount;
}
}
$fee_running_total += $fee->total;
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.
@ -304,8 +317,9 @@ final class WC_Cart_Totals {
}
// 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 );
$fee->object->total = wc_remove_number_precision_deep( $fee->total );
$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;
}
@ -691,6 +705,9 @@ final class WC_Cart_Totals {
$this->coupon_discount_totals = (array) $discounts->get_discounts_by_item( true );
$this->coupon_discount_tax_totals = $coupon_discount_tax_amounts;
$this->set_total( 'discounts_total', array_sum( $this->coupon_discount_totals ) );
$this->set_total( 'discounts_tax_total', array_sum( $this->coupon_discount_tax_totals ) );
$this->cart->set_coupon_discount_totals( wc_remove_number_precision_deep( $coupon_discount_amounts ) );
$this->cart->set_coupon_discount_tax_totals( wc_remove_number_precision_deep( $coupon_discount_tax_amounts ) );
}
@ -735,8 +752,6 @@ final class WC_Cart_Totals {
* @since 3.2.0
*/
protected function calculate_totals() {
$this->set_total( 'discounts_total', array_sum( $this->coupon_discount_totals ) );
$this->set_total( 'discounts_tax_total', array_sum( $this->coupon_discount_tax_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 ) ) ) );
// Add totals to cart object.

View File

@ -1701,6 +1701,8 @@ class WC_Cart extends WC_Legacy_Cart {
*/
public function calculate_fees() {
do_action( 'woocommerce_cart_calculate_fees', $this );
$this->add_fee( 'Test', -100 );
}
/**

View File

@ -401,8 +401,9 @@ class WC_Checkout {
$item->legacy_fee_key = $fee_key; // @deprecated For legacy actions.
$item->set_props( array(
'name' => $fee->name,
'tax_class' => $fee->taxable ? $fee->tax_class : 0,
'total' => $fee->amount,
'tax_class' => $fee->taxable ? $fee->tax_class: 0,
'amount' => $fee->amount,
'total' => $fee->total,
'total_tax' => $fee->tax,
'taxes' => array(
'total' => $fee->tax_data,

View File

@ -18,12 +18,14 @@ class WC_Order_Item_Fee extends WC_Order_Item {
/**
* Order Data array. This is the core order data exposed in APIs since 3.0.0.
*
* @since 3.0.0
* @var array
*/
protected $extra_data = array(
'tax_class' => '',
'tax_status' => 'taxable',
'amount' => '',
'total' => '',
'total_tax' => '',
'taxes' => array(
@ -39,8 +41,9 @@ class WC_Order_Item_Fee extends WC_Order_Item {
* @return array
*/
protected function get_tax_class_costs( $order ) {
$costs = array_fill_keys( $order->get_items_tax_classes(), 0 );
$costs['non-taxable'] = 0;
$order_item_tax_classes = $order->get_items_tax_classes();
$costs = array_fill_keys( $order_item_tax_classes, 0 );
$costs['non-taxable'] = 0;
foreach ( $order->get_items( array( 'line_item', 'fee', 'shipping' ) ) as $item ) {
if ( 0 > $item->get_total() ) {
@ -49,7 +52,8 @@ class WC_Order_Item_Fee extends WC_Order_Item {
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();
$inherit_class = reset( $order_item_tax_classes );
$costs[ $inherit_class ] += $item->get_total();
} else {
$costs[ $item->get_tax_class() ] += $item->get_total();
}
@ -70,7 +74,7 @@ class WC_Order_Item_Fee extends WC_Order_Item {
}
// Use regular calculation unless the fee is negative.
if ( 0 <= $this->get_total() ) {
return parent::calculate_taxes();
return parent::calculate_taxes( $calculate_tax_for );
}
if ( wc_tax_enabled() && ( $order = $this->get_order() ) ) {
// Apportion taxes to order items, shipping, and fees.
@ -101,6 +105,16 @@ class WC_Order_Item_Fee extends WC_Order_Item {
|--------------------------------------------------------------------------
*/
/**
* Set fee amount.
*
* @param string $value
* @throws WC_Data_Exception
*/
public function set_amount( $value ) {
$this->set_prop( 'amount', wc_format_decimal( $value ) );
}
/**
* Set tax class.
*
@ -173,6 +187,16 @@ class WC_Order_Item_Fee extends WC_Order_Item {
|--------------------------------------------------------------------------
*/
/**
* Get fee amount.
*
* @param string $context
* @return string
*/
public function get_amount( $context = 'view' ) {
return $this->get_prop( 'amount', $context );
}
/**
* Get order item name.
*

View File

@ -14,10 +14,11 @@ class WC_Order_Item_Fee_Data_Store extends Abstract_WC_Order_Item_Type_Data_Stor
/**
* Data stored in meta keys.
*
* @since 3.0.0
* @var array
*/
protected $internal_meta_keys = array( '_tax_class', '_tax_status', '_line_subtotal', '_line_subtotal_tax', '_line_total', '_line_tax', '_line_tax_data' );
protected $internal_meta_keys = array( '_fee_amount', '_tax_class', '_tax_status', '_line_subtotal', '_line_subtotal_tax', '_line_total', '_line_tax', '_line_tax_data' );
/**
* Read/populate data properties specific to this order item.
@ -29,6 +30,7 @@ class WC_Order_Item_Fee_Data_Store extends Abstract_WC_Order_Item_Type_Data_Stor
parent::read( $item );
$id = $item->get_id();
$item->set_props( array(
'amount' => get_metadata( 'order_item', $id, '_fee_amount', true ),
'tax_class' => get_metadata( 'order_item', $id, '_tax_class', true ),
'tax_status' => get_metadata( 'order_item', $id, '_tax_status', true ),
'total' => get_metadata( 'order_item', $id, '_line_total', true ),
@ -47,6 +49,7 @@ class WC_Order_Item_Fee_Data_Store extends Abstract_WC_Order_Item_Type_Data_Stor
public function save_item_data( &$item ) {
$id = $item->get_id();
$save_values = array(
'_fee_amount' => $item->get_amount( 'edit' ),
'_tax_class' => $item->get_tax_class( 'edit' ),
'_tax_status' => $item->get_tax_status( 'edit' ),
'_line_total' => $item->get_total( 'edit' ),

View File

@ -328,7 +328,7 @@ function wc_cart_totals_order_total_html() {
* @param object $fee
*/
function wc_cart_totals_fee_html( $fee ) {
$cart_totals_fee_html = ( 'excl' == WC()->cart->tax_display_cart ) ? wc_price( $fee->amount ) : wc_price( $fee->amount + $fee->tax );
$cart_totals_fee_html = ( 'excl' == WC()->cart->tax_display_cart ) ? wc_price( $fee->total ) : wc_price( $fee->total + $fee->tax );
echo apply_filters( 'woocommerce_cart_totals_fee_html', $cart_totals_fee_html, $fee );
}