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 hyperlink_params("$path_to_root/admin/attachments.php", _("Add an Attachment"), "filterType=$trans_type&trans_no=$invoice_no");
68 display_footer_exit();
70 } elseif (isset($_GET['UpdatedID'])) {
72 $invoice_no = $_GET['UpdatedID'];
73 $trans_type = ST_SALESINVOICE;
75 display_notification_centered(sprintf(_('Sales Invoice # %d has been updated.'),$invoice_no));
77 display_note(get_trans_view_str(ST_SALESINVOICE, $invoice_no, _("&View This Invoice")));
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);
82 hyperlink_no_params($path_to_root . "/sales/inquiry/customer_inquiry.php", _("Select Another &Invoice to Modify"));
84 display_footer_exit();
86 } elseif (isset($_GET['RemoveDN'])) {
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;
97 // Remove also src_doc delivery note
98 $sources = &$_SESSION['Items']->src_docs;
99 unset($sources[$_GET['RemoveDN']]);
102 //-----------------------------------------------------------------------------
104 if ( (isset($_GET['DeliveryNumber']) && ($_GET['DeliveryNumber'] > 0) )
105 || isset($_GET['BatchInvoice'])) {
109 if (isset($_GET['BatchInvoice'])) {
110 $src = $_SESSION['DeliveryBatch'];
111 unset($_SESSION['DeliveryBatch']);
113 $src = array($_GET['DeliveryNumber']);
116 /*read in all the selected deliveries into the Items cart */
117 $dn = new Cart(ST_CUSTDELIVERY, $src, true);
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>");
125 $_SESSION['Items'] = $dn;
128 } elseif (isset($_GET['ModifyInvoice']) && $_GET['ModifyInvoice'] > 0) {
130 check_is_closed(ST_SALESINVOICE, $_GET['ModifyInvoice']);
132 if ( get_sales_parent_numbers(ST_SALESINVOICE, $_GET['ModifyInvoice']) == 0) { // 1.xx compatibility hack
133 echo"<center><br><b>" . _("There are no delivery notes for this invoice.<br>
134 Most likely this invoice was created in Front Accounting version prior to 2.0
135 and therefore can not be modified.") . "</b></center>";
136 display_footer_exit();
139 $_SESSION['Items'] = new Cart(ST_SALESINVOICE, $_GET['ModifyInvoice']);
141 if ($_SESSION['Items']->count_items() == 0) {
142 echo"<center><br><b>" . _("All quantities on this invoice has been credited. There is nothing to modify on this invoice") . "</b></center>";
143 display_footer_exit();
146 } elseif (!processing_active()) {
147 /* This page can only be called with a delivery for invoicing or invoice no for edit */
148 display_error(_("This page can only be opened after delivery selection. Please select delivery to invoicing first."));
150 hyperlink_no_params("$path_to_root/sales/inquiry/sales_deliveries_view.php", _("Select Delivery to Invoice"));
154 } elseif (!isset($_POST['process_invoice']) && !check_quantities()) {
155 display_error(_("Selected quantity cannot be less than quantity credited nor more than quantity not invoiced yet."));
157 if (isset($_POST['Update'])) {
158 $Ajax->activate('Items');
160 if (isset($_POST['_InvoiceDate_changed'])) {
161 $_POST['due_date'] = get_invoice_duedate($_SESSION['Items']->payment, $_POST['InvoiceDate']);
162 $Ajax->activate('due_date');
164 if (list_updated('payment')) {
165 $_SESSION['Items']->payment = get_post('payment');
166 $_POST['due_date'] = get_invoice_duedate($_SESSION['Items']->payment, $_POST['InvoiceDate']);
167 $Ajax->activate('due_date');
170 //-----------------------------------------------------------------------------
171 function check_quantities()
174 foreach ($_SESSION['Items']->line_items as $line_no=>$itm) {
175 if (isset($_POST['Line'.$line_no])) {
176 if($_SESSION['Items']->trans_no) {
177 $min = $itm->qty_done;
178 $max = $itm->quantity;
181 $max = $itm->quantity - $itm->qty_done;
183 if (check_num('Line'.$line_no, $min, $max)) {
184 $_SESSION['Items']->line_items[$line_no]->qty_dispatched =
185 input_num('Line'.$line_no);
193 if (isset($_POST['Line'.$line_no.'Desc'])) {
194 $line_desc = $_POST['Line'.$line_no.'Desc'];
195 if (strlen($line_desc) > 0) {
196 $_SESSION['Items']->line_items[$line_no]->item_description = $line_desc;
203 function set_delivery_shipping_sum($delivery_notes)
208 foreach($delivery_notes as $delivery_num)
210 $myrow = get_customer_trans($delivery_num, 13);
211 //$branch = get_branch($myrow["branch_code"]);
212 //$sales_order = get_sales_order_header($myrow["order_"]);
214 //$shipping += $sales_order['freight_cost'];
215 $shipping += $myrow['ov_freight'];
217 $_POST['ChargeFreightCost'] = price_format($shipping);
221 function copy_to_cart()
223 $cart = &$_SESSION['Items'];
224 $cart->ship_via = $_POST['ship_via'];
225 $cart->freight_cost = input_num('ChargeFreightCost');
226 $cart->document_date = $_POST['InvoiceDate'];
227 $cart->due_date = $_POST['due_date'];
228 if ($cart->pos['cash_sale'] || $cart->pos['credit_sale']) {
229 $cart->payment = $_POST['payment'];
230 $cart->payment_terms = get_payment_terms($_POST['payment']);
232 $cart->Comments = $_POST['Comments'];
233 if ($_SESSION['Items']->trans_no == 0)
234 $cart->reference = $_POST['ref'];
235 $cart->dimension_id = $_POST['dimension_id'];
236 $cart->dimension2_id = $_POST['dimension2_id'];
239 //-----------------------------------------------------------------------------
241 function copy_from_cart()
243 $cart = &$_SESSION['Items'];
244 $_POST['ship_via'] = $cart->ship_via;
245 $_POST['ChargeFreightCost'] = price_format($cart->freight_cost);
246 $_POST['InvoiceDate']= $cart->document_date;
247 $_POST['due_date'] = $cart->due_date;
248 $_POST['Comments']= $cart->Comments;
249 $_POST['cart_id'] = $cart->cart_id;
250 $_POST['ref'] = $cart->reference;
251 $_POST['payment'] = $cart->payment;
252 $_POST['dimension_id'] = $cart->dimension_id;
253 $_POST['dimension2_id'] = $cart->dimension2_id;
256 //-----------------------------------------------------------------------------
258 function check_data()
262 if (!isset($_POST['InvoiceDate']) || !is_date($_POST['InvoiceDate'])) {
263 display_error(_("The entered invoice date is invalid."));
264 set_focus('InvoiceDate');
268 if (!is_date_in_fiscalyear($_POST['InvoiceDate'])) {
269 display_error(_("The entered date is out of fiscal year or is closed for further data entry."));
270 set_focus('InvoiceDate');
274 if (!isset($_POST['due_date']) || !is_date($_POST['due_date'])) {
275 display_error(_("The entered invoice due date is invalid."));
276 set_focus('due_date');
280 if ($_SESSION['Items']->trans_no == 0) {
281 if (!$Refs->is_valid($_POST['ref'])) {
282 display_error(_("You must enter a reference."));
287 if (!is_new_reference($_POST['ref'], 10)) {
288 display_error(_("The entered reference is already in use."));
294 if ($_POST['ChargeFreightCost'] == "") {
295 $_POST['ChargeFreightCost'] = price_format(0);
298 if (!check_num('ChargeFreightCost', 0)) {
299 display_error(_("The entered shipping value is not numeric."));
300 set_focus('ChargeFreightCost');
304 if ($_SESSION['Items']->has_items_dispatch() == 0 && input_num('ChargeFreightCost') == 0) {
305 display_error(_("There are no item quantities on this invoice."));
309 if (!check_quantities()) {
310 display_error(_("Selected quantity cannot be less than quantity credited nor more than quantity not invoiced yet."));
317 //-----------------------------------------------------------------------------
318 if (isset($_POST['process_invoice']) && check_data()) {
320 $newinvoice= $_SESSION['Items']->trans_no == 0;
322 if ($newinvoice) new_doc_date($_SESSION['Items']->document_date);
324 $invoice_no = $_SESSION['Items']->write();
328 meta_forward($_SERVER['PHP_SELF'], "AddedID=$invoice_no");
330 meta_forward($_SERVER['PHP_SELF'], "UpdatedID=$invoice_no");
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'];
344 // find delivery spans for batch invoice display
346 $lastdn = ''; $spanlen=1;
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) {
353 if ($line->src_no == $lastdn) {
357 $dspans[] = $spanlen;
361 $lastdn = $line->src_no;
363 $dspans[] = $spanlen;
365 //-----------------------------------------------------------------------------
367 $is_batch_invoice = count($_SESSION['Items']->src_docs) > 1;
369 $is_edition = $_SESSION['Items']->trans_type == ST_SALESINVOICE && $_SESSION['Items']->trans_no != 0;
373 start_table(TABLESTYLE2, "width=80%", 5);
377 $dim = get_company_pref('use_dimension');
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");
388 label_cells(_('Payment:'), $_SESSION['Items']->payment_terms['terms'], "class='tableheader2'", "colspan=$colspan");
393 if ($_SESSION['Items']->trans_no == 0) {
394 ref_cells(_("Reference"), 'ref', '', null, "class='tableheader2'");
396 label_cells(_("Reference"), $_SESSION['Items']->reference, "class='tableheader2'");
399 //label_cells(_("Delivery Notes:"),
400 //get_customer_trans_view_str(ST_CUSTDELIVERY, array_keys($_SESSION['Items']->src_docs)), "class='tableheader2'");
402 label_cells(_("Sales Type"), $_SESSION['Items']->sales_type_name, "class='tableheader2'");
404 label_cells(_("Currency"), $_SESSION['Items']->customer_currency, "class='tableheader2'");
405 // 2010-09-03 Joe Hunt
407 // label_cells(_("Dimension"), get_dimension_string($_SESSION['Items']->dimension_id), "class='tableheader2'");
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);
414 hidden('dimension_id', 0);
419 if (!isset($_POST['ship_via'])) {
420 $_POST['ship_via'] = $_SESSION['Items']->ship_via;
422 label_cell(_("Shipping Company"), "class='tableheader2'");
423 shippers_list_cells(null, 'ship_via', $_POST['ship_via']);
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();
432 date_cells(_("Date"), 'InvoiceDate', '', $_SESSION['Items']->trans_no == 0,
433 0, 0, 0, "class='tableheader2'", true);
435 if (!isset($_POST['due_date']) || !is_date($_POST['due_date'])) {
436 $_POST['due_date'] = get_invoice_duedate($_SESSION['Items']->payment, $_POST['InvoiceDate']);
439 date_cells(_("Due Date"), 'due_date', '', null, 0, 0, 0, "class='tableheader2'");
442 label_cells(_("Dimension"). " 2", get_dimension_string($_SESSION['Items']->dimension2_id), "class='tableheader2'");
444 label_cell(" ", "colspan=2");
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);
452 hidden('dimension2_id', 0);
456 $row = get_customer_to_order($_SESSION['Items']->customer_id);
457 if ($row['dissallow_invoices'] == 1)
459 display_error(_("The selected customer account is currently on hold. Please contact the credit control personnel to discuss."));
465 display_heading(_("Invoice 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"));
472 if ($is_batch_invoice) {
478 $th[4] = _("Credited");
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
492 alt_table_row_color($k);
493 view_stock_status_cell($ln_itm->stock_id);
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);
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>';
507 small_qty_cells(null, 'Line'.$line, qty_format($ln_itm->qty_dispatched, $ln_itm->stock_id, $dec), null, null, $dec);
509 $display_discount_percent = percent_format($ln_itm->discount_percent*100) . " %";
511 $line_total = ($ln_itm->qty_dispatched * $ln_itm->price * (1 - $ln_itm->discount_percent));
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);
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");
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 ?? */
536 if (!isset($_POST['ChargeFreightCost']) || $_POST['ChargeFreightCost'] == "") {
537 if ($_SESSION['Items']->any_already_delivered() == 1) {
538 $_POST['ChargeFreightCost'] = price_format(0);
540 $_POST['ChargeFreightCost'] = price_format($_SESSION['Items']->freight_cost);
543 if (!check_num('ChargeFreightCost')) {
544 $_POST['ChargeFreightCost'] = price_format(0);
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));
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');
561 $inv_items_total = $_SESSION['Items']->get_items_total_dispatch();
563 $display_sub_total = price_format($inv_items_total + input_num('ChargeFreightCost'));
565 label_row(_("Sub-total"), $display_sub_total, "colspan=$colspan align=right","align=right", $is_batch_invoice ? 2 : 0);
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);
570 $display_total = price_format(($inv_items_total + input_num('ChargeFreightCost') + $tax_total));
572 label_row(_("Invoice Total"), $display_total, "colspan=$colspan align=right","align=right", $is_batch_invoice ? 2 : 0);
577 start_table(TABLESTYLE2);
578 textarea_row(_("Memo"), 'Comments', null, 50, 4);
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');