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