import php.Lib; using DateTools; typedef Detail = { id : String, quantity : Int, position : Int, } typedef Order = { id:String ,quantity:String ,priority:Date ,delivery_date:String } enum ScheduleMode { Update; Cancel; Move; } class ScheduleParameters { var row_id : String; var rowDetails: Hash; public var mode:ScheduleMode; function new(rawData : Dynamic) { var data = php.Lib.hashOfAssociativeArray(rawData); row_id = data.get('row_id'); var raw_order : Dynamic = data.get('row_order'); mode = ScheduleMode.Move; var row_ids = php.Lib.toHaxeArray(raw_order); if (row_ids!= null) { rowDetails = new Hash(); var position = 1; for(id in row_ids) { var d : Dynamic = data.get(id); var quantity : Int = null; if(d != null) { var o = php.Lib.objectOfAssociativeArray(d); quantity = Std.parseInt(o.quantity); } rowDetails.set(id, { id: id ,quantity: quantity ,position: position }); position+=1; } } } public function setMode(action:String) { mode = switch(action) { case "update" : ScheduleMode.Update; case "cancel" : ScheduleMode.Cancel; default: ScheduleMode.Move; }; } public function position(id: String) : Null { if(rowDetails == null) return null; return rowDetails.get(id).position; } public function priority(order : {id: String, priority: Date}) : Int { var orderId = ItemScheduler.orderId(order); var orderPosition = position(orderId); return orderPosition != null ? orderPosition : cast(order.priority.getTime(), Int); } } class ItemScheduler { var stock_id:String; var startLocation:String; var parameters:ScheduleParameters; var qoh: Int; function new(stock_id: String, startLocation, parameters : Null) { this.stock_id = stock_id; this.startLocation = startLocation; this.parameters = parameters; qoh = untyped __call__('get_qoh_on_date', this.stock_id, startLocation); } function tableHeader() { return ["Order", "Customer", "Quantity", "Before", "After", "Loc", "From", "Required Date", "Comment"]; } function generateTable(): Void { var startDate = Date.fromTime(0); // Sort location by date var locations = this.locations(); locations.sort(function(a, b) { var as = a.delivery.getTime(); var bs = b.delivery.getTime(); if(as < bs) return -1; else if(as > bs) return 1; else return 0; }); // Get the start location, it should be the first one var locationIter = locations.iterator(); var location = locationIter.next(); var qoh : Int = location.quantityOnHand(stock_id, null); var left = qoh; formatLocation(location, "Initial", left); // We display the order ordered by priority // But insert the location when needed (meaning // when we run out of item available for(order in orders()) { var quantity : Int = Std.parseInt(order.quantity); while(0 >= left && locationIter.hasNext()) { location = locationIter.next(); var quantityForLocation : Int = location.quantityOnHand(stock_id, null) + location.quantityOnOrder(stock_id); if(quantityForLocation == null || quantityForLocation == 0 || location.delivery == null) continue; left += quantityForLocation; formatLocation(location, "Delivery", left); } left -= quantity; formatOrder(order, left, location.delivery); } // display the left locations while(0 >= left && locationIter.hasNext()) { location = locationIter.next(); var quantityForLocation : Int = location.quantityOnHand(stock_id, null); if(quantityForLocation == null || quantityForLocation == 0) continue; left += quantityForLocation; formatLocation(location, "Delivery", left); } } function printRow(tds : Array, attributes : Array) { php.Lib.print(''); var position : Int = 1; for(td in tds) { php.Lib.print(''); if(td) php.Lib.print(td); php.Lib.print(''); position++; } php.Lib.print(''); } static public function orderId(order) { return 'order_'+order.id; } function formatOrder(order : Dynamic, left : Int, date : Date) { var row_id = orderId(order); var attributes = ['id = "'+row_id+'"']; var classes = []; var before : Int = left + order.quantity; /* We have basically 3 different cases; * - the order can be fullfilled * - the order can be partially * - not at all */ if (before < 0 ) { classes.push('soldout'); } else if(left < 0) { classes.push('partial'); } else { classes.push('full'); } /* The order can also be late if we need * to wait for a delivery to get it */ var required_by : Date = FA.sql2date(order.required_date); if(required_by == null) required_by = FA.sql2date(order.delivery_date); if(required_by.getTime() < date.getTime()) { classes.push('late'); } else { classes.push('on_time'); } var cells : Array = [ order.order_id , ''+order.deliver_to+'' ,'' ,before ,left ,order.from_stk_loc ,order.delivery_date ,order.required_date ,order.comment ]; attributes.push('class="'+classes.join(' ')+'"'); printRow(cells, attributes); } function formatLocation(location : Location, type: String, left : Int) { var cells = [ type ,location.name ,location.quantityOnHand(stock_id, null) ,left-location.quantityOnHand(stock_id, null) ,left ,location.code ,location.delivery.getTime() == 0 ? '' : location.delivery.format("%F") ,"" ,"" ]; printRow(cells, ['class = "tableheader location"', 'id = "loc_'+location.code+'"']); } /* function schedules() { //return orders()+locations(); //return orders(); return cast(locations(), Array); } */ private function loadOrders() { var tb : String = untyped __php__('TB_PREF'); var sql : String = "SELECT * , d.quantity as qty, d.priority AS pp FROM "+tb+"denorm_order_details_queue d JOIN "+tb+"sales_order_details od ON (od.id = d.id) JOIN "+tb+"sales_orders so ON (so.order_no = d.order_id) WHERE stock_id = '"+this.stock_id+"' AND od.trans_type = 30 ORDER by d.priority"; return FA.query(sql); } function orders():Array { var rows = loadOrders(); var orderList = []; for(row in rows) { // for some reason, priority is null // so we use the pp field var a:Dynamic = php.Lib.objectOfAssociativeArray(row); var order:Order = a; order.priority = Date.fromString(a.pp); order.quantity = a.qty; orderList.push(order); }; if(parameters != null) { orderList.sort(function(a, b) { return parameters.priority(a)-parameters.priority(b); }); } for(order in orderList) { } return orderList; } function locations() { var TB = FA.tb(); var sql = 'SELECT * FROM '+TB+'locations'; var _locs = []; for(row in FA.query(sql)) { var location = new Location(row); if(location.code == startLocation) { location.delivery = Date.fromTime(0); } else if(location.delivery == null) { continue; } _locs.push(location); } return _locs; } function purcharseOrders() { var TB = FA.tb(); var sql = 'SELECT SUM(quantity_ordered - quantity_received) as quantity into_stock_location AS location FROM '+TB+'purch_order_details NATURAL JOIN '+TB+'purch_orders WHERE item_code = "'+this.stock_id+'" AND quantity_ordered > quantity_received GROUP by item_code,delivery_date, into_stock_location ORDER by delivery_date' ; return FA.query(sql); } public function needsUpdate():Bool { return parameters != null && parameters.mode == ScheduleMode.Move; } function update() { var orders = this.orders(); var priorities = Lambda.array(Lambda.map(orders, function(o) { return o.priority;})); priorities.sort(function(a,b) { var as = a.toString(); var bs = b.toString(); if (as < bs) return -1; if( as > bs) return 1; return 0; }); var iter = priorities.iterator(); var p = iter.next(); var position:Int = 0-priorities.length; for(order in orders) { var new_priority = DateTools.delta(p, 1000*position); untyped __call__ ('update_order_detail_priority', order.id, new_priority.toString()); position +=1; p = iter.next(); } untyped __call__ ('update_queue_quantity_for_item', stock_id); } public function action() { if(parameters != null && parameters.mode == ScheduleMode.Update) { update(); } } }