You are here

uc_coupon.module in Ubercart Discount Coupons 6

Same filename and directory in other branches
  1. 5 uc_coupon.module
  2. 7.3 uc_coupon.module
  3. 7.2 uc_coupon.module

Provides discount coupons for Ubercart.

Original code by Blake Lucchesi (www.boldsource.com) Maintained by David Long (dave@longwaveconsulting.com)

Send any suggestions and feedback to the above address.

File

uc_coupon.module
View source
<?php

require_once 'uc_coupon.ca.inc';

/**
 * @file
 * Provides discount coupons for Ubercart.
 *
 * Original code by Blake Lucchesi (www.boldsource.com)
 * Maintained by David Long (dave@longwaveconsulting.com)
 *
 * Send any suggestions and feedback to the above address.
 */

/**
 * Implementation of hook_menu().
 */
function uc_coupon_menu() {
  $items = array();
  $items['admin/store/coupons'] = array(
    'title' => 'Coupons',
    'description' => 'Manage store discount coupons.',
    'page callback' => 'uc_coupon_display',
    'page arguments' => array(
      'active',
    ),
    'access arguments' => array(
      'view store coupons',
    ),
    'type' => MENU_NORMAL_ITEM,
    'file' => 'uc_coupon.admin.inc',
  );
  $items['admin/store/coupons/list'] = array(
    'title' => 'Active coupons',
    'description' => 'View active coupons.',
    'page callback' => 'uc_coupon_display',
    'page arguments' => array(
      'active',
    ),
    'access arguments' => array(
      'view store coupons',
    ),
    'type' => MENU_NORMAL_ITEM,
    'file' => 'uc_coupon.admin.inc',
    'weight' => 0,
  );
  $items['admin/store/coupons/inactive'] = array(
    'title' => 'Inactive coupons',
    'description' => 'View inactive coupons.',
    'page callback' => 'uc_coupon_display',
    'page arguments' => array(
      'inactive',
    ),
    'access arguments' => array(
      'view store coupons',
    ),
    'type' => MENU_NORMAL_ITEM,
    'file' => 'uc_coupon.admin.inc',
    'weight' => 1,
  );
  $items['admin/store/coupons/add'] = array(
    'title' => 'Add new coupon',
    'description' => 'Add a new coupon.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'uc_coupon_add_form',
    ),
    'access arguments' => array(
      'manage store coupons',
    ),
    'type' => MENU_NORMAL_ITEM,
    'file' => 'uc_coupon.admin.inc',
    'weight' => 2,
  );
  $items['admin/store/coupons/%uc_coupon'] = array(
    'title callback' => 'uc_coupon_title',
    'title arguments' => array(
      3,
    ),
    'description' => 'View coupon details.',
    'page callback' => 'uc_coupon_view',
    'page arguments' => array(
      3,
    ),
    'access arguments' => array(
      'view store coupons',
    ),
    'type' => MENU_CALLBACK,
    'file' => 'uc_coupon.admin.inc',
    'weight' => 3,
  );
  $items['admin/store/coupons/%uc_coupon/view'] = array(
    'title' => 'View',
    'description' => 'View coupon details.',
    'access arguments' => array(
      'view store coupons',
    ),
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'file' => 'uc_coupon.admin.inc',
    'weight' => 0,
  );
  $items['admin/store/coupons/%uc_coupon/print'] = array(
    'title' => 'Print',
    'description' => 'Print coupon.',
    'page callback' => 'uc_coupon_print',
    'page arguments' => array(
      3,
      5,
      'print',
    ),
    'access arguments' => array(
      'view store coupons',
    ),
    'type' => MENU_LOCAL_TASK,
    'file' => 'uc_coupon.admin.inc',
    'weight' => 1,
  );
  $items['admin/store/coupons/%uc_coupon/edit'] = array(
    'title' => 'Edit',
    'description' => 'Edit an existing coupon.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'uc_coupon_add_form',
      3,
    ),
    'access arguments' => array(
      'manage store coupons',
    ),
    'type' => MENU_LOCAL_TASK,
    'file' => 'uc_coupon.admin.inc',
    'weight' => 2,
  );
  $items['admin/store/coupons/%uc_coupon/delete'] = array(
    'title' => 'Delete',
    'description' => 'Delete a coupon.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'uc_coupon_delete_confirm',
      3,
    ),
    'access arguments' => array(
      'manage store coupons',
    ),
    'type' => MENU_LOCAL_TASK,
    'file' => 'uc_coupon.admin.inc',
    'weight' => 3,
  );
  $items['admin/store/coupons/%uc_coupon/codes'] = array(
    'title' => 'Download bulk coupon codes',
    'description' => 'Download the list of bulk coupon codes as a CSV file.',
    'page callback' => 'uc_coupon_codes_csv',
    'page arguments' => array(
      3,
    ),
    'access arguments' => array(
      'view store coupons',
    ),
    'file' => 'uc_coupon.admin.inc',
    'type' => MENU_CALLBACK,
  );
  $items['admin/store/coupons/ahah'] = array(
    'page callback' => 'uc_coupon_ahah',
    'access arguments' => array(
      'manage store coupons',
    ),
    'type' => MENU_CALLBACK,
    'file' => 'uc_coupon.admin.inc',
  );
  $items['admin/store/coupons/autocomplete/node'] = array(
    'title' => 'Node autocomplete',
    'page callback' => 'uc_coupon_autocomplete_node',
    'access arguments' => array(
      'manage store coupons',
    ),
    'type' => MENU_CALLBACK,
    'file' => 'uc_coupon.admin.inc',
  );
  $items['admin/store/coupons/autocomplete/term'] = array(
    'title' => 'Term autocomplete',
    'page callback' => 'uc_coupon_autocomplete_term',
    'access arguments' => array(
      'manage store coupons',
    ),
    'type' => MENU_CALLBACK,
    'file' => 'uc_coupon.admin.inc',
  );
  $items['admin/store/coupons/autocomplete/user'] = array(
    'title' => 'User autocomplete',
    'page callback' => 'uc_coupon_autocomplete_user',
    'access arguments' => array(
      'manage store coupons',
    ),
    'type' => MENU_CALLBACK,
    'file' => 'uc_coupon.admin.inc',
  );
  $items['admin/store/coupons/autocomplete/role'] = array(
    'title' => 'Role autocomplete',
    'page callback' => 'uc_coupon_autocomplete_role',
    'access arguments' => array(
      'manage store coupons',
    ),
    'type' => MENU_CALLBACK,
    'file' => 'uc_coupon.admin.inc',
  );
  $items['admin/store/settings/coupon'] = array(
    'title' => 'Coupon module settings',
    'description' => 'Configure the discount coupon module settings.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'uc_coupon_settings_form',
    ),
    'access arguments' => array(
      'administer store',
    ),
    'file' => 'uc_coupon.admin.inc',
    'type' => MENU_NORMAL_ITEM,
  );
  $items['admin/store/reports/coupon'] = array(
    'title' => 'Coupon usage reports',
    'description' => 'View coupon usage reports.',
    'page callback' => 'uc_coupon_reports',
    'access arguments' => array(
      'view reports',
    ),
    'file' => 'uc_coupon.reports.inc',
    'type' => MENU_NORMAL_ITEM,
  );
  $items['cart/checkout/coupon'] = array(
    'title' => 'Apply coupon',
    'page callback' => 'uc_coupon_checkout_apply',
    'access arguments' => array(
      'access content',
    ),
    'type' => MENU_CALLBACK,
  );
  return $items;
}
function uc_coupon_title($coupon) {
  return $coupon->name;
}

