d935978f202439f181dee5653b6228fe63367e21
[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 }
224 //-----------------------------------------------------------------------------
225
226 function copy_from_cart()
227 {
228         $cart = &$_SESSION['Items'];
229         $_POST['ship_via'] = $cart->ship_via;
230         $_POST['ChargeFreightCost'] = price_format($cart->freight_cost);
231         $_POST['InvoiceDate']= $cart->document_date;
232         $_POST['due_date'] = $cart->due_date;
233         $_POST['Comments']= $cart->Comments;
234         $_POST['cart_id'] = $cart->cart_id;
235 }
236
237 //-----------------------------------------------------------------------------
238
239 function check_data()
240 {
241         if (!isset($_POST['InvoiceDate']) || !is_date($_POST['InvoiceDate'])) {
242                 display_error(_("The entered invoice date is invalid."));
243                 set_focus('InvoiceDate');
244                 return false;
245         }
246
247         if (!is_date_in_fiscalyear($_POST['InvoiceDate'])) {
248                 display_error(_("The entered invoice date is not in fiscal year."));
249                 set_focus('InvoiceDate');
250                 return false;
251         }
252
253         if (!isset($_POST['due_date']) || !is_date($_POST['due_date'])) {
254                 display_error(_("The entered invoice due date is invalid."));
255                 set_focus('due_date');
256                 return false;
257         }
258
259         if ($_SESSION['Items']->trans_no == 0) {
260                 if (!references::is_valid($_POST['ref'])) {
261                         display_error(_("You must enter a reference."));
262                         set_focus('ref');
263                         return false;
264                 }
265
266                 if (!is_new_reference($_POST['ref'], 10)) {
267                         display_error(_("The entered reference is already in use."));
268                         set_focus('ref');
269                         return false;
270                 }
271         }
272
273         if ($_POST['ChargeFreightCost'] == "") {
274                 $_POST['ChargeFreightCost'] = price_format(0);
275         }
276
277         if (!check_num('ChargeFreightCost', 0)) {
278                 display_error(_("The entered shipping value is not numeric."));
279                 set_focus('ChargeFreightCost');
280                 return false;
281         }
282
283         if ($_SESSION['Items']->has_items_dispatch() == 0 && input_num('ChargeFreightCost') == 0) {
284                 display_error(_("There are no item quantities on this invoice."));
285                 return false;
286         }
287
288         if (!check_quantities()) {
289                 display_error(_("Selected quantity cannot be less than quantity credited nor more than quantity not invoiced yet."));
290                 return false;
291         }
292
293         return true;
294 }
295
296 //-----------------------------------------------------------------------------
297 if (isset($_POST['process_invoice']) && check_data()) {
298
299         $newinvoice=  $_SESSION['Items']->trans_no == 0;
300         copy_to_cart();
301         $invoice_no = $_SESSION['Items']->write();
302
303         processing_end();
304         if ($newinvoice) {
305                 meta_forward($_SERVER['PHP_SELF'], "AddedID=$invoice_no");
306         } else {
307                 meta_forward($_SERVER['PHP_SELF'], "UpdatedID=$invoice_no");
308         }
309 }
310
311 // find delivery spans for batch invoice display
312 $dspans = array();
313 $lastdn = ''; $spanlen=1;
314
315 for ($line_no = 0; $line_no < count($_SESSION['Items']->line_items); $line_no++) {
316         $line = $_SESSION['Items']->line_items[$line_no];
317         if ($line->quantity == $line->qty_done) {
318                 continue;
319         }
320         if ($line->src_no == $lastdn) {
321                 $spanlen++;
322         } else {
323                 if ($lastdn != '') {
324                         $dspans[] = $spanlen;
325                         $spanlen = 1;
326                 }
327         }
328         $lastdn = $line->src_no;
329 }
330 $dspans[] = $spanlen;
331
332 //-----------------------------------------------------------------------------
333
334 $is_batch_invoice = count($_SESSION['Items']->src_docs) > 1;
335
336 $is_edition = $_SESSION['Items']->trans_type == 10 && $_SESSION['Items']->trans_no != 0;
337 start_form(false, true);
338 hidden('cart_id');
339
340 start_table("$table_style2 width=80%", 5);
341
342 start_row();
343 label_cells(_("Customer"), $_SESSION['Items']->customer_name, "class='tableheader2'");
344 label_cells(_("Branch"), get_branch_name($_SESSION['Items']->Branch), "class='tableheader2'");
345 label_cells(_("Currency"), $_SESSION['Items']->customer_currency, "class='tableheader2'");
346 end_row();
347 start_row();
348
349 if ($_SESSION['Items']->trans_no == 0) {
350         ref_cells(_("Reference"), 'ref', '', $_SESSION['Items']->reference, "class='tableheader2'");
351 } else {
352         label_cells(_("Reference"), $_SESSION['Items']->reference, "class='tableheader2'");
353 }
354
355 label_cells(_("Delivery Notes:"),
356 get_customer_trans_view_str(systypes::cust_dispatch(), array_keys($_SESSION['Items']->src_docs)), "class='tableheader2'");
357
358 label_cells(_("Sales Type"), $_SESSION['Items']->sales_type_name, "class='tableheader2'");
359
360 end_row();
361 start_row();
362
363 if (!isset($_POST['ship_via'])) {
364         $_POST['ship_via'] = $_SESSION['Items']->ship_via;
365 }
366 label_cell(_("Shipping Company"), "class='tableheader2'");
367 shippers_list_cells(null, 'ship_via', $_POST['ship_via']);
368
369 if (!isset($_POST['InvoiceDate']) || !is_date($_POST['InvoiceDate'])) {
370         $_POST['InvoiceDate'] = Today();
371         if (!is_date_in_fiscalyear($_POST['InvoiceDate'])) {
372                 $_POST['InvoiceDate'] = end_fiscalyear();
373         }
374 }
375
376 date_cells(_("Date"), 'InvoiceDate', '', $_POST['InvoiceDate'], 0, 0, 0, "class='tableheader2'", true);
377
378 if (!isset($_POST['due_date']) || !is_date($_POST['due_date'])) {
379         $_POST['due_date'] = get_invoice_duedate($_SESSION['Items']->customer_id, $_POST['InvoiceDate']);
380 }
381
382 date_cells(_("Due Date"), 'due_date', '', $_POST['due_date'], 0, 0, 0, "class='tableheader2'");
383
384 end_row();
385 end_table();
386
387 $row = get_customer_to_order($_SESSION['Items']->customer_id);
388 if ($row['dissallow_invoices'] == 1)
389 {
390         display_error(_("The selected customer account is currently on hold. Please contact the credit control personnel to discuss."));
391         end_form();
392         end_page();
393         exit();
394 }       
395
396 display_heading(_("Invoice Items"));
397
398 div_start('Items');
399 start_table("$table_style width=80%");
400 $th = array(_("Item Code"), _("Item Description"), _("Delivered"), _("Units"), _("Invoiced"),
401         _("This Invoice"), _("Price"), _("Tax Type"), _("Discount"), _("Total"));
402
403 if ($is_batch_invoice) {
404     $th[] = _("DN");
405     $th[] = "";
406 }
407
408 if ($is_edition) {
409     $th[4] = _("Credited");
410 }
411
412 table_header($th);
413 $k = 0;
414 $has_marked = false;
415 $show_qoh = true;
416
417 $dn_line_cnt = 0;
418
419 foreach ($_SESSION['Items']->line_items as $line=>$ln_itm) {
420         if ($ln_itm->quantity == $ln_itm->qty_done) {
421                 continue; // this line was fully invoiced
422         }
423         alt_table_row_color($k);
424         view_stock_status_cell($ln_itm->stock_id);
425
426         text_cells(null, 'Line'.$line.'Desc', $ln_itm->item_description, 30, 50);
427         $dec = get_qty_dec($ln_itm->stock_id);
428         qty_cell($ln_itm->quantity, false, $dec);
429         label_cell($ln_itm->units);
430         qty_cell($ln_itm->qty_done, false, $dec);
431
432         if ($is_batch_invoice) {
433                 // for batch invoices we can only remove whole deliveries
434                 echo '<td nowrap align=right>';
435                 hidden('Line' . $line, $ln_itm->qty_dispatched );
436                 echo number_format2($ln_itm->qty_dispatched, $dec).'</td>';
437         } else {
438                 small_qty_cells(null, 'Line'.$line, qty_format($ln_itm->qty_dispatched, $ln_itm->stock_id, $dec), null, null, $dec);
439         }
440         $display_discount_percent = percent_format($ln_itm->discount_percent*100) . " %";
441
442         $line_total = ($ln_itm->qty_dispatched * $ln_itm->price * (1 - $ln_itm->discount_percent));
443
444         amount_cell($ln_itm->price);
445         label_cell($ln_itm->tax_type_name);
446         label_cell($display_discount_percent, "nowrap align=right");
447         amount_cell($line_total);
448
449         if ($is_batch_invoice) {
450                 if ($dn_line_cnt == 0) {
451                         $dn_line_cnt = $dspans[0];
452                         $dspans = array_slice($dspans, 1);
453                         label_cell($ln_itm->src_no, "rowspan=$dn_line_cnt class=oddrow");
454                         label_cell("<a href='" . $_SERVER['PHP_SELF'] . "?RemoveDN=".
455                                 $ln_itm->src_no."'>" . _("Remove") . "</a>", "rowspan=$dn_line_cnt class=oddrow");
456                 }
457                 $dn_line_cnt--;
458         }
459         end_row();
460 }
461
462 /*Don't re-calculate freight if some of the order has already been delivered -
463 depending on the business logic required this condition may not be required.
464 It seems unfair to charge the customer twice for freight if the order
465 was not fully delivered the first time ?? */
466
467 if (!isset($_POST['ChargeFreightCost']) || $_POST['ChargeFreightCost'] == "") {
468         if ($_SESSION['Items']->any_already_delivered() == 1) {
469                 $_POST['ChargeFreightCost'] = price_format(0);
470         } else {
471                 $_POST['ChargeFreightCost'] = price_format($_SESSION['Items']->freight_cost);
472         }
473
474         if (!check_num('ChargeFreightCost')) {
475                 $_POST['ChargeFreightCost'] = price_format(0);
476         }
477 }
478
479 $accumulate_shipping = get_company_pref('accumulate_shipping');
480 if ($is_batch_invoice && $accumulate_shipping)
481         set_delivery_shipping_sum(array_keys($_SESSION['Items']->src_docs));
482
483 start_row();
484
485 small_amount_cells(_("Shipping Cost"), 'ChargeFreightCost', null, "colspan=9 align=right");
486 if ($is_batch_invoice) {
487 label_cell('', 'colspan=2');
488 }
489
490 end_row();
491 $inv_items_total = $_SESSION['Items']->get_items_total_dispatch();
492
493 $display_sub_total = price_format($inv_items_total + input_num('ChargeFreightCost'));
494
495 label_row(_("Sub-total"), $display_sub_total, "colspan=9 align=right","align=right", $is_batch_invoice ? 2 : 0);
496
497 $taxes = $_SESSION['Items']->get_taxes(input_num('ChargeFreightCost'));
498 $tax_total = display_edit_tax_items($taxes, 9, $_SESSION['Items']->tax_included, $is_batch_invoice ? 2:0);
499
500 $display_total = price_format(($inv_items_total + input_num('ChargeFreightCost') + $tax_total));
501
502 label_row(_("Invoice Total"), $display_total, "colspan=9 align=right","align=right", $is_batch_invoice ? 2 : 0);
503
504 end_table(1);
505 div_end();
506
507 start_table($table_style2);
508 textarea_row(_("Memo"), 'Comments', null, 50, 4);
509
510 end_table(1);
511
512 submit_center_first('Update', _("Update"),
513   _('Refresh document page'), true);
514 submit_center_last('process_invoice', _("Process Invoice"),
515   _('Check entered data and save document'), true);
516
517 end_form();
518
519 end_page();
520
521 ?>