--- uc_discount.ca.inc	Mon Jun 29 19:10:58 2009
+++ uc_discount.ca.inc	Wed Nov 25 18:04:28 2009
@@ -341,7 +341,7 @@
  * Wrapper function for line item discounts.
  */
 function uc_discount_get_line_item_discount($line_item, $settings) {
-  $line_item->amount += uc_discount_get_discount($line_item, $settings);
+  $line_item['amount'] += uc_discount_get_discount($line_item, $settings);
 }
 
 /**
--- uc_discount.js.	Tue Feb 17 22:40:32 2009
+++ uc_discount.js	Wed Nov 25 18:04:28 2009
@@ -1,20 +1,9 @@
 // $Id$
 
 /**
- * Calculate the number of bytes of a Unicode string.
- *
- * Gratefully stolen from http://dt.in.th/2008-09-16.string-length-in-bytes.html.
- * Javascript String.length returns the number of characters, but PHP strlen()
- * returns the number of bytes. When building serialize()d strings in JS,
- * use this function to get the correct string length.
+ * @file
+ * Handle asynchronous requests to calculate discounts.
  */
-String.prototype.bytes = function() {
-  // Drupal.encodeURIComponent() gets around some weirdness in
-  // encodeURIComponent(), but encodes some characters twice. The first
-  // replace takes care of those while the second lets String.length count
-  // the multi-byte characters.
-  return Drupal.encodeURIComponent(this).replace(/%252[36F]/g, 'x').replace(/%../g, 'x').length;
-};
 
 var pane = '';
 if ($("input[@name*=delivery_]").length) {
@@ -37,61 +26,13 @@
   $('#edit-panes-payment-current-total').click(getDiscounts);
 });
 
