'admin/store/discounts', 'title' => t('Discounts'), 'description' => t('Administer discounts'), 'access' => $access_administer_discounts, 'callback' => 'uc_discounts_show_discounts', 'type' => MENU_NORMAL_ITEM, ); $items[] = array( 'path' => 'admin/store/discounts/add', 'title' => t('Add Discount'), 'access' => $access_administer_discounts, 'callback' => 'drupal_get_form', 'callback arguments' => 'uc_discounts_discount_form', 'type' => MENU_CALLBACK, ); $items[] = array( 'path' => 'admin/store/discounts/valuehtml', 'access' => $access_administer_discounts, 'callback' => 'uc_discounts_get_condition_value_html', ); } else { $discount_id = arg(3); if (arg(0) == 'admin' && arg(1) == 'store' && arg(2) == 'discounts' && is_numeric($discount_id)) { // conditions if (arg(4) == 'conditions') { $discount = uc_discounts_load_discount($discount_id); $items[] = array( 'path' => "admin/store/discounts/$discount_id/conditions", //'title' => check_plain($discount->name), 'callback' => 'drupal_goto', 'callback arguments' => "admin/store/discounts/$discount_id", 'access' => $access_administer_discounts, 'type' => MENU_CALLBACK, ); $items[] = array( 'path' => "admin/store/discounts/$discount_id/conditions/add", 'description' => check_plain($discount->name), 'callback' => 'drupal_get_form', 'callback arguments' => array('uc_discounts_condition_form', $discount_id, 0), 'access' => $access_administer_discounts, 'type' => MENU_CALLBACK, ); $condition_id = arg(5); if (is_numeric($condition_id)) { $items[] = array( 'path' => "admin/store/discounts/$discount_id/conditions/$condition_id", 'callback' => 'drupal_get_form', 'callback arguments' => array('uc_discounts_condition_form', $discount_id, $condition_id), 'access' => $access_administer_discounts, 'type' => MENU_CALLBACK, ); $items[] = array( 'path' => "admin/store/discounts/$discount_id/conditions/$condition_id/delete", 'callback' => 'uc_discounts_condition_delete', 'callback arguments' => array($discount_id, $condition_id), 'access' => $access_administer_discounts, 'type' => MENU_CALLBACK, ); } } // actions elseif (arg(4) == 'actions') { $discount = uc_discounts_load_discount($discount_id); $items[] = array( 'path' => "admin/store/discounts/$discount_id/actions", //'title' => check_plain($discount->name), 'callback' => 'drupal_goto', 'callback arguments' => "admin/store/discounts/$discount_id", 'access' => $access_administer_discounts, 'type' => MENU_CALLBACK, ); $items[] = array( 'path' => "admin/store/discounts/$discount_id/actions/add", 'description' => check_plain($discount->name), 'callback' => 'drupal_get_form', 'callback arguments' => array('uc_discounts_action_form', $discount_id, 0), 'access' => $access_administer_discounts, 'type' => MENU_CALLBACK, ); $action_id = arg(5); if (is_numeric($action_id)) { $items[] = array( 'path' => "admin/store/discounts/$discount_id/actions/$action_id", 'callback' => 'drupal_get_form', 'callback arguments' => array('uc_discounts_action_form', $discount_id, $action_id), 'access' => $access_administer_discounts, 'type' => MENU_CALLBACK, ); $items[] = array( 'path' => "admin/store/discounts/$discount_id/actions/$action_id/delete", 'callback' => 'uc_discounts_action_delete', 'callback arguments' => array($discount_id, $action_id), 'access' => $access_administer_discounts, 'type' => MENU_CALLBACK, ); } } // just the discounts else { $items[] = array( 'path' => 'admin/store/discounts/' . $discount_id, 'description' => t('View discount') . $discount_id, 'callback' => 'uc_discounts_discount_view', 'callback arguments' => $discount_id, 'access' => $access_administer_discounts, 'type' => MENU_CALLBACK, ); $items[] = array( 'path' => "admin/store/discounts/$discount_id/view", 'title' => t('view'), 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10, ); $items[] = array( 'path' => "admin/store/discounts/$discount_id/edit", 'title' => t('edit'), 'callback' => 'drupal_get_form', 'callback arguments' => array('uc_discounts_discount_form', $discount_id), 'access' => $access_administer_discounts, 'weight' => 1, 'type' => MENU_LOCAL_TASK, ); $items[] = array( 'path' => "admin/store/discounts/$discount_id/delete", 'callback' => 'drupal_get_form', 'callback arguments' => array('uc_discounts_discount_delete_form', $discount_id), 'access' => $access_administer_discounts, 'type' => MENU_CALLBACK, ); } } } return $items; } /** * Format an "item: value" list, with item being * * @param $items * Array of items, with each item having "label" and "value" * @ingroup themeable **/ function theme_uc_discounts_list($items) { $output = '
'; foreach ($items as $item) { if (!isset($item['label']) || !isset($item['value'])) { continue; } $output .= '' . t($item['label']) . ': '; $output .= check_plain($item['value']); $output .= "
\n"; } $output .= "
\n"; $element = array( '#children' => $output, ); return theme('fieldset', $element); } /******************************************************************************* * Hook Functions (Ubercart) ******************************************************************************/ /** * Implementation of hook_line_item() */ function uc_discounts_line_item() { $items[] = array( 'id' => 'discount', 'title' => t('Discounts'), 'callback' => 'uc_line_item_discounts', 'weight' => 2, 'stored' => FALSE, 'default' => FALSE, 'calculated' => TRUE, 'display_only' => FALSE, ); return $items; } function uc_line_item_discounts($op, $arg1) { switch ($op) { case 'load': case 'display': $lines[] = array( 'id' => 'discount', 'title' => t('Discount'), 'amount' => $arg1->discounts['amount'], ); return $lines; case 'cart-preview': $apply_discounts = uc_discounts_apply_discounts($arg1); if (empty($apply_discounts)) { return; } $discount_total = 0; $script = ''; foreach ($apply_discounts as $discount) { $discount_total -= $discount['amount']; } if ($discount_total != 0) { $script .= "set_line_item('discounts', '". t('Total discount') ."', ". $discount_total .", 1);\n"; } if ($script) { drupal_add_js("\$(document).ready( function() { ". $script ." } );", 'inline'); } break; } } /** * Implementation of hook_checkout_pane */ function uc_discounts_checkout_pane() { $panes[] = array( 'id' => 'discounts', 'title' => t('Discounts'), 'desc' => t('Show discounts for the order'), 'callback' => 'uc_checkout_pane_discounts', 'weight' => 1.5, ); return $panes; } /** * Add the discount pane to the checkout screen */ function uc_checkout_pane_discounts($op, &$arg1, $arg2) { // get discounts to apply to this order switch ($op) { case 'view': // there is no order at this time. just display discounts $apply_discounts = uc_discounts_apply_discounts(); if (empty($apply_discounts)) { return; } $description = t('The following discounts will be applied to your order:'); // create discounts table $header = array( t('Discount'), array('data' => t('Amount'), 'class' => 'text-right'), ); $rows = array(); $discount_total = 0; foreach ($apply_discounts as $discount) { $rows[] = array( $discount['description'], array('data' => uc_currency_format($discount['amount']), 'class' => 'text-right'), ); $discount_total += $discount['amount']; } $rows[] = array( '', array( 'data' => ''.t('Subtotal').': '.uc_currency_format($discount_total), 'colspan' => 2, 'class' => 'text-right', ), ); $table = theme('table', $header, $rows); $contents['discounts'] = array('#value' => $table ); return array('description' => $description, 'contents' => $contents, 'next-button' => TRUE); case 'process': $apply_discounts = uc_discounts_apply_discounts(); if (empty($apply_discounts)) { return; } $discount_total = 0; foreach ($apply_discounts as $discount) { $arg1->discounts['description'] = $discount['description']; $arg1->discounts['amount'] = $discount['amount']; } return TRUE; case 'review': // clear any existing line items $review[] = array('title' => t('Total discount'), 'data' => $arg1->discounts['amount']); return $review; } } /** * Implementation of hook_order */ function uc_discounts_order($op, &$arg1, $arg2) { switch ($op) { // TOTAL case 'total': return $arg1->discounts['amount']; // Load the discount from the database. case 'load': $result = db_query("SELECT * FROM {uc_order_line_items} WHERE order_id = %d AND type = 'discount'", $arg1->order_id); if ($data = db_fetch_object($result)) { $arg1->discounts['description'] = $data->title; $arg1->discounts['amount'] = $data->amount; } break; // SAVE case 'save': // clear any existing discount line items from this order db_query("DELETE FROM {uc_order_line_items} WHERE order_id=%d AND type='discount'", $arg1->order_id); $apply_discounts = uc_discounts_apply_discounts(); if (empty($apply_discounts)) { return; } $discount_total = 0; foreach ($apply_discounts as $discount) { // store as negative $discount_total -= $discount['amount']; } db_query("INSERT INTO {uc_order_line_items} (line_item_id, order_id, type, title, amount, weight) VALUES ('', %s, 'discount', '%s', %s, 1)", $arg1->order_id, t('toto'), $discount_total); return $discount_total; // DELETE case 'delete': db_query("DELETE FROM {uc_order_line_items} WHERE order_id=%d AND type='discount'", $arg1->order_id); break; } } /** * Implementation of hook_order_pane(). */ function uc_discounts_order_pane() { $panes[] = array( 'id' => 'discounts', 'callback' => 'uc_order_pane_discounts', 'title' => t('See discounts'), 'desc' => t('See the discounts applied to an order.'), 'class' => 'abs-left', 'weight' => 5, 'show' => array('view'), ); return $panes; } function uc_order_pane_discounts($op, $arg1) { switch ($op) { case 'view': if (empty($arg1->discounts['amount'])) { $discounts = t('None specified'); } else { $discounts = $arg1->discounts['amount']; } return t('Discount applied : !discounts', array('!discounts' => $discounts)); } } /******************************************************************************* * Callback Functions, Forms, and Tables ******************************************************************************/ /** * Check order against all active discounts */ function uc_discounts_apply_discounts() { // cache discounts. return if already set static $apply_discounts = NULL; if ($apply_discounts !== NULL) { return $apply_discounts; } $cart = uc_cart_get_contents(); $cart_copy = array(); foreach ($cart as $cart_item) { $cart_copy[] = drupal_clone($cart_item); } // get and store total price $total_price = 0; foreach ($cart_copy as $product) { $total_price += $product->price * $product->qty; } // check against order discounts $apply_discounts = array(); $discount_list = uc_discounts_get_discounts(); $condition_info = _uc_discounts_get_conditions(); $action_info = _uc_discounts_get_actions(); foreach ($discount_list as $discount) { // skip discounts which may not be applied with other discounts if ($discount->is_exclusive && !empty($apply_discounts)) { continue; } // load conditions for the discount $condition_list = uc_discounts_get_discount_conditions($discount->id); $condition_groups = array(); foreach ($condition_list as $condition) { // skip if this condition group has already failed if (isset($condition_groups[$condition->condition_group]) && $condition_groups[$condition->condition_group] == FALSE) { continue; } // check if condition matches if (isset($condition_info[$condition->property]['check_callback'])) { $check_callback = $condition_info[$condition->property]['check_callback']; $condition_groups[$condition->condition_group] = $check_callback($condition, $total_price, $cart_copy); } } // did any condition groups match if (array_search(TRUE, $condition_groups) !== FALSE) { $action_list = uc_discounts_get_discount_actions($discount->id); foreach ($action_list as $action) { if (!isset($action_info[$action->property]['apply_callback'])) { continue; } $apply_callback = $action_info[$action->property]['apply_callback']; $discount_amount = $apply_callback($op, $action, $total_price, $cart_copy); if ($discount_amount > 0) { $apply_discounts[$discount->id] = array( 'description' => check_plain($discount->description), 'amount' => $discount_amount, ); $total_price -= $discount_amount; } } } } // refresh the cart - somehow, the price modules are affecting it //$cart = uc_cart_get_contents(NULL, 'rebuild'); return $apply_discounts; } /** * Generate a page which shows all of the discounts */ function uc_discounts_show_discounts() { $discounts = uc_discounts_table(); $discounts .= l('Add Discount', 'admin/store/discounts/add'); $element = array( '#title' => t('Discounts'), '#children' => $discounts, ); return theme('fieldset', $element); } /** * View the information for a discount * * @param $discount_id * @return * formatted block with discount info */ function uc_discounts_discount_view($discount_id) { $discount = uc_discounts_load_discount($discount_id); // must be a valid discount if ($discount->id == 0) { drupal_goto('/admin/store/discounts'); } drupal_set_title($discount->name); $output = uc_discounts_discount_info($discount); $output .= uc_discounts_conditions_table($discount->id); $output .= uc_discounts_actions_table($discount->id); return $output; } /** * Format the information for the list of discounts in a table. * * @return * themed table */ function uc_discounts_table() { $header = array( t('Name'), t('Active'), t('Weight'), t('End Processing'), t('Exclusive'), t('Start Time'), t('End Time'), //array('data' => t('Actions'), 'colspan' => 3), ); $caption = t('Discounts'); $rows = array(); $discount_list = uc_discounts_get_discounts(TRUE); foreach ($discount_list as $discount) { $rows[] = array( l($discount->name, 'admin/store/discounts/'.$discount->id), $discount->is_active ? UC_DISCOUNTS_YES : UC_DISCOUNTS_NO, $discount->weight, $discount->do_end_processing ? UC_DISCOUNTS_YES : UC_DISCOUNTS_NO, $discount->is_exclusive ? UC_DISCOUNTS_YES : UC_DISCOUNTS_NO, format_date($discount->start_time), format_date($discount->end_time), //l(t('View'), 'admin/store/discounts/'.$discount->id), //l(t('Edit'), 'admin/store/discounts/'.$discount->id.'/edit'), //l(t('Delete'), 'admin/store/discounts/'.$discount->id.'/delete'), ); } $table = theme('table', $header, $rows); return $table; } /** * Format discount information * * @param $discount * @return * formatted block of discount info */ function uc_discounts_discount_info($discount) { $rows = array( array( 'label' => 'Name', 'value' => $discount->name, ), array( 'label' => 'Description', 'value' => $discount->description, ), array( 'label' => 'Max Discounts', 'value' => $discount->max_discounts, ), array( 'label' => 'End Processing', 'value' => $discount->do_end_processing ? UC_DISCOUNTS_YES : UC_DISCOUNTS_NO, ), array( 'label' => 'Exclusive', 'value' => $discount->is_exclusive ? UC_DISCOUNTS_YES : UC_DISCOUNTS_NO, ), array( 'label' => 'Weight', 'value' => $discount->weight, ), array( 'label' => 'Start Time', 'value' => format_date($discount->start_time), ), array( 'label' => 'End Time', 'value' => format_date($discount->end_time), ), array( 'label' => 'Active', 'value' => $discount->is_active ? UC_DISCOUNTS_YES : UC_DISCOUNTS_NO, ), ); $output = theme('uc_discounts_list', $rows); return $output; } /** * Provides the basic discount form * * Loads the specified discount id, and pre-populated the form based on this. * If this is for a new discount, 0 is passed, getting default values. * * @param $discount_id */ function uc_discounts_discount_form($discount_id = 0) { $discount = uc_discounts_load_discount($discount_id); $start_time = getdate($discount->start_time); $default_start_time = array( 'month' => $start_time['mon'], 'day' => $start_time['mday'], 'year' => $start_time['year'], ); $end_time = getdate($discount->end_time); $default_end_time = array( 'month' => $end_time['mon'], 'day' => $end_time['mday'], 'year' => $end_time['year'], ); if ($discount->id == 0) { drupal_set_title(t('New Discount')); } else { drupal_set_title($discount->name); } $form['discount_id'] = array( '#type' => 'value', '#value' => $discount->id, ); $form['discount_name'] = array( '#title' => t('Discount Name'), '#type' => 'textfield', '#description' => t('Name of this discount.'), '#required' => TRUE, '#default_value' => $discount->name, ); $form['description'] = array( '#title' => t('Description'), '#type' => 'textfield', '#description' => t('A brief description. This will also show up on invoices.'), '#required' => TRUE, '#default_value' => $discount->description, ); $form['max_discounts'] = array( '#title' => t('Max Discounts'), '#type' => 'textfield', '#description' => t('This discount can be applied up to this many times in a single order. Set to 0 for unlimited'), '#default_value' => 1, '#size' => 4, '#required' => TRUE, '#default_value' => $discount->max_discounts, ); $form['do_end_processing'] = array( '#title' => t('End Processing'), '#type' => 'checkbox', '#description' => t('If set and this discount matches, no further discounts will be processed'), '#default_value' => $discount->do_end_processing, ); $form['is_exclusive'] = array( '#title' => t('Exclusive'), '#type' => 'checkbox', '#description' => t('If checked, this discount will not be processed if any other discounts before this have matched. If no other discounts have matched and this one matches, no further discounts will be processed'), '#default_value' => $discount->is_exclusive, ); $form['weight'] = array( '#title' => t('Weight'), '#type' => 'weight', '#description' => t('Determines the order in which discounts are applied'), '#default_value' => 0, '#size' => 4, '#required' => TRUE, '#default_value' => $discount->weight, ); $form['start_time'] = array( '#title' => t('Start Date'), '#type' => 'date', '#description' => 'Start date for this discount', '#required' => TRUE, '#default_value' => $default_start_time, ); $form['end_time'] = array( '#title' => t('End Date'), '#type' => 'date', '#description' => 'End date for this discount', '#required' => TRUE, '#default_value' => $default_end_time, ); $form['is_active'] = array( '#title' => t('Active'), '#type' => 'checkbox', '#description' => 'If not active, the discount is not available regardless of the start and end times.', '#default_value' => 1, '#default_value' => $discount->is_active, ); if ($discount->id == 0) { $form['submit'] = array( '#type' => 'submit', '#value' => t('Save'), ); } else { $form['submit'] = array( '#type' => 'submit', '#value' => t('Update'), ); $form['delete_button'] = array( '#type' => 'submit', '#name' => 'delete', '#value' => t('Delete'), ); } return $form; } /** * discounts form submission */ function uc_discounts_discount_form_submit($form_id, $form_values) { if ($form_values['discount_id'] == 0) { $id = db_next_id('{uc_discounts}_id'); $sql = "INSERT INTO {uc_discounts} (name, description, max_discounts, do_end_processing, is_exclusive, weight, start_time, end_time, is_active, id) VALUES ('%s', '%s', %d, %d, %d, %d, %d, %d, %d, %d)"; } else { $id = $form_values['discount_id']; $sql = "UPDATE {uc_discounts} SET name='%s', description='%s', max_discounts=%d, do_end_processing=%d, is_exclusive=%d, weight=%d, start_time=%d, end_time=%d, is_active=%d WHERE id=%d"; } $start_time = mktime(0, 0, 0, $form_values['start_time']['month'], $form_values['start_time']['day'], $form_values['start_time']['year'] ); $end_time = mktime(23, 59, 59, $form_values['end_time']['month'], $form_values['end_time']['day'], $form_values['end_time']['year'] ); db_query($sql, $form_values['discount_name'], $form_values['description'], $form_values['max_discounts'], $form_values['do_end_processing'], $form_values['is_exclusive'], $form_values['weight'], $start_time, $end_time, $form_values['is_active'], $id ); return "admin/store/discounts/$id"; } /** * discounts form validation */ function uc_discounts_discount_form_validate($form_id, $form_values) { if (isset($form_values['delete'])) { drupal_goto('admin/store/discounts/'.$form_values['discount_id'].'/delete'); } if (!isset($form_values['discount_id'])) { drupal_set_message(t('Invalid form submission'), 'error'); drupal_goto('admin/store/discounts'); } $start_time = mktime(0, 0, 0, $form_values['start_time']['month'], $form_values['start_time']['day'], $form_values['start_time']['year'] ); $end_time = mktime(23, 59, 59, $form_values['end_time']['month'], $form_values['end_time']['day'], $form_values['end_time']['year'] ); $today = time(); if ($end_time <= $start_time) { form_set_error('end_time', t('End date can not be before the start date')); } if ($end_time < $today) { form_set_error('end_time', t('Setting the end date in the past is pointless.')); } } /** * Discount deletion form. * * @param $discount_id * Must be a valid and existing discount */ function uc_discounts_discount_delete_form($discount_id) { if ($discount == 0) { drupal_goto('admin/store/discounts'); } $discount = uc_discounts_load_discount($discount_id); if (!$discount) { drupal_goto('admin/store/discounts'); } drupal_set_title(t('Delete !name', array('!name' => $discount->name))); $form['discount_id'] = array( '#type' => 'value', '#value' => $discount->id, ); $form['discount_name'] = array( '#type' => 'value', '#value' => $discount->name, ); $form['discount_info'] = array( '#value' => uc_discounts_discount_info($discount), ); $output = confirm_form($form, t('Delete !name?', array('!name' => $discount->name)), 'admin/store/discounts/'.$discount->id, '', t('Delete'), t('Cancel') ); return $output; } /** * discounts deletion form submissions */ function uc_discounts_discount_delete_form_submit($form_id, $form_values) { if ($form_values['confirm']) { db_query('DELETE FROM {uc_discounts} WHERE id=%d', $form_values['discount_id']); drupal_set_message(t('Discount "!name" has been deleted', array('!name' => $form_values['discount_name']))); } return 'admin/store/discounts'; } /** * Format the information for the list of conditions in a table. * * @param $discount_id * @return * themed table */ function uc_discounts_conditions_table($discount_id) { $condition_list = uc_discounts_get_discount_conditions($discount_id); $header = array( t('Group'), t('Weight'), t('Property'), t('Op'), t('Item'), t('Value'), array('data' => t('Actions'), 'colspan' => 2), ); $properties = _uc_discounts_get_condition_form_values(); $condition_info = _uc_discounts_get_conditions(); $rows = array(); $base_url = 'admin/store/discounts/'.$discount_id.'/conditions'; foreach ($condition_list as $condition) { // get item name if (isset($condition_info[$condition->property]['item_name_callback'])) { $item_name_callback = $condition_info[$condition->property]['item_name_callback']; $item = $item_name_callback($condition->item_id); } else { $item = ''; } // format value if (isset($condition_info[$condition->property]['value_format_callback'])) { $value_format_callback = $condition_info[$condition->property]['value_format_callback']; $value = $value_format_callback($condition->value); } else { $value = $condition->value; } $rows[] = array( $condition->condition_group, $condition->weight, $properties[$condition->property], $condition->op, $item, $value, l(t('edit'), $base_url.'/'.$condition->id), l(t('delete'), $base_url.'/'.$condition->id.'/delete'), ); } $table = theme('table', $header, $rows); $table .= l('Add Condition', $base_url.'/add'); $element = array( '#title' => t('Conditions'), '#collapsible' => TRUE, '#children' => $table, ); return theme('fieldset', $element); } /** * Provides the discount condition form * * Loads the specified condition id, and pre-populated the form based on this. * If this is for a new condition, 0 is passed, getting default values. * Otherwise, this checks to make sure that the condition belongs to the * specified discount. * * @param $discount_id * @param acondition_id */ function uc_discounts_condition_form($discount_id, $condition_id, $form_values = NULL) { $condition = uc_discounts_load_condition($condition_id); if (!$condition) { drupal_goto('admin/store/discounts'); } // make sure condition belongs to the right discount if ($condition_id > 0 && $condition->discount_id != $discount_id) { drupal_goto('admin/store/discounts'); } $discount = uc_discounts_load_discount($discount_id); $properties = _uc_discounts_get_condition_form_values(); // get condition property if set if (!empty($condition->property)) { $condition_property = $condition->property; } elseif (isset($form_values['property'])) { $condition_property = $form_values['property']; } else { $condition_property = ''; } // make sure condition property is valid $condition_info = _uc_discounts_get_conditions(); if ($condition_property && !isset($condition_info[$condition_property])) { $condition_property = ''; } // set page title if ($condition_id == '') { drupal_set_title(t('@name - Add Condition', array('@name' => $discount->name))); } else { drupal_set_title(t('@name - Edit Condition', array('@name' => $discount->name))); } // values for all stages $form['#multistep'] = TRUE; $form['condition_id'] = array( '#type' => 'value', '#value' => $condition->id, ); $form['discount_id'] = array( '#type' => 'value', '#value' => $discount_id, ); if ($condition_property) { $step = 2; } else { $step = 1; } $form['step'] = array( '#type' => 'hidden', '#value' => $step + 1, ); // stage 1 - select property if ($step == 1) { $form['#redirect'] = FALSE; $form['property'] = array( '#type' => 'select', '#title' => t('Property'), '#options' => $properties, ); $form['submit'] = array( '#type' => 'submit', '#value' => t('Next'), ); } // stage 2 - everything else else { // get item field fields for specific condition types if (isset($condition_info[$condition_property]['value_field_callback'])) { $value_field_callback = $condition_info[$condition_property]['value_field_callback']; $value_field = $value_field_callback(); } else { $value_field = array( '#type' => 'value', '#value' => '', ); } // item field is not required if (isset($condition_info[$condition_property]['item_field_callback'])) { $item_field_callback = $condition_info[$condition_property]['item_field_callback']; $item_field = $item_field_callback(); } else { $item_field = array( '#type' => 'value', '#value' => 0, ); } $form['property'] = array( '#type' => 'value', '#value' => $condition_property, ); $form['display_property'] = array( '#type' => 'item', '#title' => t('Property'), '#value' => $properties[$condition_property], ); $form['weight'] = array( '#type' => 'weight', '#title' => t('Weight'), '#default_value' => $condition->weight, ); $form['condition_group'] = array( '#type' => 'select', '#title' => t('Condition Group'), '#description' => t('All conditions in a condition group must be satisfied in order to match. At least one condition group must be satisfied for the discount to be applied.'), '#options' => drupal_map_assoc(range(1, 10)), '#default_value' => $condition->condition_group, ); $form['comparison'] = array( '#type' => 'select', '#title' => t('Comparison Operator'), '#options' => _uc_discounts_get_comparisons($condition_info[$condition_property]['compare_type']), '#default_value' => $condition->op, ); $form['item'] = $item_field; $form['item']['#default_value'] = $condition->item_id; $form['value'] = $value_field; $form['value']['#default_value'] = $condition->value; $form['submit'] = array( '#type' => 'submit', '#value' => t('Submit'), ); } return $form; } /** * conditions form validation */ function uc_discounts_condition_form_validate($form_id, $form_values) { if ($form_values['step'] < 3) { return; } // check if operator is valid for this property $property = $form_values['property']; $op = $form_values['comparison']; $condition_info = _uc_discounts_get_conditions(); $valid_compare_types = _uc_discounts_get_comparisons($condition_info[$property]['compare_type']); if (!in_array($op, $valid_compare_types)) { form_set_error('comparison', t('@property may not have a comparison operator of @op', array('@property' => $property, '@op' => $op))); } // check value based on compare_type switch ($condition_info[$property]['compare_type']) { case 'integer': // this will also catch negative integers, but that's okay here if (!ctype_digit($form_values['values'])) { form_set_error('value', t('Value must be an integer')); } break; case 'numeric': if (!is_numeric($form_values['values'])) { form_set_error('value', t('Value must be a number')); } break; case 'money': $pattern = '/^\d*(\.\d{2})?$/'; if (!is_numeric($form_values['value']) || !preg_match($pattern, $form_values['value'])) { form_set_error('value', t('Value must be a valid monetary number, with no commas')); } break; // no validation for text values } } /** * conditions form submission */ function uc_discounts_condition_form_submit($form_id, $form_values) { if ($form_values['step'] < 3) { return; } if ($form_values['condition_id'] == 0) { $id = db_next_id('{uc_discounts_conditions}_id'); $sql = "INSERT INTO {uc_discounts_conditions} (discount_id, condition_group, property, op, item_id, value, weight, id) VALUES (%d, %d, '%s', '%s', %d, '%s', %d, %d)"; } else { $id = $form_values['condition_id']; $sql = "UPDATE {uc_discounts_conditions} SET discount_id=%d, condition_group=%d, property='%s', op='%s', item_id=%d, value='%s', weight=%d WHERE id=%d"; } db_query($sql, $form_values['discount_id'], $form_values['condition_group'], $form_values['property'], $form_values['comparison'], $form_values['item'], $form_values['value'], $form_values['weight'], $id ); return 'admin/store/discounts/'.$form_values['discount_id']; } /** * Condition deletion form. * * @param $discount_id * Must be a valid and existing discount * @param $condition_id * Must be a valid condition which belongs to the specified discount */ function uc_discounts_condition_delete($discount_id, $condition_id) { // must be a valid condition, belonging to the specified discount $condition = uc_discounts_load_condition($condition_id); if (!$condition || $condition->discount_id != $discount_id) { drupal_goto('admin/store/discounts'); } db_query("DELETE FROM {uc_discounts_conditions} WHERE id=%d", $condition_id); drupal_goto("admin/store/discounts/$discount_id"); } /** * Format the information for the list of actions in a table. * * @param $discount_id * @return * themed table */ function uc_discounts_actions_table($discount_id) { $action_list = uc_discounts_get_discount_actions($discount_id); $properties = _uc_discounts_get_action_form_values(); $action_info = _uc_discounts_get_actions(); $header = array( t('Property'), t('Weight'), t('Item'), t('Qty'), t('Amount'), array('data' => t('Actions'), 'colspan' => 2), ); $rows = array(); $base_url = "admin/store/discounts/$discount_id/actions/"; foreach ($action_list as $action) { // get item name if (isset($action_info[$action->property]['item_name_callback'])) { $item_name_callback = $action_info[$action->property]['item_name_callback']; $item = $item_name_callback($action->item_id); } else { $item = $action->item_id; } // format amount if (isset($action_info[$action->property]['amount_callback'])) { $amount_callback = $action_info[$action->property]['amount_callback']; $amount = $amount_callback($action->amount); } else { $amount = $action->amount; } $rows[] = array( $properties[$action->property], $action->weight, $item, $action->qty == 0 ? '' : $action->qty, $amount, l(t('edit'), $base_url.$action->id), l(t('delete'), $base_url.$action->id.'/delete'), ); } $table = theme('table', $header, $rows); $table .= l('Add Action', $base_url.'add'); $element = array( '#title' => t('Actions'), '#collapsible' => TRUE, '#children' => $table, ); return theme('fieldset', $element); } /** * Provides the discount action form * * Loads the specified action id, and pre-populated the form based on this. * If this is for a new action, 0 is passed, getting default values. * Otherwise, this checks to make sure that the action belongs to the * specified discount. * * @param $discount_id * @param $action_id */ function uc_discounts_action_form($discount_id, $action_id, $form_values = NULL) { $action = uc_discounts_load_action($action_id); if (!$action) { drupal_goto('admin/store/discounts'); } // make sure action belongs to the right discount if ($action_id > 0 && $action->discount_id != $discount_id) { drupal_goto('admin/store/discounts'); } $discount = uc_discounts_load_discount($discount_id); $properties = _uc_discounts_get_action_form_values(); // get action property if set if (!empty($action->property)) { $action_property = $action->property; } elseif (isset($form_values['property'])) { $action_property = $form_values['property']; } else { $action_property = ''; } // make sure action property is valid $action_info = _uc_discounts_get_actions(); if ($action_property && !isset($action_info[$action_property])) { $action_property = ''; } // set page title if ($action_id == 0) { drupal_set_title(t('@name - Add Action', array('@name' => $discount->name))); } else { drupal_set_title(t('@name - Edit Action', array('@name' => $discount->name))); } // values for all stages $form['#multistep'] = TRUE; $form['action_id'] = array( '#type' => 'value', '#value' => $action->id, ); $form['discount_id'] = array( '#type' => 'value', '#value' => $discount_id, ); if ($action_property) { $step = 2; } else { $step = 1; } $form['step'] = array( '#type' => 'hidden', '#value' => $step + 1, ); // stage 1 - select property if ($step == 1) { $form['#redirect'] = FALSE; $form['property'] = array( '#type' => 'select', '#title' => t('Property'), '#options' => $properties, ); $form['submit'] = array( '#type' => 'submit', '#value' => t('Next'), ); } // stage 2 - everything else else { // get item field if (isset($action_info[$action_property]['item_field_callback'])) { $item_field_callback = $action_info[$action_property]['item_field_callback']; $item_field = $item_field_callback(); $item_field['#default_value'] = $action->item_id; } else { $item_field = array( '#type' => 'value', '#value' => 0, ); } // does this form have a qty field if (empty($action_info[$action_property]['has_qty_field'])) { $qty_field = array( '#type' => 'value', '#value' => 0, ); } else { $qty_field = array( '#type' => 'textfield', '#title' => t('Qty'), '#maxlength' => 4, '#size' => 4, '#default_value' => $action->qty > 0 ? $action->qty : '', ); } $form['property'] = array( '#type' => 'value', '#value' => $action_property, ); $form['display_property'] = array( '#type' => 'item', '#title' => t('Property'), '#value' => $properties[$action_property], ); $form['weight'] = array( '#type' => 'weight', '#title' => t('Weight'), '#default_value' => $action->weight, ); $form['item_id'] = $item_field; $form['qty'] = $qty_field; $form['amount'] = array( '#type' => 'textfield', '#title' => t('Amount'), '#maxlength' => 10, '#size' => 10, '#required' => TRUE, '#description' => t('Enter either a dollar amount or a percentage'), '#default_value' => $action->amount, ); $form['submit'] = array( '#type' => 'submit', '#value' => t('Submit'), ); } return $form; } /** * action form submission */ function uc_discounts_action_form_submit($form_id, $form_values) { if ($form_values['step'] < 3) { return; } if ($form_values['action_id'] == 0) { $id = db_next_id('{uc_discounts_actions}_id'); $sql = "INSERT INTO {uc_discounts_actions} (discount_id, property, item_id, qty, amount, weight, id) VALUES (%d, '%s', '%s', %d, '%s', %d, %d)"; } else { $id = $form_values['action_id']; $sql = "UPDATE {uc_discounts_actions} SET discount_id=%d, property='%s', item_id='%s', qty=%d, amount='%s', weight=%d WHERE id=%d"; } db_query($sql, $form_values['discount_id'], $form_values['property'], $form_values['item_id'], $form_values['qty'], $form_values['amount'], $form_values['weight'], $id ); return 'admin/store/discounts/'.$form_values['discount_id']; } /** * Action deletion form. * * @param $discount_id * Must be a valid and existing discount * @param $action_id * Must be a valid action which belongs to the specified discount */ function uc_discounts_action_delete($discount_id, $action_id) { $action = uc_discounts_load_action($action_id); if (!$action || $action->discount_id != $discount_id) { drupal_goto('admin/store/discounts'); } db_query('DELETE FROM {uc_discounts_actions} WHERE id=%d', $action_id); drupal_goto('admin/store/discounts/'.$discount_id); } /******************************************************************************* * Module and Helper Functions ******************************************************************************/ /** * Load a discount * * @param $discount_id * @return * discount object. If discount_id is 0, return the default values */ function uc_discounts_load_discount($discount_id) { if ($discount_id == 0) { // return object with default values $discount = new stdClass; $discount->id = 0; $discount->name = ''; $discount->description = ''; $discount->max_discounts = 0; $discount->do_end_processing = 0; $discount->is_exclusive = 0; $discount->weight = 0; $discount->start_time = time(); $discount->end_time = time(); $discount->is_active = 0; } else { $result = db_query('SELECT * FROM {uc_discounts} WHERE id=%d', $discount_id); $discount = db_fetch_object($result); if (!$discount) { drupal_goto('/admin/store/discounts'); } } return $discount; } /** * Load a condition for an order discount * * @param $condition_id */ function uc_discounts_load_condition($condition_id) { if ($condition_id == 0) { // return object with default values $condition = new stdClass; $condition->id = 0; $condition->discount_id = 0; $condition->condition_group = 0; $condition->property = ''; $condition->op = ''; $condition->item_id = 0; $condition->value = ''; $condition->weight = 0; } else { $result = db_query('SELECT * FROM {uc_discounts_conditions} WHERE id=%d', $condition_id); $condition = db_fetch_object($result); if (!$condition) { drupal_goto('admin/store/discounts'); } } return $condition; } /** * Load an action for a discount * * @param $action_id */ function uc_discounts_load_action($action_id) { if ($action_id == 0) { // return object with default values $action = new stdClass; $action->id = 0; $action->discount_id = 0; $action->property = ''; $action->item_id = 0; $action->qty = 0; $action->amount = ''; } else { $result = db_query('SELECT * FROM {uc_discounts_actions} WHERE id=%d', $action_id); $action = db_fetch_object($result); if (!$action) { drupal_goto('admin/store/discounts'); } } return $action; } /** * Fetch a list of discounts * * @param $get_all * If false, only retrieve current active discounts, based on start and * end time * @return * array of discounts */ function uc_discounts_get_discounts($get_all = FALSE) { if ($get_all) { $result = db_query("SELECT * FROM {uc_discounts} ORDER BY weight"); } else { $now = time(); $result = db_query( "SELECT * FROM {uc_discounts} WHERE start_time <= %d AND end_time >= %d ORDER BY weight", $now, $now ); } $discounts = array(); while ($row = db_fetch_object($result)) { $discounts[$row->id] = $row; } return $discounts; } /** * Retrieve the conditions for a given discount. * * @param $discount_id * @return * array of conditions */ function uc_discounts_get_discount_conditions($discount_id) { $sql = "SELECT * FROM {uc_discounts_conditions} WHERE discount_id=%d "; $sql .= "ORDER BY condition_group, weight"; $result = db_query($sql, $discount_id); $conditions = array(); while ($row = db_fetch_object($result)) { $conditions[$row->id] = $row; } return $conditions; } /** * Retrieve the actions for a given discount. * * @param $discount_id * @return * array of actions */ function uc_discounts_get_discount_actions($discount_id) { $sql = "SELECT * FROM {uc_discounts_actions} WHERE discount_id=%d "; $sql .= "ORDER BY weight"; $result = db_query($sql, $discount_id); $actions = array(); while ($row = db_fetch_object($result)) { $actions[$row->id] = $row; } return $actions; } /** * Compare two values based on a compare type and comparison operator * * @param $compare_type * the type of comparison, for checking if the $op is valid * @param $property * the value to be checked * @param $op * comparison operator * @param $value * the target value for comparison */ function uc_discounts_compare($compare_type, $property, $op, $value) { switch ($compare_type) { case 'integer': $property = (int)$property; $value = (int)$value; break; case 'numeric': $property = (float)$property; $value = (float)$value; break; case 'money': $property = sprintf('%.2f', $property); $value = sprintf('%.2f', $value); break; case 'date': // for now, assume in correct Ymd format break; // no changes needed for text default: $compare_type = 'text'; } // "not equal" is not valid for numeric comparisons $valid_compare_types = _uc_discounts_get_comparisons($compare_type); if (!in_array($op, $valid_compare_types)) { drupal_set_message("Illegal $compare_type comparison operator '$op' used", 'error'); return FALSE; } switch ($op) { case '>': return ($property > $value); case '>=': return ($property >= $value); case '<': return ($property < $value); case '<=': return ($property <= $value); case '=': return ($property == $value); case '!=': return ($property != $value); default: drupal_set_message('Unknown comparison operator '.check_plain($op).' used', 'error'); return FALSE; } } /** * Load all of the conditions provided by additional discount modules * * @return array */ function _uc_discounts_get_conditions() { static $conditions = NULL; if ($conditions === NULL) { $conditions = array(); $condition_list = module_invoke_all('discounts_condition'); foreach ($condition_list as $condition_info) { $property = $condition_info['property']; $conditions[$property] = $condition_info; } } return $conditions; } /** * Return an array of condition property names and descriptions for use as * options in a select form field. */ function _uc_discounts_get_condition_form_values() { $condition_list = _uc_discounts_get_conditions(); $form_values = array(); foreach ($condition_list as $condition) { $condition_name = $condition['property']; $form_values[$condition_name] = $condition['description']; asort($form_values); } return $form_values; } /** * Load all of the actions provided by additional discount modules * * @return array */ function _uc_discounts_get_actions() { static $actions = NULL; if ($actions === NULL) { $actions = array(); $action_list = module_invoke_all('discounts_action'); foreach ($action_list as $action_info) { $property = $action_info['property']; $actions[$property] = $action_info; } } return $actions; } /** * Return an array of action property names and descriptions for use as * options in a select form field. */ function _uc_discounts_get_action_form_values() { $action_list = _uc_discounts_get_actions(); $form_values = array(); foreach ($action_list as $action) { $action_name = $action['property']; $form_values[$action_name] = $action['description']; asort($form_values); } return $form_values; } /** * return a list of valid comparison operators for the compare type * * The generated array is suitable for use in a select form field */ function _uc_discounts_get_comparisons($compare_type) { switch ($compare_type) { case 'integer': case 'numeric': case 'money': case 'date': $valid_compare_types = array( '<' => '<', '<=' => '<=', '>' => '>', '>=' => '>=', '=' => '=', ); break; // default type is text default: $valid_compare_types = array( '=' => '=', '!=' => '!=', ); } return $valid_compare_types; }