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