=== modified file 'uc_order/uc_order.install'
--- uc_order/uc_order.install	2008-12-03 18:59:40 +0000
+++ uc_order/uc_order.install	2009-02-27 14:24:30 +0000
@@ -72,6 +72,7 @@
         title varchar(128) NOT NULL,
         amount decimal(10,2) NOT NULL,
         weight smallint(6) NOT NULL,
+        data text,
         PRIMARY KEY (line_item_id),
         KEY order_id (order_id)
       ) /*!40100 DEFAULT CHARACTER SET UTF8 */ ");
@@ -402,3 +403,19 @@
   return $ret;
 }
 
+function uc_order_update_9() {
+  $ret = array();
+
+  switch ($GLOBALS['db_type']) {
+    case 'mysql':
+    case 'mysqli':
+      $ret[] = update_sql("ALTER TABLE {uc_order_line_items} ADD data text");
+    break;
+    case 'pgsql':
+      db_add_column($ret, 'uc_order_line_items', 'data', 'text');
+    break;
+  }
+
+  return $ret;
+}
+

=== modified file 'uc_order/uc_order.module'
--- uc_order/uc_order.module	2008-12-19 20:39:40 +0000
+++ uc_order/uc_order.module	2009-02-27 14:28:55 +0000
@@ -2678,6 +2678,7 @@
         'title' => $row->title,
         'amount' => $row->amount,
         'weight' => $row->weight,
+        'data' => unserialize($row->data),
       );
     }
   }
@@ -2696,6 +2697,7 @@
               'title' => $line['title'],
               'amount' => $line['amount'],
               'weight' => isset($line['weight']) ? $line['weight'] : $type['weight'],
+              'data' => $line['data'],
             );
           }
         }
@@ -2933,6 +2935,7 @@
               'title' => $line['title'],
               'amount' => $line['amount'],
               'weight' => $item['weight'],
+              'data' => $line['data'],
             );
           }
         }

=== modified file 'uc_order/uc_order_line_item.inc'
--- uc_order/uc_order_line_item.inc	2008-04-22 21:34:21 +0000
+++ uc_order/uc_order_line_item.inc	2009-02-27 15:14:24 +0000
@@ -71,9 +71,14 @@
 /**
  * Update a line item.
  */
-function uc_order_update_line_item($id, $title, $amount) {
+function uc_order_update_line_item($id, $title, $amount, $data = NULL) {
   db_query("UPDATE {uc_order_line_items} SET title = '%s', amount = %f "
           ."WHERE line_item_id = %d", $title, $amount, $id);
+
+  if (!is_null($data)) {
+    db_query("UPDATE {uc_order_line_items} SET data = '%s' WHERE line_item_id = %d", serialize($data), $id);
+  }
+
   return TRUE;
 }
 
@@ -91,12 +96,12 @@
   return TRUE;
 }
 
