diff --git a/uc_cart/uc_cart.pages.inc b/uc_cart/uc_cart.pages.inc
index 9b114f5..5f2bafd 100644
--- a/uc_cart/uc_cart.pages.inc
+++ b/uc_cart/uc_cart.pages.inc
@@ -146,9 +146,12 @@ function uc_cart_checkout() {
     }
     elseif (!empty($_SESSION['uc_cart_order_rebuild'])) {
       // Or, if the cart has changed, then remove old products and line items.
-      db_delete('uc_order_products')
-        ->condition('order_id', $order->order_id)
+      $efq = new EntityFieldQuery();
+      $result = $efq->entityCondition('entity_type', 'uc_order_product')
+        ->propertyCondition('order_id', $order->order_id)
         ->execute();
+      $product_ids = array_keys($result['uc_order_product']);
+      uc_order_product_delete_multiple($product_ids);
       uc_order_delete_line_item($order->order_id, TRUE);
       $rebuild = TRUE;
     }
diff --git a/uc_order/uc_order.controller.inc b/uc_order/uc_order.controller.inc
index 5ff6967..311f01c 100644
--- a/uc_order/uc_order.controller.inc
+++ b/uc_order/uc_order.controller.inc
@@ -18,7 +18,7 @@ class UcOrderController extends DrupalDefaultEntityController {
         ->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);
+        $order->products = uc_order_product_load_multiple(array_keys($result['uc_order_product']), TRUE);
         foreach ($order->products as $product) {
           $product->order = $order;
           $product->order_uid = $order->uid;
@@ -114,4 +114,36 @@ class UcOrderProductController extends EntityAPIController {
     }
     return parent::buildContent($product, $view_mode, $langcode, $content);
   }
+
+  /**
+   * Overrides EntityAPIController::save().
+   */
+  public function save($product, DatabaseTransaction $transaction = NULL) {
+    // Product kits, particularly, shouldn't actually be added to an order,
+    // but instead they cause other products to be added.
+    if (isset($product->skip_save) && $product->skip_save == TRUE) {
+      return;
+    }
+
+    if (empty($product->weight_units)) {
+      if (empty($product->nid)) {
+        $product->weight_units = variable_get('uc_weight_unit', 'lb');
+      }
+      else {
+        $units = db_query("SELECT weight_units FROM {node} n JOIN {uc_products} p ON n.vid = p.vid WHERE n.nid = :nid", array(':nid' => $product->nid))->fetchField();
+        $product->weight_units = empty($units) ? variable_get('uc_weight_unit', 'lb') : $units;
+      }
+    }
+    return parent::save($product, $transaction);
+  }
+
+  /**
+   * Overrides EntityAPIController::delete().
+   */
+  public function delete($opids, DatabaseTransaction $transaction = NULL) {
+    foreach ($opids as $opid) {
+      module_invoke_all('uc_order_product_delete', $opid);
+    }
+    parent::delete($opids, $transaction);
+  }
 }
