Improved layout in the new tax info display. Also old ones.
[fa-stable.git] / sales / customer_invoice.php
1 <?php
2 /**********************************************************************
3     Copyright (C) FrontAccounting, LLC.
4         Released under the terms of the GNU General Public License, GPL, 
5         as published by the Free Software Foundation, either version 3 
6         of the License, or (at your option) any later version.
7     This program is distributed in the hope that it will be useful,
8     but WITHOUT ANY WARRANTY; without even the implied warranty of
9     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
10     See the License here <http://www.gnu.org/licenses/gpl-3.0.html>.
11 ***********************************************************************/
12 //---------------------------------------------------------------------------
13 //
14 //      Entry/Modify Sales Invoice against single delivery
15 //      Entry/Modify Batch Sales Invoice against batch of deliveries
16 //
17 $page_security = 'SA_SALESINVOICE';
18 $path_to_root = "..";
19 include_once($path_to_root . "/sales/includes/cart_class.inc");
20 include_once($path_to_root . "/includes/session.inc");
21 include_once($path_to_root . "/includes/data_checks.inc");
22 include_once($path_to_root . "/includes/manufacturing.inc");
23 include_once($path_to_root . "/sales/includes/sales_db.inc");
24 include_once($path_to_root . "/sales/includes/sales_ui.inc");
25 include_once($path_to_root . "/reporting/includes/reporting.inc");
26 include_once($path_to_root . "/taxes/tax_calc.inc");
27
28 $js = "";
29 if ($use_popup_windows) {
30         $js .= get_js_open_window(900, 500);
31 }
32 if ($use_date_picker) {
33         $js .= get_js_date_picker();
34 }
35
36 if (isset($_GET['ModifyInvoice'])) {
37         $_SESSION['page_title'] = sprintf(_("Modifying Sales Invoice # %d.") ,$_GET['ModifyInvoice']);
38         $help_context = "Modifying Sales Invoice";
39 } elseif (isset($_GET['DeliveryNumber'])) {
40         $_SESSION['page_title'] = _($help_context = "Issue an Invoice for Delivery Note");
41 } elseif (isset($_GET['BatchInvoice'])) {
42         $_SESSION['page_title'] = _($help_context = "Issue Batch Invoice for Delivery Notes");
43 }
44
45 page($_SESSION['page_title'], false, false, "", $js);
46
47 //-----------------------------------------------------------------------------
48 check_edit_conflicts();
49
50 if (isset($_GET['AddedID'])) {
51
52         $invoice_no = $_GET['AddedID'];
53         $trans_type = ST_SALESINVOICE;
54
55         display_notification(_("Selected deliveries has been processed"), true);
56
57         display_note(get_customer_trans_view_str($trans_type, $invoice_no, _("&View This Invoice")), 0, 1);
58
59         display_note(print_document_link($invoice_no, _("&Print This Invoice"), true, ST_SALESINVOICE));
60         display_note(print_document_link($invoice_no, _("&Email This Invoice"), true, ST_SALESINVOICE, false, "", "", 1),1);
61
62         display_note(get_gl_view_str($trans_type, $invoice_no, _("View the GL &Journal Entries for this Invoice")),1);
63
64         hyperlink_params("$path_to_root/sales/inquiry/sales_deliveries_view.php", _("Select Another &Delivery For Invoicing"), "OutstandingOnly=1");
65
66         display_footer_exit();
67
68 } elseif (isset($_GET['UpdatedID']))  {
69
70         $invoice_no = $_GET['UpdatedID'];
71
72         display_notification_centered(sprintf(_('Sales Invoice # %d has been updated.'),$invoice_no));
73
74         display_note(get_trans_view_str(ST_SALESINVOICE, $invoice_no, _("&View This Invoice")));
75         echo '<br>';
76         display_note(print_document_link($invoice_no, _("&Print This Invoice"), true, ST_SALESINVOICE));
77
78         hyperlink_no_params($path_to_root . "/sales/inquiry/customer_inquiry.php", _("Select A Different &Invoice to Modify"));
79
80         display_footer_exit();
81
82 } elseif (isset($_GET['RemoveDN'])) {
83
84         for($line_no = 0; $line_no < count($_SESSION['Items']->line_items); $line_no++) {
85                 $line = &$_SESSION['Items']->line_items[$line_no];
86                 if ($line->src_no == $_GET['RemoveDN']) {
87                         $line->quantity = $line->qty_done;
88                         $line->qty_dispatched=0;
89                 }
90         }
91         unset($line);
92
93     // Remove also src_doc delivery note
94     $sources = &$_SESSION['Items']->src_docs;
95     unset($sources[$_GET['RemoveDN']]);
96 }
97
98 //-----------------------------------------------------------------------------
99
100 if ( (isset($_GET['DeliveryNumber']) && ($_GET['DeliveryNumber'] > 0) )
101 || isset($_GET['BatchInvoice'])) {
102
103         processing_start();
104
105         if (isset($_GET['BatchInvoice'])) {
106                 $src = $_SESSION['DeliveryBatch'];
107                 unset($_SESSION['DeliveryBatch']);
108         } else {
109                 $src = array($_GET['DeliveryNumber']);
110         }
111         /*read in all the selected deliveries into the Items cart  */
112         $dn = new Cart(ST_CUSTDELIVERY, $src, true);
113
114         if ($dn->count_items() == 0) {
115                 hyperlink_params($path_to_root . "/sales/inquiry/sales_deliveries_view.php",
116                         _("Select a different delivery to invoice"), "OutstandingOnly=1");
117                 die ("<br><b>" . _("There are no delivered items with a quantity left to invoice. There is nothing left to invoice.") . "</b>");
118         }
119
120         $dn->trans_type = ST_SALESINVOICE;
121         $dn->src_docs = $dn->trans_no;
122         $dn->trans_no = 0;
123         $dn->reference = $Refs->get_next(ST_SALESINVOICE);
124         $dn->due_date = get_invoice_duedate($dn->customer_id, $dn->document_date);
125
126         $_SESSION['Items'] = $dn;
127         copy_from_cart();
128
129 } elseif (isset($_GET['ModifyInvoice']) && $_GET['ModifyInvoice'] > 0) {
130
131         if ( get_parent_trans(ST_SALESINVOICE, $_GET['ModifyInvoice']) == 0) { // 1.xx compatibility hack
132                 echo"<center><br><b>" . _("There are no delivery notes for this invoice.<br>
133                 Most likely this invoice was created in Front Accounting version prior to 2.0
134                 and therefore can not be modified.") . "</b></center>";
135                 display_footer_exit();
136         }
137         processing_start();
138         $_SESSION['Items'] = new Cart(ST_SALESINVOICE, $_GET['ModifyInvoice']);
139
140         if ($_SESSION['Items']->count_items() == 0) {
141                 echo"<center><br><b>" . _("All quantities on this invoice has been credited. There is nothing to modify on this invoice") . "</b></center>";
142                 display_footer_exit();
143         }
144         copy_from_cart();
145 } elseif (!processing_active()) {
146         /* This page can only be called with a delivery for invoicing or invoice no for edit */
147         display_error(_("This page can only be opened after delivery selection. Please select delivery to invoicing first."));
148
149         hyperlink_no_params("$path_to_root/sales/inquiry/sales_deliveries_view.php", _("Select Delivery to Invoice"));
150
151         end_page();
152         exit;
153 } elseif (!check_quantities()) {
154         display_error(_("Selected quantity cannot be less than quantity credited nor more than quantity not invoiced yet."));
155 }
156 if (isset($_POST['Update'])) {
157         $Ajax->activate('Items');
158 }
159 if (isset($_POST['_InvoiceDate_changed'])) {
160         $_POST['due_date'] = get_invoice_duedate($_SESSION['Items']->customer_id, 
161                 $_POST['InvoiceDate']);
162         $Ajax->activate('due_date');
163 }
164
165 //-----------------------------------------------------------------------------
166 function check_quantities()
167 {
168         $ok =1;
169         foreach ($_SESSION['Items']->line_items as $line_no=>$itm) {
170                 if (isset($_POST['Line'.$line_no])) {
171                         if($_SESSION['Items']->trans_no) {
172                                 $min = $itm->qty_done;
173                                 $max = $itm->quantity;
174                         } else {
175                                 $min = 0;
176                                 $max = $itm->quantity - $itm->qty_done;
177                         }
178                         if (check_num('Line'.$line_no, $min, $max)) {
179                                 $_SESSION['Items']->line_items[$line_no]->qty_dispatched =
180                                     input_num('Line'.$line_no);
181                         }
182                         else {
183                                 $ok = 0;
184                         }
185                                 
186                 }
187
188                 if (isset($_POST['Line'.$line_no.'Desc'])) {
189                         $line_desc = $_POST['Line'.$line_no.'Desc'];
190                         if (strlen($line_desc) > 0) {
191                                 $_SESSION['Items']->line_items[$line_no]->item_description = $line_desc;
192                         }
193                 }
194         }
195  return $ok;
196 }
197
198 function set_delivery_shipping_sum($delivery_notes) 
199 {
200     
201     $shipping = 0;
202     
203     foreach($delivery_notes as $delivery_num) 
204     {
205         $myrow = get_customer_trans($delivery_num, 13);
206         //$branch = get_branch($myrow["branch_code"]);
207         //$sales_order = get_sales_order_header($myrow["order_"]);
208         
209         //$shipping += $sales_order['freight_cost'];
210         $shipping += $myrow['ov_freight'];
211     }
212     $_POST['ChargeFreightCost'] = price_format($shipping);
213 }
214
215
216 function copy_to_cart()
217 {
218         $cart = &$_SESSION['Items'];
219         $cart->ship_via = $_POST['ship_via'];
220         $cart->freight_cost = input_num('ChargeFreightCost');
221         $cart->document_date =  $_POST['InvoiceDate'];
222         $cart->due_date =  $_POST['due_date'];
223         $cart->Comments = $_POST['Comments'];
224         if ($_SESSION['Items']->trans_no == 0)
225                 $cart->reference = $_POST['ref'];
226 }
227 //-----------------------------------------------------------------------------
228
229 function copy_from_cart()
230 {
231         $cart = &$_SESSION['Items'];
232         $_POST['ship_via'] = $cart->ship_via;
233         $_POST['ChargeFreightCost'] = price_format($cart->freight_cost);
234         $_POST['InvoiceDate']= $cart->document_date;
235         $_POST['due_date'] = $cart->due_date;
236         $_POST['Comments']= $cart->Comments;
237         $_POST['cart_id'] = $cart->cart_id;
238         $_POST['ref'] = $cart->reference;
239 }
240
241 //-----------------------------------------------------------------------------
242
243 function check_data()
244 {
245         global $Refs;
246
247         if (!isset($_POST['InvoiceDate']) || !is_date($_POST['InvoiceDate'])) {
248                 display_error(_("The entered invoice date is invalid."));
249                 set_focus('InvoiceDate');
250                 return false;
251         }
252
253         if (!is_date_in_fiscalyear($_POST['InvoiceDate'])) {
254                 display_error(_("The entered invoice date is not in fiscal year."));
255                 set_focus('InvoiceDate');
256                 return false;
257         }
258
259         if (!isset($_POST['due_date']) || !is_date($_POST['due_date'])) {
260                 display_error(_("The entered invoice due date is invalid."));
261                 set_focus('due_date');
262                 return false;
263         }
264
265         if ($_SESSION['Items']->trans_no == 0) {
266                 if (!$Refs->is_valid($_POST['ref'])) {
267                         display_error(_("You must enter a reference."));
268                         set_focus('ref');
269                         return false;
270                 }
271
272                 if (!is_new_reference($_POST['ref'], 10)) {
273                         display_error(_("The entered reference is already in use."));
274                         set_focus('ref');
275                         return false;
276                 }
277         }
278
279         if ($_POST['ChargeFreightCost'] == "") {
280                 $_POST['ChargeFreightCost'] = price_format(0);
281         }
282
283         if (!check_num('ChargeFreightCost', 0)) {
284                 display_error(_("The entered shipping value is not numeric."));
285                 set_focus('ChargeFreightCost');
286                 return false;
287         }
288
289         if ($_SESSION['Items']->has_items_dispatch() == 0 && input_num('ChargeFreightCost') == 0) {
290                 display_error(_("There are no item quantities on this invoice."));
291                 return false;
292         }
293
294         if (!check_quantities()) {
295                 display_error(_("Selected quantity cannot be less than quantity credited nor more than quantity not invoiced yet."));
296                 return false;
297         }
298
299         return true;
300 }
301
302 //-----------------------------------------------------------------------------
303 if (isset($_POST['process_invoice']) && check_data()) {
304
305         $newinvoice=  $_SESSION['Items']->trans_no == 0;
306         copy_to_cart();
307         if ($newinvoice) new_doc_date($_SESSION['Items']->document_date);
308         $invoice_no = $_SESSION['Items']->write();
309
310         processing_end();
311         if ($newinvoice) {
312                 meta_forward($_SERVER['PHP_SELF'], "AddedID=$invoice_no");
313         } else {
314                 meta_forward($_SERVER['PHP_SELF'], "UpdatedID=$invoice_no");
315         }
316 }
317
318 // find delivery spans for batch invoice display
319 $dspans = array();
320 $lastdn = ''; $spanlen=1;
321
322 for ($line_no = 0; $line_no < count($_SESSION['Items']->line_items); $line_no++) {
323         $line = $_SESSION['Items']->line_items[$line_no];
324         if ($line->quantity == $line->qty_done) {
325                 continue;
326         }
327         if ($line->src_no == $lastdn) {
328                 $spanlen++;
329         } else {
330                 if ($lastdn != '') {
331                         $dspans[] = $spanlen;
332                         $spanlen = 1;
333                 }
334         }
335         $lastdn = $line->src_no;
336 }
337 $dspans[] = $spanlen;
338
339 //-----------------------------------------------------------------------------
340
341 $is_batch_invoice = count($_SESSION['Items']->src_docs) > 1;
342
343 $is_edition = $_SESSION['Items']->trans_type == ST_SALESINVOICE && $_SESSION['Items']->trans_no != 0;
344 start_form();
345 hidden('cart_id');
346
347 start_table("$table_style2 width=80%", 5);
348
349 start_row();
350 label_cells(_("Customer"), $_SESSION['Items']->customer_name, "class='tableheader2'");
351 label_cells(_("Branch"), get_branch_name($_SESSION['Items']->Branch), "class='tableheader2'");
352 label_cells(_("Currency"), $_SESSION['Items']->customer_currency, "class='tableheader2'");
353 end_row();
354 start_row();
355
356 if ($_SESSION['Items']->trans_no == 0) {
357         ref_cells(_("Reference"), 'ref', '', null, "class='tableheader2'");
358 } else {
359         label_cells(_("Reference"), $_SESSION['Items']->reference, "class='tableheader2'");
360 }
361
362 label_cells(_("Delivery Notes:"),
363 get_customer_trans_view_str(ST_CUSTDELIVERY, array_keys($_SESSION['Items']->src_docs)), "class='tableheader2'");
364
365 label_cells(_("Sales Type"), $_SESSION['Items']->sales_type_name, "class='tableheader2'");
366
367 end_row();
368 start_row();
369
370 if (!isset($_POST['ship_via'])) {
371         $_POST['ship_via'] = $_SESSION['Items']->ship_via;
372 }
373 label_cell(_("Shipping Company"), "class='tableheader2'");
374 shippers_list_cells(null, 'ship_via', $_POST['ship_via']);
375
376 if (!isset($_POST['InvoiceDate']) || !is_date($_POST['InvoiceDate'])) {
377         $_POST['InvoiceDate'] = new_doc_date();
378         if (!is_date_in_fiscalyear($_POST['InvoiceDate'])) {
379                 $_POST['InvoiceDate'] = end_fiscalyear();
380         }
381 }
382
383 date_cells(_("Date"), 'InvoiceDate', '', $_SESSION['Items']->trans_no == 0, 
384         0, 0, 0, "class='tableheader2'", true);
385
386 if (!isset($_POST['due_date']) || !is_date($_POST['due_date'])) {
387         $_POST['due_date'] = get_invoice_duedate($_SESSION['Items']->customer_id, $_POST['InvoiceDate']);
388 }
389
390 date_cells(_("Due Date"), 'due_date', '', null, 0, 0, 0, "class='tableheader2'");
391
392 end_row();
393 end_table();
394
395 $row = get_customer_to_order($_SESSION['Items']->customer_id);
396 if ($row['dissallow_invoices'] == 1)
397 {
398         display_error(_("The selected customer account is currently on hold. Please contact the credit control personnel to discuss."));
399         end_form();
400         end_page();
401         exit();
402 }       
403
404 display_heading(_("Invoice Items"));
405
406 div_start('Items');
407 start_table("$table_style width=80%");
408 $th = array(_("Item Code"), _("Item Description"), _("Delivered"), _("Units"), _("Invoiced"),
409         _("This Invoice"), _("Price"), _("Tax Type"), _("Discount"), _("Total"));
410
411 if ($is_batch_invoice) {
412     $th[] = _("DN");
413     $th[] = "";
414 }
415
416 if ($is_edition) {
417     $th[4] = _("Credited");
418 }
419
420 table_header($th);
421 $k = 0;
422 $has_marked = false;
423 $show_qoh = true;
424
425 $dn_line_cnt = 0;
426
427 foreach ($_SESSION['Items']->line_items as $line=>$ln_itm) {
428         if ($ln_itm->quantity == $ln_itm->qty_done) {
429                 continue; // this line was fully invoiced
430         }
431         alt_table_row_color($k);
432         view_stock_status_cell($ln_itm->stock_id);
433
434         text_cells(null, 'Line'.$line.'Desc', $ln_itm->item_description, 30, 50);
435         $dec = get_qty_dec($ln_itm->stock_id);
436         qty_cell($ln_itm->quantity, false, $dec);
437         label_cell($ln_itm->units);
438         qty_cell($ln_itm->qty_done, false, $dec);
439
440         if ($is_batch_invoice) {
441                 // for batch invoices we can only remove whole deliveries
442                 echo '<td nowrap align=right>';
443                 hidden('Line' . $line, $ln_itm->qty_dispatched );
444                 echo number_format2($ln_itm->qty_dispatched, $dec).'</td>';
445         } else {
446                 small_qty_cells(null, 'Line'.$line, qty_format($ln_itm->qty_dispatched, $ln_itm->stock_id, $dec), null, null, $dec);
447         }
448         $display_discount_percent = percent_format($ln_itm->discount_percent*100) . " %";
449
450         $line_total = ($ln_itm->qty_dispatched * $ln_itm->price * (1 - $ln_itm->discount_percent));
451
452         amount_cell($ln_itm->price);
453         label_cell($ln_itm->tax_type_name);
454         label_cell($display_discount_percent, "nowrap align=right");
455         amount_cell($line_total);
456
457         if ($is_batch_invoice) {
458                 if ($dn_line_cnt == 0) {
459                         $dn_line_cnt = $dspans[0];
460                         $dspans = array_slice($dspans, 1);
461                         label_cell($ln_itm->src_no, "rowspan=$dn_line_cnt class=oddrow");
462                         label_cell("<a href='" . $_SERVER['PHP_SELF'] . "?RemoveDN=".
463                                 $ln_itm->src_no."'>" . _("Remove") . "</a>", "rowspan=$dn_line_cnt class=oddrow");
464                 }
465                 $dn_line_cnt--;
466         }
467         end_row();
468 }
469
470 /*Don't re-calculate freight if some of the order has already been delivered -
471 depending on the business logic required this condition may not be required.
472 It seems unfair to charge the customer twice for freight if the order
473 was not fully delivered the first time ?? */
474
475 if (!isset($_POST['ChargeFreightCost']) || $_POST['ChargeFreightCost'] == "") {
476         if ($_SESSION['Items']->any_already_delivered() == 1) {
477                 $_POST['ChargeFreightCost'] = price_format(0);
478         } else {
479                 $_POST['ChargeFreightCost'] = price_format($_SESSION['Items']->freight_cost);
480         }
481
482         if (!check_num('ChargeFreightCost')) {
483                 $_POST['ChargeFreightCost'] = price_format(0);
484         }
485 }
486
487 $accumulate_shipping = get_company_pref('accumulate_shipping');
488 if ($is_batch_invoice && $accumulate_shipping)
489         set_delivery_shipping_sum(array_keys($_SESSION['Items']->src_docs));
490
491 $colspan = 9;
492 start_row();
493 label_cell(_("Shipping Cost"), "colspan=$colspan align=right");
494 small_amount_cells(null, 'ChargeFreightCost', null);
495 if ($is_batch_invoice) {
496 label_cell('', 'colspan=2');
497 }
498
499 end_row();
500 $inv_items_total = $_SESSION['Items']->get_items_total_dispatch();
501
502 $display_sub_total = price_format($inv_items_total + input_num('ChargeFreightCost'));
503
504 label_row(_("Sub-total"), $display_sub_total, "colspan=$colspan align=right","align=right", $is_batch_invoice ? 2 : 0);
505
506 $taxes = $_SESSION['Items']->get_taxes(input_num('ChargeFreightCost'));
507 $tax_total = display_edit_tax_items($taxes, $colspan, $_SESSION['Items']->tax_included, $is_batch_invoice ? 2:0);
508
509 $display_total = price_format(($inv_items_total + input_num('ChargeFreightCost') + $tax_total));
510
511 label_row(_("Invoice Total"), $display_total, "colspan=$colspan align=right","align=right", $is_batch_invoice ? 2 : 0);
512
513 end_table(1);
514 div_end();
515
516 start_table($table_style2);
517 textarea_row(_("Memo"), 'Comments', null, 50, 4);
518
519 end_table(1);
520
521 submit_center_first('Update', _("Update"),
522   _('Refresh document page'), true);
523 submit_center_last('process_invoice', _("Process Invoice"),
524   _('Check entered data and save document'), 'default');
525
526 end_form();
527
528 end_page();
529
530 ?>