/**
 * Implementation of hook_perm().
 */
function uc_coupon_perm() {
  $perms = array(
    'view store coupons',
    'manage store coupons',
    'coupon wholesale pricing',
  );
  if (!module_exists('uc_reports')) {
    $perms[] = 'view reports';
  }
  return $perms;
}

/**
 * Implementation of hook_init().
 */
function uc_coupon_init() {
  global $conf;
  $conf['i18n_variables'][] = 'uc_coupon_pane_description';

  // Auto apply coupon from query string, if configured.
  if ($param = variable_get('uc_coupon_querystring', '')) {
    if (isset($_GET[$param]) && $_GET[$param]) {
      $_SESSION['uc_coupon'] = $_GET[$param];
    }
  }
}

/**
 * Implementation of hook_theme().
 */
function uc_coupon_theme() {
  return array(
    'uc_checkout_pane_coupon' => array(
      'arguments' => array(
        'form' => NULL,
      ),
    ),
    'uc_coupon_actions' => array(
      'arguments' => array(
        'coupon' => NULL,
      ),
      'file' => 'uc_coupon.admin.inc',
    ),
    'uc_coupon_code' => array(
      'arguments' => array(
        'coupon' => NULL,
      ),
      'file' => 'uc_coupon.admin.inc',
    ),
    'uc_coupon_certificate' => array(
      'arguments' => array(
        'coupon' => NULL,
        'code' => NULL,
      ),
      'template' => 'uc_coupon-certificate',
      'path' => drupal_get_path('module', 'uc_coupon') . '/theme',
    ),
    'uc_coupon_page' => array(
      'arguments' => array(
        'content' => NULL,
      ),
      'template' => 'uc_coupon-page',
      'path' => drupal_get_path('module', 'uc_coupon') . '/theme',
    ),
  );
}

/**
 * Default theme implementation for the checkout pane.
 */
function theme_uc_checkout_pane_coupon($form) {
  return drupal_render($form);
}

/**
 * Save a coupon object.
 * 
 * If the 'cid' field is set, then this will update an existing coupon.
 * Otherwise, a new bulk seed will be generated, the coupon will be
 * inserted into the database, and $coupon->cid will be set.
 * 
 * @param $coupon
 *   The coupon to save.
 * 
 * @param $edit
 *   An optional array of extra data that other modules may need to save.
 */
function uc_coupon_save(&$coupon, $edit = array()) {

  // Allow other modules to alter the coupon before saving.
  foreach (module_implements('uc_coupon_presave') as $module) {
    $callback = $module . '_uc_coupon_presave';
    $callback($coupon, $edit);
  }
  if (isset($coupon->cid)) {
    drupal_write_record('uc_coupons', $coupon, 'cid');
  }
  else {
    $coupon->created = time();
    $coupon->bulk_seed = md5(uniqid());
    drupal_write_record('uc_coupons', $coupon);
  }

  // Notify other modules that a coupon has been saved.
  module_invoke_all('uc_coupon_save', $coupon);
}

/**
 * Load a coupon object.
 *
 * @param $cid
 *   Unique coupon ID.
 *
 * @return $coupon
 *   A coupon object.
 */
function uc_coupon_load($cid) {
  $coupon = db_fetch_object(db_query("SELECT * FROM {uc_coupons} WHERE cid = %d", $cid));
  $coupon->data = $coupon->data ? unserialize($coupon->data) : array();

  // Convert old coupons that could not specify "per order" when restricted.
  if (!isset($coupon->data['apply_to'])) {
    if (isset($coupon->data['max_applicable_products_value']) && isset($coupon->data['max_applicable_products']) && $coupon->data['max_applicable_products']) {

      // Coupon was restricted to X cheapest or most expensive products.
      $coupon->data['apply_to'] = $coupon->data['max_applicable_products_value'];
      $coupon->data['apply_count'] = $coupon->data['max_applicable_products'];
    }
    else {
      if (isset($coupon->data['products']) || isset($coupon->data['skus']) || isset($coupon->data['terms']) || isset($coupon->data['product_types'])) {

        // Coupon has product restrictions, so was applied to each matching product.
        $coupon->data['apply_to'] = 'products';
      }
      else {

        // Coupon had no product restrictions, so was applied once to the subtotal.
        $coupon->data['apply_to'] = 'subtotal';
      }
    }
  }
  unset($coupon->data['max_applicable_products']);
  unset($coupon->data['max_applicable_products_value']);

  // Allow other modules to alter the coupon data.
  drupal_alter('uc_coupon', $coupon);
  return $coupon;
}

/**
 * Count usage of a coupon.
 *
 * @param $cid
 *   The coupon id to count.
 * @param $uid
 *   (optional) The user id to count. Defaults to the current user.
 *
 * @return
 *   An associative array containing:
 *   - codes: An associative array of code => usage count.
 *   - user: The usage count by the specified (or current) user.
 */
