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 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();
137 $_SESSION['Items'] = new Cart(ST_SALESINVOICE, $_GET['ModifyInvoice']);
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();
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."));
148 hyperlink_no_params("$path_to_root/sales/inquiry/sales_deliveries_view.php", _("Select Delivery to Invoice"));
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."));
155 if (isset($_POST['Update'])) {
156 $Ajax->activate('Items');
158 if (isset($_POST['_InvoiceDate_changed'])) {
159 $_POST['due_date'] = get_invoice_duedate($_SESSION['Items']->payment, $_POST['InvoiceDate']);
160 $Ajax->activate('due_date');
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');
168 //-----------------------------------------------------------------------------
169 function check_quantities()
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;
179 $max = $itm->quantity - $itm->qty_done;
181 if (check_num('Line'.$line_no, $min, $max)) {
182 $_SESSION['Items']->line_items[$line_no]->qty_dispatched =
183 input_num('Line'.$line_no);
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;
201 function set_delivery_shipping_sum($delivery_notes)
206 foreach($delivery_notes as $delivery_num)
208 $myrow = get_customer_trans($delivery_num, 13);
209 //$branch = get_branch($myrow["branch_code"]);
210 //$sales_order = get_sales_order_header($myrow["order_"]);
212 //$shipping += $sales_order['freight_cost'];
213 $shipping += $myrow['ov_freight'];
215 $_POST['ChargeFreightCost'] = price_format($shipping);
219 function copy_to_cart()
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']);
230 $cart->Comments = $_POST['Comments'];
231 if ($_SESSION['Items']->trans_no == 0)
232 $cart->reference = $_POST['ref'];
235 //-----------------------------------------------------------------------------
237 function copy_from_cart()
239 $cart = &$_SESSION['Items'];
240 $_POST['ship_via'] = $cart->ship_via;
241 $_POST['ChargeFreightCost'] = price_format($cart->freight_cost);
242 $_POST['InvoiceDate']= $cart->document_date;
243 $_POST['due_date'] = $cart->due_date;
244 $_POST['Comments']= $cart->Comments;
245 $_POST['cart_id'] = $cart->cart_id;
246 $_POST['ref'] = $cart->reference;
247 $_POST['payment'] = $cart->payment;
250 //-----------------------------------------------------------------------------
252 function check_data()
256 if (!isset($_POST['InvoiceDate']) || !is_date($_POST['InvoiceDate'])) {
257 display_error(_("The entered invoice date is invalid."));
258 set_focus('InvoiceDate');
262 if (!is_date_in_fiscalyear($_POST['InvoiceDate'])) {
263 display_error(_("The entered invoice date is not in fiscal year."));
264 set_focus('InvoiceDate');
268 if (!isset($_POST['due_date']) || !is_date($_POST['due_date'])) {
269 display_error(_("The entered invoice due date is invalid."));
270 set_focus('due_date');
274 if ($_SESSION['Items']->trans_no == 0) {
275 if (!$Refs->is_valid($_POST['ref'])) {
276 display_error(_("You must enter a reference."));
281 if (!is_new_reference($_POST['ref'], 10)) {
282 display_error(_("The entered reference is already in use."));
288 if ($_POST['ChargeFreightCost'] == "") {
289 $_POST['ChargeFreightCost'] = price_format(0);
292 if (!check_num('ChargeFreightCost', 0)) {
293 display_error(_("The entered shipping value is not numeric."));
294 set_focus('ChargeFreightCost');
298 if ($_SESSION['Items']->has_items_dispatch() == 0 && input_num('ChargeFreightCost') == 0) {
299 display_error(_("There are no item quantities on this invoice."));
303 if (!check_quantities()) {
304 display_error(_("Selected quantity cannot be less than quantity credited nor more than quantity not invoiced yet."));
311 //-----------------------------------------------------------------------------
312 if (isset($_POST['process_invoice']) && check_data()) {
314 $newinvoice= $_SESSION['Items']->trans_no == 0;
316 if ($newinvoice) new_doc_date($_SESSION['Items']->document_date);
318 $invoice_no = $_SESSION['Items']->write();
322 meta_forward($_SERVER['PHP_SELF'], "AddedID=$invoice_no");
324 meta_forward($_SERVER['PHP_SELF'], "UpdatedID=$invoice_no");
328 if(list_updated('payment')) {
329 $order = &$_SESSION['Items'];
330 $order->payment = get_post('payment');
331 $order->payment_terms = get_payment_terms($order->payment);
332 $order->due_date = get_invoice_duedate($order->payment, $order->document_date);
333 if ($order->payment_terms['cash_sale']) {
334 $_POST['Location'] = $order->Location = $order->pos['pos_location'];
335 $order->location_name = $order->pos['location_name'];
338 // find delivery spans for batch invoice display
340 $lastdn = ''; $spanlen=1;
342 for ($line_no = 0; $line_no < count($_SESSION['Items']->line_items); $line_no++) {
343 $line = $_SESSION['Items']->line_items[$line_no];
344 if ($line->quantity == $line->qty_done) {
347 if ($line->src_no == $lastdn) {
351 $dspans[] = $spanlen;
355 $lastdn = $line->src_no;
357 $dspans[] = $spanlen;
359 //-----------------------------------------------------------------------------
361 $is_batch_invoice = count($_SESSION['Items']->src_docs) > 1;
363 $is_edition = $_SESSION['Items']->trans_type == ST_SALESINVOICE && $_SESSION['Items']->trans_no != 0;
367 start_table(TABLESTYLE2, "width=80%", 5);
371 $dim = get_company_pref('use_dimension');
374 label_cells(_("Customer"), $_SESSION['Items']->customer_name, "class='tableheader2'");
375 label_cells(_("Branch"), get_branch_name($_SESSION['Items']->Branch), "class='tableheader2'");
376 if ($_SESSION['Items']->pos['credit_sale'] || $_SESSION['Items']->pos['cash_sale']) {
377 $paymcat = !$_SESSION['Items']->pos['cash_sale'] ? PM_CREDIT :
378 (!$_SESSION['Items']->pos['credit_sale'] ? PM_CASH : PM_ANY);
379 label_cells(_("Payment terms:"), sale_payment_list('payment', $paymcat),
380 "class='tableheader2'", "colspan=$colspan");
382 label_cells(_('Payment:'), $_SESSION['Items']->payment_terms['terms'], "class='tableheader2'", "colspan=$colspan");
387 if ($_SESSION['Items']->trans_no == 0) {
388 ref_cells(_("Reference"), 'ref', '', null, "class='tableheader2'");
390 label_cells(_("Reference"), $_SESSION['Items']->reference, "class='tableheader2'");
393 //label_cells(_("Delivery Notes:"),
394 //get_customer_trans_view_str(ST_CUSTDELIVERY, array_keys($_SESSION['Items']->src_docs)), "class='tableheader2'");
396 label_cells(_("Sales Type"), $_SESSION['Items']->sales_type_name, "class='tableheader2'");
398 label_cells(_("Currency"), $_SESSION['Items']->customer_currency, "class='tableheader2'");
399 // 2010-09-03 Joe Hunt
401 label_cells(_("Dimension"), get_dimension_string($_SESSION['Items']->dimension_id), "class='tableheader2'");
406 if (!isset($_POST['ship_via'])) {
407 $_POST['ship_via'] = $_SESSION['Items']->ship_via;
409 label_cell(_("Shipping Company"), "class='tableheader2'");
410 shippers_list_cells(null, 'ship_via', $_POST['ship_via']);
412 if (!isset($_POST['InvoiceDate']) || !is_date($_POST['InvoiceDate'])) {
413 $_POST['InvoiceDate'] = new_doc_date();
414 if (!is_date_in_fiscalyear($_POST['InvoiceDate'])) {
415 $_POST['InvoiceDate'] = end_fiscalyear();
419 date_cells(_("Date"), 'InvoiceDate', '', $_SESSION['Items']->trans_no == 0,
420 0, 0, 0, "class='tableheader2'", true);
422 if (!isset($_POST['due_date']) || !is_date($_POST['due_date'])) {
423 $_POST['due_date'] = get_invoice_duedate($_SESSION['Items']->payment, $_POST['InvoiceDate']);
426 date_cells(_("Due Date"), 'due_date', '', null, 0, 0, 0, "class='tableheader2'");
428 label_cells(_("Dimension"). " 2", get_dimension_string($_SESSION['Items']->dimension2_id), "class='tableheader2'");
430 label_cell(" ", "colspan=2");
435 $row = get_customer_to_order($_SESSION['Items']->customer_id);
436 if ($row['dissallow_invoices'] == 1)
438 display_error(_("The selected customer account is currently on hold. Please contact the credit control personnel to discuss."));
444 display_heading(_("Invoice Items"));
447 start_table(TABLESTYLE, "width=80%");
448 $th = array(_("Item Code"), _("Item Description"), _("Delivered"), _("Units"), _("Invoiced"),
449 _("This Invoice"), _("Price"), _("Tax Type"), _("Discount"), _("Total"));
451 if ($is_batch_invoice) {
457 $th[4] = _("Credited");
467 foreach ($_SESSION['Items']->line_items as $line=>$ln_itm) {
468 if ($ln_itm->quantity == $ln_itm->qty_done) {
469 continue; // this line was fully invoiced
471 alt_table_row_color($k);
472 view_stock_status_cell($ln_itm->stock_id);
474 text_cells(null, 'Line'.$line.'Desc', $ln_itm->item_description, 30, 50);
475 $dec = get_qty_dec($ln_itm->stock_id);
476 qty_cell($ln_itm->quantity, false, $dec);
477 label_cell($ln_itm->units);
478 qty_cell($ln_itm->qty_done, false, $dec);
480 if ($is_batch_invoice) {
481 // for batch invoices we can only remove whole deliveries
482 echo '<td nowrap align=right>';
483 hidden('Line' . $line, $ln_itm->qty_dispatched );
484 echo number_format2($ln_itm->qty_dispatched, $dec).'</td>';
486 small_qty_cells(null, 'Line'.$line, qty_format($ln_itm->qty_dispatched, $ln_itm->stock_id, $dec), null, null, $dec);
488 $display_discount_percent = percent_format($ln_itm->discount_percent*100) . " %";
490 $line_total = ($ln_itm->qty_dispatched * $ln_itm->price * (1 - $ln_itm->discount_percent));
492 amount_cell($ln_itm->price);
493 label_cell($ln_itm->tax_type_name);
494 label_cell($display_discount_percent, "nowrap align=right");
495 amount_cell($line_total);
497 if ($is_batch_invoice) {
498 if ($dn_line_cnt == 0) {
499 $dn_line_cnt = $dspans[0];
500 $dspans = array_slice($dspans, 1);
501 label_cell($ln_itm->src_no, "rowspan=$dn_line_cnt class=oddrow");
502 label_cell("<a href='" . $_SERVER['PHP_SELF'] . "?RemoveDN=".
503 $ln_itm->src_no."'>" . _("Remove") . "</a>", "rowspan=$dn_line_cnt class=oddrow");
510 /*Don't re-calculate freight if some of the order has already been delivered -
511 depending on the business logic required this condition may not be required.
512 It seems unfair to charge the customer twice for freight if the order
513 was not fully delivered the first time ?? */
515 if (!isset($_POST['ChargeFreightCost']) || $_POST['ChargeFreightCost'] == "") {
516 if ($_SESSION['Items']->any_already_delivered() == 1) {
517 $_POST['ChargeFreightCost'] = price_format(0);
519 $_POST['ChargeFreightCost'] = price_format($_SESSION['Items']->freight_cost);
522 if (!check_num('ChargeFreightCost')) {
523 $_POST['ChargeFreightCost'] = price_format(0);
527 $accumulate_shipping = get_company_pref('accumulate_shipping');
528 if ($is_batch_invoice && $accumulate_shipping)
529 set_delivery_shipping_sum(array_keys($_SESSION['Items']->src_docs));
533 label_cell(_("Shipping Cost"), "colspan=$colspan align=right");
534 small_amount_cells(null, 'ChargeFreightCost', null);
535 if ($is_batch_invoice) {
536 label_cell('', 'colspan=2');
540 $inv_items_total = $_SESSION['Items']->get_items_total_dispatch();
542 $display_sub_total = price_format($inv_items_total + input_num('ChargeFreightCost'));
544 label_row(_("Sub-total"), $display_sub_total, "colspan=$colspan align=right","align=right", $is_batch_invoice ? 2 : 0);
546 $taxes = $_SESSION['Items']->get_taxes(input_num('ChargeFreightCost'));
547 $tax_total = display_edit_tax_items($taxes, $colspan, $_SESSION['Items']->tax_included, $is_batch_invoice ? 2:0);
549 $display_total = price_format(($inv_items_total + input_num('ChargeFreightCost') + $tax_total));
551 label_row(_("Invoice Total"), $display_total, "colspan=$colspan align=right","align=right", $is_batch_invoice ? 2 : 0);
556 start_table(TABLESTYLE2);
557 textarea_row(_("Memo"), 'Comments', null, 50, 4);
561 submit_center_first('Update', _("Update"),
562 _('Refresh document page'), true);
563 submit_center_last('process_invoice', _("Process Invoice"),
564 _('Check entered data and save document'), 'default');