From b0573a32f03f75250af5068eca3f3be2346ff004 Mon Sep 17 00:00:00 2001
From: Chris Oden <wodenx@gmail.com>
Date: Wed, 23 Nov 2011 15:20:03 -0500
Subject: [PATCH 01/14] Lonwave's initial patch from #22

---
 uc_attribute/uc_attribute.module     |   69 +++++++++++++++----------------
 uc_cart/uc_cart.module               |   35 ++--------------
 uc_product/uc_product.info.inc       |    8 +++-
 uc_product/uc_product.module         |   27 +++++++++++-
 uc_product/uc_product.rules.inc      |   19 +++++++++
 uc_product_kit/uc_product_kit.module |   74 ++++++++++++++++++---------------
 uc_taxes/uc_taxes.module             |   47 +++-------------------
 7 files changed, 136 insertions(+), 143 deletions(-)

diff --git a/uc_attribute/uc_attribute.module b/uc_attribute/uc_attribute.module
index d1f01c6..72da9ac 100644
--- a/uc_attribute/uc_attribute.module
+++ b/uc_attribute/uc_attribute.module
@@ -504,40 +504,34 @@ function uc_attribute_uc_product_class($type, $op) {
 }
 
 /**
- * Implements hook_uc_cart_item().
+ * Implements hook_uc_product_alter().
  */