function uc_coupon_count_usage($cid, $uid = NULL) {
  global $user;
  $weight = uc_order_status_data(variable_get('uc_coupon_used_order_status', 'processing'), 'weight');
  $usage = array(
    'codes' => array(),
  );
  $result = db_query("SELECT uco.code, COUNT(*) AS uses FROM {uc_coupons_orders} AS uco\n    LEFT JOIN {uc_orders} AS uo ON uco.oid = uo.order_id\n    LEFT JOIN {uc_order_statuses} AS uos ON uo.order_status = uos.order_status_id\n    WHERE uos.weight >= %d AND uco.cid = %d GROUP BY uco.code", $weight, $cid);
  while ($row = db_fetch_object($result)) {
    $usage['codes'][$row->code] = $row->uses;
  }
  if (is_null($uid)) {
    $uid = $user->uid;
  }
  $usage['user'] = db_result(db_query("SELECT COUNT(*) FROM {uc_coupons_orders} AS uco\n    LEFT JOIN {uc_orders} AS uo ON uco.oid = uo.order_id\n    LEFT JOIN {uc_order_statuses} AS uos ON uo.order_status = uos.order_status_id\n    WHERE uos.weight >= %d AND uco.cid = %d AND uo.uid = %d", $weight, $cid, $uid));

  // Allow other modules to implement usage counts.
  drupal_alter('uc_coupon_usage', $usage, $cid, $uid);
  return $usage;
}

/**
 * Format a coupon depending on the type, optionally including currency symbols.
 */
function uc_coupon_format_discount($coupon, $currency = TRUE) {
  switch ($coupon->type) {
    case 'price':
      return $currency ? uc_currency_format($coupon->value) : $coupon->value;
    case 'percentage':
      return (double) $coupon->value . '%';
    case 'set_price':
      return '=' . ($currency ? uc_currency_format($coupon->value) : $coupon->value);
  }
}

/**
 * Generate a single bulk coupon code.
 */
function uc_coupon_get_bulk_code($coupon, $id) {

  // If this coupon has been validated, then $coupon->code is already a bulk code.
  if (isset($coupon->valid)) {
    $prefix = substr($coupon->code, 0, strlen($coupon->code) - $coupon->data['bulk_length']);
  }
  else {
    $prefix = $coupon->code;
  }
  $id = str_pad(dechex($id), strlen(dechex($coupon->data['bulk_number'])), '0', STR_PAD_LEFT);
  $length = strlen($prefix) + $coupon->data['bulk_length'];
  return strtoupper(substr($prefix . $id . md5($coupon->bulk_seed . $id), 0, $length));
}

/**
 * Load a coupon (single or bulk) from the supplied code.
 */
function uc_coupon_find($code) {

  // Look for matching single coupon first.
  $coupon = db_fetch_object(db_query("SELECT cid FROM {uc_coupons} WHERE code = '%s' AND status = 1 AND bulk = 0 AND valid_from < %d AND (valid_until = 0 OR valid_until > %d)", $code, time(), time()));
  if ($coupon) {
    return uc_coupon_load($coupon->cid);
  }

  // Look through bulk coupons.
  $result = db_query("SELECT cid, code, data, bulk_seed FROM {uc_coupons} WHERE status = 1 AND bulk = 1 AND valid_from < %d AND (valid_until = 0 OR valid_until > %d)", time(), time());
  while ($coupon = db_fetch_object($result)) {

    // Check coupon prefix.
    $prefix_length = strlen($coupon->code);
    if (substr($code, 0, $prefix_length) != $coupon->code) {
      continue;
    }
    if ($coupon->data) {
      $coupon->data = unserialize($coupon->data);
    }

    // Check coupon sequence ID.
    $id = substr($code, $prefix_length, strlen(dechex($coupon->data['bulk_number'])));
    if (!preg_match("/^[0-9A-F]+\$/", $id)) {
      continue;
    }
    $id = hexdec($id);
    if ($id < 0 || $id > $coupon->data['bulk_number']) {
      continue;
    }

    // Check complete coupon code.
    if ($code == uc_coupon_get_bulk_code($coupon, $id)) {
      return uc_coupon_load($coupon->cid);
    }
  }
  return FALSE;
}

/**
 * Validate a coupon, and optionally calculate the order discount.
 *
 * @param $code
 *   The coupon code entered at the checkout screen.
 * @param $order
 *   The order that the coupon is being applied to.
 *   If NULL, the current cart contents will be used.
 *   If FALSE, product and order validation will be bypassed.
 * @param $account
 *   The user who is attempting to use the coupon.
 *   If NULL, the current user will be assumed.
 *   If FALSE, user validation will be bypassed.
 *
 * @return
 *   A coupon object with extended information about the validation:
 *   - $coupon->valid: TRUE if the code was valid, FALSE otherwise.
 *   - $coupon->code: The specific code to be applied (even for bulk coupons).
 *   - $coupon->title: The line item title for the discount.
 *   - $coupon->amount: If $order !== FALSE, the discount that should be applied.
 *   - $coupon->message: If $coupon->valid == FALSE, the rejection reason.
 */
