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