diff --git a/uc_attribute/uc_attribute.module b/uc_attribute/uc_attribute.module
index d1f01c6..eb427b7 100644
--- a/uc_attribute/uc_attribute.module
+++ b/uc_attribute/uc_attribute.module
@@ -504,40 +504,33 @@ 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']) && is_array($node->data['attributes'])) {
+    $options = _uc_cart_product_get_options($node);
+    foreach ($options as $option) {
+      $node->cost += $option['cost'];
+      $node->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 +1256,22 @@ function _uc_attribute_alter_form($product) {
     // Build the attribute's options array.
     $options = array();
     foreach ($attribute->options as $option) {
+      $display_price = '';
+      $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':
-          $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']['#value']);
+          }
           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 = '';
+          $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);
+          }
           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..8db8355 100644
--- a/uc_cart/uc_cart.module
+++ b/uc_cart/uc_cart.module
@@ -303,21 +303,22 @@ 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'])),
+              '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,
             );
             $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']), NULL, TRUE);
+      // 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,50 +1353,33 @@ 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();
-  }
-
-  if (empty($item)) {
-    return;
-  }
-
-  $product = node_load($item->nid);
-  if (!$product) {
-    return;
+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);
   }
 
-  $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);
+  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);
   }
-
-  return $item;
 }
 
 /**
@@ -1689,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..2e2fa34 100644
--- a/uc_cart/uc_cart_checkout_pane.inc
+++ b/uc_cart/uc_cart_checkout_pane.inc
@@ -490,17 +490,18 @@ 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'];
       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')),
@@ -552,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 3244055..5787e52 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,24 +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);
-
-      $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);
@@ -1748,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 = '';
diff --git a/uc_order/uc_order.order_pane.inc b/uc_order/uc_order.order_pane.inc
index 7612880..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),
       );
     }
   }
@@ -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(
@@ -1117,44 +1118,9 @@ function uc_op_products_view_table($order) {
     'weight' => 5,
   );
 
-  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;
-    }
+  if (!empty($order->products)) {
+    $build = entity_view('uc_order_product', $order->products);
+    $table['#rows'] = $build['uc_order_product'];
   }
   else {
     $table['#rows'][]['product'] = array(
@@ -1168,6 +1134,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(
@@ -1221,44 +1188,9 @@ function uc_op_products_customer_table($order) {
     'weight' => 5,
   );
 
-  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;
-    }
+  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.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..26b0ad9 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',
@@ -639,12 +639,41 @@ 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 = '';
+    $nodes[$node->nid]->price = $nodes[$node->nid]->sell_price;
   }
 }
 
 /**
+ * Gets a specific altered variant of a product.  The (possibly cached) base product remains unaltered.
+ *
+ * @param $nid
+ *   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 node_load($nid);
+  if (!empty($data)) {
+    $node->data = $data;
+  }
+  drupal_alter('uc_product', $node);
+  $node->variant = TRUE;
+  return $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) {
@@ -662,10 +691,15 @@ function uc_product_delete(&$node) {
  * Implements hook_view().
  */
 function uc_product_view($node, $view_mode) {
+  // 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' => $node->display_price,
-    'suffix' => array('#markup' => $node->display_price_suffix),
+    '#value' => $variant->price,
+    '#suffixes' => array(),
     '#attributes' => array(
       'class' => array(
         'display-price',
@@ -674,13 +708,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',
@@ -690,7 +724,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',
@@ -701,7 +735,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',
@@ -710,25 +744,27 @@ 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)) {
+
+  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;
 }
@@ -1024,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->display_price * $item->qty;
-  $element['#suffix'] = $item->display_price_suffix;
+  $element['#total'] = $item->price * $item->qty;
+  $element['#suffixes'] = array();
   $element['data'] = array('#type' => 'hidden', '#value' => serialize($item->data));
   $element['qty'] = array(
     '#type' => 'uc_quantity',
@@ -1071,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',
+    );
   }
 }
 