function uc_coupon_validate($code, $order = NULL, $account = NULL) {
  global $user;
  if (is_null($order)) {
    $order = new stdClass();
    $order->products = uc_cart_get_contents();
  }
  if (is_null($account)) {
    $account = $user;
  }

  // Look for an active coupon matching the code.
  $code = trim(strtoupper($code));
  $coupon = uc_coupon_find($code);
  if (!$coupon) {
    $coupon = new stdClass();
    $coupon->valid = FALSE;
    $coupon->message = t('This coupon code is invalid or has expired.');
    return $coupon;
  }

  // Assume the coupon is valid, unless a validation hook fails.
  $coupon->code = $code;
  $coupon->valid = TRUE;
  $coupon->usage = uc_coupon_count_usage($coupon->cid, $account ? $account->uid : NULL);

  // Calculate the discount.
  // We do this before invoking the validate hook so that modules can use this information.
  if ($order) {
    $order->data['coupon'] = $code;
    $items = uc_coupon_calculate_discounts($coupon, $order);
    $coupon->amount = 0;

    // If an array of items was returned, then sum the discounts for each applicable item.
    if (is_array($items)) {
      foreach ($items as $item) {
        $coupon->amount += $item->discount;
      }
    }
    else {
      $coupon->valid = FALSE;
      $coupon->message = $items;
    }
    $coupon->amount = round($coupon->amount, variable_get('uc_currency_prec', 2));
  }

  // Invoke validation hook.
  foreach (module_implements('uc_coupon_validate') as $module) {
    $callback = $module . '_uc_coupon_validate';
    $result = $callback($coupon, $order, $account);
    if ($result === TRUE) {

      // This module wishes the coupon to be accepted.
      $coupon->valid = TRUE;
    }
    else {
      if (!is_null($result)) {

        // This module wishes the coupon to be rejected.
        $coupon->valid = FALSE;
        $coupon->message = $result;
      }
    }
  }

  // Create the line item title for this coupon.
  $format = !empty($coupon->data['line_item_format']) ? $coupon->data['line_item_format'] : variable_get('uc_coupon_line_item_format', t('Coupon: [coupon-code]'));
  $coupon->title = token_replace_multiple(check_plain($format), array(
    'coupon' => $coupon,
  ));

  // Set a custom message if one exists.
  if ($coupon->valid && empty($coupon->message) && !empty($coupon->data['apply_message'])) {
    $coupon->message = token_replace_multiple(check_plain($coupon->data['apply_message']), array(
      'coupon' => $coupon,
    ));
  }
  return $coupon;
}

/**
 * Implementation of hook_uc_coupon_validate().
 * 
 * @param $coupon
 *   The coupon object to validate, with special fields set as follows:
 *   - $coupon->code: The specific code to be applied (even for bulk coupons).
 *   - $coupon->amount: If $order !== FALSE, the discount that should be applied.
 *   - $coupon->usage: Coupon usage data from uc_coupon_count_usage().
 * @param $order
 *   The order against which this coupon is to be applied, or FALSE to bypass order validation.
 * @param $account
 *   The account of the user trying to use the coupon, or FALSE to bypass user validation.
 *
 * @return
 *   TRUE if the coupon should be accepted.
 *   NULL to allow other modules to determine validation.
 *   Otherwise, a string describing the reason for failure.
 */
function uc_coupon_uc_coupon_validate(&$coupon, $order, $account) {

  // Check maximum usage per code.
  if ($coupon->max_uses > 0 && $coupon->usage['codes'][$coupon->code] >= $coupon->max_uses) {
    return t('This coupon has reached the maximum redemption limit.');
  }

  // Check maximum usage per user.
  if ($account && isset($coupon->data['max_uses_per_user']) && $coupon->usage['user'] >= $coupon->data['max_uses_per_user']) {
    return t('This coupon has reached the maximum redemption limit.');
  }

  // Check user ID.
  if ($account && isset($coupon->data['users'])) {
    if (in_array("{$account->uid}", $coupon->data['users'], TRUE) xor !isset($coupon->data['negate_users'])) {
      return t('Your user ID is not allowed to use this coupon.');
    }
  }

  // Check roles.
  if ($account && isset($coupon->data['roles'])) {
    $role_found = FALSE;
    foreach ($coupon->data['roles'] as $role) {
      if (in_array($role, $account->roles)) {
        $role_found = TRUE;
        break;
      }
    }
    if ($role_found xor !isset($coupon->data['negate_roles'])) {
      return t('You do not have the correct permission to use this coupon.');
    }
  }

  // Check wholesale permissions.
  if ($account) {
    if ($coupon->data['wholesale'] == 2 && !user_access('coupon wholesale pricing', $account)) {
      return t('You do not have the correct permission to use this coupon.');
    }
    else {
      if ($coupon->data['wholesale'] == 3 && user_access('coupon wholesale pricing', $account)) {
        return t('You do not have the correct permission to use this coupon.');
      }
    }
  }
}

/**
 * Find items that a coupon will apply to and calculate the discounts.
 * 
 * @param $coupon
 * 		The coupon object to be applied.
 * @param $order
 * 		The order to which it should be applied.
 * @return 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.
 */
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;
}
function _uc_coupon_match_sku($model, $skus) {
  foreach ($skus as $match) {
    if (preg_match('/^' . str_replace('\\*', '.*?', preg_quote($match, '/')) . '$/', $model)) {
      return TRUE;
    }
  }
  return FALSE;
}
function _uc_coupon_sort_products($a, $b) {
  if ($a->altered_price == $b->altered_price) {
    return 0;
  }
  return $a->altered_price > $b->altered_price ? 1 : -1;
}

/**
 * Run price alterers on a coupon value.
 */
function uc_coupon_price($amount, $revision = 'formatted') {
  $context = array(
    'revision' => $revision,
    'type' => 'line_item',
    'subject' => array(
      'order' => (object) array(
        'products' => uc_cart_get_contents(),
      ),
      'line_item' => array(
        'type' => 'coupon',
        'amount' => $amount,
        'weight' => 0,
      ),
    ),
  );
  return uc_price($amount, $context);
}

/**
 * Implementation of hook_block().
 */
function uc_coupon_block($op = 'list', $delta = 0, $edit = array()) {
  switch ($op) {
    case 'list':
      $blocks = array();
      $blocks[0] = array(
        'info' => t('Coupon discount form'),
      );
      return $blocks;
    case 'view':
      if ($delta == 0) {
        $block = array(
          'subject' => t('Coupon discount'),
          'content' => drupal_get_form('uc_coupon_block_form'),
        );
        return $block;
      }
      break;
  }
}

/**
 * Implementation of hook_cart_pane().
 */
function uc_coupon_cart_pane($items) {
  $panes[] = array(
    'id' => 'coupon',
    'body' => drupal_get_form('uc_coupon_block_form', 25),
    'title' => t('Coupon discount'),
    'desc' => t('Allows shoppers to use a coupon during checkout for order discounts.'),
    'weight' => 1,
    'enabled' => TRUE,
  );
  return $panes;
}

/**
 * Coupon block form, also available as a cart pane.
 */
