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