Fixed bug resulting in nvalid postings hen cash payment was selectedfor invoice again...
[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."-".$trans_type, _("&Print This Invoice"), true, ST_SALESINVOICE));
60         display_note(print_document_link($invoice_no."-".$trans_type, _("&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         $trans_type = ST_SALESINVOICE;
72
73         display_notification_centered(sprintf(_('Sales Invoice # %d has been updated.'),$invoice_no));
74
75         display_note(get_trans_view_str(ST_SALESINVOICE, $invoice_no, _("&View This Invoice")));
76         echo '<br>';
77         display_note(print_document_link($invoice_no."-".$trans_type, _("&Print This Invoice"), true, ST_SALESINVOICE));
78         display_note(print_document_link($invoice_no."-".$trans_type, _("&Email This Invoice"), true, ST_SALESINVOICE, false, "printlink", "", 1),1);
79
80         hyperlink_no_params($path_to_root . "/sales/inquiry/customer_inquiry.php", _("Select Another &Invoice to Modify"));
81
82         display_footer_exit();
83
84 } elseif (isset($_GET['RemoveDN'])) {
85
86         for($line_no = 0; $line_no < count($_SESSION['Items']->line_items); $line_no++) {
87                 $line = &$_SESSION['Items']->line_items[$line_no];
88                 if ($line->src_no == $_GET['RemoveDN']) {
89                         $line->quantity = $line->qty_done;
90                         $line->qty_dispatched=0;
91                 }
92         }
93         unset($line);
94
95     // Remove also src_doc delivery note
96     $sources = &$_SESSION['Items']->src_docs;
97     unset($sources[$_GET['RemoveDN']]);
98 }
99
100 //-----------------------------------------------------------------------------
101
102 if ( (isset($_GET['DeliveryNumber']) && ($_GET['DeliveryNumber'] > 0) )
103 || isset($_GET['BatchInvoice'])) {
104
105         processing_start();
106
107         if (isset($_GET['BatchInvoice'])) {
108                 $src = $_SESSION['DeliveryBatch'];
109                 unset($_SESSION['DeliveryBatch']);
110         } else {
111                 $src = array($_GET['DeliveryNumber']);
112         }
113
114         /*read in all the selected deliveries into the Items cart  */
115         $dn = new Cart(ST_CUSTDELIVERY, $src, true);
116
117         if ($dn->count_items() == 0) {
118                 hyperlink_params($path_to_root . "/sales/inquiry/sales_deliveries_view.php",
119                         _("Select a different delivery to invoice"), "OutstandingOnly=1");
120                 die ("<br><b>" . _("There are no delivered items with a quantity left to invoice. There is nothing left to invoice.") . "</b>");
121         }
122
123         $_SESSION['Items'] = $dn;
124         copy_from_cart();
125
126 } elseif (isset($_GET['ModifyInvoice']) && $_GET['ModifyInvoice'] > 0) {
127
128         if ( get_sales_parent_numbers(ST_SALESINVOICE, $_GET['ModifyInvoice']) == 0) { // 1.xx compatibility hack
129                 echo"<center><br><b>" . _("There are no delivery notes for this invoice.<br>
130                 Most likely this invoice was created in Front Accounting version prior to 2.0
131                 and therefore can not be modified.") . "</b></center>";
132                 display_footer_exit();
133         }
134         processing_start();
135         $_SESSION['Items'] = new Cart(ST_SALESINVOICE, $_GET['ModifyInvoice']);
136
137         if ($_SESSION['Items']->count_items() == 0) {
138                 echo"<center><br><b>" . _("All quantities on this invoice has been credited. There is nothing to modify on this invoice") . "</b></center>";
139                 display_footer_exit();
140         }
141         copy_from_cart();
142 } elseif (!processing_active()) {
143         /* This page can only be called with a delivery for invoicing or invoice no for edit */
144         display_error(_("This page can only be opened after delivery selection. Please select delivery to invoicing first."));
145
146         hyperlink_no_params("$path_to_root/sales/inquiry/sales_deliveries_view.php", _("Select Delivery to Invoice"));
147
148         end_page();
149         exit;
150 } elseif (!check_quantities()) {
151         display_error(_("Selected quantity cannot be less than quantity credited nor more than quantity not invoiced yet."));
152 }
153 if (isset($_POST['Update'])) {
154         $Ajax->activate('Items');
155 }
156 if (isset($_POST['_InvoiceDate_changed'])) {
157         $_POST['due_date'] = get_invoice_duedate($_SESSION['Items']->payment, $_POST['InvoiceDate']);
158         $Ajax->activate('due_date');
159 }
160 if (list_updated('payment')) {
161         $_SESSION['Items']->payment = get_post('payment');
162         $_POST['due_date'] = get_invoice_duedate($_SESSION['Items']->payment, $_POST['InvoiceDate']);
163         $Ajax->activate('due_date');
164 }
165
166 //-----------------------------------------------------------------------------
167 function check_quantities()
168 {
169         $ok =1;
170         foreach ($_SESSION['Items']->line_items as $line_no=>$itm) {
171                 if (isset($_POST['Line'.$line_no])) {
172                         if($_SESSION['Items']->trans_no) {
173                                 $min = $itm->qty_done;
174                                 $max = $itm->quantity;
175                         } else {
176                                 $min = 0;
177                                 $max = $itm->quantity - $itm->qty_done;
178                         }
179                         if (check_num('Line'.$line_no, $min, $max)) {
180                                 $_SESSION['Items']->line_items[$line_no]->qty_dispatched =
181                                     input_num('Line'.$line_no);
182                         }
183                         else {
184                                 $ok = 0;
185                         }
186                                 
187                 }
188
189                 if (isset($_POST['Line'.$line_no.'Desc'])) {
190                         $line_desc = $_POST['Line'.$line_no.'Desc'];
191                         if (strlen($line_desc) > 0) {
192                                 $_SESSION['Items']->line_items[$line_no]->item_description = $line_desc;
193                         }
194                 }
195         }
196  return $ok;
197 }
198
199 function set_delivery_shipping_sum($delivery_notes) 
200 {
201     
202     $shipping = 0;
203     
204     foreach($delivery_notes as $delivery_num) 
205     {
206         $myrow = get_customer_trans($delivery_num, 13);
207         //$branch = get_branch($myrow["branch_code"]);
208         //$sales_order = get_sales_order_header($myrow["order_"]);
209         
210         //$shipping += $sales_order['freight_cost'];
211         $shipping += $myrow['ov_freight'];
212     }
213     $_POST['ChargeFreightCost'] = price_format($shipping);
214 }
215
216
217 function copy_to_cart()
218 {
219         $cart = &$_SESSION['Items'];
220         $cart->ship_via = $_POST['ship_via'];
221         $cart->freight_cost = input_num('ChargeFreightCost');
222         $cart->document_date =  $_POST['InvoiceDate'];
223         $cart->due_date =  $_POST['due_date'];
224         if ($cart->pos['cash_sale'] || $cart->pos['credit_sale']) {
225                 $cart->payment = $_POST['payment'];
226                 $cart->payment_terms = get_payment_terms($_POST['payment']);
227         }
228         $cart->Comments = $_POST['Comments'];
229         if ($_SESSION['Items']->trans_no == 0)
230                 $cart->reference = $_POST['ref'];
231
232 }
233 //-----------------------------------------------------------------------------
234
235 function copy_from_cart()
236 {
237         $cart = &$_SESSION['Items'];
238         $_POST['ship_via'] = $cart->ship_via;
239         $_POST['ChargeFreightCost'] = price_format($cart->freight_cost);
240         $_POST['InvoiceDate']= $cart->document_date;
241         $_POST['due_date'] = $cart->due_date;
242         $_POST['Comments']= $cart->Comments;
243         $_POST['cart_id'] = $cart->cart_id;
244         $_POST['ref'] = $cart->reference;
245         $_POST['payment'] = $cart->payment;
246 }
247
248 //-----------------------------------------------------------------------------
249
250 function check_data()
251 {
252         global $Refs;
253
254         if (!isset($_POST['InvoiceDate']) || !is_date($_POST['InvoiceDate'])) {
255                 display_error(_("The entered invoice date is invalid."));
256                 set_focus('InvoiceDate');
257                 return false;
258         }
259
260         if (!is_date_in_fiscalyear($_POST['InvoiceDate'])) {
261                 display_error(_("The entered invoice date is not in fiscal year."));
262                 set_focus('InvoiceDate');
263                 return false;
264         }
265
266         if (!isset($_POST['due_date']) || !is_date($_POST['due_date'])) {
267                 display_error(_("The entered invoice due date is invalid."));
268                 set_focus('due_date');
269                 return false;
270         }
271
272         if ($_SESSION['Items']->trans_no == 0) {
273                 if (!$Refs->is_valid($_POST['ref'])) {
274                         display_error(_("You must enter a reference."));
275                         set_focus('ref');
276                         return false;
277                 }
278
279                 if (!is_new_reference($_POST['ref'], 10)) {
280                         display_error(_("The entered reference is already in use."));
281                         set_focus('ref');
282                         return false;
283                 }
284         }
285
286         if ($_POST['ChargeFreightCost'] == "") {
287                 $_POST['ChargeFreightCost'] = price_format(0);
288         }
289
290         if (!check_num('ChargeFreightCost', 0)) {
291                 display_error(_("The entered shipping value is not numeric."));
292                 set_focus('ChargeFreightCost');
293                 return false;
294         }
295
296         if ($_SESSION['Items']->has_items_dispatch() == 0 && input_num('ChargeFreightCost') == 0) {
297                 display_error(_("There are no item quantities on this invoice."));
298                 return false;
299         }
300
301         if (!check_quantities()) {
302                 display_error(_("Selected quantity cannot be less than quantity credited nor more than quantity not invoiced yet."));
303                 return false;
304         }
305
306         return true;
307 }
308
309 //-----------------------------------------------------------------------------
310 if (isset($_POST['process_invoice']) && check_data()) {
311
312         $newinvoice=  $_SESSION['Items']->trans_no == 0;
313         copy_to_cart();
314         if ($newinvoice) new_doc_date($_SESSION['Items']->document_date);
315
316         $invoice_no = $_SESSION['Items']->write();
317         processing_end();
318
319         if ($newinvoice) {
320                 meta_forward($_SERVER['PHP_SELF'], "AddedID=$invoice_no");
321         } else {
322                 meta_forward($_SERVER['PHP_SELF'], "UpdatedID=$invoice_no");
323         }
324 }
325
326 if(list_updated('payment')) {
327         $order = &$_SESSION['Items'];
328         $order->payment = get_post('payment');
329         $order->payment_terms = get_payment_terms($order->payment);
330         $order->due_date = get_invoice_duedate($order->payment, $order->document_date);
331         if ($order->payment_terms['cash_sale']) {
332                 $_POST['Location'] = $order->Location = $order->pos['pos_location'];
333                 $order->location_name = $order->pos['location_name'];
334         }
335 }
336 // find delivery spans for batch invoice display
337 $dspans = array();
338 $lastdn = ''; $spanlen=1;
339
340 for ($line_no = 0; $line_no < count($_SESSION['Items']->line_items); $line_no++) {
341         $line = $_SESSION['Items']->line_items[$line_no];
342         if ($line->quantity == $line->qty_done) {
343                 continue;
344         }
345         if ($line->src_no == $lastdn) {
346                 $spanlen++;
347         } else {
348                 if ($lastdn != '') {
349                         $dspans[] = $spanlen;
350                         $spanlen = 1;
351                 }
352         }
353         $lastdn = $line->src_no;
354 }
355 $dspans[] = $spanlen;
356
357 //-----------------------------------------------------------------------------
358
359 $is_batch_invoice = count($_SESSION['Items']->src_docs) > 1;
360
361 $is_edition = $_SESSION['Items']->trans_type == ST_SALESINVOICE && $_SESSION['Items']->trans_no != 0;
362 start_form();
363 hidden('cart_id');
364
365 start_table(TABLESTYLE2, "width=80%", 5);
366
367 start_row();
368 $colspan = 1;
369 $dim = get_company_pref('use_dimension');
370 if ($dim > 0) 
371         $colspan = 3;
372 label_cells(_("Customer"), $_SESSION['Items']->customer_name, "class='tableheader2'");
373 label_cells(_("Branch"), get_branch_name($_SESSION['Items']->Branch), "class='tableheader2'");
374 if ($_SESSION['Items']->pos['credit_sale'] || $_SESSION['Items']->pos['cash_sale']) {
375         $paymcat = !$_SESSION['Items']->pos['cash_sale'] ? PM_CREDIT :
376                 (!$_SESSION['Items']->pos['credit_sale'] ? PM_CASH : PM_ANY);
377         label_cells(_("Payment terms:"), sale_payment_list('payment', $paymcat),
378                 "class='tableheader2'", "colspan=$colspan");
379 } else
380         label_cells(_('Payment:'), $_SESSION['Items']->payment_terms['terms'], "class='tableheader2'", "colspan=$colspan");
381
382 end_row();
383 start_row();
384
385 if ($_SESSION['Items']->trans_no == 0) {
386         ref_cells(_("Reference"), 'ref', '', null, "class='tableheader2'");
387 } else {
388         label_cells(_("Reference"), $_SESSION['Items']->reference, "class='tableheader2'");
389 }
390
391 //label_cells(_("Delivery Notes:"),
392 //get_customer_trans_view_str(ST_CUSTDELIVERY, array_keys($_SESSION['Items']->src_docs)), "class='tableheader2'");
393
394 label_cells(_("Sales Type"), $_SESSION['Items']->sales_type_name, "class='tableheader2'");
395
396 label_cells(_("Currency"), $_SESSION['Items']->customer_currency, "class='tableheader2'");
397 // 2010-09-03 Joe Hunt
398 if ($dim > 0) 
399         label_cells(_("Dimension"), get_dimension_string($_SESSION['Items']->dimension_id), "class='tableheader2'");
400
401 end_row();
402 start_row();
403
404 if (!isset($_POST['ship_via'])) {
405         $_POST['ship_via'] = $_SESSION['Items']->ship_via;
406 }
407 label_cell(_("Shipping Company"), "class='tableheader2'");
408 shippers_list_cells(null, 'ship_via', $_POST['ship_via']);
409
410 if (!isset($_POST['InvoiceDate']) || !is_date($_POST['InvoiceDate'])) {
411         $_POST['InvoiceDate'] = new_doc_date();
412         if (!is_date_in_fiscalyear($_POST['InvoiceDate'])) {
413                 $_POST['InvoiceDate'] = end_fiscalyear();
414         }
415 }
416
417 date_cells(_("Date"), 'InvoiceDate', '', $_SESSION['Items']->trans_no == 0, 
418         0, 0, 0, "class='tableheader2'", true);
419
420 if (!isset($_POST['due_date']) || !is_date($_POST['due_date'])) {
421         $_POST['due_date'] = get_invoice_duedate($_SESSION['Items']->payment, $_POST['InvoiceDate']);
422 }
423
424 date_cells(_("Due Date"), 'due_date', '', null, 0, 0, 0, "class='tableheader2'");
425 if ($dim > 1) 
426         label_cells(_("Dimension"). " 2", get_dimension_string($_SESSION['Items']->dimension2_id), "class='tableheader2'");
427 else if ($dim > 0)
428         label_cell("&nbsp;", "colspan=2");
429
430 end_row();
431 end_table();
432
433 $row = get_customer_to_order($_SESSION['Items']->customer_id);
434 if ($row['dissallow_invoices'] == 1)
435 {
436         display_error(_("The selected customer account is currently on hold. Please contact the credit control personnel to discuss."));
437         end_form();
438         end_page();
439         exit();
440 }       
441
442 display_heading(_("Invoice Items"));
443
444 div_start('Items');
445 start_table(TABLESTYLE, "width=80%");
446 $th = array(_("Item Code"), _("Item Description"), _("Delivered"), _("Units"), _("Invoiced"),
447         _("This Invoice"), _("Price"), _("Tax Type"), _("Discount"), _("Total"));
448
449 if ($is_batch_invoice) {
450     $th[] = _("DN");
451     $th[] = "";
452 }
453
454 if ($is_edition) {
455     $th[4] = _("Credited");
456 }
457
458 table_header($th);
459 $k = 0;
460 $has_marked = false;
461 $show_qoh = true;
462
463 $dn_line_cnt = 0;
464
465 foreach ($_SESSION['Items']->line_items as $line=>$ln_itm) {
466         if ($ln_itm->quantity == $ln_itm->qty_done) {
467                 continue; // this line was fully invoiced
468         }
469         alt_table_row_color($k);
470         view_stock_status_cell($ln_itm->stock_id);
471
472         text_cells(null, 'Line'.$line.'Desc', $ln_itm->item_description, 30, 50);
473         $dec = get_qty_dec($ln_itm->stock_id);
474         qty_cell($ln_itm->quantity, false, $dec);
475         label_cell($ln_itm->units);
476         qty_cell($ln_itm->qty_done, false, $dec);
477
478         if ($is_batch_invoice) {
479                 // for batch invoices we can only remove whole deliveries
480                 echo '<td nowrap align=right>';
481                 hidden('Line' . $line, $ln_itm->qty_dispatched );
482                 echo number_format2($ln_itm->qty_dispatched, $dec).'</td>';
483         } else {
484                 small_qty_cells(null, 'Line'.$line, qty_format($ln_itm->qty_dispatched, $ln_itm->stock_id, $dec), null, null, $dec);
485         }
486         $display_discount_percent = percent_format($ln_itm->discount_percent*100) . " %";
487
488         $line_total = ($ln_itm->qty_dispatched * $ln_itm->price * (1 - $ln_itm->discount_percent));
489
490         amount_cell($ln_itm->price);
491         label_cell($ln_itm->tax_type_name);
492         label_cell($display_discount_percent, "nowrap align=right");
493         amount_cell($line_total);
494
495         if ($is_batch_invoice) {
496                 if ($dn_line_cnt == 0) {
497                         $dn_line_cnt = $dspans[0];
498                         $dspans = array_slice($dspans, 1);
499                         label_cell($ln_itm->src_no, "rowspan=$dn_line_cnt class=oddrow");
500                         label_cell("<a href='" . $_SERVER['PHP_SELF'] . "?RemoveDN=".
501                                 $ln_itm->src_no."'>" . _("Remove") . "</a>", "rowspan=$dn_line_cnt class=oddrow");
502                 }
503                 $dn_line_cnt--;
504         }
505         end_row();
506 }
507
508 /*Don't re-calculate freight if some of the order has already been delivered -
509 depending on the business logic required this condition may not be required.
510 It seems unfair to charge the customer twice for freight if the order
511 was not fully delivered the first time ?? */
512
513 if (!isset($_POST['ChargeFreightCost']) || $_POST['ChargeFreightCost'] == "") {
514         if ($_SESSION['Items']->any_already_delivered() == 1) {
515                 $_POST['ChargeFreightCost'] = price_format(0);
516         } else {
517                 $_POST['ChargeFreightCost'] = price_format($_SESSION['Items']->freight_cost);
518         }
519
520         if (!check_num('ChargeFreightCost')) {
521                 $_POST['ChargeFreightCost'] = price_format(0);
522         }
523 }
524
525 $accumulate_shipping = get_company_pref('accumulate_shipping');
526 if ($is_batch_invoice && $accumulate_shipping)
527         set_delivery_shipping_sum(array_keys($_SESSION['Items']->src_docs));
528
529 $colspan = 9;
530 start_row();
531 label_cell(_("Shipping Cost"), "colspan=$colspan align=right");
532 small_amount_cells(null, 'ChargeFreightCost', null);
533 if ($is_batch_invoice) {
534 label_cell('', 'colspan=2');
535 }
536
537 end_row();
538 $inv_items_total = $_SESSION['Items']->get_items_total_dispatch();
539
540 $display_sub_total = price_format($inv_items_total + input_num('ChargeFreightCost'));
541
542 label_row(_("Sub-total"), $display_sub_total, "colspan=$colspan align=right","align=right", $is_batch_invoice ? 2 : 0);
543
544 $taxes = $_SESSION['Items']->get_taxes(input_num('ChargeFreightCost'));
545 $tax_total = display_edit_tax_items($taxes, $colspan, $_SESSION['Items']->tax_included, $is_batch_invoice ? 2:0);
546
547 $display_total = price_format(($inv_items_total + input_num('ChargeFreightCost') + $tax_total));
548
549 label_row(_("Invoice Total"), $display_total, "colspan=$colspan align=right","align=right", $is_batch_invoice ? 2 : 0);
550
551 end_table(1);
552 div_end();
553
554 start_table(TABLESTYLE2);
555 textarea_row(_("Memo"), 'Comments', null, 50, 4);
556
557 end_table(1);
558
559 submit_center_first('Update', _("Update"),
560   _('Refresh document page'), true);
561 submit_center_last('process_invoice', _("Process Invoice"),
562   _('Check entered data and save document'), 'default');
563
564 end_form();
565
566 end_page();
567
568 ?>