function uc_coupon_block_form($form_state, $size = 15) {
  $form['code'] = array(
    '#type' => 'textfield',
    '#title' => t('Coupon code'),
    '#default_value' => isset($_SESSION['uc_coupon']) ? $_SESSION['uc_coupon'] : NULL,
    '#size' => $size,
  );
  $form['apply'] = array(
    '#type' => 'submit',
    '#value' => t('Apply to order'),
  );
  return $form;
}

/**
 * Block form submit handler.
 * 
 * @see uc_coupon_submit()
 */
function uc_coupon_block_form_submit($form, &$form_state) {
  uc_coupon_submit('block', $form_state['values']['code']);
}

/**
 * Implementation of hook_order().
 *
 * Ensure any cart pane coupon is added to the order if the checkout page is skipped
 * (e.g. Paypal Express Checkout, Google Checkout)
 */
function uc_coupon_order($op, &$arg1, $arg2) {
  if ($op == 'save' && isset($_SESSION['uc_coupon'])) {
    $coupon = uc_coupon_validate($_SESSION['uc_coupon'], $arg1);
    if ($coupon->valid) {
      _uc_coupon_apply_to_order($arg1->order_id, $coupon);
    }
  }
}

/**
 * Implements hook_order_product_alter().
 * 
 * Ensure that dummy cart-item coupons don't get saved with an order.
 */
function uc_coupon_order_product_alter(&$product, $order) {
  if (isset($product->module) && $product->module == 'uc_coupon') {
    $product->skip_save = TRUE;
  }
}

/**
 * Implementation of hook_form_FORM_ID_alter().
 * 
 * Clear the session coupon when the order edit form is built.
 * This avoid inadvertently saving a coupon to an old order.
 */
function uc_coupon_form_uc_order_edit_form_alter(&$form, $form_state) {
  unset($_SESSION['uc_coupon']);
}

/**
 * Implementation of hook_checkout_pane().
 *
 * Show a pane just above the order total that allows shoppers to enter a coupon
 * for a discount.
 */
function uc_coupon_checkout_pane() {
  $panes[] = array(
    'id' => 'coupon',
    'callback' => 'uc_checkout_pane_coupon',
    'title' => t('Coupon discount'),
    'desc' => t('Allows shoppers to use a coupon during checkout for order discounts.'),
    'weight' => 5,
    'process' => TRUE,
  );
  return $panes;
}

/**
 * Checkout Pane callback function.
 *
 * Used to display a form in the checkout process so that customers
 * can enter discount coupons.
 */
function uc_checkout_pane_coupon($op, &$arg1, $arg2) {
  switch ($op) {
    case 'view':
      drupal_add_js(drupal_get_path('module', 'uc_coupon') . '/uc_coupon.js');
      drupal_add_js(array(
        'ucURL' => array(
          'applyCoupon' => url('cart/checkout/coupon'),
        ),
      ), 'setting');
      if (isset($_SESSION['uc_coupon'])) {
        $code = $_SESSION['uc_coupon'];
      }
      else {
        if (isset($arg1->data['coupon'])) {
          $code = $_SESSION['uc_coupon'] = $arg1->data['coupon'];
        }
        else {
          $code = '';
        }
      }
      if ($code) {
        $coupon = uc_coupon_validate($code, empty($arg1->products) ? NULL : $arg1);
        if ($coupon->valid) {
          if (variable_get('uc_coupon_show_in_cart', TRUE)) {

            // Modify stored subtotal to ignore coupon cart item.
            drupal_add_js('$(function() {
              if (window.set_line_item) {
                li_values["subtotal"] += ' . $coupon->amount . ';
              }
            });', 'inline');
          }
          drupal_add_js('$(function() {
            if (window.set_line_item) {
              set_line_item("coupon", "' . $coupon->title . '", ' . -$coupon->amount . ', ' . _line_item_data('coupon', 'weight') . ');
            }
          });', 'inline');
        }
      }
      $description = variable_get('uc_coupon_pane_description', t('Enter a coupon code for this order.'));
      $contents['code'] = array(
        '#type' => 'textfield',
        '#title' => t('Coupon code'),
        '#default_value' => $code,
        '#size' => 25,
      );
      $contents['apply'] = array(
        '#type' => 'submit',
        '#submit' => array(
          'uc_coupon_uc_cart_checkout_submit',
        ),
        '#value' => t('Apply to order'),
        '#attributes' => array(
          'onclick' => "getCoupon(); return false;",
        ),
        '#suffix' => '<span id="coupon-throbber"></span>',
      );
      return array(
        'description' => $description,
        'contents' => $contents,
        'theme' => 'uc_checkout_pane_coupon',
      );
    case 'process':
      if ($arg2['code']) {
        $arg1->data['coupon'] = $arg2['code'];
        $coupon = uc_coupon_validate($arg1->data['coupon'], $arg1);
        if (!$coupon->valid) {
          drupal_set_message($coupon->message, 'error');
          unset($_SESSION['uc_coupon']);
          unset($arg1->data['coupon']);
          return FALSE;
        }
        $_SESSION['uc_coupon'] = $arg1->data['coupon'];
        _uc_coupon_apply_to_order($arg1->order_id, $coupon);
      }
      elseif (isset($_SESSION['uc_coupon'])) {
        unset($_SESSION['uc_coupon']);
        unset($arg1->data['coupon']);
        _uc_coupon_apply_to_order($arg1->order_id, NULL);
      }
      return TRUE;
    case 'settings':
      $form['uc_coupon_collapse_pane'] = array(
        '#type' => 'checkbox',
        '#title' => t('Collapse checkout pane by default.'),
        '#default_value' => variable_get('uc_coupon_collapse_pane', FALSE),
      );
      $form['uc_coupon_pane_description'] = array(
        '#type' => 'textarea',
        '#title' => t('Checkout pane message'),
        '#default_value' => variable_get('uc_coupon_pane_description', t('Enter a coupon code for this order.')),
      );
      return $form;
  }
}

/**
 * Checkout pane AJAX callback.
 **/
function uc_coupon_checkout_apply() {
  uc_coupon_submit('ajax', $_POST['code'], unserialize($_POST['order']));

  // Does not return for ajax calls.
}

