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