Merge pull request #16303 from woocommerce/feature/discount-cart-integration
Integrate WC_Cart_Totals with Cart
This commit is contained in:
commit
591dbd5a82
|
@ -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 ] );
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -235,7 +235,7 @@ final class WC_Cart_Totals {
|
||||||
$fee->total_tax = array_sum( $fee->taxes );
|
$fee->total_tax = array_sum( $fee->taxes );
|
||||||
|
|
||||||
if ( ! $this->round_at_subtotal() ) {
|
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 ) );
|
$shipping_line->total_tax = wc_add_number_precision_deep( array_sum( $shipping_object->taxes ) );
|
||||||
|
|
||||||
if ( ! $this->round_at_subtotal() ) {
|
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;
|
$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
|
* If the customer is outside of the base location, this removes the base
|
||||||
* taxes. This is off by default unless the filter is used.
|
* taxes. This is off by default unless the filter is used.
|
||||||
*
|
*
|
||||||
|
* Uses edit context so unfiltered tax class is returned.
|
||||||
|
*
|
||||||
* @since 3.2.0
|
* @since 3.2.0
|
||||||
* @param object $item Item to adjust the prices of.
|
* @param object $item Item to adjust the prices of.
|
||||||
* @return object
|
* @return object
|
||||||
*/
|
*/
|
||||||
protected function adjust_non_base_location_price( $item ) {
|
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 ) {
|
if ( $item->tax_rates !== $base_tax_rates ) {
|
||||||
// Work out a new base price without the shop's base tax.
|
// Work out a new base price without the shop's base tax.
|
||||||
|
@ -308,7 +310,13 @@ final class WC_Cart_Totals {
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
protected function get_discounted_price_in_cents( $item_key ) {
|
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 );
|
$item->total_tax = array_sum( $item->taxes );
|
||||||
|
|
||||||
if ( ! $this->round_at_subtotal() ) {
|
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 ) {
|
if ( $item->price_includes_tax ) {
|
||||||
|
@ -433,13 +441,13 @@ final class WC_Cart_Totals {
|
||||||
$item->total = $item->total;
|
$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', 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->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
|
* @since 3.2.0
|
||||||
*/
|
*/
|
||||||
protected function calculate_item_subtotals() {
|
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 ) ) {
|
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() ) {
|
if ( $this->calculate_tax && $item->product->is_taxable() ) {
|
||||||
$subtotal_taxes = WC_Tax::calc_tax( $item->subtotal, $item->tax_rates, $item->price_includes_tax );
|
$subtotal_taxes = WC_Tax::calc_tax( $item->subtotal, $item->tax_rates, $item->price_includes_tax );
|
||||||
$item->subtotal_tax = array_sum( $subtotal_taxes );
|
$item->subtotal_tax = array_sum( $subtotal_taxes );
|
||||||
|
|
||||||
if ( ! $this->round_at_subtotal() ) {
|
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 ) {
|
if ( $item->price_includes_tax ) {
|
||||||
$item->subtotal = $item->subtotal - $item->subtotal_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', 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->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 = $this->get_total( 'items_subtotal' ) + $this->get_total( 'items_subtotal_tax' );
|
||||||
$this->object->subtotal_ex_tax = $this->get_total( 'items_total' );
|
$this->object->subtotal_ex_tax = $this->get_total( 'items_subtotal' );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -496,15 +508,11 @@ final class WC_Cart_Totals {
|
||||||
$discounts->apply_discount( $coupon );
|
$discounts->apply_discount( $coupon );
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->discount_totals = $discounts->get_discounts_by_item( true );
|
$coupon_discount_amounts = $discounts->get_discounts_by_coupon( true );
|
||||||
$this->totals['discounts_total'] = ! empty( $this->discount_totals ) ? array_sum( $this->discount_totals ) : 0;
|
$coupon_discount_tax_amounts = array();
|
||||||
$this->object->coupon_discount_amounts = $discounts->get_discounts_by_coupon();
|
|
||||||
|
|
||||||
// See how much tax was 'discounted' per item and per coupon.
|
// See how much tax was 'discounted' per item and per coupon.
|
||||||
if ( $this->calculate_tax ) {
|
if ( $this->calculate_tax ) {
|
||||||
$coupon_discount_tax_amounts = array();
|
|
||||||
$item_taxes = 0;
|
|
||||||
|
|
||||||
foreach ( $discounts->get_discounts( true ) as $coupon_code => $coupon_discounts ) {
|
foreach ( $discounts->get_discounts( true ) as $coupon_code => $coupon_discounts ) {
|
||||||
$coupon_discount_tax_amounts[ $coupon_code ] = 0;
|
$coupon_discount_tax_amounts[ $coupon_code ] = 0;
|
||||||
|
|
||||||
|
@ -512,16 +520,21 @@ final class WC_Cart_Totals {
|
||||||
$item = $this->items[ $item_key ];
|
$item = $this->items[ $item_key ];
|
||||||
|
|
||||||
if ( $item->product->is_taxable() ) {
|
if ( $item->product->is_taxable() ) {
|
||||||
$item_tax = array_sum( WC_Tax::calc_tax( $item_discount, $item->tax_rates, false ) );
|
$item_tax = array_sum( WC_Tax::calc_tax( $item_discount, $item->tax_rates, $item->price_includes_tax ) );
|
||||||
$item_taxes += $item_tax;
|
|
||||||
$coupon_discount_tax_amounts[ $coupon_code ] += $item_tax;
|
$coupon_discount_tax_amounts[ $coupon_code ] += $item_tax;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
$this->totals['discounts_tax_total'] = $item_taxes;
|
$coupon_discount_amounts[ $coupon_code ] -= $coupon_discount_tax_amounts[ $coupon_code ];
|
||||||
$this->object->coupon_discount_tax_amounts = $coupon_discount_tax_amounts;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$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 ) ) );
|
$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.
|
// Add totals to cart object.
|
||||||
$this->object->taxes = wp_list_pluck( $this->get_total( 'taxes' ), 'shipping_tax_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->shipping_taxes = wp_list_pluck( $this->get_total( 'taxes' ), 'tax_total' );
|
||||||
$this->object->tax_total = $this->get_total( 'tax_total' );
|
$this->object->cart_contents_total = $this->get_total( 'items_total' );
|
||||||
$this->object->total = $this->get_total( '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.
|
// Allow plugins to hook and alter totals before final total is calculated.
|
||||||
if ( has_action( 'woocommerce_calculate_totals' ) ) {
|
if ( has_action( 'woocommerce_calculate_totals' ) ) {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -174,10 +174,10 @@ class WC_Discounts {
|
||||||
*
|
*
|
||||||
* @since 3.2.0
|
* @since 3.2.0
|
||||||
* @param object $item Get data for this item.
|
* @param object $item Get data for this item.
|
||||||
* @return float
|
* @return int
|
||||||
*/
|
*/
|
||||||
public function get_discounted_price_in_cents( $item ) {
|
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 ) {
|
protected function apply_coupon_percent( $coupon, $items_to_apply ) {
|
||||||
$total_discount = 0;
|
$total_discount = 0;
|
||||||
|
$cart_total = 0;
|
||||||
|
|
||||||
foreach ( $items_to_apply as $item ) {
|
foreach ( $items_to_apply as $item ) {
|
||||||
// Find out how much price is available to discount for the 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.
|
// 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;
|
$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.
|
// 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 ) );
|
$discount = min( $discounted_price, apply_filters( 'woocommerce_coupon_get_discount_amount', $discount, $price_to_discount, $item->object, false, $coupon ) );
|
||||||
$total_discount += $discount;
|
$total_discount += $discount;
|
||||||
|
|
||||||
// Store code and discount amount per item.
|
// Store code and discount amount per item.
|
||||||
$this->discounts[ $coupon->get_code() ][ $item->key ] += $discount;
|
$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;
|
return $total_discount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -449,26 +461,26 @@ class WC_Discounts {
|
||||||
*/
|
*/
|
||||||
protected function apply_coupon_fixed_cart( $coupon, $items_to_apply, $amount = null ) {
|
protected function apply_coupon_fixed_cart( $coupon, $items_to_apply, $amount = null ) {
|
||||||
$total_discount = 0;
|
$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' ) );
|
$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' ) ) ) {
|
if ( ! $item_count = array_sum( wp_list_pluck( $items_to_apply, 'quantity' ) ) ) {
|
||||||
return $total_discount;
|
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 ) {
|
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 there is still discount remaining, repeat the process.
|
||||||
*/
|
*/
|
||||||
if ( $total_discounted > 0 && $total_discounted < $amount ) {
|
if ( $total_discount > 0 && $total_discount < $amount ) {
|
||||||
$total_discounted = $total_discounted + $this->apply_coupon_fixed_cart( $coupon, $items_to_apply, $amount - $total_discounted );
|
$total_discount += $this->apply_coupon_fixed_cart( $coupon, $items_to_apply, $amount - $total_discount );
|
||||||
}
|
}
|
||||||
} elseif ( $amount > 0 ) {
|
} 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;
|
return $total_discount;
|
||||||
}
|
}
|
||||||
|
@ -483,7 +495,7 @@ class WC_Discounts {
|
||||||
* @param int $amount Fixed discount amount to apply.
|
* @param int $amount Fixed discount amount to apply.
|
||||||
* @return int Total discounted.
|
* @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;
|
$total_discount = 0;
|
||||||
|
|
||||||
foreach ( $items_to_apply as $item ) {
|
foreach ( $items_to_apply as $item ) {
|
||||||
|
|
|
@ -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-frontend-scripts.php' ); // Frontend Scripts.
|
||||||
include_once( WC_ABSPATH . 'includes/class-wc-form-handler.php' ); // Form Handlers.
|
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.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-tax.php' ); // Tax class.
|
||||||
include_once( WC_ABSPATH . 'includes/class-wc-shipping-zones.php' ); // Shipping Zones 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.
|
include_once( WC_ABSPATH . 'includes/class-wc-customer.php' ); // Customer class.
|
||||||
|
|
|
@ -23,22 +23,128 @@ abstract class WC_Legacy_Cart {
|
||||||
/**
|
/**
|
||||||
* Contains an array of coupon usage counts after they have been applied.
|
* Contains an array of coupon usage counts after they have been applied.
|
||||||
*
|
*
|
||||||
|
* @deprecated 3.2.0
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
public $coupon_applied_count = 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
|
* @deprecated Calculation and coupon logic is handled in WC_Cart_Totals.
|
||||||
* @access private
|
* @param mixed $values Cart item.
|
||||||
* @param string $code
|
* @param mixed $price Price of item.
|
||||||
* @param int $count
|
* @param bool $add_totals Legacy.
|
||||||
|
* @return float price
|
||||||
*/
|
*/
|
||||||
protected function increase_coupon_applied_count( $code, $count = 1 ) {
|
public function get_discounted_price( $values, $price, $add_totals = false ) {
|
||||||
if ( empty( $this->coupon_applied_count[ $code ] ) ) {
|
wc_deprecated_function( 'WC_Cart::get_discounted_price', '3.2', '' );
|
||||||
$this->coupon_applied_count[ $code ] = 0;
|
|
||||||
|
$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' );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -150,6 +150,75 @@ class WC_Tests_Cart extends WC_Unit_Test_Case {
|
||||||
WC_Helper_Coupon::delete_coupon( $coupon->get_id() );
|
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.
|
* Test get_remove_url.
|
||||||
*
|
*
|
||||||
|
|
|
@ -147,10 +147,10 @@ class WC_Tests_Totals extends WC_Unit_Test_Case {
|
||||||
$this->assertEquals( 40.00, $cart->fee_total );
|
$this->assertEquals( 40.00, $cart->fee_total );
|
||||||
$this->assertEquals( 27.00, $cart->cart_contents_total );
|
$this->assertEquals( 27.00, $cart->cart_contents_total );
|
||||||
$this->assertEquals( 90.40, $cart->total );
|
$this->assertEquals( 90.40, $cart->total );
|
||||||
$this->assertEquals( 32.40, $cart->subtotal );
|
$this->assertEquals( 36.00, $cart->subtotal );
|
||||||
$this->assertEquals( 27.00, $cart->subtotal_ex_tax );
|
$this->assertEquals( 30.00, $cart->subtotal_ex_tax );
|
||||||
$this->assertEquals( 11.40, $cart->tax_total );
|
$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( 0.60, $cart->discount_cart_tax );
|
||||||
$this->assertEquals( 40.00, $cart->fee_total );
|
$this->assertEquals( 40.00, $cart->fee_total );
|
||||||
$this->assertEquals( 10, $cart->shipping_total );
|
$this->assertEquals( 10, $cart->shipping_total );
|
||||||
|
|
Loading…
Reference in New Issue