You are here

function uc_coupon_calculate_discounts in Ubercart Discount Coupons 6

Same name and namespace in other branches
  1. 7.3 uc_coupon.module \uc_coupon_calculate_discounts()
  2. 7.2 uc_coupon.module \uc_coupon_calculate_discounts()

Find items that a coupon will apply to and calculate the discounts.

Parameters

$coupon: The coupon object to be applied.

$order: The order to which it should be applied.

Return value

mixed An array of discounted items, each with a 'discount' field containing the total discount applied to that item; or, if there are no applicable discounts, a string indicating the reason why.

2 calls to uc_coupon_calculate_discounts()
uc_coupon_tax_adjustment in ./uc_coupon.module
Handle tax on coupons by calculating tax for individual discounted prices. This is currently only supported by the VAT module (uc_vat).
uc_coupon_validate in ./uc_coupon.module
Validate a coupon, and optionally calculate the order discount.

File

./uc_coupon.module, line 606
Provides discount coupons for Ubercart.

Code

function uc_coupon_calculate_discounts($coupon, $order) {
  $context = array(
    'revision' => 'altered',
    'type' => 'cart_item',
    'subject' => array(
      'order' => $order,
    ),
  );
  $restricted = isset($coupon->data['products']) || isset($coupon->data['skus']) || isset($coupon->data['terms']) || isset($coupon->data['product_types']);

  // Discover if any items match the restrictions, and which items the discount should be calculated against.
  $matched = 0;
  $matched_price = 0;
  $total_qty = 0;
  $total_price = 0;
  $items = array();
  foreach ($order->products as $item) {
    if ($item->module == 'uc_coupon') {
      continue;
    }
    $node = node_load($item->nid);
    $context['subject']['cart_item'] = $item;
    $context['subject']['node'] = $node;
    $item->altered_price = uc_price($item->price, $context);
    $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 = isset($node->taxonomy) ? array_keys($node->taxonomy) : array();
      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;
        }
        else {
          if (isset($coupon->data['products']) && isset($coupon->data['negate_products']) && in_array($item->nid, $coupon->data['products'])) {

            // always exclude if in list of negated products
          }
          else {
            if (isset($coupon->data['terms']) && (isset($coupon->data['negate_terms']) xor count(array_intersect($terms, $coupon->data['terms'])))) {
              $include = TRUE;
            }
            else {
              if (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
              }
              else {
                if (isset($coupon->data['skus']) && _uc_coupon_match_sku($item->model, $coupon->data['skus'])) {
                  $include = TRUE;
                }
                else {
                  if (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->altered_price * $qty;
    }
    $total_qty += $qty;
    $total_price += $item->altered_price * $qty;

    // Coupons that apply to the order subtotal affect all products.
    if ($include || $coupon->data['apply_to'] == 'subtotal') {
      $items = array_pad($items, count($items) + $qty, $item);
    }
  }

  // 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.');
  }

  // Ensure that the minimum order quantity restriction is met, if specified.
  if (($coupon->data['minimum_qty_restrict'] ? $matched : $total_qty) < $coupon->data['minimum_qty']) {
    return t('You do not have enough applicable products in your cart.');
  }
  if (($coupon->data['minimum_qty_restrict'] ? $matched_price : $total_price) < $coupon->minimum_order) {
    return $coupon->data['minimum_qty_restrict'] ? 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;
  }
  $total = 0;
  foreach ($items as $item) {
    $total += $item->altered_price;
  }
  foreach ($items as &$item) {
    switch ($coupon->type) {
      case 'price':
        if ($coupon->data['apply_to'] == 'subtotal' || $coupon->data['apply_to'] == 'products_total') {

          // Apply single discount proportionally across all qualifying items.
          $item->discount = $total == 0 ? 0 : min($coupon->value * $item->price / $total, $item->price);
        }
        else {

          // Apply discount to each product's untaxed price.
          $item->discount = min($coupon->value * $item->price / $item->altered_price, $item->price);
        }
        break;
      case 'percentage':
        $item->discount = $item->price * $coupon->value / 100;
        break;
      case 'set_price':
        $item->discount = max($item->price - $coupon->value * $item->price / $item->altered_price, 0);
        break;
    }
  }
  return $items;
}