/[drupal]/contributions/modules/invoice/invoice.module
ViewVC logotype

Contents of /contributions/modules/invoice/invoice.module

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


Revision 1.4 - (show annotations) (download) (as text)
Sat Dec 13 14:03:32 2008 UTC (11 months, 1 week ago) by pvogelaar
Branch: MAIN
CVS Tags: HEAD
Changes since 1.3: +2 -2 lines
File MIME type: text/x-php
* Fixed bug: "Invoice items : Unit Cost incl VAT is wrong whent you use a VAT less of 10%" => http://drupal.org/node/345800
* Added a TODO: Possibility to link an invoice to a site user + page for that user to view his invoices => http://drupal.org/node/346516
  - With a extra option to also send the invoice by email (if no site user is selected the customer email field that is not implemented yet
    will be choosen, if both are empty an error will be thrown if this checkbox is still checked. => http://drupal.org/node/346005
1 <?php
2 // $Id$
3
4 /**
5 * @file
6 * Invoice module
7 *
8 * This module was developed by Platina Designs, http://www.platinadesigns.nl
9 *
10 * @author Pieter Vogelaar <ps.vogelaar@platinadesigns.nl>
11 */
12
13 // Set locale so money has the right format for the preferred culture
14 setlocale(LC_MONETARY, variable_get('invoice_locale', 'en_US.utf8'));
15
16 // Include files
17
18 // Ajax functions
19 require_once dirname(__FILE__) .'/invoice_ajax.inc';
20 // Form functions
21 require_once dirname(__FILE__) .'/invoice_form.inc';
22 // Helper functions
23 require_once dirname(__FILE__) .'/invoice_helpers.inc';
24
25 /**
26 * Implementation of hook_node_info()
27 */
28 function invoice_node_info() {
29 return array(
30 'invoice' => array(
31 'name' => t('Invoice'),
32 'module' => 'invoice',
33 'description' => t("If you want to add an invoice, use this content type."),
34 'has_title' => TRUE,
35 'title_label' => t('Title'),
36 'has_body' => TRUE,
37 'body_label' => t('Body'),
38 )
39 );
40 }
41
42 /**
43 * Implementation of hook_menu()
44 */
45 function invoice_menu() {
46 $items = array();
47 $items['invoices'] = array(
48 'title' => 'Invoices',
49 'page callback' => 'invoice_invoices',
50 'access arguments' => array('access invoices'),
51 );
52 $items['invoice/list'] = array(
53 'title' => 'List invoices',
54 'type' => MENU_DEFAULT_LOCAL_TASK,
55 'access arguments' => array('access invoices'),
56 );
57 $items['invoice/list/customers'] = array(
58 'title' => 'List customers',
59 'type' => MENU_LOCAL_TASK,
60 'access arguments' => array('access invoices'),
61 );
62 $items['invoice/add/invoice'] = array(
63 'title' => 'Add invoice',
64 'page callback' => 'invoice_add_invoice',
65 'access arguments' => array('administer invoices'),
66 'type' => MENU_CALLBACK,
67 );
68 $items['invoice/add/customer'] = array(
69 'title' => 'Add customer',
70 'page callback' => 'invoice_add_customer',
71 'access arguments' => array('administer customers'),
72 'type' => MENU_CALLBACK,
73 );
74 $items['invoice/edit/invoice/%'] = array(
75 'title' => 'Edit invoice',
76 'page callback' => 'invoice_edit_invoice',
77 'page arguments' => array(2),
78 'access arguments' => array('administer invoices'),
79 'type' => MENU_CALLBACK,
80 );
81 $items['invoice/set/template'] = array(
82 'title' => 'Edit invoice',
83 'page callback' => 'invoice_set_template',
84 'access arguments' => array('administer invoices'),
85 'type' => MENU_CALLBACK,
86 );
87 $items['invoice/print/%'] = array(
88 'title' => 'Invoice in HTML print format',
89 'page callback' => 'invoice_view_print',
90 'page arguments' => array(2),
91 'access arguments' => array('access invoices'),
92 'type' => MENU_CALLBACK,
93 );
94 $items['invoice/pdf/%'] = array(
95 'title' => 'Invoice in PDF format',
96 'page callback' => 'invoice_view_pdf',
97 'page arguments' => array(2),
98 'access arguments' => array('access invoices'),
99 'type' => MENU_CALLBACK,
100 );
101 $items['invoice/set/pay_status/%/%'] = array(
102 'title' => 'Set invoice pay status',
103 'page callback' => 'invoice_set_pay_status',
104 'page arguments' => array(3, 4),
105 'access arguments' => array('administer invoices'),
106 'type' => MENU_CALLBACK,
107 );
108 $items['invoice/search/customer'] = array(
109 'title' => 'Search customer',
110 'page callback' => 'invoice_search_customer',
111 'page arguments' => array(3),
112 'access arguments' => array('administer customers'),
113 'type' => MENU_CALLBACK,
114 );
115 $items['invoice/get/customer_info'] = array(
116 'title' => 'Get customer info',
117 'page callback' => 'invoice_get_customer_info',
118 'access arguments' => array('administer customers'),
119 'type' => MENU_CALLBACK,
120 );
121 $items['invoice/delete/invoice/%'] = array(
122 'page callback' => 'invoice_delete_invoice',
123 'page arguments' => array(2),
124 'access arguments' => array('administer invoices'),
125 'type' => MENU_CALLBACK,
126 );
127 $items['invoice/save/item'] = array(
128 'title' => 'Save item',
129 'page callback' => 'invoice_save_item',
130 'access arguments' => array('administer invoices'),
131 'type' => MENU_CALLBACK,
132 );
133 $items['invoice/edit/item'] = array(
134 'title' => 'Edit item',
135 'page callback' => 'invoice_edit_item',
136 'access arguments' => array('administer invoices'),
137 'type' => MENU_CALLBACK,
138 );
139 $items['invoice/delete/item'] = array(
140 'title' => 'Delete item',
141 'page callback' => 'invoice_delete_item',
142 'access arguments' => array('administer invoices'),
143 'type' => MENU_CALLBACK,
144 );
145 $items['admin/settings/invoice'] = array(
146 'title' => 'Invoice',
147 'page callback' => 'invoice_settings',
148 'access arguments' => array('administer invoices'),
149 'description' => 'Create and manage invoices.',
150 'type' => MENU_NORMAL_ITEM,
151 );
152 $items['invoice/installed_locales'] = array(
153 'title' => 'Installed locales on your system',
154 'page callback' => 'invoice_installed_locales',
155 'access arguments' => array('administer invoices'),
156 'type' => MENU_CALLBACK,
157 );
158 return $items;
159 }
160
161 /**
162 * Implementation of hook_perm()
163 */
164 function invoice_perm() {
165 return array('access invoices', 'administer invoices', 'administer own invoices');
166 }
167
168 /**
169 * Implementation of hook_access()
170 *
171 * @param string $op
172 * @param object $node
173 * @param object $account
174 * @return boolean
175 */
176 function invoice_access($op, $node, $account) {
177 if ($op == 'view') {
178 return user_access('access invoices', $account);
179 }
180 if ($op == 'create') {
181 return user_access('administer invoices', $account);
182 }
183 if ($op == 'update') {
184 if (user_access('administer invoices', $account) || (user_access('administer own invoices', $account) && ($account->uid == $node->uid))) {
185 return TRUE;
186 }
187 }
188 if ($op == 'delete') {
189 if (user_access('administer invoices', $account) || (user_access('administer own invoices', $account) && ($account->uid == $node->uid))) {
190 return TRUE;
191 }
192 }
193 }
194
195 /**
196 * Implementation of hook_theme()
197 *
198 * @param unknown_type $existing
199 * @param unknown_type $type
200 * @param unknown_type $theme
201 * @param unknown_type $path
202 * @return array
203 */
204 function invoice_theme($existing, $type, $theme, $path) {
205 return array(
206 'invoice_body' => array(
207 'arguments' => array('node' => NULL, 'type' => NULL),
208 ),
209 'invoice_markup' => array(
210 'arguments' => array('s_fieldname' => NULL, 'value' => NULL, 's_title' => NULL),
211 ),
212 'invoice_table' => array(
213 'arguments' => array('header' => NULL, 'rows' => NULL, 'attributes' => NULL, 'caption' => NULL),
214 ),
215 );
216 }
217
218 /**
219 * Implementation of hook_load()
220 *
221 * @param object $node
222 * @return mixed
223 */
224 function invoice_load($node) {
225 (object) $o_additions;
226
227 // Get all invoices information
228 $o_invoice = db_fetch_object(db_query("SELECT *,ii.iid as invoice_number,ii.leading_zeros AS leading_zeros, ii.prefix AS prefix,
229 ii.description AS invoice_description, ii.pay_limit AS pay_limit, ic.description AS customer_description, t.name as template
230 FROM {invoice_invoices} ii
231 LEFT JOIN {invoice_customers} ic ON ic.invoice_id=ii.iid
232 LEFT JOIN {invoice_templates} t ON ii.tid=t.tid
233 WHERE nid=%d
234 GROUP BY ii.iid",
235 $node->nid
236 ));
237
238 $a_totals = _invoice_get_invoice_totals($o_invoice->invoice_number);
239
240 $o_invoice->extotal = $a_totals['extotal'];
241 $o_invoice->inctotal = $a_totals['inctotal'];
242 $o_invoice->vattotal = $a_totals['vattotal'];
243
244 // Determine template to use
245 $template = !empty($o_invoice->template) ? $o_invoice->template : variable_get('invoice_default_template', 'default');
246
247 // Set locale so money has the right format for the preferred culture
248 if ($locale = _invoice_get_variable($template, 'locale')) {
249 setlocale(LC_MONETARY, $locale);
250 }
251
252 // Calculate vat totals per different VAT percentage
253 $a_vattotals = array();
254
255 $result = db_query("SELECT vat, (quantity*unitcost)*((vat / 100) +1) * (1 - (1 / ((vat / 100) +1))) as vattotal
256 FROM {invoice_items}
257 WHERE invoice_id=%d",
258 $o_invoice->invoice_number
259 );
260
261 // SUM all vat totals per different VAT percentage
262 while ($row = db_fetch_object($result)) {
263 if (!isset($a_vattotals[$row->vat])) {
264 $a_vattotals[$row->vat] = array(
265 'vattotal' => $row->vattotal,
266 );
267 }
268 else {
269 $a_vattotals[$row->vat]['vattotal'] += $row->vattotal;
270 }
271 }
272
273 // Round every total per different VAT percentage
274 // and add a formatted version
275 foreach ($a_vattotals as $key => &$total) {
276 $total['vattotal'] = _invoice_round($total['vattotal'], 2);
277 $total['formatted_vattotal'] = money_format('%.2n', $total['vattotal']);
278 }
279
280 // Set totals
281 $extotal = _invoice_round($o_invoice->extotal, 2);
282 $inctotal = _invoice_round($o_invoice->inctotal, 2);
283 $vattotal = _invoice_round($o_invoice->vattotal, 2);
284
285 // Add general invoice information to the node object
286 $o_additions->invoice = array(
287 'invoice_number' => $o_invoice->invoice_number,
288 'formatted_invoice_number' => _invoice_get_formatted_invoice_number($o_invoice->invoice_number),
289 'invoice_number_zerofill' => $o_invoice->leading_zeros,
290 'invoice_number_prefix' => $o_invoice->prefix,
291 'description' => $o_invoice->invoice_description,
292 'vat' => $a_vattotals,
293 'pay_status' => $o_invoice->pay_status,
294 'pay_limit' => $o_invoice->pay_limit,
295 'template' => $template,
296 'extotal' => $extotal,
297 'inctotal' => $inctotal,
298 'formatted_vattotal' => money_format('%.2n', $vattotal),
299 'formatted_extotal' => money_format('%.2n', $extotal),
300 'formatted_inctotal' => money_format('%.2n', $inctotal),
301 'formatted_created' => format_date($node->created, 'custom', _invoice_get_variable($template, 'date_format')),
302 );
303
304 // Add customer information to the node object
305 $o_additions->customer = array(
306 'cid' => $o_invoice->cid,
307 'customer_number' => $o_invoice->customer_number,
308 'name' => $name,
309 'company_name' => $o_invoice->company_name,
310 'firstname' => $o_invoice->firstname,
311 'lastname' => $o_invoice->lastname,
312 'fullname' => $o_invoice->lastname . (!empty($o_invoice->firstname) ? ', '. $o_invoice->firstname : ''),
313 'street' => $o_invoice->street,
314 'building_number' => $o_invoice->building_number,
315 'zipcode' => $o_invoice->zipcode,
316 'city' => $o_invoice->city,
317 'country' => $o_invoice->country,
318 'coc_number' => $o_invoice->coc_number,
319 'vat_number' => $o_invoice->vat_number,
320 'description' => $o_invoice->customer_description,
321 );
322
323 // Add invoices items to the node object
324 $o_additions->invoice_items = array();
325
326 $result = db_query("SELECT * FROM {invoice_items} WHERE invoice_id=%d ORDER BY created ASC, weight ASC", $o_invoice->invoice_number);
327 while ($row = db_fetch_object($result)) {
328
329 // Display unitcost VAT in 2 decimals, if there are more decimals round it to 3 decimals
330 $exp = explode('.', $row->unitcost);
331 $exunitcost_roundnum = strlen($exp[1]) > 2 ? 3 : 2;
332 $exunitcost = _invoice_round($row->unitcost, $exunitcost_roundnum);
333
334 // Add invoice item row to the array
335 $o_additions->invoice_items[] = array(
336 'iid' => $row->iid,
337 'description' => $row->description,
338 'quantity' => $row->quantity,
339 'unitcost' => $row->unitcost,
340 'vat' => $row->vat,
341 'formatted_exunitcost' => money_format('%.'. $exunitcost_roundnum .'n', $exunitcost),
342 'formatted_incunitcost' => money_format('%.2n', _invoice_round($row->unitcost * _invoice_vat_percent_to_decimal($row->vat), 2)),
343 'formatted_extotal' => money_format('%.2n', _invoice_round($row->quantity * $row->unitcost, 2)),
344 'formatted_inctotal' => money_format('%.2n', _invoice_round($row->quantity * $row->unitcost * _invoice_vat_percent_to_decimal($row->vat), 2)),
345 );
346 }
347
348 return $o_additions;
349 }
350
351 /**
352 * Implementation of hook_nodeapi()
353 *
354 * @param object $node
355 * @param string $op
356 * @param string $teaser
357 * @param string $page
358 */
359 function invoice_nodeapi(&$node, $op, $teaser, $page) {
360 switch ($op) {
361 case "presave":
362 if ($node->type == 'invoice') {
363 // If true we are creating a new invoice
364 if (intval($node->invoice_number) == 0) {
365 // Get new invoice number
366 if (intval($node->user_defined_invoice_number) > 0) {
367 $node->invoice_number = $node->user_defined_invoice_number;
368 }
369 else {
370 $node->invoice_number = _invoice_get_new_invoice_number();
371 }
372 }
373
374 // Save the title, this must happen when creating AND editing a node because otherwise
375 // the pathauto module will give an error
376 if (intval($node->invoice_number) > 0) {
377 $node->title = t('Invoice') .' #'. _invoice_get_formatted_invoice_number($node->invoice_number, $node);
378 }
379
380 // Get customer number
381 if (!empty($node->company_name)) {
382 $customer_number = db_result(db_query("SELECT customer_number FROM {invoice_customers}
383 WHERE company_name='%s' AND country='%s' LIMIT 1",
384 $node->company_name,
385 $node->country
386 ));
387 }
388 elseif (!empty($node->lastname)) {
389 $customer_number = db_result(db_query("SELECT customer_number FROM {invoice_customers}
390 WHERE lastname='%s' AND zipcode='%s' AND building_number='%s' LIMIT 1",
391 $node->lastname,
392 $node->zipcode,
393 $node->building_number
394 ));
395 }
396
397 // If customer number is still empty, get a new one
398 if (empty($customer_number)) {
399 $customer_number = 1 + db_result(db_query("SELECT MAX(customer_number) FROM {invoice_customers}"));
400 }
401
402 // Add customer number to the node object
403 $node->customer_number = $customer_number;
404 }
405 break;
406 }
407 }
408
409 /**
410 * Invoice settings
411 */
412 function invoice_settings() {
413
414 // A little test to see if the function _invoice_round() rounds well on every server
415 if (_invoice_round(38.675, 2) != 38.68) {
416 drupal_set_message(t('TEST: The answer of _invoice_round(38.675, 2) must be 38.68, but is @answer! This problem is maybe caused by your PHP version.', array('@answer' => _invoice_round(38.675, 2))), 'error');
417 }
418
419 $content = '';
420 $content = drupal_get_form('invoice_settings_form');
421 return $content;
422 }
423
424 /**
425 * Submit function for the settings form
426 */
427 function invoice_settings_form_submit(&$form_state, $form) {
428 $fv =& $form['values'];
429
430 // Invoice specific settings
431 variable_set('invoice_locale', $fv['locale']);
432 variable_set('invoice_date_format', $fv['date_format']);
433 variable_set('invoice_vat', $fv['vat']);
434 variable_set('invoice_pay_limit', $fv['pay_limit']);
435 variable_set('invoice_invoice_number_zerofill', $fv['invoice_number_zerofill']);
436 variable_set('invoice_invoice_number_prefix', $fv['invoice_number_prefix']);
437 variable_set('invoice_default_template', $fv['default_template']);
438
439 // Display columns specific settings
440 variable_set('invoice_display_column_vat', $fv['display_column_vat']);
441 variable_set('invoice_display_column_exunitcost', $fv['display_column_exunitcost']);
442 variable_set('invoice_display_column_incunitcost', $fv['display_column_incunitcost']);
443 variable_set('invoice_display_column_extotal', $fv['display_column_extotal']);
444 variable_set('invoice_display_column_inctotal', $fv['display_column_inctotal']);
445
446 // Supplier specific settings
447 variable_set('invoice_supplier_company_name', $fv['supplier_company_name']);
448 variable_set('invoice_supplier_street', $fv['supplier_street']);
449 variable_set('invoice_supplier_building_number', $fv['supplier_building_number']);
450 variable_set('invoice_supplier_zipcode', $fv['supplier_zipcode']);
451 variable_set('invoice_supplier_city', $fv['supplier_city']);
452 variable_set('invoice_supplier_country', $fv['supplier_country']);
453 variable_set('invoice_supplier_phone', $fv['supplier_phone']);
454 variable_set('invoice_supplier_fax', $fv['supplier_fax']);
455 variable_set('invoice_supplier_email', $fv['supplier_email']);
456 variable_set('invoice_supplier_web', $fv['supplier_web']);
457 variable_set('invoice_supplier_coc_number', $fv['supplier_coc_number']);
458 variable_set('invoice_supplier_vat_number', $fv['supplier_vat_number']);
459
460 $a_templates = _invoice_get_templates();
461
462 foreach ($a_templates as $s_template) {
463 $count = db_result(db_query("SELECT COUNT(*) FROM {invoice_templates} WHERE name='%s'", $s_template));
464 if ($count == 0) {
465 db_query("INSERT INTO {invoice_templates} (name) VALUES ('%s')", $s_template);
466 }
467
468 db_query("UPDATE {invoice_templates} SET locale='%s', date_format='%s', vat=%d, pay_limit=%d,
469 supplier_company_name='%s', supplier_street='%s', supplier_building_number='%s', supplier_zipcode='%s', supplier_city='%s',
470 supplier_country='%s', supplier_phone='%s', supplier_fax='%s', supplier_email='%s', supplier_web='%s', supplier_coc_number='%s',
471 supplier_vat_number='%s', display_column_vat=%d, display_column_exunitcost=%d, display_column_incunitcost=%d,
472 display_column_extotal=%d, display_column_inctotal=%d
473 WHERE name='%s'",
474 $fv[$s_template .'_locale'],
475 $fv[$s_template .'_date_format'],
476 $fv[$s_template .'_vat'],
477 $fv[$s_template .'_pay_limit'],
478 $fv[$s_template .'_supplier_company_name'],
479 $fv[$s_template .'_supplier_street'],
480 $fv[$s_template .'_supplier_building_number'],
481 $fv[$s_template .'_supplier_zipcode'],
482 $fv[$s_template .'_supplier_city'],
483 $fv[$s_template .'_supplier_country'],
484 $fv[$s_template .'_supplier_phone'],
485 $fv[$s_template .'_supplier_fax'],
486 $fv[$s_template .'_supplier_email'],
487 $fv[$s_template .'_supplier_web'],
488 $fv[$s_template .'_supplier_coc_number'],
489 $fv[$s_template .'_supplier_vat_number'],
490 $count > 0 ? $fv[$s_template .'_display_column_vat'] : 0, // set default value if we deal with a new template
491 $count > 0 ? $fv[$s_template .'_display_column_exunitcost'] : 1, // set default value if we deal with a new template
492 $count > 0 ? $fv[$s_template .'_display_column_incunitcost'] : 1, // set default value if we deal with a new template
493 $count > 0 ? $fv[$s_template .'_display_column_extotal'] : 1, // set default value if we deal with a new template
494 $count > 0 ? $fv[$s_template .'_display_column_inctotal'] : 1, // set default value if we deal with a new template
495 $s_template
496 );
497 }
498
499 drupal_set_message(t('Succesfully saved'));
500 }
501
502 /**
503 * Overview of all invoices
504 */
505 function invoice_invoices() {
506
507 //_make_sure_node_promote_flag_is_off();
508
509 $content = '';
510
511 //Our header defenition
512 $header = array(
513 array('data' => t('Invoice #'), 'field' => 'ii.iid'),
514 array('data' => t('Customer'), 'field' => 'c.customer'),
515 array('data' => t('Total (ex)'), 'field' => 'extotal'),
516 array('data' => t('Total (inc)'), 'field' => 'inctotal'),
517 array('data' => t('Created'), 'field' => 'invoices.created'),
518 array('data' => _invoice_get_icon('bullet_black', NULL, array('title' => t('sort by @s', array('@s' => t('Pay status'))))), 'field' => 'ii.pay_status'),
519 t('Actions')
520 );
521
522 if (!isset($_GET['order']) || empty($_GET['order'])) {
523 $_GET['order'] = t("Invoice #");
524 $_GET['sort'] = "desc";
525 }
526
527 $query = "SELECT COUNT(*),ii.iid,ii.nid,ii.pay_limit,ii.pay_status,c.company_name,c.lastname,c.firstname,n.created,it.name as template
528 FROM {invoice_invoices} ii
529 LEFT JOIN {node} n ON ii.nid=n.nid
530 LEFT JOIN {invoice_customers} c ON ii.iid=c.invoice_id
531 LEFT JOIN {invoice_templates} it ON ii.tid=it.tid
532 GROUP BY ii.iid
533 ";
534
535 //$query .= tablesort_sql($header);
536 $query .= "ORDER BY nid DESC";
537
538 $count_query = "SELECT COUNT(*) FROM {invoice_invoices}";
539
540 $rows = array();
541 $result = pager_query($query, 15, 0, $count_query);
542 while ($row = db_fetch_object($result)) {
543
544 // Set locale so money has the right format for the preferred culture
545 if ($locale = _invoice_get_variable($row->template, 'locale')) {
546 setlocale(LC_MONETARY, $locale);
547 }
548
549 // Get locale settings
550 $a_locales = localeconv();
551
552 // Set formatted create date
553 $created = format_date($row->created, 'custom', variable_get('invoice_date_format' , ''));
554
555 // If no default invoice date format is set, use the small drupal date format
556 if (empty($created)) {
557 $created = format_date($row->created, 'small');
558 }
559
560 // Get invoice totals
561 $a_totals = _invoice_get_invoice_totals($row->iid);
562
563 // Set pay status
564 $pay_status = $row->pay_status == 'paid' ? _invoice_get_icon('bullet_green', NULL, array('title' => t('Paid'))) : _invoice_get_icon('bullet_yellow', NULL, array('title' => t('Unpaid')));
565
566 // Check if invoice has pay limit, if yes see if the date exceeded it
567 if ($row->pay_status == 'unpaid' && $row->pay_limit > 0) {
568 if (mktime(0, 0, 0, date('m'), date('d'), date('Y')) > $row->created + (86400 * $row->pay_limit)) {
569 $pay_status = _invoice_get_icon('bullet_red', NULL, array('title' => t('Overtime')));
570 }
571 }
572
573 $rows[] = array(
574 'invoices.iid' => l(_invoice_get_formatted_invoice_number($row->iid), 'node/'. $row->nid),
575 'customer' => !empty($row->company_name) ? _invoice_get_icon('building') .' '. $row->company_name : _invoice_get_icon('user') .' '. $row->lastname . (!empty($row->firstname) ? ', '. $row->firstname : ''),
576 'extotal' => money_format('%.2n', _invoice_round($a_totals['extotal'], 2)),
577 'inctotal' => money_format('%.2n', _invoice_round($a_totals['inctotal'], 2)),
578 'invoices.created' => $created,
579 'ii.status' => $pay_status,
580 'actions' => _invoice_get_icon('edit', 'node/'. $row->nid .'/edit', array('title' => t('Edit')))
581 . ($row->pay_status != 'paid' ? _invoice_get_icon('delete', 'node/'. $row->nid .'/delete', array('title' => t('Delete'))) : '')
582 . ($row->pay_status != 'paid' ? _invoice_get_icon('accept', 'invoice/set/pay_status/'. $row->iid .'/paid/'. _invoice_getvars_array_to_string($_GET), array('title' => t('Set paid'))) : '')
583 . ($row->pay_status == 'paid' ? _invoice_get_icon('coins_delete', 'invoice/set/pay_status/'. $row->iid .'/unpaid/'. _invoice_getvars_array_to_string($_GET), array('title' => t('Set unpaid'))) : ''),
584 );
585 }
586
587 $content .= theme('invoice_table', $header, $rows);
588 $content .= theme('pager');
589
590 return $content;
591 }
592
593 /**
594 * Implementation of node_validate()
595 */
596 function invoice_validate($node, $form) {
597 if ($node->op != t('Delete')) {
598 _invoice_add_css_js();
599
600 // Count invoice items
601 $count = db_result(db_query("SELECT COUNT(*) FROM {invoice_items} WHERE uid=%d AND invoice_id=%d",
602 $GLOBALS['user']->uid,
603 $node->invoice_number
604 ));
605
606 // Display an error if there are no invoice items
607 if ($count == 0) {
608 form_set_error('description', t('You have to fill in at least one invoice item!'));
609 }
610
611 if (empty($node->company_name) && empty($node->lastname)) {
612 form_set_error('company_name', t('Company name and lastname may not both be empty!'));
613 }
614 }
615
616 if ($node->op == t('Save')) {
617 $possible_new_invoice_number = _invoice_get_new_invoice_number(true);
618
619 // If user defined invoice number is higher than the new possible invoice number,
620 // use the defined invoice number as the new invoice number
621 if (intval($node->user_defined_invoice_number) > 0) {
622 if ($node->user_defined_invoice_number <= $possible_new_invoice_number) {
623 form_set_error('user_defined_invoice_number', t('The user defined invoice number is not greater than the latest invoice number "@invoice_number"!', array('@invoice_number' => $possible_new_invoice_number)));
624 }
625 }
626 }
627 }
628
629 /**
630 * Implementation of hook_insert()
631 */
632 function invoice_insert($node) {
633
634 // Get template ID
635 $tid = db_result(db_query("SELECT tid FROM {invoice_templates} WHERE name='%s'", $node->template));
636
637 // Create invoice
638 db_query("INSERT INTO {invoice_invoices} (iid,nid,leading_zeros,prefix,description,tid,pay_limit,uid) VALUES (%d,%d,%d,'%s','%s',%d,'%s',%d)",
639 $node->invoice_number,
640 $node->nid,
641 empty($node->invoice_invoice_number_zerofill) ? variable_get('invoice_invoice_number_zerofill', 0) : $node->invoice_invoice_number_zerofill,
642 empty($node->invoice_invoice_number_prefix) ? variable_get('invoice_invoice_number_prefix', NULL) : $node->invoice_invoice_number_prefix,
643 $node->invoice_description,
644 $tid,
645 $node->pay_limit,
646 $GLOBALS['user']->uid
647 );
648
649 // Create customer
650 db_query("INSERT {invoice_customers} (customer_number,company_name,firstname,lastname,street,building_number,zipcode,city,country,coc_number,vat_number,description,invoice_id)
651 VALUES (%d, '%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s',%d)",
652 $node->customer_number,
653 $node->company_name,
654 $node->firstname,
655 $node->lastname,
656 $node->street,
657 $node->building_number,
658 $node->zipcode,
659 $node->city,
660 $node->country,
661 $node->coc_number,
662 $node->vat_number,
663 $node->customer_description,
664 $node->invoice_number
665 );
666
667 // Add all temporary invoice items to this invoice
668 db_query("UPDATE {invoice_items} SET invoice_id=%d WHERE uid=%d AND invoice_id=0",
669 $node->invoice_number,
670 $GLOBALS['user']->uid
671 );
672
673 db_query("UPDATE {node} SET promote=0 WHERE type='invoice' AND nid=%d", $node->nid);
674
675 unset($_SESSION['invoice_template']);
676 }
677
678 /**
679 * Implementation of hook_update()
680 */
681 function invoice_update($node) {
682
683 // Make sure that this invoice belongs to this user
684 $count = db_result(db_query("SELECT COUNT(*) FROM {invoice_invoices} WHERE iid=%d AND uid=%d",
685 $node->invoice_number,
686 $GLOBALS['user']->uid
687 ));
688
689 if ($count > 0) {
690 // Get template ID
691 $tid = db_result(db_query("SELECT tid FROM {invoice_templates} WHERE name='%s'", $node->template));
692
693 // Update invoice
694 db_query("UPDATE {invoice_invoices} SET leading_zeros=%d, prefix='%s', description='%s', tid='%s', pay_limit=%d WHERE iid=%d AND uid=%d",
695 $node->invoice_invoice_number_zerofill,
696 $node->invoice_invoice_number_prefix,
697 $node->invoice_description,
698 $tid,
699 $node->pay_limit,
700 $node->invoice_number,
701 $GLOBALS['user']->uid
702 );
703
704 // Update customers
705 db_query("UPDATE {invoice_customers} SET customer_number=%d, company_name='%s', firstname='%s', lastname='%s', street='%s', building_number='%s',
706 zipcode='%s', city='%s', country='%s', coc_number='%s', vat_number='%s', description='%s'
707 WHERE invoice_id=%d",
708 $node->customer_number,
709 $node->company_name,
710 $node->firstname,
711 $node->lastname,
712 $node->street,
713 $node->building_number,
714 $node->zipcode,
715 $node->city,
716 $node->country,
717 $node->coc_number,
718 $node->vat_number,
719 $node->description,
720 $node->invoice_number
721 );
722
723 // It's not needed to update invoice items because they are directly updated by every AJAX call
724 }
725 else {
726 drupal_set_message(t('You are not the owner of this invoice!'), 'error');
727 }
728
729 db_query("UPDATE {node} SET promote=0 WHERE type='invoice' AND nid=%d", $node->nid);
730
731 unset($_SESSION['invoice_template']);
732 }
733
734 /**
735 * Implemenation of hook_delete()
736 */
737 function invoice_delete(&$node) {
738 $invoice_number = db_result(db_query("SELECT iid FROM {invoice_invoices} WHERE nid=%d", $node->nid));
739 db_query("DELETE FROM {invoice_invoices} WHERE iid=%d", $invoice_number);
740 db_query("DELETE FROM {invoice_customers} WHERE invoice_id=%d", $invoice_number);
741 db_query("DELETE FROM {invoice_items} WHERE invoice_id=%d", $invoice_number);
742 unset($_SESSION['invoice_template']);
743 }
744
745 /**
746 * Implementation of hook_view()
747 */
748 function invoice_view($node, $teaser = FALSE, $page = FALSE) {
749 $node->content['body'] = array(
750 '#value' => '<div class="view">'. theme('invoice_body', $node, 'view') .'</div>',
751 '#weight' => 0,
752 );
753
754 $links = '<div class="invoice-links">';
755 $links .= _invoice_get_icon('pdf', 'invoice/pdf/'. $node->invoice['invoice_number'], array('width' => '30', 'height' => '30'), 'jpg');
756 $links .= _invoice_get_icon('print', 'invoice/print/'. $node->invoice['invoice_number'], array('width' => '30', 'height' => '30'), 'jpg');
757 $links .= '</div>';
758
759 $node->content['invoice_links'] = array(
760 '#value' => $links,
761 '#weight' => 1,
762 );
763
764 return $node;
765 }
766
767 /**
768 * List of installed locales
769 */
770 function invoice_installed_locales() {
771 $locales = _invoice_get_installed_system_locales();
772
773 if (count($locales) > 0) {
774 $content = '<ul>';
775 foreach ($locales as $locale) {
776 $content .= '<li>'. $locale .'</li>';
777 }
778 $content .= '</ul>';
779 }
780 else {
781 $content = 'No locales found. But maybe the "locale -a" command is not supported on your system.';
782 }
783
784 return $content;
785 }
786
787 /**
788 * Set the status of an invoice to paid
789 */
790 function invoice_set_pay_status($invoice_number, $status) {
791
792 if ($status != 'paid' && $status != 'unpaid') {
793 drupal_set_message(t('Invalid invoice pay status'), 'error');
794 }
795 else {
796 db_query("UPDATE {invoice_invoices} SET pay_status='%s' WHERE iid=%d", $status, $invoice_number);
797 drupal_set_message('Succesfully changed pay status of invoice '. check_plain($invoice_number) .' to "paid"');
798 }
799
800 $exp = explode('?', $_GET['q']);
801 $query_string = '?q=&'. $exp[1];
802
803 $a_query_vars = _invoice_getvars_string_to_array($query_string);
804
805 drupal_goto('invoices', $a_query_vars);
806 }
807
808 /**
809 * Display the invoice in HTML print format
810 */
811 function invoice_view_print($invoice_number) {
812 echo _invoice_get_html($invoice_number);
813 }
814
815 /**
816 * Display the invoice in PDF format
817 */
818 function invoice_view_pdf($invoice_number) {
819 // include the dompdf library
820 _invoice_dompdf_include_lib();
821 $html = _invoice_get_html($invoice_number, 'pdf');
822 $dompdf = new DOMPDF();
823 $dompdf->load_html($html);
824 $dompdf->render();
825 $dompdf->stream("invoice-". $invoice_number .".pdf", array('Attachment' => 1));
826 die();
827 }
828
829 /**
830 * Theme function for displaying the invoice
831 */
832 function theme_invoice_body($node, $type=NULL) {
833
834 $content = '<div class="invoice-wrapper">';
835
836 require_once dirname(__FILE__) .'/templates/'. $node->invoice['template'] .'.inc';
837 drupal_add_css(drupal_get_path('module', 'invoice') .'/templates/'. $node->invoice['template'] .'.css', 'module');
838
839 $content_function = '_invoice_'. $node->invoice['template'] .'_get_template_output';
840 $content .= $content_function($node, $type);
841
842 $content .= '</div>';
843
844 return $content;
845 }
846
847 /**
848 * Add extra markup info to a markup form field
849 *
850 * @param string $s_field_name
851 * @param mixed $value
852 * @param string $s_title
853 */
854 function theme_invoice_markup($s_field_name, $value, $s_title) {
855 $markup_before = '<div id="edit-'. $s_field_name .'-wrapper" class="form-item">
856 <label for="edit-'. $s_field_name .'">'. $s_title .':</label><div>';
857 $markup_after = '</div></div>';
858
859 return $markup_before . $value . $markup_after;
860 }
861
862
863 /**
864 * An override for the table_theme() function
865 *
866 * Added the possibility to disable the sticky header
867 *
868 * @param unknown_type $header
869 * @param unknown_type $rows
870 * @param unknown_type $attributes
871 * @param unknown_type $caption
872 * @return unknown
873 */
874 function theme_invoice_table($header, $rows, $attributes = array(), $caption = NULL) {
875
876 // Add sticky headers, if applicable.
877 if (count($header) && (bool) $attributes['disable_sticky_header'] != TRUE) {
878 drupal_add_js('misc/tableheader.js');
879 // Add 'sticky-enabled' class to the table to identify it for JS.
880 // This is needed to target tables constructed by this function.
881 $attributes['class'] = empty($attributes['class']) ? 'sticky-enabled' : ($attributes['class'] .' sticky-enabled');
882 }
883 else {
884 unset($attributes['disable_sticky_header']);
885 }
886
887 $output = '<table'. drupal_attributes($attributes) .">\n";
888
889 if (isset($caption)) {
890 $output .= '<caption>'. $caption ."</caption>\n";
891 }
892
893 // Format the table header:
894 if (count($header)) {
895 $ts = tablesort_init($header);
896 // HTML requires that the thead tag has tr tags in it follwed by tbody
897 // tags. Using ternary operator to check and see if we have any rows.
898 $output .= (count($rows) ? ' <thead><tr>' : ' <tr>');
899 foreach ($header as $cell) {
900 $cell = tablesort_header($cell, $header, $ts);
901 $output .= _theme_table_cell($cell, TRUE);
902 }
903 // Using ternary operator to close the tags based on whether or not there are rows
904 $output .= (count($rows) ? " </tr></thead>\n" : "</tr>\n");
905 }
906 else {
907 $ts = array();
908 }
909
910 // Format the table rows:
911 if (count($rows)) {
912 $output .= "<tbody>\n";
913 $flip = array('even' => 'odd', 'odd' => 'even');
914 $class = 'even';
915 foreach ($rows as $number => $row) {
916 $attributes = array();
917
918 // Check if we're dealing with a simple or complex row
919 if (isset($row['data'])) {
920 foreach ($row as $key => $value) {
921 if ($key == 'data') {
922 $cells = $value;
923 }
924 else {
925 $attributes[$key] = $value;
926 }
927 }
928 }
929 else {
930 $cells = $row;
931 }
932 if (count($cells)) {
933 // Add odd/even class
934 $class = $flip[$class];
935 if (isset($attributes['class'])) {
936 $attributes['class'] .= ' '. $class;
937 }
938 else {
939 $attributes['class'] = $class;
940 }
941
942 // Build row
943 $output .= ' <tr'. drupal_attributes($attributes) .'>';
944 $i = 0;
945 foreach ($cells as $cell) {
946 $cell = tablesort_cell($cell, $header, $ts, $i++);
947 $output .= _theme_table_cell($cell);
948 }
949 $output .= " </tr>\n";
950 }
951 }
952 $output .= "</tbody>\n";
953 }
954
955 $output .= "</table>\n";
956 return $output;
957 }

  ViewVC Help
Powered by ViewVC 1.1.2