2017-07-18 04:43:31 +00:00
|
|
|
<?php
|
|
|
|
/**
|
|
|
|
* Discount calculation
|
|
|
|
*
|
|
|
|
* @author Automattic
|
|
|
|
* @package WooCommerce/Classes
|
|
|
|
* @version 3.2.0
|
|
|
|
* @since 3.2.0
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Discounts class.
|
2017-07-18 19:48:19 +00:00
|
|
|
*
|
|
|
|
* @todo this class will need to be called instead get_discounted_price, in the cart?
|
2017-07-18 04:43:31 +00:00
|
|
|
*/
|
|
|
|
class WC_Discounts {
|
|
|
|
|
|
|
|
/**
|
2017-07-18 13:04:56 +00:00
|
|
|
* An array of items to discount.
|
2017-07-18 04:43:31 +00:00
|
|
|
*
|
2017-07-18 13:04:56 +00:00
|
|
|
* @var array
|
2017-07-18 04:43:31 +00:00
|
|
|
*/
|
2017-07-18 13:04:56 +00:00
|
|
|
protected $items = array();
|
2017-07-18 04:43:31 +00:00
|
|
|
|
2017-07-18 17:07:46 +00:00
|
|
|
/**
|
2017-07-18 19:42:47 +00:00
|
|
|
* An array of discounts which have been applied.
|
2017-07-18 17:07:46 +00:00
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
2017-07-18 19:42:47 +00:00
|
|
|
protected $discounts = array();
|
2017-07-18 17:07:46 +00:00
|
|
|
|
|
|
|
/**
|
2017-07-18 19:42:47 +00:00
|
|
|
* Reset items and discounts to 0.
|
2017-07-18 17:07:46 +00:00
|
|
|
*/
|
2017-07-18 19:42:47 +00:00
|
|
|
protected function reset() {
|
|
|
|
$this->items = array();
|
|
|
|
$this->discounts = array( 'cart' => 0 );
|
|
|
|
}
|
2017-07-18 17:07:46 +00:00
|
|
|
|
2017-07-18 04:43:31 +00:00
|
|
|
/**
|
2017-07-18 13:04:56 +00:00
|
|
|
* Get items.
|
2017-07-18 04:43:31 +00:00
|
|
|
*
|
2017-07-18 13:04:56 +00:00
|
|
|
* @since 3.2.0
|
2017-07-18 14:42:46 +00:00
|
|
|
* @return object[]
|
2017-07-18 04:43:31 +00:00
|
|
|
*/
|
2017-07-18 13:04:56 +00:00
|
|
|
public function get_items() {
|
|
|
|
return $this->items;
|
2017-07-18 04:43:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2017-07-18 13:04:56 +00:00
|
|
|
* Set cart/order items which will be discounted.
|
2017-07-18 04:43:31 +00:00
|
|
|
*
|
2017-07-18 13:04:56 +00:00
|
|
|
* @since 3.2.0
|
2017-07-18 17:07:46 +00:00
|
|
|
* @param array $raw_items List of raw cart or order items.
|
2017-07-18 04:43:31 +00:00
|
|
|
*/
|
2017-07-18 13:04:56 +00:00
|
|
|
public function set_items( $raw_items ) {
|
2017-07-18 19:42:47 +00:00
|
|
|
$this->reset();
|
2017-07-18 17:47:05 +00:00
|
|
|
|
2017-07-18 14:42:46 +00:00
|
|
|
if ( ! empty( $raw_items ) && is_array( $raw_items ) ) {
|
|
|
|
foreach ( $raw_items as $raw_item ) {
|
|
|
|
$item = (object) array(
|
2017-07-18 19:42:47 +00:00
|
|
|
'price' => 0, // Line price without discounts.
|
|
|
|
'quantity' => 0, // Line qty.
|
|
|
|
'product' => false,
|
2017-07-18 14:42:46 +00:00
|
|
|
);
|
|
|
|
if ( is_a( $raw_item, 'WC_Cart_Item' ) ) {
|
2017-07-18 19:42:47 +00:00
|
|
|
//$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
|
2017-07-18 14:42:46 +00:00
|
|
|
} elseif ( is_a( $raw_item, 'WC_Order_Item_Product' ) ) {
|
2017-07-18 19:42:47 +00:00
|
|
|
$item->key = $raw_item->get_id();
|
2017-07-18 14:42:46 +00:00
|
|
|
$item->quantity = $raw_item->get_quantity();
|
2017-07-18 19:42:47 +00:00
|
|
|
$item->product = $raw_item->get_product();
|
2017-07-18 14:42:46 +00:00
|
|
|
} else {
|
2017-07-18 19:42:47 +00:00
|
|
|
$item->key = $raw_item['key'];
|
2017-07-18 14:42:46 +00:00
|
|
|
// @todo remove when we implement WC_Cart_Item. This is the old cart item schema.
|
|
|
|
$item->quantity = $raw_item['quantity'];
|
2017-07-18 19:42:47 +00:00
|
|
|
$item->price = $raw_item['data']->get_price() * $raw_item['quantity'];
|
|
|
|
$item->product = $raw_item['data'];
|
2017-07-18 14:42:46 +00:00
|
|
|
}
|
2017-07-18 19:42:47 +00:00
|
|
|
$this->items[ $item->key ] = $item;
|
|
|
|
$this->discounts[ $item->key ] = 0;
|
2017-07-18 13:04:56 +00:00
|
|
|
}
|
|
|
|
}
|
2017-07-18 04:43:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2017-07-18 13:04:56 +00:00
|
|
|
* Get all discount totals.
|
2017-07-18 04:43:31 +00:00
|
|
|
*
|
2017-07-18 13:04:56 +00:00
|
|
|
* @since 3.2.0
|
|
|
|
* @return array
|
2017-07-18 04:43:31 +00:00
|
|
|
*/
|
2017-07-18 13:04:56 +00:00
|
|
|
public function get_discounts() {
|
2017-07-18 19:42:47 +00:00
|
|
|
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;
|
2017-07-18 04:43:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2017-07-18 13:04:56 +00:00
|
|
|
* Apply a discount to all items using a coupon.
|
2017-07-18 04:43:31 +00:00
|
|
|
*
|
2017-07-18 13:04:56 +00:00
|
|
|
* @since 3.2.0
|
|
|
|
* @param WC_Coupon $coupon
|
|
|
|
* @return bool True if applied.
|
2017-07-18 04:43:31 +00:00
|
|
|
*/
|
2017-07-18 17:07:46 +00:00
|
|
|
public function apply_coupon( $coupon ) {
|
2017-07-18 13:04:56 +00:00
|
|
|
if ( ! is_a( $coupon, 'WC_Coupon' ) ) {
|
|
|
|
return false;
|
|
|
|
}
|
2017-07-18 19:48:19 +00:00
|
|
|
|
|
|
|
|
|
|
|
/* @todo add this logic here somewhere
|
|
|
|
if ( null === $this->get_limit_usage_to_x_items() ) {
|
|
|
|
$limit_usage_qty = $cart_item_qty;
|
|
|
|
} else {
|
|
|
|
$limit_usage_qty = min( $this->get_limit_usage_to_x_items(), $cart_item_qty );
|
|
|
|
|
|
|
|
$this->set_limit_usage_to_x_items( max( 0, ( $this->get_limit_usage_to_x_items() - $limit_usage_qty ) ) );
|
|
|
|
}
|
|
|
|
if ( $single ) {
|
|
|
|
$discount = ( $discount * $limit_usage_qty ) / $cart_item_qty;
|
|
|
|
} else {
|
|
|
|
$discount = ( $discount / $cart_item_qty ) * $limit_usage_qty;
|
|
|
|
}*/
|
|
|
|
|
|
|
|
// @todo how can we support the old woocommerce_coupon_get_discount_amount filter?
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-07-18 19:42:47 +00:00
|
|
|
switch ( $coupon->get_discount_type() ) {
|
2017-07-18 17:07:46 +00:00
|
|
|
case 'percent' :
|
2017-07-18 19:42:47 +00:00
|
|
|
$this->apply_percentage_discount( $coupon->get_amount() );
|
2017-07-18 17:07:46 +00:00
|
|
|
break;
|
2017-07-18 17:47:05 +00:00
|
|
|
case 'fixed_product' :
|
2017-07-18 19:42:47 +00:00
|
|
|
$this->apply_fixed_product_discount( $coupon->get_amount() );
|
2017-07-18 17:47:05 +00:00
|
|
|
break;
|
|
|
|
case 'fixed_cart' :
|
2017-07-18 19:42:47 +00:00
|
|
|
$this->apply_fixed_cart_discount( $coupon->get_amount() );
|
2017-07-18 17:07:46 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2017-07-18 19:42:47 +00:00
|
|
|
* Get discounted price of an item.
|
2017-07-18 17:07:46 +00:00
|
|
|
*
|
2017-07-18 19:42:47 +00:00
|
|
|
* @since 3.2.0
|
|
|
|
* @param object $item
|
|
|
|
* @return float
|
2017-07-18 17:07:46 +00:00
|
|
|
*/
|
2017-07-18 19:42:47 +00:00
|
|
|
public function get_discounted_price( $item ) {
|
|
|
|
return $item->price - $this->discounts[ $item->key ];
|
2017-07-18 17:07:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2017-07-18 19:42:47 +00:00
|
|
|
* Apply a discount amount to an item and ensure it does not go negative.
|
2017-07-18 17:47:05 +00:00
|
|
|
*
|
2017-07-18 19:42:47 +00:00
|
|
|
* @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
|
2017-07-18 17:47:05 +00:00
|
|
|
* @param float $amount
|
|
|
|
*/
|
2017-07-18 19:42:47 +00:00
|
|
|
protected function apply_percentage_discount( $amount ) {
|
2017-07-18 17:47:05 +00:00
|
|
|
foreach ( $this->items as $item ) {
|
2017-07-18 19:42:47 +00:00
|
|
|
$this->apply_discount_to_item( $item, (float) $amount * ( $this->get_discounted_price( $item ) / 100 ) );
|
2017-07-18 17:47:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2017-07-18 19:42:47 +00:00
|
|
|
* Apply fixed product discount to items.
|
2017-07-18 17:07:46 +00:00
|
|
|
*
|
2017-07-18 19:42:47 +00:00
|
|
|
* @since 3.2.0
|
2017-07-18 17:07:46 +00:00
|
|
|
* @param float $amount
|
|
|
|
*/
|
2017-07-18 19:42:47 +00:00
|
|
|
protected function apply_fixed_product_discount( $discount ) {
|
|
|
|
foreach ( $this->items as $item ) {
|
|
|
|
$this->apply_discount_to_item( $item, $discount * $item->quantity );
|
2017-07-18 17:07:46 +00:00
|
|
|
}
|
2017-07-18 04:43:31 +00:00
|
|
|
}
|
2017-07-18 17:52:50 +00:00
|
|
|
|
2017-07-18 19:42:47 +00:00
|
|
|
/*protected function filter_taxable_items( $item ) {
|
|
|
|
return $item->is_taxable;
|
|
|
|
}*/
|
|
|
|
|
2017-07-18 17:52:50 +00:00
|
|
|
/**
|
2017-07-18 19:42:47 +00:00
|
|
|
* Apply fixed cart discount to items. Cart discounts will be stored and
|
|
|
|
* displayed separately to items, and taxes will be apportioned.
|
2017-07-18 17:52:50 +00:00
|
|
|
*
|
2017-07-18 19:42:47 +00:00
|
|
|
* @since 3.2.0
|
|
|
|
* @param float $cart_discount
|
2017-07-18 17:52:50 +00:00
|
|
|
*/
|
2017-07-18 19:42:47 +00:00
|
|
|
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 );
|
2017-07-18 17:52:50 +00:00
|
|
|
}
|
2017-07-18 19:42:47 +00:00
|
|
|
|
|
|
|
var_dump($cart_discount_taxes);*/
|
|
|
|
$this->apply_discount_to_cart( $cart_discount );
|
2017-07-18 17:52:50 +00:00
|
|
|
}
|
2017-07-18 04:43:31 +00:00
|
|
|
}
|