BulkUpdater work.
[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                         var now = Date.now();
138                         //formatOrder(order, left, location.delivery); // now.getTime() > location.delivery ? now : location.delivery);
139                         formatOrder(order, left, now.getTime() > location.delivery.getTime() ? now : location.delivery);
140
141                 }
142                 // display the left locations
143                         while(0 >= left && locationIter.hasNext()) {
144                                 location = locationIter.next();
145                                 var quantityForLocation : Int = location.quantityOnHand(stock_id, null);
146                                 if(quantityForLocation == null || quantityForLocation == 0) continue;
147                                 left += quantityForLocation;
148                                 formatLocation(location, "Delivery", left);
149                         }
150
151         }
152
153         function printRow(tds : Array<Dynamic>, attributes : Array<String>) {
154                 php.Lib.print('<tr '+attributes.join(' ')+'>');
155                 var position : Int = 1;
156                 for(td in tds) {
157                         php.Lib.print('<td class="cell_'+position+'">');
158                         if(td) php.Lib.print(td);
159                         php.Lib.print('</td>');
160                         position++;
161                 }
162                 php.Lib.print('</tr>');
163         }
164
165         static public function orderId(order) {
166                 return 'order_'+order.id;
167         }
168
169         function formatOrder(order : Dynamic, left : Int, date : Date) {
170                 var row_id = orderId(order);
171                 var attributes = ['id = "'+row_id+'"'];
172                 var classes = [];
173                 var before : Int = left + order.quantity;
174                 /* We have basically 3 different cases;
175                  * - the order can be fullfilled
176                  * - the order can be partially 
177                  * - not at all
178                  */
179                 if (before < 0 ) {
180                         classes.push('soldout');
181                 }
182                 else if(left < 0) {
183                         classes.push('partial');
184                 }
185                 else {
186                         classes.push('full');
187                 }
188
189                 /* The order can also be late if we need
190                  * to wait for a delivery to get it
191                  * or early if the item is on hold.
192                  */
193                 var required_by : Date = FA.sql2date(order.required_date);
194                 if(required_by == null) required_by = FA.sql2date(order.expiry_date);
195                 if(required_by != null && required_by.getTime() < date.getTime()) {
196                         classes.push('late');
197                 }
198                 else {
199                         var hold_until : Date = FA.sql2date(order.hold_until_date);
200                         php.Lib.print(hold_until);
201                         php.Lib.print(date);
202                         if(hold_until == null) hold_until = FA.sql2date(order.delivery_date);
203                         if(hold_until.getTime() > date.getTime()) {
204                                 classes.push('early');
205                         }
206                         else {
207                                 classes.push('on_time');
208                         }
209                 }
210                 var cells : Array <Dynamic> = [
211                         order.order_id
212                         , '<a href="/modules/order_line_extra/order_lines_view.php?customer_id='+Std.string(order.debtor_no)+'">'+order.deliver_to+'</a>'
213                         ,'<input type="text" name="'+row_id+'[quantity]" value="'+order.quantity+'">'
214                         ,before
215                         ,left
216                         ,order.from_stk_loc
217                         ,order.hold_until_date
218                         ,order.required_date 
219                         ,order.comment
220                         ];
221
222                 attributes.push('class="'+classes.join(' ')+'"');
223                 printRow(cells, attributes);
224
225         }
226
227         function formatLocation(location : Location, type: String,  left : Int) {
228                 var cells = [
229                         type
230                         ,location.name
231                         ,location.quantityOnHand(stock_id, null)
232                         ,left-location.quantityOnHand(stock_id, null)
233                         ,left
234                         ,location.code
235                         ,location.delivery.getTime() == 0 ? '' : location.delivery.format("%F")
236                         ,""
237                         ,""
238                         ];
239
240                 printRow(cells, ['class = "tableheader location"', 'id = "loc_'+location.code+'"']);
241         }
242
243         /*
244                  function schedules() {
245 //return orders()+locations();
246 //return  orders();
247 return cast(locations(), Array<Dynamic>);
248
249 }
250          */
251
252 private function loadOrders() {
253         var tb : String =  untyped __php__('TB_PREF');
254         var sql : String = "SELECT *  , d.quantity as qty,   d.priority AS pp
255                 FROM "+tb+"denorm_order_details_queue  d
256                 JOIN "+tb+"sales_order_details od ON (od.id = d.id)
257                 JOIN "+tb+"sales_orders so ON (so.order_no = d.order_id)
258                 WHERE stock_id = '"+this.stock_id+"'
259                 AND od.trans_type = 30
260                 ORDER by d.priority";
261
262         return FA.query(sql);
263 }
264
265 function orders():Array<Order>  {
266         var rows = loadOrders();
267         var orderList = [];
268         for(row in rows) {
269                 // for some reason, priority is null
270                 // so we use the pp field
271                 var a:Dynamic = php.Lib.objectOfAssociativeArray(row);
272                 var order:Order = a;
273                 order.priority = Date.fromString(a.pp);
274                 order.quantity = a.qty;
275                 orderList.push(order);
276         };
277
278         if(parameters != null) {
279                 orderList.sort(function(a, b) { return parameters.priority(a)-parameters.priority(b); });
280
281         }
282                 for(order in orderList) {
283                 }
284
285         return orderList;
286 }
287
288
289
290 function locations() {
291         var TB = FA.tb();
292         var sql = 'SELECT * 
293                 FROM '+TB+'locations';
294         var _locs = [];
295         for(row in FA.query(sql)) {
296                 var location = new Location(row);
297                 if(location.code == startLocation) {
298                         location.delivery =  Date.fromTime(0);
299                 }
300                 else if(location.delivery == null) {
301                 continue;
302                 }
303                 _locs.push(location);
304         }
305
306         return _locs;
307
308 }
309
310
311         function purcharseOrders()  {
312                 var TB = FA.tb();
313                 var sql = 'SELECT SUM(quantity_ordered - quantity_received) as quantity
314                         into_stock_location AS location
315                         FROM '+TB+'purch_order_details
316                         NATURAL JOIN '+TB+'purch_orders
317                         WHERE item_code = "'+this.stock_id+'"
318                         AND quantity_ordered > quantity_received
319                         GROUP by item_code,delivery_date, into_stock_location
320                         ORDER by delivery_date' ;
321
322                 return  FA.query(sql);
323         }
324
325         public function needsUpdate():Bool {
326                 return parameters != null && parameters.mode == ScheduleMode.Move;
327         }
328
329         function update() {
330                 var orders = this.orders();
331                 var priorities = Lambda.array(Lambda.map(orders, function(o) { return o.priority;}));
332                 priorities.sort(function(a,b) {
333                         var as = a.toString();
334                         var bs = b.toString();
335                         if (as < bs) return -1;
336                         if( as > bs) return 1;
337                         return 0;
338                         });
339
340                 var iter = priorities.iterator();
341                 var p = iter.next();
342                 var position:Int = 0-priorities.length;
343                 for(order in orders) {
344                         var new_priority = DateTools.delta(p, 1000*position);
345                         untyped __call__ ('update_order_detail_priority', order.id, new_priority.toString());
346                         
347                         position +=1;
348                         p = iter.next();
349                 }
350                         untyped __call__ ('update_queue_quantity_for_item', stock_id);
351                 
352         }
353         
354         public function action() {
355                 if(parameters != null && parameters.mode == ScheduleMode.Update) {
356                         update();
357                 }
358         }
359
360
361 }
362
363