@@ -1205,8 +1247,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;
@@ -1294,7 +1338,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/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/views/uc_product_handler_field_display_price.inc b/uc_product/views/uc_product_handler_field_display_price.inc
index bf205f4..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->display_price;
+    // !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 845c264..bd64e16 100644
--- a/uc_product_kit/uc_product_kit.module
+++ b/uc_product_kit/uc_product_kit.module
@@ -365,29 +365,18 @@ 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.
@@ -395,6 +384,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() {
@@ -660,10 +665,31 @@ function theme_uc_product_kit_items_form($variables) {
  * Implements hook_view().
  */
 function uc_product_kit_view($node, $view_mode) {
+  // 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' => $node->display_price,
-    'suffix' => array('#markup' => $node->display_price_suffix),
+    '#value' => $display_price,
+    '#suffixes' => $suffixes,
     '#attributes' => array(
       'class' => array(
         'product-kit',
@@ -673,13 +699,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',
@@ -690,7 +716,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',
@@ -702,7 +728,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',
@@ -712,8 +738,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,
   );
 
@@ -731,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;
 }
 
@@ -1010,22 +1036,26 @@ 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(&$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($variant->data['kit_discount'])) {
+      $discount = $variant->data['kit_discount'];
+    }
+    elseif (($kit = node_load($variant->data['kit_id'])) && $kit->mutable != UC_PRODUCT_KIT_MUTABLE) {
+      $discount = $kit->products[$variant->nid]->discount;
+    }
+    else {
+      $discount = 0;
+    }
+    $variant->price += $discount;
   }
 }
 
+
 /**
  * Implements hook_uc_order_product_alter().
  *
@@ -1040,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;
@@ -1070,6 +1099,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().
  *
@@ -1113,8 +1143,9 @@ function uc_product_kit_uc_cart_display($item) {
       );
       $element['description'] = array('#markup' => '');
       $element['#total'] = 0;
-      $element['#suffix'] = $kit->display_price_suffix;
+      $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;
     }
@@ -1128,7 +1159,9 @@ function uc_product_kit_uc_cart_display($item) {
       );
     }
 
-    $elements[$unique_id]['#total'] += $item->display_price * $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;
 
diff --git a/uc_store/uc_store.module b/uc_store/uc_store.module
index 1945c2b..037413c 100644
--- a/uc_store/uc_store.module
+++ b/uc_store/uc_store.module
@@ -516,11 +516,14 @@ 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(),
     ),
+    'uc_qty' => array(
+      'variables' => array('qty' => 1),
+    ),
     'uc_uid' => array(
       'variables' => array('uid' => 0),
     ),
@@ -699,11 +702,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;
 }
 
 /**
@@ -716,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
diff --git a/uc_taxes/uc_taxes.module b/uc_taxes/uc_taxes.module
index b0b2e8a..79d7fc5 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', 'entity_view_alter'))) {
     $group = $implementations['uc_taxes'];
     unset($implementations['uc_taxes']);
     $implementations['uc_taxes'] = $group;
@@ -105,51 +105,43 @@ function uc_taxes_form_uc_order_edit_form_alter(&$form, &$form_state) {
 }
 
 /**
- * Implements hook_uc_cart_item().
+ * Implements hook_entity_view_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);
-
-    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;
+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;
         }
       }
-      else {
-        list($amount, $suffixes) = uc_taxes_get_included_tax($node);
+      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;
+        }
       }
-      $node->display_price += $amount;
+      break;
+
+    case 'uc_order_product':
+      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)) {
-        $node->display_price_suffix .= implode(' ', $suffixes);
+        $build['price']['#suffixes'] += $suffixes;
+        $build['total']['#suffixes'] += $suffixes;
       }
-    }
+      break;
   }
 }
 
@@ -402,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') {
@@ -548,22 +544,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 (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.
+    $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 (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.
+    $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;
   }
 }
 
@@ -583,7 +601,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;
@@ -633,18 +651,19 @@ 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, $order = NULL) {
   $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;
+  foreach (uc_taxes_filter_rates($order) as $tax) {
+    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);
 }
