Release 2.4.8
[fa-stable.git] / sales / customer_invoice.php
index f63fc523337879f169e489ec106595b29ced3562..e4ab5158322f95ef6fffea1a7f20fdf724a76317 100644 (file)
 <?php
-
-$page_security = 2;
-$path_to_root="..";
+/**********************************************************************
+    Copyright (C) FrontAccounting, LLC.
+       Released under the terms of the GNU General Public License, GPL, 
+       as published by the Free Software Foundation, either version 3 
+       of the License, or (at your option) any later version.
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+    See the License here <http://www.gnu.org/licenses/gpl-3.0.html>.
+***********************************************************************/
+//---------------------------------------------------------------------------
+//
+//     Entry/Modify Sales Invoice against single delivery
+//     Entry/Modify Batch Sales Invoice against batch of deliveries
+//
+$page_security = 'SA_SALESINVOICE';
+$path_to_root = "..";
 include_once($path_to_root . "/sales/includes/cart_class.inc");
 include_once($path_to_root . "/includes/session.inc");
-
 include_once($path_to_root . "/includes/data_checks.inc");
-
-include_once($path_to_root . "/includes/manufacturing.inc");
 include_once($path_to_root . "/sales/includes/sales_db.inc");
 include_once($path_to_root . "/sales/includes/sales_ui.inc");
-
+include_once($path_to_root . "/reporting/includes/reporting.inc");
 include_once($path_to_root . "/taxes/tax_calc.inc");
+include_once($path_to_root . "/admin/db/shipping_db.inc");
 
 $js = "";
-if ($use_popup_windows)
+if ($SysPrefs->use_popup_windows) {
        $js .= get_js_open_window(900, 500);
-page(_("Issue an Invoice and Deliver Items for a Sales Order"), false, false, "", $js);
+}
+if (user_use_date_picker()) {
+       $js .= get_js_date_picker();
+}
 
