Index: uc_discounts.module =================================================================== --- uc_discounts.module (revision 449) +++ uc_discounts.module (revision 487) @@ -371,6 +374,8 @@ // get and store total price $total_price = 0; foreach ($cart_copy as $product) { + $n = node_load($product->nid); + $product->type = $n->type; $total_price += $product->price * $product->qty; } @@ -382,6 +387,8 @@ $action_info = _uc_discounts_get_actions(); foreach ($discount_list as $discount) { +#drupal_set_message("testing discount:(".$discount->id.") ".$discount->name); + $discount_amount = 0; // skip discounts which may not be applied with other discounts if ($discount->is_exclusive && !empty($apply_discounts)) { continue; @@ -389,17 +396,40 @@ // load conditions for the discount $condition_list = uc_discounts_get_discount_conditions($discount->id); $condition_groups = array(); + $condition_counts = array(); foreach ($condition_list as $condition) { + +#drupal_set_message("checking condition:(".$condition->id.") ".$condition->condition_group.": ".$condition_info[$condition->property]['check_callback']); // skip if this condition group has already failed - if (isset($condition_groups[$condition->condition_group]) && $condition_groups[$condition->condition_group] == FALSE) { - continue; - } +# 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); + +#drupal_set_message("condition group status: ".$condition_groups[$condition->condition_group]); } + + if (isset($condition_info[$condition->property]['count_callback'])) { + $count_callback = $condition_info[$condition->property]['count_callback']; + $condition_counts[$condition->condition_group] += $count_callback($condition, $cart_copy); + } + + foreach ($condition_groups as $i => $check) { + $c = isset($condition_counts[$i])?$condition_counts[$i]:0; + + if ($c == 0) { $condition_groups[$i] = FALSE; } + elseif ($condition->op == '=' && $c >= $condition->value) { $condition_groups[$i] = TRUE; } + elseif ($condition->op == '!=' && $c != $condition->value) { $condition_groups[$i] = TRUE; } + elseif ($condition->op == '>=' && $c >= $condition->value) { $condition_groups[$i] = TRUE; } + elseif ($condition->op == '>' && $c > $condition->value) { $condition_groups[$i] = TRUE; } + elseif ($condition->op == '<=' && $c <= $condition->value) { $condition_groups[$i] = TRUE; } + elseif ($condition->op == '<' && $c < $condition->value) { $condition_groups[$i] = TRUE; } + else { $condition_groups[$i] = FALSE; } + } } // did any condition groups match @@ -409,8 +439,10 @@ if (!isset($action_info[$action->property]['apply_callback'])) { continue; } +#drupal_set_message("DISCOUNTS: applying action(".$action->id.") ".$action_info[$action->property]['apply_callback']); $apply_callback = $action_info[$action->property]['apply_callback']; - $discount_amount = $apply_callback($op, $action, $total_price, $cart_copy); + $discount_amount += $apply_callback($op, $action, $total_price, $cart_copy); +#drupal_set_message("DISCOUNTS: discount amount: $discount_amount"); if ($discount_amount > 0) { $apply_discounts[$discount->id] = array( 'description' => check_plain($discount->description), @@ -842,6 +874,14 @@ $item = ''; } + // get product type + if (isset($condition_info[$condition->property]['product_class_callback'])) { + $product_class_callback = $condition_info[$condition->property]['product_class_callback']; + $class = $product_class_callback($condition->item_id); + } else { + $class = ''; + } + // format value if (isset($condition_info[$condition->property]['value_format_callback'])) { $value_format_callback = $condition_info[$condition->property]['value_format_callback']; @@ -1047,12 +1087,12 @@ 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'])) { + if (!ctype_digit($form_values['value'])) { form_set_error('value', t('Value must be an integer')); } break; case 'numeric': - if (!is_numeric($form_values['values'])) { + if (!is_numeric($form_values['value'])) { form_set_error('value', t('Value must be a number')); } break; @@ -1079,13 +1119,13 @@ $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)"; + VALUES (%d, %d, '%s', '%s', '%s', '%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"; + item_id='%s', value='%s', weight=%d WHERE id=%d"; } db_query($sql, @@ -1155,6 +1195,14 @@ $item = $action->item_id; } + // get class name + if (isset($action_info[$action->property]['product_class_callback'])) { + $class_name_callback = $action_info[$action->property]['product_class_callback']; + $class = $class_name_callback($action->item_id); + } else { + $class = $action->item_id; + } + // format amount if (isset($action_info[$action->property]['amount_callback'])) { $amount_callback = $action_info[$action->property]['amount_callback']; @@ -1500,7 +1548,7 @@ else { $now = time(); $result = db_query( - "SELECT * FROM {uc_discounts} WHERE start_time <= %d AND end_time >= %d ORDER BY weight", + "SELECT * FROM {uc_discounts} WHERE start_time <= %d AND end_time >= %d AND is_active = 1 ORDER BY weight", $now, $now ); } Index: uc_discounts.install =================================================================== --- uc_discounts.install (revision 449) +++ uc_discounts.install (revision 487) @@ -26,7 +26,7 @@ `condition_group` smallint NOT NULL default '1', `property` varchar(255) NOT NULL, `op` char(2) NOT NULL, - `item_id` int, + `item_id` varchar(32), `value` text NOT NULL, `weight` int NOT NULL, PRIMARY KEY(`id`), @@ -36,7 +36,7 @@ `id` int NOT NULL, `discount_id` int NOT NULL, `property` varchar(255) NOT NULL, - `item_id` int, + `item_id` varchar(32), `qty` smallint NOT NULL default '1', `amount` varchar(8) NOT NULL, `weight` int NOT NULL, @@ -53,3 +53,9 @@ db_query("DROP TABLE IF EXISTS {uc_discounts_actions}"); } +function uc_discounts_update_1() { + $items = array(); + $items[] = update_sql("ALTER TABLE {uc_discounts_conditions} modify item_id varchar(32) default NULL"); + $items[] = update_sql("ALTER TABLE {uc_discounts_actions} modify item_id varchar(32) default NULL"); + return $items; +} Index: discounts/uc_discounts_product_class.info =================================================================== --- discounts/uc_discounts_product_class.info (revision 0) +++ discounts/uc_discounts_product_class.info (revision 487) @@ -0,0 +1,6 @@ +; $Id$ +name = "Product Type" +description = "Discounts on product classes" +dependencies = "uc_discounts" +package = "Ubercart - discounts" + Index: discounts/uc_discounts_product.module =================================================================== --- discounts/uc_discounts_product.module (revision 449) +++ discounts/uc_discounts_product.module (revision 487) @@ -8,8 +8,9 @@ $conditions[] = array( 'property' => 'product', 'description' => t('Product'), - 'compare_type' => 'text', + 'compare_type' => 'integer', 'check_callback' => 'uc_discounts_product_check', + 'count_callback' => 'uc_discounts_product_count', 'item_name_callback' => 'uc_discounts_product_item_name', 'item_field_callback' => 'uc_discounts_product_item_field', 'value_field_callback' => 'uc_discounts_product_value_field', @@ -38,24 +39,40 @@ function uc_discounts_product_check($condition, $total_price, $cart) { $product_count = 0; foreach ($cart as $product) { - if ($product->nid = $condition->item_id) { +#drupal_set_message(t("checking condition(%c) item_id(%i) against product(%n) %t",array('%c' => $condition->id,'%n' => $product->nid, '%t' => $product->title,'%i' => $condition->item_id))); + if ($product->nid == intval($condition->item_id)) { $product_count = $product->qty; +#drupal_set_message(t("testing condition(!c) item(!i) value(!v) !o product(!n) qty(!q)",array('!c' => $condition->id, +# '!i' => $condition->item_id, +# '!v' => $condition->value, +# '!o' => $condition->op, +# '!n' => $product->nid, +# '!q' => $product_count))); + break; } - break; } + if ($product_count == 0) { return FALSE; } - elseif ($condition->op == '=' && $product_count >= $condition->qty) { - return TRUE; + elseif ($condition->op == '=' && $product_count >= $condition->value) { return TRUE; } + elseif ($condition->op == '!=' && $product_count != $condition->value) { return TRUE; } + elseif ($condition->op == '>=' && $product_count >= $condition->value) { return TRUE; } + elseif ($condition->op == '>' && $product_count > $condition->value) { return TRUE; } + elseif ($condition->op == '<=' && $product_count <= $condition->value) { return TRUE; } + elseif ($condition->op == '<' && $product_count < $condition->value) { return TRUE; } + else { return FALSE; } +} + +function uc_discounts_product_count($condition, $cart) { + $product_count = 0; + foreach ($cart as $product) { + if ($product->nid == intval($condition->item_id)) { + $product_count = $product->qty; + } } - elseif ($condition->op == '!=' && $product_count == 0) { - return TRUE; - } - else { - return FALSE; - } + return $product_count; } /** @@ -75,7 +92,7 @@ // no function in uc_products to do this. This may need refining later $products = array(); - $result = db_query("SELECT nid, title FROM {node} WHERE type='product' ORDER BY title"); + $result = db_query("SELECT n.nid,n.title FROM {uc_products} p LEFT JOIN {node} n ON n.nid=p.nid ORDER BY title"); while ($row = db_fetch_object($result)) { $products[$row->nid] = check_plain($row->title); } @@ -115,10 +132,17 @@ function uc_discounts_product_apply($op, $action, $total_price, &$cart) { // get conditions to make sure products can't be used more than once for // a discount + $sql = "SELECT * FROM {uc_discounts_conditions} WHERE discount_id=%d "; - $sql .= "AND property='product' AND item_id=%d "; + $sql .= "AND property='product' AND item_id='%s' "; $sql .= "ORDER BY condition_group, weight"; $result = db_query($sql, $action->discount_id, $action->item_id); + +#ob_start(); print_r($action); +#drupal_set_message(" hit uc_discounts_product_apply: "); +#drupal_set_message(ob_get_contents()); ob_end_clean(); +#drupal_set_message(t("[$sql]",array('%d'=> $action->discount_id,'%s' => $action->item_id))); + $condition_list = array(); while ($row = db_fetch_object($result)) { $condition_list[$row->id] = $row; @@ -131,44 +155,59 @@ // make sure condition is satisfied and action is possible $condition_found = FALSE; $product_price = 0; +#drupal_set_message(" stepping through cart items to see if condition is met and action is possible:"); foreach ($cart as $index => $product) { +#ob_start(); print_r($product); +#drupal_set_message("cart product: ".ob_get_contents()); ob_end_clean(); // has the condition already been satisified if (empty($condition_found)) { +#drupal_set_message(" stepping through condition_list:"); foreach ($condition_list as $condition) { - if ($product->nid == $condition->item_id) { - // product id matches condition item +#ob_start(); print_r($condition); +#drupal_set_message("condition: ".ob_get_contents()); ob_end_clean(); + if ($product->nid == intval($condition->item_id)) { + // product id (or type) matches condition item +#drupal_set_message(" condition ".$condition->id."(".$condition->item_id.") matched ".$product->nid." (".$product->title.")"); $required_qty = $condition->value; - if ($condition->item_id == $action->item_id) { + if ($condition->item_id == intval($action->item_id)) { // condition item and action item are the same $product_price = $product->price; - $required_qty += $action->qty; +# $product_found[$index] = $product->qty; +# $product_count += $product->qty; } - if ($product->qty >= $required_qty) { - // save to apply discoounts later - $product_found[$index] = $required_qty; - $condition_found = TRUE; - break; - } + + #if ($product->qty >= $required_qty) { + // save to apply discounts later + # $condition_found = TRUE; + # break; + #} } } - } + } // foreach $condition + if ($product_price && $condition_found) { // condition and action have been found +#drupal_set_message(" condition and action are satisfied.. applying discount:"); break; } if ($product->nid == $action->item_id) { - if ($product->qty >= $action->qty) { - $product_found[$index] = $action->qty; +#drupal_set_message("product nid matches ACTION id"); +#ob_start(); print_r($product); +#drupal_set_message(ob_get_contents()); ob_end_clean(); + #if ($product->qty >= $action->qty) { + $product_found[$index] = $product->qty; + $product_count += $product->qty; $product_price = $product->price; continue; - } + #} } - } + } // foreach $product - if (empty($condition_found) || empty($product_price)) { + if (empty($product_price)) { return 0; } +#drupal_set_message(" calculating discount amount.."); // calculate discount amount if (substr($action->amount, -1) == '%') { $discount_percent = substr($action->amount, 0, -1) / 100; @@ -190,9 +229,9 @@ // do not allow discounting greater than the product price $discount_amount = $product_price; } - $discount = $discount_amount * $action->qty; + $discount = $discount_amount * $product_count; +#drupal_set_message("discount_amount: $discount_amount * product_count: ".$product_count); } - // no refunds if ($discount > $total_price) { $discount = $total_price; @@ -204,4 +243,4 @@ } return $discount; -} \ No newline at end of file +} Index: discounts/uc_discounts_product_class.module =================================================================== --- discounts/uc_discounts_product_class.module (revision 0) +++ discounts/uc_discounts_product_class.module (revision 487) @@ -0,0 +1,198 @@ + 'product_class', + 'description' => t('Product Type'), + 'compare_type' => 'integer', + 'check_callback' => 'uc_discounts_product_class_check', + 'product_class_callback' => 'uc_discounts_product_class_class_name', + 'item_name_callback' => 'uc_discounts_product_class_item_name', + 'item_field_callback' => 'uc_discounts_product_class_item_field', + 'value_field_callback' => 'uc_discounts_product_class_value_field', + ); + + return $conditions; +} + +function uc_discounts_product_class_discounts_action() { + $actions[] = array( + 'property' => 'product_class_items', + 'description' => t('Discount all products of a type from order'), + 'has_qty_field' => TRUE, + 'item_field_callback' => 'uc_discounts_product_class_item_field', //recycle + 'product_class_callback' => 'uc_discounts_product_class_class_name', //recycle + 'apply_callback' => 'uc_discounts_product_class_apply', + ); + + return $actions; +} + +/******************************************************************************* + * Callback Functions, Forms, and Tables + ******************************************************************************/ + +function uc_discounts_product_class_check($condition, $total_price, $cart) { + $product_count = 0; + foreach ($cart as $product) { + if ($product->type == $condition->item_id) { + $product_count += $product->qty; + } + } + + if ($product_count == 0) { + return FALSE; + } + elseif ($condition->op == '=' && $product_count >= $condition->value) { + return TRUE; + } + elseif ($condition->op == '!=' && $product_count != $conditions->value) { + return TRUE; + } + elseif ($condition->op == '>=' && $product_count >= $condition->value) { + return TRUE; + } + elseif ($condition->op == '>' && $product_count > $condition->value) { + return TRUE; + } + elseif ($condition->op == '<=' && $product_count <= $condition->value) { + return TRUE; + } + elseif ($condition->op == '<' && $product_count < $condition->value) { + return TRUE; + } + else { + return FALSE; + } +} + +/** + * Given a product id (nid), return the product name + */ +function uc_discounts_product_class_class_name($product_id) { + $result = db_query('SELECT type FROM {node} WHERE nid=%d', $product_id); + $product_name = check_plain(db_result($result)); + return $product_name; +} + +function uc_discounts_product_class_item_name($class_id) { + $result = db_query("SELECT name FROM {uc_product_classes} WHERE pcid='%s'", $class_id); + $class_name = check_plain(db_result($result)); + return $class_name; +} + +/** + * Generate the condition item field + */ +function uc_discounts_product_class_item_field() { + // load list of products from the database + // no function in uc_products to do this. This may need refining later + $classes = array(); + + $result = db_query("SELECT pcid,name FROM {uc_product_classes} ORDER BY pcid"); + while ($row = db_fetch_object($result)) { + $classes[$row->pcid] = check_plain($row->name); + } + + if (empty($classes)) { + $field = array( + '#type' => 'item', + '#title' => t('Product Type'), + '#description' => t('No product classes available'), + ); + } + else { + $field = array( + '#type' => 'select', + '#title' => t('Product Types'), + '#options' => $classes, + ); + } + + return $field; +} + +/** + * Generate the condition value field + */ +function uc_discounts_product_class_value_field() { + $field = array( + '#type' => 'textfield', + '#title' => t('Quantity'), + '#maxlength' => 12, + '#size' => 6, + '#required' => TRUE, + ); + return $field; +} + +function uc_discounts_product_class_apply($op, $action, $total_price, &$cart) { + // get conditions to make sure products can't be used more than once for + // a discount + $sql = "SELECT * FROM {uc_discounts_conditions} WHERE discount_id=%d "; + $sql .= "AND property='product_class' AND item_id='%s' "; + $sql .= "ORDER BY condition_group, weight"; + $result = db_query($sql, $action->discount_id, $action->item_id); + $condition_list = array(); + while ($row = db_fetch_object($result)) { + $condition_list[$row->id] = $row; + } + + if (empty($condition_list)) { + return 0; + } + + // make sure condition is satisfied and action is possible + $product_price = 0; + foreach ($cart as $index => $product) { + if ($product->nid == $action->item_id || $product->type == $action->item_id) { + $product_count += $product->qty; + $product_price = $product->price; + $product_found[$index] = $product->qty; + } + } // foreach product + + if (empty($product_count) || empty($product_price)) { + return 0; + } + + // calculate discount amount + if (substr($action->amount, -1) == '%') { + $discount_percent = substr($action->amount, 0, -1) / 100; + if ($discount_percent > 1) { + // invalid percentage + drupal_set_message(t('Cannot apply product discount greater than 100%', 'error')); + $discount_percent = 0; + } + $discount = $product_price * ($discount_percent * $product_count); + } + else { + if (substr($action->amount, 0, 1) == '$') { + $discount_amount = substr($action->amount, 1); + } + else { + $discount_amount = $action->amount; + } + if ($discount_amount > $product_price) { + // do not allow discounting greater than the product price + $discount_amount = $product_price; + } + $discount = $discount_amount * $product_count; + } + + // no refunds + if ($discount > $total_price) { + $discount = $total_price; + } + + // remove affected items from cart + foreach ($product_found as $cart_index => $product_count) { + $cart[$cart_index]->qty -= $product_count; + } + + return $discount; +}