-function uc_order_line_item_add($order_id, $type, $title, $amount, $weight = NULL) {
+function uc_order_line_item_add($order_id, $type, $title, $amount, $weight = NULL, $data = NULL) {
   if (is_null($weight)) {
     $weight = _line_item_data($type, 'weight');
   }
 
-  db_query("INSERT INTO {uc_order_line_items} (order_id, type, title, amount, weight) VALUES (%d, '%s', '%s', %f, %d)", $order_id, $type, $title, $amount, $weight);
+  db_query("INSERT INTO {uc_order_line_items} (order_id, type, title, amount, weight, data) VALUES (%d, '%s', '%s', %f, %d, '%s')", $order_id, $type, $title, $amount, $weight, serialize($data));
 
   return TRUE;
 }

=== added directory 'uc_tax_report'
=== added file 'uc_tax_report/uc_tax_report.info'
--- uc_tax_report/uc_tax_report.info	1970-01-01 00:00:00 +0000
+++ uc_tax_report/uc_tax_report.info	2009-02-27 14:30:26 +0000
@@ -0,0 +1,6 @@
+; $Id$
+name = Tax Report
+description = View a report about sales tax your customers paid.
+dependencies = uc_reports uc_taxes
+package = "Ubercart - core (optional)"
+

=== added file 'uc_tax_report/uc_tax_report.module'
--- uc_tax_report/uc_tax_report.module	1970-01-01 00:00:00 +0000
+++ uc_tax_report/uc_tax_report.module	2009-02-27 15:09:01 +0000
@@ -0,0 +1,259 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Display a report on sales tax to store admin.
+ *
+ */
+
+/**
+ * Implementation of hook_menu().
+ */
+function uc_tax_report_menu() {
+  $items = array();
+
+  $items[] = array(
+    'path' => 'admin/store/reports/tax',
+    'title' => t('Sales tax report'),
+    'description' => t('View report on sales tax'),
+    'callback' => 'uc_tax_report_report_page',
+    'access' => user_access('view reports'),
+    'type' => MENU_NORMAL_ITEM,
+  );
+
+  return $items;
+}
+
+// Displays the sales tax report form and table.
+function uc_tax_report_report_page($start_date = NULL, $end_date = NULL, $status = NULL) {
+  $timezone = _uc_reports_timezone_offset();
+  $timezone_offset = time() + $timezone;
+  $format = variable_get('uc_date_format_default', 'm/d/Y');
+
+  // Use default report parameters if we don't detect values in the URL.
+  if ($start_date == '') {
+    $args = array(
+      'start_date' => gmmktime(0, 0, 0, gmdate('n', $timezone_offset), 1, gmdate('Y', $timezone_offset) - 1),
+      'end_date' => time(),
+      'status' => FALSE,
+    );
+  }
+  else {
+    $args = array(
+      'start_date' => $start_date,
+      'end_date' => $end_date,
+      'status' => explode(',', urldecode($status)),
+    );
+  }
+
+  // Pull the order statuses into a SQL friendly array.
+  if ($args['status'] === FALSE) {
+    $order_statuses = _uc_reports_order_statuses();
+  }
+  else {
+    $order_statuses = "('". implode("', '", $args['status']) ."')";
+  }
+
+  // Build the header for the report table.
+  $header = array(t('Tax Name'), t('Jurisdiction'), t('Tax rate'), t('Total taxable amount'), t('Total tax collected'));
+  $rows = array();
+  $csv_rows = array();
+  $csv_rows[] = $header;
+
+  // Query to get the tax line items in this date range
+
+  $result = db_query("SELECT ucoli.amount, ucoli.title, ucoli.data FROM {uc_orders} ucord LEFT JOIN {uc_order_statuses} ON order_status_id = order_status LEFT JOIN {uc_order_line_items} ucoli ON ucord.order_id = ucoli.order_id WHERE %d <= created AND created <= %d AND order_status IN $order_statuses AND ucoli.type = 'tax'", $args['start_date'], $args['end_date']);
+
+  // add up the amounts by jurisdiction
+  $totals = array();
+  $no_meta_totals = array();
+
+  while($item = db_fetch_object($result)) {
+    $name = trim($item->title);
+    $amount = floatval($item->amount);
+
+    // get the meta-data out of the serialized array
+    $data = unserialize($item->data);
+    $jurisdiction = trim($data['tax_jurisdiction']);
+    $taxable_amount = floatval($data['taxable_amount']);
+    $rate = floatval($data['tax_rate']);
+
+    // make a line item in the report for each name/jurisdiction/rate
+    $key = strtolower($name) . strtolower($jurisdiction) . number_format($rate, 5);
+
+    if(!empty($jurisdiction) && $amount && $taxable_amount) {
+      // we have meta-data
+      if(empty($totals[$key])) {
+        $totals[$key] = array(
+          'name' => $name,
+          'jurisdiction' => $jurisdiction,
+          'rate' => $rate,
+          'taxable_amount' => $taxable_amount,
+          'amount' => $amount,
+        );
+      }
+      else {
+        $totals[$key]['taxable_amount'] += $taxable_amount;
+        $totals[$key]['amount'] += $amount;
+      }
+    }
+    elseif ($amount) {
+      // Old data: no meta-data was stored. Just report the amount collected.
+      if(empty($no_meta_totals[$key])) {
+        $no_meta_totals[$key] = array(
+          'name' => $name,
+          'amount' => $amount,
+        );
+      }
+      else {
+        $no_meta_totals[$key]['amount'] += $amount;
+      }
+    }
+  }
+
+  // sort and make this into a report
+
+  ksort($totals);
+  ksort($no_meta_totals);
+
+  $taxable_amount = 0;
+  $amount = 0;
+  $star_legend = '';
+
+  foreach($totals as $line) {
+    $row = array(
+      $line['name'],
+      $line['jurisdiction'],
+      number_format($line['rate'] * 100, 3) . '%',
+      uc_currency_format($line['taxable_amount']),
+      uc_currency_format($line['amount']),
+    );
+    $rows[] = $row;
+    $csv_rows[] = $row;
+    $taxable_amount += $line['taxable_amount'];
+    $amount += $line['amount'];
+  }
+
+  foreach($no_meta_totals as $line) {
+    $row = array(
+      $line['name'],
+      '*',
+      '*',
+      '*',
+      uc_currency_format($line['amount']),
+    );
+    $rows[] = $row;
+    $csv_rows[] = $row;
+    $amount += $line['amount'];
+    // We have at least one no-meta-data line. Explain why.
+    $star_legend = t('* No information on jurisdiction, tax rate, or taxable amount is available for this line.');
+  }
+
+  // Add a totals line.
+  $row = array(
+    t('Total'),
+    '',
+    '',
+    uc_currency_format($taxable_amount),
+    uc_currency_format($amount),
+  );
+  $rows[] = $row;
+  $csv_rows[] = $row;
+
+  // Cache the CSV export.
+  $csv_data = uc_reports_store_csv('uc_tax_report', $csv_rows);
+
+  // Build the page output holding the form, table, and CSV export link.
+  $output = drupal_get_form('uc_tax_report_params_form', $args, $args['status']);
+  $output .= theme('table', $header, $rows, array('width' => '100%', 'class' => 'uc-sales-table'));
+  if ($star_legend) {
+    $output .= '<div class="uc-reports-note"><p>' . $star_legend . '</p></div>';
+  }
+
+  $output .= '<div class="uc-reports-links">'. l(t('Export to CSV file.'), 'admin/store/reports/getcsv/'. $csv_data['report'] .'/'. $csv_data['user']) .'</div>';
+
+  return $output;
+}
+
+// Form for parms on the tax report
+function uc_tax_report_params_form($values) {
+  $form = array();
+  $form['params'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Customize tax report parameters'),
+    '#description' => t('Adjust these values and update the report to build your sales tax report. Once submitted, the report may be bookmarked for easy reference in the future.'),
+    '#collapsible' => TRUE,
+    '#collapsed' => FALSE,
+  );
+
+  $form['params']['start_date'] = array(
+    '#type' => 'date',
+    '#title' => t('Start date'),
+    '#default_value' => array(
+      'month' => format_date($values['start_date'], 'custom', 'n', 0),
+      'day' => format_date($values['start_date'], 'custom', 'j', 0),
+      'year' => format_date($values['start_date'], 'custom', 'Y', 0),
+    ),
+  );
+  $form['params']['end_date'] = array(
+    '#type' => 'date',
+    '#title' => t('End date'),
+    '#default_value' => array(
+      'month' => format_date($values['end_date'], 'custom', 'n', 0),
+      'day' => format_date($values['end_date'], 'custom', 'j', 0),
+      'year' => format_date($values['end_date'], 'custom', 'Y', 0),
+    ),
+  );
+
+  $options = array();
+  foreach (uc_order_status_list() as $status) {
+    $options[$status['id']] = $status['title'];
+  }
+
+  $stat = $values['status'];
+  if ($stat === FALSE) {
+    $stat = variable_get('uc_reports_reported_statuses', array('completed'));
+  }
+
+  $form['params']['status'] = array(
+    '#type' => 'select',
+    '#title' => t('Order statuses'),
+    '#description' => t('Only orders with selected statuses will be included in the report.') .'<br />'. t('Hold Ctrl + click to select multiple statuses.'),
+    '#options' => $options,
+    '#default_value' => $stat,
+    '#multiple' => TRUE,
+    '#size' => 5,
+  );
+
+  $form['params']['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Update report'),
+  );
+
+  return $form;
+}
+
+function uc_tax_report_params_form_validate($form_id, $form_values) {
+  if (empty($form_values['status'])) {
+    form_set_error('status', t('You must select at least one order status.'));
+  }
+}
+
+function uc_tax_report_params_form_submit($form_id, $form_values) {
+  $timezone_offset = _uc_reports_timezone_offset();
+
+  // Build the start and end dates from the form.
+  $start_date = gmmktime(0, 0, 0, $form_values['start_date']['month'], $form_values['start_date']['day'], $form_values['start_date']['year']);
+  $end_date = gmmktime(23, 59, 59, $form_values['end_date']['month'], $form_values['end_date']['day'], $form_values['end_date']['year']);
+
+  $args = array(
+    $start_date,
+    $end_date,
+    drupal_urlencode(implode(',', array_keys($form_values['status']))),
+  );
+
+  return 'admin/store/reports/tax/'. implode('/', $args);
+}
+
+

