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