+/**
+ * Get discounts for the current cart and line items.
+ */
 function getDiscounts() {
-  var products = $("[@name=cart_contents]").val();
-
-  var p_email = $("input[@name*=primary_email]").val() || '';
-  var s_f_name = $("input[@name*=delivery_first_name]").val() || '';
-  var s_l_name = $("input[@name*=delivery_last_name]").val() || '';
-  var s_street1 = $("input[@name*=delivery_street1]").val() || '';
-  var s_street2 = $("input[@name*=delivery_street2]").val() || '';
-  var s_city = $("input[@name*=delivery_city]").val() || '';
-  var s_zone = $("select[@name*=delivery_zone]").val() || '0';
-  var s_code = $("input[@name*=delivery_postal_code]").val() || '';
-  var s_country = $("select[@name*=delivery_country]").val() || '0';
+  var order = serializeOrder();
 
-  var b_f_name = $("input[@name*=billing_first_name]").val() || '';
-  var b_l_name = $("input[@name*=billing_last_name]").val() || '';
-  var b_street1 = $("input[@name*=billing_street1]").val() || '';
-  var b_street2 = $("input[@name*=billing_street2]").val() || '';
-  var b_city = $("input[@name*=billing_city]").val() || '';
-  var b_zone = $("select[@name*=billing_zone]").val() || '0';
-  var b_code = $("input[@name*=billing_postal_code]").val() || '';
-  var b_country = $("select[@name*=billing_country]").val() || '0';
-
-  var order_size = 21;
-  var line_item = '';
-  var key;
-  var i = 0;
-  for (key in li_titles) {
-    if (key != 'subtotal') {
-      line_item = line_item + 'i:' + i + ';a:3:{s:5:"title";s:' + li_titles[key].bytes() + ':"' + li_titles[key] + '";s:4:"type";s:'+ key.bytes() + ':"'+ key + '";s:6:"amount";d:' + li_values[key] + ';}';
-      i++;
-    }
-  }
-  line_item = 's:10:"line_items";a:' + i + ':{' + line_item + '}';
-  var order = 'O:8:"stdClass":' + order_size + ':{s:8:"products";' + products
-    + 's:8:"order_id";i:0;'
-    + 's:3:"uid";i:0;'
-    + 's:13:"primary_email";s:' + p_email.bytes() + ':"' + p_email
-    + '";s:19:"delivery_first_name";s:' + s_f_name.bytes() + ':"' + s_f_name
-    + '";s:18:"delivery_last_name";s:' + s_l_name.bytes() + ':"' + s_l_name
-    + '";s:16:"delivery_street1";s:' + s_street1.bytes() + ':"' + s_street1
-    + '";s:16:"delivery_street2";s:' + s_street2.bytes() + ':"' + s_street2
-    + '";s:13:"delivery_city";s:' + s_city.bytes() + ':"' + s_city
-    + '";s:13:"delivery_zone";i:' + s_zone
-    + ';s:20:"delivery_postal_code";s:' + s_code.bytes() +':"' + s_code
-    + '";s:16:"delivery_country";i:' + s_country + ';'
-    + 's:18:"billing_first_name";s:' + b_f_name.bytes() + ':"' + b_f_name
-    + '";s:17:"billing_last_name";s:' + b_l_name.bytes() + ':"' + b_l_name
-    + '";s:15:"billing_street1";s:' + b_street1.bytes() + ':"' + b_street1
-    + '";s:15:"billing_street2";s:' + b_street2.bytes() + ':"' + b_street2
-    + '";s:12:"billing_city";s:' + b_city.bytes() + ':"' + b_city
-    + '";s:12:"billing_zone";i:' + b_zone
-    + ';s:19:"billing_postal_code";s:' + b_code.bytes() +':"' + b_code
-    + '";s:15:"billing_country";i:' + b_country + ';'
-    + line_item + '}';
-  if (!!products) {
+  if (!!order) {
     $.ajax({
       type: "POST",
       url: Drupal.settings.basePath + "?q=discounts/calculate",
@@ -104,13 +45,19 @@
         var j;
         for (j in discounts) {
           key = 'discount_' + discounts[j].id;
+          // Check that this discount is a new line item, or updates its amount.
           if (!li_values[key] || li_values[key] != discounts[j].amount) {
             set_line_item("discount_" + discounts[j].id, discounts[j].title, discounts[j].amount, discounts[j].weight, 1, false);
+
+            // Set flag to render all line items at once.
             render = true;
           }
         }
         var found;
+        // Search the existing tax line items and match them to a returned tax.
         for (key in li_titles) {
+          // The discount id is the second part of the line item id if the
+          // first part is "discount".
           i = key.split('_', 2);
           if (i[0] == 'discount') {
             found = false;
@@ -120,11 +67,14 @@
                 break;
               }
             }
+            // No discount was matched this time, so remove the line item.
             if (!found) {
               delete li_titles[key];
               delete li_values[key];
               delete li_weight[key];
               delete li_summed[key];
+              // Even if no discounts were added earlier, the display must be
+              // updated.
               render = true;
             }
           }
--- uc_discount.module	Fri Aug 21 15:21:10 2009
+++ uc_discount.module	Wed Nov 25 19:34:52 2009
@@ -6,7 +6,7 @@
  * Discounts framework for Ubercart.
  */
 
-require_once('uc_discount.ca.inc');
+require_once(dirname(__FILE__) .'/uc_discount.ca.inc');
 
 /******************************************************************************
  * Drupal Hooks                                                               *
@@ -92,22 +92,34 @@
   global $user;
   static $prices = array();
 
-  if ($context['type'] == 'product') {
-    if (isset($context['field']) && $context['field'] != 'sell_price') {
-      // Don't discount list_price or cost.
+  switch ($context['type']) {
+    case 'product':
+      if (isset($context['field']) && $context['field'] != 'sell_price') {
+        // Don't discount list_price or cost.
+        return;
+      }
+      $node = clone $context['subject']['node'];
+      $cache = 'node:'. $node->nid .':'. $price_info['price'];
+      break;
+    case 'cart_item':
+      $node = clone $context['subject']['node'];
+      $item = $context['subject']['cart_item'];
+      $cache = 'cart_item:'. $node->nid .':'. $price_info['price'] .':'. serialize($item->data);
+      break;
+    case 'order_product':
+      if (isset($context['subject']['node'])){
+        $node = clone $context['subject']['node'];
+        $item = $context['subject']['product'];
+        $cache = 'order_product:'. $node->nid .':'. $price_info['price'] .':'. serialize($item->data);
+      }
+      else if (isset($context['subject']['order_product'])){
+        $order_product = clone $context['subject']['order_product'];
+        $cache = 'order_product:'. $order_product->nid .':'. $price_info['price'] .':'. serialize($order_product->data);
+      }
+      break;
+    default:
+      // Nothing to do.
       return;
-    }
-    $node = clone $context['subject']['node'];
-    $cache = 'node:'. $node->nid .':'. $price_info['price'];
-  }
-  elseif ($context['type'] == 'cart_item') {
-    $node = clone $context['subject']['node'];
-    $item = $context['subject']['cart_item'];
-    $cache = 'cart_item:'. $node->nid .':'. $price_info['price'] .':'. serialize($item->data);
-  }
-  else {
-    // Nothing to do.
-    return;
   }
 
   if (!isset($prices[$cache])) {
@@ -127,7 +139,7 @@
 /**
  * Update and save discount line items to the order.
  */
-function uc_discount_order($op, $arg1, $arg2) {
+function uc_discount_order($op, &$arg1, $arg2) {
   switch ($op) {
     case 'save':
       $changes = array();
@@ -401,9 +413,25 @@
 
 function uc_discount_order_product_discount($order, $settings) {
   $multiplier = 0;
+
+  $context = array(
+    'revision' => 'altered',
+    'type' => 'order_product',
+    'subject' => array(
+      'order' => $order,
+    ),
+  );
+
   foreach ($order->products as $product) {
     if ($product->nid == $settings['product']) {
-      $multiplier += $product->price * min($settings['qty'], $product->qty);
+      $context['subject']['product'] = $product;
+      $context['subject']['node'] = node_load($product->nid);
+      $price_info = array(
+        'price' => $product->price,
+        'qty' => min($settings['qty'], $product->qty),
+      );
+
+      $multiplier += uc_price($price_info, $context);
     }
   }
 
@@ -716,7 +744,7 @@
 function uc_discount_calculate_discounts($order) {
   global $user;
 
-  if (isset($order->uid)) {
+  if ($order->uid > 0) {
     $account = user_load($order->uid);
   }
   else {
