Index: contrib/coupon/coupon.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/ecommerce/contrib/coupon/coupon.install,v
retrieving revision 1.2.4.4.2.3
diff -u -p -r1.2.4.4.2.3 coupon.install
--- contrib/coupon/coupon.install	15 Feb 2007 06:04:32 -0000	1.2.4.4.2.3
+++ contrib/coupon/coupon.install	4 Oct 2007 00:40:39 -0000
@@ -14,9 +14,12 @@ function coupon_install() {
           coupon varchar(20) NOT NULL default '',
           operator enum('+','-','%') NOT NULL default '+',
           operand decimal(10,2) NOT NULL default '0.00',
+          type enum('c','d') NOT NULL default 'c',
           created int(10) NOT NULL default '0',
           created_txnid int(10) unsigned default NULL,
           created_vid int(10) unsigned default NULL,
+          active int(10) NOT NULL default 1,
+          nid int(10) unsigned default NULL,
           PRIMARY KEY  (coupon),
           KEY created_txnid (created_txnid, created_vid)
       ) TYPE=MyISAM /*!40100 DEFAULT CHARACTER SET utf8 */;");
@@ -32,9 +35,12 @@ function coupon_install() {
           coupon varchar(20) NOT NULL default '',
           operator varchar(1) check (operator in ('+','-','*')) NOT NULL default '+',
           operand decimal(10,2) NOT NULL default '0.00',
+          type varchar(1) check (operator in ('c','d')) NOT NULL default 'c',
           created integer NOT NULL default '0',
           created_txnid integer default NULL,
           created_vid integer default NULL,
+          nid integer default NULL,
+          active integer NOT NULL default 1,
           PRIMARY KEY  (coupon)
       )");
       db_query("CREATE INDEX {ec_coupon}_created_txnid ON {ec_coupon} (created_txnid, created_vid)");
@@ -72,3 +78,21 @@ function coupon_update_2() {
   $ret[] = update_sql("UPDATE {system} SET weight = 5 WHERE name = 'coupon'");
   return $ret;
 }
+
+function coupon_update_3() {
+  $ret = array();
+  switch ($GLOBALS['db_type']) {
+    case 'mysql':
+    case 'mysqli':
+      $ret[] = update_sql("ALTER TABLE {ec_coupon} ADD type enum('c','d') NOT NULL default 'c'");
+      $ret[] = update_sql("ALTER TABLE {ec_coupon} ADD active int(10) NOT NULL default 1");
+      $ret[] = update_sql("ALTER TABLE {ec_coupon} ADD nid int(10) unsigned default NULL");
+      break;
+    case 'pgsql':
+      $ret[] = update_sql("ALTER TABLE {ec_coupon} ADD type varchar(1) check (operator in ('c','d')) NOT NULL default 'c'");
+      $ret[] = update_sql("ALTER TABLE {ec_coupon} ADD active integer NOT NULL default 1");
+      $ret[] = update_sql("ALTER TABLE {ec_coupon} ADD nid integer default NULL");
+      break;
+  }
+  return $ret;
+}
Index: contrib/coupon/coupon.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/ecommerce/contrib/coupon/coupon.module,v
retrieving revision 1.6.2.2.2.8.2.14
diff -u -p -r1.6.2.2.2.8.2.14 coupon.module
--- contrib/coupon/coupon.module	26 Jul 2007 23:23:03 -0000	1.6.2.2.2.8.2.14
+++ contrib/coupon/coupon.module	4 Oct 2007 00:40:39 -0000
@@ -37,13 +37,21 @@ function coupon_menu($may_cache) {
     );
     $items[] = array(
       'path' => 'admin/store/coupon/create',
-      'title' => t('Create'),
+      'title' => t('Create single use coupon'),
       'callback' => 'drupal_get_form',
       'callback arguments' => array('coupon_admin_create'),
       'type' => MENU_LOCAL_TASK,
       'weight' => 1,
     );
     $items[] = array(
+      'path' => 'admin/store/coupon/create_discount_code',
+      'title' => t('Create reusable coupon'),
+      'callback' => 'drupal_get_form',
+      'callback arguments' => array('coupon_admin_create_discount_code'),
+      'type' => MENU_LOCAL_TASK,
+      'weight' => 1,
+    );
+    $items[] = array(
       'path' => 'admin/store/coupon/delete',
       'title' => t('Delete coupon'),
       'callback' => 'drupal_get_form',
@@ -65,6 +73,20 @@ function coupon_menu($may_cache) {
       'type' => MENU_NORMAL_ITEM,
       'description' => t('Configure coupons'),
     );
+    $items[] = array(
+      'path' => 'store/coupon/cancel',
+      'title' => 'make coupon inactive',
+      'callback' => 'coupon_inactivate',
+      'access' => user_access('administor store'),
+      'type' => MENU_CALLBACK,
+    );
+    $items[] = array(
+      'path' => 'store/coupon/admin/autocomplete',
+      'title' => t('product autocomplete'),
+      'callback' => 'coupon_autocomplete',
+      'access' => user_access('administor store'),
+      'type' => MENU_CALLBACK,
+    );
   }
   return $items;
 }
