function uc_coupon_calculate_discounts in Ubercart Discount Coupons 7.3
Same name and namespace in other branches
- 6 uc_coupon.module \uc_coupon_calculate_discounts()
- 7.2 uc_coupon.module \uc_coupon_calculate_discounts()
Find items that a coupon will apply to and calculate the discounts.
Parameters
$coupon: A coupon object to apply, or a coupon code as a string.
$order: The order object to which the coupon should be applied.
Return value
An array of discounts.
3 calls to uc_coupon_calculate_discounts()
- uc_coupon_get_order_coupons in ./
uc_coupon.module - Gets the fully validated coupon objects that have been applied to this order.
- uc_coupon_recurring_recurring_renewal_pending in uc_coupon_recurring/
uc_coupon_recurring.module - Implements hook_recurring_renewal_pending().
- uc_coupon_validate in ./
uc_coupon.module - Validate a coupon, and optionally calculate the order discount.
File
- ./
uc_coupon.module, line 854 - Provides discount codes and gift certificates for Ubercart.
Code
function uc_coupon_calculate_discounts($coupon, $order) {
// Can only calculate discounts if an order is provided.
if (empty($order)) {
return array();
}
if (!is_object($coupon)) {
// If argument is a code, load the corresponding coupon.
$coupon = uc_coupon_find($coupon);
}
// Discover if any items match the restrictions, and which items the discount should be calculated against.
$restricted = isset($coupon->data['products']) || isset($coupon->data['skus']) || isset($coupon->data['terms']) || isset($coupon->data['product_types']);
$matched = 0;
$matched_price = 0;
$total_qty = 0;
$total_price = 0;
$items = array();
foreach ($order->products as $item) {
if (isset($item->module) && $item->module == 'uc_coupon') {
continue;
}
$node = node_load($item->nid);
$qty = $item->qty;
if (!$restricted) {
// Coupons with no restrictions apply to all products.
$include = TRUE;
}
else {
// Other coupons only apply to matching products.
$include = FALSE;
$terms = _uc_coupon_list_terms($node);
if (isset($coupon->data['products']) && isset($item->data['kit_id'])) {
// Items that are part of product kits must be included or excluded all together, so we pre-empt other restrictions.
$include = (isset($coupon->data['negate_products']) xor in_array($item->data['kit_id'], $coupon->data['products']));
}
else {
if (isset($coupon->data['products']) && (isset($coupon->data['negate_products']) xor in_array($item->nid, $coupon->data['products']))) {
$include = TRUE;
}
elseif (isset($coupon->data['products']) && isset($coupon->data['negate_products']) && in_array($item->nid, $coupon->data['products'])) {
// always exclude if in list of negated products
}
elseif (isset($coupon->data['terms']) && (isset($coupon->data['negate_terms']) xor count(array_intersect($terms, $coupon->data['terms'])))) {
$include = TRUE;
}
elseif (isset($coupon->data['terms']) && isset($coupon->data['negate_terms']) && count(array_intersect($terms, $coupon->data['terms']))) {
// always exclude if one of the terms is in the list of negated terms
}
elseif (isset($coupon->data['skus']) && _uc_coupon_match_sku($item->model, $coupon->data['skus'])) {
$include = TRUE;
}
elseif (isset($coupon->data['product_types']) && in_array($node->type, $coupon->data['product_types'])) {
$include = TRUE;
}
}
}
// A matching product was found.
if ($include) {
$matched += $qty;
$matched_price += $item->price * $qty;
}
$total_qty += $qty;
$total_price += $item->price * $qty;
// Include this item. Coupons that apply to the order subtotal affect all products.
if ($include || $coupon->data['apply_to'] == 'subtotal') {
$clone = clone $item;
$clone->type = $node->type;
$items = array_pad($items, count($items) + $qty, $clone);
}
}
// If no matches were found, there are no discounts to calculate.
if ($matched == 0) {
return t('You do not have any applicable products in your cart.');
}
$use_matched = isset($coupon->data['minimum_qty_restrict']) && $coupon->data['minimum_qty_restrict'] != FALSE;
// Make sure the minimum quantity restriction (if any) is met.
if (isset($coupon->data['minimum_qty'])) {
if (($use_matched ? $matched : $total_qty) < (int) $coupon->data['minimum_qty']) {
return t('You do not have enough applicable products in your cart.');
}
}
// Make sure the minimum order total restriction (if any) is met.
if ($coupon->minimum_order > 0) {
if (($use_matched ? $matched_price : $total_price) < $coupon->minimum_order) {
return $use_matched ? t('You have not reached the minimum total of applicable products for this coupon.') : t('You have not reached the minimum order total for this coupon.');
}
}
// Ensure that all products match, if specified.
if (isset($coupon->data['require_match_all']) && $matched < $total_qty) {
return t('You have non-applicable products in your cart');
}
// Slice off applicable products if a limit was set.
switch ($coupon->data['apply_to']) {
case 'cheapest':
usort($items, '_uc_coupon_sort_products');
$items = array_slice($items, 0, $coupon->data['apply_count']);
break;
case 'expensive':
usort($items, '_uc_coupon_sort_products');
$items = array_slice($items, -$coupon->data['apply_count']);
break;
}
// Build the discounts array and get the order total.
$total = 0;
$discounts = array();
$included_rates = array();
foreach ($items as $item) {
if (!isset($discounts[$item->nid])) {
// First entry for this product.
// Calculate the pre-tax discount proportion for this item.
// For fixed discounts to products with taxes included, we apply the face value of the coupon
// tax-inclusively also; that is, the actual discount is reduced so that the face value is
// realized after taxes. (This already happens automatically for percentage based coupons).
$included_rate = 1;
if (module_exists('uc_taxes')) {
foreach (uc_taxes_rate_load() as $tax) {
if ($tax->display_include && is_array($tax->taxed_line_items) && in_array('coupon', $tax->taxed_line_items) && in_array($item->type, $tax->taxed_product_types) && ($tax->shippable == 0 || $item->data['shippable'] == 1)) {
$included_rate += $tax->rate;
}
}
}
// Adjust the price for any stacked coupons.
$prior_discount = 0;
if (!empty($order->data['coupons'])) {
foreach ($order->data['coupons'] as $stacked) {
if (isset($stacked[$item->nid])) {
$prior_discount += $stacked[$item->nid]->pretax_discount;
}
}
}
$total -= $prior_discount * $included_rate;
$discounts[$item->nid] = (object) array(
'qty' => 1,
'price' => $item->price - $prior_discount,
);
$included_rates[$item->nid] = $included_rate;
unset($item->type);
}
else {
// An entry for this product already exists.
// Add this item to the total for the product.
$discounts[$item->nid]->price += $item->price;
$discounts[$item->nid]->qty++;
}
$total += $item->price * $included_rate;
}
// Add in discounts for any included line items.
$items = uc_order_load_line_items($order);
if (!empty($order->line_items) && !empty($coupon->data['line_items'])) {
foreach ($order->line_items as $line_item) {
if (in_array($line_item['type'], $coupon->data['line_items'])) {
// Use a negative id to distinguish this from a product discount.
$lid = $line_item['line_item_id'];
$lid = is_numeric($lid) ? -$lid : $lid;
// No tax-inclusive line items in ubercart (yet).
$included_rate = 1;
// Adjust the price for any stacked coupons.
$prior_discount = 0;
if (!empty($order->data['coupons'])) {
foreach ($order->data['coupons'] as $stacked) {
if (isset($stacked[$lid])) {
$prior_discount += $stacked[$lid]->pretax_discount;
}
}
}
$discounts[$lid] = (object) array(
'qty' => 1,
'price' => $line_item['amount'] - $prior_discount,
);
$included_rates[$lid] = $included_rate;
$total += $discounts[$lid]->price * $included_rate;
}
}
}
// Calculate the discounts per item.
$value = $coupon->value;
if ($coupon->type === 'credit' && !empty($coupon->usage['value']['codes'][$coupon->code])) {
$value -= $coupon->usage['value']['codes'][$coupon->code];
}
foreach ($discounts as $id => $discount) {
$inclusive_price = $discount->price * $included_rates[$id];
switch ($coupon->type) {
case 'percentage':
$discount->discount = $inclusive_price * $coupon->value / 100;
break;
case 'set_price':
$discount->discount = max($inclusive_price - $coupon->value * $discount->qty, 0);
break;
default:
if ($coupon->type === 'credit' || $coupon->data['apply_to'] == 'subtotal' || $coupon->data['apply_to'] == 'products_total') {
// Apply single discount proportionally across all matching items.
$discount->discount = $total == 0 ? 0 : min($value * ($inclusive_price / $total), $inclusive_price);
}
else {
// Apply full discount value to each matching item.
$discount->discount = min($value * $discount->qty, $inclusive_price);
}
}
$discount->pretax_discount = $discount->discount / $included_rates[$id];
unset($discount->price);
unset($discount->qty);
}
return $discounts;
}