-//---------------------------------------------------------------------------------------------------------------
+if (isset($_GET['ModifyInvoice'])) {
+       $_SESSION['page_title'] = sprintf(_("Modifying Sales Invoice # %d.") ,$_GET['ModifyInvoice']);
+       $help_context = "Modifying Sales Invoice";
+} elseif (isset($_GET['DeliveryNumber'])) {
+       $_SESSION['page_title'] = _($help_context = "Issue an Invoice for Delivery Note");
+} elseif (isset($_GET['BatchInvoice'])) {
+       $_SESSION['page_title'] = _($help_context = "Issue Batch Invoice for Delivery Notes");
+} elseif (isset($_GET['AllocationNumber']) || isset($_GET['InvoicePrepayments'])) {
+       $_SESSION['page_title'] = _($help_context = "Prepayment or Final Invoice Entry");
+}
+page($_SESSION['page_title'], false, false, "", $js);
+
+//-----------------------------------------------------------------------------
+
+check_edit_conflicts(get_post('cart_id'));
+
+if (isset($_GET['AddedID'])) {
 
-if (isset($_GET['AddedID'])) 
-{
        $invoice_no = $_GET['AddedID'];
-       $trans_type = 10;
+       $trans_type = ST_SALESINVOICE;
 
-       display_notification(_("Invoice processed"), true);
-       display_note(get_customer_trans_view_str($trans_type, $invoice_no, _("View this invoice")), 0, 1);
+       display_notification(_("Selected deliveries has been processed"), true);
 
-       display_note(get_gl_view_str($trans_type, $invoice_no, _("View the GL Journal Entries for this Invoice")));
+       display_note(get_customer_trans_view_str($trans_type, $invoice_no, _("&View This Invoice")), 0, 1);
 
-       if ($_SESSION['Items']->direct_invoice)
-               hyperlink_params("$path_to_root/sales/sales_order_entry.php", _("Issue Another Invoice"), "NewInvoice=Yes");
-       else
-               hyperlink_params("$path_to_root/sales/inquiry/sales_orders_view.php", _("Select Another Order For Invoicing"), "OutstandingOnly=1");
+       display_note(print_document_link($invoice_no."-".$trans_type, _("&Print This Invoice"), true, ST_SALESINVOICE));
+       display_note(print_document_link($invoice_no."-".$trans_type, _("&Email This Invoice"), true, ST_SALESINVOICE, false, "printlink", "", 1),1);
+
+       display_note(get_gl_view_str($trans_type, $invoice_no, _("View the GL &Journal Entries for this Invoice")),1);
+
+       hyperlink_params("$path_to_root/sales/inquiry/sales_deliveries_view.php", _("Select Another &Delivery For Invoicing"), "OutstandingOnly=1");
+
+       if (!db_num_rows(get_allocatable_from_cust_transactions(null, $invoice_no, $trans_type)))
+               hyperlink_params("$path_to_root/sales/customer_payments.php", _("Entry &customer payment for this invoice"),
+               "SInvoice=".$invoice_no);
+
+       hyperlink_params("$path_to_root/admin/attachments.php", _("Add an Attachment"), "filterType=$trans_type&trans_no=$invoice_no");
+
+       display_footer_exit();
+
+} elseif (isset($_GET['UpdatedID']))  {
+
+       $invoice_no = $_GET['UpdatedID'];
+       $trans_type = ST_SALESINVOICE;
+
+       display_notification_centered(sprintf(_('Sales Invoice # %d has been updated.'),$invoice_no));
+
+       display_note(get_trans_view_str(ST_SALESINVOICE, $invoice_no, _("&View This Invoice")));
+       echo '<br>';
+       display_note(print_document_link($invoice_no."-".$trans_type, _("&Print This Invoice"), true, ST_SALESINVOICE));
+       display_note(print_document_link($invoice_no."-".$trans_type, _("&Email This Invoice"), true, ST_SALESINVOICE, false, "printlink", "", 1),1);
+
+       hyperlink_no_params($path_to_root . "/sales/inquiry/customer_inquiry.php", _("Select Another &Invoice to Modify"));
 
-       unset($_SESSION['Items']->line_items);
-       unset($_SESSION['Items']);
        display_footer_exit();
+
+} elseif (isset($_GET['RemoveDN'])) {
+
+       for($line_no = 0; $line_no < count($_SESSION['Items']->line_items); $line_no++) {
+               $line = &$_SESSION['Items']->line_items[$line_no];
+               if ($line->src_no == $_GET['RemoveDN']) {
+                       $line->quantity = $line->qty_done;
+                       $line->qty_dispatched=0;
+               }
+       }
+       unset($line);
+
+    // Remove also src_doc delivery note
+    $sources = &$_SESSION['Items']->src_docs;
+    unset($sources[$_GET['RemoveDN']]);
 }
 
-//---------------------------------------------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
 
-if (!isset($_GET['OrderNumber']) && !isset($_SESSION['ProcessingOrder']) && 
-       !isset($_GET['process_invoice'])) 
-{
-       /* This page can only be called with an order number for invoicing*/
-       display_error(_("This page can only be opened if an order has been selected. Please select an order first."));
+if ( (isset($_GET['DeliveryNumber']) && ($_GET['DeliveryNumber'] > 0) )
+       || isset($_GET['BatchInvoice'])) {
 
-       hyperlink_no_params("$path_to_root/sales/inquiry/sales_orders_view.php", _("Select a sales order to invoice"));
+       processing_start();
 
-       end_page();
-       exit;
+       if (isset($_GET['BatchInvoice'])) {
+               $src = $_SESSION['DeliveryBatch'];
+               unset($_SESSION['DeliveryBatch']);
+       } else {
+               $src = array($_GET['DeliveryNumber']);
+       }
 
-} 
-elseif (isset($_GET['OrderNumber']) && $_GET['OrderNumber'] > 0) 
-{
+       /*read in all the selected deliveries into the Items cart  */
+       $dn = new Cart(ST_CUSTDELIVERY, $src, true);
 
-       if (isset($_SESSION['Items']))
-       {
-               unset($_SESSION['Items']->line_items);
-               unset ($_SESSION['Items']);
+       if ($dn->count_items() == 0) {
+               hyperlink_params($path_to_root . "/sales/inquiry/sales_deliveries_view.php",
+                       _("Select a different delivery to invoice"), "OutstandingOnly=1");
+               die ("<br><b>" . _("There are no delivered items with a quantity left to invoice. There is nothing left to invoice.") . "</b>");
        }
 
-       session_register("Items");
-       session_register("ProcessingOrder");
+       $_SESSION['Items'] = $dn;
+       copy_from_cart();
 
-       $_SESSION['ProcessingOrder'] = $_GET['OrderNumber'];
-       $_SESSION['Items'] = new cart;
+} elseif (isset($_GET['ModifyInvoice']) && $_GET['ModifyInvoice'] > 0) {
 
-       /*read in all the selected order into the Items cart  */
+       check_is_editable(ST_SALESINVOICE, $_GET['ModifyInvoice']);
 
-       if (read_sales_order($_SESSION['ProcessingOrder'], $_SESSION['Items'], true)) 
-       {
+       processing_start();
+       $_SESSION['Items'] = new Cart(ST_SALESINVOICE, $_GET['ModifyInvoice']);
 
-       if ($_SESSION['Items']->count_items() == 0) 
-       {
-               hyperlink_params($path_to_root . "/sales/inquiry/sales_orders_view.php", _("Select a different sales order to invoice"), "OutstandingOnly=1");
-               die ("<br><b>" . _("There are no ordered items with a quantity left to deliver. There is nothing left to invoice.") . "</b>");
-       }
-       } 
-       else 
-       {
-               hyperlink_no_params("/sales_orders_view.php", _("Select a sales order to invoice"));
-               die ("<br><b>" . _("This order item could not be retrieved. Please select another order.") . "</b>");
+       if ($_SESSION['Items']->count_items() == 0) {
+               echo"<center><br><b>" . _("All quantities on this invoice has been credited. There is nothing to modify on this invoice") . "</b></center>";
+               display_footer_exit();
        }
+       copy_from_cart();
+} elseif (isset($_GET['AllocationNumber']) || isset($_GET['InvoicePrepayments'])) {
 
-} 
-else 
-{
-       /* if processing, a dispatch page has been called and ${$StkItm->stock_id} would have been set from the post */
-       foreach ($_SESSION['Items']->line_items as $itm) 
-       {
+       check_deferred_income_act(_("You have to set Deferred Income Account in GL Setup to entry prepayment invoices."));
 
-               if (isset($_SESSION['Items']->line_items[$itm->stock_id]) && 
-                       isset($_POST[$itm->stock_id]) && is_numeric($_POST[$itm->stock_id]) && 
-                       $_POST[$itm->stock_id] <= ($_SESSION['Items']->line_items[$itm->stock_id]->quantity - 
-                       $_SESSION['Items']->line_items[$itm->stock_id]->qty_inv))
-               {
-                       $_SESSION['Items']->line_items[$itm->stock_id]->qty_dispatched = $_POST[$itm->stock_id];
-               }
+       if (isset($_GET['AllocationNumber']))
+       {
+               $payments = array(get_cust_allocation($_GET['AllocationNumber']));
 
-               if (isset($_POST[$itm->stock_id . "Desc"]) && strlen($_POST[$itm->stock_id . "Desc"]) > 0) 
+               if (!$payments || ($payments[0]['trans_type_to'] != ST_SALESORDER))
                {
-                       $_SESSION['Items']->line_items[$itm->stock_id]->item_description = $_POST[$itm->stock_id . "Desc"];
+                       display_error(_("Please select correct Sales Order Prepayment to be invoiced and try again."));
+                       display_footer_exit();
                }
+               $order_no = $payments[0]['trans_no_to'];
        }
-}
+       else {
+               $order_no = $_GET['InvoicePrepayments'];
+       }
+       processing_start();
 
-//---------------------------------------------------------------------------------------------------------------
+       $_SESSION['Items'] = new cart(ST_SALESORDER, $order_no, ST_SALESINVOICE);
+       $_SESSION['Items']->order_no = $order_no;
+       $_SESSION['Items']->src_docs = array($order_no);
+       $_SESSION['Items']->trans_no = 0;
+       $_SESSION['Items']->trans_type = ST_SALESINVOICE;
 
-function order_changed_error()
-{
-       global $path_to_root;
-       display_note(_("This order has been changed or invoiced since this delivery was started to be confirmed. Processing halted."), 1, 0);
-       display_note(_("To enter and confirm this dispatch/invoice the order must be re-selected and re-read again to update the changes made by the other user."), 1, 0);
+       $_SESSION['Items']->update_payments();
 
-       hyperlink_no_params("$path_to_root/sales/inquiry/sales_orders_view.php", _("Select a sales order for confirming deliveries and invoicing"));
+       copy_from_cart();
+}
+elseif (!processing_active()) {
+       /* This page can only be called with a delivery for invoicing or invoice no for edit */
+       display_error(_("This page can only be opened after delivery selection. Please select delivery to invoicing first."));
+
+       hyperlink_no_params("$path_to_root/sales/inquiry/sales_deliveries_view.php", _("Select Delivery to Invoice"));
 
-       unset($_SESSION['Items']->line_items);
-       unset($_SESSION['Items']);
-       unset($_SESSION['ProcessingOrder']);
+       end_page();
        exit;
+} elseif (!isset($_POST['process_invoice']) && (!$_SESSION['Items']->is_prepaid() && !check_quantities())) {
+       display_error(_("Selected quantity cannot be less than quantity credited nor more than quantity not invoiced yet."));
 }
 
-//---------------------------------------------------------------------------------------------------------------
+if (isset($_POST['Update'])) {
+       $Ajax->activate('Items');
+}
+if (isset($_POST['_InvoiceDate_changed'])) {
+       $_POST['due_date'] = get_invoice_duedate($_SESSION['Items']->payment, $_POST['InvoiceDate']);
+       $Ajax->activate('due_date');
+}
 
-function check_order_changed()
+//-----------------------------------------------------------------------------
+function check_quantities()
 {
-       global $debug;
-
-       /*Now need to check that the order details are the same as they were when
-                       they were read into the Items array.
-       If they've changed then someone else may have invoiced them  -
-               as modified for bug pointed out by Sherif 1-7-03*/
+       $ok =1;
+       foreach ($_SESSION['Items']->line_items as $line_no=>$itm) {
+               if (isset($_POST['Line'.$line_no])) {
+                       if($_SESSION['Items']->trans_no) {
+                               $min = $itm->qty_done;
+                               $max = $itm->quantity;
+                       } else {
+                               $min = 0;
+                               $max = $itm->quantity - $itm->qty_done;
+                       }
+                       if (check_num('Line'.$line_no, $min, $max)) {
+                               $_SESSION['Items']->line_items[$line_no]->qty_dispatched =
+                                   input_num('Line'.$line_no);
+                       }
+                       else {
+                               $ok = 0;
+                       }
+                               
+               }
 
-       $sql = "SELECT stk_code, quantity, qty_invoiced FROM ".TB_PREF."sales_order_details WHERE
-               quantity - qty_invoiced > 0
-               AND order_no = " . $_SESSION['ProcessingOrder'];
+               if (isset($_POST['Line'.$line_no.'Desc'])) {
+                       $line_desc = $_POST['Line'.$line_no.'Desc'];
+                       if (strlen($line_desc) > 0) {
+                               $_SESSION['Items']->line_items[$line_no]->item_description = $line_desc;
+                       }
+               }
+       }
+ return $ok;
+}
 
-       $result = db_query($sql,"retreive sales order details");
+function set_delivery_shipping_sum($delivery_notes) 
+{
+    
+    $shipping = 0;
+    
+    foreach($delivery_notes as $delivery_num) 
+    {
+        $myrow = get_customer_trans($delivery_num, ST_CUSTDELIVERY);
 
-       if (db_num_rows($result) != count($_SESSION['Items']->line_items))
-       {
+        $shipping += $myrow['ov_freight'];
+    }
+    $_POST['ChargeFreightCost'] = price_format($shipping);
+}
 
-               /*there should be the same number of items returned from this query as there are lines on the invoice -
-                       if  not then someone has already invoiced or credited some lines */
-       if ($debug == 1)
-       {
-               display_note($sql, 1, 0);
-               display_note("No rows returned by sql:" . db_num_rows($result), 1, 0);
-               display_note("Count of items in the session " . count($_SESSION['Items']->line_items), 1, 0);
-       }
 
-               return false;
+function copy_to_cart()
+{
+       $cart = &$_SESSION['Items'];
+       $cart->due_date = $cart->document_date =  $_POST['InvoiceDate'];
+       $cart->Comments = $_POST['Comments'];
+       $cart->due_date =  $_POST['due_date'];
+       if (($cart->pos['cash_sale'] || $cart->pos['credit_sale']) && isset($_POST['payment'])) {
+               $cart->payment = $_POST['payment'];
+               $cart->payment_terms = get_payment_terms($_POST['payment']);
        }
-
-       while ($myrow = db_fetch($result)) 
+       if ($_SESSION['Items']->trans_no == 0)
+               $cart->reference = $_POST['ref'];
+       if (!$cart->is_prepaid())
        {
-               $stk_itm = $myrow["stk_code"];
-               if ($_SESSION['Items']->line_items[$stk_itm]->quantity != $myrow["quantity"] ||
-                       $_SESSION['Items']->line_items[$stk_itm]->qty_inv != $myrow["qty_invoiced"])
-               {
-                       display_note(_("Original order for") . " " . $myrow["stk_code"] . " " .
-                               _("has a quantity of") . " " . $myrow["quantity"] . " " . 
-                               _("and an invoiced quantity of") . " " . $myrow["qty_invoiced"] . " " .
-                               _("the session shows quantity of") . " " . 
-                               $_SESSION['Items']->line_items[$stk_itm]->quantity . " " . 
-                               _("and quantity invoice of") . " " . 
-                               $_SESSION['Items']->line_items[$stk_itm]->qty_inv, 1, 0);
+               $cart->ship_via = $_POST['ship_via'];
+               $cart->freight_cost = input_num('ChargeFreightCost');
+       }
 
-                       return false;
-               }
-       } /*loop through all line items of the order to ensure none have been invoiced */
+       $cart->update_payments();
 
-       return true;
+       $cart->dimension_id =  $_POST['dimension_id'];
+       $cart->dimension2_id =  $_POST['dimension2_id'];
 }
+//-----------------------------------------------------------------------------
 
+function copy_from_cart()
+{
+       $cart = &$_SESSION['Items'];
+       $_POST['Comments']= $cart->Comments;
+       $_POST['InvoiceDate']= $cart->document_date;
+       $_POST['ref'] = $cart->reference;
+       $_POST['cart_id'] = $cart->cart_id;
+       $_POST['due_date'] = $cart->due_date;
+       $_POST['payment'] = $cart->payment;
+       if (!$_SESSION['Items']->is_prepaid())
+       {
+               $_POST['ship_via'] = $cart->ship_via;
+               $_POST['ChargeFreightCost'] = price_format($cart->freight_cost);
+       }
+       $_POST['dimension_id'] = $cart->dimension_id;
+       $_POST['dimension2_id'] = $cart->dimension2_id;
+}
 
-//---------------------------------------------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
 
 function check_data()
 {
-       if (!isset($_POST['DispatchDate']) || !is_date($_POST['DispatchDate'])) 
-       {
+       global $Refs;
+
+       $prepaid = $_SESSION['Items']->is_prepaid();
+
+       if (!isset($_POST['InvoiceDate']) || !is_date($_POST['InvoiceDate'])) {
                display_error(_("The entered invoice date is invalid."));
+               set_focus('InvoiceDate');
                return false;
        }
-       if (!is_date_in_fiscalyear($_POST['DispatchDate'])) 
-       {
-               display_error(_("The entered invoice date is not in fiscal year."));
+
+       if (!is_date_in_fiscalyear($_POST['InvoiceDate'])) {
+               display_error(_("The entered date is out of fiscal year or is closed for further data entry."));
+               set_focus('InvoiceDate');
                return false;
        }
-       if (!isset($_POST['due_date']) || !is_date($_POST['due_date'])) 
-       {
+
+
+       if (!$prepaid &&(!isset($_POST['due_date']) || !is_date($_POST['due_date'])))   {
                display_error(_("The entered invoice due date is invalid."));
+               set_focus('due_date');
                return false;
        }
 
-       if (!references::is_valid($_POST['ref'])) 
-       {
-               display_error(_("You must enter a reference."));
-               return false;
+       if ($_SESSION['Items']->trans_no == 0) {
+               if (!$Refs->is_valid($_POST['ref'], ST_SALESINVOICE)) {
+                       display_error(_("You must enter a reference."));
+                       set_focus('ref');
+                       return false;
+               }
        }
 
-       if (!is_new_reference($_POST['ref'], 10)
+       if(!$prepaid
        {
-               display_error(_("The entered reference is already in use."));
-               return false;
-       }
-       if ($_POST['ChargeFreightCost'] == "")
-               $_POST['ChargeFreightCost'] = 0;
-       if (!is_numeric($_POST['ChargeFreightCost']) || $_POST['ChargeFreightCost'] < 0)        
-       {
-               display_error(_("The entered shipping value is not numeric."));
-               return false;
-       }
+               if ($_POST['ChargeFreightCost'] == "") {
+                       $_POST['ChargeFreightCost'] = price_format(0);
+               }
 
-       if ($_SESSION['Items']->has_items_dispatch() == 0 && $_POST['ChargeFreightCost'] == 0)  
-       {
-               display_error(_("There are no item quantities on this invoice."));
-               return false;
+               if (!check_num('ChargeFreightCost', 0)) {
+                       display_error(_("The entered shipping value is not numeric."));
+                       set_focus('ChargeFreightCost');
+                       return false;
+               }
+
+               if ($_SESSION['Items']->has_items_dispatch() == 0 && input_num('ChargeFreightCost') == 0) {
+                       display_error(_("There are no item quantities on this invoice."));
+                       return false;
+               }
+
+               if (!check_quantities()) {
+                       display_error(_("Selected quantity cannot be less than quantity credited nor more than quantity not invoiced yet."));
+                       return false;
+               }
+       } else {
+               if (($_SESSION['Items']->payment_terms['days_before_due'] == -1) && !count($_SESSION['Items']->prepayments)) {
+                       display_error(_("There is no non-invoiced payments for this order. If you want to issue final invoice, select delayed or cash payment terms."));
+                       return false;
+               }
        }
 
        return true;
 }
 
-//---------------------------------------------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+if (isset($_POST['process_invoice']) && check_data()) {
+       $newinvoice=  $_SESSION['Items']->trans_no == 0;
+       copy_to_cart();
 
-function check_qoh()
-{
-       if (!sys_prefs::allow_negative_stock())
+       if ($newinvoice) 
+               new_doc_date($_SESSION['Items']->document_date);
+
+       $invoice_no = $_SESSION['Items']->write();
+       if ($invoice_no == -1)
        {
-       foreach ($_SESSION['Items']->line_items as $itm) 
-       {
-
-                       if ($itm->qty_dispatched && has_stock_holding($itm->mb_flag))
-                       {
-                               $qoh = get_qoh_on_date($itm->stock_id, $_POST['Location'], $_POST['DispatchDate']);
-
-                       if ($itm->qty_dispatched > $qoh) 
-                       {
-                               display_error(_("The invoice cannot be processed because there is an insufficient quantity for component:") .
-                                       " " . $itm->stock_id . " - " .  $itm->item_description);
-                               return false;
-                       }
-               }
-       }
+               display_error(_("The entered reference is already in use."));
+               set_focus('ref');
        }
+       else
+       {
+               processing_end();
 
-       return true;
+               if ($newinvoice) {
+                       meta_forward($_SERVER['PHP_SELF'], "AddedID=$invoice_no");
+               } else {
+                       meta_forward($_SERVER['PHP_SELF'], "UpdatedID=$invoice_no");
+               }
+       }       
 }
 
-//---------------------------------------------------------------------------------------------------------------
-
-function process_invoice($invoicing=false)
-{
-       if ($invoicing) 
-       {
-               read_sales_order($_SESSION['Items']->order_no, $_SESSION['Items'], true);
-               $duedate = get_invoice_duedate($_SESSION['Items']->customer_id, $_SESSION['Items']->delivery_date);
-               $invoice_no = add_sales_invoice($_SESSION['Items'],
-                       $_SESSION['Items']->delivery_date, $duedate, $_SESSION['Items']->order_no,
-                       $_SESSION['Items']->tax_group_id, $_SESSION['Items']->freight_cost,
-                       $_SESSION['Items']->Location, $_SESSION['Items']->ship_via,
-                       $_SESSION['Items']->default_sales_type, references::get_next(10),
-                       $_SESSION['Items']->memo_, 0);
-       } 
-       else 
-       {
-       
-               if (!check_data())
-                       return;
-
-               if (!check_order_changed())
-                       order_changed_error();
-
-               if (!check_qoh())
-                       return;
-
-               if ($_POST['bo_policy'])
-                       $bo_policy = 0;
-               else
-                       $bo_policy = 1;
-
-               $invoice_no = add_sales_invoice($_SESSION['Items'],
-                       $_POST['DispatchDate'], $_POST['due_date'],     $_SESSION['ProcessingOrder'],
-                       $_POST['tax_group_id'], $_POST['ChargeFreightCost'], $_POST['Location'],
-                       $_POST['ship_via'],     $_POST['sales_type_id'], $_POST['ref'],
-                       $_POST['InvoiceText'], $bo_policy);
-               unset($_SESSION['ProcessingOrder']);
+if(list_updated('payment')) {
+       $order = &$_SESSION['Items']; 
+       copy_to_cart();
+       $order->payment = get_post('payment');
+       $order->payment_terms = get_payment_terms($order->payment);
+       $_POST['due_date'] = $order->due_date = get_invoice_duedate($order->payment, $order->document_date);
+       $_POST['Comments'] = '';
+       $Ajax->activate('due_date');
+       $Ajax->activate('options');
+       if ($order->payment_terms['cash_sale']) {
+               $_POST['Location'] = $order->Location = $order->pos['pos_location'];
+               $order->location_name = $order->pos['location_name'];
        }
+}
+
+// find delivery spans for batch invoice display
+$dspans = array();
+$lastdn = ''; $spanlen=1;
 
-       meta_forward($_SERVER['PHP_SELF'], "AddedID=$invoice_no");
+for ($line_no = 0; $line_no < count($_SESSION['Items']->line_items); $line_no++) {
+       $line = $_SESSION['Items']->line_items[$line_no];
+       if ($line->quantity == $line->qty_done) {
+               continue;
+       }
+       if ($line->src_no == $lastdn) {
+               $spanlen++;
+       } else {
+               if ($lastdn != '') {
+                       $dspans[] = $spanlen;
+                       $spanlen = 1;
+               }
+       }
+       $lastdn = $line->src_no;
 }
+$dspans[] = $spanlen;
 
-//---------------------------------------------------------------------------------------------------------------
-if (isset($_GET['process_invoice']))
-       process_invoice(true);
-elseif (isset($_POST['process_invoice']))
-       process_invoice();
+//-----------------------------------------------------------------------------
 
-//-------------------------------------------------------------------------------------------------
+$is_batch_invoice = count($_SESSION['Items']->src_docs) > 1;
+$prepaid = $_SESSION['Items']->is_prepaid();
 
-start_form(false, true);
+$is_edition = $_SESSION['Items']->trans_type == ST_SALESINVOICE && $_SESSION['Items']->trans_no != 0;
+start_form();
+hidden('cart_id');
 
-start_table("$table_style2 width=80%", 5);
-echo "<tr><td>"; // outer table
+start_table(TABLESTYLE2, "width='80%'", 5);
 
-start_table("$table_style width=100%");
 start_row();
+$colspan = 1;
+$dim = get_company_pref('use_dimension');
+if ($dim > 0) 
+       $colspan = 3;
 label_cells(_("Customer"), $_SESSION['Items']->customer_name, "class='tableheader2'");
 label_cells(_("Branch"), get_branch_name($_SESSION['Items']->Branch), "class='tableheader2'");
-label_cells(_("Currency"), $_SESSION['Items']->customer_currency, "class='tableheader2'");
+if (($_SESSION['Items']->pos['credit_sale'] || $_SESSION['Items']->pos['cash_sale'])) {
+       $paymcat = !$_SESSION['Items']->pos['cash_sale'] ? PM_CREDIT :
+               (!$_SESSION['Items']->pos['credit_sale'] ? PM_CASH : PM_ANY);
+       label_cells(_("Payment terms:"), sale_payment_list('payment', $paymcat),
+               "class='tableheader2'", "colspan=$colspan");
+} else
+       label_cells(_('Payment:'), $_SESSION['Items']->payment_terms['terms'], "class='tableheader2'", "colspan=$colspan");
+
 end_row();
 start_row();
 
-if (!isset($_POST['ref']))
-       $_POST['ref'] = references::get_next(10);
-
-ref_cells(_("Reference"), 'ref', null, "class='tableheader2'");
+if ($_SESSION['Items']->trans_no == 0) {
+       ref_cells(_("Reference"), 'ref', '', null, "class='tableheader2'", false, ST_SALESINVOICE,
+               array('customer' => $_SESSION['Items']->customer_id,
+                       'branch' => $_SESSION['Items']->Branch,
+                       'date' => get_post('InvoiceDate')));
+} else {
+       label_cells(_("Reference"), $_SESSION['Items']->reference, "class='tableheader2'");
+}
 
-if (!isset($_POST['tax_group_id']))
-       $_POST['tax_group_id'] = $_SESSION['Items']->tax_group_id;
-label_cell(_("Tax Group"), "class='tableheader2'");    
-tax_groups_list_cells(null, 'tax_group_id', $_POST['tax_group_id'], false, null, true);
+label_cells(_("Sales Type"), $_SESSION['Items']->sales_type_name, "class='tableheader2'");
 
-label_cells(_("For Sales Order"), get_customer_trans_view_str(systypes::sales_order(), $_SESSION['ProcessingOrder']), "class='tableheader2'");
+label_cells(_("Currency"), $_SESSION['Items']->customer_currency, "class='tableheader2'");
+if ($dim > 0) {
+       label_cell(_("Dimension").":", "class='tableheader2'");
+       $_POST['dimension_id'] = $_SESSION['Items']->dimension_id;
+       dimensions_list_cells(null, 'dimension_id', null, true, ' ', false, 1, false);
+}              
+else
+       hidden('dimension_id', 0);
 
 end_row();
 start_row();
 
-if (!isset($_POST['sales_type_id']))
-       $_POST['sales_type_id'] = $_SESSION['Items']->default_sales_type;
-label_cell(_("Sales Type"), "class='tableheader2'");   
-sales_types_list_cells(null, 'sales_type_id', $_POST['sales_type_id']);
-
-if (!isset($_POST['Location']))
-       $_POST['Location'] = $_SESSION['Items']->Location;
-label_cell(_("Delivery From"), "class='tableheader2'");        
-locations_list_cells(null, 'Location', $_POST['Location'], false, true);
-
-if (!isset($_POST['ship_via']))
+if (!isset($_POST['ship_via'])) {
        $_POST['ship_via'] = $_SESSION['Items']->ship_via;
-label_cell(_("Shipping Company"), "class='tableheader2'");     
-shippers_list_cells(null, 'ship_via', $_POST['ship_via']);
-end_row();
+}
+label_cell(_("Shipping Company"), "class='tableheader2'");
+if ($prepaid)
+{
+       $shipper = get_shipper($_SESSION['Items']->ship_via);
+       label_cells(null, $shipper['shipper_name']);
+} else
+       shippers_list_cells(null, 'ship_via', $_POST['ship_via']);
+
+if (!isset($_POST['InvoiceDate']) || !is_date($_POST['InvoiceDate'])) {
+       $_POST['InvoiceDate'] = new_doc_date();
+       if (!is_date_in_fiscalyear($_POST['InvoiceDate'])) {
+               $_POST['InvoiceDate'] = end_fiscalyear();
+       }
+}
 
-end_table();
+date_cells(_("Date"), 'InvoiceDate', '', $_SESSION['Items']->trans_no == 0, 
+       0, 0, 0, "class='tableheader2'", true);
 
-echo "</td><td>";// outer table
+if (!isset($_POST['due_date']) || !is_date($_POST['due_date'])) {
+       $_POST['due_date'] = get_invoice_duedate($_SESSION['Items']->payment, $_POST['InvoiceDate']);
+}
 
-start_table("$table_style width=90%");
+date_cells(_("Due Date"), 'due_date', '', null, 0, 0, 0, "class='tableheader2'");
+if ($dim > 1) {
+       label_cell(_("Dimension")." 2:", "class='tableheader2'");
+       $_POST['dimension2_id'] = $_SESSION['Items']->dimension2_id;
+       dimensions_list_cells(null, 'dimension2_id', null, true, ' ', false, 2, false);
+}              
+else
+       hidden('dimension2_id', 0);
+end_row();
+end_table();
 
-// set this up here cuz it's used to calc qoh
-if (!isset($_POST['DispatchDate']) || !is_date($_POST['DispatchDate']))
+$row = get_customer_to_order($_SESSION['Items']->customer_id);
+if ($row['dissallow_invoices'] == 1)
 {
-       $_POST['DispatchDate'] = Today();
-       if (!is_date_in_fiscalyear($_POST['DispatchDate']))
-               $_POST['DispatchDate'] = end_fiscalyear();
-}
-text_row(_("Date"), 'DispatchDate', $_POST['DispatchDate'], 10, 10, "class='tableheader'");
+       display_error(_("The selected customer account is currently on hold. Please contact the credit control personnel to discuss."));
+       end_form();
+       end_page();
+       exit();
+}      
 
-if (!isset($_POST['due_date']) || !is_date($_POST['due_date']))
-       //$_POST['due_date'] = $_POST['DispatchDate'];
-       $_POST['due_date'] = get_invoice_duedate($_SESSION['Items']->customer_id, $_POST['DispatchDate']);
+display_heading($prepaid ? _("Sales Order Items") : _("Invoice Items"));
 
-text_row(_("Due Date"), 'due_date', $_POST['due_date'], 10, 10, "class='tableheader'");
-end_table();
+div_start('Items');
 
-echo "</td></tr>";
-end_table(1); // outer table
+start_table(TABLESTYLE, "width='80%'");
+if ($prepaid)
+       $th = array(_("Item Code"), _("Item Description"), _("Units"), _("Quantity"),
+               _("Price"), _("Tax Type"), _("Discount"), _("Total"));
+else
+       $th = array(_("Item Code"), _("Item Description"), _("Delivered"), _("Units"), _("Invoiced"),
+               _("This Invoice"), _("Price"), _("Tax Type"), _("Discount"), _("Total"));
 
-display_heading(_("Invoice Items"));
+if ($is_batch_invoice) {
+    $th[] = _("DN");
+    $th[] = "";
+}
+
+if ($is_edition) {
+    $th[4] = _("Credited");
+}
 
-start_table("$table_style width=80%");
-$th = array(_("Item Code"), _("Item Description"), _("Ordered"), _("Units"), _("Delivered"),
-       _("This Delivery"), _("Price"), _("Tax Type"), _("Discount"), _("Total"));
 table_header($th);
 $k = 0;
 $has_marked = false;
 $show_qoh = true;
 
-foreach ($_SESSION['Items']->line_items as $ln_itm) 
-{
-
-    // if it's a non-stock item (eg. service) don't show qoh
-    if (sys_prefs::allow_negative_stock() || !has_stock_holding($ln_itm->mb_flag) ||
-               $ln_itm->qty_dispatched == 0)
-       $show_qoh = false;
-
-       if ($show_qoh)
-               $qoh = get_qoh_on_date($ln_itm->stock_id, $_POST['Location'], $_POST['DispatchDate']);
-
-       if ($show_qoh && ($ln_itm->qty_dispatched > $qoh)) 
-       {
-               // oops, we don't have enough of one of the component items
-               start_row("class='stockmankobg'");
-               $has_marked = true;
-       } 
-       else
-               alt_table_row_color($k);
+$dn_line_cnt = 0;
 
+foreach ($_SESSION['Items']->line_items as $line=>$ln_itm) {
+       if (!$prepaid && ($ln_itm->quantity == $ln_itm->qty_done)) {
+               continue; // this line was fully invoiced
+       }
+       alt_table_row_color($k);
        view_stock_status_cell($ln_itm->stock_id);
 
-       text_cells(null, $ln_itm->stock_id . "Desc", $ln_itm->item_description, 30, 50);
-       qty_cell($ln_itm->quantity);
+       if ($prepaid)
+               label_cell($ln_itm->item_description);
+       else
+               text_cells(null, 'Line'.$line.'Desc', $ln_itm->item_description, 30, 50);
+       $dec = get_qty_dec($ln_itm->stock_id);
+       if (!$prepaid)
+               qty_cell($ln_itm->quantity, false, $dec);
        label_cell($ln_itm->units);
-       qty_cell($ln_itm->qty_inv);
-
-       text_cells(null, $ln_itm->stock_id, $ln_itm->qty_dispatched, 10, 10);
-
-       $display_discount_percent = number_format2($ln_itm->discount_percent*100,user_percent_dec()) . "%";
+       if (!$prepaid)
+               qty_cell($ln_itm->qty_done, false, $dec);
+
+       if ($is_batch_invoice || $prepaid) {
+               // for batch invoices we can only remove whole deliveries
+               echo '<td nowrap align=right>';
+               hidden('Line' . $line, $ln_itm->qty_dispatched );
+               echo number_format2($ln_itm->qty_dispatched, $dec).'</td>';
+       } else {
+               small_qty_cells(null, 'Line'.$line, qty_format($ln_itm->qty_dispatched, $ln_itm->stock_id, $dec), null, null, $dec);
+       }
+       $display_discount_percent = percent_format($ln_itm->discount_percent*100) . " %";
 
        $line_total = ($ln_itm->qty_dispatched * $ln_itm->price * (1 - $ln_itm->discount_percent));
 
@@ -420,8 +572,16 @@ foreach ($_SESSION['Items']->line_items as $ln_itm)
        label_cell($display_discount_percent, "nowrap align=right");
        amount_cell($line_total);
 
-       //label_cell(get_tax_free_price_for_item($ln_itm->stock_id, $line_total, $_POST['tax_group_id']));
-
+       if ($is_batch_invoice) {
+               if ($dn_line_cnt == 0) {
+                       $dn_line_cnt = $dspans[0];
+                       $dspans = array_slice($dspans, 1);
+                       label_cell($ln_itm->src_no, "rowspan=$dn_line_cnt class='oddrow'");
+                       label_cell("<a href='" . $_SERVER['PHP_SELF'] . "?RemoveDN=".
+                               $ln_itm->src_no."'>" . _("Remove") . "</a>", "rowspan=$dn_line_cnt class='oddrow'");
+               }
+               $dn_line_cnt--;
+       }
        end_row();
 }
 
@@ -430,59 +590,81 @@ depending on the business logic required this condition may not be required.
 It seems unfair to charge the customer twice for freight if the order
 was not fully delivered the first time ?? */
 
-if (!isset($_POST['ChargeFreightCost']) || $_POST['ChargeFreightCost'] == "") 
-{
-    if ($_SESSION['Items']->any_already_delivered() == 1) 
-    {
-       $_POST['ChargeFreightCost'] = 0;
-    } 
-    else 
-    {
-       $_POST['ChargeFreightCost'] = $_SESSION['Items']->freight_cost;
-    }
-    if (!is_numeric($_POST['ChargeFreightCost']))
-    {
-       $_POST['ChargeFreightCost'] = 0;
-    }
+if (!isset($_POST['ChargeFreightCost']) || $_POST['ChargeFreightCost'] == "") {
+       if ($_SESSION['Items']->any_already_delivered() == 1) {
+               $_POST['ChargeFreightCost'] = price_format(0);
+       } else {
+               $_POST['ChargeFreightCost'] = price_format($_SESSION['Items']->freight_cost);
+       }
+
+       if (!check_num('ChargeFreightCost')) {
+               $_POST['ChargeFreightCost'] = price_format(0);
+       }
 }
 
-start_row();
+$accumulate_shipping = get_company_pref('accumulate_shipping');
+if ($is_batch_invoice && $accumulate_shipping)
+       set_delivery_shipping_sum(array_keys($_SESSION['Items']->src_docs));
 
-small_amount_cells(_("Shipping Cost"), 'ChargeFreightCost', null, "colspan=9 align=right");
+$colspan = $prepaid ? 7:9;
+start_row();
+label_cell(_("Shipping Cost"), "colspan=$colspan align=right");
+if ($prepaid)
+       label_cell($_POST['ChargeFreightCost'], 'align=right');
+else
+       small_amount_cells(null, 'ChargeFreightCost', null);
+if ($is_batch_invoice) {
+label_cell('', 'colspan=2');
+}
 
+end_row();
 $inv_items_total = $_SESSION['Items']->get_items_total_dispatch();
 
-$display_sub_total = number_format2($inv_items_total + $_POST['ChargeFreightCost'],user_price_dec());
+$display_sub_total = price_format($inv_items_total + input_num('ChargeFreightCost'));
 
-label_row(_("Sub-total"), $display_sub_total, "colspan=9 align=right","align=right");
+label_row(_("Sub-total"), $display_sub_total, "colspan=$colspan align=right","align=right", $is_batch_invoice ? 2 : 0);
 
-$taxes = $_SESSION['Items']->get_taxes($_POST['tax_group_id'], $_POST['ChargeFreightCost']);
-$tax_total = display_edit_tax_items($taxes, 9);
+$taxes = $_SESSION['Items']->get_taxes(input_num('ChargeFreightCost'));
+$tax_total = display_edit_tax_items($taxes, $colspan, $_SESSION['Items']->tax_included, $is_batch_invoice ? 2 : 0);
 
-$display_total = number_format2(($inv_items_total + $_POST['ChargeFreightCost'] + $tax_total), user_price_dec());
+$display_total = price_format(($inv_items_total + input_num('ChargeFreightCost') + $tax_total));
 
-label_row(_("Invoice Total"), $display_total, "colspan=9 align=right","align=right");
+label_row(_("Invoice Total"), $display_total, "colspan=$colspan align=right","align=right", $is_batch_invoice ? 2 : 0);
 
 end_table(1);
+div_end();
+div_start('options');
+start_table(TABLESTYLE2);
+if ($prepaid)
+{
 
-if ($has_marked)
-       display_note(_("Marked items have insufficient quantities in stock."), 0, 1, "class='red'");
+       label_row(_("Sales order:"), get_trans_view_str(ST_SALESORDER, $_SESSION['Items']->order_no, get_reference(ST_SALESORDER, $_SESSION['Items']->order_no)));
 
-start_table($table_style2);
+       $list = array(); $allocs = 0;
+       if (count($_SESSION['Items']->prepayments))
+       {
+               foreach($_SESSION['Items']->prepayments as $pmt)
+               {
+                       $list[] = get_trans_view_str($pmt['trans_type_from'], $pmt['trans_no_from'], get_reference($pmt['trans_type_from'], $pmt['trans_no_from']));
+                       $allocs += $pmt['amt'];
+               }
+       }
 
-policy_list_row(_("Action For Balance"), "bo_policy", null);
+       label_row(_("Payments received:"), implode(',', $list));
+       label_row(_("Invoiced here:"), price_format($_SESSION['Items']->prep_amount), 'class=label');
+       label_row(_("Left to be invoiced:"), price_format($_SESSION['Items']->get_trans_total()-max($_SESSION['Items']->prep_amount, $allocs)), 'class=label');
+}
 
-textarea_row(_("Memo"), 'InvoiceText', null, 50, 4);
+textarea_row(_("Memo:"), 'Comments', null, 50, 4);
 
 end_table(1);
-
-submit_center_first('Update', _("Update"));
-submit_center_last('process_invoice', _("Process Invoice"));
+div_end();
+submit_center_first('Update', _("Update"),
+  _('Refresh document page'), true);
+submit_center_last('process_invoice', _("Process Invoice"),
+  _('Check entered data and save document'), 'default');
 
 end_form();
 
-//---------------------------------------------------------------------------------------------
-
 end_page();
 
-?>