@@ -216,7 +238,7 @@ function coupon_checkoutapi(&$txn, $op, 
       }
       $form['new_coupon'] = array(
         '#type' => 'textfield',
-        '#title' => t('Coupon/Gift Certificate Number'),
+        '#title' => t('Discount Code/Coupon/Gift Certificate Number'),
         '#size' => 20,
         '#maxlength' => 20,
       );
@@ -236,9 +258,15 @@ function coupon_checkoutapi(&$txn, $op, 
           form_set_error('new_coupon', t('Coupon/Gift Certificate has already been added to this order'));
         }
         elseif ($coupon = db_fetch_object(db_query("SELECT c.*, tc.txnid, SUM(tc.discount) as coupon_discount FROM {ec_coupon} c LEFT JOIN {ec_transaction_coupon} tc ON c.coupon = tc.coupon WHERE c.coupon = '%s' GROUP BY c.coupon, c.operator, c.operand, c.created, c.created_txnid, c.created_vid, tc.txnid", $new_coupon))) {
-          if (($coupon->operator == '%' && $coupon->txnid) || ($coupon->txnid && ($coupon->operand * ($coupon->operator == '+' ? 1 : -1)) <= (float)$coupon->coupon_discount)) {
+          //if (($coupon->operator == '%' && $coupon->txnid) || ($coupon->txnid && ($coupon->operand * ($coupon->operator == '+' ? 1 : -1)) <= (float)$coupon->coupon_discount)) {
+          if ($coupon->active != 1) {
             form_set_error('new_coupon', $error);
           }
+          elseif ($coupon->type == 'c') {
+            if (($coupon->operator == '%' && $coupon->txnid) || ($coupon->txnid && ($coupon->operand * ($coupon->operator == '+' ? 1 : -1)) <= (float)$coupon->coupon_discount)) {
+              form_set_error('new_coupon', $error);
+            }
+          }
         }
         else {
           form_set_error('new_coupon', $error);
@@ -267,9 +295,26 @@ function coupon_checkoutapi(&$txn, $op, 
         foreach ($txn->coupon as $key => $value) {
           if ($coupon = db_fetch_object(db_query("SELECT c.*, tc.txnid, SUM(tc.discount) as coupon_discount FROM {ec_coupon} c LEFT JOIN {ec_transaction_coupon} tc ON c.coupon = tc.coupon WHERE c.coupon = '%s' GROUP BY c.coupon, c.operator, c.operand, c.created, c.created_txnid, c.created_vid, tc.txnid", str_replace('-', '', $value['coupon'])))) {
             $discount = 0;
+            if ($coupon->type == 'd') {
+              $coupon->coupon_discount = 0;
+            }
+            if ($coupon->is_specific = is_numeric($coupon->nid)) {
+              $coupon->applies = is_object($txn->items[$coupon->nid]);
+            }
             if ($coupon->operator == '%') {
+              if ($coupon->is_specific) {
+                if ($coupon->applies) {
+                  $discount_items = array($txn->items[$coupon->nid]);
+                }
+                else {
+                  $discount_items = array();
+                }
+              }
+              else {
+                $discount_items = $txn->items;
+              }
               $points = $coupon->operand/100;
-              foreach ($txn->items as $item) {
+              foreach ($discount_items as $item) {
                 if (function_exists('bcadd')) {
                   $discount = bcadd($discount, bcmul($item->price * $item->qty, (float)$points, 2), 2);
                 }
@@ -279,8 +324,21 @@ function coupon_checkoutapi(&$txn, $op, 
               }  
             }
             else {
-              $discount = $coupon->operator == '+' ? $coupon->operand : $coupon->operand*-1;
-              $discount_avaliable = $discount - $coupon->coupon_discount;
+              //$discount = $coupon->operator == '+' ? $coupon->operand : $coupon->operand*-1;
+              //$discount_avaliable = $discount - $coupon->coupon_discount;
+              if ($coupon->is_specific) {
+                if ($coupon->applies) {
+                  $discount = $txn->items[$coupon->nid]->qty * ($coupon->operator == '+' ? $coupon->operand : $coupon->operand * -1);
+                  $discount_avaliable = $txn->items[$coupon->nid]->price * $txn->items[$coupon->nid]->qty;
+                }
+                else {
+                  $discount_avaliable = 0;
+                }
+              }
+              else {
+                $discount = $coupon->operator == '+' ? $coupon->operand : $coupon->operand*-1;
+                $discount_avaliable = $discount - $coupon->coupon_discount;
+              }
               if ($discount > $discount_avaliable) {
                 $discount = $discount_avaliable;
               }
@@ -413,6 +471,8 @@ function coupon_admin_list($created = nu
       array('data' => t('Discount Available')),
       array('data' => t('Created'), 'field' => 'created'),
       array('data' => t('Transaction'), 'field' => 'created_txnid'),
+      array('data' => t('Active')),
+      array('data' => t('Applies to node'), 'field' => 'nid'),
       array(),
     );
   }
@@ -434,12 +494,13 @@ function coupon_admin_list($created = nu
     else {
       $rows[] = array(
         array('data' => _coupon_format($coupon->coupon)),
-        array('data' => ($coupon->operator == '%' ? "{$coupon->operand}% discount" : "\${$coupon->operator}{$coupon->operand} discount")),
+        array('data' => ($coupon->operator == '%' ? "{$coupon->operand}% " : "\${$coupon->operator}{$coupon->operand} ") . ($coupon->type == 'c' ? 'coupon' : 'discount')),
         array('data' => payment_format($coupon->coupon_discount), 'align' => 'right'),
         array('data' => $coupon->operator != '%' ? payment_format(($coupon->operand * ($coupon->operator == '+' ? 1 : -1)) - $coupon->coupon_discount) : '', 'align' => 'right'),
         array('data' => format_date($coupon->created, 'small')),
         array('data' => user_access('administor store') ? l($coupon->created_txnid, "store/transaction/view/{$coupon->created_txnid}") : $coupon->created_txnid),
-        array('data' => !$method && !$coupon->created_txnid ? l(t('Delete'), 'admin/store/coupon/delete/'. $coupon->coupon) : ''),
+        array('data' => $coupon->active == 1 ? 'active '. l('(cancel)', "store/coupon/cancel/{$coupon->coupon}") : 'cancelled '. l('(delete)', "admin/store/coupon/delete/{$coupon->coupon}")),
+        array('data' => l($coupon->nid, 'node/'. $coupon->nid)),
       );
     }
   }
@@ -492,6 +553,43 @@ function coupon_admin_create() {
   return $form;
 }
 
+/**
+ * Create Discount Code (Reusable coupon)
+ */
+function coupon_admin_create_discount_code() {
+  $form['coupon_name'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Name of Discount Code'),
+    '#size' => 12,
+    '#maxlength' => 30,
+    '#description' => t('Enter an alphanumeric discount code name'),
+  );
+  $form['coupon_value'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Value of Discount Code'),
+    '#size' => 12,
+    '#maxlength' => 12,
+    '#description' => t('Enter the value of the discount code to be created. eg. 10%, a set value of 5.00'),
+  );
+  $form['coupon_nid'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Only apply to specific product'),
+    '#size' => 30,
+    '#maxlength' => 300,
+    '#autocomplete_path' => 'store/coupon/admin/autocomplete',
+    '#description' => t('Enter the node id for the product that the discount applies to.  Leave it blank to apply to all nodes.'),
+    );
+  $form['coupon_output'] = array(
+    '#type' => 'value',
+    '#value' => t('Show on Page'),
+  );
+  $form[] = array(
+    '#type' => 'submit',
+    '#value' => t('Create Discount Code'),
+  );
+  return $form;
+}
+
 function coupon_admin_create_validate($form_id, $form_values) {
   if (!$form_values['coupon_value']) {
     form_set_error('coupon_value', t('Coupon Value is a required field'));
@@ -505,6 +603,30 @@ function coupon_admin_create_validate($f
   }
 }
 
+function coupon_admin_create_discount_code_validate($form_id, $form_values) {
+  if (!ereg("^([a-zA-Z0-9]+)$", $form_values['coupon_name'])) {
+    form_set_error('coupon_name', t('Name must be alphanumeric with no spaces'));
+  }
+  else {
+    $result = db_query("SELECT * FROM {ec_coupon} WHERE coupon = '%s'", $form_values['coupon_name']);
+    if (db_num_rows($result) >0) {
+      form_set_error('coupon_name', t('That name is already in use for a coupon/discount code'));
+    }
+  }
+  if (!$form_values['coupon_value']) {
+    form_set_error('coupon_value', t('Coupon Value is a required field'));
+  }
+  elseif ($form_values['coupon_value'] && !preg_match('/^[+]?[\d]*([.][\d]{0,2})?[%]?$/i', $form_values['coupon_value'])) {
+    form_set_error('coupon_value', t('Coupon Value is not in a valid format'));
+  }
+  if (($form_values['coupon_nid'] !== '')) {
+    $result = db_query("SELECT title from {node} WHERE type = '%s' AND title = '%s'", 'product', $form_values['coupon_nid']);
+    if (db_num_rows($result) == 0) {
+      form_set_error('coupon_nid', t('Node must be a name of a product'));
+    }
+  }
+}
+
 function coupon_admin_create_submit($form_id, $form_values) {
   $coupon = array(
     'created' => time()
@@ -526,6 +648,36 @@ function coupon_admin_create_submit($for
   return "admin/store/coupon/{$coupon['created']}/{$form_values['coupon_output']}";
 }
 
+function coupon_admin_create_discount_code_submit($form_id, $form_values) {
+  $coupon = array(
+    'created' => time()
+  );
+  preg_match('/^([+]?)([\d]*([.][\d]{0,2})?)([%]?)$/i', $form_values['coupon_value'], $match);
+  if ($match[4] == '%') {
+    $coupon['operator'] = '%';
+  }
+  else {
+    $coupon['operator'] = $match[1] == '-' ? '-' : '+';
+  }
+  $coupon['operand'] = (float)$match[2];
+
+  $coupon['coupon'] = $form_values['coupon_name'];
+
+  $coupon['nid'] = $form_values['coupon_nid'];
+
+  if ($coupon['nid'] === '') {
+    db_query("INSERT INTO {ec_coupon} (coupon, operator, operand, created, type) VALUES ('%s', '%s', %d, %d, '%s')", $coupon['coupon'], $coupon['operator'], $coupon['operand'], $coupon['created'], 'd');
+  }
+  else {
+    $result = db_query("SELECT nid from {node} WHERE type = '%s' AND title = '%s'", 'product', $coupon['nid']);
+    $nid_obj = db_fetch_object($result);
+    $coupon['nid'] = $nid_obj->nid;
+    db_query("INSERT INTO {ec_coupon} (coupon, operator, operand, created, type, nid) VALUES ('%s', '%s', %d, %d, '%s', %d)", $coupon['coupon'], $coupon['operator'], $coupon['operand'], $coupon['created'], 'd', $coupon['nid']);
+  }
+
+  return "admin/store/coupon/{$coupon['created']}/{$form_values['coupon_output']}";
+}
+
 /**
  * Delete coupons from the database
  */
@@ -552,6 +704,21 @@ function coupon_admin_delete_submit($for
   return 'admin/store/coupon';
 }
 
+function coupon_inactivate($coupon_name) {
+    db_query("UPDATE {ec_coupon} SET active = 0 WHERE coupon = '%s'", $coupon_name);
+    drupal_goto('admin/store/coupon');
+}
+
+function coupon_autocomplete($string) {
+  $matches = array();
+  $result = db_query_range("SELECT title FROM {node} WHERE type = 'product' AND LOWER(title) LIKE LOWER('%s%%')", $string, 0, 10);
+  while ($product = db_fetch_object($result)) {
+    $matches[$product->title] = check_plain($product->title);
+  }
+  print drupal_to_js($matches);
+  exit();
+}
+
 /**
  * private functions
  */
