Split fixed cart from other discounts in class.
This commit is contained in:
parent
37fbd96de8
commit
d24faebea8
|
@ -954,11 +954,12 @@ class WC_Cart {
|
|||
|
||||
// Add item after merging with $cart_item_data - hook to allow plugins to modify cart item
|
||||
$this->cart_contents[ $cart_item_key ] = apply_filters( 'woocommerce_add_cart_item', array_merge( $cart_item_data, array(
|
||||
'product_id' => $product_id,
|
||||
'variation_id' => $variation_id,
|
||||
'variation' => $variation,
|
||||
'quantity' => $quantity,
|
||||
'data' => $product_data,
|
||||
'key' => $cart_item_key,
|
||||
'product_id' => $product_id,
|
||||
'variation_id' => $variation_id,
|
||||
'variation' => $variation,
|
||||
'quantity' => $quantity,
|
||||
'data' => $product_data,
|
||||
) ), $cart_item_key );
|
||||
}
|
||||
|
||||
|
|
|
@ -20,13 +20,6 @@ class WC_Discounts {
|
|||
*/
|
||||
protected $items = array();
|
||||
|
||||
/**
|
||||
* An array of coupons which have been applied.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $coupons = array();
|
||||
|
||||
/**
|
||||
* An array of discounts which have been applied.
|
||||
*
|
||||
|
@ -34,6 +27,14 @@ class WC_Discounts {
|
|||
*/
|
||||
protected $discounts = array();
|
||||
|
||||
/**
|
||||
* Reset items and discounts to 0.
|
||||
*/
|
||||
protected function reset() {
|
||||
$this->items = array();
|
||||
$this->discounts = array( 'cart' => 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get items.
|
||||
*
|
||||
|
@ -51,40 +52,38 @@ class WC_Discounts {
|
|||
* @param array $raw_items List of raw cart or order items.
|
||||
*/
|
||||
public function set_items( $raw_items ) {
|
||||
$this->items = array();
|
||||
$this->reset();
|
||||
|
||||
if ( ! empty( $raw_items ) && is_array( $raw_items ) ) {
|
||||
foreach ( $raw_items as $raw_item ) {
|
||||
$item = (object) array(
|
||||
'price' => 0, // Unit price without discounts.
|
||||
'discounted_price' => 0, // Unit price with discounts.
|
||||
'quantity' => 0, // Line qty.
|
||||
'price' => 0, // Line price without discounts.
|
||||
'quantity' => 0, // Line qty.
|
||||
'product' => false,
|
||||
);
|
||||
|
||||
if ( is_a( $raw_item, 'WC_Cart_Item' ) ) {
|
||||
$item->quantity = $raw_item->get_quantity();
|
||||
$item->price = $raw_item->get_price();
|
||||
//$item->quantity = $raw_item->get_quantity();
|
||||
//$item->price = $raw_item->get_price() * $raw_item->get_quantity();
|
||||
//$item->is_taxable = $raw_item->is_taxable();
|
||||
//$item->tax_class = $raw_item->get_tax_class();
|
||||
// @todo
|
||||
} elseif ( is_a( $raw_item, 'WC_Order_Item_Product' ) ) {
|
||||
$item->key = $raw_item->get_id();
|
||||
$item->quantity = $raw_item->get_quantity();
|
||||
$item->price = $raw_item->get_subtotal();
|
||||
$item->product = $raw_item->get_product();
|
||||
} else {
|
||||
$item->key = $raw_item['key'];
|
||||
// @todo remove when we implement WC_Cart_Item. This is the old cart item schema.
|
||||
$item->quantity = $raw_item['quantity'];
|
||||
$item->price = $raw_item['data']->get_price();
|
||||
$item->price = $raw_item['data']->get_price() * $raw_item['quantity'];
|
||||
$item->product = $raw_item['data'];
|
||||
}
|
||||
|
||||
$item->discounted_price = $item->price;
|
||||
$this->items[] = $item;
|
||||
$this->items[ $item->key ] = $item;
|
||||
$this->discounts[ $item->key ] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function get_discounted_totals() {}
|
||||
|
||||
public function get_applied_discounts() {}
|
||||
|
||||
public function get_applied_coupons() {}
|
||||
|
||||
/**
|
||||
* Get all discount totals.
|
||||
*
|
||||
|
@ -92,12 +91,17 @@ class WC_Discounts {
|
|||
* @return array
|
||||
*/
|
||||
public function get_discounts() {
|
||||
return array(
|
||||
'items',
|
||||
'discount_totals' => array(
|
||||
// 'code' => 'amount'
|
||||
)
|
||||
);
|
||||
return $this->discounts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get discount by key.
|
||||
*
|
||||
* @since 3.2.0
|
||||
* @return array
|
||||
*/
|
||||
public function get_discount( $key ) {
|
||||
return isset( $this->discounts[ $key ] ) ? $this->discounts[ $key ] : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -111,99 +115,105 @@ class WC_Discounts {
|
|||
if ( ! is_a( $coupon, 'WC_Coupon' ) ) {
|
||||
return false;
|
||||
}
|
||||
$items = $this->get_items();
|
||||
$type = $coupon->get_discount_type();
|
||||
$amount = $coupon->get_amount();
|
||||
|
||||
switch ( $type ) {
|
||||
switch ( $coupon->get_discount_type() ) {
|
||||
case 'percent' :
|
||||
$this->apply_percentage_discount( $amount );
|
||||
$this->apply_percentage_discount( $coupon->get_amount() );
|
||||
break;
|
||||
case 'fixed_product' :
|
||||
$this->apply_fixed_product_discount( $amount );
|
||||
$this->apply_fixed_product_discount( $coupon->get_amount() );
|
||||
break;
|
||||
case 'fixed_cart' :
|
||||
$this->apply_fixed_cart_discount( $amount );
|
||||
$this->apply_fixed_cart_discount( $coupon->get_amount() );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get discounted price of an item.
|
||||
*
|
||||
* @since 3.2.0
|
||||
* @param object $item
|
||||
* @return float
|
||||
*/
|
||||
public function get_discounted_price( $item ) {
|
||||
return $item->price - $this->discounts[ $item->key ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a discount amount to an item and ensure it does not go negative.
|
||||
*
|
||||
* @since 3.2.0
|
||||
* @param object $item
|
||||
* @param float $discount
|
||||
* @return float
|
||||
*/
|
||||
protected function apply_discount_to_item( &$item, $discount ) {
|
||||
$discounted_price = $this->get_discounted_price( $item );
|
||||
$discount = $discount > $discounted_price ? $discounted_price : $discount;
|
||||
$this->discounts[ $item->key ] = $this->discounts[ $item->key ] + $discount;
|
||||
return $discount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a discount amount to the cart.
|
||||
*
|
||||
* @since 3.2.0
|
||||
* @param object $item
|
||||
* @param float $discount
|
||||
*/
|
||||
protected function apply_discount_to_cart( $discount ) {
|
||||
$this->discounts['cart'] += $discount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply percent discount to items.
|
||||
*
|
||||
* @since 3.2.0
|
||||
* @param float $amount
|
||||
*/
|
||||
private function apply_percentage_discount( $amount ) {
|
||||
protected function apply_percentage_discount( $amount ) {
|
||||
foreach ( $this->items as $item ) {
|
||||
$this->apply_discount_to_item( $item, (float) $amount * ( $item->discounted_price / 100 ) );
|
||||
$this->apply_discount_to_item( $item, (float) $amount * ( $this->get_discounted_price( $item ) / 100 ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply fixed product discount to items.
|
||||
*
|
||||
* @since 3.2.0
|
||||
* @param float $amount
|
||||
*/
|
||||
private function apply_fixed_product_discount( $discount ) {
|
||||
protected function apply_fixed_product_discount( $discount ) {
|
||||
foreach ( $this->items as $item ) {
|
||||
$this->apply_discount_to_item( $item, $discount );
|
||||
$this->apply_discount_to_item( $item, $discount * $item->quantity );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply fixed cart discount to items.
|
||||
*
|
||||
* @param float $amount
|
||||
*/
|
||||
private function apply_fixed_cart_discount( $total_amount ) {
|
||||
// Fixed amount needs to be divided equally between items.
|
||||
$item_count = array_sum( wp_list_pluck( $this->items, 'quantity' ) );
|
||||
$discount = floor( $total_amount / $item_count );
|
||||
$discounted = 0; // Keep track of what actually gets discounted, since some products may be cheaper than the discount.
|
||||
|
||||
if ( 0 < $discount ) {
|
||||
foreach ( $this->items as $item ) {
|
||||
$discounted += $this->apply_discount_to_item( $item, $discount );
|
||||
}
|
||||
|
||||
// Anything left?
|
||||
if ( $discounted && ( $remaining_discount = $total_amount - $discounted ) ) {
|
||||
$this->apply_fixed_cart_discount( $remaining_discount );
|
||||
}
|
||||
|
||||
// Amount is too small to divide equally.
|
||||
} elseif ( $total_amount ) {
|
||||
$discount = $total_amount;
|
||||
|
||||
while ( $discount > 0 ) {
|
||||
$did_discount = false;
|
||||
|
||||
foreach ( $this->items as $item ) {
|
||||
if ( $this->apply_discount_to_item( $item, 0.1 ) ) {
|
||||
$discount -= 0.1;
|
||||
$did_discount = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $did_discount ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*protected function filter_taxable_items( $item ) {
|
||||
return $item->is_taxable;
|
||||
}*/
|
||||
|
||||
/**
|
||||
* Apply a discount amount to an item and ensure it does not go negative.
|
||||
* Apply fixed cart discount to items. Cart discounts will be stored and
|
||||
* displayed separately to items, and taxes will be apportioned.
|
||||
*
|
||||
* @param object $item
|
||||
* @param float $discount
|
||||
* @return float
|
||||
* @since 3.2.0
|
||||
* @param float $cart_discount
|
||||
*/
|
||||
private function apply_discount_to_item( &$item, $discount ) {
|
||||
if ( $discount > $item->discounted_price ) {
|
||||
$discount = $item->discounted_price;
|
||||
protected function apply_fixed_cart_discount( $cart_discount ) {
|
||||
// @todo Fixed cart discounts will be apportioned based on tax class.
|
||||
/*$tax_class_counts = array_count_values( wp_list_pluck( array_filter( $this->items, array( $this, 'filter_taxable_items' ) ), 'tax_class' ) );
|
||||
$item_count = array_sum( wp_list_pluck( $this->items, 'quantity' ) );
|
||||
$cart_discount_taxes = array();
|
||||
|
||||
foreach ( $tax_class_counts as $tax_class => $tax_class_count ) {
|
||||
$proportion = $tax_class_count / $item_count;
|
||||
$cart_discount_proportion = $cart_discount * $proportion;
|
||||
$tax_rates = WC_Tax::get_rates( $tax_class );
|
||||
$cart_discount_taxes[ $tax_class ] = WC_Tax::calc_tax( $cart_discount_proportion, $tax_rates );
|
||||
}
|
||||
$item->discounted_price = $item->discounted_price - $discount;
|
||||
return $discount;
|
||||
|
||||
var_dump($cart_discount_taxes);*/
|
||||
$this->apply_discount_to_cart( $cart_discount );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,12 +25,12 @@ class WC_Tests_Discounts extends WC_Unit_Test_Case {
|
|||
// Test setting items to the cart.
|
||||
$discounts = new WC_Discounts();
|
||||
$discounts->set_items( WC()->cart->get_cart() );
|
||||
$this->assertEquals( array( (object) array( 'price' => '10', 'discounted_price' => '10', 'quantity' => 1 ) ), $discounts->get_items() );
|
||||
$this->assertEquals( 1, count( $discounts->get_items() ) );
|
||||
|
||||
// Test setting items to an order.
|
||||
$discounts = new WC_Discounts();
|
||||
$discounts->set_items( $order->get_items() );
|
||||
$this->assertEquals( array( (object) array( 'price' => '10', 'discounted_price' => '10', 'quantity' => 1 ) ), $discounts->get_items() );
|
||||
$this->assertEquals( 1, count( $discounts->get_items() ) );
|
||||
|
||||
// Empty array of items.
|
||||
$discounts = new WC_Discounts();
|
||||
|
@ -55,7 +55,22 @@ class WC_Tests_Discounts extends WC_Unit_Test_Case {
|
|||
$discounts = new WC_Discounts();
|
||||
|
||||
// Create dummy content.
|
||||
$tax_rate = array(
|
||||
'tax_rate_country' => '',
|
||||
'tax_rate_state' => '',
|
||||
'tax_rate' => '20.0000',
|
||||
'tax_rate_name' => 'VAT',
|
||||
'tax_rate_priority' => '1',
|
||||
'tax_rate_compound' => '0',
|
||||
'tax_rate_shipping' => '1',
|
||||
'tax_rate_order' => '1',
|
||||
'tax_rate_class' => '',
|
||||
);
|
||||
$tax_rate_id = WC_Tax::_insert_tax_rate( $tax_rate );
|
||||
update_option( 'woocommerce_calc_taxes', 'yes' );
|
||||
$product = WC_Helper_Product::create_simple_product();
|
||||
$product->set_tax_status( 'taxable' );
|
||||
$product->save();
|
||||
WC()->cart->empty_cart();
|
||||
WC()->cart->add_to_cart( $product->get_id(), 1 );
|
||||
$coupon = new WC_Coupon;
|
||||
|
@ -67,13 +82,13 @@ class WC_Tests_Discounts extends WC_Unit_Test_Case {
|
|||
$coupon->set_discount_type( 'percent' );
|
||||
$discounts->set_items( WC()->cart->get_cart() );
|
||||
$discounts->apply_coupon( $coupon );
|
||||
$this->assertEquals( array( (object) array( 'price' => '10', 'discounted_price' => '8', 'quantity' => 1 ) ), $discounts->get_items() );
|
||||
$this->assertEquals( 8, $discounts->get_discounted_price( current( $discounts->get_items() ) ) );
|
||||
|
||||
// Apply a fixed cart coupon.
|
||||
$coupon->set_discount_type( 'fixed_cart' );
|
||||
$discounts->set_items( WC()->cart->get_cart() );
|
||||
$discounts->apply_coupon( $coupon );
|
||||
$this->assertEquals( array( (object) array( 'price' => '10', 'discounted_price' => '0', 'quantity' => 1 ) ), $discounts->get_items() );
|
||||
$this->assertEquals( 20, $discounts->get_discount( 'cart' ) );
|
||||
|
||||
// Apply a fixed cart coupon.
|
||||
$coupon->set_discount_type( 'fixed_cart' );
|
||||
|
@ -81,7 +96,10 @@ class WC_Tests_Discounts extends WC_Unit_Test_Case {
|
|||
WC()->cart->add_to_cart( $product->get_id(), 4 );
|
||||
$discounts->set_items( WC()->cart->get_cart() );
|
||||
$discounts->apply_coupon( $coupon );
|
||||
$this->assertEquals( array( (object) array( 'price' => '40', 'discounted_price' => '20', 'quantity' => 4 ) ), $discounts->get_items() );
|
||||
$this->assertEquals( 20, $discounts->get_discount( 'cart' ) );
|
||||
|
||||
$discounts->apply_coupon( $coupon );
|
||||
$this->assertEquals( 40, $discounts->get_discount( 'cart' ) );
|
||||
|
||||
// Apply a fixed product coupon.
|
||||
$coupon->set_discount_type( 'fixed_product' );
|
||||
|
@ -90,10 +108,12 @@ class WC_Tests_Discounts extends WC_Unit_Test_Case {
|
|||
WC()->cart->add_to_cart( $product->get_id(), 4 );
|
||||
$discounts->set_items( WC()->cart->get_cart() );
|
||||
$discounts->apply_coupon( $coupon );
|
||||
$this->assertEquals( array( (object) array( 'price' => '40', 'discounted_price' => '36', 'quantity' => 4 ) ), $discounts->get_items() );
|
||||
$this->assertEquals( 36, $discounts->get_discounted_price( current( $discounts->get_items() ) ) );
|
||||
|
||||
// Cleanup.
|
||||
WC()->cart->empty_cart();
|
||||
$product->delete( true );
|
||||
WC_Tax::_delete_tax_rate( $tax_rate_id );
|
||||
update_option( 'woocommerce_calc_taxes', 'no' );
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue