Added Email link when updating documents.
[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, 
163                 $_POST['InvoiceDate']);
164         $Ajax->activate('due_date');
165 }
166 if (list_updated('payment')) {
167         $_SESSION['Items']->payment = get_post('payment');
168         $_POST['due_date'] = get_invoice_duedate($_SESSION['Items']->payment, 
169                 $_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 != -1) {
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 invoice date is not in fiscal year."));
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 label_cells(_("Customer"), $_SESSION['Items']->customer_name, "class='tableheader2'");
366 label_cells(_("Branch"), get_branch_name($_SESSION['Items']->Branch), "class='tableheader2'");
367 label_cells(_("Currency"), $_SESSION['Items']->customer_currency, "class='tableheader2'");
368 end_row();
369 start_row();
370
371 if ($_SESSION['Items']->trans_no == 0) {
372         ref_cells(_("Reference"), 'ref', '', null, "class='tableheader2'");
373 } else {
374         label_cells(_("Reference"), $_SESSION['Items']->reference, "class='tableheader2'");
375 }
376
377 //label_cells(_("Delivery Notes:"),
378 //get_customer_trans_view_str(ST_CUSTDELIVERY, array_keys($_SESSION['Items']->src_docs)), "class='tableheader2'");
379
380 label_cells(_("Sales Type"), $_SESSION['Items']->sales_type_name, "class='tableheader2'");
381
382 if ($_SESSION['Items']->pos != -1) // editable payment type
383         label_cells(_("Payment terms:"), sale_payment_list('payment'), "class='tableheader2'");
384 else
385         label_cells(_('Payment:'), $_SESSION['Items']->payment_terms['terms'], "class='tableheader2'");
386
387 end_row();
388 start_row();
389
390 if (!isset($_POST['ship_via'])) {
391         $_POST['ship_via'] = $_SESSION['Items']->ship_via;
392 }
393 label_cell(_("Shipping Company"), "class='tableheader2'");
394 shippers_list_cells(null, 'ship_via', $_POST['ship_via']);
395
396 if (!isset($_POST['InvoiceDate']) || !is_date($_POST['InvoiceDate'])) {
397         $_POST['InvoiceDate'] = new_doc_date();
398         if (!is_date_in_fiscalyear($_POST['InvoiceDate'])) {
399                 $_POST['InvoiceDate'] = end_fiscalyear();
400         }
401 }
402
403 date_cells(_("Date"), 'InvoiceDate', '', $_SESSION['Items']->trans_no == 0, 
404         0, 0, 0, "class='tableheader2'", true);
405
406 if (!isset($_POST['due_date']) || !is_date($_POST['due_date'])) {
407         $_POST['due_date'] = get_invoice_duedate($_SESSION['Items']->payment, $_POST['InvoiceDate']);
408 }
409
410 date_cells(_("Due Date"), 'due_date', '', null, 0, 0, 0, "class='tableheader2'");
411
412 end_row();
413 end_table();
414
415 $row = get_customer_to_order($_SESSION['Items']->customer_id);
416 if ($row['dissallow_invoices'] == 1)
417 {
418         display_error(_("The selected customer account is currently on hold. Please contact the credit control personnel to discuss."));
419         end_form();
420         end_page();
421         exit();
422 }       
423
424 display_heading(_("Invoice Items"));
425
426 div_start('Items');
427 start_table(TABLESTYLE, "width=80%");
428 $th = array(_("Item Code"), _("Item Description"), _("Delivered"), _("Units"), _("Invoiced"),
429         _("This Invoice"), _("Price"), _("Tax Type"), _("Discount"), _("Total"));
430
431 if ($is_batch_invoice) {
432     $th[] = _("DN");
433     $th[] = "";
434 }
435
436 if ($is_edition) {
437     $th[4] = _("Credited");
438 }
439
440 table_header($th);
441 $k = 0;
442 $has_marked = false;
443 $show_qoh = true;
444
445 $dn_line_cnt = 0;
446
447 foreach ($_SESSION['Items']->line_items as $line=>$ln_itm) {
448         if ($ln_itm->quantity == $ln_itm->qty_done) {
449                 continue; // this line was fully invoiced
450         }
451         alt_table_row_color($k);
452         view_stock_status_cell($ln_itm->stock_id);
453
454         text_cells(null, 'Line'.$line.'Desc', $ln_itm->item_description, 30, 50);
455         $dec = get_qty_dec($ln_itm->stock_id);
456         qty_cell($ln_itm->quantity, false, $dec);
457         label_cell($ln_itm->units);
458         qty_cell($ln_itm->qty_done, false, $dec);
459
460         if ($is_batch_invoice) {
461                 // for batch invoices we can only remove whole deliveries
462                 echo '<td nowrap align=right>';
463                 hidden('Line' . $line, $ln_itm->qty_dispatched );
464                 echo number_format2($ln_itm->qty_dispatched, $dec).'</td>';
465         } else {
466                 small_qty_cells(null, 'Line'.$line, qty_format($ln_itm->qty_dispatched, $ln_itm->stock_id, $dec), null, null, $dec);
467         }
468         $display_discount_percent = percent_format($ln_itm->discount_percent*100) . " %";
469
470         $line_total = ($ln_itm->qty_dispatched * $ln_itm->price * (1 - $ln_itm->discount_percent));
471
472         amount_cell($ln_itm->price);
473         label_cell($ln_itm->tax_type_name);
474         label_cell($display_discount_percent, "nowrap align=right");
475         amount_cell($line_total);
476
477         if ($is_batch_invoice) {
478                 if ($dn_line_cnt == 0) {
479                         $dn_line_cnt = $dspans[0];
480                         $dspans = array_slice($dspans, 1);
481                         label_cell($ln_itm->src_no, "rowspan=$dn_line_cnt class=oddrow");
482                         label_cell("<a href='" . $_SERVER['PHP_SELF'] . "?RemoveDN=".
483                                 $ln_itm->src_no."'>" . _("Remove") . "</a>", "rowspan=$dn_line_cnt class=oddrow");
484                 }
485                 $dn_line_cnt--;
486         }
487         end_row();
488 }
489
490 /*Don't re-calculate freight if some of the order has already been delivered -
491 depending on the business logic required this condition may not be required.
492 It seems unfair to charge the customer twice for freight if the order
493 was not fully delivered the first time ?? */
494
495 if (!isset($_POST['ChargeFreightCost']) || $_POST['ChargeFreightCost'] == "") {
496         if ($_SESSION['Items']->any_already_delivered() == 1) {
497                 $_POST['ChargeFreightCost'] = price_format(0);
498         } else {
499                 $_POST['ChargeFreightCost'] = price_format($_SESSION['Items']->freight_cost);
500         }
501
502         if (!check_num('ChargeFreightCost')) {
503                 $_POST['ChargeFreightCost'] = price_format(0);
504         }
505 }
506
507 $accumulate_shipping = get_company_pref('accumulate_shipping');
508 if ($is_batch_invoice && $accumulate_shipping)
509         set_delivery_shipping_sum(array_keys($_SESSION['Items']->src_docs));
510
511 $colspan = 9;
512 start_row();
513 label_cell(_("Shipping Cost"), "colspan=$colspan align=right");
514 small_amount_cells(null, 'ChargeFreightCost', null);
515 if ($is_batch_invoice) {
516 label_cell('', 'colspan=2');
517 }
518
519 end_row();
520 $inv_items_total = $_SESSION['Items']->get_items_total_dispatch();
521
522 $display_sub_total = price_format($inv_items_total + input_num('ChargeFreightCost'));
523
524 label_row(_("Sub-total"), $display_sub_total, "colspan=$colspan align=right","align=right", $is_batch_invoice ? 2 : 0);
525
526 $taxes = $_SESSION['Items']->get_taxes(input_num('ChargeFreightCost'));
527 $tax_total = display_edit_tax_items($taxes, $colspan, $_SESSION['Items']->tax_included, $is_batch_invoice ? 2:0);
528
529 $display_total = price_format(($inv_items_total + input_num('ChargeFreightCost') + $tax_total));
530
531 label_row(_("Invoice Total"), $display_total, "colspan=$colspan align=right","align=right", $is_batch_invoice ? 2 : 0);
532
533 end_table(1);
534 div_end();
535
536 start_table(TABLESTYLE2);
537 textarea_row(_("Memo"), 'Comments', null, 50, 4);
538
539 end_table(1);
540
541 submit_center_first('Update', _("Update"),
542   _('Refresh document page'), true);
543 submit_center_last('process_invoice', _("Process Invoice"),
544   _('Check entered data and save document'), 'default');
545
546 end_form();
547
548 end_page();
549
550 ?>