--- uc_order/uc_order.admin.inc	2010-07-16 08:45:09.000000000 -0800
+++ uc_order/uc_order.admin.inc	2011-01-27 15:43:02.000000000 -0800
@@ -72,12 +72,47 @@
         t('You are using the uBrowser to add products to orders.'),
         t('You are not using the uBrowser to add products to orders.'),
       ),
       '#default_value' => variable_get('uc_ubrowser_product_select', TRUE),
     );
   }
+  $form['rounding'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Price Rounding settings'),
+    '#summary callback' => 'summarize_form',
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+  );
 
+  $form['rounding']['uc_order_rounding_rule'] = array(
+    '#type' => 'select',
+    '#title' => t('Rounding Rule'),
+    '#options' => array(
+      'total' => t('Total all items then round'),
+      'subtotals' => t('Round subtotals before adding'),
+    ),
+    '#summary' => 'Rounding rule: ' . variable_get('uc_order_rounding_rule', 'total'),
+    '#default_value' => variable_get('uc_order_rounding_rule', 'total'),
+    '#description' => t('If your totals do not match the sum of your subtotals (products, taxes, shipping), you likely need to round subtotals first, then total them. Default: round total only.'),
+  );
 
+  $form['rounding']['uc_order_rounding_mode'] = array(
+    '#type' => 'select',
+    '#title' => t('Rounding Mode'),
+    '#options' => array(
+      'PHP_ROUND_UP' => t('Always round up'),
+      'PHP_ROUND_DOWN' => t('Always round down'),
+      'PHP_ROUND_HALF_UP' => t('Round numbers ending in 5 up'),
+      'PHP_ROUND_HALF_DOWN' => t('Round numbers ending in 5 down'),
+      'PHP_ROUND_HALF_EVEN' => t('Round numbers ending in 5 to nearest even number'),
+    ),
+    '#summary' => 'Rounding mode: ' . variable_get('uc_order_rounding_mode', 'PHP_ROUND_HALF_EVEN'),
+    '#default_value' => variable_get('uc_order_rounding_mode', 'PHP_ROUND_HALF_EVEN'),
+    '#description' => t('Defaults to rounding nubmers ending in 5 up to the next higher number (3.865 becomes 3.87).'),
 
+  );
 
   $form['customer'] = array(
     '#type' => 'fieldset',
     '#title' => t('Customer settings'),
     '#summary callback' => 'summarize_form',
     '#collapsible' => FALSE,

--- uc_order/uc_order.line_item.inc	2010-07-11 18:31:08.000000000 -0800
+++ uc_order/uc_order.line_item.inc	2011-01-27 15:32:53.000000000 -0800
@@ -94,22 +93,88 @@
     'type' => 'line_item',
     'subject' => array(
       'order' => $order,
     ),
   );
 
+  $rounding_rule = check_plain(variable_get('uc_order_rounding_rule', 'total'));
+  $rounding_mode = check_plain(variable_get('uc_order_rounding_mode', 'PHP_ROUND_HALF_EVEN'));
+  $precision = variable_get('uc_currency_prec', 2);
 
   if (is_array($order->line_items)) {
     foreach ($order->line_items as $item) {
       $context['subject']['line_item'] = $item;
       if (_line_item_data($item['type'], 'calculated') == TRUE) {
+        if ($rounding_rule == 'subtotals') {
+           $total += uc_order_round(uc_price($item['amount'], $context), $precision, $rounding_mode);
+        }
+        else {
         $total += uc_price($item['amount'], $context);
+        }
       }
     }
   }
+  if ($rounding_rule == 'total') {
+    $total = uc_order_round($total, $precision, $rounding_mode);
+  }
+//dpm($order, 'Order in uc_line_times_calculate');
 
   return $total;
+}
 
+function uc_order_round($number, $precision, $rounding_mode) {
+   // could use PHP_VERSION_ID and check for PHP version and if >= 5.3, use the native function
+   // prior to PHP 5.3, the only functionality for round is equivalent to PHP_ROUND_HALF_UP
+   // There's a lot of unnecesary repetition in the switch statement, but it allows a simple switch
+   // by the default PHP mode parameters.
 
+   $multiplier = pow(10, $precision);
+   $number = $number * $multiplier;
+   $remainder = $number - floor($number);
 
+   switch ($rounding_mode) {
+      case 'PHP_ROUND_UP':
+         $number = ceil($number);
+         break;
+      case 'PHP_ROUND_DOWN':
+         $number = floor($number);
+         break;
+      case 'PHP_ROUND_HALF_UP':
+         if ($remainder >= 0.5) {
+             $number = ceil($number);
+         }
+         else {
+           $number = floor($number);
+         }
+         break;
+      case 'PHP_ROUND_HALF_DOWN':
+         if ($remainder <= 0.5) {
+             $number = floor($number);
+         }
+         else {
+           $number = ceil($number);
+         }
+         break;
+      case 'PHP_ROUND_HALF_EVEN':
+         if ($remainder < 0.5) {
+             $number = floor($number);
+         }
+         elseif ($remainder > 0.5) {
+           $number = ceil($number);
+         }
+         else {
+           if ($number%2) {
+             $number = ceil($number);
+           }
+           else {
+             $number = floor($number);
+           }
+         }
+         break;
+   }
+   return $number/$multiplier;
 }
 
 /**
  * Update a line item.
  */
 function uc_order_update_line_item($id, $title, $amount, $data = NULL) {
