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