diff --git a/uc_order/uc_order.module b/uc_order/uc_order.module
index 1723ffd..1e6180b 100644
--- a/uc_order/uc_order.module
+++ b/uc_order/uc_order.module
@@ -84,6 +84,18 @@ class UcOrder {
 }
 
 /**
+ * Implements hook_help().
+ */
+function uc_order_help($path, $arg) {
+  switch ($path) {
+    case 'admin/store/settings/orders/products/fields':
+      return '<p>' . t('Attach fields to order product entities.  These entities represent the product line items that appear in orders.  Fields attached here are not visible in the current version of Ubercart, but they are available in Views.  A ')
+        . l(t('Computed Field'), 'http://drupal.org/project/computed_field')
+        . t(' is useful for attaching custom data to each product as it is ordered.') . '</p>';
+  }
+}
+
+/**
  * Implements hook_menu().
  */
 function uc_order_menu() {
@@ -288,6 +300,17 @@ function uc_order_menu() {
 }
 
 /**
+ * Implements hook_menu_alter().
+ */
+function uc_order_menu_alter(&$items) {
+  // Adjust the Field UI tabs on admin/store/settings/orders.
+  $items['admin/store/settings/orders/products/fields']['title'] = 'Ordered product fields';
+  $items['admin/store/settings/orders/products/fields']['weight'] = 3;
+  // Disable field display settings for ordered products, as we don't use this anywhere (yet).
+  $items['admin/store/settings/orders/products/display']['access callback'] = FALSE;
+}
+
+/**
  * Implements hook_menu_local_tasks_alter().
  */
 function uc_order_menu_local_tasks_alter(&$data, $router_item, $root_path) {
@@ -483,6 +506,8 @@ function uc_order_entity_info() {
       'base table' => 'uc_order_products',
       'controller class' => 'UcOrderProductController',
       'metadata controller class' => 'UcOrderProductMetadataController',
+      'fieldable' => TRUE,
+      'module' => 'uc_order',
       'entity keys' => array(
         'id' => 'order_product_id',
         'label' => t('Order product'),
@@ -490,6 +515,10 @@ function uc_order_entity_info() {
       'bundles' => array(
         'uc_order_product' => array(
           'label' => t('Order product'),
+          'admin' => array(
+            'path' => 'admin/store/settings/orders/products',
+            'access arguments' => array('administer store'),
+          ),
         ),
       ),
       'view modes' => array(
@@ -502,8 +531,6 @@ function uc_order_entity_info() {
       ),
       // Entity API callbacks.
       'access callback'   => 'uc_order_order_product_access',
-      'save callback'     => 'uc_order_product_entity_save',
-      'deletion callback' => 'uc_order_product_delete',
     ),
   );
 }
@@ -943,9 +970,12 @@ function uc_order_delete($order_id) {
     db_delete('uc_orders')
       ->condition('order_id', $order_id)
       ->execute();
-    db_delete('uc_order_products')
-      ->condition('order_id', $order_id)
+    $efq = new EntityFieldQuery();
+    $result = $efq->entityCondition('entity_type', 'uc_order_product')
+      ->propertyCondition('order_id', $order->order_id)
       ->execute();
+    $product_ids = array_keys($result['uc_order_product']);
+    uc_order_product_delete_multiple($product_ids);
     db_delete('uc_order_comments')
       ->condition('order_id', $order_id)
       ->execute();
@@ -987,31 +1017,11 @@ function uc_order_order_product_access($op, $product = NULL, $account = NULL) {
 }
 
 /**
- * Entity API "save callback" for uc_order_product entity.
- *
- * Saves a product to an order.
+ * Save a product to an order.
  */
 function uc_order_product_save($order_id, $product) {
-  // Product kits, particularly, shouldn't actually be added to an order,
-  // but instead they cause other products to be added.
-  if (isset($product->skip_save) && $product->skip_save == TRUE) {
-    return;
-  }
-
-  // Update if there is an order_product_id, insert if there isn't.
-  $key = empty($product->order_product_id) ? array() : 'order_product_id';
-  // @TODO order_id should be in the object by this point.
   $product->order_id = $order_id;
-  if (empty($product->weight_units)) {
-    if (empty($product->nid)) {
-      $product->weight_units = variable_get('uc_weight_unit', 'lb');
-    }
-    else {
-      $units = db_query("SELECT weight_units FROM {node} n JOIN {uc_products} p ON n.vid = p.vid WHERE n.nid = :nid", array(':nid' => $product->nid))->fetchField();
-      $product->weight_units = empty($units) ? variable_get('uc_weight_unit', 'lb') : $units;
-    }
-  }
-  return drupal_write_record('uc_order_products', $product, $key);
+  return entity_save('uc_order_product', $product);
 }
 
 /**
@@ -1024,20 +1034,58 @@ function uc_order_product_entity_save($product) {
 }
 
 /**
- * Entity API "deletion callback" for uc_order_product entity.
+ * Remove a single product line from an order.
  *
- * Removes a product from an order.
+ * @param integer $order_product_id
+ *   Ordered product ID of product to remove.
  */
 function uc_order_product_delete($order_product_id) {
-  // Allow other modules to take action before the product is removed
-  // from the order.  If the hook is involved after the item is deleted
-  // from {uc_order_products} then $order_product_id will be pointing to
-  // the deleted row, and will be useless.
-  module_invoke_all('uc_order_product_delete', $order_product_id);
-
-  db_delete('uc_order_products')
-    ->condition('order_product_id', $order_product_id)
-    ->execute();
+  return uc_order_product_delete_multiple(array($order_product_id));
+}
+
+/**
+ * Remove multiple product lines from an order.
+ *
+ * @param array $order_product_ids
+ *   List of ordered product IDs of products to remove.
+ */
+function uc_order_product_delete_multiple($order_product_ids) {
+  return entity_delete('uc_order_product', $order_product_ids);
+}
+
+/**
+ * Load a single ordered product entity.
+ *
+ * @param integer $opid
+ *   The ID of the ordered product entity.
+ * @param boolean $reset
+ *   Whether to reset the internal cache for ordered product IDs. Defaults to
+ *   FALSE.
+ *
+ * @return object
+ *   The ordered product entity matching $opid.
+ */
+function uc_order_product_load($opid, $reset = FALSE) {
+  $products = uc_order_product_load_multiple(array($opid), $reset);
+  return reset($products);
+}
+
+/**
+ * Load multiple ordered product entities.
+ *
+ * @param array $opids
+ *   Array of ordered product IDs of products to load.  Default is the empty
+ *   array, which loads all ordered products.
+ * @param boolean $reset
+ *   Whether to reset the internal cache for ordered product IDs. Defaults to
+ *   FALSE.
+ *
+ * @return array
+ *   An array of ordered product entities matching $opids, indexed by
+ *   ID.
+ */
+function uc_order_product_load_multiple($opids = array(), $reset = FALSE) {
+  return entity_load('uc_order_product', $opids, NULL, $reset);
 }
 
 /**
diff --git a/uc_order/views/uc_order.views.inc b/uc_order/views/uc_order.views.inc
index 7646531..4f86b7d 100644
--- a/uc_order/views/uc_order.views.inc
+++ b/uc_order/views/uc_order.views.inc
@@ -378,6 +378,17 @@ function uc_order_views_data() {
   }
 
   // Ordered products.
+  // Get the standard EntityAPI Views data table.
+  $data['uc_order_products'] =  entity_views_table_definition('uc_order_product');
+  // Remove undesirable fields
+  foreach(array('data') as $bad_field) {
+    if (isset($data['uc_order_products'][$bad_field])) {
+      unset($data['uc_order_products'][$bad_field]);
+    }
+  }
+  // Fix incomplete fields
+  $data['uc_order_products']['weight_units']['title'] = t('Weight units');
+
   $data['uc_order_products']['table']['group'] = t('Ordered product');
   $data['uc_order_products']['table']['base'] = array(
     'field' => 'order_product_id',
