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 //---------------------------------------------------------------------------
14 // Entry/Modify Sales Invoice against single delivery
15 // Entry/Modify Batch Sales Invoice against batch of deliveries
17 $page_security = 'SA_SALESINVOICE';
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");
29 if ($use_popup_windows) {
30 $js .= get_js_open_window(900, 500);
32 if ($use_date_picker) {
33 $js .= get_js_date_picker();
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");
45 page($_SESSION['page_title'], false, false, "", $js);
47 //-----------------------------------------------------------------------------
48 check_edit_conflicts();
50 if (isset($_GET['AddedID'])) {
52 $invoice_no = $_GET['AddedID'];
53 $trans_type = ST_SALESINVOICE;
55 display_notification(_("Selected deliveries has been processed"), true);
57 display_note(get_customer_trans_view_str($trans_type, $invoice_no, _("&View This Invoice")), 0, 1);
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);
62 display_note(get_gl_view_str($trans_type, $invoice_no, _("View the GL &Journal Entries for this Invoice")),1);
64 hyperlink_params("$path_to_root/sales/inquiry/sales_deliveries_view.php", _("Select Another &Delivery For Invoicing"), "OutstandingOnly=1");
66 display_footer_exit();
68 } elseif (isset($_GET['UpdatedID'])) {
70 $invoice_no = $_GET['UpdatedID'];
71 $trans_type = ST_SALESINVOICE;
73 display_notification_centered(sprintf(_('Sales Invoice # %d has been updated.'),$invoice_no));
75 display_note(get_trans_view_str(ST_SALESINVOICE, $invoice_no, _("&View This Invoice")));
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);
80 hyperlink_no_params($path_to_root . "/sales/inquiry/customer_inquiry.php", _("Select Another &Invoice to Modify"));
82 display_footer_exit();
84 } elseif (isset($_GET['RemoveDN'])) {
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;
95 // Remove also src_doc delivery note
96 $sources = &$_SESSION['Items']->src_docs;
97 unset($sources[$_GET['RemoveDN']]);
100 //-----------------------------------------------------------------------------
102 if ( (isset($_GET['DeliveryNumber']) && ($_GET['DeliveryNumber'] > 0) )
103 || isset($_GET['BatchInvoice'])) {
107 if (isset($_GET['BatchInvoice'])) {
108 $src = $_SESSION['DeliveryBatch'];
109 unset($_SESSION['DeliveryBatch']);
111 $src = array($_GET['DeliveryNumber']);
113 /*read in all the selected deliveries into the Items cart */
114 $dn = new Cart(ST_CUSTDELIVERY, $src, true);
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>");
122 $dn->trans_type = ST_SALESINVOICE;
123 $dn->src_docs = $dn->trans_no;
125 $dn->reference = $Refs->get_next(ST_SALESINVOICE);
126 $dn->due_date = get_invoice_duedate($dn->payment, $dn->document_date);
128 $_SESSION['Items'] = $dn;
131 } elseif (isset($_GET['ModifyInvoice']) && $_GET['ModifyInvoice'] > 0) {
133 check_is_closed(ST_SALESINVOICE, $_GET['ModifyInvoice']);
135 if ( get_sales_parent_numbers(ST_SALESINVOICE, $_GET['ModifyInvoice']) == 0) { // 1.xx compatibility hack
136 echo"<center><br><b>" . _("There are no delivery notes for this invoice.<br>
137 Most likely this invoice was created in Front Accounting version prior to 2.0
138 and therefore can not be modified.") . "</b></center>";
139 display_footer_exit();
142 $_SESSION['Items'] = new Cart(ST_SALESINVOICE, $_GET['ModifyInvoice']);
144 if ($_SESSION['Items']->count_items() == 0) {
145 echo"<center><br><b>" . _("All quantities on this invoice has been credited. There is nothing to modify on this invoice") . "</b></center>";
146 display_footer_exit();
149 } elseif (!processing_active()) {
150 /* This page can only be called with a delivery for invoicing or invoice no for edit */
151 display_error(_("This page can only be opened after delivery selection. Please select delivery to invoicing first."));
153 hyperlink_no_params("$path_to_root/sales/inquiry/sales_deliveries_view.php", _("Select Delivery to Invoice"));
157 } elseif (!check_quantities()) {
158 display_error(_("Selected quantity cannot be less than quantity credited nor more than quantity not invoiced yet."));
160 if (isset($_POST['Update'])) {
161 $Ajax->activate('Items');
163 if (isset($_POST['_InvoiceDate_changed'])) {
164 $_POST['due_date'] = get_invoice_duedate($_SESSION['Items']->payment, $_POST['InvoiceDate']);
165 $Ajax->activate('due_date');
167 if (list_updated('payment')) {
168 $_SESSION['Items']->payment = get_post('payment');
169 $_POST['due_date'] = get_invoice_duedate($_SESSION['Items']->payment, $_POST['InvoiceDate']);
170 $Ajax->activate('due_date');
173 //-----------------------------------------------------------------------------
174 function check_quantities()
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;
184 $max = $itm->quantity - $itm->qty_done;
186 if (check_num('Line'.$line_no, $min, $max)) {
187 $_SESSION['Items']->line_items[$line_no]->qty_dispatched =
188 input_num('Line'.$line_no);
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;
206 function set_delivery_shipping_sum($delivery_notes)
211 foreach($delivery_notes as $delivery_num)
213 $myrow = get_customer_trans($delivery_num, 13);
214 //$branch = get_branch($myrow["branch_code"]);
215 //$sales_order = get_sales_order_header($myrow["order_"]);
217 //$shipping += $sales_order['freight_cost'];
218 $shipping += $myrow['ov_freight'];
220 $_POST['ChargeFreightCost'] = price_format($shipping);
224 function copy_to_cart()
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['cash_sale'] || $cart->pos['credit_sale']) {
232 $cart->payment = $_POST['payment'];
233 $cart->payment_terms = get_payment_terms($_POST['payment']);
235 $cart->Comments = $_POST['Comments'];
236 if ($_SESSION['Items']->trans_no == 0)
237 $cart->reference = $_POST['ref'];
240 //-----------------------------------------------------------------------------
242 function copy_from_cart()
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;
255 //-----------------------------------------------------------------------------
257 function check_data()
261 if (!isset($_POST['InvoiceDate']) || !is_date($_POST['InvoiceDate'])) {
262 display_error(_("The entered invoice date is invalid."));
263 set_focus('InvoiceDate');
267 if (!is_date_in_fiscalyear($_POST['InvoiceDate'])) {
268 display_error(_("The entered date is out of fiscal year or is closed for further data entry."));
269 set_focus('InvoiceDate');
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');
279 if ($_SESSION['Items']->trans_no == 0) {
280 if (!$Refs->is_valid($_POST['ref'])) {
281 display_error(_("You must enter a reference."));
286 if (!is_new_reference($_POST['ref'], 10)) {
287 display_error(_("The entered reference is already in use."));
293 if ($_POST['ChargeFreightCost'] == "") {
294 $_POST['ChargeFreightCost'] = price_format(0);
297 if (!check_num('ChargeFreightCost', 0)) {
298 display_error(_("The entered shipping value is not numeric."));
299 set_focus('ChargeFreightCost');
303 if ($_SESSION['Items']->has_items_dispatch() == 0 && input_num('ChargeFreightCost') == 0) {
304 display_error(_("There are no item quantities on this invoice."));
308 if (!check_quantities()) {
309 display_error(_("Selected quantity cannot be less than quantity credited nor more than quantity not invoiced yet."));
316 //-----------------------------------------------------------------------------
317 if (isset($_POST['process_invoice']) && check_data()) {
319 $newinvoice= $_SESSION['Items']->trans_no == 0;
321 if ($newinvoice) new_doc_date($_SESSION['Items']->document_date);
323 $invoice_no = $_SESSION['Items']->write();
327 meta_forward($_SERVER['PHP_SELF'], "AddedID=$invoice_no");
329 meta_forward($_SERVER['PHP_SELF'], "UpdatedID=$invoice_no");
333 // find delivery spans for batch invoice display
335 $lastdn = ''; $spanlen=1;
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) {
342 if ($line->src_no == $lastdn) {
346 $dspans[] = $spanlen;
350 $lastdn = $line->src_no;
352 $dspans[] = $spanlen;
354 //-----------------------------------------------------------------------------
356 $is_batch_invoice = count($_SESSION['Items']->src_docs) > 1;
358 $is_edition = $_SESSION['Items']->trans_type == ST_SALESINVOICE && $_SESSION['Items']->trans_no != 0;
362 start_table(TABLESTYLE2, "width=80%", 5);
366 $dim = get_company_pref('use_dimension');
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['credit_sale'] || $_SESSION['Items']->pos['cash_sale']) {
372 // editable payment type
373 $paymcat = !$_SESSION['Items']->pos['cash_sale'] ? PM_CREDIT :
374 (!$_SESSION['Items']->pos['credit_sale'] ? PM_CASH : PM_ANY);
375 label_cells(_("Payment terms:"), sale_payment_list('payment', $paymcat),
376 "class='tableheader2'", "colspan=$colspan");
378 label_cells(_('Payment:'), $_SESSION['Items']->payment_terms['terms'], "class='tableheader2'", "colspan=$colspan");
383 if ($_SESSION['Items']->trans_no == 0) {
384 ref_cells(_("Reference"), 'ref', '', null, "class='tableheader2'");
386 label_cells(_("Reference"), $_SESSION['Items']->reference, "class='tableheader2'");
389 //label_cells(_("Delivery Notes:"),
390 //get_customer_trans_view_str(ST_CUSTDELIVERY, array_keys($_SESSION['Items']->src_docs)), "class='tableheader2'");
392 label_cells(_("Sales Type"), $_SESSION['Items']->sales_type_name, "class='tableheader2'");
394 label_cells(_("Currency"), $_SESSION['Items']->customer_currency, "class='tableheader2'");
395 // 2010-09-03 Joe Hunt
397 label_cells(_("Dimension"), get_dimension_string($_SESSION['Items']->dimension_id), "class='tableheader2'");
402 if (!isset($_POST['ship_via'])) {
403 $_POST['ship_via'] = $_SESSION['Items']->ship_via;
405 label_cell(_("Shipping Company"), "class='tableheader2'");
406 shippers_list_cells(null, 'ship_via', $_POST['ship_via']);
408 if (!isset($_POST['InvoiceDate']) || !is_date($_POST['InvoiceDate'])) {
409 $_POST['InvoiceDate'] = new_doc_date();
410 if (!is_date_in_fiscalyear($_POST['InvoiceDate'])) {
411 $_POST['InvoiceDate'] = end_fiscalyear();
415 date_cells(_("Date"), 'InvoiceDate', '', $_SESSION['Items']->trans_no == 0,
416 0, 0, 0, "class='tableheader2'", true);
418 if (!isset($_POST['due_date']) || !is_date($_POST['due_date'])) {
419 $_POST['due_date'] = get_invoice_duedate($_SESSION['Items']->payment, $_POST['InvoiceDate']);
422 date_cells(_("Due Date"), 'due_date', '', null, 0, 0, 0, "class='tableheader2'");
424 label_cells(_("Dimension"). " 2", get_dimension_string($_SESSION['Items']->dimension2_id), "class='tableheader2'");
426 label_cell(" ", "colspan=2");
431 $row = get_customer_to_order($_SESSION['Items']->customer_id);
432 if ($row['dissallow_invoices'] == 1)
434 display_error(_("The selected customer account is currently on hold. Please contact the credit control personnel to discuss."));
440 display_heading(_("Invoice Items"));
443 start_table(TABLESTYLE, "width=80%");
444 $th = array(_("Item Code"), _("Item Description"), _("Delivered"), _("Units"), _("Invoiced"),
445 _("This Invoice"), _("Price"), _("Tax Type"), _("Discount"), _("Total"));
447 if ($is_batch_invoice) {
453 $th[4] = _("Credited");
463 foreach ($_SESSION['Items']->line_items as $line=>$ln_itm) {
464 if ($ln_itm->quantity == $ln_itm->qty_done) {
465 continue; // this line was fully invoiced
467 alt_table_row_color($k);
468 view_stock_status_cell($ln_itm->stock_id);
470 text_cells(null, 'Line'.$line.'Desc', $ln_itm->item_description, 30, 50);
471 $dec = get_qty_dec($ln_itm->stock_id);
472 qty_cell($ln_itm->quantity, false, $dec);
473 label_cell($ln_itm->units);
474 qty_cell($ln_itm->qty_done, false, $dec);
476 if ($is_batch_invoice) {
477 // for batch invoices we can only remove whole deliveries
478 echo '<td nowrap align=right>';
479 hidden('Line' . $line, $ln_itm->qty_dispatched );
480 echo number_format2($ln_itm->qty_dispatched, $dec).'</td>';
482 small_qty_cells(null, 'Line'.$line, qty_format($ln_itm->qty_dispatched, $ln_itm->stock_id, $dec), null, null, $dec);
484 $display_discount_percent = percent_format($ln_itm->discount_percent*100) . " %";
486 $line_total = ($ln_itm->qty_dispatched * $ln_itm->price * (1 - $ln_itm->discount_percent));
488 amount_cell($ln_itm->price);
489 label_cell($ln_itm->tax_type_name);
490 label_cell($display_discount_percent, "nowrap align=right");
491 amount_cell($line_total);
493 if ($is_batch_invoice) {
494 if ($dn_line_cnt == 0) {
495 $dn_line_cnt = $dspans[0];
496 $dspans = array_slice($dspans, 1);
497 label_cell($ln_itm->src_no, "rowspan=$dn_line_cnt class=oddrow");
498 label_cell("<a href='" . $_SERVER['PHP_SELF'] . "?RemoveDN=".
499 $ln_itm->src_no."'>" . _("Remove") . "</a>", "rowspan=$dn_line_cnt class=oddrow");
506 /*Don't re-calculate freight if some of the order has already been delivered -
507 depending on the business logic required this condition may not be required.
508 It seems unfair to charge the customer twice for freight if the order
509 was not fully delivered the first time ?? */
511 if (!isset($_POST['ChargeFreightCost']) || $_POST['ChargeFreightCost'] == "") {
512 if ($_SESSION['Items']->any_already_delivered() == 1) {
513 $_POST['ChargeFreightCost'] = price_format(0);
515 $_POST['ChargeFreightCost'] = price_format($_SESSION['Items']->freight_cost);
518 if (!check_num('ChargeFreightCost')) {
519 $_POST['ChargeFreightCost'] = price_format(0);
523 $accumulate_shipping = get_company_pref('accumulate_shipping');
524 if ($is_batch_invoice && $accumulate_shipping)
525 set_delivery_shipping_sum(array_keys($_SESSION['Items']->src_docs));
529 label_cell(_("Shipping Cost"), "colspan=$colspan align=right");
530 small_amount_cells(null, 'ChargeFreightCost', null);
531 if ($is_batch_invoice) {
532 label_cell('', 'colspan=2');
536 $inv_items_total = $_SESSION['Items']->get_items_total_dispatch();
538 $display_sub_total = price_format($inv_items_total + input_num('ChargeFreightCost'));
540 label_row(_("Sub-total"), $display_sub_total, "colspan=$colspan align=right","align=right", $is_batch_invoice ? 2 : 0);
542 $taxes = $_SESSION['Items']->get_taxes(input_num('ChargeFreightCost'));
543 $tax_total = display_edit_tax_items($taxes, $colspan, $_SESSION['Items']->tax_included, $is_batch_invoice ? 2:0);
545 $display_total = price_format(($inv_items_total + input_num('ChargeFreightCost') + $tax_total));
547 label_row(_("Invoice Total"), $display_total, "colspan=$colspan align=right","align=right", $is_batch_invoice ? 2 : 0);
552 start_table(TABLESTYLE2);
553 textarea_row(_("Memo"), 'Comments', null, 50, 4);
557 submit_center_first('Update', _("Update"),
558 _('Refresh document page'), true);
559 submit_center_last('process_invoice', _("Process Invoice"),
560 _('Check entered data and save document'), 'default');