=== modified file 'uc_product_kit/uc_product_kit.install'
--- uc_product_kit/uc_product_kit.install	2009-01-19 19:41:59 +0000
+++ uc_product_kit/uc_product_kit.install	2009-02-26 19:29:22 +0000
@@ -51,6 +51,12 @@
         'not null' => TRUE,
         'default' => 0,
       ),
+      'synchronized' => array(
+        'type' => 'int',
+        'size' => 'tiny',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
     ),
     'primary key' => array('vid', 'product_id'),
   );
@@ -173,3 +179,16 @@
 
   return $ret;
 }
+
+function uc_product_kit_update_6002() {
+  $ret = array();
+
+  if (!db_column_exists('uc_product_kits', 'synchronized')) {
+    db_add_field($ret, 'uc_product_kits', 'synchronized', array('type' => 'int', 'size', 'tiny', 'not null' => TRUE, 'default' => 0));
+    $ret[] = update_sql("UPDATE {uc_product_kits} SET synchronized = 1 WHERE discount < 0");
+    $ret[] = update_sql("UPDATE {uc_product_kits} AS pk JOIN {uc_products} AS p ON pk.product_id = p.nid SET pk.discount = pk.discount - p.sell_price WHERE pk.discount >= 0");
+  }
+
+  return $ret;
+}
+

=== modified file 'uc_product_kit/uc_product_kit.module'
--- uc_product_kit/uc_product_kit.module	2009-02-18 13:51:39 +0000
+++ uc_product_kit/uc_product_kit.module	2009-02-26 19:22:45 +0000
@@ -140,7 +140,9 @@
     $values[] = $node->mutable;
     $values[] = 1;
     $values[] = $product->sell_price;
-    $placeholders[] = '(%d, %d, %d, %d, %d, %f)';
+    $values[] = 0;
+    $values[] = 1;
+    $placeholders[] = '(%d, %d, %d, %d, %d, %f, %d, %d)';
 
     $obj->model .= $product->model .' / ';
     $obj->list_price += $product->list_price;
@@ -151,7 +153,7 @@
       $obj->shippable = TRUE;
     }
   }
