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