afad461764c3fc169601cb68d7fce81b942737dd
[fa-stable.git] / sales / customer_invoice.php
1 <?php
2 /**********************************************************************
3     Copyright (C) FrontAccounting, LLC.
4         Released under the terms of the GNU Affero General Public License,
5         AGPL, as published by the Free Software Foundation, either version 
6         3 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/agpl-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 in 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 display_heading(_("Invoice Items"));
388
389 div_start('Items');
390 start_table("$table_style width=80%");
391 $th = array(_("Item Code"), _("Item Description"), _("Delivered"), _("Units"), _("Invoiced"),
392         _("This Invoice"), _("Price"), _("Tax Type"), _("Discount"), _("Total"));
393
394 if ($is_batch_invoice) {
395     $th[] = _("DN");
396     $th[] = "";
397 }
398
399 if ($is_edition) {
400     $th[4] = _("Credited");
401 }
402
403 table_header($th);
404 $k = 0;
405 $has_marked = false;
406 $show_qoh = true;
407
408 $dn_line_cnt = 0;
409
410 foreach ($_SESSION['Items']->line_items as $line=>$ln_itm) {
411         if ($ln_itm->quantity == $ln_itm->qty_done) {
412                 continue; // this line was fully invoiced
413         }
414         alt_table_row_color($k);
415         view_stock_status_cell($ln_itm->stock_id);
416
417         text_cells(null, 'Line'.$line.'Desc', $ln_itm->item_description, 30, 50);
418         $dec = get_qty_dec($ln_itm->stock_id);
419         qty_cell($ln_itm->quantity, false, $dec);
420         label_cell($ln_itm->units);
421         qty_cell($ln_itm->qty_done, false, $dec);
422
423         if ($is_batch_invoice) {
424                 // for batch invoices we can only remove whole deliveries
425                 echo '<td nowrap align=right>';
426                 hidden('Line' . $line, $ln_itm->qty_dispatched );
427                 echo number_format2($ln_itm->qty_dispatched, $dec).'</td>';
428         } else {
429                 small_qty_cells(null, 'Line'.$line, qty_format($ln_itm->qty_dispatched, $ln_itm->stock_id, $dec), null, null, $dec);
430         }
431         $display_discount_percent = percent_format($ln_itm->discount_percent*100) . " %";
432
433         $line_total = ($ln_itm->qty_dispatched * $ln_itm->price * (1 - $ln_itm->discount_percent));
434
435         amount_cell($ln_itm->price);
436         label_cell($ln_itm->tax_type_name);
437         label_cell($display_discount_percent, "nowrap align=right");
438         amount_cell($line_total);
439
440         if ($is_batch_invoice) {
441                 if ($dn_line_cnt == 0) {
442                         $dn_line_cnt = $dspans[0];
443                         $dspans = array_slice($dspans, 1);
444                         label_cell($ln_itm->src_no, "rowspan=$dn_line_cnt class=oddrow");
445                         label_cell("<a href='" . $_SERVER['PHP_SELF'] . "?RemoveDN=".
446                                 $ln_itm->src_no."'>" . _("Remove") . "</a>", "rowspan=$dn_line_cnt class=oddrow");
447                 }
448                 $dn_line_cnt--;
449         }
450         end_row();
451 }
452
453 /*Don't re-calculate freight if some of the order has already been delivered -
454 depending on the business logic required this condition may not be required.
455 It seems unfair to charge the customer twice for freight if the order
456 was not fully delivered the first time ?? */
457
458 if (!isset($_POST['ChargeFreightCost']) || $_POST['ChargeFreightCost'] == "") {
459         if ($_SESSION['Items']->any_already_delivered() == 1) {
460                 $_POST['ChargeFreightCost'] = price_format(0);
461         } else {
462                 $_POST['ChargeFreightCost'] = price_format($_SESSION['Items']->freight_cost);
463         }
464
465         if (!check_num('ChargeFreightCost')) {
466                 $_POST['ChargeFreightCost'] = price_format(0);
467         }
468 }
469
470 $accumulate_shipping = get_company_pref('accumulate_shipping');
471 if ($is_batch_invoice && $accumulate_shipping)
472         set_delivery_shipping_sum(array_keys($_SESSION['Items']->src_docs));
473
474 start_row();
475
476 small_amount_cells(_("Shipping Cost"), 'ChargeFreightCost', null, "colspan=9 align=right");
477 if ($is_batch_invoice) {
478 label_cell('', 'colspan=2');
479 }
480
481 end_row();
482 $inv_items_total = $_SESSION['Items']->get_items_total_dispatch();
483
484 $display_sub_total = price_format($inv_items_total + input_num('ChargeFreightCost'));
485
486 label_row(_("Sub-total"), $display_sub_total, "colspan=9 align=right","align=right", $is_batch_invoice ? 2 : 0);
487
488 $taxes = $_SESSION['Items']->get_taxes(input_num('ChargeFreightCost'));
489 $tax_total = display_edit_tax_items($taxes, 9, $_SESSION['Items']->tax_included, $is_batch_invoice ? 2:0);
490
491 $display_total = price_format(($inv_items_total + input_num('ChargeFreightCost') + $tax_total));
492
493 label_row(_("Invoice Total"), $display_total, "colspan=9 align=right","align=right", $is_batch_invoice ? 2 : 0);
494
495 end_table(1);
496 div_end();
497
498 start_table($table_style2);
499 textarea_row(_("Memo"), 'Comments', null, 50, 4);
500
501 end_table(1);
502
503 submit_center_first('Update', _("Update"),
504   _('Refresh document page'), true);
505 submit_center_last('process_invoice', _("Process Invoice"),
506   _('Check entered data and save document'), true);
507
508 end_form();
509
510 end_page();
511
512 ?>