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']);
114 /*read in all the selected deliveries into the Items cart */
115 $dn = new Cart(ST_CUSTDELIVERY, $src, true);
117 if ($dn->count_items() == 0) {
118 hyperlink_params($path_to_root . "/sales/inquiry/sales_deliveries_view.php",
119 _("Select a different delivery to invoice"), "OutstandingOnly=1");
120 die ("<br><b>" . _("There are no delivered items with a quantity left to invoice. There is nothing left to invoice.") . "</b>");
123 $_SESSION['Items'] = $dn;
126 } elseif (isset($_GET['ModifyInvoice']) && $_GET['ModifyInvoice'] > 0) {
128 if ( get_sales_parent_numbers(ST_SALESINVOICE, $_GET['ModifyInvoice']) == 0) { // 1.xx compatibility hack
129 echo"<center><br><b>" . _("There are no delivery notes for this invoice.<br>
130 Most likely this invoice was created in Front Accounting version prior to 2.0
131 and therefore can not be modified.") . "</b></center>";
132 display_footer_exit();
135 $_SESSION['Items'] = new Cart(ST_SALESINVOICE, $_GET['ModifyInvoice']);
137 if ($_SESSION['Items']->count_items() == 0) {
138 echo"<center><br><b>" . _("All quantities on this invoice has been credited. There is nothing to modify on this invoice") . "</b></center>";
139 display_footer_exit();
142 } elseif (!processing_active()) {
143 /* This page can only be called with a delivery for invoicing or invoice no for edit */
144 display_error(_("This page can only be opened after delivery selection. Please select delivery to invoicing first."));
146 hyperlink_no_params("$path_to_root/sales/inquiry/sales_deliveries_view.php", _("Select Delivery to Invoice"));
150 } elseif (!check_quantities()) {
151 display_error(_("Selected quantity cannot be less than quantity credited nor more than quantity not invoiced yet."));
153 if (isset($_POST['Update'])) {
154 $Ajax->activate('Items');
156 if (isset($_POST['_InvoiceDate_changed'])) {
157 $_POST['due_date'] = get_invoice_duedate($_SESSION['Items']->payment, $_POST['InvoiceDate']);
158 $Ajax->activate('due_date');
160 if (list_updated('payment')) {
161 $_SESSION['Items']->payment = get_post('payment');
162 $_POST['due_date'] = get_invoice_duedate($_SESSION['Items']->payment, $_POST['InvoiceDate']);
163 $Ajax->activate('due_date');
166 //-----------------------------------------------------------------------------
167 function check_quantities()
170 foreach ($_SESSION['Items']->line_items as $line_no=>$itm) {
171 if (isset($_POST['Line'.$line_no])) {
172 if($_SESSION['Items']->trans_no) {
173 $min = $itm->qty_done;
174 $max = $itm->quantity;
177 $max = $itm->quantity - $itm->qty_done;
179 if (check_num('Line'.$line_no, $min, $max)) {
180 $_SESSION['Items']->line_items[$line_no]->qty_dispatched =
181 input_num('Line'.$line_no);
189 if (isset($_POST['Line'.$line_no.'Desc'])) {
190 $line_desc = $_POST['Line'.$line_no.'Desc'];
191 if (strlen($line_desc) > 0) {
192 $_SESSION['Items']->line_items[$line_no]->item_description = $line_desc;
199 function set_delivery_shipping_sum($delivery_notes)
204 foreach($delivery_notes as $delivery_num)
206 $myrow = get_customer_trans($delivery_num, 13);
207 //$branch = get_branch($myrow["branch_code"]);
208 //$sales_order = get_sales_order_header($myrow["order_"]);
210 //$shipping += $sales_order['freight_cost'];
211 $shipping += $myrow['ov_freight'];
213 $_POST['ChargeFreightCost'] = price_format($shipping);
217 function copy_to_cart()
219 $cart = &$_SESSION['Items'];
220 $cart->ship_via = $_POST['ship_via'];
221 $cart->freight_cost = input_num('ChargeFreightCost');
222 $cart->document_date = $_POST['InvoiceDate'];
223 $cart->due_date = $_POST['due_date'];
224 if ($cart->pos['cash_sale'] || $cart->pos['credit_sale']) {
225 $cart->payment = $_POST['payment'];
226 $cart->payment_terms = get_payment_terms($_POST['payment']);
228 $cart->Comments = $_POST['Comments'];
229 if ($_SESSION['Items']->trans_no == 0)
230 $cart->reference = $_POST['ref'];
233 //-----------------------------------------------------------------------------
235 function copy_from_cart()
237 $cart = &$_SESSION['Items'];
238 $_POST['ship_via'] = $cart->ship_via;
239 $_POST['ChargeFreightCost'] = price_format($cart->freight_cost);
240 $_POST['InvoiceDate']= $cart->document_date;
241 $_POST['due_date'] = $cart->due_date;
242 $_POST['Comments']= $cart->Comments;
243 $_POST['cart_id'] = $cart->cart_id;
244 $_POST['ref'] = $cart->reference;
245 $_POST['payment'] = $cart->payment;
248 //-----------------------------------------------------------------------------
250 function check_data()
254 if (!isset($_POST['InvoiceDate']) || !is_date($_POST['InvoiceDate'])) {
255 display_error(_("The entered invoice date is invalid."));
256 set_focus('InvoiceDate');
260 if (!is_date_in_fiscalyear($_POST['InvoiceDate'])) {
261 display_error(_("The entered invoice date is not in fiscal year."));
262 set_focus('InvoiceDate');
266 if (!isset($_POST['due_date']) || !is_date($_POST['due_date'])) {
267 display_error(_("The entered invoice due date is invalid."));
268 set_focus('due_date');
272 if ($_SESSION['Items']->trans_no == 0) {
273 if (!$Refs->is_valid($_POST['ref'])) {
274 display_error(_("You must enter a reference."));
279 if (!is_new_reference($_POST['ref'], 10)) {
280 display_error(_("The entered reference is already in use."));
286 if ($_POST['ChargeFreightCost'] == "") {
287 $_POST['ChargeFreightCost'] = price_format(0);
290 if (!check_num('ChargeFreightCost', 0)) {
291 display_error(_("The entered shipping value is not numeric."));
292 set_focus('ChargeFreightCost');
296 if ($_SESSION['Items']->has_items_dispatch() == 0 && input_num('ChargeFreightCost') == 0) {
297 display_error(_("There are no item quantities on this invoice."));
301 if (!check_quantities()) {
302 display_error(_("Selected quantity cannot be less than quantity credited nor more than quantity not invoiced yet."));
309 //-----------------------------------------------------------------------------
310 if (isset($_POST['process_invoice']) && check_data()) {
312 $newinvoice= $_SESSION['Items']->trans_no == 0;
314 if ($newinvoice) new_doc_date($_SESSION['Items']->document_date);
316 $invoice_no = $_SESSION['Items']->write();
320 meta_forward($_SERVER['PHP_SELF'], "AddedID=$invoice_no");
322 meta_forward($_SERVER['PHP_SELF'], "UpdatedID=$invoice_no");
326 // find delivery spans for batch invoice display
328 $lastdn = ''; $spanlen=1;
330 for ($line_no = 0; $line_no < count($_SESSION['Items']->line_items); $line_no++) {
331 $line = $_SESSION['Items']->line_items[$line_no];
332 if ($line->quantity == $line->qty_done) {
335 if ($line->src_no == $lastdn) {
339 $dspans[] = $spanlen;
343 $lastdn = $line->src_no;
345 $dspans[] = $spanlen;
347 //-----------------------------------------------------------------------------
349 $is_batch_invoice = count($_SESSION['Items']->src_docs) > 1;
351 $is_edition = $_SESSION['Items']->trans_type == ST_SALESINVOICE && $_SESSION['Items']->trans_no != 0;
355 start_table(TABLESTYLE2, "width=80%", 5);
359 $dim = get_company_pref('use_dimension');
362 label_cells(_("Customer"), $_SESSION['Items']->customer_name, "class='tableheader2'");
363 label_cells(_("Branch"), get_branch_name($_SESSION['Items']->Branch), "class='tableheader2'");
364 if ($_SESSION['Items']->pos['credit_sale'] || $_SESSION['Items']->pos['cash_sale']) {
365 // editable payment type
366 $paymcat = !$_SESSION['Items']->pos['cash_sale'] ? PM_CREDIT :
367 (!$_SESSION['Items']->pos['credit_sale'] ? PM_CASH : PM_ANY);
368 label_cells(_("Payment terms:"), sale_payment_list('payment', $paymcat),
369 "class='tableheader2'", "colspan=$colspan");
371 label_cells(_('Payment:'), $_SESSION['Items']->payment_terms['terms'], "class='tableheader2'", "colspan=$colspan");
376 if ($_SESSION['Items']->trans_no == 0) {
377 ref_cells(_("Reference"), 'ref', '', null, "class='tableheader2'");
379 label_cells(_("Reference"), $_SESSION['Items']->reference, "class='tableheader2'");
382 //label_cells(_("Delivery Notes:"),
383 //get_customer_trans_view_str(ST_CUSTDELIVERY, array_keys($_SESSION['Items']->src_docs)), "class='tableheader2'");
385 label_cells(_("Sales Type"), $_SESSION['Items']->sales_type_name, "class='tableheader2'");
387 label_cells(_("Currency"), $_SESSION['Items']->customer_currency, "class='tableheader2'");
388 // 2010-09-03 Joe Hunt
390 label_cells(_("Dimension"), get_dimension_string($_SESSION['Items']->dimension_id), "class='tableheader2'");
395 if (!isset($_POST['ship_via'])) {
396 $_POST['ship_via'] = $_SESSION['Items']->ship_via;
398 label_cell(_("Shipping Company"), "class='tableheader2'");
399 shippers_list_cells(null, 'ship_via', $_POST['ship_via']);
401 if (!isset($_POST['InvoiceDate']) || !is_date($_POST['InvoiceDate'])) {
402 $_POST['InvoiceDate'] = new_doc_date();
403 if (!is_date_in_fiscalyear($_POST['InvoiceDate'])) {
404 $_POST['InvoiceDate'] = end_fiscalyear();
408 date_cells(_("Date"), 'InvoiceDate', '', $_SESSION['Items']->trans_no == 0,
409 0, 0, 0, "class='tableheader2'", true);
411 if (!isset($_POST['due_date']) || !is_date($_POST['due_date'])) {
412 $_POST['due_date'] = get_invoice_duedate($_SESSION['Items']->payment, $_POST['InvoiceDate']);
415 date_cells(_("Due Date"), 'due_date', '', null, 0, 0, 0, "class='tableheader2'");
417 label_cells(_("Dimension"). " 2", get_dimension_string($_SESSION['Items']->dimension2_id), "class='tableheader2'");
419 label_cell(" ", "colspan=2");
424 $row = get_customer_to_order($_SESSION['Items']->customer_id);
425 if ($row['dissallow_invoices'] == 1)
427 display_error(_("The selected customer account is currently on hold. Please contact the credit control personnel to discuss."));
433 display_heading(_("Invoice Items"));
436 start_table(TABLESTYLE, "width=80%");
437 $th = array(_("Item Code"), _("Item Description"), _("Delivered"), _("Units"), _("Invoiced"),
438 _("This Invoice"), _("Price"), _("Tax Type"), _("Discount"), _("Total"));
440 if ($is_batch_invoice) {
446 $th[4] = _("Credited");
456 foreach ($_SESSION['Items']->line_items as $line=>$ln_itm) {
457 if ($ln_itm->quantity == $ln_itm->qty_done) {
458 continue; // this line was fully invoiced
460 alt_table_row_color($k);
461 view_stock_status_cell($ln_itm->stock_id);
463 text_cells(null, 'Line'.$line.'Desc', $ln_itm->item_description, 30, 50);
464 $dec = get_qty_dec($ln_itm->stock_id);
465 qty_cell($ln_itm->quantity, false, $dec);
466 label_cell($ln_itm->units);
467 qty_cell($ln_itm->qty_done, false, $dec);
469 if ($is_batch_invoice) {
470 // for batch invoices we can only remove whole deliveries
471 echo '<td nowrap align=right>';
472 hidden('Line' . $line, $ln_itm->qty_dispatched );
473 echo number_format2($ln_itm->qty_dispatched, $dec).'</td>';
475 small_qty_cells(null, 'Line'.$line, qty_format($ln_itm->qty_dispatched, $ln_itm->stock_id, $dec), null, null, $dec);
477 $display_discount_percent = percent_format($ln_itm->discount_percent*100) . " %";
479 $line_total = ($ln_itm->qty_dispatched * $ln_itm->price * (1 - $ln_itm->discount_percent));
481 amount_cell($ln_itm->price);
482 label_cell($ln_itm->tax_type_name);
483 label_cell($display_discount_percent, "nowrap align=right");
484 amount_cell($line_total);
486 if ($is_batch_invoice) {
487 if ($dn_line_cnt == 0) {
488 $dn_line_cnt = $dspans[0];
489 $dspans = array_slice($dspans, 1);
490 label_cell($ln_itm->src_no, "rowspan=$dn_line_cnt class=oddrow");
491 label_cell("<a href='" . $_SERVER['PHP_SELF'] . "?RemoveDN=".
492 $ln_itm->src_no."'>" . _("Remove") . "</a>", "rowspan=$dn_line_cnt class=oddrow");
499 /*Don't re-calculate freight if some of the order has already been delivered -
500 depending on the business logic required this condition may not be required.
501 It seems unfair to charge the customer twice for freight if the order
502 was not fully delivered the first time ?? */
504 if (!isset($_POST['ChargeFreightCost']) || $_POST['ChargeFreightCost'] == "") {
505 if ($_SESSION['Items']->any_already_delivered() == 1) {
506 $_POST['ChargeFreightCost'] = price_format(0);
508 $_POST['ChargeFreightCost'] = price_format($_SESSION['Items']->freight_cost);
511 if (!check_num('ChargeFreightCost')) {
512 $_POST['ChargeFreightCost'] = price_format(0);
516 $accumulate_shipping = get_company_pref('accumulate_shipping');
517 if ($is_batch_invoice && $accumulate_shipping)
518 set_delivery_shipping_sum(array_keys($_SESSION['Items']->src_docs));
522 label_cell(_("Shipping Cost"), "colspan=$colspan align=right");
523 small_amount_cells(null, 'ChargeFreightCost', null);
524 if ($is_batch_invoice) {
525 label_cell('', 'colspan=2');
529 $inv_items_total = $_SESSION['Items']->get_items_total_dispatch();
531 $display_sub_total = price_format($inv_items_total + input_num('ChargeFreightCost'));
533 label_row(_("Sub-total"), $display_sub_total, "colspan=$colspan align=right","align=right", $is_batch_invoice ? 2 : 0);
535 $taxes = $_SESSION['Items']->get_taxes(input_num('ChargeFreightCost'));
536 $tax_total = display_edit_tax_items($taxes, $colspan, $_SESSION['Items']->tax_included, $is_batch_invoice ? 2:0);
538 $display_total = price_format(($inv_items_total + input_num('ChargeFreightCost') + $tax_total));
540 label_row(_("Invoice Total"), $display_total, "colspan=$colspan align=right","align=right", $is_batch_invoice ? 2 : 0);
545 start_table(TABLESTYLE2);
546 textarea_row(_("Memo"), 'Comments', null, 50, 4);
550 submit_center_first('Update', _("Update"),
551 _('Refresh document page'), true);
552 submit_center_last('process_invoice', _("Process Invoice"),
553 _('Check entered data and save document'), 'default');