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;
207 // Fixing floating point problem in PHP.
208 $max = round2($itm->quantity - $itm->qty_done, get_unit_dec($itm->stock_id));
210 if (check_num('Line'.$line_no, $min, $max)) {
211 $_SESSION['Items']->line_items[$line_no]->qty_dispatched =
212 input_num('Line'.$line_no);
220 if (isset($_POST['Line'.$line_no.'Desc'])) {
221 $line_desc = $_POST['Line'.$line_no.'Desc'];
222 if (strlen($line_desc) > 0) {
223 $_SESSION['Items']->line_items[$line_no]->item_description = $line_desc;
230 function set_delivery_shipping_sum($delivery_notes)
235 foreach($delivery_notes as $delivery_num)
237 $myrow = get_customer_trans($delivery_num, ST_CUSTDELIVERY);
239 $shipping += $myrow['ov_freight'];
241 $_POST['ChargeFreightCost'] = price_format($shipping);
245 function copy_to_cart()
247 $cart = &$_SESSION['Items'];
248 $cart->due_date = $cart->document_date = $_POST['InvoiceDate'];
249 $cart->Comments = $_POST['Comments'];
250 $cart->due_date = $_POST['due_date'];
251 if (($cart->pos['cash_sale'] || $cart->pos['credit_sale']) && isset($_POST['payment'])) {
252 $cart->payment = $_POST['payment'];
253 $cart->payment_terms = get_payment_terms($_POST['payment']);
255 if ($_SESSION['Items']->trans_no == 0)
256 $cart->reference = $_POST['ref'];
257 if (!$cart->is_prepaid())
259 $cart->ship_via = $_POST['ship_via'];
260 $cart->freight_cost = input_num('ChargeFreightCost');
263 $cart->update_payments();
265 $cart->dimension_id = $_POST['dimension_id'];
266 $cart->dimension2_id = $_POST['dimension2_id'];
268 //-----------------------------------------------------------------------------
270 function copy_from_cart()
272 $cart = &$_SESSION['Items'];
273 $_POST['Comments']= $cart->Comments;
274 $_POST['InvoiceDate']= $cart->document_date;
275 $_POST['ref'] = $cart->reference;
276 $_POST['cart_id'] = $cart->cart_id;
277 $_POST['due_date'] = $cart->due_date;
278 $_POST['payment'] = $cart->payment;
279 if (!$_SESSION['Items']->is_prepaid())
281 $_POST['ship_via'] = $cart->ship_via;
282 $_POST['ChargeFreightCost'] = price_format($cart->freight_cost);
284 $_POST['dimension_id'] = $cart->dimension_id;
285 $_POST['dimension2_id'] = $cart->dimension2_id;
288 //-----------------------------------------------------------------------------
290 function check_data()
294 $prepaid = $_SESSION['Items']->is_prepaid();
296 if (!isset($_POST['InvoiceDate']) || !is_date($_POST['InvoiceDate'])) {
297 display_error(_("The entered invoice date is invalid."));
298 set_focus('InvoiceDate');
302 if (!is_date_in_fiscalyear($_POST['InvoiceDate'])) {
303 display_error(_("The entered date is out of fiscal year or is closed for further data entry."));
304 set_focus('InvoiceDate');
309 if (!$prepaid &&(!isset($_POST['due_date']) || !is_date($_POST['due_date']))) {
310 display_error(_("The entered invoice due date is invalid."));
311 set_focus('due_date');
315 if ($_SESSION['Items']->trans_no == 0) {
316 if (!$Refs->is_valid($_POST['ref'], ST_SALESINVOICE)) {
317 display_error(_("You must enter a reference."));
325 if ($_POST['ChargeFreightCost'] == "") {
326 $_POST['ChargeFreightCost'] = price_format(0);
329 if (!check_num('ChargeFreightCost', 0)) {
330 display_error(_("The entered shipping value is not numeric."));
331 set_focus('ChargeFreightCost');
335 if ($_SESSION['Items']->has_items_dispatch() == 0 && input_num('ChargeFreightCost') == 0) {
336 display_error(_("There are no item quantities on this invoice."));
340 if (!check_quantities()) {
341 display_error(_("Selected quantity cannot be less than quantity credited nor more than quantity not invoiced yet."));
345 if (($_SESSION['Items']->payment_terms['days_before_due'] == -1) && !count($_SESSION['Items']->prepayments)) {
346 display_error(_("There is no non-invoiced payments for this order. If you want to issue final invoice, select delayed or cash payment terms."));
354 //-----------------------------------------------------------------------------
355 if (isset($_POST['process_invoice']) && check_data()) {
356 $newinvoice= $_SESSION['Items']->trans_no == 0;
360 new_doc_date($_SESSION['Items']->document_date);
362 $invoice_no = $_SESSION['Items']->write();
363 if ($invoice_no == -1)
365 display_error(_("The entered reference is already in use."));
373 meta_forward($_SERVER['PHP_SELF'], "AddedID=$invoice_no");
375 meta_forward($_SERVER['PHP_SELF'], "UpdatedID=$invoice_no");
380 if(list_updated('payment')) {
381 $order = &$_SESSION['Items'];
383 $order->payment = get_post('payment');
384 $order->payment_terms = get_payment_terms($order->payment);
385 $_POST['due_date'] = $order->due_date = get_invoice_duedate($order->payment, $order->document_date);
386 $_POST['Comments'] = '';
387 $Ajax->activate('due_date');
388 $Ajax->activate('options');
389 if ($order->payment_terms['cash_sale']) {
390 $_POST['Location'] = $order->Location = $order->pos['pos_location'];
391 $order->location_name = $order->pos['location_name'];
395 // find delivery spans for batch invoice display
397 $lastdn = ''; $spanlen=1;
399 for ($line_no = 0; $line_no < count($_SESSION['Items']->line_items); $line_no++) {
400 $line = $_SESSION['Items']->line_items[$line_no];
401 if ($line->quantity == $line->qty_done) {
404 if ($line->src_no == $lastdn) {
408 $dspans[] = $spanlen;
412 $lastdn = $line->src_no;
414 $dspans[] = $spanlen;
416 //-----------------------------------------------------------------------------
418 $is_batch_invoice = count($_SESSION['Items']->src_docs) > 1;
419 $prepaid = $_SESSION['Items']->is_prepaid();
421 $is_edition = $_SESSION['Items']->trans_type == ST_SALESINVOICE && $_SESSION['Items']->trans_no != 0;
425 start_table(TABLESTYLE2, "width='80%'", 5);
429 $dim = get_company_pref('use_dimension');
432 label_cells(_("Customer"), $_SESSION['Items']->customer_name, "class='tableheader2'");
433 label_cells(_("Branch"), get_branch_name($_SESSION['Items']->Branch), "class='tableheader2'");
434 if (($_SESSION['Items']->pos['credit_sale'] || $_SESSION['Items']->pos['cash_sale'])) {
435 $paymcat = !$_SESSION['Items']->pos['cash_sale'] ? PM_CREDIT :
436 (!$_SESSION['Items']->pos['credit_sale'] ? PM_CASH : PM_ANY);
437 label_cells(_("Payment terms:"), sale_payment_list('payment', $paymcat),
438 "class='tableheader2'", "colspan=$colspan");
440 label_cells(_('Payment:'), $_SESSION['Items']->payment_terms['terms'], "class='tableheader2'", "colspan=$colspan");
445 if ($_SESSION['Items']->trans_no == 0) {
446 ref_cells(_("Reference"), 'ref', '', null, "class='tableheader2'", false, ST_SALESINVOICE,
447 array('customer' => $_SESSION['Items']->customer_id,
448 'branch' => $_SESSION['Items']->Branch,
449 'date' => get_post('InvoiceDate')));
451 label_cells(_("Reference"), $_SESSION['Items']->reference, "class='tableheader2'");
454 label_cells(_("Sales Type"), $_SESSION['Items']->sales_type_name, "class='tableheader2'");
456 label_cells(_("Currency"), $_SESSION['Items']->customer_currency, "class='tableheader2'");
458 label_cell(_("Dimension").":", "class='tableheader2'");
459 $_POST['dimension_id'] = $_SESSION['Items']->dimension_id;
460 dimensions_list_cells(null, 'dimension_id', null, true, ' ', false, 1, false);
463 hidden('dimension_id', 0);
468 if (!isset($_POST['ship_via'])) {
469 $_POST['ship_via'] = $_SESSION['Items']->ship_via;
471 label_cell(_("Shipping Company"), "class='tableheader2'");
474 $shipper = get_shipper($_SESSION['Items']->ship_via);
475 label_cells(null, $shipper['shipper_name']);
477 shippers_list_cells(null, 'ship_via', $_POST['ship_via']);
479 if (!isset($_POST['InvoiceDate']) || !is_date($_POST['InvoiceDate'])) {
480 $_POST['InvoiceDate'] = new_doc_date();
481 if (!is_date_in_fiscalyear($_POST['InvoiceDate'])) {
482 $_POST['InvoiceDate'] = end_fiscalyear();
486 date_cells(_("Date"), 'InvoiceDate', '', $_SESSION['Items']->trans_no == 0,
487 0, 0, 0, "class='tableheader2'", true);
489 if (!isset($_POST['due_date']) || !is_date($_POST['due_date'])) {
490 $_POST['due_date'] = get_invoice_duedate($_SESSION['Items']->payment, $_POST['InvoiceDate']);
493 date_cells(_("Due Date"), 'due_date', '', null, 0, 0, 0, "class='tableheader2'");
495 label_cell(_("Dimension")." 2:", "class='tableheader2'");
496 $_POST['dimension2_id'] = $_SESSION['Items']->dimension2_id;
497 dimensions_list_cells(null, 'dimension2_id', null, true, ' ', false, 2, false);
500 hidden('dimension2_id', 0);
504 $row = get_customer_to_order($_SESSION['Items']->customer_id);
505 if ($row['dissallow_invoices'] == 1)
507 display_error(_("The selected customer account is currently on hold. Please contact the credit control personnel to discuss."));
513 display_heading($prepaid ? _("Sales Order Items") : _("Invoice Items"));
517 start_table(TABLESTYLE, "width='80%'");
519 $th = array(_("Item Code"), _("Item Description"), _("Units"), _("Quantity"),
520 _("Price"), _("Tax Type"), _("Discount"), _("Total"));
522 $th = array(_("Item Code"), _("Item Description"), _("Delivered"), _("Units"), _("Invoiced"),
523 _("This Invoice"), _("Price"), _("Tax Type"), _("Discount"), _("Total"));
525 if ($is_batch_invoice) {
531 $th[4] = _("Credited");
541 foreach ($_SESSION['Items']->line_items as $line=>$ln_itm) {
542 if (!$prepaid && ($ln_itm->quantity == $ln_itm->qty_done)) {
543 continue; // this line was fully invoiced
545 alt_table_row_color($k);
546 view_stock_status_cell($ln_itm->stock_id);
549 label_cell($ln_itm->item_description);
551 text_cells(null, 'Line'.$line.'Desc', $ln_itm->item_description, 30, 50);
552 $dec = get_qty_dec($ln_itm->stock_id);
554 qty_cell($ln_itm->quantity, false, $dec);
555 label_cell($ln_itm->units);
557 qty_cell($ln_itm->qty_done, false, $dec);
559 if ($is_batch_invoice || $prepaid) {
560 // for batch invoices we can only remove whole deliveries
561 echo '<td nowrap align=right>';
562 hidden('Line' . $line, $ln_itm->qty_dispatched );
563 echo number_format2($ln_itm->qty_dispatched, $dec).'</td>';
565 small_qty_cells(null, 'Line'.$line, qty_format($ln_itm->qty_dispatched, $ln_itm->stock_id, $dec), null, null, $dec);
567 $display_discount_percent = percent_format($ln_itm->discount_percent*100) . " %";
569 $line_total = ($ln_itm->qty_dispatched * $ln_itm->price * (1 - $ln_itm->discount_percent));
571 amount_cell($ln_itm->price);
572 label_cell($ln_itm->tax_type_name);
573 label_cell($display_discount_percent, "nowrap align=right");
574 amount_cell($line_total);
576 if ($is_batch_invoice) {
577 if ($dn_line_cnt == 0) {
578 $dn_line_cnt = $dspans[0];
579 $dspans = array_slice($dspans, 1);
580 label_cell($ln_itm->src_no, "rowspan=$dn_line_cnt class='oddrow'");
581 label_cell("<a href='" . $_SERVER['PHP_SELF'] . "?RemoveDN=".
582 $ln_itm->src_no."'>" . _("Remove") . "</a>", "rowspan=$dn_line_cnt class='oddrow'");
589 /*Don't re-calculate freight if some of the order has already been delivered -
590 depending on the business logic required this condition may not be required.
591 It seems unfair to charge the customer twice for freight if the order
592 was not fully delivered the first time ?? */
594 if (!isset($_POST['ChargeFreightCost']) || $_POST['ChargeFreightCost'] == "") {
595 if ($_SESSION['Items']->any_already_delivered() == 1) {
596 $_POST['ChargeFreightCost'] = price_format(0);
598 $_POST['ChargeFreightCost'] = price_format($_SESSION['Items']->freight_cost);
601 if (!check_num('ChargeFreightCost')) {
602 $_POST['ChargeFreightCost'] = price_format(0);
606 $accumulate_shipping = get_company_pref('accumulate_shipping');
607 if ($is_batch_invoice && $accumulate_shipping)
608 set_delivery_shipping_sum(array_keys($_SESSION['Items']->src_docs));
610 $colspan = $prepaid ? 7:9;
612 label_cell(_("Shipping Cost"), "colspan=$colspan align=right");
614 label_cell($_POST['ChargeFreightCost'], 'align=right');
616 small_amount_cells(null, 'ChargeFreightCost', null);
617 if ($is_batch_invoice) {
618 label_cell('', 'colspan=2');
622 $inv_items_total = $_SESSION['Items']->get_items_total_dispatch();
624 $display_sub_total = price_format($inv_items_total + input_num('ChargeFreightCost'));
626 label_row(_("Sub-total"), $display_sub_total, "colspan=$colspan align=right","align=right", $is_batch_invoice ? 2 : 0);
628 $taxes = $_SESSION['Items']->get_taxes(input_num('ChargeFreightCost'));
629 $tax_total = display_edit_tax_items($taxes, $colspan, $_SESSION['Items']->tax_included, $is_batch_invoice ? 2 : 0);
631 $display_total = price_format(($inv_items_total + input_num('ChargeFreightCost') + $tax_total));
633 label_row(_("Invoice Total"), $display_total, "colspan=$colspan align=right","align=right", $is_batch_invoice ? 2 : 0);
637 div_start('options');
638 start_table(TABLESTYLE2);
642 label_row(_("Sales order:"), get_trans_view_str(ST_SALESORDER, $_SESSION['Items']->order_no, get_reference(ST_SALESORDER, $_SESSION['Items']->order_no)));
644 $list = array(); $allocs = 0;
645 if (count($_SESSION['Items']->prepayments))
647 foreach($_SESSION['Items']->prepayments as $pmt)
649 $list[] = get_trans_view_str($pmt['trans_type_from'], $pmt['trans_no_from'], get_reference($pmt['trans_type_from'], $pmt['trans_no_from']));
650 $allocs += $pmt['amt'];
653 label_row(_("Payments received:"), implode(',', $list));
654 label_row(_("Invoiced here:"), price_format($_SESSION['Items']->prep_amount), 'class=label');
655 label_row($_SESSION['Items']->payment_terms['days_before_due'] == -1 ? _("Left to be invoiced:") : _("Invoiced so far:"),
656 price_format($_SESSION['Items']->get_trans_total()-max($_SESSION['Items']->prep_amount, $allocs)), 'class=label');
659 textarea_row(_("Memo:"), 'Comments', null, 50, 4);
663 submit_center_first('Update', _("Update"),
664 _('Refresh document page'), true);
665 submit_center_last('process_invoice', _("Process Invoice"),
666 _('Check entered data and save document'), 'default');