=== modified file 'uc_taxes/uc_taxes.module'
--- uc_taxes/uc_taxes.module	2009-02-06 18:26:57 +0000
+++ uc_taxes/uc_taxes.module	2009-02-27 15:19:59 +0000
@@ -138,7 +138,7 @@
             foreach ($line_items as $id => $new_line) {
               if ($new_line['title'] == $line['title']) {
                 if ($new_line['amount'] != $line['amount']) {
-                  uc_order_update_line_item($line['line_item_id'], $new_line['title'], $new_line['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']));
                 }
                 unset($line_items[$id]);
@@ -155,7 +155,7 @@
       }
       if (is_array($line_items)) {
         foreach ($line_items as $line) {
-          uc_order_line_item_add($arg1->order_id, $line['id'], $line['title'], $line['amount'], $line['weight']);
+          uc_order_line_item_add($arg1->order_id, $line['id'], $line['title'], $line['amount'], $line['weight'], $line['data']);
           $changes[] = t('Added %amount for %title.', array('%amount' => uc_currency_format($line['amount']), '%title' => $line['title']));
         }
       }
@@ -363,6 +363,7 @@
           'title' => $tax['name'],
           'amount' => $tax['amount'],
           'weight' => variable_get('uc_li_tax_weight', 9) + $tax['weight'] / 10,
