207 lines
4.7 KiB
PHP
207 lines
4.7 KiB
PHP
<?php
|
|
/**
|
|
* Discount calculation
|
|
*
|
|
* @author Automattic
|
|
* @package WooCommerce/Classes
|
|
* @version 3.2.0
|
|
* @since 3.2.0
|
|
*/
|
|
|
|
/**
|
|
* Discounts class.
|
|
*/
|
|
class WC_Discounts {
|
|
|
|
/**
|
|
* An array of items to discount.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $items = array();
|
|
|
|
/**
|
|
* An array of coupons which have been applied.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $coupons = array();
|
|
|
|
/**
|
|
* An array of discounts which have been applied.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $discounts = array();
|
|
|
|
/**
|
|
* Get items.
|
|
*
|
|
* @since 3.2.0
|
|
* @return object[]
|
|
*/
|
|
public function get_items() {
|
|
return $this->items;
|
|
}
|
|
|
|
/**
|
|
* Set cart/order items which will be discounted.
|
|
*
|
|
* @since 3.2.0
|
|
* @param array $raw_items List of raw cart or order items.
|
|
*/
|
|
public function set_items( $raw_items ) {
|
|
$this->items = array();
|
|
|
|
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.
|
|
);
|
|
|
|
if ( is_a( $raw_item, 'WC_Cart_Item' ) ) {
|
|
$item->quantity = $raw_item->get_quantity();
|
|
$item->price = $raw_item->get_price();
|
|
} elseif ( is_a( $raw_item, 'WC_Order_Item_Product' ) ) {
|
|
$item->quantity = $raw_item->get_quantity();
|
|
$item->price = $raw_item->get_subtotal();
|
|
} else {
|
|
// @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->discounted_price = $item->price;
|
|
$this->items[] = $item;
|
|
}
|
|
}
|
|
}
|
|
|
|
public function get_discounted_totals() {}
|
|
|
|
public function get_applied_discounts() {}
|
|
|
|
public function get_applied_coupons() {}
|
|
|
|
/**
|
|
* Get all discount totals.
|
|
*
|
|
* @since 3.2.0
|
|
* @return array
|
|
*/
|
|
public function get_discounts() {
|
|
return array(
|
|
'items',
|
|
'discount_totals' => array(
|
|
// 'code' => 'amount'
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Apply a discount to all items using a coupon.
|
|
*
|
|
* @since 3.2.0
|
|
* @param WC_Coupon $coupon
|
|
* @return bool True if applied.
|
|
*/
|
|
public function apply_coupon( $coupon ) {
|
|
if ( ! is_a( $coupon, 'WC_Coupon' ) ) {
|
|
return false;
|
|
}
|
|
$items = $this->get_items();
|
|
$type = $coupon->get_discount_type();
|
|
$amount = $coupon->get_amount();
|
|
|
|
switch ( $type ) {
|
|
case 'percent' :
|
|
$this->apply_percentage_discount( $amount );
|
|
break;
|
|
case 'fixed_product' :
|
|
$this->apply_fixed_product_discount( $amount );
|
|
break;
|
|
case 'fixed_cart' :
|
|
$this->apply_fixed_cart_discount( $amount );
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Apply percent discount to items.
|
|
*
|
|
* @param float $amount
|
|
*/
|
|
private function apply_percentage_discount( $amount ) {
|
|
foreach ( $this->items as $item ) {
|
|
$discount = (float) $amount * ( $item->discounted_price / 100 );
|
|
$item->discounted_price -= $discount;
|
|
$item->discounted_price = max( 0, $item->discounted_price );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Apply fixed product discount to items.
|
|
*
|
|
* @param float $amount
|
|
*/
|
|
private function apply_fixed_product_discount( $discount ) {
|
|
foreach ( $this->items as $item ) {
|
|
$item->discounted_price -= $discount;
|
|
$item->discounted_price = max( 0, $item->discounted_price );
|
|
}
|
|
}
|
|
|
|
private function apply_discount_to_item( &$item, $discount ) {
|
|
if ( $discount > $item->discounted_price ) {
|
|
$discount = $item->discounted_price;
|
|
}
|
|
$item->discounted_price = $item->discounted_price - $discount;
|
|
return $discount;
|
|
}
|
|
|
|
/**
|
|
* 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 ( $item->discounted_price ) {
|
|
$item->discounted_price --;
|
|
$discount --;
|
|
$did_discount = true;
|
|
}
|
|
}
|
|
|
|
if ( ! $did_discount ) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|