5 include_once($path_to_root . "/sales/includes/cart_class.inc");
6 include_once($path_to_root . "/includes/session.inc");
8 include_once($path_to_root . "/includes/data_checks.inc");
10 include_once($path_to_root . "/includes/manufacturing.inc");
11 include_once($path_to_root . "/sales/includes/sales_db.inc");
12 include_once($path_to_root . "/sales/includes/sales_ui.inc");
14 include_once($path_to_root . "/taxes/tax_calc.inc");
17 if ($use_popup_windows)
18 $js .= get_js_open_window(900, 500);
20 $js .= get_js_date_picker();
21 page(_("Issue an Invoice and Deliver Items for a Sales Order"), false, false, "", $js);
23 //---------------------------------------------------------------------------------------------------------------
25 if (isset($_GET['AddedID']))
27 $invoice_no = $_GET['AddedID'];
30 display_notification(_("Invoice processed"), true);
31 display_note(get_customer_trans_view_str($trans_type, $invoice_no, _("View this invoice")), 0, 1);
33 display_note(get_gl_view_str($trans_type, $invoice_no, _("View the GL Journal Entries for this Invoice")));
35 if ($_SESSION['Items']->direct_invoice)
36 hyperlink_params("$path_to_root/sales/sales_order_entry.php", _("Issue Another Invoice"), "NewInvoice=Yes");
38 hyperlink_params("$path_to_root/sales/inquiry/sales_orders_view.php", _("Select Another Order For Invoicing"), "OutstandingOnly=1");
40 unset($_SESSION['Items']->line_items);
41 unset($_SESSION['Items']);
42 display_footer_exit();
45 //---------------------------------------------------------------------------------------------------------------
47 if (!isset($_GET['OrderNumber']) && !isset($_SESSION['ProcessingOrder']) &&
48 !isset($_GET['process_invoice']))
50 /* This page can only be called with an order number for invoicing*/
51 display_error(_("This page can only be opened if an order has been selected. Please select an order first."));
53 hyperlink_no_params("$path_to_root/sales/inquiry/sales_orders_view.php", _("Select a sales order to invoice"));
59 elseif (isset($_GET['OrderNumber']) && $_GET['OrderNumber'] > 0)
62 if (isset($_SESSION['Items']))
64 unset($_SESSION['Items']->line_items);
65 unset ($_SESSION['Items']);
68 session_register("Items");
69 session_register("ProcessingOrder");
71 $_SESSION['ProcessingOrder'] = $_GET['OrderNumber'];
72 $_SESSION['Items'] = new cart;
74 /*read in all the selected order into the Items cart */
76 if (read_sales_order($_SESSION['ProcessingOrder'], $_SESSION['Items'], true))
79 if ($_SESSION['Items']->count_items() == 0)
81 hyperlink_params($path_to_root . "/sales/inquiry/sales_orders_view.php", _("Select a different sales order to invoice"), "OutstandingOnly=1");
82 die ("<br><b>" . _("There are no ordered items with a quantity left to deliver. There is nothing left to invoice.") . "</b>");
87 hyperlink_no_params("/sales_orders_view.php", _("Select a sales order to invoice"));
88 die ("<br><b>" . _("This order item could not be retrieved. Please select another order.") . "</b>");
94 /* if processing, a dispatch page has been called and ${$StkItm->stock_id} would have been set from the post */
95 foreach ($_SESSION['Items']->line_items as $itm)
98 if (isset($_SESSION['Items']->line_items[$itm->stock_id]) &&
99 isset($_POST[$itm->stock_id]) && is_numeric($_POST[$itm->stock_id]) &&
100 $_POST[$itm->stock_id] <= ($_SESSION['Items']->line_items[$itm->stock_id]->quantity -
101 $_SESSION['Items']->line_items[$itm->stock_id]->qty_inv))
103 $_SESSION['Items']->line_items[$itm->stock_id]->qty_dispatched = $_POST[$itm->stock_id];
106 if (isset($_POST[$itm->stock_id . "Desc"]) && strlen($_POST[$itm->stock_id . "Desc"]) > 0)
108 $_SESSION['Items']->line_items[$itm->stock_id]->item_description = $_POST[$itm->stock_id . "Desc"];
113 //---------------------------------------------------------------------------------------------------------------
115 function order_changed_error()
117 global $path_to_root;
118 display_note(_("This order has been changed or invoiced since this delivery was started to be confirmed. Processing halted."), 1, 0);
119 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);
121 hyperlink_no_params("$path_to_root/sales/inquiry/sales_orders_view.php", _("Select a sales order for confirming deliveries and invoicing"));
123 unset($_SESSION['Items']->line_items);
124 unset($_SESSION['Items']);
125 unset($_SESSION['ProcessingOrder']);
129 //---------------------------------------------------------------------------------------------------------------
131 function check_order_changed()
135 /*Now need to check that the order details are the same as they were when
136 they were read into the Items array.
137 If they've changed then someone else may have invoiced them -
138 as modified for bug pointed out by Sherif 1-7-03*/
140 $sql = "SELECT stk_code, quantity, qty_invoiced FROM ".TB_PREF."sales_order_details WHERE
141 quantity - qty_invoiced > 0
142 AND order_no = " . $_SESSION['ProcessingOrder'];
144 $result = db_query($sql,"retreive sales order details");
146 if (db_num_rows($result) != count($_SESSION['Items']->line_items))
149 /*there should be the same number of items returned from this query as there are lines on the invoice -
150 if not then someone has already invoiced or credited some lines */
153 display_note($sql, 1, 0);
154 display_note("No rows returned by sql:" . db_num_rows($result), 1, 0);
155 display_note("Count of items in the session " . count($_SESSION['Items']->line_items), 1, 0);
161 while ($myrow = db_fetch($result))
163 $stk_itm = $myrow["stk_code"];
164 if ($_SESSION['Items']->line_items[$stk_itm]->quantity != $myrow["quantity"] ||
165 $_SESSION['Items']->line_items[$stk_itm]->qty_inv != $myrow["qty_invoiced"])
167 display_note(_("Original order for") . " " . $myrow["stk_code"] . " " .
168 _("has a quantity of") . " " . $myrow["quantity"] . " " .
169 _("and an invoiced quantity of") . " " . $myrow["qty_invoiced"] . " " .
170 _("the session shows quantity of") . " " .
171 $_SESSION['Items']->line_items[$stk_itm]->quantity . " " .
172 _("and quantity invoice of") . " " .
173 $_SESSION['Items']->line_items[$stk_itm]->qty_inv, 1, 0);
177 } /*loop through all line items of the order to ensure none have been invoiced */
183 //---------------------------------------------------------------------------------------------------------------
185 function check_data()
187 if (!isset($_POST['DispatchDate']) || !is_date($_POST['DispatchDate']))
189 display_error(_("The entered invoice date is invalid."));
192 if (!is_date_in_fiscalyear($_POST['DispatchDate']))
194 display_error(_("The entered invoice date is not in fiscal year."));
197 if (!isset($_POST['due_date']) || !is_date($_POST['due_date']))
199 display_error(_("The entered invoice due date is invalid."));
203 if (!references::is_valid($_POST['ref']))
205 display_error(_("You must enter a reference."));
209 if (!is_new_reference($_POST['ref'], 10))
211 display_error(_("The entered reference is already in use."));
214 if ($_POST['ChargeFreightCost'] == "")
215 $_POST['ChargeFreightCost'] = 0;
216 if (!is_numeric($_POST['ChargeFreightCost']) || $_POST['ChargeFreightCost'] < 0)
218 display_error(_("The entered shipping value is not numeric."));
222 if ($_SESSION['Items']->has_items_dispatch() == 0 && $_POST['ChargeFreightCost'] == 0)
224 display_error(_("There are no item quantities on this invoice."));
231 //---------------------------------------------------------------------------------------------------------------
235 if (!sys_prefs::allow_negative_stock())
237 foreach ($_SESSION['Items']->line_items as $itm)
240 if ($itm->qty_dispatched && has_stock_holding($itm->mb_flag))
242 $qoh = get_qoh_on_date($itm->stock_id, $_POST['Location'], $_POST['DispatchDate']);
244 if ($itm->qty_dispatched > $qoh)
246 display_error(_("The invoice cannot be processed because there is an insufficient quantity for component:") .
247 " " . $itm->stock_id . " - " . $itm->item_description);
257 //---------------------------------------------------------------------------------------------------------------
259 function process_invoice($invoicing=false)
263 read_sales_order($_SESSION['Items']->order_no, $_SESSION['Items'], true);
264 $duedate = get_invoice_duedate($_SESSION['Items']->customer_id, $_SESSION['Items']->delivery_date);
265 $invoice_no = add_sales_invoice($_SESSION['Items'],
266 $_SESSION['Items']->delivery_date, $duedate, $_SESSION['Items']->order_no,
267 $_SESSION['Items']->tax_group_id, $_SESSION['Items']->freight_cost,
268 $_SESSION['Items']->Location, $_SESSION['Items']->ship_via,
269 $_SESSION['Items']->default_sales_type, references::get_next(10),
270 $_SESSION['Items']->memo_, 0);
278 if (!check_order_changed())
279 order_changed_error();
284 if ($_POST['bo_policy'])
289 $invoice_no = add_sales_invoice($_SESSION['Items'],
290 $_POST['DispatchDate'], $_POST['due_date'], $_SESSION['ProcessingOrder'],
291 $_POST['tax_group_id'], $_POST['ChargeFreightCost'], $_POST['Location'],
292 $_POST['ship_via'], $_POST['sales_type_id'], $_POST['ref'],
293 $_POST['InvoiceText'], $bo_policy);
294 unset($_SESSION['ProcessingOrder']);
297 meta_forward($_SERVER['PHP_SELF'], "AddedID=$invoice_no");
300 //---------------------------------------------------------------------------------------------------------------
301 if (isset($_GET['process_invoice']))
302 process_invoice(true);
303 elseif (isset($_POST['process_invoice']))
306 //-------------------------------------------------------------------------------------------------
308 start_form(false, true);
310 start_table("$table_style2 width=80%", 5);
311 echo "<tr><td>"; // outer table
313 start_table("$table_style width=100%");
315 label_cells(_("Customer"), $_SESSION['Items']->customer_name, "class='tableheader2'");
316 label_cells(_("Branch"), get_branch_name($_SESSION['Items']->Branch), "class='tableheader2'");
317 label_cells(_("Currency"), $_SESSION['Items']->customer_currency, "class='tableheader2'");
321 if (!isset($_POST['ref']))
322 $_POST['ref'] = references::get_next(10);
324 ref_cells(_("Reference"), 'ref', null, "class='tableheader2'");
326 if (!isset($_POST['tax_group_id']))
327 $_POST['tax_group_id'] = $_SESSION['Items']->tax_group_id;
328 label_cell(_("Tax Group"), "class='tableheader2'");
329 tax_groups_list_cells(null, 'tax_group_id', $_POST['tax_group_id'], false, null, true);
331 label_cells(_("For Sales Order"), get_customer_trans_view_str(systypes::sales_order(), $_SESSION['ProcessingOrder']), "class='tableheader2'");
336 if (!isset($_POST['sales_type_id']))
337 $_POST['sales_type_id'] = $_SESSION['Items']->default_sales_type;
338 label_cell(_("Sales Type"), "class='tableheader2'");
339 sales_types_list_cells(null, 'sales_type_id', $_POST['sales_type_id']);
341 if (!isset($_POST['Location']))
342 $_POST['Location'] = $_SESSION['Items']->Location;
343 label_cell(_("Delivery From"), "class='tableheader2'");
344 locations_list_cells(null, 'Location', $_POST['Location'], false, true);
346 if (!isset($_POST['ship_via']))
347 $_POST['ship_via'] = $_SESSION['Items']->ship_via;
348 label_cell(_("Shipping Company"), "class='tableheader2'");
349 shippers_list_cells(null, 'ship_via', $_POST['ship_via']);
354 echo "</td><td>";// outer table
356 start_table("$table_style width=90%");
358 // set this up here cuz it's used to calc qoh
359 if (!isset($_POST['DispatchDate']) || !is_date($_POST['DispatchDate']))
361 $_POST['DispatchDate'] = Today();
362 if (!is_date_in_fiscalyear($_POST['DispatchDate']))
363 $_POST['DispatchDate'] = end_fiscalyear();
365 date_row(_("Date"), 'DispatchDate', $_POST['DispatchDate'], 0, 0, 0, "class='tableheader'");
367 if (!isset($_POST['due_date']) || !is_date($_POST['due_date']))
368 //$_POST['due_date'] = $_POST['DispatchDate'];
369 $_POST['due_date'] = get_invoice_duedate($_SESSION['Items']->customer_id, $_POST['DispatchDate']);
371 date_row(_("Due Date"), 'due_date', $_POST['due_date'], 0, 0, 0, "class='tableheader'");
375 end_table(1); // outer table
377 display_heading(_("Invoice Items"));
379 start_table("$table_style width=80%");
380 $th = array(_("Item Code"), _("Item Description"), _("Ordered"), _("Units"), _("Delivered"),
381 _("This Delivery"), _("Price"), _("Tax Type"), _("Discount"), _("Total"));
387 foreach ($_SESSION['Items']->line_items as $ln_itm)
390 // if it's a non-stock item (eg. service) don't show qoh
391 if (sys_prefs::allow_negative_stock() || !has_stock_holding($ln_itm->mb_flag) ||
392 $ln_itm->qty_dispatched == 0)
396 $qoh = get_qoh_on_date($ln_itm->stock_id, $_POST['Location'], $_POST['DispatchDate']);
398 if ($show_qoh && ($ln_itm->qty_dispatched > $qoh))
400 // oops, we don't have enough of one of the component items
401 start_row("class='stockmankobg'");
405 alt_table_row_color($k);
407 view_stock_status_cell($ln_itm->stock_id);
409 text_cells(null, $ln_itm->stock_id . "Desc", $ln_itm->item_description, 30, 50);
410 qty_cell($ln_itm->quantity);
411 label_cell($ln_itm->units);
412 qty_cell($ln_itm->qty_inv);
414 text_cells(null, $ln_itm->stock_id, $ln_itm->qty_dispatched, 10, 10);
416 $display_discount_percent = number_format2($ln_itm->discount_percent*100,user_percent_dec()) . "%";
418 $line_total = ($ln_itm->qty_dispatched * $ln_itm->price * (1 - $ln_itm->discount_percent));
420 amount_cell($ln_itm->price);
421 label_cell($ln_itm->tax_type_name);
422 label_cell($display_discount_percent, "nowrap align=right");
423 amount_cell($line_total);
425 //label_cell(get_tax_free_price_for_item($ln_itm->stock_id, $line_total, $_POST['tax_group_id']));
430 /*Don't re-calculate freight if some of the order has already been delivered -
431 depending on the business logic required this condition may not be required.
432 It seems unfair to charge the customer twice for freight if the order
433 was not fully delivered the first time ?? */
435 if (!isset($_POST['ChargeFreightCost']) || $_POST['ChargeFreightCost'] == "")
437 if ($_SESSION['Items']->any_already_delivered() == 1)
439 $_POST['ChargeFreightCost'] = 0;
443 $_POST['ChargeFreightCost'] = $_SESSION['Items']->freight_cost;
445 if (!is_numeric($_POST['ChargeFreightCost']))
447 $_POST['ChargeFreightCost'] = 0;
453 small_amount_cells(_("Shipping Cost"), 'ChargeFreightCost', null, "colspan=9 align=right");
455 $inv_items_total = $_SESSION['Items']->get_items_total_dispatch();
457 $display_sub_total = number_format2($inv_items_total + $_POST['ChargeFreightCost'],user_price_dec());
459 label_row(_("Sub-total"), $display_sub_total, "colspan=9 align=right","align=right");
461 $taxes = $_SESSION['Items']->get_taxes($_POST['tax_group_id'], $_POST['ChargeFreightCost']);
462 $tax_total = display_edit_tax_items($taxes, 9);
464 $display_total = number_format2(($inv_items_total + $_POST['ChargeFreightCost'] + $tax_total), user_price_dec());
466 label_row(_("Invoice Total"), $display_total, "colspan=9 align=right","align=right");
471 display_note(_("Marked items have insufficient quantities in stock."), 0, 1, "class='red'");
473 start_table($table_style2);
475 policy_list_row(_("Action For Balance"), "bo_policy", null);
477 textarea_row(_("Memo"), 'InvoiceText', null, 50, 4);
481 submit_center_first('Update', _("Update"));
482 submit_center_last('process_invoice', _("Process Invoice"));
486 //---------------------------------------------------------------------------------------------