/**
 * Submit a coupon from checkout page or block.
 * 
 * @param $code 
 * 		The user-entered code to submit.
 * @param $order 
 * 		The current order object.  If NULL, will use the current cart contents.
 * 
 * @see 
 *    uc_coupon_uc_cart_checkout_submit()
 *    uc_coupon_checkout_apply()
 *    uc_coupon_block_form_submit()
 */
function uc_coupon_submit($context, $code, $order = NULL) {
  $error = FALSE;
  $code = trim($code);
  if (empty($code)) {
    $coupon = new stdClass();
    $coupon->valid = FALSE;
    if (isset($_SESSION['uc_coupon'])) {
      $coupon->message = t('Coupon "@code" has been removed from your order.', array(
        '@code' => $_SESSION['uc_coupon'],
      ));
      unset($_SESSION['uc_coupon']);
    }
    else {
      $coupon->message = t('You must enter a valid coupon code.');
      $error = TRUE;
    }
  }
  else {
    $coupon = uc_coupon_validate($code, $order);
    if ($coupon->valid) {
      $_SESSION['uc_coupon'] = $code;
      if (!$coupon->message) {
        $amount = uc_coupon_price($coupon->amount);
        if ($coupon->amount) {
          if (variable_get('uc_coupon_show_in_cart', TRUE) || $context != 'block') {
            $coupon->message = t('A discount of !amount has been applied to your order.', array(
              '!amount' => $amount,
            ));
          }
          else {
            $coupon->message = t('A discount of !amount will be applied at checkout.', array(
              '!amount' => $amount,
            ));
          }
        }
        else {
          $coupon->message = t('Coupon "@code" has been applied to your order', array(
            '@code' => $code,
          ));
        }
      }
    }
    else {
      unset($_SESSION['uc_coupon']);
      $error = TRUE;
    }
  }

  // Return the coupon to JS for an ajax request; otherwise display a message.
  if ($context == 'ajax') {
    drupal_json($coupon);
    exit;
  }
  else {
    drupal_set_message($coupon->message, $error ? 'error' : 'status');
  }
}

/**
 * Submit handler for "Apply to Order" button in uc_cart_checkout.
 * This handler is only invoked when JS is disabled.
 * 
 * @see 
 * 		uc_coupon_checkout_apply()
 * 		uc_coupon_submit()
 */
function uc_coupon_uc_cart_checkout_submit(&$form, &$form_state) {
  uc_coupon_submit('checkout', $form_state['values']['panes']['coupon']['code']);
}

/**
 * Validation handler for uc_cart_checkout.
 * Used to clear checkout form validation errors when the "Apply to order" button is clicked and JS is disabled.
 * 
 * @see 
 * 		uc_coupon_submit()
 * 		uc_coupon_uc_cart_checkout_submit()
 */
function uc_coupon_uc_cart_checkout_validate($form, $form_state) {

  // Clear all form errors only when the "Apply Coupon" button is clicked (and JS is disabled).
  if (preg_match('/coupon-apply$/', $form_state['clicked_button']['#id'])) {
    form_set_error(NULL, '', TRUE);
    drupal_get_messages('error');
  }
}

/**
 * Create, update or remove the coupon line item in an order.
 */
function _uc_coupon_apply_to_order($order_id, $coupon) {
  $lid = db_result(db_query("SELECT line_item_id FROM {uc_order_line_items} WHERE order_id = %d AND type = 'coupon'", $order_id));
  if ($lid) {
    if ($coupon) {
      db_query("UPDATE {uc_coupons_orders} SET cid = %d, code = '%s', value = %f WHERE oid = %d", $coupon->cid, $coupon->code, $coupon->amount, $order_id);
      uc_order_update_line_item($lid, $coupon->title, -$coupon->amount);
    }
    else {
      db_query('DELETE FROM {uc_coupons_orders} WHERE oid = %d', $order_id);
      uc_order_delete_line_item($lid);
    }
  }
  else {
    db_query("INSERT INTO {uc_coupons_orders} (cid, oid, code, value) VALUES (%d, %d, '%s', %f)", $coupon->cid, $order_id, $coupon->code, $coupon->amount);
    uc_order_line_item_add($order_id, 'coupon', $coupon->title, -$coupon->amount);
  }
}

/**
 * Implementation of hook_line_item().
 */
function uc_coupon_line_item() {
  $items[] = array(
    'id' => 'coupon',
    'title' => t('Coupon discount'),
    'tax_adjustment' => 'uc_coupon_tax_adjustment',
    'weight' => 0,
    'default' => FALSE,
    'stored' => TRUE,
    'add_list' => TRUE,
    'calculated' => TRUE,
  );
  return $items;
}

/**
 * Handle tax on coupons by calculating tax for individual discounted prices.
 * This is currently only supported by the VAT module (uc_vat).
 */
function uc_coupon_tax_adjustment($price, $order, $tax) {
  if (isset($order->data['coupon'])) {
    $code = $order->data['coupon'];
  }
  else {
    if (isset($_SESSION['uc_coupon'])) {
      $code = $_SESSION['uc_coupon'];
    }
    else {
      return;
    }
  }
  $coupon = uc_coupon_find($code);
  $items = uc_coupon_calculate_discounts($coupon, $order);
  $amount = 0;
  if (is_array($items)) {
    foreach ($items as $item) {
      $node = node_load($item->nid);
      if (in_array($node->type, $tax->taxed_product_types) && ($tax->shippable == 0 || $node->shippable == 1)) {
        $amount += $item->discount * $tax->rate * ($price > 0 ? 1 : -1);
      }
    }
  }
  return $amount;
}

/**
 * Implementation of hook_store_status().
 */
function uc_coupon_store_status() {
  if (variable_get('uc_payment_method_paypal_wps_checkout', 0) && variable_get('uc_paypal_wps_submit_method', 'single') == 'itemized') {
    $statuses[] = array(
      'status' => 'warning',
      'title' => t('Coupons'),
      'desc' => t('To use coupons with PayPal you must select "Submit the whole order as a single line item". <a href="!url">Click here to change this setting</a>.', array(
        '!url' => url('admin/store/settings/payment/edit/methods'),
      )),
    );
  }
  return $statuses;
}

