Call hooks on split.
[order_line_extra.git] / haxe / ItemScheduler.hx
1 import php.Lib;
2 using DateTools;
3
4 typedef Detail = {
5         id : String,
6         quantity : Int,
7         position : Int,
8 }
9
10 typedef Order = {
11                 id:String
12                 ,quantity:String
13                 ,priority:Date
14                 ,delivery_date:String
15
16 }
17
18 enum ScheduleMode {
19         Update;
20         Cancel;
21         Move;
22 }
23
24 class ScheduleParameters {
25         var row_id : String;
26         var rowDetails: Hash<Detail>;
27         public var mode:ScheduleMode;
28
29         function new(rawData : Dynamic) {
30                 var data  = php.Lib.hashOfAssociativeArray(rawData);
31                 row_id = data.get('row_id');
32                 var raw_order : Dynamic = data.get('row_order');
33                 mode = ScheduleMode.Move;
34
35                 var row_ids = php.Lib.toHaxeArray(raw_order);
36                 if (row_ids!= null) {
37                         rowDetails = new Hash<Detail>();
38                         var position = 1;
39                         for(id in row_ids) {
40                                 var d : Dynamic = data.get(id);
41
42                                 var quantity : Int = null;
43                                 if(d != null) {
44                                                 var o = php.Lib.objectOfAssociativeArray(d);
45                                                 quantity = Std.parseInt(o.quantity);
46                                 }
47                                 
48
49                                 rowDetails.set(id, {
50                                         id: id
51                                         ,quantity: quantity
52                                         ,position: position
53                                         });
54                                 position+=1;
55                         }
56                 }
57         }
58
59         public function setMode(action:String) {
60                 mode = switch(action) {
61                         case "update" :
62                                 ScheduleMode.Update;
63                         case "cancel" :
64                                 ScheduleMode.Cancel;
65                         default:
66                                 ScheduleMode.Move;
67                 };
68         }
69
70         public function position(id: String) : Null<Int> {
71                 if(rowDetails == null) return null;
72                 return rowDetails.get(id).position;
73
74         }
75
76         public function priority(order : {id: String, priority: Date})  : Int {
77                 var orderId = ItemScheduler.orderId(order);
78                 var orderPosition = position(orderId);
79                 return orderPosition != null  ? orderPosition : cast(order.priority.getTime(), Int);
80         }
81 }
82
83
84 class ItemScheduler {
85         var stock_id:String;
86         var startLocation:String;
87         var parameters:ScheduleParameters; 
88         var qoh: Int;
89         function new(stock_id: String, startLocation, parameters : Null<ScheduleParameters>) {
90                 this.stock_id = stock_id;
91                 this.startLocation = startLocation;
92                 this.parameters = parameters;
93                 qoh =  untyped __call__('get_qoh_on_date', this.stock_id, startLocation);
94         }
95
96         function tableHeader() {
97                 return ["Order", "Customer", "Quantity", "Before", "After", "Loc", "From",  "Required Date", "Comment"];
98         }
99
100         function generateTable(): Void {
101                 var startDate = Date.fromTime(0);
102
103                 // Sort location by date
104                 var locations = this.locations();
105                 locations.sort(function(a, b) {
106                         var as = a.delivery.getTime();
107                         var bs = b.delivery.getTime();
108                         if(as < bs) 
109                                 return -1;
110                         else if(as > bs)
111                                 return 1;
112                         else return 0;
113                                 });
114
115                 // Get the start location, it should be the first one
116                 var locationIter = locations.iterator();
117                 var location  = locationIter.next();
118                 var qoh : Int = location.quantityOnHand(stock_id, null);
119                 var left = qoh;
120                 formatLocation(location, "Initial", left);
121
122                 // We display the order ordered by priority 
123                 // But insert the location when needed (meaning
124                 // when we run out of item available
125                 for(order in orders()) {
126                         var quantity : Int = Std.parseInt(order.quantity);
127
128                         while(0 >= left && locationIter.hasNext()) {
129                                 location = locationIter.next();
130                                 var quantityForLocation : Int = location.quantityOnHand(stock_id, null) + location.quantityOnOrder(stock_id);
131                                 if(quantityForLocation == null || quantityForLocation == 0 || location.delivery == null) continue;
132                                 left += quantityForLocation;
133                                 formatLocation(location, "Delivery", left);
134                         }
135                         left -= quantity;
136
137                         formatOrder(order, left, location.delivery);
138
139                 }
140                 // display the left locations
141                         while(0 >= left && locationIter.hasNext()) {
142                                 location = locationIter.next();
143                                 var quantityForLocation : Int = location.quantityOnHand(stock_id, null);
144                                 if(quantityForLocation == null || quantityForLocation == 0) continue;
145                                 left += quantityForLocation;
146                                 formatLocation(location, "Delivery", left);
147                         }
148
149         }
150
151         function printRow(tds : Array<Dynamic>, attributes : Array<String>) {
152                 php.Lib.print('<tr '+attributes.join(' ')+'>');
153                 var position : Int = 1;
154                 for(td in tds) {
155                         php.Lib.print('<td class="cell_'+position+'">');
156                         if(td) php.Lib.print(td);
157                         php.Lib.print('</td>');
158                         position++;
159                 }
160                 php.Lib.print('</tr>');
161         }
162
163         static public function orderId(order) {
164                 return 'order_'+order.id;
165         }
166
167         function formatOrder(order : Dynamic, left : Int, date : Date) {
168                 var row_id = orderId(order);
169                 var attributes = ['id = "'+row_id+'"'];
170                 var classes = [];
171                 var before : Int = left + order.quantity;
172                 /* We have basically 3 different cases;
173                  * - the order can be fullfilled
174                  * - the order can be partially 
175                  * - not at all
176                  */
177                 if (before < 0 ) {
178                         classes.push('soldout');
179                 }
180                 else if(left < 0) {
181                         classes.push('partial');
182                 }
183                 else {
184                         classes.push('full');
185                 }
186
187                 /* The order can also be late if we need
188                  * to wait for a delivery to get it
189                  */
190                 var required_by : Date = FA.sql2date(order.required_date);
191                 if(required_by == null) required_by = FA.sql2date(order.delivery_date);
192                 if(required_by.getTime() < date.getTime()) {
193                         classes.push('late');
194                 }
195                 else {
196                         classes.push('on_time');
197                 }
198                 var cells : Array <Dynamic> = [
199                         order.order_id
200                         , '<a href="/modules/order_line_extra/order_lines_view.php?customer_id='+Std.string(order.debtor_no)+'">'+order.deliver_to+'</a>'
201                         ,'<input type="text" name="'+row_id+'[quantity]" value="'+order.quantity+'">'
202                         ,before
203                         ,left
204                         ,order.from_stk_loc
205                         ,order.delivery_date
206                         ,order.required_date 
207                         ,order.comment
208                         ];
209
210                 attributes.push('class="'+classes.join(' ')+'"');
211                 printRow(cells, attributes);
212
213         }
214
215         function formatLocation(location : Location, type: String,  left : Int) {
216                 var cells = [
217                         type
218                         ,location.name
219                         ,location.quantityOnHand(stock_id, null)
220                         ,left-location.quantityOnHand(stock_id, null)
221                         ,left
222                         ,location.code
223                         ,location.delivery.getTime() == 0 ? '' : location.delivery.format("%F")
224                         ,""
225                         ,""
226                         ];
227
228                 printRow(cells, ['class = "tableheader location"', 'id = "loc_'+location.code+'"']);
229         }
230
231         /*
232                  function schedules() {
233 //return orders()+locations();
234 //return  orders();
235 return cast(locations(), Array<Dynamic>);
236
237 }
238          */
239
240 private function loadOrders() {
241         var tb : String =  untyped __php__('TB_PREF');
242         var sql : String = "SELECT *  , d.quantity as qty,   d.priority AS pp
243                 FROM "+tb+"denorm_order_details_queue  d
244                 JOIN "+tb+"sales_order_details od ON (od.id = d.id)
245                 JOIN "+tb+"sales_orders so ON (so.order_no = d.order_id)
246                 WHERE stock_id = '"+this.stock_id+"'
247                 AND od.trans_type = 30
248                 ORDER by d.priority";
249
250         return FA.query(sql);
251 }
252
253 function orders():Array<Order>  {
254         var rows = loadOrders();
255         var orderList = [];
256         for(row in rows) {
257                 // for some reason, priority is null
258                 // so we use the pp field
259                 var a:Dynamic = php.Lib.objectOfAssociativeArray(row);
260                 var order:Order = a;
261                 order.priority = Date.fromString(a.pp);
262                 order.quantity = a.qty;
263                 orderList.push(order);
264         };
265
266         if(parameters != null) {
267                 orderList.sort(function(a, b) { return parameters.priority(a)-parameters.priority(b); });
268
269         }
270                 for(order in orderList) {
271                 }
272
273         return orderList;
274 }
275
276
277
278 function locations() {
279         var TB = FA.tb();
280         var sql = 'SELECT * 
281                 FROM '+TB+'locations';
282         var _locs = [];
283         for(row in FA.query(sql)) {
284                 var location = new Location(row);
285                 if(location.code == startLocation) {
286                         location.delivery =  Date.fromTime(0);
287                 }
288                 else if(location.delivery == null) {
289                 continue;
290                 }
291                 _locs.push(location);
292         }
293
294         return _locs;
295
296 }
297
298
299         function purcharseOrders()  {
300                 var TB = FA.tb();
301                 var sql = 'SELECT SUM(quantity_ordered - quantity_received) as quantity
302                         into_stock_location AS location
303                         FROM '+TB+'purch_order_details
304                         NATURAL JOIN '+TB+'purch_orders
305                         WHERE item_code = "'+this.stock_id+'"
306                         AND quantity_ordered > quantity_received
307                         GROUP by item_code,delivery_date, into_stock_location
308                         ORDER by delivery_date' ;
309
310                 return  FA.query(sql);
311         }
312
313         public function needsUpdate():Bool {
314                 return parameters != null && parameters.mode == ScheduleMode.Move;
315         }
316
317         function update() {
318                 var orders = this.orders();
319                 var priorities = Lambda.array(Lambda.map(orders, function(o) { return o.priority;}));
320                 priorities.sort(function(a,b) {
321                         var as = a.toString();
322                         var bs = b.toString();
323                         if (as < bs) return -1;
324                         if( as > bs) return 1;
325                         return 0;
326                         });
327
328                 var iter = priorities.iterator();
329                 var p = iter.next();
330                 var position:Int = 0-priorities.length;
331                 for(order in orders) {
332                         var new_priority = DateTools.delta(p, 1000*position);
333                         untyped __call__ ('update_order_detail_priority', order.id, new_priority.toString());
334                         
335                         position +=1;
336                         p = iter.next();
337                 }
338                         untyped __call__ ('update_queue_quantity_for_item', stock_id);
339                 
340         }
341         
342         public function action() {
343                 if(parameters != null && parameters.mode == ScheduleMode.Update) {
344                         update();
345                 }
346         }
347
348
349 }
350
351