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