View source
<?php
require_once 'uc_taxes_workflow.inc';
function uc_taxes_help($section) {
switch ($section) {
case 'admin/store/settings/taxes':
return t('Add tax rates through this page and then use <a href="!url">Workflow-ng</a> to add conditions to the taxes that limit which orders they are applied to. Especially important are the geographic area conditions for the delivery address. Use the conditions link to jump to a particular tax rate Workflow-ng configuration page.', array(
'!url' => _workflow_ng_ui_path(TRUE),
));
}
}
function uc_taxes_perm() {
return array(
'configure taxes',
);
}
function uc_taxes_menu($may_cache) {
$items = array();
if ($may_cache) {
$items[] = array(
'path' => 'admin/store/settings/taxes',
'title' => t('Tax rates and settings'),
'access' => user_access('configure taxes'),
'callback' => 'uc_taxes_admin_settings',
'type' => MENU_NORMAL_ITEM,
);
$items[] = array(
'path' => 'admin/store/settings/taxes/edit',
'title' => t('Edit tax rule'),
'access' => user_access('configure taxes'),
'callback' => 'drupal_get_form',
'callback arguments' => array(
'uc_taxes_form',
),
'type' => MENU_CALLBACK,
);
$items[] = array(
'path' => 'admin/store/settings/taxes/copy',
'access' => user_access('configure taxes'),
'callback' => 'uc_taxes_copy',
'type' => MENU_CALLBACK,
);
$items[] = array(
'path' => 'admin/store/settings/taxes/delete',
'title' => t('Delete tax rule'),
'access' => user_access('configure taxes'),
'callback' => 'drupal_get_form',
'callback arguments' => array(
'uc_taxes_delete',
),
'type' => MENU_CALLBACK,
);
$items[] = array(
'path' => 'taxes/calculate',
'access' => TRUE,
'callback' => 'uc_taxes_javascript',
'type' => MENU_CALLBACK,
);
}
return $items;
}
function uc_taxes_form_alter($form_id, &$form) {
if ($form_id == 'uc_cart_checkout_form') {
if (isset($form['panes']['payment'])) {
uc_add_js('var tax_weight = ' . variable_get('uc_li_tax_weight', 9), 'inline');
uc_add_js(drupal_get_path('module', 'uc_taxes') . '/uc_taxes.js');
}
}
else {
if ($form_id == 'uc_order_edit_form') {
if (isset($form['quotes'])) {
uc_add_js(drupal_get_path('module', 'uc_taxes') . '/uc_taxes.js');
}
}
}
}
function uc_taxes_line_item() {
$items[] = array(
'id' => 'tax',
'title' => t('Tax'),
'callback' => 'uc_line_item_tax',
'weight' => 9,
'stored' => true,
'default' => FALSE,
'calculated' => TRUE,
'display_only' => FALSE,
);
$items[] = array(
'id' => 'tax_subtotal',
'title' => t('Subtotal excluding taxes'),
'callback' => 'uc_line_item_tax_subtotal',
'weight' => 7,
'stored' => false,
'calculated' => false,
);
return $items;
}
function uc_taxes_order($op, $arg1, $arg2) {
switch ($op) {
case 'save':
$changes = array();
$line_items = uc_line_item_tax('load', $arg1);
if (is_array($arg1->line_items)) {
foreach ($arg1->line_items as $line) {
if ($line['type'] == 'tax') {
$delete = true;
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'], $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]);
$delete = false;
break;
}
}
if ($delete) {
uc_order_delete_line_item($line['line_item_id']);
$changes[] = t('Removed %title.', array(
'%title' => $line['title'],
));
}
}
}
}
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'], $line['data']);
$changes[] = t('Added %amount for %title.', array(
'%amount' => uc_currency_format($line['amount']),
'%title' => $line['title'],
));
}
}
if (count($changes)) {
uc_order_log_changes($arg1->order_id, $changes);
}
break;
}
}
function uc_taxes_admin_settings() {
$header = array(
t('Name'),
t('Rate'),
t('Taxed product types'),
t('Tax only shippable?'),
t('Taxed line items'),
t('Weight'),
array(
'data' => t('Actions'),
'colspan' => 3,
),
);
$result = db_query("SELECT * FROM {uc_taxes} ORDER BY weight");
while ($rule = db_fetch_object($result)) {
$rule->taxed_product_types = (array) unserialize($rule->taxed_product_types);
$rule->taxed_line_items = (array) unserialize($rule->taxed_line_items);
$rows[] = array(
$rule->name,
$rule->rate * 100 . '%',
implode(', ', $rule->taxed_product_types),
$rule->shippable ? t('Yes') : '',
implode(', ', $rule->taxed_line_items),
$rule->weight,
l(t('edit'), 'admin/store/settings/taxes/edit/' . $rule->id),
l(t('copy'), 'admin/store/settings/taxes/copy/' . $rule->id),
l(t('delete'), 'admin/store/settings/taxes/delete/' . $rule->id),
);
}
$output = theme('table', $header, $rows);
$output .= l('Make a new tax rule.', 'admin/store/settings/taxes/edit');
return $output;
}
function uc_taxes_form($tax_id = 0) {
$form = array();
if ($tax_id != 0) {
$form_values = db_fetch_array(db_query("SELECT * FROM {uc_taxes} WHERE id = %d", $tax_id));
$form_values['taxed_product_types'] = array_values(unserialize($form_values['taxed_product_types']));
$form_values['taxed_line_items'] = array_values(unserialize($form_values['taxed_line_items']));
}
$form['id'] = array(
'#type' => 'hidden',
'#value' => $form_values['id'],
);
$form['name'] = array(
'#type' => 'textfield',
'#title' => t('Name'),
'#default_value' => isset($form_values['name']) ? $form_values['name'] : '',
'#required' => true,
);
$form['rate'] = array(
'#type' => 'textfield',
'#title' => t('Rate'),
'#description' => t('The tax rate as a percent or decimal. Examples: 6%, .06'),
'#default_value' => isset($form_values['rate']) ? $form_values['rate'] : '',
'#size' => 15,
'#required' => true,
);
$shippable_options = array(
'0' => t('Tax all products regardless of shipping status'),
1 => t('Tax only shippable products'),
);
$form['shippable'] = array(
'#type' => 'radios',
'#title' => t('Tax products based on shipping status?'),
'#options' => $shippable_options,
'#default_value' => $form_values['shippable'],
);
$types = array();
$node_types = module_invoke_all('node_info');
$product_types = module_invoke_all('product_types');
foreach ($product_types as $type) {
$types[$type] = $node_types[$type]['name'];
}
$form['taxed_product_types'] = array(
'#type' => 'checkboxes',
'#title' => t('Taxed product types'),
'#multiple' => true,
'#options' => $types,
'#default_value' => $form_values['taxed_product_types'],
'#description' => t('Apply taxes to specific product types.'),
);
$options = array();
foreach (_line_item_list() as $line_item) {
if (!in_array($line_item['id'], array(
'subtotal',
'tax_subtotal',
'total',
))) {
$options[$line_item['id']] = $line_item['title'];
}
}
$form['taxed_line_items'] = array(
'#type' => 'checkboxes',
'#title' => t('Taxed line items'),
'#multiple' => true,
'#options' => $options,
'#default_value' => $form_values['taxed_line_items'],
'#description' => t('Adds the checked line item types to the total before applying this tax.'),
);
$form['weight'] = array(
'#type' => 'weight',
'#title' => t('Weight'),
'#default_value' => isset($form_values['weight']) ? $form_values['weight'] : 0,
'#description' => t('Lighter taxes are applied to an order first. This value is unimportant if there are no taxes on tax line items.'),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
return $form;
}
function uc_taxes_form_submit($form_id, $form_values) {
if ($form_values['op'] == t('Submit')) {
if (strpos($form_values['rate'], '%')) {
$rate = floatval($form_values['rate']) / 100;
}
else {
$rate = floatval($form_values['rate']);
}
if ($form_values['id'] != '') {
db_query("UPDATE {uc_taxes} SET name = '%s', rate = %f, taxed_product_types = '%s', taxed_line_items = '%s', weight = %d, shippable = %d WHERE id = %d", $form_values['name'], $rate, serialize(array_filter($form_values['taxed_product_types'])), serialize(array_filter($form_values['taxed_line_items'])), $form_values['weight'], $form_values['shippable'], $form_values['id']);
}
else {
db_query("INSERT INTO {uc_taxes} (id, name, rate, taxed_product_types, taxed_line_items, weight, shippable) VALUES (%d, '%s', %f, '%s', '%s', %d, %d)", db_next_id('{uc_taxes}_id'), $form_values['name'], $rate, serialize(array_filter($form_values['taxed_product_types'])), serialize(array_filter($form_values['taxed_line_items'])), $form_values['weight'], $form_values['shippable']);
}
drupal_set_message(t('%name settings have been saved.', array(
'%name' => $form_values['name'],
)));
}
return 'admin/store/settings/taxes';
}
function uc_taxes_copy($tax_id) {
$rule = db_fetch_array(db_query("SELECT * FROM {uc_taxes} WHERE id = %d", $tax_id));
db_query("INSERT INTO {uc_taxes} (id, name, rate, taxed_product_types, taxed_line_items, weight, shippable) VALUES (%d, '%s', %f, '%s', '%s', %d, %d)", db_next_id('{uc_taxes}_id'), t('Copy of @rule', array(
'%rule' => $rule['name'],
)), $rule['rate'], $rule['taxed_product_types'], $rule['taxed_line_items'], $rule['weight'], $rule['shippable']);
drupal_goto('admin/store/settings/taxes');
}
function uc_taxes_delete($tax_id = 0) {
if ($tax_id != 0) {
$rule = db_fetch_object(db_query("SELECT * FROM {uc_taxes} WHERE id = %d", $tax_id));
$form = array(
'id' => array(
'#type' => 'value',
'#value' => $tax_id,
),
);
$output = confirm_form($form, t('Delete !rule?', array(
'!rule' => $rule->name,
)), 'admin/store/settings/taxes', '', t('Continue'), t('Cancel'));
return $output;
}
else {
drupal_goto('admin/store/settings/taxes');
}
}
function uc_taxes_delete_submit($form_id, $form_values) {
if ($form_values['confirm']) {
db_query("DELETE FROM {uc_taxes} WHERE id = %d", $form_values['id']);
db_query("DELETE FROM {workflow_ng_cfgs} WHERE name = '%s'", 'uc_taxes_' . $form_values['id']);
workflow_ng_clear_cache();
}
drupal_set_message(t('Tax rule deleted.'));
return 'admin/store/settings/taxes';
}
function uc_line_item_tax($op, $order) {
switch ($op) {
case 'cart-preview':
$taxes = module_invoke_all('calculate_tax', $order);
$script = '';
foreach ($taxes as $tax) {
$script .= "set_line_item('tax_" . $tax['id'] . "', '" . $tax['name'] . "', " . $tax['amount'] . ", + " . variable_get('uc_li_tax_weight', 9) + $tax['weight'] / 10 . ", 1, false);\n";
}
if ($script) {
uc_add_js("\$(document).ready(function() {\n if (window.set_line_item) {\n " . $script . ";\n render_line_items();\n }\n });", 'inline');
}
break;
case 'load':
$lines = array();
$taxes = module_invoke_all('calculate_tax', $order);
foreach ($taxes as $tax) {
$lines[] = array(
'id' => 'tax',
'title' => $tax['name'],
'amount' => $tax['amount'],
'weight' => variable_get('uc_li_tax_weight', 9) + $tax['weight'] / 10,
'data' => $tax['data'],
);
}
return $lines;
}
}
function uc_line_item_tax_subtotal($op, $order) {
$amount = 0;
if (is_array($order->products)) {
foreach ($order->products as $item) {
$amount += $item->price * $item->qty;
}
}
if (is_array($order->line_items)) {
foreach ($order->line_items as $key => $line_item) {
if (substr($line_item['type'], 0, 3) != 'tax') {
$amount += $line_item['amount'];
$different = true;
}
else {
$has_taxes = true;
}
}
}
if (is_array($order->taxes)) {
$has_taxes = true;
}
if ($different && $has_taxes) {
switch ($op) {
case 'cart-preview':
uc_add_js("\$(document).ready(function() {\n if (window.set_line_item) {\n set_line_item('tax_subtotal', '" . t('Subtotal excluding taxes') . "', " . $amount . ", " . variable_get('uc_li_tax_subtotal_weight', 8) . ");\n }\n });", 'inline');
break;
case 'load':
return array(
array(
'id' => 'tax_subtotal',
'title' => t('Subtotal excluding taxes'),
'amount' => $amount,
'weight' => variable_get('uc_li_tax_subtotal_weight', 7),
),
);
}
}
}
function uc_taxes_get_rates() {
static $taxes = array();
if (empty($taxes)) {
$result = db_query("SELECT * FROM {uc_taxes} ORDER BY weight");
while ($rule = db_fetch_object($result)) {
$rule->taxed_product_types = unserialize($rule->taxed_product_types);
$rule->taxed_line_items = unserialize($rule->taxed_line_items);
$taxes[$rule->id] = $rule;
}
}
return $taxes;
}
function uc_taxes_calculate($order) {
$taxes = module_invoke_all('calculate_tax', $order);
return $taxes;
}
function uc_taxes_calculate_tax($order) {
global $user;
if (is_numeric($order)) {
$order = uc_order_load($order);
$account = user_load(array(
'uid' => $order->uid,
));
}
else {
if ((int) $order->uid) {
$account = user_load(array(
'uid' => intval($order->uid),
));
}
else {
$account = $user;
}
}
if (!is_object($order)) {
return array();
}
if (empty($order->delivery_postal_code)) {
$order->delivery_postal_code = $order->billing_postal_code;
}
if (empty($order->delivery_zone)) {
$order->delivery_zone = $order->billing_zone;
}
if (empty($order->delivery_country)) {
$order->delivery_country = $order->billing_country;
}
if (is_array($order->line_items)) {
foreach ($order->line_items as $i => $line) {
if (substr($line['type'], 0, 4) == 'tax_' && substr($line['type'], 5) != 'subtotal') {
unset($order->line_items[$i]);
}
}
}
$_SESSION['taxes'] = array();
$taxes = uc_taxes_get_rates();
foreach ($taxes as $tax) {
workflow_ng_invoke_event('calculate_tax_' . $tax->id, $order, $tax, $account, array());
}
$order->taxes = $_SESSION['taxes'];
unset($_SESSION['taxes']);
return $order->taxes;
}
function uc_taxes_javascript() {
drupal_set_header("Content-Type: text/javascript; charset=utf-8");
$order = $_POST['order'];
if ($order = unserialize(rawurldecode($order))) {
$taxes = module_invoke_all('calculate_tax', $order);
$subtotal = uc_line_item_tax_subtotal('load', $order);
if (is_array($subtotal) && !empty($taxes)) {
$taxes['subtotal'] = array(
'id' => 'subtotal',
'name' => $subtotal[0]['title'],
'amount' => $subtotal[0]['amount'],
'weight' => -10,
);
}
if (count($taxes)) {
print drupal_to_js((array) $taxes);
}
else {
print '{}';
}
}
else {
print '{}';
}
exit;
}
function _workflow_ng_ui_path($url = FALSE) {
return $url ? url(WORKFLOW_NG_UI_PATH) : WORKFLOW_NG_UI_PATH;
}