-function uc_attribute_uc_cart_item($op, $item) {
-  switch ($op) {
-    case 'load':
-      if (!isset($item->data['attributes'])) {
-        return;
-      }
-
-      $options = _uc_cart_product_get_options($item);
-      foreach ($options as $option) {
-        $item->cost += $option['cost'];
-        $item->price += $option['price'];
-        $item->display_price += $option['price'];
-        $item->weight += $option['weight'];
-      }
+function uc_attribute_uc_product_alter(&$node) {
+  if (isset($node->data['attributes'])) {
+    $options = _uc_cart_product_get_options($node);
+    foreach ($options as $option) {
+      $node->cost += $option['cost'];
+      $node->price += $option['price'];
+      $node->display_price += $option['price'];
+      $node->weight += $option['weight'];
+    }
 
-      $combination = array();
-      foreach ((array)$item->data['attributes'] as $aid => $value) {
-        if (is_numeric($value)) {
-          $attribute = uc_attribute_load($aid, $item->nid, 'product');
-          if ($attribute && ($attribute->display == 1 || $attribute->display == 2)) {
-            $combination[$aid] = $value;
-          }
+    $combination = array();
+    foreach ($node->data['attributes'] as $aid => $value) {
+      if (is_numeric($value)) {
+        $attribute = uc_attribute_load($aid, $node->nid, 'product');
+        if ($attribute && ($attribute->display == 1 || $attribute->display == 2)) {
+          $combination[$aid] = $value;
         }
       }
-      ksort($combination);
+    }
+    ksort($combination);
 
-      $model = db_query("SELECT model FROM {uc_product_adjustments} WHERE nid = :nid AND combination LIKE :combo", array(':nid' => $item->nid, ':combo' => serialize($combination)))->fetchField();
+    $model = db_query("SELECT model FROM {uc_product_adjustments} WHERE nid = :nid AND combination LIKE :combo", array(':nid' => $node->nid, ':combo' => serialize($combination)))->fetchField();
 
-      if (!empty($model)) {
-        $item->model = $model;
-      }
-      break;
+    if (!empty($model)) {
+      $node->model = $model;
+    }
   }
 }
 
@@ -1263,18 +1257,21 @@ function _uc_attribute_alter_form($product) {
     // Build the attribute's options array.
     $options = array();
     foreach ($attribute->options as $option) {
+      $display_price = '';
+      $variant = uc_product_load_variant($product->nid, array('attributes' => array($attribute->aid => $option->oid)));
       switch (variable_get('uc_attribute_option_price_format', 'adjustment')) {
         case 'total':
-          $display_price = in_array($attribute->aid, $priced_attributes) ? ', ' . uc_currency_format($product->sell_price + $option->price) : '';
+          if (in_array($attribute->aid, $priced_attributes)) {
+            $display_price = uc_currency_format($variant->display_price);
+          }
           if (count($priced_attributes) == 1 && $attribute->display != 3) {
             break;
           }
         case 'adjustment':
-          $display_price = ($option->price != 0 ? ', ' . ($option->price > 0 ? '+' : '') . uc_currency_format($option->price) : '');
-          break;
-        case 'none':
-        default:
-          $display_price = '';
+          $adjustment = $variant->display_price - $product->display_price;
+          if ($adjustment) {
+            $display_price = ($adjustment > 0 ? '+' : '') . uc_currency_format($adjustment);
+          }
           break;
       }
       $options[$option->oid] = $option->name;
@@ -1282,7 +1279,9 @@ function _uc_attribute_alter_form($product) {
         // Select options are check_plain()ed, but radio button labels are not.
         $options[$option->oid] = check_plain($options[$option->oid]);
       }
-      $options[$option->oid] .= $display_price;
+      if ($display_price) {
+        $options[$option->oid] .= ', ' . $display_price;
+      }
     }
 
     if (count($attribute->options) && $attribute->display > 0) {
diff --git a/uc_cart/uc_cart.module b/uc_cart/uc_cart.module
index d42eb2c..a6b3535 100644
--- a/uc_cart/uc_cart.module
+++ b/uc_cart/uc_cart.module
@@ -1353,37 +1353,12 @@ function uc_cart_get_item($item) {
     $item = db_query("SELECT * FROM {uc_cart_products} WHERE cart_item_id = :id", array(':id' => $item))->fetchObject();
   }
 
-  if (empty($item)) {
-    return;
-  }
-
-  $product = node_load($item->nid);
-  if (!$product) {
-    return;
+  if ($item && $product = uc_product_load_variant($item->nid, unserialize($item->data))) {
+    $product->cart_item_id = $item->cart_item_id;
+    $product->qty = $item->qty;
+    $product->module = $product->data['module'];
+    return $product;
   }
-
-  $item->vid = $product->vid;
-  $item->title = $product->title;
-  $item->cost = $product->cost;
-  $item->price = $product->sell_price;
-  $item->weight = $product->weight;
-  $item->weight_units = $product->weight_units;
-  $item->data = unserialize($item->data);
-  $item->module = $item->data['module'];
-  $item->model = $product->model;
-  $item->display_price = $product->sell_price;
-  $item->display_price_suffix = '';
-
-  // Invoke hook_uc_cart_item() with $op = 'load' in enabled modules.
-  foreach (module_implements('uc_cart_item') as $module) {
-    $func = $module . '_uc_cart_item';
-    if (function_exists($func)) {
-      // $item must be passed by reference.
-      $func('load', $item);
-    }
-  }
-
-  return $item;
 }
 
 /**
diff --git a/uc_product/uc_product.info.inc b/uc_product/uc_product.info.inc
index 76758cd..9d0b8d5 100644
--- a/uc_product/uc_product.info.inc
+++ b/uc_product/uc_product.info.inc
@@ -29,9 +29,15 @@ function uc_product_entity_property_info_alter(&$info) {
     );
     $properties['sell_price'] = array(
       'label' => t('Sell price'),
-      'description' => t('The amount the customer pays for the product.'),
+      'description' => t('The base amount the customer pays for the product.'),
       'type' => 'decimal',
     );
+    $properties['price'] = array(
+      'label' => t('Price'),
+      'description' => t('The amount the customer pays for this specific product.'),
+      'type' => 'decimal',
+      'setter callback' => 'entity_property_verbatim_set',
+    );
     $properties['weight'] = array(
       'label' => t('Weight'),
       'description' => t('The physical weight of the product.'),
diff --git a/uc_product/uc_product.module b/uc_product/uc_product.module
index 0489a72..cd822e7 100644
--- a/uc_product/uc_product.module
+++ b/uc_product/uc_product.module
@@ -639,12 +639,35 @@ function uc_product_load($nodes) {
     foreach ($node as $field => $value) {
       $nodes[$node->nid]->$field = $value;
     }
-    $nodes[$node->nid]->display_price = $node->sell_price;
-    $nodes[$node->nid]->display_price_suffix = '';
+    _uc_product_calculate($nodes[$node->nid]);
   }
 }
 
 /**
+ * Load a specific altered variant of a product.
+ */
+function uc_product_load_variant($nid, $data) {
+  $node = clone node_load($nid);
+  $node->data = $data;
+  _uc_product_calculate($node);
+  return $node;
+}
+
+/**
+ * Calculate the sale and display prices for a product.
+ */
+function _uc_product_calculate(&$node) {
+  $node->price = $node->sell_price;
+
+  rules_invoke_event('uc_product_load', $node);
+
+  $node->display_price = $node->price;
+  $node->display_price_suffix = '';
+
+  drupal_alter('uc_product', $node);
+}
+
+/**
  * Implements hook_delete().
  */
 function uc_product_delete(&$node) {
diff --git a/uc_product/uc_product.rules.inc b/uc_product/uc_product.rules.inc
index 20f35d7..c3d0c2e 100644
--- a/uc_product/uc_product.rules.inc
+++ b/uc_product/uc_product.rules.inc
@@ -6,6 +6,25 @@
  */
 
 /**
+ * Implements hook_rules_event_info().
+ */
+function uc_product_rules_event_info() {
+  $events['uc_product_load'] = array(
+    'label' => t('A product is being loaded'),
+    'group' => t('Node'),
+    'variables' => array(
+      'node' => array(
+        'type' => 'node',
+        'label' => t('Node'),
+        'skip save' => TRUE,
+      ),
+    ),
+  );
+
+  return $events;
+}
+
+/**
  * Implements hook_rules_condition_info().
  */
 function uc_product_rules_condition_info() {
diff --git a/uc_product_kit/uc_product_kit.module b/uc_product_kit/uc_product_kit.module
index 845c264..6b64452 100644
--- a/uc_product_kit/uc_product_kit.module
+++ b/uc_product_kit/uc_product_kit.module
@@ -365,33 +365,34 @@ function uc_product_kit_load($nodes) {
     $nodes[$prod->nid]->mutable = $prod->mutable;
     $nodes[$prod->nid]->synchronized = $prod->synchronized;
 
-    // Save products in an associative array to know exactly which products
-    // belong to which kit.
-    $all_products[$prod->nid][$prod->product_id] = $prod;
-  }
-
-  foreach ($all_products as $kit_products) {
-    $products = node_load_multiple(array_keys($kit_products));
-    foreach ($products as $p) {
-      // Get our component product.
-      $prod = $kit_products[$p->nid];
-
-      // We need to clone the object to avoid changes by reference
-      // http://www.php.net/manual/en/language.references.php#93812
-      $product = clone $p;
-
-      // Add the component information.
-      $product->qty = $prod->qty;
-      $product->discount = $prod->discount;
-      $product->ordering = $prod->ordering;
-
-      // Add product to the kit.
-      $nodes[$prod->nid]->products[$product->nid] = $product;
+    // Add the component information.
+    $data = array();
+    if ($prod->mutable != UC_PRODUCT_KIT_MUTABLE) {
+      $data = array('kit_id' => $prod->nid, 'kit_discount' => $prod->discount);
     }
+    $product = uc_product_load_variant($prod->product_id, $data);
+    $product->qty = $prod->qty;
+    $product->discount = $prod->discount;
+    $product->ordering = $prod->ordering;
+
+    // Add product to the kit.
+    $nodes[$prod->nid]->products[$product->nid] = $product;
   }
 
   // Add product data to kits.
   uc_product_load($nodes);
+
+  // Recalculate display price.
+  foreach ($nodes as &$node) {
+    if ($node->mutable != UC_PRODUCT_KIT_MUTABLE) {
+      $node->display_price = 0;
+      $node->display_price_suffix = '';
+      foreach ($node->products as $pid => &$product) {
+        $node->display_price += $product->display_price * $product->qty;
+        $node->display_price_suffix .= $product->display_price_suffix;
+      }
+    }
+  }
 }
 
 /**
@@ -1010,22 +1011,27 @@ function uc_product_kit_uc_add_to_cart($nid, $qty, $kit_data) {
 }
 
 /**
- * Implements hook_uc_cart_item().
+ * Implements hook_uc_product_alter().
  */
-function uc_product_kit_uc_cart_item($op, $item) {
-  switch ($op) {
-    case 'load':
-      if (isset($item->data['kit_id']) && ($kit = node_load($item->data['kit_id'])) && $kit->mutable != UC_PRODUCT_KIT_MUTABLE) {
-        $kit_discount = $kit->products[$item->nid]->discount;
-        if ($kit_discount !== '') {
-          $item->price += $kit_discount;
-          $item->display_price += $kit_discount;
-        }
-      }
-    break;
+function uc_product_kit_uc_product_alter(&$node) {
+  if (isset($node->data['kit_id'])) {
+    // If this is a kit component load, we would cause infinite recursion trying
+    // to node_load() the parent, but we already have the discount available.
+    if (isset($node->data['kit_discount'])) {
+      $discount = isset($node->data['kit_discount']);
+    }
+    elseif (($kit = node_load($node->data['kit_id'])) && $kit->mutable != UC_PRODUCT_KIT_MUTABLE) {
+      $discount = $kit->products[$node->nid]->discount;
+    }
+    else {
+      $discount = 0;
+    }
+    $node->price += $discount;
+    $node->display_price += $discount;
   }
 }
 
+
 /**
  * Implements hook_uc_order_product_alter().
  *
diff --git a/uc_taxes/uc_taxes.module b/uc_taxes/uc_taxes.module
index b0b2e8a..4192fc9 100644
--- a/uc_taxes/uc_taxes.module
+++ b/uc_taxes/uc_taxes.module
@@ -105,51 +105,16 @@ function uc_taxes_form_uc_order_edit_form_alter(&$form, &$form_state) {
 }
 
 /**
- * Implements hook_uc_cart_item().
+ * Implements hook_uc_product_alter().
  *
  * Adds included taxes (VAT) to display price of applicable products.
  */
-function uc_taxes_uc_cart_item($op, $item) {
-  if ($op == 'load') {
-    $product = node_load($item->nid);
+function uc_taxes_uc_product_alter(&$node) {
+  list($amount, $suffixes) = uc_taxes_get_included_tax($node, $node->price);
 
-    list($amount, $suffixes) = uc_taxes_get_included_tax($product, $item->price);
-
-    $item->display_price += $amount;
-    if (!empty($suffixes)) {
-      $item->display_price_suffix .= implode(' ', $suffixes);
-    }
-  }
-}
-
-/**
- * Implements hook_node_load().
- *
- * Adds included taxes (VAT) to display price of applicable products.
- */
-function uc_taxes_node_load($nodes, $types) {
-  foreach ($nodes as &$node) {
-    if (uc_product_is_product($node)) {
-      $amount = 0;
-      $suffixes = array();
-      if ($node->type == 'product_kit') {
-        // For product kits, the total tax is the sum of the tax on the discounted
-        // prices of component products.
-        foreach ($node->products as $pid => &$product) {
-          $discount =  ($node->mutable != UC_PRODUCT_KIT_MUTABLE) ? $product->discount : 0;
-          list($this_amount, $these_suffixes) = uc_taxes_get_included_tax($product, $product->sell_price + $discount);
-          $amount += $this_amount * $product->qty;
-          $suffixes += $these_suffixes;
-        }
-      }
-      else {
-        list($amount, $suffixes) = uc_taxes_get_included_tax($node);
-      }
-      $node->display_price += $amount;
-      if (!empty($suffixes)) {
-        $node->display_price_suffix .= implode(' ', $suffixes);
-      }
-    }
+  $node->display_price += $amount;
+  if (!empty($suffixes)) {
+    $node->display_price_suffix .= implode(' ', $suffixes);
   }
 }
 
-- 
1.7.3.4


From 39387711f323f83924b592456937593bd71fac9c Mon Sep 17 00:00:00 2001
From: Chris Oden <wodenx@gmail.com>
Date: Wed, 23 Nov 2011 15:22:49 -0500
Subject: [PATCH 02/14] Fix minor bug in uc_product_kit_uc_product_alter()

---
 uc_product_kit/uc_product_kit.module |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/uc_product_kit/uc_product_kit.module b/uc_product_kit/uc_product_kit.module
index 6b64452..fe03904 100644
--- a/uc_product_kit/uc_product_kit.module
+++ b/uc_product_kit/uc_product_kit.module
@@ -1018,7 +1018,7 @@ function uc_product_kit_uc_product_alter(&$node) {
     // If this is a kit component load, we would cause infinite recursion trying
     // to node_load() the parent, but we already have the discount available.
     if (isset($node->data['kit_discount'])) {
-      $discount = isset($node->data['kit_discount']);
+      $discount = $node->data['kit_discount'];
     }
     elseif (($kit = node_load($node->data['kit_id'])) && $kit->mutable != UC_PRODUCT_KIT_MUTABLE) {
       $discount = $kit->products[$node->nid]->discount;
-- 
1.7.3.4


From 5a7aafdc3ee3f6e37a638b32d5844165578023ba Mon Sep 17 00:00:00 2001
From: Chris Oden <wodenx@gmail.com>
Date: Wed, 23 Nov 2011 15:23:58 -0500
Subject: [PATCH 03/14] Make display price suffixes into an array

---
 uc_product/uc_product.module         |    6 +++---
 uc_product_kit/uc_product_kit.module |    8 ++++----
 uc_taxes/uc_taxes.module             |    2 +-
 3 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/uc_product/uc_product.module b/uc_product/uc_product.module
index cd822e7..2d23382 100644
--- a/uc_product/uc_product.module
+++ b/uc_product/uc_product.module
@@ -662,7 +662,7 @@ function _uc_product_calculate(&$node) {
   rules_invoke_event('uc_product_load', $node);
 
   $node->display_price = $node->price;
-  $node->display_price_suffix = '';
+  $node->display_price_suffixes = array();
 
   drupal_alter('uc_product', $node);
 }
@@ -688,7 +688,7 @@ function uc_product_view($node, $view_mode) {
   $node->content['display_price'] = array(
     '#theme' => 'uc_product_price',
     '#value' => $node->display_price,
-    'suffix' => array('#markup' => $node->display_price_suffix),
+    'suffix' => array('#markup' => implode(' ', $node->display_price_suffixes)),
     '#attributes' => array(
       'class' => array(
         'display-price',
@@ -1048,7 +1048,7 @@ function uc_product_uc_cart_display($item) {
   );
 
   $element['#total'] = $item->display_price * $item->qty;
-  $element['#suffix'] = $item->display_price_suffix;
+  $element['#suffix'] = implode(' ', $item->display_price_suffixes);
   $element['data'] = array('#type' => 'hidden', '#value' => serialize($item->data));
   $element['qty'] = array(
     '#type' => 'uc_quantity',
diff --git a/uc_product_kit/uc_product_kit.module b/uc_product_kit/uc_product_kit.module
index fe03904..0721814 100644
--- a/uc_product_kit/uc_product_kit.module
+++ b/uc_product_kit/uc_product_kit.module
@@ -386,10 +386,10 @@ function uc_product_kit_load($nodes) {
   foreach ($nodes as &$node) {
     if ($node->mutable != UC_PRODUCT_KIT_MUTABLE) {
       $node->display_price = 0;
-      $node->display_price_suffix = '';
+      $node->display_price_suffixes = array();
       foreach ($node->products as $pid => &$product) {
         $node->display_price += $product->display_price * $product->qty;
-        $node->display_price_suffix .= $product->display_price_suffix;
+        $node->display_price_suffixes += $product->display_price_suffixes;
       }
     }
   }
@@ -664,7 +664,7 @@ function uc_product_kit_view($node, $view_mode) {
   $node->content['display_price'] = array(
     '#theme' => 'uc_product_price',
     '#value' => $node->display_price,
-    'suffix' => array('#markup' => $node->display_price_suffix),
+    'suffix' => array('#markup' => implode(' ', $node->display_price_suffixes)),
     '#attributes' => array(
       'class' => array(
         'product-kit',
@@ -1119,7 +1119,7 @@ function uc_product_kit_uc_cart_display($item) {
       );
       $element['description'] = array('#markup' => '');
       $element['#total'] = 0;
-      $element['#suffix'] = $kit->display_price_suffix;
+      $element['#suffix'] = implode(' ', $kit->display_price_suffixes);
       $element['#extra'] = array();
 
       $elements[$unique_id] = $element;
diff --git a/uc_taxes/uc_taxes.module b/uc_taxes/uc_taxes.module
index 4192fc9..9a7d47d 100644
--- a/uc_taxes/uc_taxes.module
+++ b/uc_taxes/uc_taxes.module
@@ -114,7 +114,7 @@ function uc_taxes_uc_product_alter(&$node) {
 
   $node->display_price += $amount;
   if (!empty($suffixes)) {
-    $node->display_price_suffix .= implode(' ', $suffixes);
+    $node->display_price_suffixes += $suffixes;
   }
 }
 
-- 
1.7.3.4


From b66866aea1f0e2a6e3fd98df6d2887811db18d73 Mon Sep 17 00:00:00 2001
From: Chris Oden <wodenx@gmail.com>
Date: Wed, 23 Nov 2011 15:25:33 -0500
Subject: [PATCH 04/14] Fix rules-based price alteration for product kits.

---
 uc_attribute/uc_attribute.module                   |    5 +--
 uc_product/uc_product.module                       |   19 +++++++++-----
 .../uc_product_handler_field_display_price.inc     |    2 +-
 uc_product_kit/uc_product_kit.module               |   25 ++++++++++++++++----
 uc_taxes/uc_taxes.module                           |    4 +-
 5 files changed, 37 insertions(+), 18 deletions(-)

diff --git a/uc_attribute/uc_attribute.module b/uc_attribute/uc_attribute.module
index 72da9ac..d91a8a4 100644
--- a/uc_attribute/uc_attribute.module
+++ b/uc_attribute/uc_attribute.module
@@ -512,7 +512,6 @@ function uc_attribute_uc_product_alter(&$node) {
     foreach ($options as $option) {
       $node->cost += $option['cost'];
       $node->price += $option['price'];
-      $node->display_price += $option['price'];
       $node->weight += $option['weight'];
     }
 
@@ -1262,13 +1261,13 @@ function _uc_attribute_alter_form($product) {
       switch (variable_get('uc_attribute_option_price_format', 'adjustment')) {
         case 'total':
           if (in_array($attribute->aid, $priced_attributes)) {
-            $display_price = uc_currency_format($variant->display_price);
+            $display_price = uc_currency_format($variant->price + $variant->display_price_adjustment);
           }
           if (count($priced_attributes) == 1 && $attribute->display != 3) {
             break;
           }
         case 'adjustment':
-          $adjustment = $variant->display_price - $product->display_price;
+          $adjustment = ($variant->price + $variant->display_price_adjustment) - ($product->price + $product->display_price_adjustment);
           if ($adjustment) {
             $display_price = ($adjustment > 0 ? '+' : '') . uc_currency_format($adjustment);
           }
diff --git a/uc_product/uc_product.module b/uc_product/uc_product.module
index 2d23382..c4930f1 100644
--- a/uc_product/uc_product.module
+++ b/uc_product/uc_product.module
@@ -658,16 +658,21 @@ function uc_product_load_variant($nid, $data) {
  */
 function _uc_product_calculate(&$node) {
   $node->price = $node->sell_price;
-
-  rules_invoke_event('uc_product_load', $node);
-
-  $node->display_price = $node->price;
+  $node->display_price_adjustment = 0;
   $node->display_price_suffixes = array();
-
   drupal_alter('uc_product', $node);
 }
 
 /**
+ * Implements hook_uc_product_alter.
+ * 
+ * Invokes rules event to allow product modifications.
+ */
+function uc_product_uc_product_alter(&$node) {
+  rules_invoke_event('uc_product_load', $node);
+}
+
+/**
  * Implements hook_delete().
  */
 function uc_product_delete(&$node) {
@@ -687,7 +692,7 @@ function uc_product_delete(&$node) {
 function uc_product_view($node, $view_mode) {
   $node->content['display_price'] = array(
     '#theme' => 'uc_product_price',
-    '#value' => $node->display_price,
+    '#value' => $node->price + $node->display_price_adjustment,
     'suffix' => array('#markup' => implode(' ', $node->display_price_suffixes)),
     '#attributes' => array(
       'class' => array(
@@ -1047,7 +1052,7 @@ function uc_product_uc_cart_display($item) {
     '#markup' => node_access('view', $node) ? l($item->title, 'node/' . $node->nid) : check_plain($item->title),
   );
 
-  $element['#total'] = $item->display_price * $item->qty;
+  $element['#total'] = ($item->price + $item->display_price_adjustment) * $item->qty;
   $element['#suffix'] = implode(' ', $item->display_price_suffixes);
   $element['data'] = array('#type' => 'hidden', '#value' => serialize($item->data));
   $element['qty'] = array(
diff --git a/uc_product/views/uc_product_handler_field_display_price.inc b/uc_product/views/uc_product_handler_field_display_price.inc
index bf205f4..b7de82b 100644
--- a/uc_product/views/uc_product_handler_field_display_price.inc
+++ b/uc_product/views/uc_product_handler_field_display_price.inc
@@ -21,6 +21,6 @@ class uc_product_handler_field_display_price extends uc_product_handler_field_pr
   function get_value($values, $field = NULL) {
     $nid = parent::get_value($values, $field);
     $node = node_load($nid);
-    return $node->display_price;
+    return $node->price + $node->display_price_adjustment;
   }
 }
diff --git a/uc_product_kit/uc_product_kit.module b/uc_product_kit/uc_product_kit.module
index 0721814..76dd10a 100644
--- a/uc_product_kit/uc_product_kit.module
+++ b/uc_product_kit/uc_product_kit.module
@@ -385,10 +385,10 @@ function uc_product_kit_load($nodes) {
   // Recalculate display price.
   foreach ($nodes as &$node) {
     if ($node->mutable != UC_PRODUCT_KIT_MUTABLE) {
-      $node->display_price = 0;
+      $node->display_price_adjustment = 0;
       $node->display_price_suffixes = array();
       foreach ($node->products as $pid => &$product) {
-        $node->display_price += $product->display_price * $product->qty;
+        $node->display_price_adjustment += $product->display_price_adjustment * $product->qty;
         $node->display_price_suffixes += $product->display_price_suffixes;
       }
     }
@@ -396,6 +396,22 @@ function uc_product_kit_load($nodes) {
 }
 
 /**
+ * Implements hook_module_implements_alter().
+ *
+ * Ensure that our component products have their discounts applied before any other
+ * product alterations are made.
+ */
+function uc_product_kit_module_implements_alter(&$implementations, $hook) {
+  if ($hook == 'uc_product_alter') {
+    $group = $implementations['uc_product_kit'];
+    unset($implementations['uc_product_kit']);
+    $implementations =  array('uc_product_kit' => $group) + $implementations;
+  }
+}
+
+
+
+/**
  * Implements hook_theme().
  */
 function uc_product_kit_theme() {
@@ -663,7 +679,7 @@ function theme_uc_product_kit_items_form($variables) {
 function uc_product_kit_view($node, $view_mode) {
   $node->content['display_price'] = array(
     '#theme' => 'uc_product_price',
-    '#value' => $node->display_price,
+    '#value' => $node->price + $node->display_price_adjustment,
     'suffix' => array('#markup' => implode(' ', $node->display_price_suffixes)),
     '#attributes' => array(
       'class' => array(
@@ -1027,7 +1043,6 @@ function uc_product_kit_uc_product_alter(&$node) {
       $discount = 0;
     }
     $node->price += $discount;
-    $node->display_price += $discount;
   }
 }
 
@@ -1134,7 +1149,7 @@ function uc_product_kit_uc_cart_display($item) {
       );
     }
 
-    $elements[$unique_id]['#total'] += $item->display_price * $item->qty;
+    $elements[$unique_id]['#total'] += ($item->price + $item->display_price_adjustment) * $item->qty;
     $elements[$unique_id]['data'][$item->nid] = $item;
     $products[$unique_id][] = $item->nid;
 
diff --git a/uc_taxes/uc_taxes.module b/uc_taxes/uc_taxes.module
index 9a7d47d..f26841d 100644
--- a/uc_taxes/uc_taxes.module
+++ b/uc_taxes/uc_taxes.module
@@ -76,7 +76,7 @@ function uc_taxes_menu() {
  * calculations are made.
  */
 function uc_taxes_module_implements_alter(&$implementations, $hook) {
-  if ($hook == 'uc_order') {
+  if (in_array($hook, array('uc_order', 'uc_product_alter'))) {
     $group = $implementations['uc_taxes'];
     unset($implementations['uc_taxes']);
     $implementations['uc_taxes'] = $group;
@@ -112,7 +112,7 @@ function uc_taxes_form_uc_order_edit_form_alter(&$form, &$form_state) {
 function uc_taxes_uc_product_alter(&$node) {
   list($amount, $suffixes) = uc_taxes_get_included_tax($node, $node->price);
 
-  $node->display_price += $amount;
+  $node->display_price_adjustment += $amount;
   if (!empty($suffixes)) {
     $node->display_price_suffixes += $suffixes;
   }
-- 
1.7.3.4


From 2a1ef2ae5beef5b33a22e39b63d7c9b611591c34 Mon Sep 17 00:00:00 2001
From: Chris Oden <wodenx@gmail.com>
Date: Wed, 23 Nov 2011 15:27:21 -0500
Subject: [PATCH 05/14] Only run uc_product_alter sequence when uc_product_load_variant() is called explicitly (i.e. not at node_load)

---
 uc_attribute/uc_attribute.module     |    7 ++--
 uc_product/uc_product.module         |   56 +++++++++++++++++-----------------
 uc_product_kit/uc_product_kit.module |   19 ++++++-----
 3 files changed, 42 insertions(+), 40 deletions(-)

diff --git a/uc_attribute/uc_attribute.module b/uc_attribute/uc_attribute.module
index d91a8a4..7eb435e 100644
--- a/uc_attribute/uc_attribute.module
+++ b/uc_attribute/uc_attribute.module
@@ -507,7 +507,7 @@ function uc_attribute_uc_product_class($type, $op) {
  * Implements hook_uc_product_alter().
  */
 function uc_attribute_uc_product_alter(&$node) {
-  if (isset($node->data['attributes'])) {
+  if (isset($node->data['attributes']) && is_array($node->data['attributes'])) {
     $options = _uc_cart_product_get_options($node);
     foreach ($options as $option) {
       $node->cost += $option['cost'];
@@ -1257,7 +1257,7 @@ function _uc_attribute_alter_form($product) {
     $options = array();
     foreach ($attribute->options as $option) {
       $display_price = '';
-      $variant = uc_product_load_variant($product->nid, array('attributes' => array($attribute->aid => $option->oid)));
+      $variant = uc_product_load_variant($product, array('attributes' => array($attribute->aid => $option->oid)));
       switch (variable_get('uc_attribute_option_price_format', 'adjustment')) {
         case 'total':
           if (in_array($attribute->aid, $priced_attributes)) {
@@ -1267,7 +1267,8 @@ function _uc_attribute_alter_form($product) {
             break;
           }
         case 'adjustment':
-          $adjustment = ($variant->price + $variant->display_price_adjustment) - ($product->price + $product->display_price_adjustment);
+          $base = uc_product_load_variant($product);
+          $adjustment = ($variant->price + $variant->display_price_adjustment) - ($base->price + $base->display_price_adjustment);
           if ($adjustment) {
             $display_price = ($adjustment > 0 ? '+' : '') . uc_currency_format($adjustment);
           }
diff --git a/uc_product/uc_product.module b/uc_product/uc_product.module
index c4930f1..1bdd8b7 100644
--- a/uc_product/uc_product.module
+++ b/uc_product/uc_product.module
@@ -639,28 +639,27 @@ function uc_product_load($nodes) {
     foreach ($node as $field => $value) {
       $nodes[$node->nid]->$field = $value;
     }
-    _uc_product_calculate($nodes[$node->nid]);
+    $nodes[$node->nid]->price = $nodes[$node->nid]->sell_price;
+    $nodes[$node->nid]->display_price_adjustment = 0;
+    $nodes[$node->nid]->display_price_suffixes = array();
   }
 }
 
 /**
- * Load a specific altered variant of a product.
- */
-function uc_product_load_variant($nid, $data) {
-  $node = clone node_load($nid);
-  $node->data = $data;
-  _uc_product_calculate($node);
-  return $node;
-}
-
-/**
- * Calculate the sale and display prices for a product.
+ * Gets a specific altered variant of a product.
+ * 
+ * @param $nid
+ *   The nid of the product.  May also supply a complete, previously loaded product object.
+ * @param $data
+ *   Optional data to add to the product before invoking the alter hooks.
  */
-function _uc_product_calculate(&$node) {
-  $node->price = $node->sell_price;
-  $node->display_price_adjustment = 0;
-  $node->display_price_suffixes = array();
+function uc_product_load_variant($nid, $data = FALSE) {
+  $node = clone (is_object($nid) ? $nid : node_load($nid));
+  if (!empty($data)) {
+    $node->data = $data;
+  }
   drupal_alter('uc_product', $node);
+  return $node;
 }
 
 /**
@@ -690,10 +689,11 @@ function uc_product_delete(&$node) {
  * Implements hook_view().
  */
 function uc_product_view($node, $view_mode) {
+  $variant = uc_product_load_variant($node);
   $node->content['display_price'] = array(
     '#theme' => 'uc_product_price',
-    '#value' => $node->price + $node->display_price_adjustment,
-    'suffix' => array('#markup' => implode(' ', $node->display_price_suffixes)),
+    '#value' => $variant->price + $variant->display_price_adjustment,
+    'suffix' => array('#markup' => implode(' ', $variant->display_price_suffixes)),
     '#attributes' => array(
       'class' => array(
         'display-price',
@@ -702,13 +702,13 @@ function uc_product_view($node, $view_mode) {
   );
   $node->content['model'] = array(
     '#theme' => 'uc_product_model',
-    '#model' => $node->model,
+    '#model' => $variant->model,
     '#view_mode' => $view_mode,
   );
   $node->content['list_price'] = array(
     '#theme' => 'uc_product_price',
     '#title' => t('List price:'),
-    '#value' => $node->list_price,
+    '#value' => $variant->list_price,
     '#attributes' => array(
       'class' => array(
         'list-price',
@@ -718,7 +718,7 @@ function uc_product_view($node, $view_mode) {
   $node->content['cost'] = array(
     '#theme' => 'uc_product_price',
     '#title' => t('Cost:'),
-    '#value' => $node->cost,
+    '#value' => $variant->cost,
     '#attributes' => array(
       'class' => array(
         'cost',
@@ -729,7 +729,7 @@ function uc_product_view($node, $view_mode) {
   $node->content['sell_price'] = array(
     '#theme' => 'uc_product_price',
     '#title' => t('Price:'),
-    '#value' => $node->sell_price,
+    '#value' => $variant->sell_price,
     '#attributes' => array(
       'class' => array(
         'sell-price',
@@ -738,16 +738,16 @@ function uc_product_view($node, $view_mode) {
   );
   $node->content['weight'] = array(
     '#theme' => 'uc_product_weight',
-    '#amount' => $node->weight,
-    '#units' => $node->weight_units,
+    '#amount' => $variant->weight,
+    '#units' => $variant->weight_units,
     '#view_mode' => $view_mode,
   );
   $node->content['dimensions'] = array(
     '#theme' => 'uc_product_dimensions',
-    '#length' => $node->length,
-    '#width' => $node->width,
-    '#height' => $node->height,
-    '#units' => $node->length_units,
+    '#length' => $variant->length,
+    '#width' => $variant->width,
+    '#height' => $variant->height,
+    '#units' => $variant->length_units,
     '#view_mode' => $view_mode,
   );
   if (module_exists('uc_cart') && isset($node->nid)) {
diff --git a/uc_product_kit/uc_product_kit.module b/uc_product_kit/uc_product_kit.module
index 76dd10a..f5a58c9 100644
--- a/uc_product_kit/uc_product_kit.module
+++ b/uc_product_kit/uc_product_kit.module
@@ -677,10 +677,11 @@ function theme_uc_product_kit_items_form($variables) {
  * Implements hook_view().
  */
 function uc_product_kit_view($node, $view_mode) {
+  $variant = uc_product_load_variant($node);
   $node->content['display_price'] = array(
     '#theme' => 'uc_product_price',
-    '#value' => $node->price + $node->display_price_adjustment,
-    'suffix' => array('#markup' => implode(' ', $node->display_price_suffixes)),
+    '#value' => $variant->price + $variant->display_price_adjustment,
+    'suffix' => array('#markup' => implode(' ', $variant->display_price_suffixes)),
     '#attributes' => array(
       'class' => array(
         'product-kit',
@@ -690,13 +691,13 @@ function uc_product_kit_view($node, $view_mode) {
   );
   $node->content['model'] = array(
     '#theme' => 'uc_product_model',
-    '#model' => $node->model,
+    '#model' => $variant->model,
     '#view_mode' => $view_mode,
   );
   $node->content['list_price'] = array(
     '#theme' => 'uc_product_price',
     '#title' => t('List price:'),
-    '#value' => $node->list_price,
+    '#value' => $variant->list_price,
     '#attributes' => array(
       'class' => array(
         'product-kit',
@@ -707,7 +708,7 @@ function uc_product_kit_view($node, $view_mode) {
   $node->content['cost'] = array(
     '#theme' => 'uc_product_price',
     '#title' => t('Cost:'),
-    '#value' => $node->cost,
+    '#value' => $variant->cost,
     '#attributes' => array(
       'class' => array(
         'product-kit',
@@ -719,7 +720,7 @@ function uc_product_kit_view($node, $view_mode) {
   $node->content['sell_price'] = array(
     '#theme' => 'uc_product_price',
     '#title' => t('Price:'),
-    '#value' => $node->sell_price,
+    '#value' => $variant->sell_price,
     '#attributes' => array(
       'class' => array(
         'product-kit',
@@ -729,8 +730,8 @@ function uc_product_kit_view($node, $view_mode) {
   );
   $node->content['weight'] = array(
     '#theme' => 'uc_product_weight',
-    '#amount' => $node->weight,
-    '#units' => $node->weight_units,
+    '#amount' => $variant->weight,
+    '#units' => $variant->weight_units,
     '#view_mode' => $view_mode,
   );
 
@@ -738,7 +739,7 @@ function uc_product_kit_view($node, $view_mode) {
     $node->content['products'] = array('#weight' => 6);
 
     $i = 0;
-    foreach ($node->products as $product) {
+    foreach ($variant->products as $product) {
       $node->content['products'][$product->nid]['qty'] = array(
         '#markup' => '<div class="product-qty">' . theme('uc_product_kit_list_item', array('product' => $product)) . '</div>',
       );
-- 
1.7.3.4


From e49f127252fe3f2975b2cf505f4a80930d9e1673 Mon Sep 17 00:00:00 2001
From: Chris Oden <wodenx@gmail.com>
Date: Wed, 7 Dec 2011 09:30:19 -0500
Subject: [PATCH 06/14] Split product alteration into load_variant and view stages. Add 'uc_cart_item' entity.

---
 uc_attribute/uc_attribute.module                   |    8 +-
 uc_cart/uc_cart.module                             |  123 ++++++++++++++------
 uc_cart/uc_cart_checkout_pane.inc                  |    7 +-
 uc_order/uc_order.module                           |    1 +
 uc_order/uc_order.order_pane.inc                   |   74 +-----------
 uc_product/uc_product.module                       |   46 +++++---
 .../uc_product_handler_field_display_price.inc     |    5 +-
 uc_product_kit/uc_product_kit.module               |   71 +++++++-----
 uc_store/uc_store.module                           |    9 +-
 uc_taxes/uc_taxes.module                           |   48 ++++++--
 10 files changed, 216 insertions(+), 176 deletions(-)

diff --git a/uc_attribute/uc_attribute.module b/uc_attribute/uc_attribute.module
index 7eb435e..f0705f8 100644
--- a/uc_attribute/uc_attribute.module
+++ b/uc_attribute/uc_attribute.module
@@ -1257,18 +1257,18 @@ function _uc_attribute_alter_form($product) {
     $options = array();
     foreach ($attribute->options as $option) {
       $display_price = '';
-      $variant = uc_product_load_variant($product, array('attributes' => array($attribute->aid => $option->oid)));
+      $variant = node_view(uc_product_load_variant($product->nid, array('attributes' => array($attribute->aid => $option->oid))));
       switch (variable_get('uc_attribute_option_price_format', 'adjustment')) {
         case 'total':
           if (in_array($attribute->aid, $priced_attributes)) {
-            $display_price = uc_currency_format($variant->price + $variant->display_price_adjustment);
+            $display_price = uc_currency_format($variant['display_price']['#value']);
           }
           if (count($priced_attributes) == 1 && $attribute->display != 3) {
             break;
           }
         case 'adjustment':
-          $base = uc_product_load_variant($product);
-          $adjustment = ($variant->price + $variant->display_price_adjustment) - ($base->price + $base->display_price_adjustment);
+          $base = node_view($product);
+          $adjustment = $variant['display_price']['#value'] - $base['display_price']['#value'];
           if ($adjustment) {
             $display_price = ($adjustment > 0 ? '+' : '') . uc_currency_format($adjustment);
           }
diff --git a/uc_cart/uc_cart.module b/uc_cart/uc_cart.module
index a6b3535..39c3834 100644
--- a/uc_cart/uc_cart.module
+++ b/uc_cart/uc_cart.module
@@ -303,10 +303,11 @@ function uc_cart_block_view($delta = '') {
       $total = 0;
 
       if ($product_count) {
-        foreach (uc_cart_get_contents() as $item) {
-          $display_item = module_invoke($item->module, 'uc_cart_display', $item);
+        $display_items = entity_view('uc_cart_item', uc_cart_get_contents());
+        foreach (element_children($display_items['uc_cart_item']) as $key) {
+          $display_item = $display_items['uc_cart_item'][$key];
 
-          if (!empty($display_item)) {
+          if (count(element_children($display_item))) {
             $items[] = array(
               'nid' => $display_item['nid']['#value'],
               'qty' => t('@qty&times;', array('@qty' => $display_item['qty']['#default_value'])),
@@ -315,9 +316,9 @@ function uc_cart_block_view($delta = '') {
               'desc' => isset($display_item['description']['#markup']) ? $display_item['description']['#markup'] : FALSE,
             );
             $total += $display_item['#total'];
+            $item_count += $display_item['qty']['#default_value'];
           }
 
-          $item_count += $item->qty;
         }
       }
 
@@ -780,6 +781,7 @@ function theme_uc_empty_cart() {
   return '<p class="uc-cart-empty">' . t('There are no products in your shopping cart.') . '</p>';
 }
 
+
 /**
  * Displays a page allowing the customer to view the contents of his or her cart.
  *
@@ -803,20 +805,16 @@ function uc_cart_view_form($form, &$form_state, $items = NULL) {
   );
 
   $i = 0;
-  foreach ($items as $item) {
-    $display_item = module_invoke($item->module, 'uc_cart_display', $item);
-    if (!empty($display_item)) {
+  $display_items = entity_view('uc_cart_item', $items);
+  foreach (element_children($display_items['uc_cart_item']) as $key) {
+    $display_item = $display_items['uc_cart_item'][$key];
+    if (count(element_children($display_item))) {
       $form['items'][$i] = $display_item;
       $form['items'][$i]['image'] = uc_product_get_picture($display_item['nid']['#value'], 'uc_cart');
 
       $description = $display_item['title']['#markup'] . $display_item['description']['#markup'];
       $form['items'][$i]['desc']['#markup'] = $description;
 
-      $form['items'][$i]['cart_item_id'] = array(
-        '#type' => 'hidden',
-        '#value' => $item->cart_item_id,
-      );
-
       if (isset($form['items'][$i]['remove'])) {
         // Backward compatibility with old checkbox method.
         if ($form['items'][$i]['remove']['#type'] == 'checkbox') {
@@ -840,12 +838,11 @@ function uc_cart_view_form($form, &$form_state, $items = NULL) {
         '#theme' => 'uc_price',
         '#price' => $display_item['#total'],
       );
-      if (!empty($display_item['#suffix'])) {
-        $form['items'][$i]['total']['#suffix'] = $display_item['#suffix'];
-        unset($form['items'][$i]['#suffix']);
+      if (!empty($display_item['#suffixes'])) {
+        $form['items'][$i]['total']['#suffixes'] = $display_item['#suffixes'];
       }
-      $i++;
     }
+    $i++;
   }
 
   $form['items'] = tapir_get_table('uc_cart_view_table', $form['items']);
@@ -1282,14 +1279,30 @@ function uc_cart_get_contents($cid = NULL, $action = NULL) {
   }
 
   if (!isset($items[$cid])) {
-    $items[$cid] = array();
-    $result = db_query("SELECT * FROM {uc_cart_products} WHERE cart_id = :id ORDER BY cart_item_id ASC", array(':id' => $cid));
-
-    foreach ($result as $item) {
-      if ($item = uc_cart_get_item($item)) {
-        $items[$cid][] = $item;
+    // Find all cart items associated with this cart.
+    $efq = new EntityFieldQuery();
+    $result = $efq->entityCondition('entity_type', 'uc_cart_item')
+      ->propertyCondition('cart_id', $cid)
+      ->propertyOrderBy('cart_item_id', 'ASC')
+      ->execute();
+    if (!empty($result['uc_cart_item'])) {
+      $items[$cid] = entity_load('uc_cart_item', array_keys($result['uc_cart_item']));
+      // Create a bare order and attach it to each item as context.
+      $order = new UcOrder();
+      $order->uid = $cid;
+      if ($account = user_load($cid)) {
+        $order->primary_email = $account->mail;
+      }
+      $order->order_status = uc_order_state_default('in_checkout');
+      $order->created = REQUEST_TIME;
+      $order->modified = REQUEST_TIME;
+      foreach ($items[$cid] as $item) {
+        $item->order = $order;
       }
     }
+    else {
+      $items[$cid] = array();
+    }
 
     // Allow other modules a chance to alter the fully loaded cart object.
     drupal_alter('uc_cart', $items[$cid]);
@@ -1340,24 +1353,32 @@ function uc_cart_get_total_qty($cid = NULL) {
 }
 
 /**
- * Allows us to get one single line item in a cart.
- *
- * @param $item
- *   Either an item row from the database, or the cart item ID to fetch.
- *
- * @return
- *   Fully loaded cart item or NULL if item not found.
+ * Entity controller for cart items.
  */
-function uc_cart_get_item($item) {
-  if (!is_object($item)) {
-    $item = db_query("SELECT * FROM {uc_cart_products} WHERE cart_item_id = :id", array(':id' => $item))->fetchObject();
+class UcCartItemController extends EntityAPIController {
+
+  public function buildContent($item, $view_mode = 'full', $langcode = NULL, $content = array()) {
+    $content = array();
+    $content += module_invoke($item->module, 'uc_cart_display', $item);
+    if (!empty($content)) {
+      $content['cart_item_id'] = array(
+				'#type' => 'hidden',
+				'#value' => $item->cart_item_id,
+      );
+    }
+    return parent::buildContent($item, $view_mode, $langcode, $content);
   }
 
-  if ($item && $product = uc_product_load_variant($item->nid, unserialize($item->data))) {
-    $product->cart_item_id = $item->cart_item_id;
-    $product->qty = $item->qty;
-    $product->module = $product->data['module'];
-    return $product;
+  public function attachLoad(&$items, $revision_id = FALSE) {
+    foreach ($items as &$item) {
+      $product = uc_product_load_variant($item->nid, $item->data);
+      // Merge in fields from the product.
+      foreach ($product as $key => $value) {
+        $item->$key = $value;
+      }
+      $item->module = $item->data['module'];
+    }
+    parent::attachLoad($items, $revision_id);
   }
 }
 
@@ -1664,3 +1685,31 @@ function uc_cart_filter_checkout_panes($panes, $remove) {
 
   return $panes;
 }
+
+
+
+/**
+ * Implements hook_entity_info().
+ */
+function uc_cart_entity_info() {
+  return array(
+    'uc_cart_item' => array(
+      'label' => t('Cart item'),
+      'base table' => 'uc_cart_products',
+      'controller class' => 'UcCartItemController',
+      'entity keys' => array(
+        'id' => 'cart_item_id',
+      ),
+      'bundles' => array(
+        'uc_cart_item' => array(
+          'label' => t('Cart item'),
+        ),
+      ),
+      // Entity API callbacks
+      // !TODO Implement these callbacks.
+      //'access callback' => 'uc_cart_item_access',
+      //'save callback'   => 'uc_cart_item_save',
+      //'delete callback' => 'uc_order_product_delete',
+    ),
+  );
+}
diff --git a/uc_cart/uc_cart_checkout_pane.inc b/uc_cart/uc_cart_checkout_pane.inc
index 81a863d..68477bb 100644
--- a/uc_cart/uc_cart_checkout_pane.inc
+++ b/uc_cart/uc_cart_checkout_pane.inc
@@ -490,9 +490,10 @@ function theme_uc_cart_review_table($variables) {
   );
 
   // Set up table rows.
-  foreach ($items as $item) {
-    $display_item = module_invoke($item->module, 'uc_cart_display', $item);
-    if (!empty($display_item)) {
+  $display_items = entity_view('uc_cart_item', $items);
+  foreach (element_children($display_items['uc_cart_item']) as $key) {
+    $display_item = $display_items['uc_cart_item'][$key];
+    if (count(element_children($display_item))) {
       $total = $display_item['#total'];
       $subtotal += $total;
       $description = $display_item['title']['#markup'];
diff --git a/uc_order/uc_order.module b/uc_order/uc_order.module
index 3244055..4d9c32b 100644
--- a/uc_order/uc_order.module
+++ b/uc_order/uc_order.module
@@ -1229,6 +1229,7 @@ class UcOrderController extends DrupalDefaultEntityController {
     foreach ($orders as &$order) {
       $order->data = unserialize($order->data);
 
+      // !FIXME Refactor to use uc_order_product_load().
       $result = db_query("SELECT * FROM {uc_order_products} WHERE order_id = :id ORDER BY order_product_id", array(':id' => $order->order_id));
       $order->products = array();
       foreach ($result as $product) {
diff --git a/uc_order/uc_order.order_pane.inc b/uc_order/uc_order.order_pane.inc
index 7612880..f7bf1ac 100644
--- a/uc_order/uc_order.order_pane.inc
+++ b/uc_order/uc_order.order_pane.inc
@@ -1064,6 +1064,7 @@ function uc_order_view_update_form_submit($form, &$form_state) {
 
 /**
  * Builds the order view products table.
+ * !FIXME Refactor to use uc_order_product_view.
  */
 function uc_op_products_view_table($order) {
   $table = array(
@@ -1119,40 +1120,6 @@ function uc_op_products_view_table($order) {
 
   if (is_array($order->products)) {
     foreach ($order->products as $product) {
-      $data = array();
-      $data['qty'] = array(
-        '#markup' => $product->qty . '&times;',
-        '#cell_attributes' => array('align' => 'right'),
-      );
-      $node = node_load($product->nid);
-      $title = node_access('view', $node) ? l($product->title, 'node/'. $node->nid) : check_plain($product->title);
-      $data['product'] = array(
-        '#markup' => $title . uc_product_get_description($product),
-      );
-      $data['model'] = array(
-        '#markup' => check_plain($product->model),
-        '#cell_attributes' => array('align' => 'center'),
-      );
-
-      if (user_access('administer products')) {
-        $data['cost'] = array(
-          '#theme' => 'uc_price',
-          '#price' => $product->cost,
-          '#cell_attributes' => array('align' => 'right'),
-        );
-      }
-
-      $data['price'] = array(
-        '#theme' => 'uc_price',
-        '#price' => $product->price,
-        '#cell_attributes' => array('align' => 'right'),
-      );
-      $data['total'] = array(
-        '#theme' => 'uc_price',
-        '#price' => $product->price * $product->qty,
-        '#cell_attributes' => array('align' => 'right'),
-      );
-      $data['#attributes'] = array('valign' => 'top');
       $table['#rows'][] = $data;
     }
   }
@@ -1168,6 +1135,7 @@ function uc_op_products_view_table($order) {
 
 /**
  * Builds the order customer's view products table.
+ * !FIXME Refactor to use uc_order_product_view.
  */
 function uc_op_products_customer_table($order) {
   $table = array(
@@ -1222,43 +1190,7 @@ function uc_op_products_customer_table($order) {
   );
 
   if (is_array($order->products)) {
-    foreach ($order->products as $product) {
-      $data = array();
-      $data['qty'] = array(
-        '#markup' => $product->qty . '&times;',
-        '#cell_attributes' => array('align' => 'right'),
-      );
-      $node = node_load($product->nid);
-      $title = node_access('view', $node) ? l($product->title, 'node/'. $node->nid) : check_plain($product->title);
-      $data['product'] = array(
-        '#markup' => $title . uc_product_get_description($product),
-      );
-      $data['model'] = array(
-        '#markup' => check_plain($product->model),
-        '#cell_attributes' => array('align' => 'center'),
-      );
-
-      if (user_access('administer products')) {
-        $data['cost'] = array(
-          '#theme' => 'uc_price',
-          '#price' => $product->cost,
-          '#cell_attributes' => array('align' => 'right'),
-        );
-      }
-
-      $data['price'] = array(
-        '#theme' => 'uc_price',
-        '#price' => $product->price,
-        '#cell_attributes' => array('align' => 'right'),
-      );
-      $data['total'] = array(
-        '#theme' => 'uc_price',
-        '#price' => $product->price * $product->qty,
-        '#cell_attributes' => array('align' => 'right'),
-      );
-      $data['#attributes'] = array('valign' => 'top');
-      $table['#rows'][] = $data;
-    }
+    $table['#rows'] = entity_view('uc_order_product', $order->products);
   }
   else {
     $table['#rows'][]['product'] = array(
diff --git a/uc_product/uc_product.module b/uc_product/uc_product.module
index 1bdd8b7..5df140c 100644
--- a/uc_product/uc_product.module
+++ b/uc_product/uc_product.module
@@ -216,7 +216,7 @@ function uc_product_theme() {
       'variables' => array('body' => '', 'view_mode' => 'full'),
     ),
     'uc_product_add_to_cart' => array(
-      'variables' => array('form' => NULL, 'view_mode' => 'full'),
+      'variables' => array('node' => NULL, 'view_mode' => 'full'),
     ),
     'uc_product_price' => array(
       'render element' => 'element',
@@ -640,25 +640,27 @@ function uc_product_load($nodes) {
       $nodes[$node->nid]->$field = $value;
     }
     $nodes[$node->nid]->price = $nodes[$node->nid]->sell_price;
-    $nodes[$node->nid]->display_price_adjustment = 0;
-    $nodes[$node->nid]->display_price_suffixes = array();
   }
 }
 
 /**
- * Gets a specific altered variant of a product.
- * 
+ * Gets a specific altered variant of a product.  The (possibly cached) base product remains unaltered.
+ *
  * @param $nid
- *   The nid of the product.  May also supply a complete, previously loaded product object.
+ *   The nid of the product to load.
  * @param $data
  *   Optional data to add to the product before invoking the alter hooks.
+ *
+ * @return
+ *   An variant of the product, altered based on the provided data.
  */
 function uc_product_load_variant($nid, $data = FALSE) {
-  $node = clone (is_object($nid) ? $nid : node_load($nid));
+  $node = clone node_load($nid);
   if (!empty($data)) {
     $node->data = $data;
   }
   drupal_alter('uc_product', $node);
+  $node->variant = TRUE;
   return $node;
 }
 
@@ -689,11 +691,15 @@ function uc_product_delete(&$node) {
  * Implements hook_view().
  */
 function uc_product_view($node, $view_mode) {
-  $variant = uc_product_load_variant($node);
+  // Give modules a chance to alter this product.  If it is a variant, this will have been
+  // done already by uc_product_load_variant(), so we check a flag to be sure not to
+  // alter twice -- cf. entity_prepare_view().
+  $variant = empty($node->variant) ? uc_product_load_variant($node->nid) : $node;
+
   $node->content['display_price'] = array(
     '#theme' => 'uc_product_price',
-    '#value' => $variant->price + $variant->display_price_adjustment,
-    'suffix' => array('#markup' => implode(' ', $variant->display_price_suffixes)),
+    '#value' => $variant->price,
+    '#suffixes' => array(),
     '#attributes' => array(
       'class' => array(
         'display-price',
@@ -750,13 +756,15 @@ function uc_product_view($node, $view_mode) {
     '#units' => $variant->length_units,
     '#view_mode' => $view_mode,
   );
-  if (module_exists('uc_cart') && isset($node->nid)) {
+
+  if (module_exists('uc_cart') && isset($variant->nid)) {
     $node->content['add_to_cart'] = array(
       '#theme' => 'uc_product_add_to_cart',
-      '#form' => drupal_get_form('uc_product_add_to_cart_form_' . $node->nid, $node),
       '#view_mode' => $view_mode,
+      '#node' => $variant,
     );
   }
+  $node->content['#node'] = $variant;
 
   return $node;
 }
@@ -1052,8 +1060,8 @@ function uc_product_uc_cart_display($item) {
     '#markup' => node_access('view', $node) ? l($item->title, 'node/' . $node->nid) : check_plain($item->title),
   );
 
-  $element['#total'] = ($item->price + $item->display_price_adjustment) * $item->qty;
-  $element['#suffix'] = implode(' ', $item->display_price_suffixes);
+  $element['#total'] = $item->price * $item->qty;
+  $element['#suffixes'] = array();
   $element['data'] = array('#type' => 'hidden', '#value' => serialize($item->data));
   $element['qty'] = array(
     '#type' => 'uc_quantity',
@@ -1233,8 +1241,10 @@ function theme_uc_product_body($variables) {
  * @ingroup themeable
  */
 function theme_uc_product_add_to_cart($variables) {
+  $node = $variables['node'];
+
   $output = '<div class="add-to-cart">';
-  $output .= drupal_render($variables['form']);
+  $output .= drupal_render(drupal_get_form('uc_product_add_to_cart_form_' . $node->nid, $node));
   $output .= '</div>';
 
   return $output;
@@ -1322,7 +1332,11 @@ function theme_uc_product_price($variables) {
   if ($label) {
     $output .= '<span class="uc-price-label">' . $label . '</span> ';
   }
-  $output .= theme('uc_price', array('price' => $price));
+  $vars = array('price' => $price);
+  if (!empty($element['#suffixes'])) {
+    $vars['suffixes'] = $element['#suffixes'];
+  }
+  $output .= theme('uc_price', $vars);
   $output .= drupal_render_children($element);
   $output .= '</div>';
 
diff --git a/uc_product/views/uc_product_handler_field_display_price.inc b/uc_product/views/uc_product_handler_field_display_price.inc
index b7de82b..7517c5a 100644
--- a/uc_product/views/uc_product_handler_field_display_price.inc
+++ b/uc_product/views/uc_product_handler_field_display_price.inc
@@ -20,7 +20,8 @@ class uc_product_handler_field_display_price extends uc_product_handler_field_pr
 
   function get_value($values, $field = NULL) {
     $nid = parent::get_value($values, $field);
-    $node = node_load($nid);
-    return $node->price + $node->display_price_adjustment;
+    // !TODO Refactor so that all variants are loaded at once in the pre_render hook.
+    $node = node_view(node_load($nid));
+    return $node['display_price']['#value'];
   }
 }
diff --git a/uc_product_kit/uc_product_kit.module b/uc_product_kit/uc_product_kit.module
index f5a58c9..d42efc8 100644
--- a/uc_product_kit/uc_product_kit.module
+++ b/uc_product_kit/uc_product_kit.module
@@ -381,18 +381,6 @@ function uc_product_kit_load($nodes) {
 
   // Add product data to kits.
   uc_product_load($nodes);
-
-  // Recalculate display price.
-  foreach ($nodes as &$node) {
-    if ($node->mutable != UC_PRODUCT_KIT_MUTABLE) {
-      $node->display_price_adjustment = 0;
-      $node->display_price_suffixes = array();
-      foreach ($node->products as $pid => &$product) {
-        $node->display_price_adjustment += $product->display_price_adjustment * $product->qty;
-        $node->display_price_suffixes += $product->display_price_suffixes;
-      }
-    }
-  }
 }
 
 /**
@@ -677,11 +665,31 @@ function theme_uc_product_kit_items_form($variables) {
  * Implements hook_view().
  */
 function uc_product_kit_view($node, $view_mode) {
-  $variant = uc_product_load_variant($node);
+  // Give modules a chance to alter this product.  If it is a variant, this will have been done
+  // already by uc_product_load_variant(), so we check a flag to be sure not to alter twice.
+  $variant = empty($node->variant) ? uc_product_load_variant($node->nid) : $node;
+
+  // Calculate the display price.
+  $display_price = 0;
+  $suffixes = array();
+  if ($node->mutable != UC_PRODUCT_KIT_MUTABLE) {
+    // If this is a non-mutable kit, then sum the display price of each of the component products.
+    foreach ($variant->products as $product) {
+      $build = node_view($product);
+      $display_price += $build['display_price']['#value'] * $product->qty;
+      $suffixes += $build['display_price']['#suffixes'];
+    }
+  }
+  else {
+    // For mutable, just use the price.
+    $display_price = $variant->price;
+    $suffixes = array();
+  }
+
   $node->content['display_price'] = array(
     '#theme' => 'uc_product_price',
-    '#value' => $variant->price + $variant->display_price_adjustment,
-    'suffix' => array('#markup' => implode(' ', $variant->display_price_suffixes)),
+    '#value' => $display_price,
+    '#suffixes' => $suffixes,
     '#attributes' => array(
       'class' => array(
         'product-kit',
@@ -739,7 +747,7 @@ function uc_product_kit_view($node, $view_mode) {
     $node->content['products'] = array('#weight' => 6);
 
     $i = 0;
-    foreach ($variant->products as $product) {
+    foreach ($node->products as $product) {
       $node->content['products'][$product->nid]['qty'] = array(
         '#markup' => '<div class="product-qty">' . theme('uc_product_kit_list_item', array('product' => $product)) . '</div>',
       );
@@ -749,12 +757,12 @@ function uc_product_kit_view($node, $view_mode) {
 
   if (module_exists('uc_cart')) {
     $node->content['add_to_cart'] = array(
-      '#theme' => 'uc_product_kit_add_to_cart',
-      '#node' => $node,
-      '#view_mode' => $view_mode,
+			'#theme' => 'uc_product_kit_add_to_cart',
+			'#node' => $variant,
+			'#view_mode' => $view_mode,
     );
   }
-
+  $node->content['#node'] = $variant;
   return $node;
 }
 
@@ -1030,20 +1038,20 @@ function uc_product_kit_uc_add_to_cart($nid, $qty, $kit_data) {
 /**
  * Implements hook_uc_product_alter().
  */
-function uc_product_kit_uc_product_alter(&$node) {
-  if (isset($node->data['kit_id'])) {
+function uc_product_kit_uc_product_alter(&$variant) {
+  if (isset($variant->data['kit_id'])) {
     // If this is a kit component load, we would cause infinite recursion trying
     // to node_load() the parent, but we already have the discount available.
-    if (isset($node->data['kit_discount'])) {
-      $discount = $node->data['kit_discount'];
+    if (isset($variant->data['kit_discount'])) {
+      $discount = $variant->data['kit_discount'];
     }
-    elseif (($kit = node_load($node->data['kit_id'])) && $kit->mutable != UC_PRODUCT_KIT_MUTABLE) {
-      $discount = $kit->products[$node->nid]->discount;
+    elseif (($kit = node_load($variant->data['kit_id'])) && $kit->mutable != UC_PRODUCT_KIT_MUTABLE) {
+      $discount = $kit->products[$variant->nid]->discount;
     }
     else {
       $discount = 0;
     }
-    $node->price += $discount;
+    $variant->price += $discount;
   }
 }
 
@@ -1092,6 +1100,7 @@ function uc_product_kit_uc_order_product_alter(&$product, $order) {
   // Don't save the base kit node, though.
   $product->skip_save = TRUE;
 }
+
 /**
  * Implements hook_uc_cart_display().
  *
@@ -1135,8 +1144,9 @@ function uc_product_kit_uc_cart_display($item) {
       );
       $element['description'] = array('#markup' => '');
       $element['#total'] = 0;
-      $element['#suffix'] = implode(' ', $kit->display_price_suffixes);
+      $element['#suffixes'] = array();
       $element['#extra'] = array();
+      $element['#entity'] = $kit; // Override the entity associated with this render-array to be the kit itself.
 
       $elements[$unique_id] = $element;
     }
@@ -1150,7 +1160,9 @@ function uc_product_kit_uc_cart_display($item) {
       );
     }
 
-    $elements[$unique_id]['#total'] += ($item->price + $item->display_price_adjustment) * $item->qty;
+    $build = node_view($item);
+    $elements[$unique_id]['#total'] += $build['display_price']['#value'] * $item->qty;
+    $elements[$unique_id]['#suffixes'] += $build['display_price']['#suffixes'];
     $elements[$unique_id]['data'][$item->nid] = $item;
     $products[$unique_id][] = $item->nid;
 
@@ -1172,6 +1184,7 @@ function uc_product_kit_uc_cart_display($item) {
       $element = $elements[$unique_id];
       unset($products[$unique_id]);
       unset($elements[$unique_id]);
+      //  dpm($element);
       return $element;
     }
   }
diff --git a/uc_store/uc_store.module b/uc_store/uc_store.module
index 1945c2b..59bacac 100644
--- a/uc_store/uc_store.module
+++ b/uc_store/uc_store.module
@@ -516,7 +516,7 @@ function uc_store_theme() {
       'render element' => 'element',
     ),
     'uc_price' => array(
-      'variables' => array('price' => 0),
+      'variables' => array('price' => 0, 'suffixes' => array()),
     ),
     'uc_qty_label' => array(
       'variables' => array(),
@@ -699,11 +699,16 @@ function uc_store_reviews() {
  * @param $variables
  *   An associative array containing:
  *   - price: A numerical price value.
+ *   - suffixes: An array of suffixes to be attached to this price.
  *
  * @ingroup themeable
  */
 function theme_uc_price($variables) {
-  return '<span class="uc-price">' . uc_currency_format($variables['price']) . '</span>';
+  $output = '<span class="uc-price">' . uc_currency_format($variables['price']) . '</span>';
+  if (!empty($variables['suffixes'])) {
+    $output .= '<span class="price-suffixes">'. implode(' ', $variables['suffixes']) .'</span>';
+  }
+  return $output;
 }
 
 /**
diff --git a/uc_taxes/uc_taxes.module b/uc_taxes/uc_taxes.module
index f26841d..1bba68a 100644
--- a/uc_taxes/uc_taxes.module
+++ b/uc_taxes/uc_taxes.module
@@ -76,7 +76,7 @@ function uc_taxes_menu() {
  * calculations are made.
  */
 function uc_taxes_module_implements_alter(&$implementations, $hook) {
-  if (in_array($hook, array('uc_order', 'uc_product_alter'))) {
+  if (in_array($hook, array('uc_order', 'entity_view_alter'))) {
     $group = $implementations['uc_taxes'];
     unset($implementations['uc_taxes']);
     $implementations['uc_taxes'] = $group;
@@ -105,16 +105,43 @@ function uc_taxes_form_uc_order_edit_form_alter(&$form, &$form_state) {
 }
 
 /**
- * Implements hook_uc_product_alter().
+ * Implements hook_entity_view().
  *
  * Adds included taxes (VAT) to display price of applicable products.
  */
-function uc_taxes_uc_product_alter(&$node) {
-  list($amount, $suffixes) = uc_taxes_get_included_tax($node, $node->price);
+function uc_taxes_entity_view_alter(&$build, $entity_type) {
+  switch ($entity_type) {
+    case 'node':
+      if (uc_product_is_product($build['#node'])) {
+        list($amount, $suffixes) = uc_taxes_get_included_tax($build['#node']);
+        $build['display_price']['#value'] += $amount;
+        if (!empty($suffixes)) {
+          $build['display_price']['#suffixes'] += $suffixes;
+        }
+      }
+      break;
+
+    case 'uc_cart_item':
+      if (!empty($build['#total'])) {
+        list($amount, $suffixes) = uc_taxes_get_included_tax($build['#entity']);
+        if (!empty($amount)) {
+          $build['#total'] += $amount * $build['qty']['#default_value'];
+        }
+        if (!empty($suffixes)) {
+          $build['#suffixes'] += $suffixes;
+        }
+      }
+      break;
 
-  $node->display_price_adjustment += $amount;
-  if (!empty($suffixes)) {
-    $node->display_price_suffixes += $suffixes;
+    case 'uc_order_product':
+      list($amount, $suffixes) = uc_taxes_get_included_tax($build['#entity']);
+      $build['price']['#price'] += $amount;
+      $build['total']['#total'] += $amount * $build['#entity']->qty;
+      if (!empty($suffixes)) {
+        $build['price']['#suffixes'] += $suffixes;
+        $build['total']['#suffixes'] += $suffixes;
+      }
+      break;
   }
 }
 
@@ -598,16 +625,13 @@ function uc_taxes_apply_tax($order, $tax) {
  * Calculates the taxes that should be included in the display price of a product.
  * @param $product
  *   The product whose included taxes are to be calculated.
- * @param $price
- *   If specified, use this value as the tax-base.  Otherwise use the product sell price.
  */
-function uc_taxes_get_included_tax($product, $price = FALSE) {
+function uc_taxes_get_included_tax($product) {
   $amount = 0;
   $suffixes = array();
-  $price = ($price === FALSE) ? $product->sell_price : $price;
   foreach (uc_taxes_rate_load() as $tax) {
     if ($tax->display_include && in_array($product->type, $tax->taxed_product_types) && ($tax->shippable == 0 || $product->shippable == 1)) {
-      $amount += $price * $tax->rate;
+      $amount += $product->price * $tax->rate;
       $suffixes[$tax->inclusion_text] = $tax->inclusion_text;
     }
   }
-- 
1.7.3.4


From c8d2a366a09e3e62d890023f9138bb87ccf0bcd6 Mon Sep 17 00:00:00 2001
From: Chris Oden <wodenx@gmail.com>
Date: Wed, 7 Dec 2011 14:21:04 -0500
Subject: [PATCH 07/14] Some minor fixes to previous commit

---
 uc_attribute/uc_attribute.module     |    2 +-
 uc_cart/uc_cart.module               |    2 +-
 uc_product_kit/uc_product_kit.module |    1 -
 3 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/uc_attribute/uc_attribute.module b/uc_attribute/uc_attribute.module
index f0705f8..eb427b7 100644
--- a/uc_attribute/uc_attribute.module
+++ b/uc_attribute/uc_attribute.module
@@ -1267,7 +1267,7 @@ function _uc_attribute_alter_form($product) {
             break;
           }
         case 'adjustment':
-          $base = node_view($product);
+          $base = node_view(uc_product_load_variant($product->nid));
           $adjustment = $variant['display_price']['#value'] - $base['display_price']['#value'];
           if ($adjustment) {
             $display_price = ($adjustment > 0 ? '+' : '') . uc_currency_format($adjustment);
diff --git a/uc_cart/uc_cart.module b/uc_cart/uc_cart.module
index 39c3834..76918cc 100644
--- a/uc_cart/uc_cart.module
+++ b/uc_cart/uc_cart.module
@@ -1286,7 +1286,7 @@ function uc_cart_get_contents($cid = NULL, $action = NULL) {
       ->propertyOrderBy('cart_item_id', 'ASC')
       ->execute();
     if (!empty($result['uc_cart_item'])) {
-      $items[$cid] = entity_load('uc_cart_item', array_keys($result['uc_cart_item']));
+      $items[$cid] = entity_load('uc_cart_item', array_keys($result['uc_cart_item']), NULL, TRUE);
       // Create a bare order and attach it to each item as context.
       $order = new UcOrder();
       $order->uid = $cid;
diff --git a/uc_product_kit/uc_product_kit.module b/uc_product_kit/uc_product_kit.module
index d42efc8..d7f7ede 100644
--- a/uc_product_kit/uc_product_kit.module
+++ b/uc_product_kit/uc_product_kit.module
@@ -1184,7 +1184,6 @@ function uc_product_kit_uc_cart_display($item) {
       $element = $elements[$unique_id];
       unset($products[$unique_id]);
       unset($elements[$unique_id]);
-      //  dpm($element);
       return $element;
     }
   }
-- 
1.7.3.4


From 79457b3f24c72ba6777f7d0249451497ed34aacb Mon Sep 17 00:00:00 2001
From: Chris Oden <wodenx@gmail.com>
Date: Wed, 7 Dec 2011 14:22:05 -0500
Subject: [PATCH 08/14] Add uc_qty theme

---
 uc_cart/uc_cart.module            |    2 +-
 uc_cart/uc_cart_checkout_pane.inc |    4 ++--
 uc_store/uc_store.module          |   15 +++++++++++++++
 3 files changed, 18 insertions(+), 3 deletions(-)

diff --git a/uc_cart/uc_cart.module b/uc_cart/uc_cart.module
index 76918cc..8db8355 100644
--- a/uc_cart/uc_cart.module
+++ b/uc_cart/uc_cart.module
@@ -310,7 +310,7 @@ function uc_cart_block_view($delta = '') {
           if (count(element_children($display_item))) {
             $items[] = array(
               'nid' => $display_item['nid']['#value'],
-              'qty' => t('@qty&times;', array('@qty' => $display_item['qty']['#default_value'])),
+              'qty' => theme('uc_qty', array('qty' => $display_item['qty']['#default_value'])),
               'title' => $display_item['title']['#markup'],
               'price' => $display_item['#total'],
               'desc' => isset($display_item['description']['#markup']) ? $display_item['description']['#markup'] : FALSE,
diff --git a/uc_cart/uc_cart_checkout_pane.inc b/uc_cart/uc_cart_checkout_pane.inc
index 68477bb..2bfb8c5 100644
--- a/uc_cart/uc_cart_checkout_pane.inc
+++ b/uc_cart/uc_cart_checkout_pane.inc
@@ -500,8 +500,8 @@ function theme_uc_cart_review_table($variables) {
       if (!empty($display_item['description']['#markup'])) {
         $description .= $display_item['description']['#markup'];
       }
-      $qty = t('@qty&times;', array('@qty' => $display_item['qty']['#default_value']));
-      $suffix = !empty($display_item['#suffix']) ? $display_item['#suffix'] : '';
+      $qty = theme('uc_qty', array('qty' => $display_item['qty']['#default_value']));
+      $suffix = !empty($display_item['#suffixes']) ? implode(' ', $display_item['#suffixes']) : '';
 
       $rows[] = array(
         array('data' => $qty, 'class' => array('qty')),
diff --git a/uc_store/uc_store.module b/uc_store/uc_store.module
index 59bacac..037413c 100644
--- a/uc_store/uc_store.module
+++ b/uc_store/uc_store.module
@@ -521,6 +521,9 @@ function uc_store_theme() {
     'uc_qty_label' => array(
       'variables' => array(),
     ),
+    'uc_qty' => array(
+      'variables' => array('qty' => 1),
+    ),
     'uc_uid' => array(
       'variables' => array('uid' => 0),
     ),
@@ -721,6 +724,18 @@ function theme_uc_qty_label() {
 }
 
 /**
+ * Displays a quantity.
+ *
+ * @param $variables
+ *   -qty = The quantity to display.
+ *
+ * @ingroup themeable
+ */
+function theme_uc_qty($variables) {
+  return t('@qty &times;', array('@qty' => $variables['qty']));
+}
+
+/**
  * Displays a username in the standard format and with consistent markup.
  *
  * @param $variables
-- 
1.7.3.4


From 1490afb08a921513688958fc3f46b91d970406ab Mon Sep 17 00:00:00 2001
From: Chris Oden <wodenx@gmail.com>
Date: Wed, 7 Dec 2011 14:22:37 -0500
Subject: [PATCH 09/14] Implement product_alter mechanism for order products.

---
 uc_cart/uc_cart_checkout_pane.inc    |   14 ++++--
 uc_order/uc_order.info.inc           |   87 +++++++++++----------------------
 uc_order/uc_order.module             |   69 ++++++++++++++++++++++----
 uc_order/uc_order.order_pane.inc     |   36 +++++++-------
 uc_product/uc_product.module         |   10 +++-
 uc_product_kit/uc_product_kit.module |    1 -
 uc_taxes/uc_taxes.module             |   56 ++++++++++++++++------
 7 files changed, 163 insertions(+), 110 deletions(-)

diff --git a/uc_cart/uc_cart_checkout_pane.inc b/uc_cart/uc_cart_checkout_pane.inc
index 2bfb8c5..2e2fa34 100644
--- a/uc_cart/uc_cart_checkout_pane.inc
+++ b/uc_cart/uc_cart_checkout_pane.inc
@@ -553,11 +553,15 @@ function theme_uc_cart_review_table($variables) {
  */
 function theme_uc_checkout_pane_cart_review($variables) {
   $output = '<table>';
-  foreach ($variables['items'] as $item) {
-    $desc = check_plain($item->title) . uc_product_get_description($item);
-
-    $output .= '<tr valign="top"><td>' . $item->qty . '&times;</td><td width="100%">' . $desc
-              . '</td><td nowrap="nowrap">' . theme('uc_price', array('price' => $item->price * $item->qty)) . '</td></tr>';
+  $display_items = entity_view('uc_order_product', $variables['items']);
+  foreach (element_children($display_items['uc_order_product']) as $key) {
+    $display_item = $display_items['uc_order_product'][$key];
+    $output .= '<tr valign="top"><td nowrap="nowrap">'
+              . render($display_item['qty'])
+              .'</td><td width="100%">'
+              . render($display_item['product'])
+              . '</td><td nowrap="nowrap">'
+              . render($display_item['total']) . '</td></tr>';
   }
   $output .= '</table>';
 
diff --git a/uc_order/uc_order.info.inc b/uc_order/uc_order.info.inc
index 2e45576..214fcd2 100644
--- a/uc_order/uc_order.info.inc
+++ b/uc_order/uc_order.info.inc
@@ -12,64 +12,6 @@ function uc_order_entity_property_info() {
   $address_info = uc_address_property_info();
 
   return array(
-    'uc_order_product' => array(
-      'properties' => array(
-        'order_product_id' => array(
-          'type' => 'integer',
-          'label' => t('Order product ID'),
-          'description' => t('The unique ID for the purchased product.'),
-        ),
-        'order_id' => array(
-          'type' => 'integer',
-          'label' => t('Order ID'),
-          'description' => t('The order ID that owns the product.'),
-          'required' => TRUE,
-        ),
-        'nid' => array(
-          'type' => 'integer',
-          'label' => t('Node ID'),
-          'description' => t('The unique ID of the node representing the product.'),
-          'clear' => array('node'),
-        ),
-        'node' => array(
-          'type' => 'node',
-          'label' => t('Node'),
-          'description' => t('The node representing the product.'),
-          'getter callback' => 'uc_order_product_node_property_get',
-          'setter callback' => 'uc_order_product_node_property_set',
-        ),
-        'title' => array(
-          'type' => 'text',
-          'label' => t('Title'),
-          'description' => t('The product title.'),
-        ),
-        'model' => array(
-          'type' => 'text',
-          'label' => t('Model/SKU'),
-          'description' => t('The model number of the product.'),
-        ),
-        'qty' => array(
-          'type' => 'integer',
-          'label' => t('Quantity'),
-          'description' => t('The number of the same product ordered.'),
-        ),
-        'cost' => array(
-          'type' => 'decimal',
-          'label' => t('Cost'),
-          'description' => t('The cost to the store for the product.'),
-        ),
-        'price' => array(
-          'type' => 'decimal',
-          'label' => t('Price'),
-          'description' => t('The price paid for the ordered product.'),
-        ),
-        'weight' => array(
-          'type' => 'decimal',
-          'label' => t('Weight'),
-          'description' => t('The physical weight of the product.'),
-        ),
-      ),
-    ),
     'uc_order' => array(
       'properties' => array(
         'order_id' => array(
@@ -171,3 +113,32 @@ function uc_order_entity_property_info() {
     ),
   );
 }
+
+/**
+ * Metadata controller class for uc_order_product entities.
+ */
+class UcOrderProductMetadataController extends EntityDefaultMetadataController {
+  public function entityPropertyInfo() {
+    $props = parent::entityPropertyInfo();
+    // Copy the descriptions from the schema. Drupal discards this information, so we have to
+    // call uc_order_schema() directly.
+    module_load_include('install', 'uc_order', 'uc_order');
+    $schema = uc_order_schema();
+    foreach ($schema['uc_order_products']['fields'] as $name => $info) {
+      if (is_array($props['uc_order_product']['properties'][$name]) && !empty($info['description'])) {
+        $props['uc_order_product']['properties'][$name]['description'] = $info['description'];
+      }
+    }
+
+    // Add the 'node' property.
+    $props['uc_order_product']['properties']['node'] = array(
+      'type' => 'node',
+      'label' => t('Node'),
+      'description' => t('The node representing the product.'),
+      'getter callback' => 'uc_order_product_node_property_get',
+      'setter callback' => 'uc_order_product_node_property_set',
+    );
+
+    return $props;
+  }
+}
diff --git a/uc_order/uc_order.module b/uc_order/uc_order.module
index 4d9c32b..931c397 100644
--- a/uc_order/uc_order.module
+++ b/uc_order/uc_order.module
@@ -550,6 +550,8 @@ function uc_order_entity_info() {
     'uc_order_product' => array(
       'label' => t('Order product'),
       'base table' => 'uc_order_products',
+      'controller class' => 'UcOrderProductController',
+      'metadata controller class' => 'UcOrderProductMetadataController',
       'entity keys' => array(
         'id' => 'order_product_id',
       ),
@@ -1217,25 +1219,70 @@ function uc_order_view_invoice($order, $print = FALSE) {
   return $build;
 }
 
+
+
+class UcOrderProductController extends EntityAPIController {
+  public function buildContent($product, $view_mode = 'full', $langcode = NULL, $content = array()) {
+    $content['qty'] = array(
+      '#theme' => 'uc_qty',
+      '#qty' => $product->qty,
+    );
+    $node = node_load($product->nid);
+    $title = node_access('view', $node) ? l($product->title, 'node/'. $node->nid) : check_plain($product->title);
+    $content['product'] = array(
+      '#markup' => $title . uc_product_get_description($product),
+    );
+    $content['model'] = array(
+      '#markup' => check_plain($product->model),
+      '#cell_attributes' => array('align' => 'center'),
+    );
+
+    if (user_access('administer products')) {
+      $content['cost'] = array(
+        '#theme' => 'uc_price',
+        '#price' => $product->cost,
+        '#cell_attributes' => array('align' => 'right'),
+      );
+    }
+    $content['price'] = array(
+      '#theme' => 'uc_price',
+      '#price' => $product->price,
+      '#suffixes' => array(),
+      '#cell_attributes' => array('align' => 'right'),
+    );
+    $content['total'] = array(
+      '#theme' => 'uc_price',
+      '#price' => $product->price * $product->qty,
+      '#suffixes' => array(),
+      '#cell_attributes' => array('align' => 'right'),
+    );
+    $content['#attributes'] = array('valign' => 'top');
+    return parent::buildContent($product, $view_mode, $langcode, $content);
+  }
+}
+
 /**
  * Controller class for uc_order entity.
  */
 class UcOrderController extends DrupalDefaultEntityController {
 
-  /**
-   *
-   */
   function attachLoad(&$orders, $revision_id = FALSE) {
     foreach ($orders as &$order) {
       $order->data = unserialize($order->data);
-
-      // !FIXME Refactor to use uc_order_product_load().
-      $result = db_query("SELECT * FROM {uc_order_products} WHERE order_id = :id ORDER BY order_product_id", array(':id' => $order->order_id));
-      $order->products = array();
-      foreach ($result as $product) {
-        $product->data = unserialize($product->data);
-        $product->order_uid = $order->uid;
-        $order->products[] = $product;
+      $efq = new EntityFieldQuery();
+      $result = $efq->entityCondition('entity_type', 'uc_order_product')
+        ->propertyCondition('order_id', $order->order_id)
+        ->propertyOrderBy('order_product_id', 'ASC')
+        ->execute();
+      if (!empty($result['uc_order_product'])) {
+        $order->products = entity_load('uc_order_product', array_keys($result['uc_order_product']), NULL, TRUE);
+        foreach ($order->products as $product) {
+          $product->order = $order;
+          $product->order_uid = $order->uid;
+        }
+      }
+      else {
+        $order->products = array();
       }
 
       uc_order_module_invoke('load', $order, NULL);
diff --git a/uc_order/uc_order.order_pane.inc b/uc_order/uc_order.order_pane.inc
index f7bf1ac..89b75ad 100644
--- a/uc_order/uc_order.order_pane.inc
+++ b/uc_order/uc_order.order_pane.inc
@@ -376,7 +376,7 @@ function uc_order_add_product_form($form, &$form_state, $order, $node) {
 function uc_order_edit_products_form($form, &$form_state, $products) {
   if (($product_count = count($products)) > 0) {
     $form['products'] = tapir_get_table('uc_op_products_edit_table');
-    for ($i = 0; $i < $product_count; $i++) {
+    for ($i=0, $product = reset($products); $i < $product_count; $i++, $product = next($products)) {
       $form['products'][$i]['remove'] = array(
         '#type' => 'image_button',
         '#title' => t('Remove this product.'),
@@ -385,27 +385,27 @@ function uc_order_edit_products_form($form, &$form_state, $products) {
           'style' => 'padding-top: 1px; padding-left: .8em; padding-right: 2px; float: left; cursor: pointer;',
         ),
         '#submit' => array('uc_order_edit_products_remove', 'uc_order_edit_form_submit'),
-        '#return_value' => $products[$i]->order_product_id,
+        '#return_value' => $product->order_product_id,
       );
       $form['products'][$i]['order_product_id'] = array(
         '#type' => 'hidden',
-        '#value' => $products[$i]->order_product_id,
+        '#value' => $product->order_product_id,
       );
       $form['products'][$i]['nid'] = array(
         '#type' => 'hidden',
-        '#value' => $products[$i]->nid,
+        '#value' => $product->nid,
       );
       $form['products'][$i]['qty'] = array(
         '#type' => 'uc_quantity',
         '#title' => theme('uc_qty_label'),
         '#title_display' => 'invisible',
-        '#default_value' => $products[$i]->qty,
+        '#default_value' => $product->qty,
       );
       $form['products'][$i]['title'] = array(
         '#type' => 'textfield',
         '#title' => t('Title'),
         '#title_display' => 'invisible',
-        '#default_value' => $products[$i]->title,
+        '#default_value' => $product->title,
         '#size' => 30,
         '#maxlength' => 255,
       );
@@ -413,14 +413,14 @@ function uc_order_edit_products_form($form, &$form_state, $products) {
         '#type' => 'textfield',
         '#title' => t('SKU'),
         '#title_display' => 'invisible',
-        '#default_value' => $products[$i]->model,
+        '#default_value' => $product->model,
         '#size' => 6,
       );
       $form['products'][$i]['weight'] = array(
         '#type' => 'textfield',
         '#title' => t('Weight'),
         '#title_display' => 'invisible',
-        '#default_value' => $products[$i]->weight,
+        '#default_value' => $product->weight,
         '#size' => 3,
       );
       $units = array(
@@ -433,26 +433,26 @@ function uc_order_edit_products_form($form, &$form_state, $products) {
         '#type' => 'select',
         '#title' => t('Units'),
         '#title_display' => 'invisible',
-        '#default_value' => $products[$i]->weight_units,
+        '#default_value' => $product->weight_units,
         '#options' => $units,
       );
       $form['products'][$i]['cost'] = array(
         '#type' => 'uc_price',
         '#title' => t('Cost'),
         '#title_display' => 'invisible',
-        '#default_value' => $products[$i]->cost,
+        '#default_value' => $product->cost,
         '#size' => 5,
       );
       $form['products'][$i]['price'] = array(
         '#type' => 'uc_price',
         '#title' => t('Price'),
         '#title_display' => 'invisible',
-        '#default_value' => $products[$i]->price,
+        '#default_value' => $product->price,
         '#size' => 5,
       );
       $form['products'][$i]['data'] = array(
         '#type' => 'hidden',
-        '#value' => serialize($products[$i]->data),
+        '#value' => serialize($product->data),
       );
     }
   }
@@ -1118,10 +1118,9 @@ function uc_op_products_view_table($order) {
     'weight' => 5,
   );
 
-  if (is_array($order->products)) {
-    foreach ($order->products as $product) {
-      $table['#rows'][] = $data;
-    }
+  if (!empty($order->products)) {
+    $build = entity_view('uc_order_product', $order->products);
+    $table['#rows'] = $build['uc_order_product'];
   }
   else {
     $table['#rows'][]['product'] = array(
@@ -1189,8 +1188,9 @@ function uc_op_products_customer_table($order) {
     'weight' => 5,
   );
 
-  if (is_array($order->products)) {
-    $table['#rows'] = entity_view('uc_order_product', $order->products);
+  if (!empty($order->products)) {
+    $build = entity_view('uc_order_product', $order->products);
+    $table['#rows'] = $build['uc_order_product'];
   }
   else {
     $table['#rows'][]['product'] = array(
diff --git a/uc_product/uc_product.module b/uc_product/uc_product.module
index 5df140c..26b0ad9 100644
--- a/uc_product/uc_product.module
+++ b/uc_product/uc_product.module
@@ -1107,10 +1107,16 @@ function uc_product_uc_update_cart_item($nid, $data = array(), $qty, $cid = NULL
 function uc_product_uc_add_to_cart_data($form_values) {
   if (isset($form_values['nid'])) {
     $node = node_load($form_values['nid']);
-    return array('shippable' => isset($node->shippable) ? $node->shippable : variable_get('uc_product_shippable_' . $node->type, 1));
+    return array(
+      'shippable' => isset($node->shippable) ? $node->shippable : variable_get('uc_product_shippable_' . $node->type, 1),
+      'type' => $node->type
+    );
   }
   else {
-    return array('shippable' => variable_get('uc_product_shippable_product', 1));
+    return array(
+      'shippable' => variable_get('uc_product_shippable_product', 1),
+      'type' => 'product',
+    );
   }
 }
 
diff --git a/uc_product_kit/uc_product_kit.module b/uc_product_kit/uc_product_kit.module
index d7f7ede..bd64e16 100644
--- a/uc_product_kit/uc_product_kit.module
+++ b/uc_product_kit/uc_product_kit.module
@@ -1070,7 +1070,6 @@ function uc_product_kit_uc_order_product_alter(&$product, $order) {
   if (empty($product->type) || $product->type !== 'product_kit') {
     return;
   }
-
   // Have to save each individual product if this is a kit.
   foreach ($product->products as $kit_product) {
     $kit_product->price = $kit_product->sell_price;
diff --git a/uc_taxes/uc_taxes.module b/uc_taxes/uc_taxes.module
index 1bba68a..3325567 100644
--- a/uc_taxes/uc_taxes.module
+++ b/uc_taxes/uc_taxes.module
@@ -105,7 +105,7 @@ function uc_taxes_form_uc_order_edit_form_alter(&$form, &$form_state) {
 }
 
 /**
- * Implements hook_entity_view().
+ * Implements hook_entity_view_alter().
  *
  * Adds included taxes (VAT) to display price of applicable products.
  */
@@ -136,7 +136,7 @@ function uc_taxes_entity_view_alter(&$build, $entity_type) {
     case 'uc_order_product':
       list($amount, $suffixes) = uc_taxes_get_included_tax($build['#entity']);
       $build['price']['#price'] += $amount;
-      $build['total']['#total'] += $amount * $build['#entity']->qty;
+      $build['total']['#price'] += $amount * $build['#entity']->qty;
       if (!empty($suffixes)) {
         $build['price']['#suffixes'] += $suffixes;
         $build['total']['#suffixes'] += $suffixes;
@@ -540,22 +540,44 @@ function uc_taxes_uc_calculate_tax($order) {
 }
 
 /**
- * Calculates tax for a single product.
+ * Calculates taxable amount for a single product.
  */
 function uc_taxes_apply_item_tax($item, $tax) {
-  $node = node_load($item->nid);
+  // Determine the product type.
+  if (isset($item->data['type'])) { // Saved in the order product data array.
+    $type = $item->data['type'];
+  }
+  elseif (empty($item->nid)) { // "Blank-line" product.
+    $type = 'blank-line';
+  }
+  elseif ($node = node_load($item->nid)) { // Use type of current node, if it exists.
+    $type = $node->type;
+  }
+  else { // Default to generic product.
+    $type = 'product';
+  }
 
-  // Special handling for manually added "Blank line" products.
-  if (!$node) {
-    $node = new stdClass();
-    $node->type = 'blank-line';
-    $node->shippable = $item->weight > 0;
+  // Determine whether this is a shippable product.
+  if (isset($item->data['shippable'])) { // Saved in the order product data array.
+    $shippable = $item->data['shippable'];
+  }
+  elseif (empty($item->nid)) { // "Blank line" product.
+    $shippable = $item->weight > 0;
+  }
+  elseif ($node = node_load($item->nid)) { // Use current node.
+    $shippable = $node->shippable;
+  }
+  else {
+    $shippable = variable_get('uc_product_shippable_' . $type); // Use default for this node type.
   }
 
   // Tax products if they are of a taxed type and if it is shippable if
   // the tax only applies to shippable products.
-  if (in_array($node->type, $tax->taxed_product_types) && ($tax->shippable == 0 || $node->shippable == 1)) {
-    return $item->price * $item->qty;
+  if (in_array($type, $tax->taxed_product_types) && ($tax->shippable == 0 || $shippable == 1)) {
+    return $item->price;
+  }
+  else {
+    return FALSE;
   }
 }
 
@@ -575,7 +597,7 @@ function uc_taxes_apply_tax($order, $tax) {
   $taxable_amount = 0;
   if (is_array($order->products)) {
     foreach ($order->products as $item) {
-      $taxable_amount += uc_taxes_apply_item_tax($item, $tax);
+      $taxable_amount += $item->qty * uc_taxes_apply_item_tax($item, $tax);
     }
   }
   $taxed_line_items = $tax->taxed_line_items;
@@ -630,10 +652,14 @@ function uc_taxes_get_included_tax($product) {
   $amount = 0;
   $suffixes = array();
   foreach (uc_taxes_rate_load() as $tax) {
-    if ($tax->display_include && in_array($product->type, $tax->taxed_product_types) && ($tax->shippable == 0 || $product->shippable == 1)) {
-      $amount += $product->price * $tax->rate;
-      $suffixes[$tax->inclusion_text] = $tax->inclusion_text;
+    if ($tax->display_include) {
+      $taxable = uc_taxes_apply_item_tax($product, $tax);
+      if (!empty($taxable)) {
+        $amount += $taxable * $tax->rate;
+        $suffixes[$tax->inclusion_text] = $tax->inclusion_text;
+      }
     }
   }
+
   return array($amount, $suffixes);
 }
-- 
1.7.3.4


From c259afc0183df2f9378603ed1c9ca302c80a1899 Mon Sep 17 00:00:00 2001
From: Chris Oden <wodenx@gmail.com>
Date: Wed, 7 Dec 2011 15:30:58 -0500
Subject: [PATCH 10/14] Check to be sure ->data is an array before testing values, bc user module sometimes sets it to a serialized string.

---
 uc_taxes/gitlog.txt          |  407 +++++++++++++++++++++++++++
 uc_taxes/uc_taxes.module     |    4 +-
 uc_taxes/uc_taxes.module.new |  640 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1049 insertions(+), 2 deletions(-)
 create mode 100755 uc_taxes/gitlog.txt
 create mode 100644 uc_taxes/uc_taxes.module.new

diff --git a/uc_taxes/gitlog.txt b/uc_taxes/gitlog.txt
new file mode 100755
index 0000000..2c18aae
--- /dev/null
+++ b/uc_taxes/gitlog.txt
@@ -0,0 +1,407 @@
+commit bb1a084cdc53c6e9ce6fa190d09bdfb11c869b18
+Author: Dave Long <dave@longwaveconsulting.com>
+Date:   Sun Sep 11 17:33:42 2011 +0100
+
+    Issue #1274786: Convert admin product listing to Views.
+
+:100644 100644 68b6516... d439f5a... M	uc_taxes/uc_taxes.module
+
+commit 500b6bb37232f5a3d19d9c77c56e64fe4c3aac9b
+Author: Dave Long <dave@longwaveconsulting.com>
+Date:   Thu Aug 25 10:42:38 2011 -0400
+
+    Issue #1164216: Make Rules a hard dependency.
+
+:100644 100644 532f4c1... 68b6516... M	uc_taxes/uc_taxes.module
+
+commit 317d0f038403aae3f7c7c992d1c1f55eb194bfd6
+Author: Lyle Mantooth <lyle@ubercart.org>
+Date:   Thu Aug 4 15:05:45 2011 -0400
+
+    Issue #1210194: Show taxes and subtotal in line item preview.
+
+:100644 100644 152db52... 532f4c1... M	uc_taxes/uc_taxes.module
+
+commit 6f028362686b1a95b788710b9bd7b0ee0ccb9c04
+Author: Dave Long <dave@longwaveconsulting.com>
+Date:   Tue Jul 26 22:45:23 2011 +0100
+
+    Fatal error: Call to a member function execute() on a non-object in uc_taxes.module on line 529.
+
+:100644 100644 a511205... 152db52... M	uc_taxes/uc_taxes.module
+
+commit 84833ca3fd39c4374ecf6f095d4d11657e1cf370
+Author: Dave Long <dave@longwaveconsulting.com>
+Date:   Tue Jul 26 20:23:09 2011 +0100
+
+    Issue #1050940: Improve settings menu titles and descriptions.
+
+:100644 100644 c8a44e2... a511205... M	uc_taxes/uc_taxes.module
+
+commit 2e087b9a76b951616677f3b73eac6bdc201998d3
+Author: Tim Rohaly <tr@202830.no-reply.drupal.org>
+Date:   Tue Jul 19 22:03:25 2011 -0700
+
+    Remove commented-out function.
+
+:100644 100644 a0c84bf... c8a44e2... M	uc_taxes/uc_taxes.module
+
+commit 08355a3c9c4df2086381968cee27189fedd743fa
+Author: Dave Long <dave@longwaveconsulting.com>
+Date:   Wed Jun 22 15:06:48 2011 +0100
+
+    Issue #1168280: Change hook_uc_line_item() to be keyed by state ID.
+
+:100644 100644 3fc6c87... a0c84bf... M	uc_taxes/uc_taxes.module
+
+commit 77978395d8416d6ebfe439459854a81cc57483c8
+Author: Dave Long <dave@longwaveconsulting.com>
+Date:   Wed May 25 14:11:17 2011 +0100
+
+    Issue #603356 by Island Usurper: uc_taxes module's weight should be set heavier on install.
+
+:100644 100644 e6aa73f... 3fc6c87... M	uc_taxes/uc_taxes.module
+
+commit ea47142996282de2744f7edd83963b0734ecf707
+Author: Dave Long <dave@longwaveconsulting.com>
+Date:   Mon May 23 22:44:11 2011 +0100
+
+    Issue #1161094 by wodenx: Skip invalid nodes in tax calculations.
+
+:100644 100644 fffa9f0... e6aa73f... M	uc_taxes/uc_taxes.module
+
+commit 023722979bee25be622b9d72067eada1751cdb72
+Author: Dave Long <dave@longwaveconsulting.com>
+Date:   Thu Apr 7 21:38:38 2011 +0100
+
+    Issue #1092738 by longwave, Danylo Dragon: Make Rules less of a dependency.
+
+:100644 100644 3ff85f0... fffa9f0... M	uc_taxes/uc_taxes.module
+
+commit 9507a47b5e5417dd8811554eda00fd8cdf102326
+Author: The Great Git Migration <tggm@no-reply.drupal.org>
+Date:   Fri Feb 25 02:07:29 2011 +0000
+
+    Stripping CVS keywords
+
+:100644 100644 46619f1... 3ff85f0... M	uc_taxes/uc_taxes.module
+
+commit 158fa034f6ae6b27412e4bb395a6ee63f63b6926
+Author: Dave Long <dave@longwaveconsulting.com>
+Date:   Thu Feb 24 22:11:14 2011 +0000
+
+    #1062988 by essbee: Taxes not taxing other tax line items.
+
+:100644 100644 a816e0f... 46619f1... M	uc_taxes/uc_taxes.module
+
+commit 74df0e852e127d10e6f6c81c5ca88c0281e9d0eb
+Author: Tim Rohaly <tr@202830.no-reply.drupal.org>
+Date:   Thu Feb 17 09:36:46 2011 +0000
+
+    Still more changes to bring documentation up to standards.
+
+:100644 100644 aaa7a3a... a816e0f... M	uc_taxes/uc_taxes.module
+
+commit 0a7e220275ed95dbff2f19d05f94963f139f8843
+Author: Dave Long <dave@longwaveconsulting.com>
+Date:   Wed Feb 16 01:25:51 2011 +0000
+
+    Move 'add a tax rate' to a local action.
+
+:100644 100644 11344aa... aaa7a3a... M	uc_taxes/uc_taxes.module
+
+commit 8983fcb52f8eb2db2ccc8fb86f7e4852c49b7536
+Author: Tim Rohaly <tr@202830.no-reply.drupal.org>
+Date:   Tue Feb 15 21:05:52 2011 +0000
+
+    Some work on conforming to documentation standards.  Also comment out uc_taxes_form_alter() because it has an empty body.
+
+:100644 100644 d523ee3... 11344aa... M	uc_taxes/uc_taxes.module
+
+commit 162fd7504c097d96fb1a1daef665acf92fb53707
+Author: Tim Rohaly <tr@202830.no-reply.drupal.org>
+Date:   Mon Feb 14 21:19:22 2011 +0000
+
+    Make documentation comments for hook implementations conform to documentation standards
+
+:100644 100644 d42cb6c... d523ee3... M	uc_taxes/uc_taxes.module
+
+commit bb92d3bf387a3f0936a79a8b7559e6ef5e322289
+Author: Lyle Mantooth <lyle@ubercart.org>
+Date:   Fri Feb 4 16:20:33 2011 +0000
+
+    Fix variable name in hook_uc_order().
+
+:100644 100644 88447df... d42cb6c... M	uc_taxes/uc_taxes.module
+
+commit 78978a09817f243688426e24e3cd5567f670a053
+Author: Dave Long <dave@longwaveconsulting.com>
+Date:   Thu Feb 3 21:13:05 2011 +0000
+
+    #1022868 by hanoii: Tax calculation modifies cached tax rates.
+
+:100644 100644 a8a8010... 88447df... M	uc_taxes/uc_taxes.module
+
+commit 1e117a39bc6c974e051d42877798a8d67fe8a582
+Author: Dave Long <dave@longwaveconsulting.com>
+Date:   Thu Feb 3 21:08:44 2011 +0000
+
+    #990744: uc_taxes does not respect hook_line_item_data_alter().
+
+:100644 100644 16d955c... a8a8010... M	uc_taxes/uc_taxes.module
+
+commit af06822af218433f58ae8484ad5b6e060564baeb
+Author: Lyle Mantooth <lyle@ubercart.org>
+Date:   Thu Oct 7 19:10:47 2010 +0000
+
+    Fix hook_menus() so breadcrumbs show up right. Remove manual breadcrumbs from those paths.
+
+:100644 100644 7fce3ce... 16d955c... M	uc_taxes/uc_taxes.module
+
+commit 536cc3301d86518b23e77629e6e921087be6966e
+Author: Lyle Mantooth <lyle@ubercart.org>
+Date:   Tue Sep 14 13:48:35 2010 +0000
+
+    Update uc_taxes_node_view() to the new price element structure.
+
+:100644 100644 2341beb... 7fce3ce... M	uc_taxes/uc_taxes.module
+
+commit 44a945cb6d38df1bca73e31ac042be49799bf8d5
+Author: Lyle Mantooth <lyle@ubercart.org>
+Date:   Wed Sep 1 15:08:57 2010 +0000
+
+    Implement hooks to display prices with taxes included.
+
+:100644 100644 0959106... 2341beb... M	uc_taxes/uc_taxes.module
+
+commit fa6722d91f462dc0bf733e759bccb8d9e9ae5cb0
+Author: Lyle Mantooth <lyle@ubercart.org>
+Date:   Wed Sep 1 15:05:29 2010 +0000
+
+    Moved taxed_product_types and taxed_line_items to separate tables to make them easier to query.
+
+:100644 100644 dd72187... 0959106... M	uc_taxes/uc_taxes.module
+
+commit 270cb2a9e3f8efaf32f7f03045f4691c36d254cc
+Author: Lyle Mantooth <lyle@ubercart.org>
+Date:   Wed Aug 25 13:40:26 2010 +0000
+
+    Remove uc_price().
+
+:100644 100644 c20c48a... dd72187... M	uc_taxes/uc_taxes.module
+
+commit 40f0d85d58e69bf604fa881a13a9220b12a9ce1b
+Author: Lyle Mantooth <lyle@ubercart.org>
+Date:   Fri Jul 16 13:30:08 2010 +0000
+
+    Add dependency on Rules to Taxes.
+
+:100644 100644 a2c6f63... c20c48a... M	uc_taxes/uc_taxes.module
+
+commit 6adcfe2bfe011196eab1da76e519761510d209c9
+Author: Lyle Mantooth <lyle@ubercart.org>
+Date:   Fri Jul 16 12:57:37 2010 +0000
+
+    #736770 by Island Usurper: Replace Conditional Actions with Rules.
+
+:100644 100644 dc8c786... a2c6f63... M	uc_taxes/uc_taxes.module
+
+commit 9f2f213860912cf7662eb06b97df4142ccfc9a74
+Author: Lyle Mantooth <lyle@ubercart.org>
+Date:   Fri Jun 4 15:38:41 2010 +0000
+
+    #305496 by Island Usurper: Overhaul the cart, checkout, and order panes to use AJAX when products, shipping quotes, and line items are updated.
+
+:100644 100644 2d418fa... dc8c786... M	uc_taxes/uc_taxes.module
+
+commit cc2e947ebf20a5121ece5616e597e106a82d7f43
+Author: Lyle Mantooth <lyle@ubercart.org>
+Date:   Tue Apr 6 13:20:01 2010 +0000
+
+    Fix notices during checkout.
+
+:100644 100644 133ac3d... 2d418fa... M	uc_taxes/uc_taxes.module
+
+commit 9eb81d8bf990b664856e2e08c20ca56578545d02
+Author: Lyle Mantooth <lyle@ubercart.org>
+Date:   Wed Mar 24 13:13:13 2010 +0000
+
+    #750784 by Island Usurper: Namespace functions.
+
+:100644 100644 42559bf... 133ac3d... M	uc_taxes/uc_taxes.module
+
+commit 57703a07193cbff3e401715d41b88ba9a1490bf0
+Author: Lyle Mantooth <lyle@ubercart.org>
+Date:   Tue Mar 23 20:17:44 2010 +0000
+
+    Various fixes to product creation and checkout processes.
+
+:100644 100644 1d543d8... 42559bf... M	uc_taxes/uc_taxes.module
+
+commit 508a278da629cd158007335996ae84228fd39c85
+Author: Lyle Mantooth <lyle@ubercart.org>
+Date:   Tue Mar 23 19:12:38 2010 +0000
+
+    Change drupal_json() to drupal_json_output().
+
+:100644 100644 002ffb1... 1d543d8... M	uc_taxes/uc_taxes.module
+
+commit 003934e5990b0e8d26cf18d877a87a2919a36bc6
+Author: Lyle Mantooth <lyle@ubercart.org>
+Date:   Wed Mar 17 17:51:58 2010 +0000
+
+    Coder review fixes.
+
+:100644 100644 8a3a379... 002ffb1... M	uc_taxes/uc_taxes.module
+
+commit 233c8b831a80f402255680f7dfce7d5241e9fe6b
+Author: Lyle Mantooth <lyle@ubercart.org>
+Date:   Wed Mar 10 14:02:22 2010 +0000
+
+    #510382 by Island Usurper: Namespace Ubercart's hooks.
+
+:100644 100644 39053d1... 8a3a379... M	uc_taxes/uc_taxes.module
+
+commit 543ba1ccfbf07267c0aa7d7c8a9740d4fe1397a1
+Author: Lyle Mantooth <lyle@ubercart.org>
+Date:   Wed Mar 3 19:34:13 2010 +0000
+
+    #702640 by Island Usurper: Adjust input sanitation for Drupal 7.
+
+:100644 100644 ba4fa3b... 39053d1... M	uc_taxes/uc_taxes.module
+
+commit 52f1df94c6bbe9af4602371ee81d6664f73855b4
+Author: Lyle Mantooth <lyle@ubercart.org>
+Date:   Wed Feb 10 19:21:50 2010 +0000
+
+    #702660 by Island Usurper: Port JavaScript changes.
+
+:100644 100644 b85f195... ba4fa3b... M	uc_taxes/uc_taxes.module
+
+commit 79ffae71293973e010f0c4dcec7f3db2a122716f
+Author: Lyle Mantooth <lyle@ubercart.org>
+Date:   Mon Feb 8 20:18:54 2010 +0000
+
+    #704584 by Island Usurper: Port the other Ubercart modules to the new Database API.
+
+:100644 100644 ab86d38... b85f195... M	uc_taxes/uc_taxes.module
+
+commit f4d765c17e3d2191500f88c2ae7be04857a1d035
+Author: Lyle Mantooth <lyle@ubercart.org>
+Date:   Mon Feb 8 17:28:24 2010 +0000
+
+    Use sentence case on permission titles.
+
+:100644 100644 a44c76f... ab86d38... M	uc_taxes/uc_taxes.module
+
+commit c88c74de1d7b60c1e94dd51764bc9e09ddb91eda
+Author: Lyle Mantooth <lyle@ubercart.org>
+Date:   Mon Feb 8 17:24:46 2010 +0000
+
+    #702510 by Andy_Lowe: Ported permissions issues.
+
+:100644 100644 412d4b3... a44c76f... M	uc_taxes/uc_taxes.module
+
+commit cf367d2b45a73a77afc84cd06194f5352dfae243
+Author: Lyle Mantooth <lyle@ubercart.org>
+Date:   Mon Jan 25 16:42:00 2010 +0000
+
+    #694570 by maximpodorov: Do not expect the form state to be passed by reference.
+
+:100644 100644 68a3db3... 412d4b3... M	uc_taxes/uc_taxes.module
+
+commit fbb801c694de95f8011ad5948376fa18a4789f31
+Author: Lyle Mantooth <lyle@ubercart.org>
+Date:   Mon Jan 25 16:05:35 2010 +0000
+
+    Merge DRUPAL-6--2 back into HEAD to prepare for Ubercart 7.x-3.x.
+
+:100644 100644 3209431... 68a3db3... M	uc_taxes/uc_taxes.module
+
+commit 9337e08986dcea578e2d32e0ce9ef832ecb4adf3
+Author: Lyle Mantooth <lyle@ubercart.org>
+Date:   Thu Jul 10 12:41:07 2008 +0000
+
+    Begin the Ubercart 6.x-2.x branch.
+
+:100644 100644 1f0edab... 3209431... M	uc_taxes/uc_taxes.module
+
+commit c051df261484119ae515435912fba8f5297e8dc0
+Author: Ryan Szrama <rszrama@49344.no-reply.drupal.org>
+Date:   Mon Nov 5 20:32:02 2007 +0000
+
+    Bulk update for Alpha 8.
+
+:100644 100644 b26b1c8... 1f0edab... M	uc_taxes/uc_taxes.module
+
+commit 28b34d49f0d66f1a2db33dadc52e940b873328af
+Author: Ryan Szrama <rszrama@49344.no-reply.drupal.org>
+Date:   Tue Oct 2 18:11:58 2007 +0000
+
+    Mass update for Alpha 7d.
+
+:100644 100644 3a9b782... b26b1c8... M	uc_taxes/uc_taxes.module
+
+commit b734719ed627e64ab91d5da6a36b3a5e4eeaf2d0
+Author: Ryan Szrama <rszrama@49344.no-reply.drupal.org>
+Date:   Thu Sep 13 15:57:20 2007 +0000
+
+    Mass update for Ubercart Alpha 7c.
+
+:100644 100644 ba571f5... 3a9b782... M	uc_taxes/uc_taxes.module
+
+commit 29313a7bbd39e7e21a44745f9adfce4d7486f9f0
+Author: Ryan Szrama <rszrama@49344.no-reply.drupal.org>
+Date:   Tue Jul 10 20:57:31 2007 +0000
+
+    Bugfixes for Alpha 7b.
+
+:100644 100644 2242248... ba571f5... M	uc_taxes/uc_taxes.module
+
+commit 7097ed39252b9ea418fbea040dc479e2c74415dc
+Author: Ryan Szrama <rszrama@49344.no-reply.drupal.org>
+Date:   Fri Jul 6 21:47:55 2007 +0000
+
+    Mass update for Alpha 7.
+
+:100644 100644 b3329aa... 2242248... M	uc_taxes/uc_taxes.module
+
+commit d1129d8f66e09f94c8baa4cd122fb15b0ac8c9ef
+Author: Ryan Szrama <rszrama@49344.no-reply.drupal.org>
+Date:   Thu Jun 7 21:41:44 2007 +0000
+
+    Mass update for Alpha 6c.
+
+:100644 100644 ad8ce18... b3329aa... M	uc_taxes/uc_taxes.module
+
+commit b512e7cf77ee22c7a3ec11fc03028a64c9628341
+Author: Ryan Szrama <rszrama@49344.no-reply.drupal.org>
+Date:   Tue May 8 21:15:32 2007 +0000
+
+    Alpha 6 upload!  Plenty of bugfixes and feature additions, including code updates, more countries, and better international support.
+
+:100644 100644 7d4dbc3... ad8ce18... M	uc_taxes/uc_taxes.module
+
+commit 7f98c8bca9b790ea32a49254819f5f9e2c5c834e
+Author: Ryan Szrama <rszrama@49344.no-reply.drupal.org>
+Date:   Tue Apr 10 16:21:24 2007 +0000
+
+    Alpha 5 upload with installation fixes, bugfixes, and brand-spanking new country import files. See http://www.ubercart.org/news/apr-10-2007/alpha_5_alive for more information.
+
+:100644 100644 f8d803a... 7d4dbc3... M	uc_taxes/uc_taxes.module
+
+commit 1b0ea3d764cdb6a0ef6be796bb7f5442683ef939
+Author: Ryan Szrama <rszrama@49344.no-reply.drupal.org>
+Date:   Thu Apr 5 15:47:28 2007 +0000
+
+    Upload to get Alpha 4 out the door.
+
+:100644 100644 27ddfd7... f8d803a... M	uc_taxes/uc_taxes.module
+
+commit a2b2f1102a659fa9232ad120902f7d90e5c478fd
+Author: Ryan Szrama <rszrama@49344.no-reply.drupal.org>
+Date:   Mon Mar 19 22:59:42 2007 +0000
+
+    Initial upload of Ubercart, woohoo!
+
+:000000 100644 0000000... 27ddfd7... A	uc_taxes/uc_taxes.module
diff --git a/uc_taxes/uc_taxes.module b/uc_taxes/uc_taxes.module
index 3325567..8c2d8a1 100644
--- a/uc_taxes/uc_taxes.module
+++ b/uc_taxes/uc_taxes.module
@@ -544,7 +544,7 @@ function uc_taxes_uc_calculate_tax($order) {
  */
 function uc_taxes_apply_item_tax($item, $tax) {
   // Determine the product type.
-  if (isset($item->data['type'])) { // Saved in the order product data array.
+  if (is_array($item->data) && isset($item->data['type'])) { // Saved in the order product data array.
     $type = $item->data['type'];
   }
   elseif (empty($item->nid)) { // "Blank-line" product.
@@ -558,7 +558,7 @@ function uc_taxes_apply_item_tax($item, $tax) {
   }
 
   // Determine whether this is a shippable product.
-  if (isset($item->data['shippable'])) { // Saved in the order product data array.
+  if (is_array($item->data) && isset($item->data['shippable'])) { // Saved in the order product data array.
     $shippable = $item->data['shippable'];
   }
   elseif (empty($item->nid)) { // "Blank line" product.
diff --git a/uc_taxes/uc_taxes.module.new b/uc_taxes/uc_taxes.module.new
new file mode 100644
index 0000000..94053e3
--- /dev/null
+++ b/uc_taxes/uc_taxes.module.new
@@ -0,0 +1,640 @@
+<?php
+
+/**
+ * @file
+ * Ubercart Taxes module.
+ *
+ * Allows tax rules to be set up and applied to orders.
+ */
+
+/******************************************************************************
+ * Drupal Hooks                                                               *
+ ******************************************************************************/
+
+/**
+ * Implements hook_permission().
+ */
+function uc_taxes_permission() {
+  return array(
+    'configure taxes' => array(
+      'title' => t('Configure taxes'),
+    )
+  );
+}
+
+/**
+ * Implements hook_menu().
+ */
+function uc_taxes_menu() {
+  $items = array();
+
+  $items['admin/store/settings/taxes'] = array(
+    'title' => 'Taxes',
+    'description' => 'Configure tax rates and rules.',
+    'page callback' => 'uc_taxes_admin_settings',
+    'access arguments' => array('configure taxes'),
+    'file' => 'uc_taxes.admin.inc',
+  );
+  $items['admin/store/settings/taxes/add'] = array(
+    'title' => 'Add a tax rate',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('uc_taxes_form'),
+    'access arguments' => array('configure taxes'),
+    'file' => 'uc_taxes.admin.inc',
+    'type' => MENU_LOCAL_ACTION,
+  );
+  $items['admin/store/settings/taxes/%/edit'] = array(
+    'title' => 'Edit a tax rate',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('uc_taxes_form', 4),
+    'access arguments' => array('configure taxes'),
+    'file' => 'uc_taxes.admin.inc',
+  );
+  $items['admin/store/settings/taxes/%/clone'] = array(
+    'page callback' => 'uc_taxes_clone',
+    'page arguments' => array(4),
+    'access arguments' => array('configure taxes'),
+    'file' => 'uc_taxes.admin.inc',
+  );
+  $items['admin/store/settings/taxes/%/delete'] = array(
+    'title' => 'Delete tax rule',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('uc_taxes_delete_form', 4),
+    'access arguments' => array('configure taxes'),
+    'file' => 'uc_taxes.admin.inc',
+  );
+
+  $items += rules_ui()->config_menu('admin/store/settings/taxes');
+
+  return $items;
+}
+
+/**
+ * Implements hook_module_implements_alter().
+ *
+ * Ensures that all other line items are added to the order before tax
+ * calculations are made.
+ */
+function uc_taxes_module_implements_alter(&$implementations, $hook) {
+  if ($hook == 'uc_order') {
+    $group = $implementations['uc_taxes'];
+    unset($implementations['uc_taxes']);
+    $implementations['uc_taxes'] = $group;
+  }
+}
+
+/**
+ * Implements hook_form_uc_order_edit_form_alter().
+ */
+function uc_taxes_form_uc_order_edit_form_alter(&$form, &$form_state) {
+  $order = $form['#order'];
+  $line_items = $order->line_items;
+  foreach ($line_items as $item) {
+    // Tax line items are stored in the database, but they can't be changed by
+    // the user.
+    if ($item['type'] == 'tax') {
+      $form['line_items'][$item['line_item_id']]['title'] = array(
+        '#markup' => check_plain($item['title']),
+      );
+      $form['line_items'][$item['line_item_id']]['amount'] = array(
+        '#theme' => 'uc_price',
+        '#price' => $item['amount'],
+      );
+    }
+  }
+}
+
+/**
+ * Implements hook_uc_cart_item().
+ *
+ * Adds included taxes (VAT) to display price of applicable products.
+ */
+function uc_taxes_uc_cart_item($op, $item) {
+  if ($op == 'load') {
+    $product = node_load($item->nid);
+
+    list($amount, $suffixes) = uc_taxes_get_included_tax($product, $item->price);
+
+    $item->display_price += $amount;
+    if (!empty($suffixes)) {
+      $item->display_price_suffix .= implode(' ', $suffixes);
+    }
+  }
+}
+
+/**
+ * Implements hook_node_load().
+ *
+ * Adds included taxes (VAT) to display price of applicable products.
+ */
+function uc_taxes_node_load($nodes, $types) {
+  foreach ($nodes as &$node) {
+    if (uc_product_is_product($node)) {
+      $amount = 0;
+      $suffixes = array();
+      if ($node->type == 'product_kit') {
+        // For product kits, the total tax is the sum of the tax on the discounted
+        // prices of component products.
+        foreach ($node->products as $pid => &$product) {
+          $discount =  ($node->mutable != UC_PRODUCT_KIT_MUTABLE) ? $product->discount : 0;
+          list($this_amount, $these_suffixes) = uc_taxes_get_included_tax($product, $product->sell_price + $discount);
+          $amount += $this_amount * $product->qty;
+          $suffixes += $these_suffixes;
+        }
+      }
+      else {
+        list($amount, $suffixes) = uc_taxes_get_included_tax($node);
+      }
+      $node->display_price += $amount;
+      if (!empty($suffixes)) {
+        $node->display_price_suffix .= implode(' ', $suffixes);
+      }
+    }
+  }
+}
+
+/*******************************************************************************
+ * Ubercart Hooks
+ ******************************************************************************/
+
+/**
+ * Implements hook_uc_line_item().
+ */
+function uc_taxes_uc_line_item() {
+  $items['tax'] = array(
+    'title' => t('Tax'),
+    'callback' => 'uc_line_item_tax',
+    'weight' => 9,
+    'stored' => TRUE,
+    'default' => FALSE,
+    'calculated' => TRUE,
+    'display_only' => FALSE,
+  );
+  $items['tax_subtotal'] = array(
+    'title' => t('Subtotal excluding taxes'),
+    'callback' => 'uc_line_item_tax_subtotal',
+    'weight' => 7,
+    'stored' => FALSE,
+    'calculated' => FALSE,
+    'display_only' => TRUE,
+  );
+
+  return $items;
+}
+
+/**
+ * Implements hook_uc_order().
+ *
+ * Updates and saves tax line items to the order.
+ */
+function uc_taxes_uc_order($op, $order, $arg2) {
+  switch ($op) {
+    case 'save':
+      $changes = array();
+      
+      // Make sure we have the most up-to-date version of tax line items.
+      uc_order_load_line_items($order); 
+      
+      // Get all previously stored tax line items.
+      $old = $order->stored_taxes;
+      $order->stored_taxes = array();
+      if (is_array($order->line_items)) {
+        foreach ($order->line_items as $new_line) {
+          if ($new_line['type'] == 'tax') {
+            $tax_id = $new_line['data']['tax_id'];
+            if (!empty($old[$tax_id])) {
+              if ($new_line['amount'] != $stored[$tax_id]['amount']) {
+                uc_order_update_line_item($line['line_item_id'], $new_line['title'], $new_line['amount'], $new_line['data']);
+                $changes[] = t('Changed %title to %amount.', array('%amount' => uc_currency_format($new_line['amount']), '%title' => $new_line['title']));
+              }
+              $order->stored_taxes[$tax_id] = $new_line;
+              unset ($stored[$tax_id]);
+            }
+            else {
+              $new_line['line_item_id'] = uc_order_line_item_add($order->order_id, 'tax', $new_line['title'], $new_line['amount'], $new_line['weight'], $new_line['data']);
+              $changes[] = t('Added %amount for %title.', array('%amount' => uc_currency_format($new_line['amount']), '%title' => $new_line['title']));
+            }
+          }
+        }
+      }
+      
+      if (!empty($order->stored_taxes)) {
+        foreach ($order->stored_taxes as $line) {
+          uc_order_delete_line_item($line['line_item_id']);
+          $changes[] = t('Removed %title.', array('%title' => $line['title']));
+        }
+      }
+      if (count($changes)) {
+        uc_order_log_changes($order->order_id, $changes);
+      }
+    break;
+  }
+}
+
+/******************************************************************************
+ * Menu Callbacks                                                             *
+ ******************************************************************************/
+
+/**
+ * Handles the tax line item.
+ */
+function uc_line_item_tax($op, &$order) {
+  switch ($op) {
+    case 'load':
+      // Calculate the updated taxes.
+      $taxes = uc_taxes_calculate($order);
+      
+      // Index existing stored tax line items and remove them from the order.  They will be replaced
+      // by updated versions from $taxes, but we want to remember them to avoid having to query
+      // stored line items again in hook_uc_order('save', ...).
+      $order->stored_taxes = array();
+      foreach ($order->line_items as $key => $item) {
+        if ($item['type'] == 'tax') {
+          $order->stored_taxes[$item['data']['tax_id']] = $item;
+          unset($order->line_items[$key]);
+        }
+      }
+      
+      // Build and return an array of updated tax line items.
+      $lines = array();
+      foreach ($taxes as $tax_id => $tax) {
+        $line = array(
+          'id' => $tax_id,
+          'title' => $tax->name,
+          'amount' => $tax->amount,
+          'weight' => variable_get('uc_li_tax_weight', 9) + $tax->weight / 10,
+          'data' => $tax->data,
+        );
+        // Make sure the tax_id is saved to the data array.
+        $line['data']['tax_id'] = $tax_id;
+        // If a line already exists for this tax_id, use the saved line_item_id.
+        if (!empty($index[$id])) {
+          $line['id'] = $index[$tax_id]['line_item_id'];
+        }
+        $lines[] = $line;
+      }
+      return $lines;
+  }
+}
+
+/**
+ * Handles the line item subtotal before taxes.
+ */
+function uc_line_item_tax_subtotal($op, $order) {
+  $amount = 0;
+  switch ($op) {
+    case 'display':
+      $has_taxes = FALSE;
+      $different = FALSE;
+
+      if (is_array($order->products)) {
+        foreach ($order->products as $item) {
+          $amount += $item->price * $item->qty;
+        }
+      }
+      if (is_array($order->line_items)) {
+        foreach ($order->line_items as $key => $line_item) {
+          if ($line_item['type'] == 'subtotal') {
+            continue;
+          }
+          if (substr($line_item['type'], 0, 3) != 'tax') {
+            $amount += $line_item['amount'];
+            $different = TRUE;
+          }
+          else {
+            $has_taxes = TRUE;
+          }
+        }
+      }
+
+      if (isset($order->taxes) && is_array($order->taxes) && count($order->taxes)) {
+        $has_taxes = TRUE;
+      }
+
+      if ($different && $has_taxes) {
+        return array(array(
+          'id' => 'tax_subtotal',
+          'title' => t('Subtotal excluding taxes'),
+          'amount' => $amount,
+          'weight' => variable_get('uc_li_tax_subtotal_weight', 7),
+        ));
+      }
+      break;
+  }
+}
+
+
+/******************************************************************************
+ * Module and Helper Functions
+ ******************************************************************************/
+
+/**
+ * Saves a tax rate to the database.
+ *
+ * @param $rate
+ *   The tax rate object to be saved.
+ *
+ * @return
+ *   The saved tax rate object including the rate ID for new rates.
+ */
+function uc_taxes_rate_save($rate) {
+  // Save it as a new rate if no ID is specified.
+  if (!$rate->id) {
+    drupal_write_record('uc_taxes', $rate);
+  }
+  // Otherwise update the existing tax rate's data.
+  else {
+    drupal_write_record('uc_taxes', $rate, array('id'));
+  }
+
+  db_delete('uc_taxed_product_types')
+    ->condition('tax_id', $rate->id)
+    ->execute();
+  db_delete('uc_taxed_line_items')
+    ->condition('tax_id', $rate->id)
+    ->execute();
+
+  $p_insert = db_insert('uc_taxed_product_types')->fields(array('tax_id', 'type'));
+  $l_insert = db_insert('uc_taxed_line_items')->fields(array('tax_id', 'type'));
+
+  foreach ($rate->taxed_product_types as $type) {
+    $p_insert->values(array(
+      'tax_id' => $rate->id,
+      'type' => $type,
+    ));
+  }
+
+  foreach ($rate->taxed_line_items as $type) {
+    $l_insert->values(array(
+      'tax_id' => $rate->id,
+      'type' => $type,
+    ));
+  }
+
+  $p_insert->execute();
+  $l_insert->execute();
+
+  rules_clear_cache();
+
+  return $rate;
+}
+
+
+/**
+ * List all the taxes that can apply to an order.  These depend on the order status.  For orders which
+ * are still in checkout, any tax can apply.  For orders out of checkout, only taxes originally saved
+ * as line items can apply.
+ *
+ * @param $order
+ *   The order
+ */
+function uc_taxes_filter_rates($order) {
+  $taxes = array();
+
+  // For orders no longer in checkout, only the saved tax rates can apply.
+  if (isset($order->order_status) && uc_order_status_data($order->order_status, 'state') != 'in_checkout') {
+    if (isset($order->line_items)) {
+      foreach($order->line_items as $item) {
+        if ($item['type'] == 'tax') {
+          if (!empty($item['data']['tax'])) {
+            // Use the rate stored in the line-item.
+            $taxes[] = clone $item['data']['tax'];
+          }
+          else if ($tax = uc_taxes_rate_load($item['data']['tax_id'])) {
+            // For old orders that don't have all the tax info, all we can do is preserve the rate.
+            $tax = clone $tax;
+            if (!empty($item['data']['tax_rate'])) {
+              $tax->rate = $item['data']['tax_rate'];
+            }
+            $taxes[] = $tax;
+          }
+        }
+      }
+    }
+  }
+  // For orders still in checkout, any tax whose conditions are satisfied can apply.
+  else {
+    foreach  (uc_taxes_rate_load() as $rate) {
+      $tax = clone ($rate);
+      if (rules_invoke_component('uc_taxes_' . $tax->id, $order)) {
+        $taxes[] = $tax;
+      }
+    }
+  }
+
+  return $taxes;
+}
+
+/**
+ * Loads a tax rate or all tax rates from the database.
+ *
+ * @param $rate_id
+ *   The ID of the specific rate to load or NULL to return all available rates.
+ *
+ * @return
+ *   An object representing the requested tax rate or an array of all tax rates
+ *   keyed by rate ID.
+ */
+function uc_taxes_rate_load($rate_id = NULL) {
+  static $rates = array();
+
+  // If the rates have not been cached yet...
+  if (empty($rates)) {
+    // Get all the rate data from the database.
+    $result = db_query("SELECT * FROM {uc_taxes} ORDER BY weight");
+
+    // Loop through each returned row.
+    foreach ($result as $rate) {
+      $rate->taxed_product_types = array();
+      $rate->taxed_line_items = array();
+
+      $rates[$rate->id] = $rate;
+    }
+
+    foreach (array('taxed_product_types', 'taxed_line_items') as $field) {
+      $result = db_select('uc_' . $field, 't')->fields('t', array('tax_id', 'type'))->execute();
+      foreach ($result as $record) {
+        $rates[$record->tax_id]->{$field}[] = $record->type;
+      }
+    }
+  }
+
+  // Return a rate as specified.
+  if ($rate_id) {
+    return isset($rates[$rate_id]) ? $rates[$rate_id] : FALSE;
+  }
+  // Or return the whole shebang.
+  else {
+    return $rates;
+  }
+}
+
+/**
+ * Deletes a tax rate from the database.
+ *
+ * @param $rate_id
+ *   The ID of the tax rate to delete.
+ */
+function uc_taxes_rate_delete($rate_id) {
+  // Delete the tax rate record.
+  db_delete('uc_taxes')
+    ->condition('id', $rate_id)
+    ->execute();
+
+  db_delete('uc_taxed_product_types')
+    ->condition('tax_id', $rate_id)
+    ->execute();
+
+  db_delete('uc_taxed_line_items')
+    ->condition('tax_id', $rate_id)
+    ->execute();
+
+  // Delete the associated conditions if they have been saved to the database.
+  rules_config_delete(array('uc_taxes_' . $rate_id));
+}
+
+/**
+ * Calculates the taxes for an order based on enabled tax modules.
+ *
+ * @param $order
+ *   The full order object for the order want to calculate taxes for.
+ *
+ * @return
+ *   An array of taxes for the order.
+ */
+function uc_taxes_calculate($order) {
+  // Find any taxes specified by enabled modules.
+  $taxes = module_invoke_all('uc_calculate_tax', $order);
+
+  return $taxes;
+}
+
+/**
+ * Calculates the amount and types of taxes that apply to an order.
+ */
+function uc_taxes_uc_calculate_tax($order) {
+  if (!is_object($order)) {
+    return array();
+  }
+  if (empty($order->delivery_postal_code)) {
+    $order->delivery_postal_code = $order->billing_postal_code;
+  }
+  if (empty($order->delivery_zone)) {
+    $order->delivery_zone = $order->billing_zone;
+  }
+  if (empty($order->delivery_country)) {
+    $order->delivery_country = $order->billing_country;
+  }
+
+  $order->taxes = array();
+  foreach (uc_taxes_filter_rates($order) as $tax) {
+    if ($line_item = uc_taxes_apply_tax($order, $tax)) {
+      $order->taxes[$line_item->id] = $line_item;
+    }
+  }
+
+  return $order->taxes;
+}
+
+/**
+ * Calculates tax for a single product.
+ */
+function uc_taxes_apply_item_tax($item, $tax) {
+  $node = node_load($item->nid);
+
+  // Special handling for manually added "Blank line" products.
+  if (!$node) {
+    $node = new stdClass();
+    $node->type = 'blank-line';
+    $node->shippable = $item->weight > 0;
+  }
+
+  // Tax products if they are of a taxed type and if it is shippable if
+  // the tax only applies to shippable products.
+  if (in_array($node->type, $tax->taxed_product_types) && ($tax->shippable == 0 || $node->shippable == 1)) {
+    return $item->price * $item->qty;
+  }
+}
+
+/**
+ * Applies taxes to an order.
+ *
+ * @param $order
+ *   The order object being considered.
+ * @param $tax
+ *   The tax rule calculating the amount.
+ *
+ * @return
+ *   The line item array representing the amount of tax.
+ */
+function uc_taxes_apply_tax($order, $tax) {
+  $amount = 0;
+  $taxable_amount = 0;
+  if (is_array($order->products)) {
+    foreach ($order->products as $item) {
+      $taxable_amount += uc_taxes_apply_item_tax($item, $tax);
+    }
+  }
+  $taxed_line_items = $tax->taxed_line_items;
+  if (is_array($order->line_items) && is_array($taxed_line_items)) {
+    foreach ($order->line_items as $key => $line_item) {
+      if ($line_item['type'] == 'tax') {
+        // Don't tax old taxes.
+        continue;
+      }
+      if (in_array($line_item['type'], $taxed_line_items)) {
+        $callback = _uc_line_item_data($line_item['type'], 'tax_adjustment');
+        if (isset($callback) && function_exists($callback)) {
+          $taxable_amount += $callback($line_item['amount'], $order, $tax);
+        }
+        else {
+          $taxable_amount += $line_item['amount'];
+        }
+      }
+    }
+  }
+  if (in_array('tax', $taxed_line_items)) {
+    // Tax taxes that were just calculated.
+    foreach ($order->taxes as $other_tax) {
+      $taxable_amount += $other_tax->amount;
+    }
+  }
+  $amount = $taxable_amount * $tax->rate;
+  if ($amount) {
+    $line_item = (object)array(
+      'id' => $tax->id,
+      'name' => $tax->name,
+      'amount' => $amount,
+      'weight' => $tax->weight,
+      'summed' => 1,
+    );
+    $line_item->data = array(
+      'tax_id' => $tax->id,
+      'tax_rate' => $tax->rate,
+      'tax' => $tax,
+      'taxable_amount' => $taxable_amount,
+      'tax_jurisdiction' => $tax->name,
+    );
+    return $line_item;
+  }
+}
+
+/**
+ * Calculates the taxes that should be included in the display price of a product.
+ * @param $product
+ *   The product whose included taxes are to be calculated.
+ * @param $price
+ *   If specified, use this value as the tax-base.  Otherwise use the product sell price.
+ */
+function uc_taxes_get_included_tax($product, $price = FALSE) {
+  $amount = 0;
+  $suffixes = array();
+  $price = ($price === FALSE) ? $product->sell_price : $price;
+  foreach (uc_taxes_rate_load() as $tax) {
+    if ($tax->display_include && in_array($product->type, $tax->taxed_product_types) && ($tax->shippable == 0 || $product->shippable == 1)) {
+      $amount += $price * $tax->rate;
+      $suffixes[$tax->inclusion_text] = $tax->inclusion_text;
+    }
+  }
+  return array($amount, $suffixes);
+}
-- 
1.7.3.4


From fc9283b430680b64c7180e94c4aa8ac044b56a6d Mon Sep 17 00:00:00 2001
From: Chris Oden <wodenx@gmail.com>
Date: Wed, 7 Dec 2011 15:31:09 -0500
Subject: [PATCH 11/14] Add display-adjusted prices to order invoice.

---
 uc_order/uc_order.module |    5 +++--
 1 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/uc_order/uc_order.module b/uc_order/uc_order.module
index 931c397..5787e52 100644
--- a/uc_order/uc_order.module
+++ b/uc_order/uc_order.module
@@ -1796,10 +1796,11 @@ function template_preprocess_uc_order(&$variables) {
   $variables['shippable'] = uc_order_is_shippable($order);
 
   $variables['products'] = $order->products;
+  $display = entity_view('uc_order_product', $order->products);
   foreach ($variables['products'] as &$product) {
-    $product->total_price = uc_currency_format($product->price * $product->qty);
+    $product->total_price = render($display['uc_order_product'][$product->order_product_id]['total']);
     if ($product->qty > 1) {
-      $product->individual_price = t('(!price each)', array('!price' => uc_currency_format($product->price)));
+      $product->individual_price = t('(!price each)', array('!price' => uc_currency_format($display['uc_order_product'][$product->order_product_id]['price']['#price'])));
     }
     else {
       $product->individual_price = '';
-- 
1.7.3.4


From da71fc404db99d872fe1f96046d7fc134e0e8b91 Mon Sep 17 00:00:00 2001
From: Chris Oden <wodenx@gmail.com>
Date: Wed, 7 Dec 2011 16:46:27 -0500
Subject: [PATCH 12/14] Revert "Check to be sure ->data is an array before testing values, bc user module sometimes sets it to a serialized string." Remove extraneous files.

This reverts commit bae59795b8c159fe69dd60fbba3b3bc90e59378b.
---
 uc_taxes/gitlog.txt          |  407 ---------------------------
 uc_taxes/uc_taxes.module     |    4 +-
 uc_taxes/uc_taxes.module.new |  640 ------------------------------------------
 3 files changed, 2 insertions(+), 1049 deletions(-)
 delete mode 100755 uc_taxes/gitlog.txt
 delete mode 100644 uc_taxes/uc_taxes.module.new

diff --git a/uc_taxes/gitlog.txt b/uc_taxes/gitlog.txt
deleted file mode 100755
index 2c18aae..0000000
--- a/uc_taxes/gitlog.txt
+++ /dev/null
@@ -1,407 +0,0 @@
-commit bb1a084cdc53c6e9ce6fa190d09bdfb11c869b18
-Author: Dave Long <dave@longwaveconsulting.com>
-Date:   Sun Sep 11 17:33:42 2011 +0100
-
-    Issue #1274786: Convert admin product listing to Views.
-
-:100644 100644 68b6516... d439f5a... M	uc_taxes/uc_taxes.module
-
-commit 500b6bb37232f5a3d19d9c77c56e64fe4c3aac9b
-Author: Dave Long <dave@longwaveconsulting.com>
-Date:   Thu Aug 25 10:42:38 2011 -0400
-
-    Issue #1164216: Make Rules a hard dependency.
-
-:100644 100644 532f4c1... 68b6516... M	uc_taxes/uc_taxes.module
-
-commit 317d0f038403aae3f7c7c992d1c1f55eb194bfd6
-Author: Lyle Mantooth <lyle@ubercart.org>
-Date:   Thu Aug 4 15:05:45 2011 -0400
-
-    Issue #1210194: Show taxes and subtotal in line item preview.
-
-:100644 100644 152db52... 532f4c1... M	uc_taxes/uc_taxes.module
-
-commit 6f028362686b1a95b788710b9bd7b0ee0ccb9c04
-Author: Dave Long <dave@longwaveconsulting.com>
-Date:   Tue Jul 26 22:45:23 2011 +0100
-
-    Fatal error: Call to a member function execute() on a non-object in uc_taxes.module on line 529.
-
-:100644 100644 a511205... 152db52... M	uc_taxes/uc_taxes.module
-
-commit 84833ca3fd39c4374ecf6f095d4d11657e1cf370
-Author: Dave Long <dave@longwaveconsulting.com>
-Date:   Tue Jul 26 20:23:09 2011 +0100
-
-    Issue #1050940: Improve settings menu titles and descriptions.
-
-:100644 100644 c8a44e2... a511205... M	uc_taxes/uc_taxes.module
-
-commit 2e087b9a76b951616677f3b73eac6bdc201998d3
-Author: Tim Rohaly <tr@202830.no-reply.drupal.org>
-Date:   Tue Jul 19 22:03:25 2011 -0700
-
-    Remove commented-out function.
-
-:100644 100644 a0c84bf... c8a44e2... M	uc_taxes/uc_taxes.module
-
-commit 08355a3c9c4df2086381968cee27189fedd743fa
-Author: Dave Long <dave@longwaveconsulting.com>
-Date:   Wed Jun 22 15:06:48 2011 +0100
-
-    Issue #1168280: Change hook_uc_line_item() to be keyed by state ID.
-
-:100644 100644 3fc6c87... a0c84bf... M	uc_taxes/uc_taxes.module
-
-commit 77978395d8416d6ebfe439459854a81cc57483c8
-Author: Dave Long <dave@longwaveconsulting.com>
-Date:   Wed May 25 14:11:17 2011 +0100
-
-    Issue #603356 by Island Usurper: uc_taxes module's weight should be set heavier on install.
-
-:100644 100644 e6aa73f... 3fc6c87... M	uc_taxes/uc_taxes.module
-
-commit ea47142996282de2744f7edd83963b0734ecf707
-Author: Dave Long <dave@longwaveconsulting.com>
-Date:   Mon May 23 22:44:11 2011 +0100
-
-    Issue #1161094 by wodenx: Skip invalid nodes in tax calculations.
-
-:100644 100644 fffa9f0... e6aa73f... M	uc_taxes/uc_taxes.module
-
-commit 023722979bee25be622b9d72067eada1751cdb72
-Author: Dave Long <dave@longwaveconsulting.com>
-Date:   Thu Apr 7 21:38:38 2011 +0100
-
-    Issue #1092738 by longwave, Danylo Dragon: Make Rules less of a dependency.
-
-:100644 100644 3ff85f0... fffa9f0... M	uc_taxes/uc_taxes.module
-
-commit 9507a47b5e5417dd8811554eda00fd8cdf102326
-Author: The Great Git Migration <tggm@no-reply.drupal.org>
-Date:   Fri Feb 25 02:07:29 2011 +0000
-
-    Stripping CVS keywords
-
-:100644 100644 46619f1... 3ff85f0... M	uc_taxes/uc_taxes.module
-
-commit 158fa034f6ae6b27412e4bb395a6ee63f63b6926
-Author: Dave Long <dave@longwaveconsulting.com>
-Date:   Thu Feb 24 22:11:14 2011 +0000
-
-    #1062988 by essbee: Taxes not taxing other tax line items.
-
-:100644 100644 a816e0f... 46619f1... M	uc_taxes/uc_taxes.module
-
-commit 74df0e852e127d10e6f6c81c5ca88c0281e9d0eb
-Author: Tim Rohaly <tr@202830.no-reply.drupal.org>
-Date:   Thu Feb 17 09:36:46 2011 +0000
-
-    Still more changes to bring documentation up to standards.
-
-:100644 100644 aaa7a3a... a816e0f... M	uc_taxes/uc_taxes.module
-
-commit 0a7e220275ed95dbff2f19d05f94963f139f8843
-Author: Dave Long <dave@longwaveconsulting.com>
-Date:   Wed Feb 16 01:25:51 2011 +0000
-
-    Move 'add a tax rate' to a local action.
-
-:100644 100644 11344aa... aaa7a3a... M	uc_taxes/uc_taxes.module
-
-commit 8983fcb52f8eb2db2ccc8fb86f7e4852c49b7536
-Author: Tim Rohaly <tr@202830.no-reply.drupal.org>
-Date:   Tue Feb 15 21:05:52 2011 +0000
-
-    Some work on conforming to documentation standards.  Also comment out uc_taxes_form_alter() because it has an empty body.
-
-:100644 100644 d523ee3... 11344aa... M	uc_taxes/uc_taxes.module
-
-commit 162fd7504c097d96fb1a1daef665acf92fb53707
-Author: Tim Rohaly <tr@202830.no-reply.drupal.org>
-Date:   Mon Feb 14 21:19:22 2011 +0000
-
-    Make documentation comments for hook implementations conform to documentation standards
-
-:100644 100644 d42cb6c... d523ee3... M	uc_taxes/uc_taxes.module
-
-commit bb92d3bf387a3f0936a79a8b7559e6ef5e322289
-Author: Lyle Mantooth <lyle@ubercart.org>
-Date:   Fri Feb 4 16:20:33 2011 +0000
-
-    Fix variable name in hook_uc_order().
-
-:100644 100644 88447df... d42cb6c... M	uc_taxes/uc_taxes.module
-
-commit 78978a09817f243688426e24e3cd5567f670a053
-Author: Dave Long <dave@longwaveconsulting.com>
-Date:   Thu Feb 3 21:13:05 2011 +0000
-
-    #1022868 by hanoii: Tax calculation modifies cached tax rates.
-
-:100644 100644 a8a8010... 88447df... M	uc_taxes/uc_taxes.module
-
-commit 1e117a39bc6c974e051d42877798a8d67fe8a582
-Author: Dave Long <dave@longwaveconsulting.com>
-Date:   Thu Feb 3 21:08:44 2011 +0000
-
-    #990744: uc_taxes does not respect hook_line_item_data_alter().
-
-:100644 100644 16d955c... a8a8010... M	uc_taxes/uc_taxes.module
-
-commit af06822af218433f58ae8484ad5b6e060564baeb
-Author: Lyle Mantooth <lyle@ubercart.org>
-Date:   Thu Oct 7 19:10:47 2010 +0000
-
-    Fix hook_menus() so breadcrumbs show up right. Remove manual breadcrumbs from those paths.
-
-:100644 100644 7fce3ce... 16d955c... M	uc_taxes/uc_taxes.module
-
-commit 536cc3301d86518b23e77629e6e921087be6966e
-Author: Lyle Mantooth <lyle@ubercart.org>
-Date:   Tue Sep 14 13:48:35 2010 +0000
-
-    Update uc_taxes_node_view() to the new price element structure.
-
-:100644 100644 2341beb... 7fce3ce... M	uc_taxes/uc_taxes.module
-
-commit 44a945cb6d38df1bca73e31ac042be49799bf8d5
-Author: Lyle Mantooth <lyle@ubercart.org>
-Date:   Wed Sep 1 15:08:57 2010 +0000
-
-    Implement hooks to display prices with taxes included.
-
-:100644 100644 0959106... 2341beb... M	uc_taxes/uc_taxes.module
-
-commit fa6722d91f462dc0bf733e759bccb8d9e9ae5cb0
-Author: Lyle Mantooth <lyle@ubercart.org>
-Date:   Wed Sep 1 15:05:29 2010 +0000
-
-    Moved taxed_product_types and taxed_line_items to separate tables to make them easier to query.
-
-:100644 100644 dd72187... 0959106... M	uc_taxes/uc_taxes.module
-
-commit 270cb2a9e3f8efaf32f7f03045f4691c36d254cc
-Author: Lyle Mantooth <lyle@ubercart.org>
-Date:   Wed Aug 25 13:40:26 2010 +0000
-
-    Remove uc_price().
-
-:100644 100644 c20c48a... dd72187... M	uc_taxes/uc_taxes.module
-
-commit 40f0d85d58e69bf604fa881a13a9220b12a9ce1b
-Author: Lyle Mantooth <lyle@ubercart.org>
-Date:   Fri Jul 16 13:30:08 2010 +0000
-
-    Add dependency on Rules to Taxes.
-
-:100644 100644 a2c6f63... c20c48a... M	uc_taxes/uc_taxes.module
-
-commit 6adcfe2bfe011196eab1da76e519761510d209c9
-Author: Lyle Mantooth <lyle@ubercart.org>
-Date:   Fri Jul 16 12:57:37 2010 +0000
-
-    #736770 by Island Usurper: Replace Conditional Actions with Rules.
-
-:100644 100644 dc8c786... a2c6f63... M	uc_taxes/uc_taxes.module
-
-commit 9f2f213860912cf7662eb06b97df4142ccfc9a74
-Author: Lyle Mantooth <lyle@ubercart.org>
-Date:   Fri Jun 4 15:38:41 2010 +0000
-
-    #305496 by Island Usurper: Overhaul the cart, checkout, and order panes to use AJAX when products, shipping quotes, and line items are updated.
-
-:100644 100644 2d418fa... dc8c786... M	uc_taxes/uc_taxes.module
-
-commit cc2e947ebf20a5121ece5616e597e106a82d7f43
-Author: Lyle Mantooth <lyle@ubercart.org>
-Date:   Tue Apr 6 13:20:01 2010 +0000
-
-    Fix notices during checkout.
-
-:100644 100644 133ac3d... 2d418fa... M	uc_taxes/uc_taxes.module
-
-commit 9eb81d8bf990b664856e2e08c20ca56578545d02
-Author: Lyle Mantooth <lyle@ubercart.org>
-Date:   Wed Mar 24 13:13:13 2010 +0000
-
-    #750784 by Island Usurper: Namespace functions.
-
-:100644 100644 42559bf... 133ac3d... M	uc_taxes/uc_taxes.module
-
-commit 57703a07193cbff3e401715d41b88ba9a1490bf0
-Author: Lyle Mantooth <lyle@ubercart.org>
-Date:   Tue Mar 23 20:17:44 2010 +0000
-
-    Various fixes to product creation and checkout processes.
-
-:100644 100644 1d543d8... 42559bf... M	uc_taxes/uc_taxes.module
-
-commit 508a278da629cd158007335996ae84228fd39c85
-Author: Lyle Mantooth <lyle@ubercart.org>
-Date:   Tue Mar 23 19:12:38 2010 +0000
-
-    Change drupal_json() to drupal_json_output().
-
-:100644 100644 002ffb1... 1d543d8... M	uc_taxes/uc_taxes.module
-
-commit 003934e5990b0e8d26cf18d877a87a2919a36bc6
-Author: Lyle Mantooth <lyle@ubercart.org>
-Date:   Wed Mar 17 17:51:58 2010 +0000
-
-    Coder review fixes.
-
-:100644 100644 8a3a379... 002ffb1... M	uc_taxes/uc_taxes.module
-
-commit 233c8b831a80f402255680f7dfce7d5241e9fe6b
-Author: Lyle Mantooth <lyle@ubercart.org>
-Date:   Wed Mar 10 14:02:22 2010 +0000
-
-    #510382 by Island Usurper: Namespace Ubercart's hooks.
-
-:100644 100644 39053d1... 8a3a379... M	uc_taxes/uc_taxes.module
-
-commit 543ba1ccfbf07267c0aa7d7c8a9740d4fe1397a1
-Author: Lyle Mantooth <lyle@ubercart.org>
-Date:   Wed Mar 3 19:34:13 2010 +0000
-
-    #702640 by Island Usurper: Adjust input sanitation for Drupal 7.
-
-:100644 100644 ba4fa3b... 39053d1... M	uc_taxes/uc_taxes.module
-
-commit 52f1df94c6bbe9af4602371ee81d6664f73855b4
-Author: Lyle Mantooth <lyle@ubercart.org>
-Date:   Wed Feb 10 19:21:50 2010 +0000
-
-    #702660 by Island Usurper: Port JavaScript changes.
-
-:100644 100644 b85f195... ba4fa3b... M	uc_taxes/uc_taxes.module
-
-commit 79ffae71293973e010f0c4dcec7f3db2a122716f
-Author: Lyle Mantooth <lyle@ubercart.org>
-Date:   Mon Feb 8 20:18:54 2010 +0000
-
-    #704584 by Island Usurper: Port the other Ubercart modules to the new Database API.
-
-:100644 100644 ab86d38... b85f195... M	uc_taxes/uc_taxes.module
-
-commit f4d765c17e3d2191500f88c2ae7be04857a1d035
-Author: Lyle Mantooth <lyle@ubercart.org>
-Date:   Mon Feb 8 17:28:24 2010 +0000
-
-    Use sentence case on permission titles.
-
-:100644 100644 a44c76f... ab86d38... M	uc_taxes/uc_taxes.module
-
-commit c88c74de1d7b60c1e94dd51764bc9e09ddb91eda
-Author: Lyle Mantooth <lyle@ubercart.org>
-Date:   Mon Feb 8 17:24:46 2010 +0000
-
-    #702510 by Andy_Lowe: Ported permissions issues.
-
-:100644 100644 412d4b3... a44c76f... M	uc_taxes/uc_taxes.module
-
-commit cf367d2b45a73a77afc84cd06194f5352dfae243
-Author: Lyle Mantooth <lyle@ubercart.org>
-Date:   Mon Jan 25 16:42:00 2010 +0000
-
-    #694570 by maximpodorov: Do not expect the form state to be passed by reference.
-
-:100644 100644 68a3db3... 412d4b3... M	uc_taxes/uc_taxes.module
-
-commit fbb801c694de95f8011ad5948376fa18a4789f31
-Author: Lyle Mantooth <lyle@ubercart.org>
-Date:   Mon Jan 25 16:05:35 2010 +0000
-
-    Merge DRUPAL-6--2 back into HEAD to prepare for Ubercart 7.x-3.x.
-
-:100644 100644 3209431... 68a3db3... M	uc_taxes/uc_taxes.module
-
-commit 9337e08986dcea578e2d32e0ce9ef832ecb4adf3
-Author: Lyle Mantooth <lyle@ubercart.org>
-Date:   Thu Jul 10 12:41:07 2008 +0000
-
-    Begin the Ubercart 6.x-2.x branch.
-
-:100644 100644 1f0edab... 3209431... M	uc_taxes/uc_taxes.module
-
-commit c051df261484119ae515435912fba8f5297e8dc0
-Author: Ryan Szrama <rszrama@49344.no-reply.drupal.org>
-Date:   Mon Nov 5 20:32:02 2007 +0000
-
-    Bulk update for Alpha 8.
-
-:100644 100644 b26b1c8... 1f0edab... M	uc_taxes/uc_taxes.module
-
-commit 28b34d49f0d66f1a2db33dadc52e940b873328af
-Author: Ryan Szrama <rszrama@49344.no-reply.drupal.org>
-Date:   Tue Oct 2 18:11:58 2007 +0000
-
-    Mass update for Alpha 7d.
-
-:100644 100644 3a9b782... b26b1c8... M	uc_taxes/uc_taxes.module
-
-commit b734719ed627e64ab91d5da6a36b3a5e4eeaf2d0
-Author: Ryan Szrama <rszrama@49344.no-reply.drupal.org>
-Date:   Thu Sep 13 15:57:20 2007 +0000
-
-    Mass update for Ubercart Alpha 7c.
-
-:100644 100644 ba571f5... 3a9b782... M	uc_taxes/uc_taxes.module
-
-commit 29313a7bbd39e7e21a44745f9adfce4d7486f9f0
-Author: Ryan Szrama <rszrama@49344.no-reply.drupal.org>
-Date:   Tue Jul 10 20:57:31 2007 +0000
-
-    Bugfixes for Alpha 7b.
-
-:100644 100644 2242248... ba571f5... M	uc_taxes/uc_taxes.module
-
-commit 7097ed39252b9ea418fbea040dc479e2c74415dc
-Author: Ryan Szrama <rszrama@49344.no-reply.drupal.org>
-Date:   Fri Jul 6 21:47:55 2007 +0000
-
-    Mass update for Alpha 7.
-
-:100644 100644 b3329aa... 2242248... M	uc_taxes/uc_taxes.module
-
-commit d1129d8f66e09f94c8baa4cd122fb15b0ac8c9ef
-Author: Ryan Szrama <rszrama@49344.no-reply.drupal.org>
-Date:   Thu Jun 7 21:41:44 2007 +0000
-
-    Mass update for Alpha 6c.
-
-:100644 100644 ad8ce18... b3329aa... M	uc_taxes/uc_taxes.module
-
-commit b512e7cf77ee22c7a3ec11fc03028a64c9628341
-Author: Ryan Szrama <rszrama@49344.no-reply.drupal.org>
-Date:   Tue May 8 21:15:32 2007 +0000
-
-    Alpha 6 upload!  Plenty of bugfixes and feature additions, including code updates, more countries, and better international support.
-
-:100644 100644 7d4dbc3... ad8ce18... M	uc_taxes/uc_taxes.module
-
-commit 7f98c8bca9b790ea32a49254819f5f9e2c5c834e
-Author: Ryan Szrama <rszrama@49344.no-reply.drupal.org>
-Date:   Tue Apr 10 16:21:24 2007 +0000
-
-    Alpha 5 upload with installation fixes, bugfixes, and brand-spanking new country import files. See http://www.ubercart.org/news/apr-10-2007/alpha_5_alive for more information.
-
-:100644 100644 f8d803a... 7d4dbc3... M	uc_taxes/uc_taxes.module
-
-commit 1b0ea3d764cdb6a0ef6be796bb7f5442683ef939
-Author: Ryan Szrama <rszrama@49344.no-reply.drupal.org>
-Date:   Thu Apr 5 15:47:28 2007 +0000
-
-    Upload to get Alpha 4 out the door.
-
-:100644 100644 27ddfd7... f8d803a... M	uc_taxes/uc_taxes.module
-
-commit a2b2f1102a659fa9232ad120902f7d90e5c478fd
-Author: Ryan Szrama <rszrama@49344.no-reply.drupal.org>
-Date:   Mon Mar 19 22:59:42 2007 +0000
-
-    Initial upload of Ubercart, woohoo!
-
-:000000 100644 0000000... 27ddfd7... A	uc_taxes/uc_taxes.module
diff --git a/uc_taxes/uc_taxes.module b/uc_taxes/uc_taxes.module
index 8c2d8a1..3325567 100644
--- a/uc_taxes/uc_taxes.module
+++ b/uc_taxes/uc_taxes.module
@@ -544,7 +544,7 @@ function uc_taxes_uc_calculate_tax($order) {
  */
 function uc_taxes_apply_item_tax($item, $tax) {
   // Determine the product type.
-  if (is_array($item->data) && isset($item->data['type'])) { // Saved in the order product data array.
+  if (isset($item->data['type'])) { // Saved in the order product data array.
     $type = $item->data['type'];
   }
   elseif (empty($item->nid)) { // "Blank-line" product.
@@ -558,7 +558,7 @@ function uc_taxes_apply_item_tax($item, $tax) {
   }
 
   // Determine whether this is a shippable product.
-  if (is_array($item->data) && isset($item->data['shippable'])) { // Saved in the order product data array.
+  if (isset($item->data['shippable'])) { // Saved in the order product data array.
     $shippable = $item->data['shippable'];
   }
   elseif (empty($item->nid)) { // "Blank line" product.
diff --git a/uc_taxes/uc_taxes.module.new b/uc_taxes/uc_taxes.module.new
deleted file mode 100644
index 94053e3..0000000
--- a/uc_taxes/uc_taxes.module.new
+++ /dev/null
@@ -1,640 +0,0 @@
-<?php
-
-/**
- * @file
- * Ubercart Taxes module.
- *
- * Allows tax rules to be set up and applied to orders.
- */
-
-/******************************************************************************
- * Drupal Hooks                                                               *
- ******************************************************************************/
-
-/**
- * Implements hook_permission().
- */
-function uc_taxes_permission() {
-  return array(
-    'configure taxes' => array(
-      'title' => t('Configure taxes'),
-    )
-  );
-}
-
-/**
- * Implements hook_menu().
- */
-function uc_taxes_menu() {
-  $items = array();
-
-  $items['admin/store/settings/taxes'] = array(
-    'title' => 'Taxes',
-    'description' => 'Configure tax rates and rules.',
-    'page callback' => 'uc_taxes_admin_settings',
-    'access arguments' => array('configure taxes'),
-    'file' => 'uc_taxes.admin.inc',
-  );
-  $items['admin/store/settings/taxes/add'] = array(
-    'title' => 'Add a tax rate',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('uc_taxes_form'),
-    'access arguments' => array('configure taxes'),
-    'file' => 'uc_taxes.admin.inc',
-    'type' => MENU_LOCAL_ACTION,
-  );
-  $items['admin/store/settings/taxes/%/edit'] = array(
-    'title' => 'Edit a tax rate',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('uc_taxes_form', 4),
-    'access arguments' => array('configure taxes'),
-    'file' => 'uc_taxes.admin.inc',
-  );
-  $items['admin/store/settings/taxes/%/clone'] = array(
-    'page callback' => 'uc_taxes_clone',
-    'page arguments' => array(4),
-    'access arguments' => array('configure taxes'),
-    'file' => 'uc_taxes.admin.inc',
-  );
-  $items['admin/store/settings/taxes/%/delete'] = array(
-    'title' => 'Delete tax rule',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('uc_taxes_delete_form', 4),
-    'access arguments' => array('configure taxes'),
-    'file' => 'uc_taxes.admin.inc',
-  );
-
-  $items += rules_ui()->config_menu('admin/store/settings/taxes');
-
-  return $items;
-}
-
-/**
- * Implements hook_module_implements_alter().
- *
- * Ensures that all other line items are added to the order before tax
- * calculations are made.
- */
-function uc_taxes_module_implements_alter(&$implementations, $hook) {
-  if ($hook == 'uc_order') {
-    $group = $implementations['uc_taxes'];
-    unset($implementations['uc_taxes']);
-    $implementations['uc_taxes'] = $group;
-  }
-}
-
-/**
- * Implements hook_form_uc_order_edit_form_alter().
- */
-function uc_taxes_form_uc_order_edit_form_alter(&$form, &$form_state) {
-  $order = $form['#order'];
-  $line_items = $order->line_items;
-  foreach ($line_items as $item) {
-    // Tax line items are stored in the database, but they can't be changed by
-    // the user.
-    if ($item['type'] == 'tax') {
-      $form['line_items'][$item['line_item_id']]['title'] = array(
-        '#markup' => check_plain($item['title']),
-      );
-      $form['line_items'][$item['line_item_id']]['amount'] = array(
-        '#theme' => 'uc_price',
-        '#price' => $item['amount'],
-      );
-    }
-  }
-}
-
-/**
- * Implements hook_uc_cart_item().
- *
- * Adds included taxes (VAT) to display price of applicable products.
- */
-function uc_taxes_uc_cart_item($op, $item) {
-  if ($op == 'load') {
-    $product = node_load($item->nid);
-
-    list($amount, $suffixes) = uc_taxes_get_included_tax($product, $item->price);
-
-    $item->display_price += $amount;
-    if (!empty($suffixes)) {
-      $item->display_price_suffix .= implode(' ', $suffixes);
-    }
-  }
-}
-
-/**
- * Implements hook_node_load().
- *
- * Adds included taxes (VAT) to display price of applicable products.
- */
-function uc_taxes_node_load($nodes, $types) {
-  foreach ($nodes as &$node) {
-    if (uc_product_is_product($node)) {
-      $amount = 0;
-      $suffixes = array();
-      if ($node->type == 'product_kit') {
-        // For product kits, the total tax is the sum of the tax on the discounted
-        // prices of component products.
-        foreach ($node->products as $pid => &$product) {
-          $discount =  ($node->mutable != UC_PRODUCT_KIT_MUTABLE) ? $product->discount : 0;
-          list($this_amount, $these_suffixes) = uc_taxes_get_included_tax($product, $product->sell_price + $discount);
-          $amount += $this_amount * $product->qty;
-          $suffixes += $these_suffixes;
-        }
-      }
-      else {
-        list($amount, $suffixes) = uc_taxes_get_included_tax($node);
-      }
-      $node->display_price += $amount;
-      if (!empty($suffixes)) {
-        $node->display_price_suffix .= implode(' ', $suffixes);
-      }
-    }
-  }
-}
-
-/*******************************************************************************
- * Ubercart Hooks
- ******************************************************************************/
-
-/**
- * Implements hook_uc_line_item().
- */
-function uc_taxes_uc_line_item() {
-  $items['tax'] = array(
-    'title' => t('Tax'),
-    'callback' => 'uc_line_item_tax',
-    'weight' => 9,
-    'stored' => TRUE,
-    'default' => FALSE,
-    'calculated' => TRUE,
-    'display_only' => FALSE,
-  );
-  $items['tax_subtotal'] = array(
-    'title' => t('Subtotal excluding taxes'),
-    'callback' => 'uc_line_item_tax_subtotal',
-    'weight' => 7,
-    'stored' => FALSE,
-    'calculated' => FALSE,
-    'display_only' => TRUE,
-  );
-
-  return $items;
-}
-
-/**
- * Implements hook_uc_order().
- *
- * Updates and saves tax line items to the order.
- */
-function uc_taxes_uc_order($op, $order, $arg2) {
-  switch ($op) {
-    case 'save':
-      $changes = array();
-      
-      // Make sure we have the most up-to-date version of tax line items.
-      uc_order_load_line_items($order); 
-      
-      // Get all previously stored tax line items.
-      $old = $order->stored_taxes;
-      $order->stored_taxes = array();
-      if (is_array($order->line_items)) {
-        foreach ($order->line_items as $new_line) {
-          if ($new_line['type'] == 'tax') {
-            $tax_id = $new_line['data']['tax_id'];
-            if (!empty($old[$tax_id])) {
-              if ($new_line['amount'] != $stored[$tax_id]['amount']) {
-                uc_order_update_line_item($line['line_item_id'], $new_line['title'], $new_line['amount'], $new_line['data']);
-                $changes[] = t('Changed %title to %amount.', array('%amount' => uc_currency_format($new_line['amount']), '%title' => $new_line['title']));
-              }
-              $order->stored_taxes[$tax_id] = $new_line;
-              unset ($stored[$tax_id]);
-            }
-            else {
-              $new_line['line_item_id'] = uc_order_line_item_add($order->order_id, 'tax', $new_line['title'], $new_line['amount'], $new_line['weight'], $new_line['data']);
-              $changes[] = t('Added %amount for %title.', array('%amount' => uc_currency_format($new_line['amount']), '%title' => $new_line['title']));
-            }
-          }
-        }
-      }
-      
-      if (!empty($order->stored_taxes)) {
-        foreach ($order->stored_taxes as $line) {
-          uc_order_delete_line_item($line['line_item_id']);
-          $changes[] = t('Removed %title.', array('%title' => $line['title']));
-        }
-      }
-      if (count($changes)) {
-        uc_order_log_changes($order->order_id, $changes);
-      }
-    break;
-  }
-}
-
-/******************************************************************************
- * Menu Callbacks                                                             *
- ******************************************************************************/
-
-/**
- * Handles the tax line item.
- */
-function uc_line_item_tax($op, &$order) {
-  switch ($op) {
-    case 'load':
-      // Calculate the updated taxes.
-      $taxes = uc_taxes_calculate($order);
-      
-      // Index existing stored tax line items and remove them from the order.  They will be replaced
-      // by updated versions from $taxes, but we want to remember them to avoid having to query
-      // stored line items again in hook_uc_order('save', ...).
-      $order->stored_taxes = array();
-      foreach ($order->line_items as $key => $item) {
-        if ($item['type'] == 'tax') {
-          $order->stored_taxes[$item['data']['tax_id']] = $item;
-          unset($order->line_items[$key]);
-        }
-      }
-      
-      // Build and return an array of updated tax line items.
-      $lines = array();
-      foreach ($taxes as $tax_id => $tax) {
-        $line = array(
-          'id' => $tax_id,
-          'title' => $tax->name,
-          'amount' => $tax->amount,
-          'weight' => variable_get('uc_li_tax_weight', 9) + $tax->weight / 10,
-          'data' => $tax->data,
-        );
-        // Make sure the tax_id is saved to the data array.
-        $line['data']['tax_id'] = $tax_id;
-        // If a line already exists for this tax_id, use the saved line_item_id.
-        if (!empty($index[$id])) {
-          $line['id'] = $index[$tax_id]['line_item_id'];
-        }
-        $lines[] = $line;
-      }
-      return $lines;
-  }
-}
-
-/**
- * Handles the line item subtotal before taxes.
- */
-function uc_line_item_tax_subtotal($op, $order) {
-  $amount = 0;
-  switch ($op) {
-    case 'display':
-      $has_taxes = FALSE;
-      $different = FALSE;
-
-      if (is_array($order->products)) {
-        foreach ($order->products as $item) {
-          $amount += $item->price * $item->qty;
-        }
-      }
-      if (is_array($order->line_items)) {
-        foreach ($order->line_items as $key => $line_item) {
-          if ($line_item['type'] == 'subtotal') {
-            continue;
-          }
-          if (substr($line_item['type'], 0, 3) != 'tax') {
-            $amount += $line_item['amount'];
-            $different = TRUE;
-          }
-          else {
-            $has_taxes = TRUE;
-          }
-        }
-      }
-
-      if (isset($order->taxes) && is_array($order->taxes) && count($order->taxes)) {
-        $has_taxes = TRUE;
-      }
-
-      if ($different && $has_taxes) {
-        return array(array(
-          'id' => 'tax_subtotal',
-          'title' => t('Subtotal excluding taxes'),
-          'amount' => $amount,
-          'weight' => variable_get('uc_li_tax_subtotal_weight', 7),
-        ));
-      }
-      break;
-  }
-}
-
-
-/******************************************************************************
- * Module and Helper Functions
- ******************************************************************************/
-
-/**
- * Saves a tax rate to the database.
- *
- * @param $rate
- *   The tax rate object to be saved.
- *
- * @return
- *   The saved tax rate object including the rate ID for new rates.
- */
-function uc_taxes_rate_save($rate) {
-  // Save it as a new rate if no ID is specified.
-  if (!$rate->id) {
-    drupal_write_record('uc_taxes', $rate);
-  }
-  // Otherwise update the existing tax rate's data.
-  else {
-    drupal_write_record('uc_taxes', $rate, array('id'));
-  }
-
-  db_delete('uc_taxed_product_types')
-    ->condition('tax_id', $rate->id)
-    ->execute();
-  db_delete('uc_taxed_line_items')
-    ->condition('tax_id', $rate->id)
-    ->execute();
-
-  $p_insert = db_insert('uc_taxed_product_types')->fields(array('tax_id', 'type'));
-  $l_insert = db_insert('uc_taxed_line_items')->fields(array('tax_id', 'type'));
-
-  foreach ($rate->taxed_product_types as $type) {
-    $p_insert->values(array(
-      'tax_id' => $rate->id,
-      'type' => $type,
-    ));
-  }
-
-  foreach ($rate->taxed_line_items as $type) {
-    $l_insert->values(array(
-      'tax_id' => $rate->id,
-      'type' => $type,
-    ));
-  }
-
-  $p_insert->execute();
-  $l_insert->execute();
-
-  rules_clear_cache();
-
-  return $rate;
-}
-
-
-/**
- * List all the taxes that can apply to an order.  These depend on the order status.  For orders which
- * are still in checkout, any tax can apply.  For orders out of checkout, only taxes originally saved
- * as line items can apply.
- *
- * @param $order
- *   The order
- */
-function uc_taxes_filter_rates($order) {
-  $taxes = array();
-
-  // For orders no longer in checkout, only the saved tax rates can apply.
-  if (isset($order->order_status) && uc_order_status_data($order->order_status, 'state') != 'in_checkout') {
-    if (isset($order->line_items)) {
-      foreach($order->line_items as $item) {
-        if ($item['type'] == 'tax') {
-          if (!empty($item['data']['tax'])) {
-            // Use the rate stored in the line-item.
-            $taxes[] = clone $item['data']['tax'];
-          }
-          else if ($tax = uc_taxes_rate_load($item['data']['tax_id'])) {
-            // For old orders that don't have all the tax info, all we can do is preserve the rate.
-            $tax = clone $tax;
-            if (!empty($item['data']['tax_rate'])) {
-              $tax->rate = $item['data']['tax_rate'];
-            }
-            $taxes[] = $tax;
-          }
-        }
-      }
-    }
-  }
-  // For orders still in checkout, any tax whose conditions are satisfied can apply.
-  else {
-    foreach  (uc_taxes_rate_load() as $rate) {
-      $tax = clone ($rate);
-      if (rules_invoke_component('uc_taxes_' . $tax->id, $order)) {
-        $taxes[] = $tax;
-      }
-    }
-  }
-
-  return $taxes;
-}
-
-/**
- * Loads a tax rate or all tax rates from the database.
- *
- * @param $rate_id
- *   The ID of the specific rate to load or NULL to return all available rates.
- *
- * @return
- *   An object representing the requested tax rate or an array of all tax rates
- *   keyed by rate ID.
- */
-function uc_taxes_rate_load($rate_id = NULL) {
-  static $rates = array();
-
-  // If the rates have not been cached yet...
-  if (empty($rates)) {
-    // Get all the rate data from the database.
-    $result = db_query("SELECT * FROM {uc_taxes} ORDER BY weight");
-
-    // Loop through each returned row.
-    foreach ($result as $rate) {
-      $rate->taxed_product_types = array();
-      $rate->taxed_line_items = array();
-
-      $rates[$rate->id] = $rate;
-    }
-
-    foreach (array('taxed_product_types', 'taxed_line_items') as $field) {
-      $result = db_select('uc_' . $field, 't')->fields('t', array('tax_id', 'type'))->execute();
-      foreach ($result as $record) {
-        $rates[$record->tax_id]->{$field}[] = $record->type;
-      }
-    }
-  }
-
-  // Return a rate as specified.
-  if ($rate_id) {
-    return isset($rates[$rate_id]) ? $rates[$rate_id] : FALSE;
-  }
-  // Or return the whole shebang.
-  else {
-    return $rates;
-  }
-}
-
-/**
- * Deletes a tax rate from the database.
- *
- * @param $rate_id
- *   The ID of the tax rate to delete.
- */
-function uc_taxes_rate_delete($rate_id) {
-  // Delete the tax rate record.
-  db_delete('uc_taxes')
-    ->condition('id', $rate_id)
-    ->execute();
-
-  db_delete('uc_taxed_product_types')
-    ->condition('tax_id', $rate_id)
-    ->execute();
-
-  db_delete('uc_taxed_line_items')
-    ->condition('tax_id', $rate_id)
-    ->execute();
-
-  // Delete the associated conditions if they have been saved to the database.
-  rules_config_delete(array('uc_taxes_' . $rate_id));
-}
-
-/**
- * Calculates the taxes for an order based on enabled tax modules.
- *
- * @param $order
- *   The full order object for the order want to calculate taxes for.
- *
- * @return
- *   An array of taxes for the order.
- */
-function uc_taxes_calculate($order) {
-  // Find any taxes specified by enabled modules.
-  $taxes = module_invoke_all('uc_calculate_tax', $order);
-
-  return $taxes;
-}
-
-/**
- * Calculates the amount and types of taxes that apply to an order.
- */
-function uc_taxes_uc_calculate_tax($order) {
-  if (!is_object($order)) {
-    return array();
-  }
-  if (empty($order->delivery_postal_code)) {
-    $order->delivery_postal_code = $order->billing_postal_code;
-  }
-  if (empty($order->delivery_zone)) {
-    $order->delivery_zone = $order->billing_zone;
-  }
-  if (empty($order->delivery_country)) {
-    $order->delivery_country = $order->billing_country;
-  }
-
-  $order->taxes = array();
-  foreach (uc_taxes_filter_rates($order) as $tax) {
-    if ($line_item = uc_taxes_apply_tax($order, $tax)) {
-      $order->taxes[$line_item->id] = $line_item;
-    }
-  }
-
-  return $order->taxes;
-}
-
-/**
- * Calculates tax for a single product.
- */
-function uc_taxes_apply_item_tax($item, $tax) {
-  $node = node_load($item->nid);
-
-  // Special handling for manually added "Blank line" products.
-  if (!$node) {
-    $node = new stdClass();
-    $node->type = 'blank-line';
-    $node->shippable = $item->weight > 0;
-  }
-
-  // Tax products if they are of a taxed type and if it is shippable if
-  // the tax only applies to shippable products.
-  if (in_array($node->type, $tax->taxed_product_types) && ($tax->shippable == 0 || $node->shippable == 1)) {
-    return $item->price * $item->qty;
-  }
-}
-
-/**
- * Applies taxes to an order.
- *
- * @param $order
- *   The order object being considered.
- * @param $tax
- *   The tax rule calculating the amount.
- *
- * @return
- *   The line item array representing the amount of tax.
- */
-function uc_taxes_apply_tax($order, $tax) {
-  $amount = 0;
-  $taxable_amount = 0;
-  if (is_array($order->products)) {
-    foreach ($order->products as $item) {
-      $taxable_amount += uc_taxes_apply_item_tax($item, $tax);
-    }
-  }
-  $taxed_line_items = $tax->taxed_line_items;
-  if (is_array($order->line_items) && is_array($taxed_line_items)) {
-    foreach ($order->line_items as $key => $line_item) {
-      if ($line_item['type'] == 'tax') {
-        // Don't tax old taxes.
-        continue;
-      }
-      if (in_array($line_item['type'], $taxed_line_items)) {
-        $callback = _uc_line_item_data($line_item['type'], 'tax_adjustment');
-        if (isset($callback) && function_exists($callback)) {
-          $taxable_amount += $callback($line_item['amount'], $order, $tax);
-        }
-        else {
-          $taxable_amount += $line_item['amount'];
-        }
-      }
-    }
-  }
-  if (in_array('tax', $taxed_line_items)) {
-    // Tax taxes that were just calculated.
-    foreach ($order->taxes as $other_tax) {
-      $taxable_amount += $other_tax->amount;
-    }
-  }
-  $amount = $taxable_amount * $tax->rate;
-  if ($amount) {
-    $line_item = (object)array(
-      'id' => $tax->id,
-      'name' => $tax->name,
-      'amount' => $amount,
-      'weight' => $tax->weight,
-      'summed' => 1,
-    );
-    $line_item->data = array(
-      'tax_id' => $tax->id,
-      'tax_rate' => $tax->rate,
-      'tax' => $tax,
-      'taxable_amount' => $taxable_amount,
-      'tax_jurisdiction' => $tax->name,
-    );
-    return $line_item;
-  }
-}
-
-/**
- * Calculates the taxes that should be included in the display price of a product.
- * @param $product
- *   The product whose included taxes are to be calculated.
- * @param $price
- *   If specified, use this value as the tax-base.  Otherwise use the product sell price.
- */
-function uc_taxes_get_included_tax($product, $price = FALSE) {
-  $amount = 0;
-  $suffixes = array();
-  $price = ($price === FALSE) ? $product->sell_price : $price;
-  foreach (uc_taxes_rate_load() as $tax) {
-    if ($tax->display_include && in_array($product->type, $tax->taxed_product_types) && ($tax->shippable == 0 || $product->shippable == 1)) {
-      $amount += $price * $tax->rate;
-      $suffixes[$tax->inclusion_text] = $tax->inclusion_text;
-    }
-  }
-  return array($amount, $suffixes);
-}
-- 
1.7.3.4


From c1f09ddff588170cd3993a4725a3d2e421121612 Mon Sep 17 00:00:00 2001
From: Chris Oden <wodenx@gmail.com>
Date: Wed, 7 Dec 2011 16:48:48 -0500
Subject: [PATCH 13/14] REVISED: Check to be sure ->data is an array before testing values, bc user module sometimes sets it to a serialized string.

---
 uc_taxes/uc_taxes.module |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/uc_taxes/uc_taxes.module b/uc_taxes/uc_taxes.module
index 3325567..8c2d8a1 100644
--- a/uc_taxes/uc_taxes.module
+++ b/uc_taxes/uc_taxes.module
@@ -544,7 +544,7 @@ function uc_taxes_uc_calculate_tax($order) {
  */
 function uc_taxes_apply_item_tax($item, $tax) {
   // Determine the product type.
-  if (isset($item->data['type'])) { // Saved in the order product data array.
+  if (is_array($item->data) && isset($item->data['type'])) { // Saved in the order product data array.
     $type = $item->data['type'];
   }
   elseif (empty($item->nid)) { // "Blank-line" product.
@@ -558,7 +558,7 @@ function uc_taxes_apply_item_tax($item, $tax) {
   }
 
   // Determine whether this is a shippable product.
-  if (isset($item->data['shippable'])) { // Saved in the order product data array.
+  if (is_array($item->data) && isset($item->data['shippable'])) { // Saved in the order product data array.
     $shippable = $item->data['shippable'];
   }
   elseif (empty($item->nid)) { // "Blank line" product.
-- 
1.7.3.4


From e6d82ba3600b4c1400f60f33a05cca5f55312b7d Mon Sep 17 00:00:00 2001
From: Chris Oden <wodenx@gmail.com>
Date: Wed, 7 Dec 2011 18:23:23 -0500
Subject: [PATCH 14/14] Integrate stored tax data into inclusive tax calculations.

---
 uc_taxes/uc_taxes.module |   12 ++++++++----
 1 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/uc_taxes/uc_taxes.module b/uc_taxes/uc_taxes.module
index 8c2d8a1..79d7fc5 100644
--- a/uc_taxes/uc_taxes.module
+++ b/uc_taxes/uc_taxes.module
@@ -134,7 +134,7 @@ function uc_taxes_entity_view_alter(&$build, $entity_type) {
       break;
 
     case 'uc_order_product':
-      list($amount, $suffixes) = uc_taxes_get_included_tax($build['#entity']);
+      list($amount, $suffixes) = uc_taxes_get_included_tax($build['#entity'], $build['#entity']->order);
       $build['price']['#price'] += $amount;
       $build['total']['#price'] += $amount * $build['#entity']->qty;
       if (!empty($suffixes)) {
@@ -394,8 +394,12 @@ function uc_taxes_rate_save($rate) {
 function uc_taxes_filter_rates($order) {
   $taxes = array();
 
+  // If no order, then just return all rates.
+  if (empty($order)) {
+    $taxes = uc_taxes_rate_load();
+  }
   // For orders no longer in checkout, only the saved tax rates can apply.
-  if (isset($order->order_status) && uc_order_status_data($order->order_status, 'state') != 'in_checkout') {
+  elseif (isset($order->order_status) && uc_order_status_data($order->order_status, 'state') != 'in_checkout') {
     if (isset($order->line_items)) {
       foreach($order->line_items as $item) {
         if ($item['type'] == 'tax') {
@@ -648,10 +652,10 @@ function uc_taxes_apply_tax($order, $tax) {
  * @param $product
  *   The product whose included taxes are to be calculated.
  */
-function uc_taxes_get_included_tax($product) {
+function uc_taxes_get_included_tax($product, $order = NULL) {
   $amount = 0;
   $suffixes = array();
-  foreach (uc_taxes_rate_load() as $tax) {
+  foreach (uc_taxes_filter_rates($order) as $tax) {
     if ($tax->display_include) {
       $taxable = uc_taxes_apply_item_tax($product, $tax);
       if (!empty($taxable)) {
-- 
1.7.3.4

