c7ba4b6183df4eb194e91aaed9d81ede66f365f5
[fa-stable.git] / sales / customer_delivery.php
1 <?php
2
3 $page_security = 2;
4 $path_to_root="..";
5 include_once($path_to_root . "/sales/includes/cart_class.inc");
6 include_once($path_to_root . "/includes/session.inc");
7
8 include_once($path_to_root . "/includes/data_checks.inc");
9
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");
13
14 include_once($path_to_root . "/taxes/tax_calc.inc");
15
16 $js = "";
17 if ($use_popup_windows)
18         $js .= get_js_open_window(900, 500);
19 if ($use_date_picker)
20         $js .= get_js_date_picker();
21
22 if(isset($_GET['ModifyDelivery'])) {
23         $_SESSION['page_title'] = _("Modifying Delivery Note") . " #".$_GET['ModifyDelivery']; 
24 }
25 else { 
26         $_SESSION['page_title'] = _("Deliver Items for a Sales Order");
27 }
28
29 page($_SESSION['page_title'], false, false, "", $js); 
30
31 if (isset($_GET['AddedID'])) 
32 {
33         $dispatch_no = $_GET['AddedID'];
34         $trans_type = 13;
35
36         display_notification(_("Dispatch processed:") . ' '.$_GET['AddedID'], true);
37
38         display_note(get_customer_trans_view_str($trans_type, $dispatch_no, _("View this dispatch")), 0, 1);
39
40         display_note(get_gl_view_str($trans_type, $dispatch_no, _("View the GL Journal Entries for this Dispatch")));
41
42         hyperlink_params("$path_to_root/sales/customer_invoice.php", _("Invoice This Delivery"), "DeliveryNumber=$dispatch_no");
43
44         if ($_SESSION['Items']->trans_type=='invoice')
45                 hyperlink_params("$path_to_root/sales/sales_order_entry.php", _("Make Another Dispatch"), "NewDispatch=Yes");
46         else
47                 hyperlink_params("$path_to_root/sales/inquiry/sales_orders_view.php", _("Select Another Order For Dispatch"), "OutstandingOnly=1");
48
49         display_footer_exit();
50 }
51
52 //---------------------------------------------------------------------------------------------------------------
53
54 if (!isset($_GET['OrderNumber']) && !isset($_SESSION['ProcessingOrder']) && 
55  !isset($_GET['ModifyDelivery']) && !isset($_GET['process_delivery'])) 
56 {
57         /* This page can only be called with an order number for invoicing*/
58         display_error(_("This page can only be opened if an order or delivery note has been selected. Please select it first."));
59
60         hyperlink_params("$path_to_root/sales/inquiry/sales_orders_view.php", _("Select a Sales Order to Delivery"), "OutstandingOnly=1");
61
62         end_page();
63         exit;
64
65
66 elseif (isset($_GET['OrderNumber']) && $_GET['OrderNumber'] > 0) 
67 {
68
69         if (isset($_SESSION['Items']))
70         {
71                 unset($_SESSION['Items']->line_items);
72                 unset ($_SESSION['Items']);
73         }
74
75         $_SESSION['ProcessingOrder'] = $_GET['OrderNumber'];
76         $_SESSION['Items'] = new cart;
77         
78         /*read in all the selected order into the Items cart  */
79
80         if (read_sales_order($_SESSION['ProcessingOrder'], $_SESSION['Items'])) 
81         {
82                 if ($_SESSION['Items']->count_items() == 0) 
83                 {
84                 hyperlink_params($path_to_root . "/sales/inquiry/sales_orders_view.php", _("Select a different sales order to delivery"), "OutstandingOnly=1");
85                 die ("<br><b>" . _("This order has no items. There is nothing to delivery.") . "</b>");
86                 }
87         } 
88         else 
89         {
90                 hyperlink_no_params("/sales_orders_view.php", _("Select a sales order to dispatch"));
91                 die ("<br><b>" . _("This order items could not be retrieved. Please select another order.") . "</b>");
92         }
93
94
95 elseif (isset($_GET['ModifyDelivery']) && $_GET['ModifyDelivery'] > 0) {
96         if (isset($_SESSION['Items']))
97         {
98                 unset($_SESSION['Items']->line_items);
99                 unset ($_SESSION['Items']);
100         }
101         $_SESSION['Items'] = new cart;
102         
103         if(read_sales_delivery($_GET['ModifyDelivery'],$_SESSION['Items'] ))
104         {
105             if ($_SESSION['Items']->count_items() == 0) 
106             {
107                 hyperlink_params($path_to_root . "/sales/inquiry/sales_orders_view.php", _("Select a different delivery"), "OutstandingOnly=1");
108                 die ("<br><b>" . _("This delivery has all items invoiced. There is nothing to modify.") . "</b>");
109             }
110         } 
111         else 
112         {
113                 hyperlink_no_params("/sales_orders_view.php", _("Select a sales order to dispatch"));
114                 die ("<br><b>" . _("This sales delivery item could not be retrieved. Please select another delivery.") . "</b>");
115         }
116
117         $_SESSION['ProcessingOrder'] = $_SESSION['Items']->order_no;
118 } else 
119 {
120  // Update cart delivery quantities/descriptions
121         foreach ($_SESSION['Items']->line_items as $line=>$itm) 
122         {
123           if(isset($_POST['Line'.$line])) {
124                 $line_qty = $_POST['Line'.$line];
125                 if (is_numeric($line_qty) && $_POST['Line'.$line] <= ($itm->quantity - $itm->qty_done))
126                 {
127                         $_SESSION['Items']->line_items[$line]->qty_dispatched = $line_qty;
128                 }
129           }
130         
131           if(isset($_POST['Line'.$line.'Desc'])) {
132                 $line_desc = $_POST['Line'.$line.'Desc'];
133                 if (strlen($line_desc) > 0) 
134                 {
135                         $_SESSION['Items']->line_items[$line]->item_description = $line_desc;
136                 }
137           }
138         }
139 }
140
141 //---------------------------------------------------------------------------------------------------------------
142
143 function order_changed_error()
144 {
145         global $path_to_root;
146         display_note(_("This order has been changed or invoiced since this delivery was started to be confirmed. Processing halted."), 1, 0);
147         display_note(_("To enter and confirm this dispatch the order must be re-selected and re-read again to update the changes made by the other user."), 1, 0);
148
149         hyperlink_no_params("$path_to_root/sales/inquiry/sales_orders_view.php", _("Select a sales order for confirming deliveries"));
150
151         unset($_SESSION['ProcessingOrder']);
152         exit;
153 }
154
155 //---------------------------------------------------------------------------------------------------------------
156
157 function check_order_changed()
158 {
159         global $debug;
160
161         /*Now need to check that the order details are the same as they were when
162                         they were read into the Items array.
163         If they've changed then someone else may have dispatch them */
164
165         $sql = "SELECT id, stk_code, quantity, qty_sent FROM ".TB_PREF."sales_order_details WHERE
166                 order_no = " . $_SESSION['ProcessingOrder']. " ORDER BY id";
167
168         $result = db_query($sql,"retreive sales order details");
169
170         if (db_num_rows($result) != count($_SESSION['Items']->line_items))
171         {
172
173                 /*there should be the same number of items returned from this query as 
174                    count of lines on the delivery notes - if  not       then someone has 
175                    already invoiced or credited some lines */
176         if ($debug == 1)
177         {
178                 display_note($sql, 1, 0);
179                 display_note("No rows returned by sql:" . db_num_rows($result), 1, 0);
180                 display_note("Count of items in the cart " . count($_SESSION['Items']->line_items), 1, 0);
181         }
182
183                 return false;
184         }
185         $line=0;
186         while ($myrow = db_fetch($result)) 
187         {
188                 $stk_itm = $myrow["stk_code"];
189                 if ($_SESSION['Items']->line_items[$line]->quantity != $myrow["quantity"] ||
190                         $_SESSION['Items']->line_items[$line]->qty_done != $myrow["qty_sent"])
191                 {
192                         display_note(_("Original order for") . " '" . $myrow["stk_code"] . "' " .
193                                 _("has a quantity of") . " " . $myrow["quantity"] . " " . 
194                                 _("and an delivered quantity of") . " " . $myrow["qty_sent"] . " " .
195                                 _("the cart shows quantity of") . " " . 
196                                 $_SESSION['Items']->line_items[$line]->quantity . " " . 
197                                 _("and delivered quantity of") . " " . 
198                                 $_SESSION['Items']->line_items[$line]->qty_done, 1, 0);
199
200                         return false;
201                 }
202         $line++;
203         } /*loop through all line items of the order to ensure none have been invoiced */
204         return true;
205 }
206
207 //---------------------------------------------------------------------------------------------------------------
208
209 function check_data()
210 {
211         if (!isset($_POST['DispatchDate']) || !is_date($_POST['DispatchDate'])) 
212         {
213                 display_error(_("The entered date of delivery is invalid."));
214                 return false;
215         }
216         if (!is_date_in_fiscalyear($_POST['DispatchDate'])) 
217         {
218                 display_error(_("The entered date of delivery is not in fiscal year."));
219                 return false;
220         }
221         if (!isset($_POST['due_date']) || !is_date($_POST['due_date'])) 
222         {
223                 display_error(_("The entered dead-line for invoice is invalid."));
224                 return false;
225         }
226
227         if (!references::is_valid($_POST['ref'])) 
228         {
229                 display_error(_("You must enter a reference."));
230                 return false;
231         }
232
233         if (!is_new_reference($_POST['ref'], 13)) 
234         {
235                 display_error(_("The entered reference is already in use."));
236                 return false;
237         }
238         if ($_POST['ChargeFreightCost'] == "")
239                 $_POST['ChargeFreightCost'] = 0;
240         if (!is_numeric($_POST['ChargeFreightCost']) || $_POST['ChargeFreightCost'] < 0)        
241         {
242                 display_error(_("The entered shipping value is not numeric."));
243                 return false;
244         }
245
246         if ($_SESSION['Items']->has_items_dispatch() == 0 && $_POST['ChargeFreightCost'] == 0)  
247         {
248                 display_error(_("There are no item quantities on this delivery note."));
249                 return false;
250         }
251
252         return true;
253 }
254
255 //---------------------------------------------------------------------------------------------------------------
256
257 function check_qoh()
258 {
259         if (!sys_prefs::allow_negative_stock())
260         {
261         foreach ($_SESSION['Items']->line_items as $itm) 
262         {
263
264                         if ($itm->qty_dispatched && has_stock_holding($itm->mb_flag))
265                         {
266                                 $qoh = get_qoh_on_date($itm->stock_id, $_POST['Location'], $_POST['DispatchDate']);
267
268                         if ($itm->qty_dispatched > $qoh) 
269                         {
270                                 display_error(_("The delivery cannot be processed because there is an insufficient quantity for item:") .
271                                         " " . $itm->stock_id . " - " .  $itm->item_description);
272                                 return false;
273                         }
274                 }
275         }
276         }
277
278         return true;
279 }
280
281 //---------------------------------------------------------------------------------------------------------------
282
283 function process_delivery($get_from_order=false)
284 {
285         if ($get_from_order)
286         {   // entry point for direct delivery - cart contains completed order;
287             // we should have qty_dispatched and standard cost set anyway
288                 unset($_SESSION['Items']->line_items);
289             read_sales_order($_SESSION['Items']->order_no, $_SESSION['Items']);
290
291                 $duedate = get_invoice_duedate($_SESSION['Items']->customer_id, $_SESSION['Items']->delivery_date);
292                 $delivery_no = add_sales_delivery($_SESSION['Items'],
293                         $_SESSION['Items']->delivery_date, $duedate, $_SESSION['Items']->order_no,
294                         $_SESSION['Items']->tax_group_id, $_SESSION['Items']->freight_cost,
295                         $_SESSION['Items']->Location, $_SESSION['Items']->ship_via,
296                         $_SESSION['Items']->default_sales_type, references::get_next(13),
297                         $_SESSION['Items']->memo_,0);
298         } 
299         else 
300         {
301         
302                 if (!check_data())
303                         return;
304
305                 if (!check_order_changed())
306                         order_changed_error();
307
308                 if (!check_qoh())
309                         return;
310
311                 if ($_POST['bo_policy'])
312                         $bo_policy = 0;
313                 else
314                         $bo_policy = 1;
315                         
316                 $delivery_no = add_sales_delivery($_SESSION['Items'],
317                         $_POST['DispatchDate'], $_POST['due_date'],     $_SESSION['ProcessingOrder'],
318                         $_SESSION['Items']->tax_group_id,$_POST['ChargeFreightCost'], $_POST['Location'],
319                         $_POST['ship_via'],     $_POST['sales_type_id'], $_POST['ref'],
320                         $_POST['InvoiceText'], $bo_policy);
321                 unset($_SESSION['ProcessingOrder']);
322         }
323         meta_forward($_SERVER['PHP_SELF'], "AddedID=$delivery_no");
324 }
325
326 //---------------------------------------------------------------------------------------------------------------
327 if (isset($_GET['process_delivery']))
328         process_delivery(true);
329 elseif (isset($_POST['process_delivery']))
330         process_delivery();
331
332 //-------------------------------------------------------------------------------------------------
333
334 start_form(false, true);
335
336 start_table("$table_style2 width=80%", 5);
337 echo "<tr><td>"; // outer table
338
339 start_table("$table_style width=100%");
340 start_row();
341 label_cells(_("Customer"), $_SESSION['Items']->customer_name, "class='tableheader2'");
342 label_cells(_("Branch"), get_branch_name($_SESSION['Items']->Branch), "class='tableheader2'");
343 label_cells(_("Currency"), $_SESSION['Items']->customer_currency, "class='tableheader2'");
344 end_row();
345 start_row();
346
347 if (!isset($_POST['ref']))
348         $_POST['ref'] = references::get_next(13);
349
350 ref_cells(_("Reference"), 'ref', null, "class='tableheader2'");
351
352 label_cells(_("For Sales Order"), get_customer_trans_view_str(systypes::sales_order(), $_SESSION['ProcessingOrder']), "class='tableheader2'");
353
354 if (!isset($_POST['sales_type_id']))
355         $_POST['sales_type_id'] = $_SESSION['Items']->default_sales_type;
356 label_cell(_("Sales Type"), "class='tableheader2'");    
357 sales_types_list_cells(null, 'sales_type_id', $_POST['sales_type_id']);
358
359 end_row();
360 start_row();
361
362 if (!isset($_POST['Location']))
363         $_POST['Location'] = $_SESSION['Items']->Location;
364 label_cell(_("Delivery From"), "class='tableheader2'"); 
365 locations_list_cells(null, 'Location', $_POST['Location'], false, true);
366
367 if (!isset($_POST['ship_via']))
368         $_POST['ship_via'] = $_SESSION['Items']->ship_via;
369 label_cell(_("Shipping Company"), "class='tableheader2'");      
370 shippers_list_cells(null, 'ship_via', $_POST['ship_via']);
371
372 // set this up here cuz it's used to calc qoh
373 if (!isset($_POST['DispatchDate']) || !is_date($_POST['DispatchDate']))
374 {
375         $_POST['DispatchDate'] = Today();
376         if (!is_date_in_fiscalyear($_POST['DispatchDate']))
377                 $_POST['DispatchDate'] = end_fiscalyear();
378 }
379 date_cells(_("Date"), 'DispatchDate', $_POST['DispatchDate'], 0, 0, 0, "class='tableheader'");
380 end_row();
381
382 end_table();
383
384 echo "</td><td>";// outer table
385
386 start_table("$table_style width=90%");
387
388 if (!isset($_POST['due_date']) || !is_date($_POST['due_date']))
389
390         $_POST['due_date'] = get_invoice_duedate($_SESSION['Items']->customer_id, $_POST['DispatchDate']);
391
392 date_row(_("Invoice Dead-line"), 'due_date', $_POST['due_date'], 0, 0, 0, "class='tableheader'");
393 end_table();
394
395 echo "</td></tr>";
396 end_table(1); // outer table
397
398 display_heading(_("Invoice Items"));
399
400 start_table("$table_style width=80%");
401 $th = array(_("Item Code"), _("Item Description"), _("Ordered"), _("Units"), _("Delivered"),
402         _("This Delivery"), _("Price"), _("Tax Type"), _("Discount"), _("Total"));
403 table_header($th);
404 $k = 0;
405 $has_marked = false;
406 $show_qoh = true;
407
408 foreach ($_SESSION['Items']->line_items as $line=>$ln_itm) 
409 {       
410         if($ln_itm->quantity==$ln_itm->qty_done) continue; //this line is fully delivered
411     // if it's a non-stock item (eg. service) don't show qoh
412     if (sys_prefs::allow_negative_stock() || !has_stock_holding($ln_itm->mb_flag) ||
413                 $ln_itm->qty_dispatched == 0)
414         $show_qoh = false;
415
416         if ($show_qoh)
417                 $qoh = get_qoh_on_date($ln_itm->stock_id, $_POST['Location'], $_POST['DispatchDate']);
418
419         if ($show_qoh && ($ln_itm->qty_dispatched > $qoh)) 
420         {
421                 // oops, we don't have enough of one of the component items
422                 start_row("class='stockmankobg'");
423                 $has_marked = true;
424         } 
425         else
426                 alt_table_row_color($k);
427
428         view_stock_status_cell($ln_itm->stock_id);
429
430         text_cells(null, 'Line'.$line.'Desc', $ln_itm->item_description, 30, 50);
431         qty_cell($ln_itm->quantity);
432         label_cell($ln_itm->units);
433         qty_cell($ln_itm->qty_done);
434
435         text_cells(null, 'Line'.$line, $ln_itm->qty_dispatched, 10, 10);
436
437         $display_discount_percent = number_format2($ln_itm->discount_percent*100,user_percent_dec()) . "%";
438
439         $line_total = ($ln_itm->qty_dispatched * $ln_itm->price * (1 - $ln_itm->discount_percent));
440
441         amount_cell($ln_itm->price);
442         label_cell($ln_itm->tax_type_name);
443         label_cell($display_discount_percent, "nowrap align=right");
444         amount_cell($line_total);
445
446         end_row();
447 }
448   
449 $_POST['ChargeFreightCost'] = $_SESSION['Items']->freight_cost;
450
451 if (!is_numeric($_POST['ChargeFreightCost']))
452 {
453         $_POST['ChargeFreightCost'] = 0;
454 }
455
456 start_row();
457
458 small_amount_cells(_("Shipping Cost"), 'ChargeFreightCost', null, "colspan=9 align=right");
459
460 $inv_items_total = $_SESSION['Items']->get_items_total_dispatch();
461
462 $display_sub_total = number_format2($inv_items_total + $_POST['ChargeFreightCost'],user_price_dec());
463
464 label_row(_("Sub-total"), $display_sub_total, "colspan=9 align=right","align=right");
465
466 $taxes = $_SESSION['Items']->get_taxes($_SESSION['Items']->tax_group_id, $_POST['ChargeFreightCost']);
467 $tax_total = display_edit_tax_items($taxes, 9);
468
469 $display_total = number_format2(($inv_items_total + $_POST['ChargeFreightCost'] + $tax_total), user_price_dec());
470
471 label_row(_("Amount Total"), $display_total, "colspan=9 align=right","align=right");
472
473 end_table(1);
474
475 if ($has_marked)
476         display_note(_("Marked items have insufficient quantities in stock."), 0, 1, "class='red'");
477
478 start_table($table_style2);
479
480 policy_list_row(_("Action For Balance"), "bo_policy", null);
481
482 textarea_row(_("Memo"), 'InvoiceText', null, 50, 4);
483
484 end_table(1);
485
486 submit_center_first('Update', _("Update"));
487 submit_center_last('process_delivery', _("Process Dispatch"));
488
489 end_form();
490
491 //---------------------------------------------------------------------------------------------
492
493 end_page();
494
495 ?>