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 . "/sales/includes/sales_db.inc");
23 include_once($path_to_root . "/sales/includes/sales_ui.inc");
24 include_once($path_to_root . "/reporting/includes/reporting.inc");
25 include_once($path_to_root . "/taxes/tax_calc.inc");
26 include_once($path_to_root . "/admin/db/shipping_db.inc");
29 if ($SysPrefs->use_popup_windows) {
30 $js .= get_js_open_window(900, 500);
32 if (user_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");
43 } elseif (isset($_GET['AllocationNumber']) || isset($_GET['InvoicePrepayments'])) {
44 $_SESSION['page_title'] = _($help_context = "Prepayment or Final Invoice Entry");
46 page($_SESSION['page_title'], false, false, "", $js);
48 //-----------------------------------------------------------------------------
50 check_edit_conflicts(get_post('cart_id'));
52 if (isset($_GET['AddedID'])) {
54 $invoice_no = $_GET['AddedID'];
55 $trans_type = ST_SALESINVOICE;
57 display_notification(_("Selected deliveries has been processed"), true);
59 display_note(get_customer_trans_view_str($trans_type, $invoice_no, _("&View This Invoice")), 0, 1);
61 display_note(print_document_link($invoice_no."-".$trans_type, _("&Print This Invoice"), true, ST_SALESINVOICE));
62 display_note(print_document_link($invoice_no."-".$trans_type, _("&Email This Invoice"), true, ST_SALESINVOICE, false, "printlink", "", 1),1);
64 display_note(get_gl_view_str($trans_type, $invoice_no, _("View the GL &Journal Entries for this Invoice")),1);
66 hyperlink_params("$path_to_root/sales/inquiry/sales_deliveries_view.php", _("Select Another &Delivery For Invoicing"), "OutstandingOnly=1");
68 if (!db_num_rows(get_allocatable_from_cust_transactions(null, $invoice_no, $trans_type)))
69 hyperlink_params("$path_to_root/sales/customer_payments.php", _("Entry &customer payment for this invoice"),
70 "SInvoice=".$invoice_no);
72 hyperlink_params("$path_to_root/admin/attachments.php", _("Add an Attachment"), "filterType=$trans_type&trans_no=$invoice_no");
74 display_footer_exit();
76 } elseif (isset($_GET['UpdatedID'])) {
78 $invoice_no = $_GET['UpdatedID'];
79 $trans_type = ST_SALESINVOICE;
81 display_notification_centered(sprintf(_('Sales Invoice # %d has been updated.'),$invoice_no));
83 display_note(get_trans_view_str(ST_SALESINVOICE, $invoice_no, _("&View This Invoice")));
85 display_note(print_document_link($invoice_no."-".$trans_type, _("&Print This Invoice"), true, ST_SALESINVOICE));
86 display_note(print_document_link($invoice_no."-".$trans_type, _("&Email This Invoice"), true, ST_SALESINVOICE, false, "printlink", "", 1),1);
88 hyperlink_no_params($path_to_root . "/sales/inquiry/customer_inquiry.php", _("Select Another &Invoice to Modify"));
90 display_footer_exit();
92 } elseif (isset($_GET['RemoveDN'])) {
94 for($line_no = 0; $line_no < count($_SESSION['Items']->line_items); $line_no++) {
95 $line = &$_SESSION['Items']->line_items[$line_no];
96 if ($line->src_no == $_GET['RemoveDN']) {
97 $line->quantity = $line->qty_done;
98 $line->qty_dispatched=0;
103 // Remove also src_doc delivery note
104 $sources = &$_SESSION['Items']->src_docs;
105 unset($sources[$_GET['RemoveDN']]);
108 //-----------------------------------------------------------------------------
110 if ( (isset($_GET['DeliveryNumber']) && ($_GET['DeliveryNumber'] > 0) )
111 || isset($_GET['BatchInvoice'])) {
115 if (isset($_GET['BatchInvoice'])) {
116 $src = $_SESSION['DeliveryBatch'];
117 unset($_SESSION['DeliveryBatch']);
119 $src = array($_GET['DeliveryNumber']);
122 /*read in all the selected deliveries into the Items cart */
123 $dn = new Cart(ST_CUSTDELIVERY, $src, true);
125 if ($dn->count_items() == 0) {
126 hyperlink_params($path_to_root . "/sales/inquiry/sales_deliveries_view.php",
127 _("Select a different delivery to invoice"), "OutstandingOnly=1");
128 die ("<br><b>" . _("There are no delivered items with a quantity left to invoice. There is nothing left to invoice.") . "</b>");
131 $_SESSION['Items'] = $dn;
134 } elseif (isset($_GET['ModifyInvoice']) && $_GET['ModifyInvoice'] > 0) {
136 check_is_editable(ST_SALESINVOICE, $_GET['ModifyInvoice']);
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 (isset($_GET['AllocationNumber']) || isset($_GET['InvoicePrepayments'])) {
148 check_deferred_income_act(_("You have to set Deferred Income Account in GL Setup to entry prepayment invoices."));
150 if (isset($_GET['AllocationNumber']))
152 $payments = array(get_cust_allocation($_GET['AllocationNumber']));
154 if (!$payments || ($payments[0]['trans_type_to'] != ST_SALESORDER))
156 display_error(_("Please select correct Sales Order Prepayment to be invoiced and try again."));
157 display_footer_exit();
159 $order_no = $payments[0]['trans_no_to'];
162 $order_no = $_GET['InvoicePrepayments'];
166 $_SESSION['Items'] = new cart(ST_SALESORDER, $order_no, ST_SALESINVOICE);
167 $_SESSION['Items']->order_no = $order_no;
168 $_SESSION['Items']->src_docs = array($order_no);
169 $_SESSION['Items']->trans_no = 0;
170 $_SESSION['Items']->trans_type = ST_SALESINVOICE;
172 $_SESSION['Items']->update_payments();
176 elseif (!processing_active()) {
177 /* This page can only be called with a delivery for invoicing or invoice no for edit */
178 display_error(_("This page can only be opened after delivery selection. Please select delivery to invoicing first."));
180 hyperlink_no_params("$path_to_root/sales/inquiry/sales_deliveries_view.php", _("Select Delivery to Invoice"));
184 } elseif (!isset($_POST['process_invoice']) && (!$_SESSION['Items']->is_prepaid() && !check_quantities())) {
185 display_error(_("Selected quantity cannot be less than quantity credited nor more than quantity not invoiced yet."));
188 if (isset($_POST['Update'])) {
189 $Ajax->activate('Items');
191 if (isset($_POST['_InvoiceDate_changed'])) {
192 $_POST['due_date'] = get_invoice_duedate($_SESSION['Items']->payment, $_POST['InvoiceDate']);
193 $Ajax->activate('due_date');
196 //-----------------------------------------------------------------------------
197 function check_quantities()
200 foreach ($_SESSION['Items']->line_items as $line_no=>$itm) {
201 if (isset($_POST['Line'.$line_no])) {
202 if($_SESSION['Items']->trans_no) {
203 $min = $itm->qty_done;
204 $max = $itm->quantity;
206 global $path_to_root;
208 // Fixing floating point problem in PHP.
209 include_once($path_to_root . "/inventory/includes/db/items_units_db.inc");
210 $max = round2($itm->quantity - $itm->qty_done, get_unit_dec($itm->stock_id));
212 if (check_num('Line'.$line_no, $min, $max)) {
213 $_SESSION['Items']->line_items[$line_no]->qty_dispatched =
214 input_num('Line'.$line_no);
222 if (isset($_POST['Line'.$line_no.'Desc'])) {
223 $line_desc = $_POST['Line'.$line_no.'Desc'];
224 if (strlen($line_desc) > 0) {
225 $_SESSION['Items']->line_items[$line_no]->item_description = $line_desc;
232 function set_delivery_shipping_sum($delivery_notes)
237 foreach($delivery_notes as $delivery_num)
239 $myrow = get_customer_trans($delivery_num, ST_CUSTDELIVERY);
241 $shipping += $myrow['ov_freight'];
243 $_POST['ChargeFreightCost'] = price_format($shipping);
247 function copy_to_cart()
249 $cart = &$_SESSION['Items'];
250 $cart->due_date = $cart->document_date = $_POST['InvoiceDate'];
251 $cart->Comments = $_POST['Comments'];
252 $cart->due_date = $_POST['due_date'];
253 if (($cart->pos['cash_sale'] || $cart->pos['credit_sale']) && isset($_POST['payment'])) {
254 $cart->payment = $_POST['payment'];
255 $cart->payment_terms = get_payment_terms($_POST['payment']);
257 if ($_SESSION['Items']->trans_no == 0)
258 $cart->reference = $_POST['ref'];
259 if (!$cart->is_prepaid())
261 $cart->ship_via = $_POST['ship_via'];
262 $cart->freight_cost = input_num('ChargeFreightCost');
265 $cart->update_payments();
267 $cart->dimension_id = $_POST['dimension_id'];
268 $cart->dimension2_id = $_POST['dimension2_id'];
270 //-----------------------------------------------------------------------------
272 function copy_from_cart()
274 $cart = &$_SESSION['Items'];
275 $_POST['Comments']= $cart->Comments;
276 $_POST['InvoiceDate']= $cart->document_date;
277 $_POST['ref'] = $cart->reference;
278 $_POST['cart_id'] = $cart->cart_id;
279 $_POST['due_date'] = $cart->due_date;
280 $_POST['payment'] = $cart->payment;
281 if (!$_SESSION['Items']->is_prepaid())
283 $_POST['ship_via'] = $cart->ship_via;
284 $_POST['ChargeFreightCost'] = price_format($cart->freight_cost);
286 $_POST['dimension_id'] = $cart->dimension_id;
287 $_POST['dimension2_id'] = $cart->dimension2_id;
290 //-----------------------------------------------------------------------------
292 function check_data()
296 $prepaid = $_SESSION['Items']->is_prepaid();
298 if (!isset($_POST['InvoiceDate']) || !is_date($_POST['InvoiceDate'])) {
299 display_error(_("The entered invoice date is invalid."));
300 set_focus('InvoiceDate');
304 if (!is_date_in_fiscalyear($_POST['InvoiceDate'])) {
305 display_error(_("The entered date is out of fiscal year or is closed for further data entry."));
306 set_focus('InvoiceDate');
311 if (!$prepaid &&(!isset($_POST['due_date']) || !is_date($_POST['due_date']))) {
312 display_error(_("The entered invoice due date is invalid."));
313 set_focus('due_date');
317 if ($_SESSION['Items']->trans_no == 0) {
318 if (!$Refs->is_valid($_POST['ref'], ST_SALESINVOICE)) {
319 display_error(_("You must enter a reference."));
327 if ($_POST['ChargeFreightCost'] == "") {
328 $_POST['ChargeFreightCost'] = price_format(0);
331 if (!check_num('ChargeFreightCost', 0)) {
332 display_error(_("The entered shipping value is not numeric."));
333 set_focus('ChargeFreightCost');
337 if ($_SESSION['Items']->has_items_dispatch() == 0 && input_num('ChargeFreightCost') == 0) {
338 display_error(_("There are no item quantities on this invoice."));
342 if (!check_quantities()) {
343 display_error(_("Selected quantity cannot be less than quantity credited nor more than quantity not invoiced yet."));
347 if (($_SESSION['Items']->payment_terms['days_before_due'] == -1) && !count($_SESSION['Items']->prepayments)) {
348 display_error(_("There is no non-invoiced payments for this order. If you want to issue final invoice, select delayed or cash payment terms."));
356 //-----------------------------------------------------------------------------
357 if (isset($_POST['process_invoice']) && check_data()) {
358 $newinvoice= $_SESSION['Items']->trans_no == 0;
362 new_doc_date($_SESSION['Items']->document_date);
364 $invoice_no = $_SESSION['Items']->write();
365 if ($invoice_no == -1)
367 display_error(_("The entered reference is already in use."));
375 meta_forward($_SERVER['PHP_SELF'], "AddedID=$invoice_no");
377 meta_forward($_SERVER['PHP_SELF'], "UpdatedID=$invoice_no");
382 if(list_updated('payment')) {
383 $order = &$_SESSION['Items'];
385 $order->payment = get_post('payment');
386 $order->payment_terms = get_payment_terms($order->payment);
387 $_POST['due_date'] = $order->due_date = get_invoice_duedate($order->payment, $order->document_date);
388 $_POST['Comments'] = '';
389 $Ajax->activate('due_date');
390 $Ajax->activate('options');
391 if ($order->payment_terms['cash_sale']) {
392 $_POST['Location'] = $order->Location = $order->pos['pos_location'];
393 $order->location_name = $order->pos['location_name'];
397 // find delivery spans for batch invoice display
399 $lastdn = ''; $spanlen=1;
401 for ($line_no = 0; $line_no < count($_SESSION['Items']->line_items); $line_no++) {
402 $line = $_SESSION['Items']->line_items[$line_no];
403 if ($line->quantity == $line->qty_done) {
406 if ($line->src_no == $lastdn) {
410 $dspans[] = $spanlen;
414 $lastdn = $line->src_no;
416 $dspans[] = $spanlen;
418 //-----------------------------------------------------------------------------
420 $is_batch_invoice = count($_SESSION['Items']->src_docs) > 1;
421 $prepaid = $_SESSION['Items']->is_prepaid();
423 $is_edition = $_SESSION['Items']->trans_type == ST_SALESINVOICE && $_SESSION['Items']->trans_no != 0;
427 start_table(TABLESTYLE2, "width='80%'", 5);
431 $dim = get_company_pref('use_dimension');
434 label_cells(_("Customer"), $_SESSION['Items']->customer_name, "class='tableheader2'");
435 label_cells(_("Branch"), get_branch_name($_SESSION['Items']->Branch), "class='tableheader2'");
436 if (($_SESSION['Items']->pos['credit_sale'] || $_SESSION['Items']->pos['cash_sale'])) {
437 $paymcat = !$_SESSION['Items']->pos['cash_sale'] ? PM_CREDIT :
438 (!$_SESSION['Items']->pos['credit_sale'] ? PM_CASH : PM_ANY);
439 label_cells(_("Payment terms:"), sale_payment_list('payment', $paymcat),
440 "class='tableheader2'", "colspan=$colspan");
442 label_cells(_('Payment:'), $_SESSION['Items']->payment_terms['terms'], "class='tableheader2'", "colspan=$colspan");
447 if ($_SESSION['Items']->trans_no == 0) {
448 ref_cells(_("Reference"), 'ref', '', null, "class='tableheader2'", false, ST_SALESINVOICE,
449 array('customer' => $_SESSION['Items']->customer_id,
450 'branch' => $_SESSION['Items']->Branch,
451 'date' => get_post('InvoiceDate')));
453 label_cells(_("Reference"), $_SESSION['Items']->reference, "class='tableheader2'");
456 label_cells(_("Sales Type"), $_SESSION['Items']->sales_type_name, "class='tableheader2'");
458 label_cells(_("Currency"), $_SESSION['Items']->customer_currency, "class='tableheader2'");
460 label_cell(_("Dimension").":", "class='tableheader2'");
461 $_POST['dimension_id'] = $_SESSION['Items']->dimension_id;
462 dimensions_list_cells(null, 'dimension_id', null, true, ' ', false, 1, false);
465 hidden('dimension_id', 0);
470 if (!isset($_POST['ship_via'])) {
471 $_POST['ship_via'] = $_SESSION['Items']->ship_via;
473 label_cell(_("Shipping Company"), "class='tableheader2'");
476 $shipper = get_shipper($_SESSION['Items']->ship_via);
477 label_cells(null, $shipper['shipper_name']);
479 shippers_list_cells(null, 'ship_via', $_POST['ship_via']);
481 if (!isset($_POST['InvoiceDate']) || !is_date($_POST['InvoiceDate'])) {
482 $_POST['InvoiceDate'] = new_doc_date();
483 if (!is_date_in_fiscalyear($_POST['InvoiceDate'])) {
484 $_POST['InvoiceDate'] = end_fiscalyear();
488 date_cells(_("Date"), 'InvoiceDate', '', $_SESSION['Items']->trans_no == 0,
489 0, 0, 0, "class='tableheader2'", true);
491 if (!isset($_POST['due_date']) || !is_date($_POST['due_date'])) {
492 $_POST['due_date'] = get_invoice_duedate($_SESSION['Items']->payment, $_POST['InvoiceDate']);
495 date_cells(_("Due Date"), 'due_date', '', null, 0, 0, 0, "class='tableheader2'");
497 label_cell(_("Dimension")." 2:", "class='tableheader2'");
498 $_POST['dimension2_id'] = $_SESSION['Items']->dimension2_id;
499 dimensions_list_cells(null, 'dimension2_id', null, true, ' ', false, 2, false);
502 hidden('dimension2_id', 0);
506 $row = get_customer_to_order($_SESSION['Items']->customer_id);
507 if ($row['dissallow_invoices'] == 1)
509 display_error(_("The selected customer account is currently on hold. Please contact the credit control personnel to discuss."));
515 display_heading($prepaid ? _("Sales Order Items") : _("Invoice Items"));
519 start_table(TABLESTYLE, "width='80%'");
521 $th = array(_("Item Code"), _("Item Description"), _("Units"), _("Quantity"),
522 _("Price"), _("Tax Type"), _("Discount"), _("Total"));
524 $th = array(_("Item Code"), _("Item Description"), _("Delivered"), _("Units"), _("Invoiced"),
525 _("This Invoice"), _("Price"), _("Tax Type"), _("Discount"), _("Total"));
527 if ($is_batch_invoice) {
533 $th[4] = _("Credited");
543 foreach ($_SESSION['Items']->line_items as $line=>$ln_itm) {
544 if (!$prepaid && ($ln_itm->quantity == $ln_itm->qty_done)) {
545 continue; // this line was fully invoiced
547 alt_table_row_color($k);
548 view_stock_status_cell($ln_itm->stock_id);
551 label_cell($ln_itm->item_description);
553 text_cells(null, 'Line'.$line.'Desc', $ln_itm->item_description, 30, 50);
554 $dec = get_qty_dec($ln_itm->stock_id);
556 qty_cell($ln_itm->quantity, false, $dec);
557 label_cell($ln_itm->units);
559 qty_cell($ln_itm->qty_done, false, $dec);
561 if ($is_batch_invoice || $prepaid) {
562 // for batch invoices we can only remove whole deliveries
563 echo '<td nowrap align=right>';
564 hidden('Line' . $line, $ln_itm->qty_dispatched );
565 echo number_format2($ln_itm->qty_dispatched, $dec).'</td>';
567 small_qty_cells(null, 'Line'.$line, qty_format($ln_itm->qty_dispatched, $ln_itm->stock_id, $dec), null, null, $dec);
569 $display_discount_percent = percent_format($ln_itm->discount_percent*100) . " %";
571 $line_total = ($ln_itm->qty_dispatched * $ln_itm->price * (1 - $ln_itm->discount_percent));
573 amount_cell($ln_itm->price);
574 label_cell($ln_itm->tax_type_name);
575 label_cell($display_discount_percent, "nowrap align=right");
576 amount_cell($line_total);
578 if ($is_batch_invoice) {
579 if ($dn_line_cnt == 0) {
580 $dn_line_cnt = $dspans[0];
581 $dspans = array_slice($dspans, 1);
582 label_cell($ln_itm->src_no, "rowspan=$dn_line_cnt class='oddrow'");
583 label_cell("<a href='" . $_SERVER['PHP_SELF'] . "?RemoveDN=".
584 $ln_itm->src_no."'>" . _("Remove") . "</a>", "rowspan=$dn_line_cnt class='oddrow'");
591 /*Don't re-calculate freight if some of the order has already been delivered -
592 depending on the business logic required this condition may not be required.
593 It seems unfair to charge the customer twice for freight if the order
594 was not fully delivered the first time ?? */
596 if (!isset($_POST['ChargeFreightCost']) || $_POST['ChargeFreightCost'] == "") {
597 if ($_SESSION['Items']->any_already_delivered() == 1) {
598 $_POST['ChargeFreightCost'] = price_format(0);
600 $_POST['ChargeFreightCost'] = price_format($_SESSION['Items']->freight_cost);
603 if (!check_num('ChargeFreightCost')) {
604 $_POST['ChargeFreightCost'] = price_format(0);
608 $accumulate_shipping = get_company_pref('accumulate_shipping');
609 if ($is_batch_invoice && $accumulate_shipping)
610 set_delivery_shipping_sum(array_keys($_SESSION['Items']->src_docs));
612 $colspan = $prepaid ? 7:9;
614 label_cell(_("Shipping Cost"), "colspan=$colspan align=right");
616 label_cell($_POST['ChargeFreightCost'], 'align=right');
618 small_amount_cells(null, 'ChargeFreightCost', null);
619 if ($is_batch_invoice) {
620 label_cell('', 'colspan=2');
624 $inv_items_total = $_SESSION['Items']->get_items_total_dispatch();
626 $display_sub_total = price_format($inv_items_total + input_num('ChargeFreightCost'));
628 label_row(_("Sub-total"), $display_sub_total, "colspan=$colspan align=right","align=right", $is_batch_invoice ? 2 : 0);
630 $taxes = $_SESSION['Items']->get_taxes(input_num('ChargeFreightCost'));
631 $tax_total = display_edit_tax_items($taxes, $colspan, $_SESSION['Items']->tax_included, $is_batch_invoice ? 2 : 0);
633 $display_total = price_format(($inv_items_total + input_num('ChargeFreightCost') + $tax_total));
635 label_row(_("Invoice Total"), $display_total, "colspan=$colspan align=right","align=right", $is_batch_invoice ? 2 : 0);
639 div_start('options');
640 start_table(TABLESTYLE2);
644 label_row(_("Sales order:"), get_trans_view_str(ST_SALESORDER, $_SESSION['Items']->order_no, get_reference(ST_SALESORDER, $_SESSION['Items']->order_no)));
646 $list = array(); $allocs = 0;
647 if (count($_SESSION['Items']->prepayments))
649 foreach($_SESSION['Items']->prepayments as $pmt)
651 $list[] = get_trans_view_str($pmt['trans_type_from'], $pmt['trans_no_from'], get_reference($pmt['trans_type_from'], $pmt['trans_no_from']));
652 $allocs += $pmt['amt'];
655 label_row(_("Payments received:"), implode(',', $list));
656 label_row(_("Invoiced here:"), price_format($_SESSION['Items']->prep_amount), 'class=label');
657 label_row($_SESSION['Items']->payment_terms['days_before_due'] == -1 ? _("Left to be invoiced:") : _("Invoiced so far:"),
658 price_format($_SESSION['Items']->get_trans_total()-max($_SESSION['Items']->prep_amount, $allocs)), 'class=label');
661 textarea_row(_("Memo:"), 'Comments', null, 50, 4);
665 submit_center_first('Update', _("Update"),
666 _('Refresh document page'), true);
667 submit_center_last('process_invoice', _("Process Invoice"),
668 _('Check entered data and save document'), 'default');