%d)
AND d.is_active = 1
AND d.discount_type = %d
ORDER BY weight", time(), time(), DISCOUNT_TYPE_FREE_ITEMS);
while ($discount = db_fetch_object($result)) {
if (in_array($product->nid, get_product_ids_for_discount_object($discount, DISCOUNT_FILTER_GROUPING_QUALIFICATION, TRUE))) {
$new_product_ids = get_product_ids_for_discount_object($discount, DISCOUNT_FILTER_GROUPING_APPLICATION, TRUE);
break;
}
}
if (!empty($new_product_ids)) {
foreach ($new_product_ids as $id) {
$potential_product = node_load($id);
// How many of the item are in the cart? We're only interested in node IDs right now.
$items = uc_cart_get_contents();
$qty = 0;
$source_qty = 0;
foreach ($items as $item) {
if ($item->nid == $potential_product->nid) {
$qty += $item->qty;
}
if ($item->nid == $product->nid) {
$source_qty += $item->qty;
}
}
$target_qty = $discount->discount_amount;
$qualifying_amount = $discount->qualifying_amount;
$times_applied = floor($qty / $target_qty);
$times_to_apply = ((($target_qty / $qualifying_amount) * $added_qty) / $target_qty);
// Make sure max_times_applied is respected
if ($times_applied < $discount->max_times_applied || $discount->max_times_applied == 0) {
// Calculate how many there should be and subtract what we already have
$to_add = ($target_qty * ($discount->max_times_applied ? min($discount->max_times_applied - $times_applied, $times_to_apply) : $times_to_apply));
}
// Don't add items immediately so that uc_cart_get_contents() will behave predictably.
if ($to_add > 0) {
$items_to_add[] = array('nid' => $potential_product->nid, 'qty' => $to_add, 'data' => array());
}
}
}
foreach ($items_to_add as $p) {
uc_cart_add_item($p['nid'], $p['qty'], $p['data'] + module_invoke_all('add_to_cart_data', $p), NULL, FALSE, FALSE, FALSE);
}
}
}
/**
* Implementation of hook_perm().
*/
function uc_discounts_perm() {
return array('configure discounts');
}
/**
* Implementation of hook_menu().
*/
function uc_discounts_menu() {
$items = array();
$items['admin/store/uc_discounts'] = array(
'title' => 'Discounts',
'description' => 'Add and review discounts.',
'page callback' => 'uc_discounts_admin_list',
'access arguments' => array('configure discounts'),
'type' => MENU_NORMAL_ITEM,
'file' => 'uc_discounts.admin.inc',
);
$items['admin/store/uc_discounts/list'] = array(
'title' => 'List',
'description' => 'View list of discounts.',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => 0,
);
$items['admin/store/uc_discounts/add'] = array(
'title' => 'Add discount',
'page callback' => 'drupal_get_form',
'page arguments' => array('uc_discounts_form'),
'access arguments' => array('configure discounts'),
'type' => MENU_LOCAL_TASK,
'weight' => 1,
'file' => 'uc_discounts.admin.inc',
);
$items['admin/store/uc_discounts/edit/%'] = array(
'title' => 'Edit discount rule',
'page callback' => 'drupal_get_form',
'page arguments' => array('uc_discounts_form', 4),
'access arguments' => array('configure discounts'),
'type' => MENU_CALLBACK,
'file' => 'uc_discounts.admin.inc',
);
$items['admin/store/uc_discounts/copy/%'] = array(
'page callback' => 'uc_discounts_copy',
'page arguments' => array(4),
'access arguments' => array('configure discounts'),
'type' => MENU_CALLBACK,
'file' => 'uc_discounts.admin.inc',
);
$items['admin/store/uc_discounts/delete/%'] = array(
'title' => 'Delete discount rule',
'page callback' => 'drupal_get_form',
'page arguments' => array('uc_discounts_delete', 4),
'access arguments' => array('configure discounts'),
'type' => MENU_CALLBACK,
'file' => 'uc_discounts.admin.inc',
);
$items['admin/store/uc_discounts/generate_codes/%'] = array(
'title' => 'Generate codes',
'page callback' => 'drupal_get_form',
'page arguments' => array('uc_discounts_generate_codes_form', 4),
'access arguments' => array('configure discounts'),
'type' => MENU_CALLBACK,
'file' => 'uc_discounts.admin.inc',
'weight' => 2,
);
$items['cart/checkout/uc_discounts/calculate'] = array(
'page callback' => 'uc_discounts_js_calculate',
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
);
$items['admin/reports/uc_discounts/all'] = array(
'title' => 'Ubercart discounts usage data',
'description' => 'View usage data for each Ubercart discount.',
'page callback' => 'uc_discounts_report',
'access arguments' => array('access site reports'),
'file' => 'uc_discounts.admin.inc',
);
$items['admin/reports/uc_discounts/all/download'] = array(
'title' => 'Download Ubercart discounts usage data',
'description' => 'Download usage data for each Ubercart discount.',
'page callback' => 'uc_discounts_report',
'access arguments' => array('access site reports'),
'file' => 'uc_discounts.admin.inc',
'type' => MENU_CALLBACK,
);
$items['admin/reports/uc_discounts/discount'] = array(
'title' => 'Ubercart discount usage data',
'page callback' => 'uc_discounts_report_for_discount',
'access arguments' => array('access site reports'),
'type' => MENU_CALLBACK,
'file' => 'uc_discounts.admin.inc',
);
return $items;
}
/**
* Implementation of hook_order().
*
* Manages order->uc_discounts_codes (array of code) and order->discounts (array of uses)
*/
function uc_discounts_order($op, &$arg1, $arg2) {
switch ($op) {
case 'load':
//Get order's codes from database
$arg1->uc_discounts_codes = uc_discounts_get_codes_for_order($arg1->order_id);
break;
case 'save':
//If discount line items need updating
if ($arg1->uc_discounts_line_items_need_updating) {
//Delete existing order codes
uc_discounts_order_codes_delete($arg1->order_id);
//Save order's codes as string
$codes_string = uc_discounts_codes_to_str($arg1->uc_discounts_codes);
$new_discount_order_code = array('order_id' => $arg1->order_id, 'codes' => $codes_string);
drupal_write_record('uc_discounts_order_codes', $new_discount_order_code);
//Get order line items
if (is_array($arg1->line_items)) {
$existing_line_items = $arg1->line_items;
}
else {
$existing_line_items = uc_order_load_line_items($arg1, TRUE);
}
// Use new_order_line_items to populate $arg1->line_items by:
// Storing all non-discounts line items
// Storing new discount line items
$new_order_line_items = array();
//Delete existing discount line items
foreach ($existing_line_items as $line_item) {
// Allow LINE_ITEM_KEY_NAME suffixes SCUK
if (strpos($line_item["type"], LINE_ITEM_KEY_NAME) !== false) {
uc_order_delete_line_item($line_item["line_item_id"]);
}
//Otherwise store non-discount line item
else {
$new_order_line_items[] = $line_item;
}
}
//Add discount line items
foreach ($arg1->uc_discounts_line_items as $line_item) {
if ($line_item['amount'] != 0) {
uc_order_line_item_add($arg1->order_id, $line_item['type'], $line_item['title'], $line_item['amount'], $line_item['weight'], $line_item['data']);
$new_order_line_items[] = $line_item;
}
}
//Update order line items (see new_order_line_items notes above)
$arg1->line_items = $new_order_line_items;
//Force tax recalculation (currently unused but may be required if line item weights change)
//module_invoke("uc_taxes", "order", "save", $arg1, "");
$arg1->uc_discounts_line_items_need_updating = FALSE;
}
break;
case 'update':
//If status changes to "cancelled", delete order uses
if ($arg2 == "cancelled") {
uc_discounts_uses_delete_for_order($arg1->order_id);
}
break;
// Ensure stored discounts are accurate (recalculate and match new amounts against stored amounts)
case 'submit':
$result = uc_discounts_apply($arg1, FALSE);
if (!$result['success']) {
return array(array('pass' => FALSE, 'message' => $result['message']));
}
break;
case 'delete':
// Delete existing order codes
uc_discounts_order_codes_delete($arg1->order_id);
//TO DO: determine if uses should be deleted or put global setting in for user to decide
break;
}
}
/**
* Implementation of hook_uc_checkout_complete().
* Upon successful completion of checkout, record usage
* Note: $order->uc_discounts_codes is set by the checkout pane
*/
function uc_discounts_uc_checkout_complete($order, $account) {
uc_discounts_uses_save_for_order($order);
// Don't allow user to re-use codes on another order without re-entering them
unset($_SESSION['uc_discounts_codes']);
}
/**
* Implementation of hook_add_to_cart()
*/
function uc_discounts_add_to_cart($nid, $qty, $data) {
// @see hook_init()
$_SESSION['uc_discounts_after_add_to_cart'] = array('nid' => $nid, 'qty' => $qty);
}
/**
* Implementation of Ubercart's hook_line_item().
*
* Displays all discounts as a single line item
* If uc_vat and uc_taxes modules exist and enabled: display a discount line item per VAT type SCUK
*
* @see hook_line_item()
*/
function uc_discounts_line_item() {
// If uc_vat module exists and enabled, add a line item per VAT type
if (module_exists('uc_vat') && module_exists('uc_taxes')) {
$taxes = uc_taxes_rate_load();
foreach ($taxes as $tax) {
$line_items[] = array(
"id" => LINE_ITEM_KEY_NAME . $tax->id,
"title" => t("Discount") . ' ' . check_plain($tax->name),
"weight" => LINE_ITEM_WEIGHT,
"stored" => TRUE,
// Added to total
"calculated" => TRUE,
"display_only" => FALSE,
);
}
}
else {
// If not, single line item
$line_items[] = array(
"id" => LINE_ITEM_KEY_NAME,
"title" => t("Discount"),
"weight" => LINE_ITEM_WEIGHT,
"stored" => TRUE,
// Added to total
"calculated" => TRUE,
"display_only" => FALSE,
);
}
return $line_items;
}
/**
* Implementation of hook_order_pane
*/
function uc_discounts_order_pane() {
$panes[] = array(
'id' => 'uc_discounts',
'callback' => 'uc_discounts_order_pane_callback',
'title' => t('Discount codes'),
'weight' => 8,
'show' => array('edit'),
);
return $panes;
}
/**
* Callback from hook_order_pane
*/
function uc_discounts_order_pane_callback($op, $arg1) {
switch ($op) {
case 'edit-form':
$form['uc-discounts']['uc-discounts-codes'] = array(
'#type' => 'textarea',
'#rows' => 3,
);
$form['uc-discounts']['uc-discounts-button'] = array(
'#type' => 'submit',
'#value' => t('Apply discounts'),
);
return $form;
case 'edit-theme':
$form_rendered = drupal_render($arg1['uc-discounts']);
return $form_rendered;
case 'edit-ops':
return array(t('Apply discounts'));
case t('Apply discounts'):
if ($order = uc_order_load($arg1['order_id'])) {
$order->uc_discounts_codes = array_merge($order->uc_discounts_codes, uc_discounts_codes_to_array($arg1['uc-discounts-codes']));
$result = uc_discounts_apply($order, TRUE, FALSE);
drupal_set_message($result['message'], ($result['success'] ? 'notice' : 'error'));
/*
$has_code_errors = FALSE;
$errors = array();
$warnings = array();
$discounts = get_discounts_for_order($order, $errors, $warnings);
foreach ($errors as $error) {
drupal_set_message($error, 'error');
}
foreach ($warnings as $warning) {
drupal_set_message($warning, 'warning');
}
if (empty($errors)) {
if (empty($discounts)) {
drupal_set_message(t('The code(s) did not yield a discount for this order.'), 'warning');
}
else {
add_discount_line_items_to_order($order, $discounts);
$order->uc_discounts_line_items_need_updating = TRUE;
uc_discounts_order('save', $order, NULL);
uc_discounts_uses_save_for_order($order);
drupal_set_message(t('Discount code(s) applied to order.'));
}
}
*/
}
break;
}
}
/**
* Returns the price for a product including Tax, if using uc_vat module
*
* @param object $item
*/
function get_uc_price($item, $revision = 'altered')
{
$price_info = array(
'price' => $item->price,
'qty' => $item->qty,
);
$context = array(
'type' => 'cart_item',
'revision' => $revision,
'subject' => array(
'cart_item' => $item,
'node' => node_load($item->nid),
),
);
return uc_price($price_info, $context);
}
/**
* Implementation of hook_cart_pane().
*
* @see hook_cart_pane()
*/
function uc_discounts_cart_pane($items) {
$panes[] = array(
'id' => 'uc_discounts_pane',
'title' => t('Discounts'),
'enabled' => TRUE,
'weight' => 1,
'body' => !is_null($items) ? uc_discounts_cart_pane_output($items) : '',
);
return $panes;
}
/**
* Adds the discounts to the cart page as line items via javascript
*/
function uc_discounts_cart_pane_output($items) {
global $user;
//Create phony order object to call to get_discounts_for_order
$order = new stdClass();
$order->uid = $user->uid;
$order->products = $items;
$errors = array();
$warnings = array();
$messages = array();
$discounts = get_discounts_for_order($order, $errors, $warnings, $messages);
//If there are no discounts, do not alter cart
if (count($discounts) == 0) {
return '';
}
//Calculate subtotal with discounts
$subtotal = 0;
if (is_array($items)) {
// Used in /cart
foreach ($items as $item) $subtotal += get_uc_price($item);
}
$total_discount_amount = 0;
if (is_array($discounts)) {
foreach ($discounts as $discount) {
$total_discount_amount += $discount->total;
}
}
$subtotal_including_discounts = $subtotal - $total_discount_amount;
//Add total discount message
$messages[] = "". t("Total discount") .": ". uc_currency_format($total_discount_amount);
//Add new subtotal message
$messages[] = "". t("Subtotal including discounts") .": ". uc_currency_format($subtotal_including_discounts);
//Start row index at item count + 2 (1 for subtotal row in cart form, 1 more for our first row)
$i = count($items) + 2;
//Create table to hold discount messages
$body = "
";
foreach ($messages as $message) {
$evenOddClass = (($i % 2) == 0) ? "even" : "odd";
$body .= sprintf("| ", $evenOddClass, "uc-discounts-cart-pane-table-cell") . $message ." |
";
$i += 1;
}
//Close table
$body .= "
";
//Write table using javascript between items and cart form buttons
drupal_add_js(sprintf('$(document).ready(function() { $("#cart-form-buttons").before("%s"); });', $body), 'inline');
}
/**
* Implementation of hook_checkout_pane().
*
* @see hook_checkout_pane()
*/
function uc_discounts_checkout_pane() {
$panes[] = array(
"id" => "uc_discounts",
"callback" => "uc_checkout_pane_discounts",
'process' => TRUE,
"title" => t("Enter discount codes"),
"weight" => 5,
);
return $panes;
}
/**
* Implementation of hook_form_FORM_ID_alter()
* Ensures that javascript for adding discounts is added to checkout,
* regardless of whether discounts pane is used.
*/
function uc_discounts_form_uc_cart_checkout_form_alter(&$form, $form_state) {
drupal_add_js(array(
'uc_discounts' => array(
'url' => url('cart/checkout/uc_discounts/calculate'),
'line_item_key_name' => LINE_ITEM_KEY_NAME,
'line_item_weight' => LINE_ITEM_WEIGHT,
'total_discount_text' => t('Total discount'),
'calculate_discount_response_line_items_key' => CALCULATE_DISCOUNT_RESPONSE_LINE_ITEMS_KEY,
'calculate_discount_response_errors_key' => CALCULATE_DISCOUNT_RESPONSE_ERRORS_KEY,
'calculate_discount_response_messages_key' => CALCULATE_DISCOUNT_RESPONSE_MESSAGES_KEY,
// discount_rates_ids are used to distinguish the different discount line items SCUK
'discount_rates_ids' => DISCOUNT_RATES_IDS_KEY,
'progress_msg' => t('Calculating discounts...'),
'no_codes_entered' => t('Please enter at least one code'),
'no_applicable_discounts' => t('No applicable discounts'),
'err_msg' => t('There were problems determining if any discounts apply. Please try again shortly. If this does not resolve the issue, please call @phone to complete your order.',
array('@phone' => variable_get('uc_store_phone', NULL))
),
'response_parse_err_msg' => t('Unable to parse response text: '),
),
), 'setting');
drupal_add_js('$(document).ready(function(e) { uc_discountsOnLoad(e); });', 'inline');
drupal_add_js('misc/progress.js');
drupal_add_js(drupal_get_path('module', 'uc_discounts') .'/uc_discounts.js');
}
/**
* Discounts checkout pane callback
*
* More information at http://www.ubercart.org/docs/developer/245/checkout
*/
function uc_checkout_pane_discounts($op, &$arg1, $arg2) {
global $user;
switch ($op) {
case "view":
$description = t("Enter discount codes in the box below (one per line).");
//If viewing an existing order, load order's codes
if (!empty($arg1->order_id)) {
$codes_string = uc_discounts_codes_to_str(uc_discounts_get_codes_for_order($arg1->order_id));
}
$contents["uc-discounts-codes"] = array(
"#type" => "textarea",
"#default_value" => $codes_string,
"#rows" => 5,
"#prefix" => "",
"#suffix" => "
",
);
$contents["uc-discounts-placeholder"] = array(
"#type" => "hidden",
"#prefix" => "",
"#suffix" => "
",
);
$contents["uc-discounts-button"] = array(
"#type" => "button",
"#value" => t("Click to calculate discounts"),
);
return array("description" => $description, "contents" => $contents);
case "process":
// Save form values from checkout pane in order ($arg1).
$arg1->uc_discounts_codes = uc_discounts_codes_to_array($arg2['uc-discounts-codes']);
drupal_alter('uc_discounts_codes', $arg1, 'pane_submit');
$has_code_errors = FALSE;
$errors = array();
$warnings = array();
$discounts = get_discounts_for_order($arg1, $errors, $warnings);
// Make sure the recorded discount codes for the order were only the ones that were actually used
// for a discount.
$applied_codes = array();
foreach ($discounts as $discount) {
$applied_codes[] = $discount->code;
}
$arg1->uc_discounts_codes = $applied_codes;
foreach ($errors as $error) {
drupal_set_message($error, "error");
}
foreach ($warnings as $warning) {
drupal_set_message(t('Warning: @warning', array('@warning' => $warning)), 'error');
}
//If there were errors, return FALSE
if (!empty($errors)) {
return FALSE;
}
//Add discount line items to order
add_discount_line_items_to_order($arg1, $discounts);
//Mark order as needing discount line items updated
$arg1->uc_discounts_line_items_need_updating = TRUE;
break;
}
}
/**
* Add discount line items to order
*
* Note: assumes discount objects are the result of a call to get_discounts_for_order()
*/
function add_discount_line_items_to_order(&$order, $discounts) {
//Create line items for discounts and store in order's uc_discounts_line_items field
$line_items = array();
foreach ($discounts as $discount) {
$line_item = array(
// If exists the rate_type, use it SCUK
'type' => empty($discount->rate_type) ? LINE_ITEM_KEY_NAME : LINE_ITEM_KEY_NAME . $discount->rate_type,
'title' => $discount->short_description,
'amount' => -$discount->amount,
'weight' => LINE_ITEM_WEIGHT,
'data' => array('discount_id' => $discount->discount_id),
);
$line_items[] = $line_item;
}
$order->uc_discounts_line_items = $line_items;
}
/**
* AJAX callback for discounts calculation.
*
* Calculate discount for an order in the checkout page.
*/
function uc_discounts_js_calculate() {
global $user;
if (!empty($_SESSION["cart_order"])) {
$order_id = $_SESSION['cart_order'];
$order = uc_order_load($order_id);
//If session order exists, use it
if (is_null($order)) {
print '{}';
exit;
}
}
//Otherwise create phony order
else {
$order = new stdClass();
$order->uid = $user->uid;
$order->products = uc_cart_get_contents();
}
$order->uc_discounts_codes = uc_discounts_codes_to_array($_POST['uc-discounts-codes']);
drupal_alter('uc_discounts_codes', $order, 'js_calculate');
$line_items = array();
$errors = array();
$warnings = array();
$messages = array();
$discounts = get_discounts_for_order($order, $errors, $warnings, $messages);
$i = 0;
// Session vars get used by conditional action
$_SESSION['uc_discounts_codes'] = $order->uc_discounts_codes;
// Hold the rates ids involved in discounts SCUK
$discount_rates_ids = array();
foreach ($discounts as $discount) {
if ($discount->amount != 0) {
$line_item = array();
// Add rate type suffix if exist
$line_item["id"] = LINE_ITEM_KEY_NAME . (empty($discount->rate_type) ? '' : $discount->rate_type);
// If line item id doesn't exists in array, add it'
if (!in_array($line_item["id"],$discount_rates_ids)) {
$discount_rates_ids[] = $line_item["id"];
}
$line_item["type"] = $discount->type;
$line_item["title"] = $discount->title;
$line_item["amount"] = -$discount->amount;
$line_item["weight"] = $discount->weight;
// Add the rate type and rate name if not empty
$line_item["rate_type"] = empty($discount->rate_type) ? 0 : $discount->rate_type;
$line_item["rate_name"] = empty($discount->rate_name) ? "" : $discount->rate_name;
$line_items[] = $line_item;
}
}
if (!empty($warnings)) {
$warnings2 = array();
foreach ($warnings as $warning) {
$warnings2[] = t('Warning: @warning', array('@warning' => $warning));
}
$errors = array_merge($errors, $warnings2);
}
$calculate_discount_response = array(
CALCULATE_DISCOUNT_RESPONSE_LINE_ITEMS_KEY => $line_items,
CALCULATE_DISCOUNT_RESPONSE_ERRORS_KEY => $errors,
CALCULATE_DISCOUNT_RESPONSE_MESSAGES_KEY => $messages,
// Pass the discount rates ids SCUK
DISCOUNT_RATES_IDS_KEY => $discount_rates_ids,
);
drupal_json($calculate_discount_response);
exit;
}
/**
* Implements hook_theme().
*/
function uc_discounts_theme() {
return array(
'uc_discounts_cart_checkout_table' => array(
'arguments' => array('form' => NULL),
),
);
}
/**
* Implementation of hook_checkout_pane_alter()
* Get discounted price to show up on checkout, & review
*/
function uc_discounts_checkout_pane_alter(&$panes) {
foreach ($panes as &$pane) {
if ($pane['id'] == 'cart') {
$pane['callback'] = 'uc_discounts_checkout_pane_content';
}
}
}
function uc_discounts_checkout_pane_content($op) {
switch ($op) {
case 'view':
$contents['cart_review_table'] = array(
'#value' => theme('uc_discounts_cart_checkout_table'),
'#weight' => variable_get('uc_pane_cart_field_cart_weight', 2),
);
return array('contents' => $contents, 'next-button' => FALSE);
case 'review':
$discount_amount = uc_discounts_get_discount_amount_for_order($op);
$items = uc_cart_get_contents();
$output = '';
$context = array(
'revision' => 'themed',
'type' => 'cart_item',
'subject' => array(),
);
$total = 0;
foreach ($items as $item) {
$total += ($item->price * $item->qty);
$desc = check_plain($item->title) . uc_product_get_description($item);
$price_info = array(
'price' => $item->price,
'qty' => $item->qty,
);
$context['subject'] = array(
'cart' => $items,
'cart_item' => $item,
'node' => node_load($item->nid),
);
$output .= '| ' . $item->qty . '× | ' . $desc
. ' | ' . uc_price($price_info, $context) . ' |
';
}
if ($discount_amount > 0) {
$final_price = $total - $discount_amount;
$output .= '| ' . t('Discount') . ': | ' . uc_price($discount_amount, $context) . ' |
';
$output .= '| ' . t('Total') . ': | ' . uc_price($final_price, $context) . ' |
';
}
$output .= '
';
$review[] = $output;
return $review;
}
}
function theme_uc_discounts_cart_checkout_table($show_subtotal = TRUE) {
$subtotal = 0;
$discount_amount = uc_discounts_get_discount_amount_for_order('checkout');
// Set up table header.
$header = array(
array('data' => t('Qty'), 'class' => 'qty'),
array('data' => t('Products'), 'class' => 'products'),
array('data' => t('Price'), 'class' => 'price'),
);
$context = array();
// Set up table rows.
$contents = uc_cart_get_contents();
foreach ($contents as $item) {
$price_info = array(
'price' => $item->price,
'qty' => $item->qty,
);
$context['revision'] = 'altered';
$context['type'] = 'cart_item';
$context['subject'] = array(
'cart' => $contents,
'cart_item' => $item * $discount_amount,
'node' => node_load($item->nid),
);
$total = uc_price($price_info, $context);
$subtotal += $total;
$description = check_plain($item->title) . uc_product_get_description($item);
// Remove node from context to prevent the price from being altered.
$context['revision'] = 'themed-original';
$context['type'] = 'amount';
unset($context['subject']);
$rows[] = array(
array('data' => t('@qty×', array('@qty' => $item->qty)), 'class' => 'qty'),
array('data' => $description, 'class' => 'products'),
array('data' => uc_price($total, $context), 'class' => 'price'),
);
}
if ($discount_amount > 0) {
$rows[] = array(
array('data' => ''),
array('data' => t('Discount:'), 'align' => 'right'),
array('data' => uc_price($discount_amount, $context), 'class' => 'price'),
);
$subtotal = $subtotal - $discount_amount;
}
// Add the subtotal as the final row.
if ($show_subtotal) {
$context = array(
'revision' => 'themed-original',
'type' => 'amount',
);
$rows[] = array(
'data' => array(array('data' => '' . t('Subtotal:') . ' ' . uc_price($subtotal, $context), 'colspan' => 3, 'class' => 'subtotal')),
'class' => 'subtotal',
);
}
return theme('table', $header, $rows, array('class' => 'cart-review'));
}
///////////////////////////////////////////////////////////////////
//Database operations
///////////////////////////////////////////////////////////////////
/**
* Deletes a uc_discounts row and all dependencies.
*/
function uc_discounts_delete_all($discount) {
foreach (module_implements('uc_discount') as $module) {
$function = $module . '_uc_discount';
$function('delete', $discount);
}
db_query("DELETE FROM {uc_discounts_uses} WHERE discount_id=%d", $discount->discount_id);
db_query("DELETE FROM {uc_discounts_products} WHERE discount_id=%d", $discount->discount_id);
db_query("DELETE FROM {uc_discounts_terms} WHERE discount_id=%d", $discount->discount_id);
db_query("DELETE FROM {uc_discounts_skus} WHERE discount_id=%d", $discount->discount_id);
db_query("DELETE FROM {uc_discounts_roles} WHERE discount_id=%d", $discount->discount_id);
db_query("DELETE FROM {uc_discounts_codes} WHERE discount_id=%d", $discount->discount_id);
db_query("DELETE FROM {uc_discounts_authors} WHERE discount_id=%d", $discount->discount_id);
db_query("DELETE FROM {uc_discounts} WHERE discount_id=%d", $discount->discount_id);
}
/**
* Loads a discount
*/
function uc_discounts_load($discount_id) {
$discount = db_fetch_object(db_query("SELECT * FROM {uc_discounts} WHERE discount_id=%d", $discount_id));
foreach (module_implements('uc_discount') as $module) {
$function = $module . '_uc_discount';
$function('load', $discount);
}
return $discount;
}
/**
* Returns codes for discount.
*/
function get_codes_for_discount($discount_id) {
$codes = array();
$result = db_query('SELECT code FROM {uc_discounts_codes} WHERE discount_id = %d', $discount_id);
while ($row = db_fetch_array($result)) {
$codes[] = $row['code'];
}
return $codes;
}
/**
* Deletes all uc_discounts_codes rows for a discount.
*/
function uc_discounts_codes_delete($discount_id) {
$query = "DELETE FROM {uc_discounts_codes} WHERE discount_id=%d";
db_query($query, $discount_id);
}
/**
* Returns product_ids for discount object.
* Note: this function correctly returns all products for term-based discounts.
*
* @param $discount object
* @param $exclude_all_products bool
* @param $grouping const
*
* @return array of product ids (nids)
*/
function get_product_ids_for_discount_object($discount, $grouping = DISCOUNT_FILTER_GROUPING_APPLICATION, $exclude_all_products = FALSE) {
$filter = $grouping == DISCOUNT_FILTER_GROUPING_APPLICATION ? $discount->filter_type : $discount->required_product_type;
switch ($filter) {
case FILTER_TYPE_PRODUCTS:
return get_product_ids_for_discount($discount->discount_id, $grouping, $exclude_all_products);
case FILTER_TYPE_TERMS:
$product_ids = array();
//Get products for terms
$terms = get_term_ids_for_discount($discount->discount_id, $grouping, $exclude_all_products);
$query = 'SELECT DISTINCT p.nid FROM {uc_products} p';
if (!empty($terms)) {
$query .= ' INNER JOIN {term_node} tn ON p.nid=tn.nid
INNER JOIN {uc_discounts_terms} dt ON tn.tid=dt.term_id
WHERE dt.discount_id=%d';
}
$result = db_query($query, $discount->discount_id);
while ($row = db_fetch_object($result)) {
$product_ids[] = $row->nid;
}
return $product_ids;
case FILTER_TYPE_SKUS:
$skus = get_skus_for_discount($discount->discount_id, $grouping, $exclude_all_products);
$query = 'SELECT DISTINCT p.nid FROM {uc_products} p';
if (!empty($skus)) {
$query .= ' INNER JOIN {uc_discounts_skus} ds ON p.model=ds.sku
WHERE ds.discount_id=%d';
}
$result = db_query($query, $discount->discount_id);
while ($row = db_fetch_object($result)) {
$product_ids[] = $row->nid;
}
return $product_ids;
case FILTER_TYPE_CLASS:
$classes = get_classes_for_discount($discount->discount_id, $grouping, $exclude_all_products);
$query = 'SELECT DISTINCT n.nid FROM {node} n';
if (!empty($classes)) {
$query .= ' INNER JOIN {uc_discounts_classes} dcl ON n.type=dcl.class
WHERE dcl.discount_id=%d';
}
$result = db_query($query, $discount->discount_id);
while ($row = db_fetch_object($result)) {
$product_ids[] = $row->nid;
}
return $product_ids;
case FILTER_TYPE_AUTHORS:
$authors = get_author_ids_for_discount($discount->discount_id, $grouping, $exclude_all_products);
$query = 'SELECT DISTINCT n.nid FROM {node} n';
if (!empty($authors)) {
$query .= ' INNER JOIN {uc_discounts_authors} dau ON n.uid=dau.author_id
WHERE dau.discount_id=%d';
}
$result = db_query($query, $discount->discount_id);
while ($row = db_fetch_object($result)) {
$product_ids[] = $row->nid;
}
return $product_ids;
}
return array();
}
/**
* Returns product_ids for discount.
* Note: this function does not check filter_type so a discount with filter_type other than
* FILTER_TYPE_PRODUCTS will return no values.
*
* @param $discount_id
* @param $grouping
* @param $exclude_all_products
*
* @return array of product ids (nids)
*/
function get_product_ids_for_discount($discount_id, $grouping, $exclude_all_products = FALSE) {
$query = "SELECT product_id FROM {uc_discounts_products} WHERE discount_id = %d AND grouping = %d";
$args = array($discount_id, $grouping);
if ($exclude_all_products) {
$query .= ' AND product_id <> %d';
$args[] = ALL_PRODUCTS;
}
$result = db_query($query, $args);
$ids = array();
while ($row = db_fetch_array($result)) {
$ids[] = $row["product_id"];
}
return $ids;
}
/**
* Returns term_ids for discount.
*/
function get_term_ids_for_discount($discount_id, $grouping, $exclude_all_terms = FALSE) {
$query = "SELECT term_id FROM {uc_discounts_terms} WHERE discount_id = %d AND grouping = %d";
$args = array($discount_id, $grouping);
if ($exclude_all_products) {
$query .= ' AND term_id <> %d';
$args[] = ALL_TERMS;
}
$result = db_query($query, $args);
$ids = array();
while ($row = db_fetch_array($result)) {
$ids[] = $row["term_id"];
}
return $ids;
}
/**
* Returns SKUs for discount.
*/
function get_skus_for_discount($discount_id, $grouping, $exclude_all_skus = FALSE) {
$query = "SELECT sku FROM {uc_discounts_skus} WHERE discount_id = %d AND grouping = %d";
$args = array($discount_id, $grouping);
if ($exclude_all_products) {
$query .= ' AND sku <> "%s"';
$args[] = ALL_SKUS;
}
$result = db_query($query, $args);
$ids = array();
while ($row = db_fetch_array($result)) {
$ids[] = $row["sku"];
}
return $ids;
}
/**
* Returns Product Class names for discount.
*/
function get_classes_for_discount($discount_id, $grouping, $exclude_all_classes = FALSE) {
$query = "SELECT class FROM {uc_discounts_classes} WHERE discount_id = %d AND grouping = %d";
$args = array($discount_id, $grouping);
if ($exclude_all_products) {
$query .= ' AND class <> "%s"';
$args[] = ALL_CLASSES;
}
$result = db_query($query, $args);
$ids = array();
while ($row = db_fetch_array($result)) {
$ids[] = $row["class"];
}
return $ids;
}
/**
* Returns author_ids for discount.
*/
function get_author_ids_for_discount($discount_id, $grouping, $exclude_all_authors = FALSE) {
$query = "SELECT author_id FROM {uc_discounts_authors} WHERE discount_id = %d AND grouping = %d";
$args = array($discount_id, $grouping);
if ($exclude_all_products) {
$query .= ' AND author_id <> %d';
$args[] = ALL_AUTHORS;
}
$result = db_query($query, $args);
$ids = array();
while ($row = db_fetch_array($result)) {
$ids[] = $row["author_id"];
}
return $ids;
}
/**
* Returns role_ids for discount.
*/
function get_role_ids_for_discount($discount_id, $exclude_all_roles = FALSE) {
$query = "SELECT role_id FROM {uc_discounts_roles} WHERE discount_id = %d";
$args = array($discount_id);
if ($exclude_all_products) {
$query .= ' AND role_id <> %d';
$args[] = ALL_ROLES;
}
$result = db_query($query, $args);
$ids = array();
while ($row = db_fetch_array($result)) {
$ids[] = $row["role_id"];
}
return $ids;
}
/**
* Deletes all uc_discounts_products rows for a discount.
*/
function uc_discounts_products_delete($discount_id) {
$query = "DELETE FROM {uc_discounts_products} WHERE discount_id=%d";
db_query($query, $discount_id);
}
/**
* Deletes all uc_discounts_terms rows for a discount.
*/
function uc_discounts_terms_delete($discount_id) {
$query = "DELETE FROM {uc_discounts_terms} WHERE discount_id=%d";
db_query($query, $discount_id);
}
/**
* Deletes all uc_discounts_skus rows for a discount.
*/
function uc_discounts_skus_delete($discount_id) {
$query = "DELETE FROM {uc_discounts_skus} WHERE discount_id=%d";
db_query($query, $discount_id);
}
/**
* Deletes all uc_discounts_classes rows for a discount.
*/
function uc_discounts_classes_delete($discount_id) {
$query = "DELETE FROM {uc_discounts_classes} WHERE discount_id=%d";
db_query($query, $discount_id);
}
/**
* Deletes all uc_discounts_authors rows for a discount.
*/
function uc_discounts_authors_delete($discount_id) {
$query = "DELETE FROM {uc_discounts_authors} WHERE discount_id=%d";
db_query($query, $discount_id);
}
/**
* Deletes all uc_discounts_roles rows for a discount.
*/
function uc_discounts_roles_delete($discount_id) {
$query = "DELETE FROM {uc_discounts_roles} WHERE discount_id=%d";
db_query($query, $discount_id);
}
/**
* Returns discounts for order.
* Note: $order->uc_discounts_codes must be set
*
* @param $order Order to get discounts for
* @param $errors Reference to array to add error messages to
* @param $messages Reference to array to add success messages to
*
* @return array of discount objects
*/
function get_discounts_for_order($order, &$errors = NULL, &$warnings = NULL, &$messages = NULL) {
// 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;
//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;
$kits[$kit_id]['product_qty'] += $product->qty;
}
uc_discounts_add_to_existing_map_number_value($order_product_id_subtotal_map, $nid, get_uc_price($product));
uc_discounts_add_to_existing_map_number_value($order_product_id_quantity_map, $nid, $product->qty);
$a = $order_product_id_product_array_map[$nid];
if (!is_array($a)) {
$a = array();
}
$a[] = $product;
$order_product_id_product_array_map[$nid] = $a;
$order_subtotal += get_uc_price($product);
}
if (is_array($kits) && !empty($kits)) {
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 && $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']);
}
}
}
// Populate product NID array with NIDs from the order
$order_product_ids = array_keys($order_product_ids_set);
$temp_product_ids = $order_product_ids;
$temp_product_ids[] = ALL_PRODUCTS;
$product_ids_clause = sprintf("d.filter_type<>%d OR dp.product_id IN(%s)",
FILTER_TYPE_PRODUCTS, join(",", $temp_product_ids)
);
//Create IN string of term TIDs in order
$temp_term_ids = array();
$temp_term_ids[] = ALL_TERMS;
if (is_array($order->products) && !empty($order->products)) {
//Get terms for order's products
$result = db_query("SELECT DISTINCT tid FROM {term_node} WHERE nid IN(%s)", join(",", $order_product_ids));
while ($row = db_fetch_array($result)) {
$temp_term_ids[] = $row["tid"];
$order_term_ids[] = $row["tid"];
}
}
$term_ids_clause = sprintf("d.filter_type<>%d OR dt.term_id IN(%s)",
FILTER_TYPE_TERMS, join(",", $temp_term_ids)
);
//Create IN string of SKUs in order
$temp_skus = array();
$temp_skus[] = "'". db_escape_string(ALL_SKUS) ."'";
if (is_array($order->products) && !empty($order->products)) {
//Get SKUs for order's products
$result = db_query("SELECT DISTINCT model FROM {uc_products} WHERE nid IN(%s)", join(",", $order_product_ids));
while ($row = db_fetch_array($result)) $temp_skus[] = "'". db_escape_string($row["model"]) ."'";
}
$skus_clause = sprintf("d.filter_type<>%d OR ds.sku IN(%s)",
FILTER_TYPE_SKUS, join(",", $temp_skus)
);
//Create IN string of classes in order
$temp_classes = array();
$temp_classes[] = "'". db_escape_string(ALL_CLASSES) ."'";
if (is_array($order->products) && !empty($order->products)) {
//Get classes for order's products
$result = db_query("SELECT DISTINCT type FROM {node} WHERE nid IN(%s)", join(",", $order_product_ids));
while ($row = db_fetch_array($result)) $temp_classes[] = "'". db_escape_string($row["type"]) ."'";
}
$classes_clause = sprintf("d.filter_type<>%d OR dcl.class IN(%s)",
FILTER_TYPE_CLASS, join(",", $temp_classes)
);
//Create IN string of authors in order
$temp_authors = array();
$temp_authors[] = "'". db_escape_string(ALL_AUTHORS) ."'";
if (is_array($order->products) && !empty($order->products)) {
//Get authors for order's products
$result = db_query("SELECT DISTINCT uid FROM {node} WHERE nid IN(%s)", join(",", $order_product_ids));
while ($row = db_fetch_array($result)) $temp_authors[] = "'". db_escape_string($row["uid"]) ."'";
}
$authors_clause = sprintf("d.filter_type<>%d OR dau.author_id IN(%s)",
FILTER_TYPE_AUTHORS, join(",", $temp_authors)
);
//Create codes clause
$escaped_codes_string = NULL;
if (!empty($order->uc_discounts_codes)) {
//Create IN string of product node IDs in order
$escaped_codes = array();
foreach ($order->uc_discounts_codes as $code) {
$escaped_codes[] = "'". db_escape_string($code) ."'";
}
$escaped_codes_string = join(",", $escaped_codes);
$codes_clause = sprintf(" OR d.discount_id IN( SELECT discount_id FROM {uc_discounts_codes} WHERE code IN(%s) )",
$escaped_codes_string
);
}
else {
$codes_clause = "";
}
//Create roles clause
$auth_rid = ($order->uid != 0) ? DRUPAL_AUTHENTICATED_RID : DRUPAL_ANONYMOUS_RID;
$roles_clause = sprintf(" OR d.discount_id IN(SELECT dr.discount_id FROM {uc_discounts_roles} dr LEFT JOIN {users_roles} ur ON (dr.role_id=ur.rid AND ur.uid=%d) WHERE ur.uid IS NOT NULL OR dr.role_id=%d OR dr.role_id=%d)",
$order->uid, ALL_ROLES, $auth_rid
);
$grouping = DISCOUNT_FILTER_GROUPING_APPLICATION;
//Add warnings for expired discounts with codes (if necessary)
if (!empty($order->uc_discounts_codes)) {
$query = sprintf("SELECT DISTINCT d.*, dc.code code FROM {uc_discounts} d
LEFT JOIN {uc_discounts_products} dp ON d.discount_id=dp.discount_id AND dp.grouping = $grouping
LEFT JOIN {uc_discounts_terms} dt ON d.discount_id=dt.discount_id AND dt.grouping = $grouping
LEFT JOIN {uc_discounts_skus} ds ON d.discount_id=ds.discount_id AND ds.grouping = $grouping
LEFT JOIN {uc_discounts_classes} dcl ON d.discount_id=dcl.discount_id AND dcl.grouping = $grouping
LEFT JOIN {uc_discounts_authors} dau ON d.discount_id=dau.discount_id AND dau.grouping = $grouping
LEFT JOIN {uc_discounts_roles} dr ON d.discount_id=dr.discount_id
LEFT JOIN {uc_discounts_codes} dc ON d.discount_id=dc.discount_id
WHERE dc.code IN(%s)
AND (d.has_role_filter=0%s)
AND (%s)
AND (%s)
AND (%s)
AND (%s)
AND (%s)
AND (d.has_expiration<>0 AND d.expiration<=%d)
AND (d.is_active=%d)
ORDER BY weight", $escaped_codes_string, $roles_clause, $product_ids_clause,
$term_ids_clause, $skus_clause, $classes_clause, $authors_clause, time(), IS_ACTIVE
);
$result = db_query($query);
while ($discount = db_fetch_object($result)) {
$warnings[] = t('The discount for code "@code" has expired.',
array("@code" => $discount->code)
);
}
}
$query = sprintf("SELECT DISTINCT d.* FROM {uc_discounts} d
LEFT JOIN {uc_discounts_products} dp ON d.discount_id=dp.discount_id AND dp.grouping = $grouping
LEFT JOIN {uc_discounts_terms} dt ON d.discount_id=dt.discount_id AND dt.grouping = $grouping
LEFT JOIN {uc_discounts_skus} ds ON d.discount_id=ds.discount_id AND ds.grouping = $grouping
LEFT JOIN {uc_discounts_classes} dcl ON d.discount_id=dcl.discount_id AND dcl.grouping = $grouping
LEFT JOIN {uc_discounts_authors} dau ON d.discount_id=dau.discount_id AND dau.grouping = $grouping
WHERE (d.requires_code=0%s)
AND (d.has_role_filter=0%s)
AND (%s)
AND (%s)
AND (%s)
AND (%s)
AND (%s)
AND (d.has_activation=0 OR d.activates_on<%d)
AND (d.has_expiration=0 OR d.expiration>%d)
AND (d.is_active=%d)
ORDER BY weight", $codes_clause, $roles_clause, $product_ids_clause,
$term_ids_clause, $skus_clause, $classes_clause, $authors_clause, time(), time(), IS_ACTIVE
);
$result = db_query($query);
$total_discount_amount = 0;
$discounts = array();
// Appears to check if order qualifies for each discount then applies discount. Functionality should be separated, no?
while ($discount = db_fetch_object($result)) {
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)
$discount->code = NULL;
if (!empty($escaped_codes_string)) {
$query = sprintf("SELECT code FROM {uc_discounts_codes} WHERE code IN(%s) AND discount_id=%d",
$escaped_codes_string, $discount->discount_id
);
$row = db_fetch_array(db_query($query));
if (!empty($row)) {
$discount->code = $row["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_fetch_array(db_query("SELECT COUNT(*) as uses_count FROM {uc_discounts_uses} WHERE discount_id=%d",
$discount->discount_id
));
if ($row["uses_count"] >= $discount->max_uses) {
//If this is a coded discount, add error message
if (!is_null($warnings) && !is_null($discount->code)) {
$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_fetch_array(db_query("SELECT COUNT(*) as user_uses_count FROM {uc_discounts_uses} WHERE discount_id=%d AND user_id=%d",
$discount->discount_id, $order->uid
));
if ($row["user_uses_count"] >= $discount->max_uses_per_user) {
//If this is a coded discount, add warning message
if (!is_null($warnings) && !is_null($discount->code)) {
$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_fetch_array(db_query("SELECT COUNT(*) as code_uses_count FROM {uc_discounts_uses} WHERE discount_id=%d AND code='%s'",
$discount->discount_id, $discount->code
));
if ($row["code_uses_count"] >= $discount->max_uses_per_code) {
//Add warning message
if (!is_null($warnings)) {
$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 (!is_null($warnings) && !is_null($discount->code)) {
$warnings[] = t('The discount for 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) {
//If first discount was a coded discount, add error message (only print warning if both
//discounts have codes)
if (!is_null($warnings) && !empty($discounts[0]->code) && !is_null($discount->code)) {
$warnings[] = t('The discount for 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 = get_product_ids_for_discount_object($discount);
if (in_array(ALL_PRODUCTS, $discount_product_ids)) {
$discount_product_ids = $order_product_ids;
}
// Get product IDs for determining discount qualification
$required_product_ids = get_product_ids_for_discount_object($discount, DISCOUNT_FILTER_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)) {
$warnings[] = t('The discount for 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 (store in order_qualifying_amount)
$order_qualifying_amount = 0;
switch ($discount->qualifying_type) {
case 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] >= $order->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 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 (!is_null($warnings) && !is_null($discount->code)) {
switch ($discount->qualifying_type) {
case QUALIFYING_TYPE_MINIMUM_PRICE:
$qualifying_amount = uc_currency_format($discount->qualifying_amount);
$warnings[] = t('The discount for code "@code" requires a minimum amount of @qualifying_amount to qualify.',
array("@code" => $discount->code, "@qualifying_amount" => $qualifying_amount)
);
break;
case QUALIFYING_TYPE_MINIMUM_QUANTITY:
$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 the 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 (!is_null($warnings) && !is_null($discount->code)) {
$qualifying_amount_max = uc_currency_format($discount->qualifying_amount_max);
switch ($discount->qualifying_type) {
case QUALIFYING_TYPE_MINIMUM_PRICE:
$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 QUALIFYING_TYPE_MINIMUM_QUANTITY:
$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 the 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);
}
// If uc_vat module exists, add the rate type and rate name to discount SCUK
if (module_exists('uc_vat') && module_exists('uc_taxes')) {
uc_discounts_add_discount_rate_id($discount, $order_and_discount_product_ids);
}
switch ($discount->discount_type) {
case 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;
foreach ($order_and_discount_products as $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;
}
else {
//If this product is cheaper than the current cheapest product,
//use this product instead
if ($product->price < $cheapest_product->price) {
$cheapest_product = $product;
}
}
}
//If no cheapest product could be found, there are no more products to
//discount, 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
$cheapest_product->uc_discounts_is_fully_discounted = TRUE;
$free_items_remaining -= $discount_count;
}
$discount->amount = $discount_amount;
break;
case DISCOUNT_TYPE_PERCENTAGE_OFF_PER_QUALIFYING_ITEM:
$discount->amount = ($discount_products_amount / $discount_products_qty) * $discount->discount_amount * $discount->times_applied;
break;
case 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
//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 == DISCOUNT_TYPE_PERCENTAGE_OFF) {
//Last discount's and this discount's products must match exactly
$are_equal = TRUE;
$last_discount_product_ids = get_product_ids_for_discount_object($last_discount);
$this_discount_product_ids = get_product_ids_for_discount_object($discount);
//If both contain "all products" they are equal
if (in_array(ALL_PRODUCTS, $last_discount_product_ids) && in_array(ALL_PRODUCTS, $this_discount_product_ids)) {
$are_equal = TRUE;
}
//Otherwise check arrays for equality
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) {
//($last_discount->amount / $last_discount->discount_amount) == last discount's subtotal
$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
//(but 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 = get_product_ids_for_discount_object($discount, DISCOUNT_FILTER_GROUPING_APPLICATION, TRUE);
// Do we have any products
/* DUNXX
* discount amount for text, that includes VAT
* discount amount, that excludes VAT for table.
* Is uc_price (context revision altered/original) including VAT through hook_uc_price_handler?
*/
if (count($discount_product_ids) > 0) {
$discounted_products_amount = 0;
$discounted_products_total = 0;
foreach ($order_and_discount_products as $product) {
$discounted_products_total += get_uc_price($product);
$discounted_products_amount += get_uc_price($product, 'original');
}
$discount->amount = $discounted_products_amount * $discount->discount_amount;
$discount->total = $discounted_products_total * $discount->discount_amount;
}
else {
$discount->amount = max($order_subtotal - $total_discount_amount, 0) * $discount->discount_amount;
}
//End patch from lutegrass
break;
case 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 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_total" => uc_currency_format($discount->total),
"@time_string" => $time_string,
);
if (!is_null($discount->code)) {
if (empty($discount->amount)) {
$messages[] = t("The discount, '@short_description', with code '@code' was applied.", $options);
}
elseif ($discount->times_applied == 1) {
$messages[] = t("The discount, '@short_description', with code '@code' was applied for a discount of @discount_total", $options);
}
else {
$messages[] = t("The discount, '@short_description', with code '@code' was applied @times_applied times for a discount of @discount_total", $options);
}
}
else {
if (empty($discount->amount)) {
$messages[] = t("The discount, '@short_description' was applied.", $options);
}
elseif ($discount->times_applied == 1) {
$messages[] = t("The discount, '@short_description', was applied for a discount of @discount_total", $options);
}
else {
$messages[] = t("The discount, '@short_description', was applied @times_applied times for a discount of @discount_total", $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 (count($discounts) == 0 && !empty($order->uc_discounts_codes)) {
$warnings[] = t('Coupon does not exist or is not valid.');
}
// end of db fetch while loop
return $discounts;
}
/**
* Helper function that gets the discount amount for the order
* @return int *
*/
function uc_discounts_get_discount_amount_for_order($op) {
global $user;
$order = uc_order_load($_SESSION["cart_order"]);
if (!$order) {
$items = uc_cart_get_contents();
$order = new stdClass();
$order->uid = $user->uid;
$order->products = $items;
}
$errors = array();
$warnings = array();
$messages = array();
$discounts = get_discounts_for_order($order, $errors, $warnings, $messages);
$total_discount_amount = 0;
if (is_array($discounts)) {
foreach ($discounts as $discount) {
if (!$discount->requires_code || $op == 'review') {
$total_discount_amount += $discount->amount;
}
}
}
return $total_discount_amount;
}
/**
* Helper function that gets the codeless discounted price of a product
* @param $product A node product
* @param $return_null Return NULL for the price if there are no discounts. Otherwise it returns the sell_price
*/
function uc_discounts_get_discounted_price_for_product($product, $return_null = TRUE) {
$discounts = get_codeless_discounts_for_product_and_quantity($product, 1);
if (empty($discounts)) {
return $return_null ? NULL : $product->sell_price;
}
$total_discount_amount = 0;
foreach ($discounts as $discount) {
$total_discount_amount += $discount->amount;
}
return $product->sell_price - $total_discount_amount;
}
/**
* Returns all codeless discounts for product.
*/
function get_codeless_discounts_for_product($product, $sort_column = "weight", $is_ascending_sort = TRUE) {
return get_codeless_discounts_for_product_and_quantity($product, NULL, $sort_column, $is_ascending_sort);
}
/**
* Returns all codeless discounts for product when specified quantity is purchased.
*
* @param $product_id Node ID for product
*/
function get_codeless_discounts_for_product_and_quantity($product, $quantity = NULL, $sort_column = "weight", $is_ascending_sort = TRUE) {
if (is_null($product) || !$product) {
return array();
}
//If quantity was specified
if (!is_null($quantity)) {
global $user;
//Create phony order and get discounts for order
$product->price = $product->sell_price;
$product->qty = $quantity;
$order = new stdClass();
$order->uid = $user->uid;
$order->products = array($product);
return get_discounts_for_order($order);
}
//Otherwise make special query
//Get terms for product
$term_ids = array();
$term_ids[] = ALL_TERMS;
$result = db_query("SELECT DISTINCT tid FROM {term_node} WHERE nid=%d", $product->nid);
while ($row = db_fetch_array($result)) $term_ids[] = $row["tid"];
//Get SKUs for product
$skus = array();
$skus[] = "'". db_escape_string(ALL_SKUS) ."'";
$result = db_query("SELECT DISTINCT model FROM {uc_products} WHERE nid=%d", $product->nid);
while ($row = db_fetch_array($result)) $skus[] = "'". db_escape_string($row["model"]) ."'";
//Get classes for product
$classes = array();
$classes[] = "'". db_escape_string(ALL_CLASSES) ."'";
$result = db_query("SELECT DISTINCT type FROM {node} WHERE nid=%d", $product->nid);
while ($row = db_fetch_array($result)) $classes[] = "'". db_escape_string($row["type"]) ."'";
//Get uids for product
$authors = array();
$authors[] = "'". db_escape_string(ALL_AUTHORS) ."'";
$result = db_query("SELECT DISTINCT uid FROM {node} WHERE nid=%d", $product->nid);
while ($row = db_fetch_array($result)) $authors[] = "'". db_escape_string($row["uid"]) ."'";
//Create roles clause
global $user;
$auth_rid = ($user->uid != 0) ? DRUPAL_AUTHENTICATED_RID : DRUPAL_ANONYMOUS_RID;
$roles_clause = sprintf(" OR d.discount_id IN(SELECT dr.discount_id FROM {uc_discounts_roles} dr" . ", {users_roles} ur WHERE (dr.role_id=ur.rid AND ur.uid=%d) OR dr.role_id=%d OR dr.role_id=%d)",
$user->uid, ALL_ROLES, $auth_rid
);
$product_ids = array(ALL_PRODUCTS, $product->nid);
$product_ids_clause = sprintf("d.filter_type<>%d OR dp.product_id IN(%s)",
FILTER_TYPE_PRODUCTS, join(",", $product_ids)
);
$term_ids_clause = sprintf("d.filter_type<>%d OR dt.term_id IN(%s)",
FILTER_TYPE_TERMS, join(",", $term_ids)
);
$skus_clause = sprintf("d.filter_type<>%d OR ds.sku IN(%s)",
FILTER_TYPE_SKUS, join(",", $skus)
);
$classes_clause = sprintf("d.filter_type<>%d OR dcl.class IN(%s)",
FILTER_TYPE_CLASS, join(",", $classes)
);
$authors_clause = sprintf("d.filter_type<>%d OR dau.author_id IN(%s)",
FILTER_TYPE_AUTHORS, join(",", $authors)
);
$sort_order_string = (is_ascending_sort) ? "ASC" : "DESC";
$grouping = DISCOUNT_FILTER_GROUPING_APPLICATION;
$query = sprintf("SELECT d.* FROM {uc_discounts} d
LEFT JOIN {uc_discounts_products} dp ON d.discount_id = dp.discount_id AND dp.grouping = $grouping
LEFT JOIN {uc_discounts_terms} dt ON d.discount_id = dt.discount_id AND dt.grouping = $grouping
LEFT JOIN {uc_discounts_skus} ds ON d.discount_id = ds.discount_id AND ds.grouping = $grouping
LEFT JOIN {uc_discounts_classes} dcl ON d.discount_id = dcl.discount_id AND dcl.grouping = $grouping
LEFT JOIN {uc_discounts_authors} dau ON d.discount_id = dau.discount_id AND dau.grouping = $grouping
WHERE d.requires_code = 0
AND (d.has_role_filter = 0%s)
AND (%s)
AND (%s)
AND (%s)
AND (%s)
AND (%s)
AND (d.has_activation = 0 OR d.activates_on < %d)
AND (d.has_expiration = 0 OR d.expiration > %d)
AND (d.is_active = %d)
ORDER BY d.%s %s",
$roles_clause, $product_ids_clause, $term_ids_clause, $skus_clause, $classes_clause, $authors_clause,
time(), time(), IS_ACTIVE, $sort_column, $sort_order_string
);
$result = db_query($query);
$discounts = array();
while ($discount = db_fetch_object($result)) {
$discounts[] = $discount;
}
return $discounts;
}
/**
* Deletes all uc_discounts_uses rows for a discount.
*/
function uc_discounts_uses_delete_for_discount($discount_id) {
$query = "DELETE FROM {uc_discounts_uses} WHERE discount_id=%d";
db_query($query, $discount_id);
}
/**
* Deletes all uc_discounts_uses rows for an order.
*/
function uc_discounts_uses_delete_for_order($order_id) {
$query = "DELETE FROM {uc_discounts_uses} WHERE order_id=%d";
db_query($query, $order_id);
}
/**
* Records uses of a discount for an order
*/
function uc_discounts_uses_save_for_order($order) {
$discounts = get_discounts_for_order($order);
// Delete existing uses for order
uc_discounts_uses_delete_for_order($order->order_id);
// Insert uses (for best results use discounts returned by call to get_discounts_for_order)
foreach ($discounts as $discount) {
$code = (!empty($discount->code)) ? $discount->code : '';
$times_applied = (is_numeric($discount->times_applied)) ? $discount->times_applied : 1;
$amount = (is_numeric($discount->amount)) ? $discount->amount : 0;
$discount_use = new stdClass();
$discount_use->discount_id = $discount->discount_id;
$discount_use->user_id = $order->uid;
$discount_use->order_id = $order->order_id;
$discount_use->code = $code;
$discount_use->times_applied = $times_applied;
$discount_use->amount = $amount;
$discount_use->insert_timestamp = time();
drupal_write_record('uc_discounts_uses', $discount_use);
}
}
/**
* Returns order codes for order.
*/
function uc_discounts_get_codes_for_order($order_id) {
return uc_discounts_codes_to_array(db_result(db_query('SELECT codes FROM {uc_discounts_order_codes} WHERE order_id = %d', $order_id)));
}
/**
* Applies the discounts to an order by creating the necessary line items.
* @param $order The order object with any codes set in the uc_discounts_code property
* @param $compare_to_existing Compare new discounts to existing order discounts and fail if they have changed?
* @param $save_uses Save the discounts to the uses table? If not done here it will need to be done when the order is successfully created.
* @return array An array with the keys: 'success' => @bool, 'message' => @str
*/
function uc_discounts_apply($order, $save_uses = TRUE, $compare_to_existing = TRUE) {
// Store existing discount amounts
$existing_discount_amounts = array();
foreach (get_existing_discount_line_items($order) as $line_item) {
$existing_discount_amounts[] = uc_currency_format($line_item["amount"]);
}
// Regenerate discount amounts
$errors = array();
$warnings = array();
$messages = array();
$discounts = get_discounts_for_order($order, $errors, $warnings, $messages);
foreach ($warnings as $warning) {
drupal_set_message($warning, 'error');
}
// If there were errors, print and return FALSE
if (!empty($errors)) {
uc_order_log_changes($order->order_id, $errors);
foreach ($errors as $error) {
drupal_set_message($error, 'error');
}
return array('success' => FALSE, 'message' => t('Discounts have changed. Please review your cart and continue checkout.'));
}
// Add discount line items to order
add_discount_line_items_to_order($order, $discounts);
$new_discount_amounts = array();
foreach ($order->uc_discounts_line_items as $line_item) {
$new_discount_amounts[] = uc_currency_format($line_item["amount"]);
}
if ($compare_to_existing) {
$discount_intersection = array_intersect($existing_discount_amounts, $new_discount_amounts);
if (count($discount_intersection) != count($existing_discount_amounts)) {
// Save new discount line items
$order->uc_discounts_line_items_need_updating = TRUE;
uc_discounts_order('save', $order, NULL);
return array('success' => FALSE, 'message' => t('Discounts have changed. Please review your cart and continue checkout.'));
}
}
else {
$order->uc_discounts_line_items_need_updating = TRUE;
uc_discounts_order('save', $order, NULL);
}
if ($save_uses) {
uc_discounts_uses_save_for_order($order);
}
uc_order_log_changes($order->order_id, $messages);
return array('success' => TRUE, 'message' => t('Discount code(s) applied to order.'));
}
/**
* Returns existing discounts line items for order.
*/
function get_existing_discount_line_items($order) {
if (is_array($order->line_items)) {
$existing_line_items = $order->line_items;
}
else {
$existing_line_items = uc_order_load_line_items($order, TRUE);
}
$line_items = array();
foreach ($existing_line_items as $line_item) {
//If line item type is LINE_ITEM_KEY_NAME, add it to array
// Allow LINE_ITEM_KEY_NAME suffixes SCUK
if (strpos($line_item["type"], LINE_ITEM_KEY_NAME) !== false) {
$line_items[] = $line_item;
}
}
return $line_items;
}
/**
* Deletes all uc_discounts_order_codes rows for an order.
*/
function uc_discounts_order_codes_delete($order_id) {
$query = "DELETE FROM {uc_discounts_order_codes} WHERE order_id=%d";
db_query($query, $order_id);
}
///////////////////////////////////////////////////////////////////
//Misc. helper functions
///////////////////////////////////////////////////////////////////
/**
* Returns a string list of codes into an array of codes
*/
function uc_discounts_codes_to_array($codes_string) {
$codes = array();
$raw_codes = explode("\n", $codes_string);
foreach ($raw_codes as $raw_code) {
$code = trim($raw_code);
if (!empty($code)) {
$codes[] = $code;
}
}
return $codes;
}
/**
* Create a codes string from passed codes array.
* Note: returns "" if passed array is null
*/
function uc_discounts_codes_to_str($codes) {
return implode("\n", (array) $codes);
}
/**
* Returns an array of qualifying types with descriptions.
*/
function qualifying_type_options() {
static $options = NULL;
if (is_null($options)) {
$options = array(QUALIFYING_TYPE_MINIMUM_PRICE => t("Minimum price"),
QUALIFYING_TYPE_MINIMUM_QUANTITY => t("Minimum quantity"),
);
}
return $options;
}
function qualifying_type_name($qualifying_type) {
$options = qualifying_type_options();
return $options[$qualifying_type];
}
/**
* Returns an array of discount types with descriptions.
*/
function discount_type_options() {
static $options = NULL;
if (is_null($options)) {
$options = array(
DISCOUNT_TYPE_PERCENTAGE_OFF => t("Percentage off"),
DISCOUNT_TYPE_PERCENTAGE_OFF_PER_QUALIFYING_ITEM => t("Percentage off per qualifying item"),
DISCOUNT_TYPE_FIXED_AMOUNT_OFF => t("Fixed amount off"),
DISCOUNT_TYPE_FIXED_AMOUNT_OFF_PER_QUALIFYING_ITEM => t("Fixed amount off per qualifying item"),
DISCOUNT_TYPE_FREE_ITEMS => t("Free items"),
);
}
return $options;
}
function discount_type_name($discount_type) {
$options = discount_type_options();
return $options[$discount_type];
}
function discount_amount_formatted($discount) {
if (in_array($discount->discount_type, array(DISCOUNT_TYPE_PERCENTAGE_OFF, DISCOUNT_TYPE_PERCENTAGE_OFF_PER_QUALIFYING_ITEM))) {
return ($discount->discount_amount * 100) . '%';
}
elseif (in_array($discount->discount_type, array(DISCOUNT_TYPE_FIXED_AMOUNT_OFF, DISCOUNT_TYPE_FIXED_AMOUNT_OFF_PER_QUALIFYING_ITEM))) {
return uc_currency_format($discount->discount_amount);
}
else {
return $discount->discount_amount;
}
}
function uc_discounts_add_to_existing_map_number_value(&$a, $key, $value) {
$a[$key] = (array_key_exists($key, $a)) ? $a[$key] + $value : $value;
}
function uc_discounts_views_api() {
return array('api' => 2);
}
/** SCUK
* Returns an array with taxed product types as keys and
* an array with tax rate id and tax rate name as values
*/
function uc_discounts_get_taxes_array() {
$rates = uc_taxes_rate_load();
$taxes = array();
foreach ($rates as $rate) {
foreach ($rate->taxed_product_types as $type) {
// The tax type per product type (class) must be unique!
$taxes[$type] = array(
"id" => $rate->id,
"name" => $rate->name,
);
}
}
return $taxes;
}
/** SCUK
* Return an array with the cart products nids as keys and the product type (class) as values
*/
function uc_discounts_get_cart_nids() {
// Get the cart_id
$cid = uc_cart_get_id(FALSE);
// If we didn't get a cid, return empty.
if (!$cid) {
return array();
}
$result = db_query("SELECT n.nid, n.type FROM {node} n INNER JOIN {uc_cart_products} c ON n.nid = c.nid WHERE c.cart_id = '%s'", $cid);
$cart_products_nids = array();
while ($item = db_fetch_object($result)) {
$cart_products_nids[$item->nid] = $item->type;
}
return $cart_products_nids;
}
/** SCUK
* Add rate type and rate name to discount
* Warning: Products that qualifying for this discount must have the same tax rate,
* if mixed the rate applied to discount will be random and the calculations will be wrong
*/
function uc_discounts_add_discount_rate_id(&$discount, $products_with_discount_ids) {
$taxes = uc_discounts_get_taxes_array();
$products_in_cart = uc_discounts_get_cart_nids();
foreach ($products_with_discount_ids as $product) {
// Check if product nid exists in cart
if (array_key_exists($product, $products_in_cart)) {
// Check if product type (class) exists in taxes
if (array_key_exists($products_in_cart[$product],$taxes)) {
// Get the tax rate type
$discount->rate_type = $taxes[$products_in_cart[$product]]["id"];
// and the tax rate name
$discount->rate_name = $taxes[$products_in_cart[$product]]["name"];
}
}
}
}