function uc_discounts_get_discounts_for_order in Ubercart Discounts (Alternative) 7.2
Returns discounts for order.
Note: $order->uc_discounts_codes must be set
Return value
array Keyed array with 'discounts' array and 'messages' array.
10 calls to uc_discounts_get_discounts_for_order()
- uc_discounts_apply in uc_discounts/
uc_discounts.module - Applies the discounts to an order by creating the necessary line items.
- uc_discounts_condition_discount_applied in uc_discounts/
uc_discounts.ca.inc - Determines if a discounts has been applied.
- uc_discounts_condition_discount_applied_condition in uc_discounts/
uc_discounts.rules.inc - Checks if a particluar discount id has been applied on an order.
- uc_discounts_condition_total in uc_discounts/
uc_discounts.ca.inc - Check the current order balance minus any discounts.
- uc_discounts_condition_total_after_discounts in uc_discounts/
uc_discounts.rules.inc - Check the current order balance minus any discounts.
File
- uc_discounts/
uc_discounts.module, line 1250
Code
function uc_discounts_get_discounts_for_order($order, $reset = FALSE) {
// Store discounts in static variable.
$order_discounts =& drupal_static(__FUNCTION__);
$messages = array(
'success' => array(),
'warnings' => array(),
'errors' => array(),
);
$discounts = array();
$total_discount_amount = 0;
if (!isset($order->order_id)) {
$messages['errors'][] = t('Order ID does not exist.');
return array(
'discounts' => $discounts,
'messages' => $messages,
'total_discount_amount' => $total_discount_amount,
);
}
// If reset then delete static variable for the order id. Also orders that
// have order_id = 0 are not in the checkout process yet. These keep changing
// so they are not saved.
if (($reset || $order->order_id == 0) && isset($order_discounts[$order->order_id])) {
unset($order_discounts[$order->order_id]);
}
// If static variable exists then return it.
if (isset($order_discounts[$order->order_id])) {
return $order_discounts[$order->order_id];
}
// If no static variable exists then do all the heavy lifting.
// Product NIDS in cart => subtotal of individual item.
$order_product_id_subtotal_map = array();
// Product NIDS in cart => quantity of individual item.
$order_product_id_quantity_map = array();
// Product NIDS in cart.
$order_product_ids = array();
// Product NIDS in cart=> bool.
$order_product_ids_set = array();
// Product objects in cart.
$order_product_id_product_array_map = array();
$order_subtotal = 0;
$kits = array();
$where_clauses = array();
// Create IN string of product node IDs in order.
if (is_array($order->products) && !empty($order->products)) {
foreach ($order->products as $product) {
$nid = $product->nid;
$order_product_ids_set[$nid] = TRUE;
if (is_array($product->data) && !empty($product->data['kit_id'])) {
$kit_id = $product->data['kit_id'];
$order_product_ids_set[$kit_id] = TRUE;
if (!isset($kits[$kit_id])) {
$kits[$kit_id] = array(
'product_qty' => $product->qty,
);
}
elseif (!isset($kits[$kit_id]['product_qty'])) {
$kits[$kit_id]['product_qty'] = $product->qty;
}
else {
$kits[$kit_id]['product_qty'] += $product->qty;
}
}
uc_discounts_add_to_existing_map_number_value($order_product_id_subtotal_map, $nid, $product->price * $product->qty);
uc_discounts_add_to_existing_map_number_value($order_product_id_quantity_map, $nid, $product->qty);
$a = array();
if (isset($order_product_id_product_array_map[$nid]) && is_array($order_product_id_product_array_map[$nid])) {
$a = $order_product_id_product_array_map[$nid];
}
$a[] = $product;
$order_product_id_product_array_map[$nid] = $a;
$order_subtotal += $product->price * $product->qty;
}
foreach ($kits as $kit_id => $value) {
$kit_node = node_load($kit_id);
foreach ($kit_node->products as $product_in_kit) {
$pik_nid = $product_in_kit->nid;
foreach ($order->products as $key => $product) {
if ($product->nid == $pik_nid && isset($product->data['kit_id']) && $product->data['kit_id'] == $kit_id) {
$kits[$kit_id]['kit_qty'] = $product->qty / $product_in_kit->qty;
break;
}
}
}
uc_discounts_add_to_existing_map_number_value($order_product_id_quantity_map, $kit_id, $kits[$kit_id]['kit_qty']);
}
$order_product_ids = array_keys($order_product_ids_set);
}
// Populate product NID array with NIDs from the order.
$where_product_ids = $order_product_ids;
$where_product_ids[] = UC_DISCOUNTS_OPTION_ALL_PRODUCTS;
$where_clauses['products'] = array(
'sql' => 'd.filter_type <> :filter_type_products OR dp.product_id IN (:product_ids)',
'args' => array(
':filter_type_products' => UC_DISCOUNTS_FILTER_TYPE_PRODUCTS,
':product_ids' => $where_product_ids,
),
);
// Search for product terms and add where clause to main query.
$where_product_terms = array();
$where_product_terms[] = UC_DISCOUNTS_OPTION_ALL_TERMS;
if (!empty($order_product_ids)) {
// Get terms for order's products.
$where_product_terms_result = db_query("SELECT DISTINCT tid\n FROM {taxonomy_index}\n WHERE nid IN (:nids)", array(
':nids' => $order_product_ids,
));
$where_product_terms += $where_product_terms_result
->fetchCol();
}
$where_clauses['terms'] = array(
'sql' => 'd.filter_type <> :filter_type_terms OR dt.term_id IN (:product_terms)',
'args' => array(
':filter_type_terms' => UC_DISCOUNTS_FILTER_TYPE_TERMS,
':product_terms' => $where_product_terms,
),
);
// Create IN string of SKUs in order.
$where_product_skus = array();
$where_product_skus[] = UC_DISCOUNTS_OPTION_ALL_SKUS;
if (!empty($order_product_ids)) {
// Get SKUs for order's products.
$result = db_query("SELECT DISTINCT model\n FROM {uc_products}\n WHERE nid IN (:nids)", array(
':nids' => $order_product_ids,
));
$where_product_skus += $result
->fetchCol();
}
$where_clauses['skus'] = array(
'sql' => 'd.filter_type <> :filter_type_skus OR ds.sku IN (:product_skus)',
'args' => array(
':filter_type_skus' => UC_DISCOUNTS_FILTER_TYPE_SKUS,
':product_skus' => $where_product_skus,
),
);
// Create IN string of classes in order.
$where_product_classes = array();
$where_product_classes[] = UC_DISCOUNTS_OPTION_ALL_CLASSES;
if (!empty($order_product_ids)) {
// Get classes for order's products.
$where_product_classes_result = db_query("SELECT DISTINCT type\n FROM {node}\n WHERE nid IN (:nids)", array(
':nids' => $order_product_ids,
));
$where_product_classes += $where_product_classes_result
->fetchCol();
}
$where_clauses['classes'] = array(
'sql' => 'd.filter_type <> :filter_type_class OR dcl.class IN (:product_classes)',
'args' => array(
':filter_type_class' => UC_DISCOUNTS_FILTER_TYPE_CLASS,
':product_classes' => $where_product_classes,
),
);
// Create IN string of authors in order.
$where_authors = array();
$where_authors[] = UC_DISCOUNTS_OPTION_ALL_AUTHORS;
if (!empty($order_product_ids)) {
// Get authors for order's products.
$where_authors_result = db_query("SELECT DISTINCT uid\n FROM {node}\n WHERE nid IN (:nids)", array(
':nids' => $order_product_ids,
));
$where_authors += $where_authors_result
->fetchCol();
}
$where_clauses['authors'] = array(
'sql' => 'd.filter_type <> :filter_type_authors OR dau.author_id IN (:product_authors)',
'args' => array(
':filter_type_authors' => UC_DISCOUNTS_FILTER_TYPE_AUTHORS,
':product_authors' => $where_authors,
),
);
// Create codes clause.
$order_discount_codes = array();
$where_clauses['codes'] = array(
'sql' => 'd.requires_code = 0',
'args' => array(),
);
if (!empty($order->uc_discounts_codes)) {
$order_discount_codes = $order->uc_discounts_codes;
$valid_codes_result = db_query('SELECT discount_id FROM {uc_discounts_codes} WHERE code IN (:order_discount_codes)', array(
':order_discount_codes' => $order_discount_codes,
));
$valid_codes_ids = $valid_codes_result
->fetchCol();
if (!empty($valid_codes_ids)) {
$where_clauses['codes'] = array(
'sql' => 'd.requires_code = 0 OR d.discount_id IN (:codes_ids)',
'args' => array(
':codes_ids' => $valid_codes_ids,
),
);
}
}
// Create roles clause.
$auth_rid = $order->uid != 0 ? DRUPAL_AUTHENTICATED_RID : DRUPAL_ANONYMOUS_RID;
$roles_discounts_sql = 'SELECT DISTINCT dr.discount_id FROM {uc_discounts_roles} dr' . ' LEFT JOIN {users_roles} ur ON (ur.rid = dr.role_id AND ur.uid = :uid)' . ' WHERE ur.uid IS NOT NULL' . ' OR dr.role_id = :all_roles' . ' OR dr.role_id = :auth_role';
$roles_discounts_args = array(
':uid' => $order->uid,
':all_roles' => UC_DISCOUNTS_OPTION_ALL_ROLES,
':auth_role' => $auth_rid,
);
$roles_discounts_result = db_query($roles_discounts_sql, $roles_discounts_args);
$roles_discounts_ids = $roles_discounts_result
->fetchCol();
if (!empty($roles_discounts_ids)) {
$where_clauses['roles'] = array(
'sql' => 'd.has_role_filter = 0 OR d.discount_id IN (:role_discounts)',
'args' => array(
':role_discounts' => $roles_discounts_ids,
),
);
}
else {
$where_clauses['roles'] = array(
'sql' => 'd.has_role_filter = 0',
'args' => array(),
);
}
$grouping = UC_DISCOUNTS_GROUPING_APPLICATION;
// Add warnings for expired discounts with codes if necessary.
if (!empty($order->uc_discounts_codes)) {
$expired_sql = "SELECT DISTINCT d.*, dc.code code FROM {uc_discounts} d\n LEFT JOIN {uc_discounts_products} dp ON (dp.discount_id = d.discount_id AND dp.grouping = :grouping)\n LEFT JOIN {uc_discounts_terms} dt ON (dt.discount_id = d.discount_id AND dt.grouping = :grouping)\n LEFT JOIN {uc_discounts_skus} ds ON (ds.discount_id = d.discount_id AND ds.grouping = :grouping)\n LEFT JOIN {uc_discounts_classes} dcl ON (dcl.discount_id = d.discount_id AND dcl.grouping = :grouping)\n LEFT JOIN {uc_discounts_authors} dau ON (dau.discount_id = d.discount_id AND dau.grouping = :grouping)\n LEFT JOIN {uc_discounts_roles} dr ON (dr.discount_id = d.discount_id)\n LEFT JOIN {uc_discounts_codes} dc ON (dc.discount_id = d.discount_id)\n WHERE dc.code IN (:order_discount_codes)\n AND (d.has_expiration <> 0 AND d.expiration <= :request_time)\n AND (d.is_active = :discount_active_constant)";
$expired_args = array(
':grouping' => $grouping,
':order_discount_codes' => $order_discount_codes,
':request_time' => REQUEST_TIME,
':discount_active_constant' => UC_DISCOUNTS_DISCOUNT_ACTIVE,
);
foreach ($where_clauses as $key => $clause) {
// @todo Why is the 'codes' clause ommited from the original sql?
if ($key != 'codes') {
$expired_sql .= ' AND (' . $clause['sql'] . ')';
$expired_args += $clause['args'];
}
}
$expired_sql .= " ORDER BY d.weight";
$expired_result = db_query($expired_sql, $expired_args);
foreach ($expired_result as $discount) {
$messages['warnings'][] = t('The discount code %code has expired.', array(
'%code' => $discount->code,
));
}
}
$valid_discounts_sql = "SELECT DISTINCT d.* FROM {uc_discounts} d\n LEFT JOIN {uc_discounts_products} dp ON (dp.discount_id = d.discount_id AND dp.grouping = :grouping)\n LEFT JOIN {uc_discounts_terms} dt ON (dt.discount_id = d.discount_id AND dt.grouping = :grouping)\n LEFT JOIN {uc_discounts_skus} ds ON (ds.discount_id = d.discount_id AND ds.grouping = :grouping)\n LEFT JOIN {uc_discounts_classes} dcl ON (dcl.discount_id = d.discount_id AND dcl.grouping = :grouping)\n LEFT JOIN {uc_discounts_authors} dau ON (dau.discount_id = d.discount_id AND dau.grouping = :grouping)\n WHERE (d.has_activation = 0 AND d.activates_on < :request_time)\n AND (d.has_expiration = 0 OR d.expiration > :request_time)\n AND (d.is_active = :discount_active_constant)";
$valid_discounts_args = array(
':grouping' => $grouping,
':request_time' => REQUEST_TIME,
':discount_active_constant' => UC_DISCOUNTS_DISCOUNT_ACTIVE,
);
foreach ($where_clauses as $clause) {
$valid_discounts_sql .= ' AND (' . $clause['sql'] . ')';
$valid_discounts_args += $clause['args'];
}
$valid_discounts_sql .= " ORDER BY d.weight";
$valid_discounts_result = db_query($valid_discounts_sql, $valid_discounts_args);
// Checks if order qualifies for each discount then applies discount.
// @todo Functionality should be separated?
foreach ($valid_discounts_result as $discount) {
foreach (module_implements('uc_discount') as $module) {
$function = $module . '_uc_discount';
$function('load', $discount, $order);
}
// In case the hook modified the discount.
if (!$discount->is_active) {
continue;
}
// Get code for discount if one exists.
// @todo Why is this overwritting the discount code? This validation has
// already happened at least once in above code.
$discount->code = NULL;
if (!empty($order->uc_discounts_codes)) {
$code = db_select('uc_discounts_codes', 'dc')
->fields('dc', array(
'code',
))
->condition('discount_id', $discount->discount_id)
->condition('code', $order->uc_discounts_codes, 'IN')
->execute()
->fetchField();
if (!empty($code)) {
$discount->code = $code;
}
}
// The query handled valid codes and expiration, this block must:
// - Check max uses (if applicable).
// - Check if discount is being combined and can be combined.
// - Check if order qualifies (type, requires_single_product_to_qualify,
// required_products, can_be_combined_with_other_discounts).
// - Determine number of times to apply discount.
// If this discount has a max uses amount, check max uses.
if ($discount->max_uses > 0) {
$row = db_query("SELECT COUNT(*) as uses_count FROM {uc_discounts_uses} WHERE discount_id = :discount_id", array(
':discount_id' => $discount->discount_id,
))
->fetchArray();
if ($row["uses_count"] >= $discount->max_uses) {
// If this is a coded discount add error message.
if (!empty($discount->code)) {
$messages['warnings'][] = t('The discount for code "@code" has reached its maximum number of uses.', array(
"@code" => $discount->code,
));
}
continue;
}
$discount->uses_count = $row["uses_count"];
}
// If this discount has a max uses per user amount, check max uses per user.
if ($discount->max_uses_per_user > 0) {
$row = db_query("SELECT COUNT(*) as user_uses_count FROM {uc_discounts_uses} WHERE discount_id = :discount_id AND user_id = :user_id", array(
':discount_id' => $discount->discount_id,
':user_id' => $order->uid,
))
->fetchArray();
if ($row["user_uses_count"] >= $discount->max_uses_per_user) {
// If this is a coded discount add warning message.
if (!empty($discount->code)) {
$messages['warnings'][] = t('The discount for code %code has reached its maximum number of uses.', array(
"%code" => $discount->code,
));
}
continue;
}
$discount->user_uses_count = $row["user_uses_count"];
}
// If code exists and this discount has a max uses per code amount, check
// max uses per code.
if (!is_null($discount->code) && $discount->max_uses_per_code > 0) {
$row = db_query("SELECT COUNT(*) as code_uses_count FROM {uc_discounts_uses} WHERE discount_id = :discount_id AND code = :code", array(
':discount_id' => $discount->discount_id,
':code' => $discount->code,
))
->fetchArray();
if ($row['code_uses_count'] >= $discount->max_uses_per_code) {
// Add warning message.
$messages['warnings'][] = t('The discount code %code has reached its max number of uses.', array(
'%code' => $discount->code,
));
continue;
}
$discount->code_uses_count = $row["code_uses_count"];
}
// If there are applied discounts, check if discount is being combined and
// can be combined.
if (count($discounts) > 0) {
if (!$discount->can_be_combined_with_other_discounts) {
// If this is a coded discount add error message.
if (!empty($discount->code)) {
$messages['warnings'][] = t('The discount code %code cannot be combined with other discounts.', array(
'%code' => $discount->code,
));
}
continue;
}
// Check if the first discount can't be combined.
if (!$discounts[0]->can_be_combined_with_other_discounts) {
// Add warning message only if both discounts have codes.
if (!empty($discounts[0]->code) && !empty($discount->code)) {
$messages['warnings'][] = t('The discount code %code cannot be combined with other discounts.', array(
'%code' => $discounts[0]->code,
));
}
continue;
}
}
// Check if order qualifies for this discount (check type,
// requires_single_product_to_qualify, required_products).
// Get product IDs for determining discount application.
$discount_product_ids = uc_discounts_get_product_ids_for_discount($discount);
if (in_array(UC_DISCOUNTS_OPTION_ALL_PRODUCTS, $discount_product_ids)) {
$discount_product_ids = $order_product_ids;
}
// Get product IDs for determining discount qualification.
$required_product_ids = uc_discounts_get_product_ids_for_discount($discount, UC_DISCOUNTS_GROUPING_QUALIFICATION);
$required_ids_in_order = array_intersect($required_product_ids, $order_product_ids);
if (!empty($discount->code) && !empty($required_product_ids) && empty($required_ids_in_order)) {
$messages['warnings'][] = t('The discount code %code requires a product that is not in your cart.', array(
'%code' => $discount->code,
));
continue;
}
if ($discount->use_only_discounted_products_to_qualify) {
$qualification_product_ids = $discount_product_ids;
}
elseif (!empty($required_product_ids)) {
$qualification_product_ids = $required_product_ids;
}
else {
$qualification_product_ids = $order_product_ids;
}
// Determine total qualifying amount of order and save in
// order_qualifying_amount.
$order_qualifying_amount = 0;
switch ($discount->qualifying_type) {
case UC_DISCOUNTS_QUALIFYING_TYPE_MINIMUM_PRICE:
// Determine the total subtotal of discount's products.
foreach ($qualification_product_ids as $product_id) {
if (isset($order_product_id_subtotal_map[$product_id])) {
if ($discount->requires_single_product_to_qualify) {
if ($order_product_id_subtotal_map[$product_id] >= $discount->qualifying_amount) {
// In this case, $order_qualifying amount should be the sum of
// prices of products that both qualify and meet the minimum
// qualification amount based on their individual price.
$order_qualifying_amount += $order_product_id_subtotal_map[$product_id];
}
}
else {
$order_qualifying_amount += $order_product_id_subtotal_map[$product_id];
}
}
}
// Subtract already discounted amount.
$order_qualifying_amount -= $total_discount_amount;
break;
case UC_DISCOUNTS_QUALIFYING_TYPE_MINIMUM_QUANTITY:
// Determine the total quantity of discount's products.
foreach ($qualification_product_ids as $product_id) {
if (isset($order_product_id_quantity_map[$product_id])) {
if ($discount->requires_single_product_to_qualify) {
if ($order_product_id_quantity_map[$product_id] >= $discount->qualifying_amount) {
// In this case, $order_qualifying amount should be the sum of
// products that both qualify and meet the minimum qualification
// amount based on their quantity.
$order_qualifying_amount += $order_product_id_quantity_map[$product_id];
}
}
else {
$order_qualifying_amount += $order_product_id_quantity_map[$product_id];
}
}
}
break;
}
// If order does not qualify for this discount.
if ($order_qualifying_amount < $discount->qualifying_amount) {
// If this is a coded discount, add warning message.
if (!empty($discount->code)) {
switch ($discount->qualifying_type) {
case UC_DISCOUNTS_QUALIFYING_TYPE_MINIMUM_PRICE:
$qualifying_amount = uc_currency_format($discount->qualifying_amount);
$messages['warnings'][] = t('The discount code %code requires a minimum amount of @qualifying_amount to qualify.', array(
'%code' => $discount->code,
'@qualifying_amount' => $qualifying_amount,
));
break;
case UC_DISCOUNTS_QUALIFYING_TYPE_MINIMUM_QUANTITY:
$messages['warnings'][] = t('The discount for code %code requires a minimum quantity of @qualifying_amount to qualify.', array(
'%code' => $discount->code,
'@qualifying_amount' => $discount->qualifying_amount,
));
break;
}
}
continue;
}
// If this discount has a maximum qualifying amount and order exceeds it.
if ($discount->has_qualifying_amount_max && $order_qualifying_amount > $discount->qualifying_amount_max) {
// If this is a coded discount, add error message.
if (!empty($discount->code)) {
$qualifying_amount_max = uc_currency_format($discount->qualifying_amount_max);
switch ($discount->qualifying_type) {
case UC_DISCOUNTS_QUALIFYING_TYPE_MINIMUM_PRICE:
$messages['warnings'][] = t('The discount for code %code cannot exceed the price of @qualifying_amount_max to qualify.', array(
'%code' => $discount->code,
'@qualifying_amount_max' => $qualifying_amount_max,
));
break;
case UC_DISCOUNTS_QUALIFYING_TYPE_MINIMUM_QUANTITY:
$messages['warnings'][] = t('The discount for code %code cannot exceed the quantity of @qualifying_amount_max to qualify.', array(
'%code' => $discount->code,
'@qualifying_amount_max' => $discount->qualifying_amount_max,
));
break;
}
}
continue;
}
// Get product IDs in order that are in discount.
$order_and_discount_product_ids = array_intersect($discount_product_ids, $order_product_ids);
// Create array of product objects in cart to which this discount gets
// applied.
$order_and_discount_products = array();
foreach ($order_and_discount_product_ids as $product_id) {
if (array_key_exists($product_id, $order_product_id_product_array_map)) {
$order_and_discount_products = array_merge($order_and_discount_products, $order_product_id_product_array_map[$product_id]);
}
}
// Amount of products to which discounts get applied.
$discount_products_amount = 0;
// Quantity of products to which discounts get applied.
$discount_products_qty = 0;
foreach ($order_and_discount_products as $product) {
$discount_products_qty += $product->qty;
$discount_products_amount += $product->qty * $product->price;
}
// Determine number of times to apply discount. By default once for every
// qualifying product.
$discount->times_applied = $discount_products_qty;
// See if it should be limited based on number of required products in cart.
if ($discount->limit_max_times_applied && !empty($required_product_ids)) {
$times = 0;
foreach ($required_product_ids as $id) {
if (isset($order_product_id_quantity_map[$id])) {
$times += $order_product_id_quantity_map[$id];
}
}
$discount->times_applied = min($discount->times_applied, $times);
}
// See if we need to limit the number of applications with a hard cap.
if ($discount->max_times_applied != 0) {
$discount->times_applied = min($discount->times_applied, $discount->max_times_applied);
}
switch ($discount->discount_type) {
case UC_DISCOUNTS_DISCOUNT_TYPE_FREE_ITEMS:
// The variable discount_amount is the monitary amount of discount.
$discount_amount = 0;
// The variable free_items_remaining is the [max] number of free items
// for the order.
$free_items_remaining = $discount->discount_amount * $discount->times_applied;
// Loop until all free items have been applied or there are no more
// products to discount (discount cheapest first).
while ($free_items_remaining > 0) {
// Determine cheapest remaining qualifying item.
$cheapest_product = NULL;
$cheapest_product_key = NULL;
foreach ($order_and_discount_products as $key => $product) {
// If this product has been fully discounted, continue.
if ($product->uc_discounts_is_fully_discounted) {
continue;
}
// If no current cheapest product exists, use this product.
if (is_null($cheapest_product)) {
$cheapest_product = $product;
$cheapest_product_key = $key;
}
else {
// If this product is cheaper than the current cheapest product,
// use this product instead.
if ($product->price < $cheapest_product->price) {
$cheapest_product = $product;
$cheapest_product_key = $key;
}
}
}
// If no cheapest product could be found, there are no more products
// to discount so break.
if (is_null($cheapest_product)) {
break;
}
// Discount up to the lesser of cheapest product quantity and
// free_items_remaining.
$discount_count = min($cheapest_product->qty, $free_items_remaining);
// Add current discount amount to running total.
$discount_amount += $discount_count * $cheapest_product->price;
// Mark item fully discounted.
$order_and_discount_products[$cheapest_product_key]->uc_discounts_is_fully_discounted = TRUE;
$free_items_remaining -= $discount_count;
}
$discount->amount = $discount_amount;
break;
case UC_DISCOUNTS_DISCOUNT_TYPE_PERCENTAGE_OFF_PER_QUALIFYING_ITEM:
$discount->amount = $discount_products_amount / $discount_products_qty * $discount->discount_amount * $discount->times_applied;
break;
case UC_DISCOUNTS_DISCOUNT_TYPE_PERCENTAGE_OFF:
// This is so complicated because we need to ensure only qualifying
// products get discounted and no product is discounted more than 100%.
// Always apply once since it applies to the whole order.
$discount->times_applied = 1;
// If this discount uses all products and previous discount is:
// - same weight as this discount
// - percentage off
// - products of discounts must match
// Then discount using same subtotal as last discount.
if (count($discounts) > 0) {
$last_discount = $discounts[count($discounts) - 1];
if ($last_discount->weight == $discount->weight && $last_discount->discount_type == UC_DISCOUNTS_DISCOUNT_TYPE_PERCENTAGE_OFF) {
// Last discount's and this discount's products must match exactly.
$are_equal = TRUE;
$last_discount_product_ids = uc_discounts_get_product_ids_for_discount($last_discount);
$this_discount_product_ids = uc_discounts_get_product_ids_for_discount($discount);
// If both contain "all products" they are equal.
if (in_array(UC_DISCOUNTS_OPTION_ALL_PRODUCTS, $last_discount_product_ids) && in_array(UC_DISCOUNTS_OPTION_ALL_PRODUCTS, $this_discount_product_ids)) {
$are_equal = TRUE;
}
else {
foreach ($this_discount_product_ids as $product_id) {
if (!in_array($product_id, $last_discount_product_ids)) {
$are_equal = FALSE;
break;
}
}
if ($are_equal) {
foreach ($last_discount_product_ids as $product_id) {
if (!in_array($product_id, $this_discount_product_ids)) {
$are_equal = FALSE;
break;
}
}
}
}
if ($are_equal) {
$local_order_subtotal = $last_discount->amount / $last_discount->discount_amount;
$discount->amount = $local_order_subtotal * $discount->discount_amount;
break;
}
}
}
// Start patch from lutegrass.
// This fixes the problem where a percent discount does not apply to all
// products. It doesn't fix the problem where the products being
// discounted have already been discounted in full, or the case where
// the cart consists only of the products included in this discount.
// Get qualifying products -- ignore "all products" selection.
$discount_product_ids = uc_discounts_get_product_ids_for_discount($discount, UC_DISCOUNTS_GROUPING_APPLICATION, TRUE);
// Do we have any products.
if (count($discount_product_ids) > 0) {
$discounted_products_amount = 0;
foreach ($order_and_discount_products as $product) {
$discounted_products_amount += $product->price * $product->qty;
}
$discount->amount = $discounted_products_amount * $discount->discount_amount;
// Discount the subtotal so far.
}
else {
$discount->amount = max($order_subtotal - $total_discount_amount, 0) * $discount->discount_amount;
}
// End patch from lutegrass.
break;
case UC_DISCOUNTS_DISCOUNT_TYPE_FIXED_AMOUNT_OFF:
// Always apply once since it applies to the whole order.
$discount->times_applied = 1;
$discount->amount = $discount->discount_amount;
break;
case UC_DISCOUNTS_DISCOUNT_TYPE_FIXED_AMOUNT_OFF_PER_QUALIFYING_ITEM:
$discount->amount = $discount->discount_amount * $discount->times_applied;
break;
}
if (!is_null($messages)) {
$options = array(
'@short_description' => $discount->short_description,
'@code' => $discount->code,
'@times_applied' => $discount->times_applied,
'@discount_amount' => uc_currency_format($discount->amount),
);
if (!is_null($discount->code)) {
// @todo - Improve the following success messages.
if (empty($discount->amount)) {
$messages['success'][] = t("The discount, '@short_description', with code '@code' was applied.", $options);
}
elseif ($discount->times_applied == 1) {
$messages['success'][] = t("The discount, '@short_description', with code '@code' was applied for a discount of @discount_amount.", $options);
}
else {
$messages['success'][] = t("The discount, '@short_description', with code '@code' was applied @times_applied times for a discount of @discount_amount.", $options);
}
}
else {
if (empty($discount->amount)) {
$messages['success'][] = t("The discount, '@short_description' was applied.", $options);
}
elseif ($discount->times_applied == 1) {
$messages['success'][] = t("The discount, '@short_description', was applied for a discount of @discount_amount.", $options);
}
else {
$messages['success'][] = t("The discount, '@short_description', was applied @times_applied times for a discount of @discount_amount.", $options);
}
}
}
// Round the discount to two places.
$discount->amount = round($discount->amount, 2);
// Add this discount's amount to running total.
$total_discount_amount += $discount->amount;
// Add this discount to list of discounts applied to order.
$discounts[] = $discount;
}
// If no discount array was filled in, means that the discount was not found
// in the database.
if (empty($discounts) && !empty($order->uc_discounts_codes)) {
// @todo - Fix text to using translation plural.
$messages['warnings'][] = t('Coupon code %code does not exist or is not valid.', array(
'%code' => implode(', ', $order->uc_discounts_codes),
));
}
// Save to static variable.
$order_discounts[$order->order_id] = array(
'discounts' => $discounts,
'messages' => $messages,
'total_discount_amount' => $total_discount_amount,
);
return $order_discounts[$order->order_id];
}