/**
 * Implementation of hook_token_list().
 */
function uc_coupon_token_list($type = 'all') {
  $tokens = array();
  if ($type == 'order' || $type == 'ubercart' || $type == 'all') {
    $tokens['order']['order-coupon-code'] = t('The coupon code used in the order.');
  }
  if ($type == 'coupon' || $type == 'all') {
    $tokens['coupon']['coupon-name'] = t('The coupon name.');
    $tokens['coupon']['coupon-code'] = t('The coupon code.');
    $tokens['coupon']['coupon-bulk-codes'] = t('The list of bulk coupon codes, if the coupon is a bulk coupon.');
    $tokens['coupon']['coupon-value'] = t('The value of the coupon.');
  }
  return $tokens;
}

/**
 * Implementation of hook_token_values().
 */
function uc_coupon_token_values($type, $object = NULL) {
  $values = array();
  switch ($type) {
    case 'order':
      $values['order-coupon-code'] = isset($object->data['coupon']) ? $object->data['coupon'] : '';
      break;
    case 'coupon':
      $values['coupon-name'] = $object->name;
      $values['coupon-code'] = $object->code;
      if ($object->bulk) {
        $codes = array();
        for ($id = 0; $id < $object->data['bulk_number']; $id++) {
          $codes[] = uc_coupon_get_bulk_code($object, $id);
        }
        $values['coupon-bulk-codes'] .= implode("\n", $codes);
      }
      $values['coupon-value'] = isset($object->amount) ? uc_coupon_price($object->amount) : uc_coupon_format_discount($object);
      break;
  }
  return $values;
}

/**
 * Implementation of hook_uc_cart_alter().
 *
 * If a coupon is in use, add it as a (fake) cart item.
 * This will be converted to a real line item during checkout.
 */
function uc_coupon_uc_cart_alter(&$items, $ignore = NULL) {
  if (variable_get('uc_coupon_show_in_cart', TRUE) && isset($_SESSION['uc_coupon'])) {
    $coupon = uc_coupon_validate($_SESSION['uc_coupon']);
    if ($coupon->valid) {
      $items[] = (object) array(
        'module' => 'uc_coupon',
        'coupon' => $coupon,
        'title' => $coupon->title,
        'nid' => 0,
        'qty' => 1,
        'price' => uc_coupon_price(-$coupon->amount, 'altered'),
        'data' => array(
          'shippable' => FALSE,
        ),
      );
    }
  }
}

/**
 * Implementation of hook_cart_display().
 */
function uc_coupon_cart_display($item) {
  return array(
    '#module' => 'uc_coupon',
    // Not used in core, but allows modules/themes to act on this special cart item.
    'nid' => array(
      '#type' => 'value',
      '#value' => 0,
    ),
    'title' => array(
      '#value' => $item->title,
    ),
    '#total' => $item->price,
  );
}

/**
 * Implementation of hook_form_FORM_ID_alter() for uc_cart_checkout_form().
 *
 * Remove the coupon cart item, as it will be handled as a line item during checkout.
 * Collapse coupon checkout pane, if configured to do so.
 */
function uc_coupon_form_uc_cart_checkout_form_alter(&$form, $form_state) {
  if (variable_get('uc_coupon_show_in_cart', TRUE) && isset($_SESSION['uc_coupon'])) {
    $items = unserialize($form['cart_contents']['#value']);
    foreach ($items as $key => $item) {
      if ($item->module == 'uc_coupon') {
        unset($items[$key]);
      }
    }
    $form['cart_contents']['#value'] = serialize($items);
  }
  if (variable_get('uc_coupon_collapse_pane', FALSE) && isset($form['panes']['coupon'])) {
    $form['panes']['coupon']['#collapsed'] = TRUE;
  }

  // Add a validate callback to clear errors when coupon is applied without JavaScript.
  $form['#validate'][] = 'uc_coupon_uc_cart_checkout_validate';
}

/**
 * Implementation of hook_uc_checkout_complete().
 *
 * Ensure the stored coupon code is reset after checkout.
 */
function uc_coupon_uc_checkout_complete($order, $account) {
  unset($_SESSION['uc_coupon']);
}

/**
 * Preprocess template for a printed coupon certificate.
 * @see uc_coupon-certificate.tpl.php
 */
function template_preprocess_uc_coupon_certificate(&$variables) {
  $coupon = $variables['coupon'];
  $variables['value'] = uc_coupon_format_discount($coupon);
  $variables['display_name'] = check_plain($coupon->name);
  $n = stripos($variables['display_name'], 'purchased by');
  if ($n) {
    $variables['display_name'] = substr($variables['display_name'], 0, $n - 1);
  }
  if ($coupon->valid_until) {
    $variables['not_yet_valid'] = $coupon->valid_from > time();
    $variables['valid_from'] = format_date($coupon->valid_from, 'custom', variable_get('uc_date_format_default', 'm/d/Y'), 0);
    $variables['valid_until'] = format_date($coupon->valid_until, 'custom', variable_get('uc_date_format_default', 'm/d/Y'), 0);
  }
  else {
    $variables['not_yet_valid'] = FALSE;
    $variables['valid_from'] = FALSE;
    $variables['valid_until'] = FALSE;
  }
  $variables['max_uses_per_user'] = $coupon->data['max_uses_per_user'];
  $variables['include'] = array();
  $variables['exclude'] = array();
  if (isset($coupon->data['product_types'])) {
    foreach ($coupon->data['product_types'] as $type) {
      $variables['include'][] = node_get_types('name', $type);
    }
  }
  if (isset($coupon->data['products'])) {
    $key = isset($coupon->data['negate_products']) ? 'exclude' : 'include';
    foreach ($coupon->data['products'] as $nid) {
      $node = node_load($nid);
      $variables[$key][] = $node->title;
    }
  }
  if (isset($coupon->data['skus'])) {
    foreach ($coupon->data['skus'] as $sku) {
      $variables['include'][] = t('SKU') . ' ' . $sku;
    }
  }
  if (isset($coupon->data['terms'])) {
    $key = isset($coupon->data['negate_terms']) ? 'exclude' : 'include';
    foreach ($coupon->data['terms'] as $tid) {
      $term = taxonomy_get_term($tid);
      $variables[$key][] = $term->name;
    }
  }

  // Merge in global tokens.
  $tokens = token_get_values();
  foreach ($tokens->tokens as $key => $token) {
    $value = $tokens->values[$key];
    $variables[str_replace('-', '_', $token)] = $value;
  }
  if (isset($variables['coupon']->data['base_cid'])) {
    $variables['template_files'][] = 'uc_coupon-certificate-base-' . $variables['coupon']->data['base_cid'];
  }
  $variables['template_files'][] = 'uc_coupon-certificate-' . $variables['coupon']->cid;
}

