bugfix_improvement_planning_mrp

bzr revid: fp@tinyerp.com-20090529225255-ipf3tu682mgtp3x0
This commit is contained in:
Fabien Pinckaers
2009-05-30 00:52:55 +02:00
parent 1d42a31a57
commit 6072b3fa61
5 changed files with 271 additions and 91 deletions
+43 -35
View File
@@ -22,6 +22,7 @@
from mx import DateTime
import time
import math
from osv import fields, osv
from tools.translate import _
@@ -34,50 +35,57 @@ class hr_timesheet_group(osv.osv):
'timesheet_id' : fields.one2many('hr.timesheet', 'tgroup_id', 'Working Time'),
'manager' : fields.many2one('res.users', 'Workgroup manager'),
}
#
# TODO: improve; very slow !
# bug if transition to another period
#
def interval_min_get(self, cr, uid, id, dt_from, hours):
if not id:
return [(dt_from-DateTime.RelativeDateTime(hours=int(hours)*3), dt_from)]
todo = hours
cycle = 0
result = []
maxrecur = 100
current_hour = dt_from.hour
while (todo>0) and maxrecur:
cr.execute("select hour_from,hour_to from hr_timesheet where dayofweek='%s' and tgroup_id=%s order by hour_from desc", (dt_from.day_of_week,id))
for (hour_from,hour_to) in cr.fetchall():
if (hour_from<current_hour) and (todo>0):
m = min(hour_to, current_hour)
if (m-hour_from)>todo:
hour_from = m-todo
d1 = DateTime.DateTime(dt_from.year,dt_from.month,dt_from.day,int(math.floor(hour_from)),int((hour_from%1) * 60))
d2 = DateTime.DateTime(dt_from.year,dt_from.month,dt_from.day,int(math.floor(m)),int((m%1) * 60))
result.append((d1, d2))
current_hour = hour_from
todo -= (m-hour_from)
dt_from -= DateTime.RelativeDateTime(days=1)
current_hour = 24
maxrecur -= 1
result.reverse()
return result
def interval_get(self, cr, uid, id, dt_from, hours, byday=True):
if not id:
return [(dt_from,dt_from+DateTime.RelativeDateTime(hours=int(hours)*3))]
todo = hours
cycle = 0
result = []
while todo>0:
maxrecur = 100
current_hour = dt_from.hour
while (todo>0) and maxrecur:
cr.execute("select hour_from,hour_to from hr_timesheet where dayofweek='%s' and tgroup_id=%s order by hour_from", (dt_from.day_of_week,id))
for (hour_from,hour_to) in cr.fetchall():
import math
hour_from = '%02d:%02d' % (math.floor(abs(hour_from)),round(abs(hour_from)%1+0.01,2) * 60)
hour_to = '%02d:%02d' % (math.floor(abs(hour_to)),round(abs(hour_to)%1+0.01,2) * 60)
h1,m1 = map(int,hour_from.split(':'))
h2,m2 = map(int,hour_to.split(':'))
d1 = DateTime.DateTime(dt_from.year,dt_from.month,dt_from.day,h1,m1)
d2 = DateTime.DateTime(dt_from.year,dt_from.month,dt_from.day,h2,m2)
if dt_from<d2:
date1 = max(dt_from,d1)
if date1+DateTime.RelativeDateTime(hours=todo)<=d2:
result.append((date1, date1+DateTime.RelativeDateTime(hours=todo)))
todo = 0
else:
todo -= (d2-date1).hours
result.append((date1, d2))
dt_from = DateTime.DateTime(dt_from.year,dt_from.month,dt_from.day)+DateTime.RelativeDateTime(days=1)
cycle+=1
if cycle>7 and todo==hours:
return [(dt_from,dt_from+DateTime.RelativeDateTime(hours=hours*3))]
if byday:
i = 1
while i<len(result):
if (result[i][0]-result[i-1][1]).days<1:
result[i-1]=(result[i-1][0],result[i][1])
del result[i]
else:
i+=1
if (hour_to>current_hour) and (todo>0):
m = max(hour_from, current_hour)
if (hour_to-m)>todo:
hour_to = m+todo
d1 = DateTime.DateTime(dt_from.year,dt_from.month,dt_from.day,int(math.floor(m)),int((m%1) * 60))
d2 = DateTime.DateTime(dt_from.year,dt_from.month,dt_from.day,int(math.floor(hour_to)),int((hour_to%1) * 60))
result.append((d1, d2))
current_hour = hour_to
todo -= (hour_to - m)
dt_from += DateTime.RelativeDateTime(days=1)
current_hour = 0
maxrecur -= 1
return result
hr_timesheet_group()
+12 -4
View File
@@ -380,6 +380,12 @@ class mrp_production(osv.osv):
result[prod.id]['cycle_total'] += wc.cycle
return result
def _production_date_end(self, cr, uid, ids, prop, unknow_none, context={}):
result = {}
for prod in self.browse(cr, uid, ids, context=context):
result[prod.id] = prod.date_planned
return result
def _production_date(self, cr, uid, ids, prop, unknow_none, context={}):
result = {}
for prod in self.browse(cr, uid, ids, context=context):
@@ -408,7 +414,8 @@ class mrp_production(osv.osv):
'location_dest_id': fields.many2one('stock.location', 'Finished Products Location', required=True,
help="Location where the system will stock the finished products."),
'date_planned_date': fields.function(_production_date, method=True, type='date', string='Planned Date'),
'date_planned_end': fields.function(_production_date_end, method=True, type='date', string='Scheduled End'),
'date_planned_date': fields.function(_production_date, method=True, type='date', string='Scheduled Date'),
'date_planned': fields.datetime('Scheduled date', required=True, select=1),
'date_start': fields.datetime('Start Date'),
'date_finnished': fields.datetime('End Date'),
@@ -456,7 +463,8 @@ class mrp_production(osv.osv):
default.update({
'name': self.pool.get('ir.sequence').get(cr, uid, 'mrp.production'),
'move_lines' : [],
'move_created_ids': []
'move_created_ids': [],
'state': 'draft'
})
return super(mrp_production, self).copy(cr, uid, id, default, context)
@@ -722,9 +730,9 @@ stock_move()
class mrp_production_workcenter_line(osv.osv):
_name = 'mrp.production.workcenter.line'
_description = 'Production workcenters used'
_description = 'Work Orders'
_columns = {
'name': fields.char('Name', size=64, required=True),
'name': fields.char('Work Order', size=64, required=True),
'workcenter_id': fields.many2one('mrp.workcenter', 'Workcenter', required=True),
'cycle': fields.float('Nbr of cycle', digits=(16,2)),
'hour': fields.float('Nbr of hour', digits=(16,2)),
+1 -1
View File
@@ -409,7 +409,7 @@
<field name="model">mrp.production</field>
<field name="type">gantt</field>
<field name="arch" type="xml">
<gantt color="product_id" date_delay="hour_total" date_start="date_planned" string="Productions">
<gantt color="product_id" date_stop="date_finnished" date_start="date_start" string="Productions">
<level object="mrp.routing" link="routing_id" domain="[]">
<field name="product_id"/>
<field name="origin"/>
+155 -34
View File
@@ -2,7 +2,7 @@
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
# dmr Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
# $Id$
#
# This program is free software: you can redistribute it and/or modify
@@ -39,22 +39,44 @@ from tools.translate import _
# TODO: Work Center may be recursive ?
#
class stock_move(osv.osv):
_inherit = 'stock.move'
_columns = {
'move_dest_id_lines': fields.one2many('stock.move','move_dest_id', 'Children Moves')
}
stock_move()
class mrp_production_workcenter_line(osv.osv):
def _get_date_end(self, cr, uid, ids, field_name, arg, context):
res={}
for op in self.browse(cr, uid, ids, context=context):
d = DateTime.strptime(op.date_planned,'%Y-%m-%d %H:%M:%S')
i = self.pool.get('hr.timesheet.group').interval_get(cr, uid, op.workcenter_id.timesheet_id.id or False, d, op.hour or 0.0)
if i:
res[op.id] = i[-1][1].strftime('%Y-%m-%d %H:%M:%S')
else:
res[op.id] = op.date_planned
return res
_inherit = 'mrp.production.workcenter.line'
_columns = {
'state': fields.selection([('draft','Draft'),('startworking', 'In Progress'),('pause','Pause'),('cancel','Canceled'),('done','Finished')],'Status', readonly=True),
'date_planned': fields.related('production_id', 'date_planned', type='datetime', string='Date Planned'),
'date_planned': fields.datetime('Scheduled Date'),
'date_planned_end': fields.function(_get_date_end, method=True, string='End Date', type='datetime'),
'date_start': fields.datetime('Start Date'),
'date_finnished': fields.datetime('End Date'),
'delay': fields.char('Delay',size=128,help="This is delay between operation start and stop in this workcenter",readonly=True),
'production_state':fields.related('production_id','state',type='char',string='Prod.State'),
'product':fields.related('production_id','product_id',type='many2one',relation='product.product',string='Product'),
'qty':fields.related('production_id','product_qty',type='float',string='Qty'),
'uom':fields.related('production_id','product_uom',type='many2one',relation='product.uom',string='UOM'),
'delay': fields.float('Working Hours',help="This is delay between operation start and stop in this workcenter",readonly=True),
'production_state':fields.related('production_id','state',
type='selection',
selection=[('draft','Draft'),('picking_except', 'Packing Exception'),('confirmed','Waiting Goods'),('ready','Ready to Produce'),('in_production','In Production'),('cancel','Canceled'),('done','Done')],
string='Prod.State', readonly=True),
'product':fields.related('production_id','product_id',type='many2one',relation='product.product',string='Product',
readonly=True),
'qty':fields.related('production_id','product_qty',type='float',string='Qty',readonly=True),
'uom':fields.related('production_id','product_uom',type='many2one',relation='product.uom',string='UOM',readonly=True),
}
_defaults = {
'state': lambda *a: 'draft',
'delay': lambda *a: '0 Days 0 hrs and 0 mins'
'delay': lambda *a: 0.0
}
def modify_production_order_state(self,cr,uid,ids,action):
@@ -82,17 +104,30 @@ class mrp_production_workcenter_line(osv.osv):
wf_service.trg_validate(uid, 'mrp.production', oper_obj.production_id.id, 'button_produce_done', cr)
return
def write(self, cr, uid, ids, vals, context={}, update=True):
result = super(mrp_production_workcenter_line, self).write(cr, uid, ids, vals, context=context)
if vals.get('date_planned', False) and update:
pids = {}
for prod in self.browse(cr, uid, ids, context=context):
if prod.production_id.date_start>vals['date_planned']:
pids[prod.production_id.id] = True
continue
if prod.production_id.date_finnished<prod.date_planned_end:
pids[prod.production_id.id] = True
self.pool.get('mrp.production').write(cr, uid, pids.keys(), {'date_start':vals['date_planned']})
return result
def action_draft(self, cr, uid, ids):
self.write(cr, uid, ids, {'state':'draft'})
return True
def action_start_working(self, cr, uid, ids):
self.modify_production_order_state(cr,uid,ids,'start')
self.write(cr, uid, ids, {'state':'startworking'})
self.write(cr, uid, ids, {'state':'startworking', 'date_start': time.strftime('%Y-%m-%d %H:%M:%S')})
return True
def action_done(self, cr, uid, ids):
self.write(cr, uid, ids, {'state':'done'})
self.write(cr, uid, ids, {'state':'done', 'date_finnished': time.strftime('%Y-%m-%d %H:%M:%S')})
self.modify_production_order_state(cr,uid,ids,'done')
return True
@@ -115,6 +150,14 @@ class mrp_production(osv.osv):
_inherit = 'mrp.production'
_description = 'Production'
def _production_date_end(self, cr, uid, ids, prop, unknow_none, context={}):
result = {}
for prod in self.browse(cr, uid, ids, context=context):
result[prod.id] = prod.date_planned
for line in prod.workcenter_lines:
result[prod.id] = max(line.date_planned_end, result[prod.id])
return result
def action_production_end(self, cr, uid, ids):
obj=self.browse(cr,uid,ids)[0]
for workcenter_line in obj.workcenter_lines:
@@ -127,6 +170,98 @@ class mrp_production(osv.osv):
tmp=self.pool.get('mrp.production.workcenter.line').action_cancel(cr,uid,[workcenter_line.id])
return super(mrp_production,self).action_cancel(cr,uid,ids)
def _compute_planned_workcenter(self, cr, uid, ids, context={}):
dt_end = DateTime.now()
for po in self.browse(cr, uid, ids, context=context):
dt_end = DateTime.strptime(po.date_start or po.date_planned, '%Y-%m-%d %H:%M:%S')
if not po.date_start:
self.write(cr, uid, [po.id], {
'date_start': po.date_planned
}, context=context, update=False)
old = None
for wci in range(len(po.workcenter_lines)):
wc = po.workcenter_lines[wci]
if (old is None) or (wc.sequence>old):
dt = dt_end
self.pool.get('mrp.production.workcenter.line').write(cr, uid, [wc.id], {
'date_planned':dt.strftime('%Y-%m-%d %H:%M:%S')
}, context=context, update=False)
i = self.pool.get('hr.timesheet.group').interval_get(
cr,
uid,
wc.workcenter_id.timesheet_id and wc.workcenter_id.timesheet_id.id or False,
dt,
wc.hour or 0.0
)
old = wc.sequence
if i:
dt_end = max(dt_end, i[-1][1])
super(mrp_production, self).write(cr, uid, [po.id], {
'date_finnished': dt_end
})
return dt_end
def _move_pass(self, cr, uid, ids, context={}):
for po in self.browse(cr, uid, ids, context):
todo = po.move_lines
dt = DateTime.strptime(po.date_start,'%Y-%m-%d %H:%M:%S')
while todo:
l = todo.pop(0)
if l.state in ('done','cancel','draft'):
continue
todo += l.move_dest_id_lines
if l.production_id and (l.production_id.date_finnished>dt):
for wc in l.production_id.workcenter_lines:
i = self.pool.get('hr.timesheet.group').interval_min_get(
cr,
uid,
wc.workcenter_id.timesheet_id.id or False,
dt, wc.hour or 0.0
)
dt = i[0][0]
self.write(cr, uid, [l.production_id.id], {'date_start':dt.strftime('%Y-%m-%d %H:%M:%S')})
continue
return True
def _move_futur(self, cr, uid, ids, context={}):
for po in self.browse(cr, uid, ids, context):
for line in po.move_created_ids:
l = line
while l.move_dest_id:
l = l.move_dest_id
if l.state in ('done','cancel','draft'):
break
if l.production_id and (l.production_id.date_start<po.date_finnished):
self.write(cr, uid, [l.production_id.id], {'date_start':po.date_finnished})
break
def write(self, cr, uid, ids, vals, context={}, update=True):
direction = {}
if vals.get('date_start', False):
for po in self.browse(cr, uid, ids, context=context):
print po, po.date_start, type(po.date_start)
print 'o'
direction[po.id] = cmp(po.date_start, vals.get('date_start', False))
result = super(mrp_production, self).write(cr, uid, ids, vals, context=context)
if (vals.get('workcenter_lines', False) or vals.get('date_start', False)) and update:
self._compute_planned_workcenter(cr, uid, ids, context=context)
for d in direction:
if direction[d]==1:
# the production order has been moved to the passed
self._move_pass(cr, uid, [d], context=context)
pass
elif direction[d]==-1:
self._move_futur(cr, uid, [d], context=context)
# the production order has been moved to the future
pass
return result
def action_compute(self, cr, uid, ids, properties=[]):
result = super(mrp_production, self).action_compute(cr, uid, ids, properties=properties)
self._compute_planned_workcenter(cr, uid, ids, context={})
return result
mrp_production()
class mrp_operations_operation_code(osv.osv):
@@ -168,31 +303,17 @@ class mrp_operations_operation(osv.osv):
code_lst.append(code.start_stop)
time_lst.append(vals['date_start'])
h=m=s=days=0
diff = 0
for i in range(0,len(code_lst)):
if code_lst[i]=='pause' or code_lst[i]=='done' or code_lst[i]=='cancel':
if code_lst[i]=='cancel' and code_lst[i-1]=='pause':
if not i: continue
if code_lst[i-1] not in ('resume','start'):
continue
a=datetime.datetime(int(time_lst[i-1][0:4]),int(time_lst[i-1][5:7]),int(time_lst[i-1][8:10]),int(time_lst[i-1][11:13]),int(time_lst[i-1][14:16]),int(time_lst[i-1][17:19]))
b=datetime.datetime(int(time_lst[i][0:4]),int(time_lst[i][5:7]),int(time_lst[i][8:10]),int(time_lst[i][11:13]),int(time_lst[i][14:16]),int(time_lst[i][17:19]))
diff =b-a
min, sec = divmod(diff.seconds, 60)
hrs, min = divmod(min, 60)
days+=diff.days
h+=hrs
m+=min
s+=sec
if s>=60:
m+=1
s=0
if m>=60:
h+=1
m=0
if h>=24:
days+=1
h=0
delay=str(days) +" Days "+ str(h) + " hrs and " + str(m)+ " mins"
return delay
a = datetime.datetime.strptime(time_lst[i-1],'%Y:%m:%d %H:%M:%S')
b = datetime.datetime.strptime(time_lst[i],'%Y:%m:%d %H:%M:%S')
diff += (b-a).days * 24
diff += (b-a).seconds / (60*60)
return diff
def check_operation(self,cr,uid,vals):
code_ids=self.pool.get('mrp_operations.operation.code').search(cr,uid,[('id','=',vals['code_id'])])
@@ -300,8 +421,8 @@ class mrp_operations_operation(osv.osv):
'order_date': fields.function(_get_order_date,method=True,string='Order Date',type='date',store={'mrp.production':(_order_date_search_production,['date_planned'], 10)}),
}
_defaults={
'date_start': lambda *a:DateTime.now().strftime('%Y-%m-%d %H:%M:%S')
}
'date_start': lambda *a:DateTime.now().strftime('%Y-%m-%d %H:%M:%S')
}
mrp_operations_operation()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+60 -17
View File
@@ -10,6 +10,7 @@
<field name="arch" type="xml">
<tree string="Work Orders">
<field name="production_id"/>
<field name="date_planned"/>
<field name="product"/>
<field name="qty"/>
<field name="uom"/>
@@ -31,22 +32,52 @@
<field name="inherit_id" eval="False"/>
<field name="arch" type="xml">
<form string="Work Orders">
<field colspan="4" name="name" select="1"/>
<field colspan="4" name="workcenter_id" select="1"/>
<field name="sequence"/>
<field name="cycle"/>
<field name="hour"/>
<field name="production_id"/>
<field name="delay"/>
<group colspan="8" col="8">
<field name="state" select="2"/>
<button name="button_draft" string="Set Draft" states="cancel"/>
<button name="button_start_working" string="Start working" states="draft"/>
<button name="button_resume" string="Resume" states="pause"/>
<button name="button_done" string="Finished" states="startworking"/>
<button name="button_pause" string="Pause" states="startworking"/>
<button name="button_cancel" string="Cancel" states="draft,startworking"/>
<group col="6" colspan="4">
<field colspan="4" name="name" select="1"/>
<field name="workcenter_id" select="1"/>
<field name="production_id"/>
<field name="production_state"/>
<field name="sequence"/>
</group>
<notebook colspan="4">
<page string="Information">
<group colspan="2" col="2">
<separator string="Planned Date" colspan="2"/>
<field name="date_planned"/>
<field name="date_planned_end"/>
</group>
<group colspan="2" col="2">
<separator string="Duration" colspan="2"/>
<field name="cycle"/>
<field name="hour" widget="float_time"/>
</group>
<group colspan="2" col="2">
<separator string="Real" colspan="2"/>
<field name="date_start" readonly="1"/>
<field name="date_finnished" readonly="1"/>
<field name="delay" widget="float_time"/>
</group>
<group colspan="2" col="2">
<separator string="Product to Produce" colspan="2"/>
<field name="product"/>
<field name="qty"/>
<field name="uom"/>
</group>
<separator string="Status" colspan="4"/>
<group colspan="8" col="8">
<field name="state" select="2"/>
<button name="button_draft" string="Set Draft" states="cancel"/>
<button name="button_start_working" string="Start" states="draft"/>
<button name="button_resume" string="Resume" states="pause"/>
<button name="button_cancel" string="Cancel" states="draft,startworking"/>
<button name="button_pause" string="Pause" states="startworking"/>
<button name="button_done" string="Finished" states="startworking"/>
</group>
</page>
</notebook>
</form>
</field>
</record>
@@ -69,8 +100,9 @@
<field name="model">mrp.production.workcenter.line</field>
<field name="type">gantt</field>
<field name="arch" type="xml">
<gantt color="workcenter_id" date_delay="hour" date_start="date_planned" string="Operations">
<level object="product.product" link="product" domain="[]">
<gantt color="production_id" date_stop="date_planned_end" date_start="date_planned" string="Operations">
<level object="mrp.workcenter" link="workcenter_id" domain="[]">
<field name="product"/>
<field name="production_id"/>
</level>
</gantt>
@@ -99,6 +131,16 @@
<field name="view_mode">tree,form,graph,calendar,gantt</field>
</record>
<record model="ir.actions.act_window" id="mrp_production_wc_action_planning">
<field name="name">Work Orders Planning</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">mrp.production.workcenter.line</field>
<field name="view_type">form</field>
<field name="view_mode">gantt,tree,form,graph,calendar</field>
</record>
<!-- Action for Confirm Operations -->
<record model="ir.actions.act_window" id="mrp_production_wc_confirm_action">
@@ -133,6 +175,7 @@
<menuitem name="All Work Orders" parent="mrp.menu_mrp_root" id="menu_mrp_production_wc_action" action="mrp_production_wc_action"/>
<menuitem name="Work Orders to Do" parent="menu_mrp_production_wc_action" action="mrp_production_wc_confirm_action" id="menu_mrp_production_wc_confirm_action"/>
<menuitem name="Future Work Orders" parent="menu_mrp_production_wc_action" action="mrp_production_wc_draft_action" id="menu_mrp_production_wc_draft_action"/>
<menuitem parent="menu_mrp_production_wc_action" action="mrp_production_wc_action_planning" id="menu_mrp_production_wc_draft_action_planning"/>
<!-- Operation codes -->