+          'data' => $tax['data'],
         );
       }
       return $lines;

=== modified file 'uc_taxes/uc_taxes_workflow.inc'
--- uc_taxes/uc_taxes_workflow.inc	2009-02-06 18:26:57 +0000
+++ uc_taxes/uc_taxes_workflow.inc	2009-02-27 15:18:46 +0000
@@ -82,13 +82,14 @@
  */
 function uc_taxes_action_apply_tax($order, $tax) {
   $amount = 0;
+  $taxable_amount = 0;
   if (is_array($order->products)) {
     foreach ($order->products as $item) {
       $node = node_load($item->nid);
       // 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)) {
-        $amount += $item->price * $item->qty * $tax->rate;
+        $taxable_amount += $item->price * $item->qty;
       }
     }
   }
@@ -100,17 +101,28 @@
         continue;
       }
       if (in_array($line_item['type'], $taxed_line_items)) {
-        $amount += $line_item['amount'] * $tax->rate;
+        $taxable_amount += $line_item['amount'];
       }
     }
   }
   if (isset($taxed_line_items['tax'])) {
     foreach ($_SESSION['taxes'] as $other_tax) {
-      $amount += $other_tax['amount'] * $tax->rate;
+      $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);
+    $line_item = array(
+      'id' => $tax->id,
+      'name' => $tax->name,
+      'amount' => $amount,
+      'weight' => $tax->weight,
+      'data' => array(
+        'tax_rate' => $tax->rate,
+        'taxable_amount' => $taxable_amount,
+        'tax_jurisdiction' => $tax->name,
+      ),
+    );
     return array('tax_line_item' => $line_item);
   }
 }
@@ -160,5 +172,5 @@
  * Preserve each tax line item for future use.
  */
 function uc_taxes_save_line_item($line_item) {
-  $_SESSION['taxes'][$line_item->id] = array('id' => 'uc_tax_'. $line_item->id, 'name' => $line_item->name, 'amount' => $line_item->amount, 'weight' => $line_item->weight);
+  $_SESSION['taxes'][$line_item->id] = (array)$line_item;
 }