/**
 * Page template for printed coupons.
 * @see uc_coupon-page.tpl.php
 */
function template_preprocess_uc_coupon_page(&$variables) {
  $variables['styles'] = drupal_get_css();
}

/**
 * Implementation of hook_uc_coupon_actions().
 */
function uc_coupon_uc_coupon_actions($coupon) {
  $actions = array();
  if (user_access('view store coupons')) {
    $actions[] = array(
      'url' => 'admin/store/coupons/' . $coupon->cid,
      'icon' => drupal_get_path('module', 'uc_store') . '/images/order_view.gif',
      'title' => t('View coupon: @name', array(
        '@name' => $coupon->name,
      )),
    );
    $actions[] = array(
      'url' => 'admin/store/coupons/' . $coupon->cid . '/print',
      'icon' => drupal_get_path('module', 'uc_store') . '/images/print.gif',
      'title' => t('Print coupon: @name', array(
        '@name' => $coupon->name,
      )),
    );
    if ($coupon->bulk) {
      $actions[] = array(
        'url' => 'admin/store/coupons/' . $coupon->cid . '/codes',
        'icon' => drupal_get_path('module', 'uc_store') . '/images/menu_reports_small.gif',
        'title' => t('Download codes as CSV: @name', array(
          '@name' => $coupon->name,
        )),
      );
    }
  }
  if (user_access('manage store coupons')) {
    $actions[] = array(
      'url' => 'admin/store/coupons/' . $coupon->cid . '/edit',
      'icon' => drupal_get_path('module', 'uc_store') . '/images/order_edit.gif',
      'title' => t('Edit coupon: @name', array(
        '@name' => $coupon->name,
      )),
    );
    $actions[] = array(
      'url' => 'admin/store/coupons/' . $coupon->cid . '/delete',
      'icon' => drupal_get_path('module', 'uc_store') . '/images/order_delete.gif',
      'title' => t('Delete coupon: @name', array(
        '@name' => $coupon->name,
      )),
    );
  }
  return $actions;
}

/**
 * Implementation of hook_views_api().
 */
function uc_coupon_views_api() {
  return array(
    'api' => '2.0',
    'path' => drupal_get_path('module', 'uc_coupon') . '/views',
  );
}

Functions

Namesort descending Description
template_preprocess_uc_coupon_certificate Preprocess template for a printed coupon certificate.
template_preprocess_uc_coupon_page Page template for printed coupons.
theme_uc_checkout_pane_coupon Default theme implementation for the checkout pane.
uc_checkout_pane_coupon Checkout Pane callback function.
uc_coupon_block Implementation of hook_block().
uc_coupon_block_form Coupon block form, also available as a cart pane.
uc_coupon_block_form_submit Block form submit handler.
uc_coupon_calculate_discounts Find items that a coupon will apply to and calculate the discounts.
uc_coupon_cart_display Implementation of hook_cart_display().
uc_coupon_cart_pane Implementation of hook_cart_pane().
uc_coupon_checkout_apply Checkout pane AJAX callback.
uc_coupon_checkout_pane Implementation of hook_checkout_pane().
uc_coupon_count_usage Count usage of a coupon.
uc_coupon_find Load a coupon (single or bulk) from the supplied code.
uc_coupon_format_discount Format a coupon depending on the type, optionally including currency symbols.
uc_coupon_form_uc_cart_checkout_form_alter Implementation of hook_form_FORM_ID_alter() for uc_cart_checkout_form().
uc_coupon_form_uc_order_edit_form_alter Implementation of hook_form_FORM_ID_alter().
uc_coupon_get_bulk_code Generate a single bulk coupon code.
uc_coupon_init Implementation of hook_init().
uc_coupon_line_item Implementation of hook_line_item().
uc_coupon_load Load a coupon object.
uc_coupon_menu Implementation of hook_menu().
uc_coupon_order Implementation of hook_order().
uc_coupon_order_product_alter Implements hook_order_product_alter().
uc_coupon_perm Implementation of hook_perm().
uc_coupon_price Run price alterers on a coupon value.
uc_coupon_save Save a coupon object.
uc_coupon_store_status Implementation of hook_store_status().
uc_coupon_submit Submit a coupon from checkout page or block.
uc_coupon_tax_adjustment Handle tax on coupons by calculating tax for individual discounted prices. This is currently only supported by the VAT module (uc_vat).
uc_coupon_theme Implementation of hook_theme().
uc_coupon_title
uc_coupon_token_list Implementation of hook_token_list().
uc_coupon_token_values Implementation of hook_token_values().
uc_coupon_uc_cart_alter Implementation of hook_uc_cart_alter().
uc_coupon_uc_cart_checkout_submit Submit handler for "Apply to Order" button in uc_cart_checkout. This handler is only invoked when JS is disabled.
uc_coupon_uc_cart_checkout_validate Validation handler for uc_cart_checkout. Used to clear checkout form validation errors when the "Apply to order" button is clicked and JS is disabled.
uc_coupon_uc_checkout_complete Implementation of hook_uc_checkout_complete().
uc_coupon_uc_coupon_actions Implementation of hook_uc_coupon_actions().
uc_coupon_uc_coupon_validate Implementation of hook_uc_coupon_validate().
uc_coupon_validate Validate a coupon, and optionally calculate the order discount.
uc_coupon_views_api Implementation of hook_views_api().
_uc_coupon_apply_to_order Create, update or remove the coupon line item in an order.
_uc_coupon_match_sku
_uc_coupon_sort_products