=== 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 @@ + '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 .= '

' . $star_legend . '

'; + } + + $output .= ''; + + 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.') .'
'. 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; }