/[drupal]/contributions/modules/ubercart/uc_taxes/uc_taxes.module
ViewVC logotype

Contents of /contributions/modules/ubercart/uc_taxes/uc_taxes.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.12 - (show annotations) (download) (as text)
Thu Jul 10 12:41:07 2008 UTC (16 months, 2 weeks ago) by islandusurper
Branch: MAIN
CVS Tags: HEAD
Branch point for: DRUPAL-6--2
Changes since 1.11: +296 -151 lines
File MIME type: text/x-php
Begin the Ubercart 6.x-2.x branch.
1 <?php
2 // $Id$
3
4 /**
5 * @file
6 * Übercart Taxes module.
7 *
8 * Allows tax rules to be set up and applied to orders.
9 *
10 * Coded by Lyle Mantooth
11 */
12
13 /******************************************************************************
14 * Drupal Hooks *
15 ******************************************************************************/
16
17 function uc_taxes_help($path, $arg) {
18 $output = '';
19 switch ($path) {
20 case 'admin/store/settings/taxes':
21 $output .= t('After setting up tax rates and rules on this page, go to the
22 <a href="!link">Conditional actions settings</a> to add
23 conditions to the taxes. Especially important are the geographic area
24 conditions for the delivery address.', array('!link' => url('admin/settings/ca')));
25 break;
26 }
27 return $output;
28 }
29
30 /**
31 * Implementation of hook_perm().
32 */
33 function uc_taxes_perm() {
34 return array('configure taxes');
35 }
36
37 /**
38 * Implementation of hook_menu().
39 */
40 function uc_taxes_menu() {
41 $items = array();
42
43 $items['admin/store/settings/taxes'] = array(
44 'title' => 'Tax rates and settings',
45 'page callback' => 'uc_taxes_admin_settings',
46 'access arguments' => array('configure taxes'),
47 'type' => MENU_NORMAL_ITEM,
48 );
49 $items['admin/store/settings/taxes/edit'] = array(
50 'title' => 'Edit tax rule',
51 'page callback' => 'drupal_get_form',
52 'page arguments' => array('uc_taxes_form'),
53 'access arguments' => array('configure taxes'),
54 'type' => MENU_CALLBACK,
55 );
56 $items['admin/store/settings/taxes/copy'] = array(
57 'page callback' => 'uc_taxes_copy',
58 'access arguments' => array('configure taxes'),
59 'type' => MENU_CALLBACK,
60 );
61 $items['admin/store/settings/taxes/delete'] = array(
62 'title' => 'Delete tax rule',
63 'page callback' => 'drupal_get_form',
64 'page arguments' => array('uc_taxes_delete'),
65 'access arguments' => array('configure taxes'),
66 'type' => MENU_CALLBACK,
67 );
68 $items['taxes/calculate'] = array(
69 'page callback' => 'uc_taxes_javascript',
70 'access arguments' => array('access content'),
71 'type' => MENU_CALLBACK,
72 );
73
74 return $items;
75 }
76
77 function uc_taxes_init() {
78 require_once('uc_taxes.ca.inc');
79 }
80
81 function uc_taxes_form_alter(&$form, &$form_state, $form_id) {
82 if ($form_id == 'uc_cart_checkout_form') {
83 if (isset($form['panes']['payment'])) {
84 uc_add_js('var tax_weight = '. variable_get('uc_li_tax_weight', 9), 'inline');
85 uc_add_js(drupal_get_path('module', 'uc_taxes') .'/uc_taxes.js');
86 }
87 }
88 else if ($form_id == 'uc_order_edit_form') {
89 if (isset($form['quotes'])) {
90 uc_add_js(drupal_get_path('module', 'uc_taxes') .'/uc_taxes.js');
91 }
92 }
93 }
94
95 /*******************************************************************************
96 * Übercart Hooks
97 ******************************************************************************/
98
99 /**
100 * Implementation of Übercart's hook_line_item().
101 */
102 function uc_taxes_line_item() {
103 $items[] = array(
104 'id' => 'tax',
105 'title' => t('Tax'),
106 'callback' => 'uc_line_item_tax',
107 'weight' => 9,
108 'stored' => true,
109 'default' => FALSE,
110 'calculated' => TRUE,
111 'display_only' => FALSE,
112 );
113 $items[] = array(
114 'id' => 'tax_subtotal',
115 'title' => t('Subtotal excluding taxes'),
116 'callback' => 'uc_line_item_tax_subtotal',
117 'weight' => 7,
118 'stored' => false,
119 'calculated' => false,
120 );
121
122 return $items;
123 }
124
125 /**
126 * Update and save tax line items to the order.
127 */
128 function uc_taxes_order($op, $arg1, $arg2) {
129 switch ($op) {
130 case 'save':
131 $changes = array();
132 $line_items = uc_line_item_tax('load', $arg1);
133 //$arg1->line_items = uc_order_load_line_items($arg1, true);
134 if (is_array($arg1->line_items)) {
135 //drupal_set_message('<pre>'. var_export($arg1->line_items, true) .'</pre>');
136 foreach ($arg1->line_items as $line) {
137 if ($line['type'] == 'tax') {
138 $delete = true;
139 foreach ($line_items as $id => $new_line) {
140 if ($new_line['title'] == $line['title']) {
141 if ($new_line['amount'] != $line['amount']) {
142 uc_order_update_line_item($line['line_item_id'], $new_line['title'], $new_line['amount']);
143 $changes[] = t('Changed %title to %amount.', array('%amount' => uc_currency_format($new_line['amount']), '%title' => $new_line['title']));
144 }
145 unset($line_items[$id]);
146 $delete = false;
147 break;
148 }
149 }
150 if ($delete) {
151 uc_order_delete_line_item($line['line_item_id']);
152 $changes[] = t('Removed %title.', array('%title' => $line['title']));
153 }
154 }
155 }
156 }
157 if (is_array($line_items)) {
158 foreach ($line_items as $line) {
159 uc_order_line_item_add($arg1->order_id, $line['id'], $line['title'], $line['amount'], $line['weight']);
160 $changes[] = t('Added %amount for %title.', array('%amount' => uc_currency_format($line['amount']), '%title' => $line['title']));
161 }
162 }
163 if (count($changes)) {
164 uc_order_log_changes($arg1->order_id, $changes);
165 }
166 break;
167 }
168 }
169
170 /******************************************************************************
171 * Menu Callbacks *
172 ******************************************************************************/
173
174 /**
175 * Display a list of tax rates.
176 */
177 function uc_taxes_admin_settings() {
178 $header = array(t('Name'), t('Rate'), t('Taxed product types'), t('Taxed line items'), t('Weight'), array('data' => t('Actions'), 'colspan' => 3));
179 $rows = array();
180 $result = db_query("SELECT * FROM {uc_taxes} ORDER BY weight");
181 while ($rule = db_fetch_object($result)) {
182 $rule->taxed_product_types = (array)unserialize($rule->taxed_product_types);
183 $rule->taxed_line_items = (array)unserialize($rule->taxed_line_items);
184 $rows[] = array($rule->name, $rule->rate * 100 .'%', implode(', ', $rule->taxed_product_types), 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));
185 }
186 $output = theme('table', $header, $rows);
187 $output .= l('Make a new tax rule.', 'admin/store/settings/taxes/edit');
188 return $output;
189 }
190
191 /**
192 * Create or edit a tax rule.
193 *
194 * @ingroup forms
195 * @see uc_taxes_form_submit
196 */
197 function uc_taxes_form($form_state, $tax_id = 0) {
198 $form = array();
199 if ($tax_id != 0) {
200 $form_state['values'] = db_fetch_array(db_query("SELECT * FROM {uc_taxes} WHERE id = %d", $tax_id));
201 $form_state['values']['taxed_product_types'] = array_values(unserialize($form_state['values']['taxed_product_types']));
202 $form_state['values']['taxed_line_items'] = array_values(unserialize($form_state['values']['taxed_line_items']));
203 }
204
205 $form['id'] = array('#type' => 'hidden', '#value' => $form_state['values']['id']);
206 $form['name'] = array('#type' => 'textfield',
207 '#title' => t('Name'),
208 '#default_value' => isset($form_state['values']['name']) ? $form_state['values']['name'] : '',
209 '#required' => true,
210 );
211 $form['rate'] = array('#type' => 'textfield',
212 '#title' => t('Rate'),
213 '#description' => t('The tax rate as a percent or decimal. Examples: 6%, .06'),
214 '#default_value' => isset($form_state['values']['rate']) ? $form_state['values']['rate'] : '',
215 '#size' => 15,
216 '#required' => true,
217 );
218 $types = array();
219 $node_types = module_invoke_all('node_info');
220 $product_types = module_invoke_all('product_types');
221 foreach ($product_types as $type) {
222 $types[$type] = $node_types[$type]['name'];
223 }
224 $form['taxed_product_types'] = array('#type' => 'checkboxes',
225 '#title' => t('Taxed product types'),
226 '#multiple' => true,
227 '#options' => $types,
228 '#default_value' => isset($form_state['values']['taxed_product_types']) ? $form_state['values']['taxed_product_types'] : NULL,
229 '#description' => t('Apply taxes to specific product types.'),
230 );
231 $options = array();
232 foreach (_line_item_list() as $line_item) {
233 if (!in_array($line_item['id'], array('subtotal', 'tax_subtotal', 'total'))) {
234 $options[$line_item['id']] = $line_item['title'];
235 }
236 }
237 $form['taxed_line_items'] = array('#type' => 'checkboxes',
238 '#title' => t('Taxed line items'),
239 '#multiple' => true,
240 '#options' => $options,
241 '#default_value' => isset($form_state['values']['taxed_line_items']) ? $form_state['values']['taxed_line_items'] : NULL,
242 '#description' => t('Adds the checked line item types to the total before applying this tax.'),
243 );
244 $form['weight'] = array('#type' => 'weight',
245 '#title' => t('Weight'),
246 '#default_value' => isset($form_state['values']['weight']) ? $form_state['values']['weight'] : 0,
247 '#description' => t('Lighter taxes are applied to an order first. This value is unimportant if there are no taxes on tax line items.'),
248 );
249 $form['submit'] = array('#type' => 'submit', '#value' => t('Submit'));
250
251 return $form;
252 }
253
254 /**
255 * Submit handler for uc_taxes_form().
256 */
257 function uc_taxes_form_submit($form, &$form_state) {
258 if ($form_state['values']['op'] == t('Submit')) {
259 if (strpos($form_state['values']['rate'], '%')) {
260 $rate = floatval($form_state['values']['rate']) / 100;
261 }
262 else {
263 $rate = floatval($form_state['values']['rate']);
264 }
265 if (($form_state['values']['id']) != '') {
266 db_query("UPDATE {uc_taxes} SET name = '%s', rate = %f, taxed_product_types = '%s', taxed_line_items = '%s', weight = %d WHERE id = %d",
267 $form_state['values']['name'], $rate, serialize(array_filter($form_state['values']['taxed_product_types'])), serialize(array_filter($form_state['values']['taxed_line_items'])), $form_state['values']['weight'], $form_state['values']['id']
268 );
269 }
270 else {
271 db_query("INSERT INTO {uc_taxes} (name, rate, taxed_product_types, taxed_line_items, weight) VALUES ('%s', %f, '%s', '%s', %d)",
272 $form_state['values']['name'], $rate, serialize(array_filter($form_state['values']['taxed_product_types'])), serialize(array_filter($form_state['values']['taxed_line_items'])), $form_state['values']['weight']
273 );
274 }
275
276 drupal_set_message(t('%name settings have been saved.', array('%name' => $form_state['values']['name'])));
277 }
278 $form_state['redirect'] = 'admin/store/settings/taxes';
279 }
280
281 /**
282 * Copy a tax rule.
283 */
284 function uc_taxes_copy($tax_id) {
285 $rule = db_fetch_array(db_query("SELECT * FROM {uc_taxes} WHERE id = %d", $tax_id));
286
287 db_query("INSERT INTO {uc_taxes} (name, rate, taxed_product_types, taxed_line_items, weight) VALUES ('%s', %f, '%s', '%s', %d)",
288 t('Copy of @rule', array('%rule' => $rule['name'])), $rule['rate'], $rule['taxed_product_types'], $rule['taxed_line_items'], $rule['weight']
289 );
290
291 drupal_goto('admin/store/settings/taxes');
292 }
293
294 /**
295 * Delete a tax rule.
296 *
297 * @ingroup forms
298 * @see uc_taxes_delete_submit
299 */
300 function uc_taxes_delete($tax_id = 0) {
301 //drupal_set_message($tax_id);
302 if ($tax_id != 0) {
303 $rule = db_fetch_object(db_query("SELECT * FROM {uc_taxes} WHERE id = %d", $tax_id));
304 $form = array(
305 'id' => array('#type' => 'value', '#value' => $tax_id),
306 );
307 $output = confirm_form($form, t('Delete !rule?', array('!rule' => $rule->name)), 'admin/store/settings/taxes', '', t('Continue'), t('Cancel'));
308 return $output;
309 }
310 else {
311 drupal_goto('admin/store/settings/taxes');
312 }
313 }
314
315 /**
316 * Submit handler for uc_taxes_delete().
317 */
318 function uc_taxes_delete_submit($form, &$form_state) {
319 if ($form_state['values']['confirm']) {
320 db_query("DELETE FROM {uc_taxes} WHERE id = %d", $form_state['values']['id']);
321 ca_delete_predicate('uc_taxes_'. $form_state['values']['id']);
322 }
323
324 drupal_set_message(t('Tax rule deleted.'));
325
326 $form_state['redirect'] = 'admin/store/settings/taxes';
327 }
328
329 /**
330 * Handle the tax line item.
331 */
332 function uc_line_item_tax($op, $order) {
333 switch ($op) {
334 case 'cart-preview':
335 $taxes = uc_taxes_calculate($order);
336 $script = '';
337 foreach ($taxes as $tax) {
338 $script .= "set_line_item('tax_". $tax->id ."', '". $tax->name ."', ". $tax->amount .", + ". variable_get('uc_li_tax_weight', 9) + ($tax->weight / 10) .", 1, false);\n";
339 }
340 if ($script) {
341 uc_add_js("\$(document).ready(function() {
342 if (window.set_line_item) {
343 ". $script .";
344 render_line_items();
345 }
346 });", 'inline');
347 }
348 break;
349 case 'load':
350 $lines = array();
351 $taxes = uc_taxes_calculate($order);
352 foreach ($taxes as $tax) {
353 $lines[] = array(
354 'id' => 'tax',
355 'title' => $tax->name,
356 'amount' => $tax->amount,
357 'weight' => variable_get('uc_li_tax_weight', 9) + $tax->weight / 10,
358 );
359 }
360 return $lines;
361 }
362 }
363
364 function uc_line_item_tax_subtotal($op, $order) {
365 $amount = 0;
366 if (is_array($order->products)) {
367 foreach ($order->products as $item) {
368 $amount += $item->price * $item->qty;
369 }
370 }
371 if (is_array($order->line_items)) {
372 foreach ($order->line_items as $key => $line_item) {
373 if (substr($line_item['type'], 0, 3) != 'tax') {
374 $amount += $line_item['amount'];
375 $different = true;
376 }
377 else {
378 $has_taxes = true;
379 }
380 }
381 }
382 if (is_array($order->taxes)) {
383 $has_taxes = true;
384 }
385 if ($different && $has_taxes) {
386 switch ($op) {
387 case 'cart-preview':
388 uc_add_js("\$(document).ready(function() {
389 if (window.set_line_item) {
390 set_line_item('tax_subtotal', '". t('Subtotal excluding taxes') ."', ". $amount .", ". variable_get('uc_li_tax_subtotal_weight', 8) .");
391 }
392 });", 'inline');
393 break;
394 case 'load':
395 return array(array(
396 'id' => 'tax_subtotal',
397 'title' => t('Subtotal excluding taxes'),
398 'amount' => $amount,
399 'weight' => variable_get('uc_li_tax_subtotal_weight', 7),
400 ));
401 }
402 }
403 }
404
405 /******************************************************************************
406 * Module and Helper Functions
407 ******************************************************************************/
408
409 /**
410 * Load all tax rates.
411 */
412 function uc_taxes_get_rates() {
413 static $taxes = array();
414
415 if (empty($taxes)) {
416 $result = db_query("SELECT * FROM {uc_taxes} ORDER BY weight");
417 while ($rule = db_fetch_object($result)) {
418 $rule->taxed_product_types = unserialize($rule->taxed_product_types);
419 $rule->taxed_line_items = unserialize($rule->taxed_line_items);
420 $taxes[$rule->id] = $rule;
421 }
422 }
423
424 return $taxes;
425 }
426
427 /**
428 * Calculate the amount and types of taxes that apply to an order.
429 */
430 function uc_taxes_calculate($order) {
431 global $user;
432 if (is_numeric($order)) {
433 $order = uc_order_load($order);
434 $account = user_load(array('uid' => $order->uid));
435 }
436 else if ((int)$order->uid) {
437 $account = user_load(array('uid' => intval($order->uid)));
438 }
439 else {
440 $account = $user;
441 }
442 if (!is_object($order)) {
443 return array();
444 }
445 if (empty($order->delivery_postal_code)) {
446 $order->delivery_postal_code = $order->billing_postal_code;
447 }
448 if (empty($order->delivery_zone)) {
449 $order->delivery_zone = $order->billing_zone;
450 }
451 if (empty($order->delivery_country)) {
452 $order->delivery_country = $order->billing_country;
453 }
454 if (is_array($order->line_items)) {
455 foreach ($order->line_items as $i => $line) {
456 if (substr($line['type'], 0, 4) == 'tax_' && substr($line['type'], 5) != 'subtotal') {
457 unset($order->line_items[$i]);
458 }
459 }
460 }
461 $_SESSION['taxes'] = array();
462 $taxes = uc_taxes_get_rates();
463 $order->taxes = array();
464 $arguments = array(
465 'order' => array(
466 '#entity' => 'uc_order',
467 '#title' => t('Order'),
468 '#data' => $order,
469 ),
470 'tax' => array(
471 '#entity' => 'tax',
472 '#title' => t('Tax rule'),
473 // #data => each $tax in the following foreach() loop;
474 ),
475 'account' => array(
476 '#entity' => 'user',
477 '#title' => t('User'),
478 '#data' => $account,
479 ),
480 );
481 foreach ($taxes as $tax) {
482 $arguments['tax']['#data'] = $tax;
483 $predicates = ca_load_trigger_predicates('calculate_tax_'. $tax->id);
484 if (ca_evaluate_conditions($predicates['uc_taxes_'. $tax->id], $arguments)) {
485 $line_item = uc_taxes_action_apply_tax($order, $tax);
486 if ($line_item) {
487 $order->taxes[$line_item->id] = $line_item;
488 }
489 }
490 }
491 return $order->taxes;
492 }
493
494 /**
495 * AJAX callback for order preview.
496 *
497 * Calculate tax amounts for an order in the payment checkout pane.
498 */
499 function uc_taxes_javascript() {
500 drupal_set_header("Content-Type: text/javascript; charset=utf-8");
501 $order = $_POST['order'];
502 if ($order = unserialize(rawurldecode($order))) {
503 $taxes = uc_taxes_calculate($order);
504 $subtotal = uc_line_item_tax_subtotal('load', $order);
505 if (is_array($subtotal) && !empty($taxes)) {
506 $taxes['subtotal'] = array('id' => 'subtotal', 'name' => $subtotal[0]['title'], 'amount' => $subtotal[0]['amount'], 'weight' => -10);
507 }
508 //drupal_set_message('<pre>'. print_r($taxes, true) .'</pre>');
509 if (count($taxes)) {
510 print drupal_to_js((array)$taxes);
511 }
512 else {
513 print '{}';
514 }
515 }
516 else {
517 print '{}';
518 }
519 exit();
520 }

  ViewVC Help
Powered by ViewVC 1.1.2