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 if(list_updated('payment')) {
327 $order = &$_SESSION['Items'];
328 $order->payment = get_post('payment');
329 $order->payment_terms = get_payment_terms($order->payment);
330 $order->due_date = get_invoice_duedate($order->payment, $order->document_date);
331 if ($order->payment_terms['cash_sale']) {
332 $_POST['Location'] = $order->Location = $order->pos['pos_location'];
333 $order->location_name = $order->pos['location_name'];
336 // find delivery spans for batch invoice display
338 $lastdn = ''; $spanlen=1;
340 for ($line_no = 0; $line_no < count($_SESSION['Items']->line_items); $line_no++) {
341 $line = $_SESSION['Items']->line_items[$line_no];
342 if ($line->quantity == $line->qty_done) {
345 if ($line->src_no == $lastdn) {
349 $dspans[] = $spanlen;
353 $lastdn = $line->src_no;
355 $dspans[] = $spanlen;
357 //-----------------------------------------------------------------------------
359 $is_batch_invoice = count($_SESSION['Items']->src_docs) > 1;
361 $is_edition = $_SESSION['Items']->trans_type == ST_SALESINVOICE && $_SESSION['Items']->trans_no != 0;
365 start_table(TABLESTYLE2, "width=80%", 5);
369 $dim = get_company_pref('use_dimension');
372 label_cells(_("Customer"), $_SESSION['Items']->customer_name, "class='tableheader2'");
373 label_cells(_("Branch"), get_branch_name($_SESSION['Items']->Branch), "class='tableheader2'");
374 if ($_SESSION['Items']->pos['credit_sale'] || $_SESSION['Items']->pos['cash_sale']) {
375 $paymcat = !$_SESSION['Items']->pos['cash_sale'] ? PM_CREDIT :
376 (!$_SESSION['Items']->pos['credit_sale'] ? PM_CASH : PM_ANY);
377 label_cells(_("Payment terms:"), sale_payment_list('payment', $paymcat),
378 "class='tableheader2'", "colspan=$colspan");
380 label_cells(_('Payment:'), $_SESSION['Items']->payment_terms['terms'], "class='tableheader2'", "colspan=$colspan");
385 if ($_SESSION['Items']->trans_no == 0) {
386 ref_cells(_("Reference"), 'ref', '', null, "class='tableheader2'");
388 label_cells(_("Reference"), $_SESSION['Items']->reference, "class='tableheader2'");
391 //label_cells(_("Delivery Notes:"),
392 //get_customer_trans_view_str(ST_CUSTDELIVERY, array_keys($_SESSION['Items']->src_docs)), "class='tableheader2'");
394 label_cells(_("Sales Type"), $_SESSION['Items']->sales_type_name, "class='tableheader2'");
396 label_cells(_("Currency"), $_SESSION['Items']->customer_currency, "class='tableheader2'");
397 // 2010-09-03 Joe Hunt
399 label_cells(_("Dimension"), get_dimension_string($_SESSION['Items']->dimension_id), "class='tableheader2'");
404 if (!isset($_POST['ship_via'])) {
405 $_POST['ship_via'] = $_SESSION['Items']->ship_via;
407 label_cell(_("Shipping Company"), "class='tableheader2'");
408 shippers_list_cells(null, 'ship_via', $_POST['ship_via']);
410 if (!isset($_POST['InvoiceDate']) || !is_date($_POST['InvoiceDate'])) {
411 $_POST['InvoiceDate'] = new_doc_date();
412 if (!is_date_in_fiscalyear($_POST['InvoiceDate'])) {
413 $_POST['InvoiceDate'] = end_fiscalyear();
417 date_cells(_("Date"), 'InvoiceDate', '', $_SESSION['Items']->trans_no == 0,
418 0, 0, 0, "class='tableheader2'", true);
420 if (!isset($_POST['due_date']) || !is_date($_POST['due_date'])) {
421 $_POST['due_date'] = get_invoice_duedate($_SESSION['Items']->payment, $_POST['InvoiceDate']);
424 date_cells(_("Due Date"), 'due_date', '', null, 0, 0, 0, "class='tableheader2'");
426 label_cells(_("Dimension"). " 2", get_dimension_string($_SESSION['Items']->dimension2_id), "class='tableheader2'");
428 label_cell(" ", "colspan=2");
433 $row = get_customer_to_order($_SESSION['Items']->customer_id);
434 if ($row['dissallow_invoices'] == 1)
436 display_error(_("The selected customer account is currently on hold. Please contact the credit control personnel to discuss."));
442 display_heading(_("Invoice Items"));
445 start_table(TABLESTYLE, "width=80%");
446 $th = array(_("Item Code"), _("Item Description"), _("Delivered"), _("Units"), _("Invoiced"),
447 _("This Invoice"), _("Price"), _("Tax Type"), _("Discount"), _("Total"));
449 if ($is_batch_invoice) {
455 $th[4] = _("Credited");
465 foreach ($_SESSION['Items']->line_items as $line=>$ln_itm) {
466 if ($ln_itm->quantity == $ln_itm->qty_done) {
467 continue; // this line was fully invoiced
469 alt_table_row_color($k);
470 view_stock_status_cell($ln_itm->stock_id);
472 text_cells(null, 'Line'.$line.'Desc', $ln_itm->item_description, 30, 50);
473 $dec = get_qty_dec($ln_itm->stock_id);
474 qty_cell($ln_itm->quantity, false, $dec);
475 label_cell($ln_itm->units);
476 qty_cell($ln_itm->qty_done, false, $dec);
478 if ($is_batch_invoice) {
479 // for batch invoices we can only remove whole deliveries
480 echo '<td nowrap align=right>';
481 hidden('Line' . $line, $ln_itm->qty_dispatched );
482 echo number_format2($ln_itm->qty_dispatched, $dec).'</td>';
484 small_qty_cells(null, 'Line'.$line, qty_format($ln_itm->qty_dispatched, $ln_itm->stock_id, $dec), null, null, $dec);
486 $display_discount_percent = percent_format($ln_itm->discount_percent*100) . " %";
488 $line_total = ($ln_itm->qty_dispatched * $ln_itm->price * (1 - $ln_itm->discount_percent));
490 amount_cell($ln_itm->price);
491 label_cell($ln_itm->tax_type_name);
492 label_cell($display_discount_percent, "nowrap align=right");
493 amount_cell($line_total);
495 if ($is_batch_invoice) {
496 if ($dn_line_cnt == 0) {
497 $dn_line_cnt = $dspans[0];
498 $dspans = array_slice($dspans, 1);
499 label_cell($ln_itm->src_no, "rowspan=$dn_line_cnt class=oddrow");
500 label_cell("<a href='" . $_SERVER['PHP_SELF'] . "?RemoveDN=".
501 $ln_itm->src_no."'>" . _("Remove") . "</a>", "rowspan=$dn_line_cnt class=oddrow");
508 /*Don't re-calculate freight if some of the order has already been delivered -
509 depending on the business logic required this condition may not be required.
510 It seems unfair to charge the customer twice for freight if the order
511 was not fully delivered the first time ?? */
513 if (!isset($_POST['ChargeFreightCost']) || $_POST['ChargeFreightCost'] == "") {
514 if ($_SESSION['Items']->any_already_delivered() == 1) {
515 $_POST['ChargeFreightCost'] = price_format(0);
517 $_POST['ChargeFreightCost'] = price_format($_SESSION['Items']->freight_cost);
520 if (!check_num('ChargeFreightCost')) {
521 $_POST['ChargeFreightCost'] = price_format(0);
525 $accumulate_shipping = get_company_pref('accumulate_shipping');
526 if ($is_batch_invoice && $accumulate_shipping)
527 set_delivery_shipping_sum(array_keys($_SESSION['Items']->src_docs));
531 label_cell(_("Shipping Cost"), "colspan=$colspan align=right");
532 small_amount_cells(null, 'ChargeFreightCost', null);
533 if ($is_batch_invoice) {
534 label_cell('', 'colspan=2');
538 $inv_items_total = $_SESSION['Items']->get_items_total_dispatch();
540 $display_sub_total = price_format($inv_items_total + input_num('ChargeFreightCost'));
542 label_row(_("Sub-total"), $display_sub_total, "colspan=$colspan align=right","align=right", $is_batch_invoice ? 2 : 0);
544 $taxes = $_SESSION['Items']->get_taxes(input_num('ChargeFreightCost'));
545 $tax_total = display_edit_tax_items($taxes, $colspan, $_SESSION['Items']->tax_included, $is_batch_invoice ? 2:0);
547 $display_total = price_format(($inv_items_total + input_num('ChargeFreightCost') + $tax_total));
549 label_row(_("Invoice Total"), $display_total, "colspan=$colspan align=right","align=right", $is_batch_invoice ? 2 : 0);
554 start_table(TABLESTYLE2);
555 textarea_row(_("Memo"), 'Comments', null, 50, 4);
559 submit_center_first('Update', _("Update"),
560 _('Refresh document page'), true);
561 submit_center_last('process_invoice', _("Process Invoice"),
562 _('Check entered data and save document'), 'default');