Fixed final page message text.
[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, "printlink", "", 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 Another &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->payment, $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']->payment, 
161                 $_POST['InvoiceDate']);
162         $Ajax->activate('due_date');
163 }
164 if (list_updated('payment')) {
165         $_SESSION['Items']->payment = get_post('payment');
166         $_POST['due_date'] = get_invoice_duedate($_SESSION['Items']->payment, 
167                 $_POST['InvoiceDate']);
168         $Ajax->activate('due_date');
169 }
170
171 //-----------------------------------------------------------------------------
172 function check_quantities()
173 {
174         $ok =1;
175         foreach ($_SESSION['Items']->line_items as $line_no=>$itm) {
176                 if (isset($_POST['Line'.$line_no])) {
177                         if($_SESSION['Items']->trans_no) {
178                                 $min = $itm->qty_done;
179                                 $max = $itm->quantity;
180                         } else {
181                                 $min = 0;
182                                 $max = $itm->quantity - $itm->qty_done;
183                         }
184                         if (check_num('Line'.$line_no, $min, $max)) {
185                                 $_SESSION['Items']->line_items[$line_no]->qty_dispatched =
186                                     input_num('Line'.$line_no);
187                         }
188                         else {
189                                 $ok = 0;
190                         }
191                                 
192                 }
193
194                 if (isset($_POST['Line'.$line_no.'Desc'])) {
195                         $line_desc = $_POST['Line'.$line_no.'Desc'];
196                         if (strlen($line_desc) > 0) {
197                                 $_SESSION['Items']->line_items[$line_no]->item_description = $line_desc;
198                         }
199                 }
200         }
201  return $ok;
202 }
203
204 function set_delivery_shipping_sum($delivery_notes) 
205 {
206     
207     $shipping = 0;
208     
209     foreach($delivery_notes as $delivery_num) 
210     {
211         $myrow = get_customer_trans($delivery_num, 13);
212         //$branch = get_branch($myrow["branch_code"]);
213         //$sales_order = get_sales_order_header($myrow["order_"]);
214         
215         //$shipping += $sales_order['freight_cost'];
216         $shipping += $myrow['ov_freight'];
217     }
218     $_POST['ChargeFreightCost'] = price_format($shipping);
219 }
220
221
222 function copy_to_cart()
223 {
224         $cart = &$_SESSION['Items'];
225         $cart->ship_via = $_POST['ship_via'];
226         $cart->freight_cost = input_num('ChargeFreightCost');
227         $cart->document_date =  $_POST['InvoiceDate'];
228         $cart->due_date =  $_POST['due_date'];
229         $cart->payment = $_POST['payment'];
230         $cart->payment_terms = get_payment_terms($_POST['payment']);
231         $cart->Comments = $_POST['Comments'];
232         if ($_SESSION['Items']->trans_no == 0)
233                 $cart->reference = $_POST['ref'];
234
235 }
236 //-----------------------------------------------------------------------------
237
238 function copy_from_cart()
239 {
240         $cart = &$_SESSION['Items'];
241         $_POST['ship_via'] = $cart->ship_via;
242         $_POST['ChargeFreightCost'] = price_format($cart->freight_cost);
243         $_POST['InvoiceDate']= $cart->document_date;
244         $_POST['due_date'] = $cart->due_date;
245         $_POST['Comments']= $cart->Comments;
246         $_POST['cart_id'] = $cart->cart_id;
247         $_POST['ref'] = $cart->reference;
248         $_POST['payment'] = $cart->payment;
249 }
250
251 //-----------------------------------------------------------------------------
252
253 function check_data()
254 {
255         global $Refs;
256
257         if (!isset($_POST['InvoiceDate']) || !is_date($_POST['InvoiceDate'])) {
258                 display_error(_("The entered invoice date is invalid."));
259                 set_focus('InvoiceDate');
260                 return false;
261         }
262
263         if (!is_date_in_fiscalyear($_POST['InvoiceDate'])) {
264                 display_error(_("The entered invoice date is not in fiscal year."));
265                 set_focus('InvoiceDate');
266                 return false;
267         }
268
269         if (!isset($_POST['due_date']) || !is_date($_POST['due_date'])) {
270                 display_error(_("The entered invoice due date is invalid."));
271                 set_focus('due_date');
272                 return false;
273         }
274
275         if ($_SESSION['Items']->trans_no == 0) {
276                 if (!$Refs->is_valid($_POST['ref'])) {
277                         display_error(_("You must enter a reference."));
278                         set_focus('ref');
279                         return false;
280                 }
281
282                 if (!is_new_reference($_POST['ref'], 10)) {
283                         display_error(_("The entered reference is already in use."));
284                         set_focus('ref');
285                         return false;
286                 }
287         }
288
289         if ($_POST['ChargeFreightCost'] == "") {
290                 $_POST['ChargeFreightCost'] = price_format(0);
291         }
292
293         if (!check_num('ChargeFreightCost', 0)) {
294                 display_error(_("The entered shipping value is not numeric."));
295                 set_focus('ChargeFreightCost');
296                 return false;
297         }
298
299         if ($_SESSION['Items']->has_items_dispatch() == 0 && input_num('ChargeFreightCost') == 0) {
300                 display_error(_("There are no item quantities on this invoice."));
301                 return false;
302         }
303
304         if (!check_quantities()) {
305                 display_error(_("Selected quantity cannot be less than quantity credited nor more than quantity not invoiced yet."));
306                 return false;
307         }
308
309         return true;
310 }
311
312 //-----------------------------------------------------------------------------
313 if (isset($_POST['process_invoice']) && check_data()) {
314
315         $newinvoice=  $_SESSION['Items']->trans_no == 0;
316         copy_to_cart();
317         if ($newinvoice) new_doc_date($_SESSION['Items']->document_date);
318
319         $invoice_no = $_SESSION['Items']->write();
320         processing_end();
321
322         if ($newinvoice) {
323                 meta_forward($_SERVER['PHP_SELF'], "AddedID=$invoice_no");
324         } else {
325                 meta_forward($_SERVER['PHP_SELF'], "UpdatedID=$invoice_no");
326         }
327 }
328
329 // find delivery spans for batch invoice display
330 $dspans = array();
331 $lastdn = ''; $spanlen=1;
332
333 for ($line_no = 0; $line_no < count($_SESSION['Items']->line_items); $line_no++) {
334         $line = $_SESSION['Items']->line_items[$line_no];
335         if ($line->quantity == $line->qty_done) {
336                 continue;
337         }
338         if ($line->src_no == $lastdn) {
339                 $spanlen++;
340         } else {
341                 if ($lastdn != '') {
342                         $dspans[] = $spanlen;
343                         $spanlen = 1;
344                 }
345         }
346         $lastdn = $line->src_no;
347 }
348 $dspans[] = $spanlen;
349
350 //-----------------------------------------------------------------------------
351
352 $is_batch_invoice = count($_SESSION['Items']->src_docs) > 1;
353
354 $is_edition = $_SESSION['Items']->trans_type == ST_SALESINVOICE && $_SESSION['Items']->trans_no != 0;
355 start_form();
356 hidden('cart_id');
357
358 start_table(TABLESTYLE2, "width=80%", 5);
359
360 start_row();
361 label_cells(_("Customer"), $_SESSION['Items']->customer_name, "class='tableheader2'");
362 label_cells(_("Branch"), get_branch_name($_SESSION['Items']->Branch), "class='tableheader2'");
363 label_cells(_("Currency"), $_SESSION['Items']->customer_currency, "class='tableheader2'");
364 end_row();
365 start_row();
366
367 if ($_SESSION['Items']->trans_no == 0) {
368         ref_cells(_("Reference"), 'ref', '', null, "class='tableheader2'");
369 } else {
370         label_cells(_("Reference"), $_SESSION['Items']->reference, "class='tableheader2'");
371 }
372
373 //label_cells(_("Delivery Notes:"),
374 //get_customer_trans_view_str(ST_CUSTDELIVERY, array_keys($_SESSION['Items']->src_docs)), "class='tableheader2'");
375
376 label_cells(_("Sales Type"), $_SESSION['Items']->sales_type_name, "class='tableheader2'");
377
378 if ($_SESSION['Items']->pos != -1) // editable payment type
379         label_cells(_("Payment terms:"), sale_payment_list('payment'), "class='tableheader2'");
380 else
381         label_cells(_('Payment:'), $_SESSION['Items']->payment_terms['terms'], "class='tableheader2'");
382
383 end_row();
384 start_row();
385
386 if (!isset($_POST['ship_via'])) {
387         $_POST['ship_via'] = $_SESSION['Items']->ship_via;
388 }
389 label_cell(_("Shipping Company"), "class='tableheader2'");
390 shippers_list_cells(null, 'ship_via', $_POST['ship_via']);
391
392 if (!isset($_POST['InvoiceDate']) || !is_date($_POST['InvoiceDate'])) {
393         $_POST['InvoiceDate'] = new_doc_date();
394         if (!is_date_in_fiscalyear($_POST['InvoiceDate'])) {
395                 $_POST['InvoiceDate'] = end_fiscalyear();
396         }
397 }
398
399 date_cells(_("Date"), 'InvoiceDate', '', $_SESSION['Items']->trans_no == 0, 
400         0, 0, 0, "class='tableheader2'", true);
401
402 if (!isset($_POST['due_date']) || !is_date($_POST['due_date'])) {
403         $_POST['due_date'] = get_invoice_duedate($_SESSION['Items']->payment, $_POST['InvoiceDate']);
404 }
405
406 date_cells(_("Due Date"), 'due_date', '', null, 0, 0, 0, "class='tableheader2'");
407
408 end_row();
409 end_table();
410
411 $row = get_customer_to_order($_SESSION['Items']->customer_id);
412 if ($row['dissallow_invoices'] == 1)
413 {
414         display_error(_("The selected customer account is currently on hold. Please contact the credit control personnel to discuss."));
415         end_form();
416         end_page();
417         exit();
418 }       
419
420 display_heading(_("Invoice Items"));
421
422 div_start('Items');
423 start_table(TABLESTYLE, "width=80%");
424 $th = array(_("Item Code"), _("Item Description"), _("Delivered"), _("Units"), _("Invoiced"),
425         _("This Invoice"), _("Price"), _("Tax Type"), _("Discount"), _("Total"));
426
427 if ($is_batch_invoice) {
428     $th[] = _("DN");
429     $th[] = "";
430 }
431
432 if ($is_edition) {
433     $th[4] = _("Credited");
434 }
435
436 table_header($th);
437 $k = 0;
438 $has_marked = false;
439 $show_qoh = true;
440
441 $dn_line_cnt = 0;
442
443 foreach ($_SESSION['Items']->line_items as $line=>$ln_itm) {
444         if ($ln_itm->quantity == $ln_itm->qty_done) {
445                 continue; // this line was fully invoiced
446         }
447         alt_table_row_color($k);
448         view_stock_status_cell($ln_itm->stock_id);
449
450         text_cells(null, 'Line'.$line.'Desc', $ln_itm->item_description, 30, 50);
451         $dec = get_qty_dec($ln_itm->stock_id);
452         qty_cell($ln_itm->quantity, false, $dec);
453         label_cell($ln_itm->units);
454         qty_cell($ln_itm->qty_done, false, $dec);
455
456         if ($is_batch_invoice) {
457                 // for batch invoices we can only remove whole deliveries
458                 echo '<td nowrap align=right>';
459                 hidden('Line' . $line, $ln_itm->qty_dispatched );
460                 echo number_format2($ln_itm->qty_dispatched, $dec).'</td>';
461         } else {
462                 small_qty_cells(null, 'Line'.$line, qty_format($ln_itm->qty_dispatched, $ln_itm->stock_id, $dec), null, null, $dec);
463         }
464         $display_discount_percent = percent_format($ln_itm->discount_percent*100) . " %";
465
466         $line_total = ($ln_itm->qty_dispatched * $ln_itm->price * (1 - $ln_itm->discount_percent));
467
468         amount_cell($ln_itm->price);
469         label_cell($ln_itm->tax_type_name);
470         label_cell($display_discount_percent, "nowrap align=right");
471         amount_cell($line_total);
472
473         if ($is_batch_invoice) {
474                 if ($dn_line_cnt == 0) {
475                         $dn_line_cnt = $dspans[0];
476                         $dspans = array_slice($dspans, 1);
477                         label_cell($ln_itm->src_no, "rowspan=$dn_line_cnt class=oddrow");
478                         label_cell("<a href='" . $_SERVER['PHP_SELF'] . "?RemoveDN=".
479                                 $ln_itm->src_no."'>" . _("Remove") . "</a>", "rowspan=$dn_line_cnt class=oddrow");
480                 }
481                 $dn_line_cnt--;
482         }
483         end_row();
484 }
485
486 /*Don't re-calculate freight if some of the order has already been delivered -
487 depending on the business logic required this condition may not be required.
488 It seems unfair to charge the customer twice for freight if the order
489 was not fully delivered the first time ?? */
490
491 if (!isset($_POST['ChargeFreightCost']) || $_POST['ChargeFreightCost'] == "") {
492         if ($_SESSION['Items']->any_already_delivered() == 1) {
493                 $_POST['ChargeFreightCost'] = price_format(0);
494         } else {
495                 $_POST['ChargeFreightCost'] = price_format($_SESSION['Items']->freight_cost);
496         }
497
498         if (!check_num('ChargeFreightCost')) {
499                 $_POST['ChargeFreightCost'] = price_format(0);
500         }
501 }
502
503 $accumulate_shipping = get_company_pref('accumulate_shipping');
504 if ($is_batch_invoice && $accumulate_shipping)
505         set_delivery_shipping_sum(array_keys($_SESSION['Items']->src_docs));
506
507 $colspan = 9;
508 start_row();
509 label_cell(_("Shipping Cost"), "colspan=$colspan align=right");
510 small_amount_cells(null, 'ChargeFreightCost', null);
511 if ($is_batch_invoice) {
512 label_cell('', 'colspan=2');
513 }
514
515 end_row();
516 $inv_items_total = $_SESSION['Items']->get_items_total_dispatch();
517
518 $display_sub_total = price_format($inv_items_total + input_num('ChargeFreightCost'));
519
520 label_row(_("Sub-total"), $display_sub_total, "colspan=$colspan align=right","align=right", $is_batch_invoice ? 2 : 0);
521
522 $taxes = $_SESSION['Items']->get_taxes(input_num('ChargeFreightCost'));
523 $tax_total = display_edit_tax_items($taxes, $colspan, $_SESSION['Items']->tax_included, $is_batch_invoice ? 2:0);
524
525 $display_total = price_format(($inv_items_total + input_num('ChargeFreightCost') + $tax_total));
526
527 label_row(_("Invoice Total"), $display_total, "colspan=$colspan align=right","align=right", $is_batch_invoice ? 2 : 0);
528
529 end_table(1);
530 div_end();
531
532 start_table(TABLESTYLE2);
533 textarea_row(_("Memo"), 'Comments', null, 50, 4);
534
535 end_table(1);
536
537 submit_center_first('Update', _("Update"),
538   _('Refresh document page'), true);
539 submit_center_last('process_invoice', _("Process Invoice"),
540   _('Check entered data and save document'), 'default');
541
542 end_form();
543
544 end_page();
545
546 ?>