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);
19 page(_("Issue an Invoice and Deliver Items for a Sales Order"), false, false, "", $js);
21 //---------------------------------------------------------------------------------------------------------------
23 if (isset($_GET['AddedID']))
25 $invoice_no = $_GET['AddedID'];
28 display_notification(_("Invoice processed"), true);
29 display_note(get_customer_trans_view_str($trans_type, $invoice_no, _("View this invoice")), 0, 1);
31 display_note(get_gl_view_str($trans_type, $invoice_no, _("View the GL Journal Entries for this Invoice")));
33 if ($_SESSION['Items']->direct_invoice)
34 hyperlink_params("$path_to_root/sales/sales_order_entry.php", _("Issue Another Invoice"), "NewInvoice=Yes");
36 hyperlink_params("$path_to_root/sales/inquiry/sales_orders_view.php", _("Select Another Order For Invoicing"), "OutstandingOnly=1");
38 unset($_SESSION['Items']->line_items);
39 unset($_SESSION['Items']);
40 display_footer_exit();
43 //---------------------------------------------------------------------------------------------------------------
45 if (!isset($_GET['OrderNumber']) && !isset($_SESSION['ProcessingOrder']) &&
46 !isset($_GET['process_invoice']))
48 /* This page can only be called with an order number for invoicing*/
49 display_error(_("This page can only be opened if an order has been selected. Please select an order first."));
51 hyperlink_no_params("$path_to_root/sales/inquiry/sales_orders_view.php", _("Select a sales order to invoice"));
57 elseif (isset($_GET['OrderNumber']) && $_GET['OrderNumber'] > 0)
60 if (isset($_SESSION['Items']))
62 unset($_SESSION['Items']->line_items);
63 unset ($_SESSION['Items']);
66 session_register("Items");
67 session_register("ProcessingOrder");
69 $_SESSION['ProcessingOrder'] = $_GET['OrderNumber'];
70 $_SESSION['Items'] = new cart;
72 /*read in all the selected order into the Items cart */
74 if (read_sales_order($_SESSION['ProcessingOrder'], $_SESSION['Items'], true))
77 if ($_SESSION['Items']->count_items() == 0)
79 hyperlink_params($path_to_root . "/sales/inquiry/sales_orders_view.php", _("Select a different sales order to invoice"), "OutstandingOnly=1");
80 die ("<br><b>" . _("There are no ordered items with a quantity left to deliver. There is nothing left to invoice.") . "</b>");
85 hyperlink_no_params("/sales_orders_view.php", _("Select a sales order to invoice"));
86 die ("<br><b>" . _("This order item could not be retrieved. Please select another order.") . "</b>");
92 /* if processing, a dispatch page has been called and ${$StkItm->stock_id} would have been set from the post */
93 foreach ($_SESSION['Items']->line_items as $itm)
96 if (isset($_SESSION['Items']->line_items[$itm->stock_id]) &&
97 isset($_POST[$itm->stock_id]) && is_numeric($_POST[$itm->stock_id]) &&
98 $_POST[$itm->stock_id] <= ($_SESSION['Items']->line_items[$itm->stock_id]->quantity -
99 $_SESSION['Items']->line_items[$itm->stock_id]->qty_inv))
101 $_SESSION['Items']->line_items[$itm->stock_id]->qty_dispatched = $_POST[$itm->stock_id];
104 if (isset($_POST[$itm->stock_id . "Desc"]) && strlen($_POST[$itm->stock_id . "Desc"]) > 0)
106 $_SESSION['Items']->line_items[$itm->stock_id]->item_description = $_POST[$itm->stock_id . "Desc"];
111 //---------------------------------------------------------------------------------------------------------------
113 function order_changed_error()
115 global $path_to_root;
116 display_note(_("This order has been changed or invoiced since this delivery was started to be confirmed. Processing halted."), 1, 0);
117 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);
119 hyperlink_no_params("$path_to_root/sales/inquiry/sales_orders_view.php", _("Select a sales order for confirming deliveries and invoicing"));
121 unset($_SESSION['Items']->line_items);
122 unset($_SESSION['Items']);
123 unset($_SESSION['ProcessingOrder']);
127 //---------------------------------------------------------------------------------------------------------------
129 function check_order_changed()
133 /*Now need to check that the order details are the same as they were when
134 they were read into the Items array.
135 If they've changed then someone else may have invoiced them -
136 as modified for bug pointed out by Sherif 1-7-03*/
138 $sql = "SELECT stk_code, quantity, qty_invoiced FROM ".TB_PREF."sales_order_details WHERE
139 quantity - qty_invoiced > 0
140 AND order_no = " . $_SESSION['ProcessingOrder'];
142 $result = db_query($sql,"retreive sales order details");
144 if (db_num_rows($result) != count($_SESSION['Items']->line_items))
147 /*there should be the same number of items returned from this query as there are lines on the invoice -
148 if not then someone has already invoiced or credited some lines */
151 display_note($sql, 1, 0);
152 display_note("No rows returned by sql:" . db_num_rows($result), 1, 0);
153 display_note("Count of items in the session " . count($_SESSION['Items']->line_items), 1, 0);
159 while ($myrow = db_fetch($result))
161 $stk_itm = $myrow["stk_code"];
162 if ($_SESSION['Items']->line_items[$stk_itm]->quantity != $myrow["quantity"] ||
163 $_SESSION['Items']->line_items[$stk_itm]->qty_inv != $myrow["qty_invoiced"])
165 display_note(_("Original order for") . " " . $myrow["stk_code"] . " " .
166 _("has a quantity of") . " " . $myrow["quantity"] . " " .
167 _("and an invoiced quantity of") . " " . $myrow["qty_invoiced"] . " " .
168 _("the session shows quantity of") . " " .
169 $_SESSION['Items']->line_items[$stk_itm]->quantity . " " .
170 _("and quantity invoice of") . " " .
171 $_SESSION['Items']->line_items[$stk_itm]->qty_inv, 1, 0);
175 } /*loop through all line items of the order to ensure none have been invoiced */
181 //---------------------------------------------------------------------------------------------------------------
183 function check_data()
185 if (!isset($_POST['DispatchDate']) || !is_date($_POST['DispatchDate']))
187 display_error(_("The entered invoice date is invalid."));
190 if (!is_date_in_fiscalyear($_POST['DispatchDate']))
192 display_error(_("The entered invoice date is not in fiscal year."));
195 if (!isset($_POST['due_date']) || !is_date($_POST['due_date']))
197 display_error(_("The entered invoice due date is invalid."));
201 if (!references::is_valid($_POST['ref']))
203 display_error(_("You must enter a reference."));
207 if (!is_new_reference($_POST['ref'], 10))
209 display_error(_("The entered reference is already in use."));
212 if ($_POST['ChargeFreightCost'] == "")
213 $_POST['ChargeFreightCost'] = 0;
214 if (!is_numeric($_POST['ChargeFreightCost']) || $_POST['ChargeFreightCost'] < 0)
216 display_error(_("The entered shipping value is not numeric."));
220 if ($_SESSION['Items']->has_items_dispatch() == 0 && $_POST['ChargeFreightCost'] == 0)
222 display_error(_("There are no item quantities on this invoice."));
229 //---------------------------------------------------------------------------------------------------------------
233 if (!sys_prefs::allow_negative_stock())
235 foreach ($_SESSION['Items']->line_items as $itm)
238 if ($itm->qty_dispatched && has_stock_holding($itm->mb_flag))
240 $qoh = get_qoh_on_date($itm->stock_id, $_POST['Location'], $_POST['DispatchDate']);
242 if ($itm->qty_dispatched > $qoh)
244 display_error(_("The invoice cannot be processed because there is an insufficient quantity for component:") .
245 " " . $itm->stock_id . " - " . $itm->item_description);
255 //---------------------------------------------------------------------------------------------------------------
257 function process_invoice($invoicing=false)
261 read_sales_order($_SESSION['Items']->order_no, $_SESSION['Items'], true);
262 $duedate = get_invoice_duedate($_SESSION['Items']->customer_id, $_SESSION['Items']->delivery_date);
263 $invoice_no = add_sales_invoice($_SESSION['Items'],
264 $_SESSION['Items']->delivery_date, $duedate, $_SESSION['Items']->order_no,
265 $_SESSION['Items']->tax_group_id, $_SESSION['Items']->freight_cost,
266 $_SESSION['Items']->Location, $_SESSION['Items']->ship_via,
267 $_SESSION['Items']->default_sales_type, references::get_next(10),
268 $_SESSION['Items']->memo_, 0);
276 if (!check_order_changed())
277 order_changed_error();
282 if ($_POST['bo_policy'])
287 $invoice_no = add_sales_invoice($_SESSION['Items'],
288 $_POST['DispatchDate'], $_POST['due_date'], $_SESSION['ProcessingOrder'],
289 $_POST['tax_group_id'], $_POST['ChargeFreightCost'], $_POST['Location'],
290 $_POST['ship_via'], $_POST['sales_type_id'], $_POST['ref'],
291 $_POST['InvoiceText'], $bo_policy);
292 unset($_SESSION['ProcessingOrder']);
295 meta_forward($_SERVER['PHP_SELF'], "AddedID=$invoice_no");
298 //---------------------------------------------------------------------------------------------------------------
299 if (isset($_GET['process_invoice']))
300 process_invoice(true);
301 elseif (isset($_POST['process_invoice']))
304 //-------------------------------------------------------------------------------------------------
306 start_form(false, true);
308 start_table("$table_style2 width=80%", 5);
309 echo "<tr><td>"; // outer table
311 start_table("$table_style width=100%");
313 label_cells(_("Customer"), $_SESSION['Items']->customer_name, "class='tableheader2'");
314 label_cells(_("Branch"), get_branch_name($_SESSION['Items']->Branch), "class='tableheader2'");
315 label_cells(_("Currency"), $_SESSION['Items']->customer_currency, "class='tableheader2'");
319 if (!isset($_POST['ref']))
320 $_POST['ref'] = references::get_next(10);
322 ref_cells(_("Reference"), 'ref', null, "class='tableheader2'");
324 if (!isset($_POST['tax_group_id']))
325 $_POST['tax_group_id'] = $_SESSION['Items']->tax_group_id;
326 label_cell(_("Tax Group"), "class='tableheader2'");
327 tax_groups_list_cells(null, 'tax_group_id', $_POST['tax_group_id'], false, null, true);
329 label_cells(_("For Sales Order"), get_customer_trans_view_str(systypes::sales_order(), $_SESSION['ProcessingOrder']), "class='tableheader2'");
334 if (!isset($_POST['sales_type_id']))
335 $_POST['sales_type_id'] = $_SESSION['Items']->default_sales_type;
336 label_cell(_("Sales Type"), "class='tableheader2'");
337 sales_types_list_cells(null, 'sales_type_id', $_POST['sales_type_id']);
339 if (!isset($_POST['Location']))
340 $_POST['Location'] = $_SESSION['Items']->Location;
341 label_cell(_("Delivery From"), "class='tableheader2'");
342 locations_list_cells(null, 'Location', $_POST['Location'], false, true);
344 if (!isset($_POST['ship_via']))
345 $_POST['ship_via'] = $_SESSION['Items']->ship_via;
346 label_cell(_("Shipping Company"), "class='tableheader2'");
347 shippers_list_cells(null, 'ship_via', $_POST['ship_via']);
352 echo "</td><td>";// outer table
354 start_table("$table_style width=90%");
356 // set this up here cuz it's used to calc qoh
357 if (!isset($_POST['DispatchDate']) || !is_date($_POST['DispatchDate']))
359 $_POST['DispatchDate'] = Today();
360 if (!is_date_in_fiscalyear($_POST['DispatchDate']))
361 $_POST['DispatchDate'] = end_fiscalyear();
363 text_row(_("Date"), 'DispatchDate', $_POST['DispatchDate'], 10, 10, "class='tableheader'");
365 if (!isset($_POST['due_date']) || !is_date($_POST['due_date']))
366 //$_POST['due_date'] = $_POST['DispatchDate'];
367 $_POST['due_date'] = get_invoice_duedate($_SESSION['Items']->customer_id, $_POST['DispatchDate']);
369 text_row(_("Due Date"), 'due_date', $_POST['due_date'], 10, 10, "class='tableheader'");
373 end_table(1); // outer table
375 display_heading(_("Invoice Items"));
377 start_table("$table_style width=80%");
378 $th = array(_("Item Code"), _("Item Description"), _("Ordered"), _("Units"), _("Delivered"),
379 _("This Delivery"), _("Price"), _("Tax Type"), _("Discount"), _("Total"));
385 foreach ($_SESSION['Items']->line_items as $ln_itm)
388 // if it's a non-stock item (eg. service) don't show qoh
389 if (sys_prefs::allow_negative_stock() || !has_stock_holding($ln_itm->mb_flag) ||
390 $ln_itm->qty_dispatched == 0)
394 $qoh = get_qoh_on_date($ln_itm->stock_id, $_POST['Location'], $_POST['DispatchDate']);
396 if ($show_qoh && ($ln_itm->qty_dispatched > $qoh))
398 // oops, we don't have enough of one of the component items
399 start_row("class='stockmankobg'");
403 alt_table_row_color($k);
405 view_stock_status_cell($ln_itm->stock_id);
407 text_cells(null, $ln_itm->stock_id . "Desc", $ln_itm->item_description, 30, 50);
408 qty_cell($ln_itm->quantity);
409 label_cell($ln_itm->units);
410 qty_cell($ln_itm->qty_inv);
412 text_cells(null, $ln_itm->stock_id, $ln_itm->qty_dispatched, 10, 10);
414 $display_discount_percent = number_format2($ln_itm->discount_percent*100,user_percent_dec()) . "%";
416 $line_total = ($ln_itm->qty_dispatched * $ln_itm->price * (1 - $ln_itm->discount_percent));
418 amount_cell($ln_itm->price);
419 label_cell($ln_itm->tax_type_name);
420 label_cell($display_discount_percent, "nowrap align=right");
421 amount_cell($line_total);
423 //label_cell(get_tax_free_price_for_item($ln_itm->stock_id, $line_total, $_POST['tax_group_id']));
428 /*Don't re-calculate freight if some of the order has already been delivered -
429 depending on the business logic required this condition may not be required.
430 It seems unfair to charge the customer twice for freight if the order
431 was not fully delivered the first time ?? */
433 if (!isset($_POST['ChargeFreightCost']) || $_POST['ChargeFreightCost'] == "")
435 if ($_SESSION['Items']->any_already_delivered() == 1)
437 $_POST['ChargeFreightCost'] = 0;
441 $_POST['ChargeFreightCost'] = $_SESSION['Items']->freight_cost;
443 if (!is_numeric($_POST['ChargeFreightCost']))
445 $_POST['ChargeFreightCost'] = 0;
451 small_amount_cells(_("Shipping Cost"), 'ChargeFreightCost', null, "colspan=9 align=right");
453 $inv_items_total = $_SESSION['Items']->get_items_total_dispatch();
455 $display_sub_total = number_format2($inv_items_total + $_POST['ChargeFreightCost'],user_price_dec());
457 label_row(_("Sub-total"), $display_sub_total, "colspan=9 align=right","align=right");
459 $taxes = $_SESSION['Items']->get_taxes($_POST['tax_group_id'], $_POST['ChargeFreightCost']);
460 $tax_total = display_edit_tax_items($taxes, 9);
462 $display_total = number_format2(($inv_items_total + $_POST['ChargeFreightCost'] + $tax_total), user_price_dec());
464 label_row(_("Invoice Total"), $display_total, "colspan=9 align=right","align=right");
469 display_note(_("Marked items have insufficient quantities in stock."), 0, 1, "class='red'");
471 start_table($table_style2);
473 policy_list_row(_("Action For Balance"), "bo_policy", null);
475 textarea_row(_("Memo"), 'InvoiceText', null, 50, 4);
479 submit_center_first('Update', _("Update"));
480 submit_center_last('process_invoice', _("Process Invoice"));
484 //---------------------------------------------------------------------------------------------