-  db_query("INSERT INTO {uc_product_kits} (vid, nid, product_id, mutable, qty, discount) VALUES ". implode(',', $placeholders), $values);
+  db_query("INSERT INTO {uc_product_kits} (vid, nid, product_id, mutable, qty, discount, ordering, synchronized) VALUES ". implode(',', $placeholders), $values);
   $obj->model = rtrim($obj->model, ' / ');
   db_query("INSERT INTO {uc_products} (vid, nid, model, list_price, cost, sell_price, weight, weight_units, default_qty, unique_hash, ordering, shippable) VALUES (%d, %d, '%s', %f, %f, %f, %f, '%s', %d, '%s', %d, %d)",
     $obj->vid, $obj->nid, $obj->model, $obj->list_price, $obj->cost, $obj->sell_price, $obj->weight, $obj->weight_units, $obj->default_qty,
@@ -183,6 +185,23 @@
   $obj->ordering = $node->ordering;
   $obj->shippable = FALSE;
 
+  $override_discounts = isset($node->kit_total) && is_numeric($node->kit_total);
+  $product_count = count($node->products);
+
+  // Get the price of all the products without any discounts. This number is
+  // used if a total kit price was specified to calculate the individual
+  // product discounts.
+  if ($override_discounts) {
+    $base_price = 0;
+    foreach ($node->products as $nid) {
+      $product = node_load($nid, NULL, TRUE);
+      if (is_null($node->items[$nid]['qty']) || $node->items[$nid]['qty'] === '') {
+        $node->items[$nid]['qty'] = 1;
+      }
+      $base_price += $product->sell_price * $node->items[$nid]['qty'];
+    }
+  }
+
   $values = array();
   $placeholders = array();
   foreach ($node->products as $nid) {
@@ -190,26 +209,35 @@
     $values[] = $node->nid;
     $values[] = $nid;
     $values[] = $node->mutable;
-    $product = node_load($nid, NULL, TRUE);
+    $product = node_load($nid);
+
+    // When a total kit price is specified, calculate the individual product
+    // discounts needed to reach it, taking into account the product quantities
+    // and their relative prices. More expensive products should be given a
+    // proportionally higher discount.
+    if ($override_discounts) {
+      $price = $product->sell_price;
+      // After all the algebra that went into finding this formula, it's
+      // surprising how simple it is.
+      $discount = ($node->kit_total - $base_price) * $price / $base_price;
+    }
+    else {
+      $discount = $node->items[$nid]['discount'];
+    }
 
     if (is_null($node->items[$nid]['qty']) || $node->items[$nid]['qty'] === '') {
       $node->items[$nid]['qty'] = 1;
     }
-    $values[] = $node->items[$nid]['qty'];
-    $values[] = $node->items[$nid]['discount'];
+
+    $product->qty = $node->items[$nid]['qty'];
+    $product->sell_price += $discount;
+
+    $values[] = $product->qty;
+    $values[] = $discount;
     $values[] = $node->items[$nid]['ordering'];
-    $placeholders[] = '(%d, %d, %d, %d, %d, %f, %d)';
+    $values[] = !$override_discounts;
+    $placeholders[] = '(%d, %d, %d, %d, %d, %f, %d, %d)';
 
-    $product->qty = $node->items[$nid]['qty'];
-    if (is_numeric($node->items[$nid]['discount'])) {
-      if ($node->items[$nid]['discount'] < 0) {
-        $product->sell_price += $node->items[$nid]['discount'];
-      }
-      else {
-        $product->sell_price = $node->items[$nid]['discount'];
-      }
-    }
-    $obj->model .= $product->model .' / ';
     $obj->list_price += $product->list_price * $product->qty;
     $obj->cost += $product->cost * $product->qty;
     $obj->sell_price += $product->sell_price * $product->qty;
@@ -222,7 +250,7 @@
   if (!$node->revision) {
     db_query("DELETE FROM {uc_product_kits} WHERE nid = %d", $node->nid);
   }
-  db_query("INSERT INTO {uc_product_kits} (vid, nid, product_id, mutable, qty, discount, ordering) VALUES ". implode(',', $placeholders), $values);
+  db_query("INSERT INTO {uc_product_kits} (vid, nid, product_id, mutable, qty, discount, ordering, synchronized) VALUES ". implode(',', $placeholders), $values);
   if ($node->revision) {
     db_query("INSERT INTO {uc_products} (vid, nid, model, list_price, cost, sell_price, weight, weight_units, default_qty, unique_hash, ordering, shippable) VALUES (%d, %d, '%s', %f, %f, %f, %f, '%s', %d, '%s', %d, %d)",
       $obj->vid, $obj->nid, $obj->model, $obj->list_price, $obj->cost, $obj->sell_price, $obj->weight, $obj->weight_units, $obj->default_qty,
@@ -295,14 +323,16 @@
 function uc_product_kit_nodeapi(&$node, $op, $arg3 = NULL, $arg4 = NULL) {
   switch ($op) {
     case 'update':
-      $result = db_query("SELECT DISTINCT nid FROM {uc_product_kits} WHERE product_id = %d", $node->nid);
+      $result = db_query("SELECT DISTINCT nid, synchronized FROM {uc_product_kits} WHERE product_id = %d", $node->nid);
       while ($k = db_fetch_object($result)) {
-        $kit = node_load($k->nid, NULL, TRUE);
-        $form_state = array(
-          'values' => array('nid' => $kit->nid),
-          'submit_handlers' => array('node_form_submit'),
-        );
-        drupal_execute('product_kit_node_form', $form_state, $kit);
+        if ($k->synchronized) {
+          $kit = node_load($k->nid, NULL, TRUE);
+          $form_state = array(
+            'values' => array('nid' => $kit->nid),
+            'submit_handlers' => array('node_form_submit'),
+          );
+          drupal_execute('product_kit_node_form', $form_state, $kit);
+        }
       }
     break;
   }
@@ -332,6 +362,9 @@
 function uc_product_kit_form(&$node) {
   $form = array();
 
+  $sign_flag = variable_get('uc_sign_after_amount', FALSE);
+  $currency_sign = variable_get('uc_currency_sign', '$');
+
   $form['title'] = array('#type' => 'textfield',
     '#title' => t('Name'),
     '#required' => TRUE,
@@ -386,7 +419,9 @@
     '#options' => $products,
     '#default_value' => isset($node->products) ? array_keys($node->products) : array(),
   );
-  $form['base']['items'] = array('#tree' => TRUE);
+  $synchronized = FALSE;
+  $total = 0;
+  $form['base']['items'] = array('#tree' => TRUE, '#weight' => 1);
   if (isset($node->products)) {
     foreach ($node->products as $i => $product) {
       $form['base']['items'][$i] = array('#type' => 'fieldset',
@@ -407,11 +442,28 @@
       $item = node_load($i);
       $form['base']['items'][$i]['discount'] = array('#type' => 'textfield',
         '#title' => t('Discount'),
-        '#description' => t('Enter a negative value to lower the item price by that amount. Enter a postive value to set the item price to that amount. This discount is applied to each %product in the kit.', array('%product' => $product->title)),
-        '#default_value' => (is_null($product->discount) || $product->discount === '' ? $item->sell_price : $product->discount),
+        '#description' => t('Enter a positive or negative value to raise or lower the item price by that amount. This change is applied to each %product in the kit.', array('%product' => $product->title)),
+        '#field_prefix' => t('@price + ', array('@price' => uc_currency_format($product->sell_price))),
+        '#default_value' => isset($product->discount) ? $product->discount : 0,
         '#size' => 5,
       );
+      $total += $product->sell_price * $product->qty;
+      if (isset($product->discount)) {
+        $total += $product->discount * $product->qty;
+      }
+      $synchronized = $product->synchronized;
     }
+    $form['base']['kit_total'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Total price'),
+      '#default_value' => $synchronized ? '' : $total,
+      '#description' => t('If this field is set, the discounts of the individual products will be recalculated to equal this value. Currently, the total sell price is %price.', array('%price' => uc_currency_format($total))),
+      '#weight' => 0,
+      '#size' => 20,
+      '#maxlength' => 35,
+      '#field_prefix' => $sign_flag ? '' : $currency_sign,
+      '#field_suffix' => $sign_flag ? $currency_sign : '',
+    );
   }
   $form['base']['default_qty'] = array('#type' => 'textfield',
     '#title' => t('Default quantity to add to cart'),

