[IMP] project_long_term: merge improvements from rvo
bzr revid: mra@tinyerp.com-20100309115235-qkv9c4uuskgraqrz
This commit is contained in:
parent
8cc8ca8e68
commit
45ea2256a5
|
@ -26,7 +26,7 @@
|
|||
"author" : "Tiny",
|
||||
"website" : "http://www.openerp.com",
|
||||
"category" : "Generic Modules/Projects & Services",
|
||||
"depends" : ["project","resource"],
|
||||
"depends" : ["project", "resource"],
|
||||
"description": """Long Term Project management module that track planning, scheduling, resources allocation.
|
||||
""",
|
||||
"init_xml" : [],
|
||||
|
@ -35,7 +35,7 @@
|
|||
#"security/ir.model.access.csv",
|
||||
"project_wizard.xml" ,
|
||||
"project_view.xml",
|
||||
"project_phase_workflow.xml"
|
||||
"project_workflow.xml"
|
||||
],
|
||||
'installable': True,
|
||||
'active': False,
|
||||
|
|
|
@ -30,25 +30,21 @@ class project_phase(osv.osv):
|
|||
_name = "project.phase"
|
||||
_description = "Project Phase"
|
||||
|
||||
|
||||
def _check_recursion(self,cr,uid,ids):
|
||||
def _check_recursion(self, cr, uid, ids):
|
||||
obj_self = self.browse(cr, uid, ids[0])
|
||||
prev_ids = obj_self.previous_phase_ids
|
||||
next_ids = obj_self.next_phase_ids
|
||||
#it should nither be in prev_ids nor in next_ids
|
||||
# it should nither be in prev_ids nor in next_ids
|
||||
if (obj_self in prev_ids) or (obj_self in next_ids):
|
||||
return False
|
||||
ids = [id for id in prev_ids if id in next_ids]
|
||||
|
||||
#both prev_ids and next_ids must be unique
|
||||
# both prev_ids and next_ids must be unique
|
||||
if ids:
|
||||
return False
|
||||
#unrelated project
|
||||
|
||||
# unrelated project
|
||||
prev_ids = [rec.id for rec in prev_ids]
|
||||
next_ids = [rec.id for rec in next_ids]
|
||||
|
||||
#iter prev_ids
|
||||
# iter prev_ids
|
||||
while prev_ids:
|
||||
cr.execute('select distinct prv_phase_id from project_phase_rel where next_phase_id in ('+','.join(map(str, prev_ids))+')')
|
||||
prv_phase_ids = filter(None, map(lambda x: x[0], cr.fetchall()))
|
||||
|
@ -58,8 +54,7 @@ class project_phase(osv.osv):
|
|||
if ids:
|
||||
return False
|
||||
prev_ids = prv_phase_ids
|
||||
|
||||
#iter next_ids
|
||||
# iter next_ids
|
||||
while next_ids:
|
||||
cr.execute('select distinct next_phase_id from project_phase_rel where prv_phase_id in ('+','.join(map(str, next_ids))+')')
|
||||
next_phase_ids = filter(None, map(lambda x: x[0], cr.fetchall()))
|
||||
|
@ -72,21 +67,21 @@ class project_phase(osv.osv):
|
|||
return True
|
||||
|
||||
def _check_dates(self, cr, uid, ids):
|
||||
phase = self.read(cr, uid, ids[0],['date_start','date_end'])
|
||||
phase = self.read(cr, uid, ids[0], ['date_start', 'date_end'])
|
||||
if phase['date_start'] and phase['date_end']:
|
||||
if phase['date_start'] > phase['date_end']:
|
||||
return False
|
||||
return True
|
||||
|
||||
def _check_constraint_start(self, cr, uid, ids):
|
||||
phase = self.read(cr, uid, ids[0],['date_start','constraint_date_start'])
|
||||
phase = self.read(cr, uid, ids[0], ['date_start', 'constraint_date_start'])
|
||||
if phase['date_start'] and phase['constraint_date_start']:
|
||||
if phase['date_start'] < phase['constraint_date_start']:
|
||||
return False
|
||||
return True
|
||||
|
||||
def _check_constraint_end(self, cr, uid, ids):
|
||||
phase = self.read(cr, uid, ids[0],['date_end','constraint_date_end'])
|
||||
phase = self.read(cr, uid, ids[0], ['date_end', 'constraint_date_end'])
|
||||
if phase['date_end'] and phase['constraint_date_end']:
|
||||
if phase['date_end'] > phase['constraint_date_end']:
|
||||
return False
|
||||
|
@ -102,74 +97,76 @@ class project_phase(osv.osv):
|
|||
'next_phase_ids': fields.many2many('project.phase', 'project_phase_rel', 'prv_phase_id', 'next_phase_id', 'Next Phases'),
|
||||
'previous_phase_ids': fields.many2many('project.phase', 'project_phase_rel', 'next_phase_id', 'prv_phase_id', 'Previous Phases'),
|
||||
'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of phases."),
|
||||
'duration': fields.float('Duration',required=True),
|
||||
'product_uom': fields.many2one('product.uom', 'Duration UoM',required=True, help="UoM (Unit of Measure) is the unit of measurement for Duration"),
|
||||
'duration': fields.float('Duration', required=True),
|
||||
'product_uom': fields.many2one('product.uom', 'Duration UoM', required=True, help="UoM (Unit of Measure) is the unit of measurement for Duration"),
|
||||
'task_ids': fields.one2many('project.task', 'phase_id', "Project Tasks"),
|
||||
'resource_ids': fields.one2many('project.resource.allocation', 'phase_id', "Project Resources"),
|
||||
'responsible_id':fields.many2one('res.users', 'Responsible'),
|
||||
'state': fields.selection([('draft', 'Draft'),('open', 'In Progress'),('pending', 'Pending'), ('cancelled', 'Cancelled'), ('done', 'Done')], 'State', readonly=True, required=True,
|
||||
'state': fields.selection([('draft', 'Draft'), ('open', 'In Progress'), ('pending', 'Pending'), ('cancelled', 'Cancelled'), ('done', 'Done')], 'State', readonly=True, required=True,
|
||||
help='If the phase is created the state \'Draft\'.\n If the phase is started, the state becomes \'In Progress\'.\n If review is needed the phase is in \'Pending\' state.\
|
||||
\n If the phase is over, the states is set to \'Done\'.')
|
||||
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'responsible_id': lambda obj,cr,uid,context: uid,
|
||||
'date_start': lambda *a: time.strftime('%Y-%m-%d'),
|
||||
'state': lambda *a: 'draft',
|
||||
'sequence': lambda *a: 10,
|
||||
}
|
||||
|
||||
_order = "name"
|
||||
_constraints = [
|
||||
(_check_recursion,'Error ! Loops In Phases Not Allowed',['next_phase_ids','previous_phase_ids']),
|
||||
(_check_recursion,'Error ! Loops In Phases Not Allowed',['next_phase_ids', 'previous_phase_ids']),
|
||||
(_check_dates, 'Error! Phase start-date must be lower then Phase end-date.', ['date_start', 'date_end']),
|
||||
(_check_constraint_start, 'Error! Phase must start-after Constraint Start Date.', ['date_start', 'constraint_date_start']),
|
||||
(_check_constraint_end, 'Error! Phase must end-before Constraint End Date.', ['date_end', 'constraint_date_end']),
|
||||
|
||||
]
|
||||
|
||||
def onchange_project(self, cr, uid, ids, project):
|
||||
result = {}
|
||||
if project:
|
||||
project_pool = self.pool.get('project.project')
|
||||
project_id = project_pool.browse(cr, uid, project)
|
||||
project_obj = self.pool.get('project.project')
|
||||
project_id = project_obj.browse(cr, uid, project)
|
||||
if project_id.date_start:
|
||||
result['date_start'] = mx.DateTime.strptime(project_id.date_start, "%Y-%m-%d").strftime('%Y-%m-%d %H:%M:%S')
|
||||
return {'value' : result}
|
||||
return {'value' : {'date_start' : []}}
|
||||
return {'value': result}
|
||||
return {'value': {'date_start': []}}
|
||||
|
||||
def constraint_date_start(self, cr, uid, phase, date_end, context=None):
|
||||
# Recursive call for all previous phases if change in date_start < older time
|
||||
|
||||
resource_cal_pool = self.pool.get('resource.calendar')
|
||||
uom_pool = self.pool.get('product.uom')
|
||||
resource_pool = self.pool.get('resource.resource')
|
||||
def _check_date_start(self, cr, uid, phase, date_end, context=None):
|
||||
"""
|
||||
Check And Compute date_end of phase if change in date_start < older time.
|
||||
"""
|
||||
resource_calendar_obj = self.pool.get('resource.calendar')
|
||||
uom_obj = self.pool.get('product.uom')
|
||||
resource_obj = self.pool.get('resource.resource')
|
||||
calendar_id = phase.project_id.resource_calendar_id.id
|
||||
resource_id = resource_pool.search(cr, uid, [('user_id','=',phase.responsible_id.id)])
|
||||
resource_id = resource_obj.search(cr, uid, [('user_id', '=', phase.responsible_id.id)])
|
||||
if resource_id:
|
||||
calendar_id = resource_pool.browse(cr, uid, resource_id[0]).calendar_id.id
|
||||
default_uom_id = uom_pool.search(cr, uid, [('name','=','Hour')])[0]
|
||||
avg_hours = uom_pool._compute_qty(cr, uid, phase.product_uom.id, phase.duration, default_uom_id)
|
||||
work_time = resource_cal_pool.interval_min_get(cr, uid, calendar_id or False, date_end, avg_hours or 0.0, resource_id or False)
|
||||
dt_start = work_time[0][0].strftime('%Y-%m-%d %H:%M:%S')
|
||||
self.write(cr, uid, [phase.id], {'date_start' : dt_start, 'date_end' : date_end.strftime('%Y-%m-%d %H:%M:%S')})
|
||||
cal_id = resource_obj.browse(cr, uid, resource_id[0]).calendar_id.id
|
||||
if cal_id:
|
||||
calendar_id = cal_id
|
||||
default_uom_id = uom_obj.search(cr, uid, [('name', '=', 'Hour')])[0]
|
||||
avg_hours = uom_obj._compute_qty(cr, uid, phase.product_uom.id, phase.duration, default_uom_id)
|
||||
work_times = resource_calendar_obj.interval_min_get(cr, uid, calendar_id or False, date_end, avg_hours or 0.0, resource_id or False)
|
||||
dt_start = work_times[0][0].strftime('%Y-%m-%d %H:%M:%S')
|
||||
self.write(cr, uid, [phase.id], {'date_start': dt_start, 'date_end': date_end.strftime('%Y-%m-%d %H:%M:%S')})
|
||||
|
||||
def constraint_date_end(self, cr, uid, phase, date_start, context=None):
|
||||
# Recursive call for all next phases if change in date_end > older time
|
||||
|
||||
resource_cal_pool = self.pool.get('resource.calendar')
|
||||
uom_pool = self.pool.get('product.uom')
|
||||
resource_pool = self.pool.get('resource.resource')
|
||||
def _check_date_end(self, cr, uid, phase, date_start, context=None):
|
||||
"""
|
||||
Check And Compute date_end of phase if change in date_end > older time.
|
||||
"""
|
||||
resource_calendar_obj = self.pool.get('resource.calendar')
|
||||
uom_obj = self.pool.get('product.uom')
|
||||
resource_obj = self.pool.get('resource.resource')
|
||||
calendar_id = phase.project_id.resource_calendar_id.id
|
||||
resource_id = resource_pool.search(cr, uid, [('user_id','=',phase.responsible_id.id)])
|
||||
resource_id = resource_obj.search(cr, uid, [('user_id', '=', phase.responsible_id.id)])
|
||||
if resource_id:
|
||||
calendar_id = resource_pool.browse(cr, uid, resource_id[0]).calendar_id.id
|
||||
default_uom_id = uom_pool.search(cr, uid, [('name','=','Hour')])[0]
|
||||
avg_hours = uom_pool._compute_qty(cr, uid, phase.product_uom.id, phase.duration, default_uom_id)
|
||||
work_time = resource_cal_pool.interval_get(cr, uid, calendar_id or False, date_start, avg_hours or 0.0, resource_id or False)
|
||||
dt_end = work_time[-1][1].strftime('%Y-%m-%d %H:%M:%S')
|
||||
self.write(cr, uid, [phase.id], {'date_start' : date_start.strftime('%Y-%m-%d %H:%M:%S'), 'date_end' : dt_end})
|
||||
cal_id = resource_obj.browse(cr, uid, resource_id[0]).calendar_id.id
|
||||
if cal_id:
|
||||
calendar_id = cal_id
|
||||
default_uom_id = uom_obj.search(cr, uid, [('name', '=', 'Hour')])[0]
|
||||
avg_hours = uom_obj._compute_qty(cr, uid, phase.product_uom.id, phase.duration, default_uom_id)
|
||||
work_times = resource_calendar_obj.interval_get(cr, uid, calendar_id or False, date_start, avg_hours or 0.0, resource_id or False)
|
||||
dt_end = work_times[-1][1].strftime('%Y-%m-%d %H:%M:%S')
|
||||
self.write(cr, uid, [phase.id], {'date_start': date_start.strftime('%Y-%m-%d %H:%M:%S'), 'date_end': dt_end})
|
||||
|
||||
def write(self, cr, uid, ids, vals, context=None):
|
||||
|
||||
|
@ -179,62 +176,62 @@ class project_phase(osv.osv):
|
|||
if context.get('scheduler',False):
|
||||
return super(project_phase, self).write(cr, uid, ids, vals, context=context)
|
||||
|
||||
# if the phase is performed by a resource then its calendar and efficiency also taken
|
||||
# otherwise the project's working calendar considered
|
||||
# Consider calendar and efficiency if the phase is performed by a resource
|
||||
# otherwise consider the project's working calendar
|
||||
phase = self.browse(cr, uid, ids[0])
|
||||
resource_cal_pool = self.pool.get('resource.calendar')
|
||||
resource_pool = self.pool.get('resource.resource')
|
||||
uom_pool = self.pool.get('product.uom')
|
||||
resource_calendar_obj = self.pool.get('resource.calendar')
|
||||
resource_obj = self.pool.get('resource.resource')
|
||||
uom_obj = self.pool.get('product.uom')
|
||||
resource_id = False
|
||||
calendar_id = phase.project_id.resource_calendar_id.id
|
||||
resource_id = resource_pool.search(cr, uid, [('user_id','=',phase.responsible_id.id)])
|
||||
resource_id = resource_obj.search(cr, uid, [('user_id', '=', phase.responsible_id.id)])
|
||||
if resource_id:
|
||||
calendar_id = resource_pool.browse(cr, uid, resource_id[0]).calendar_id.id
|
||||
default_uom_id = uom_pool.search(cr, uid, [('name','=','Hour')])[0]
|
||||
avg_hours = uom_pool._compute_qty(cr, uid, phase.product_uom.id, phase.duration, default_uom_id)
|
||||
|
||||
# write method changes the date_start and date_end
|
||||
#for previous and next phases respectively based on valid condition
|
||||
cal_id = resource_obj.browse(cr, uid, resource_id[0]).calendar_id.id
|
||||
if cal_id:
|
||||
calendar_id = cal_id
|
||||
default_uom_id = uom_obj.search(cr, uid, [('name', '=', 'Hour')])[0]
|
||||
avg_hours = uom_obj._compute_qty(cr, uid, phase.product_uom.id, phase.duration, default_uom_id)
|
||||
|
||||
# Change the date_start and date_end
|
||||
# for previous and next phases respectively based on valid condition
|
||||
if vals.get('date_start'):
|
||||
if vals['date_start'] < phase.date_start:
|
||||
dt_start = mx.DateTime.strptime(vals['date_start'],'%Y-%m-%d %H:%M:%S')
|
||||
work_times = resource_cal_pool.interval_get(cr, uid, calendar_id or False, dt_start, avg_hours or 0.0, resource_id or False)
|
||||
work_times = resource_calendar_obj.interval_get(cr, uid, calendar_id or False, dt_start, avg_hours or 0.0, resource_id or False)
|
||||
vals['date_end'] = work_times[-1][1].strftime('%Y-%m-%d %H:%M:%S')
|
||||
super(project_phase, self).write(cr, uid, ids, vals, context=context)
|
||||
|
||||
for prv_phase in phase.previous_phase_ids:
|
||||
self.constraint_date_start(cr, uid, prv_phase, dt_start)
|
||||
|
||||
self._check_date_start(cr, uid, prv_phase, dt_start)
|
||||
if vals.get('date_end'):
|
||||
if vals['date_end'] > phase.date_end:
|
||||
dt_end = mx.DateTime.strptime(vals['date_end'],'%Y-%m-%d %H:%M:%S')
|
||||
work_times = resource_cal_pool.interval_min_get(cr, uid, calendar_id or False, dt_end, avg_hours or 0.0, resource_id or False)
|
||||
work_times = resource_calendar_obj.interval_min_get(cr, uid, calendar_id or False, dt_end, avg_hours or 0.0, resource_id or False)
|
||||
vals['date_start'] = work_times[0][0].strftime('%Y-%m-%d %H:%M:%S')
|
||||
super(project_phase, self).write(cr, uid, ids, vals, context=context)
|
||||
|
||||
for next_phase in phase.next_phase_ids:
|
||||
self.constraint_date_end(cr, uid, next_phase, dt_end)
|
||||
self._check_date_end(cr, uid, next_phase, dt_end)
|
||||
|
||||
return super(project_phase, self).write(cr, uid, ids, vals, context=context)
|
||||
|
||||
def phase_draft(self, cr, uid, ids,*args):
|
||||
def set_draft(self, cr, uid, ids, *args):
|
||||
self.write(cr, uid, ids, {'state': 'draft'})
|
||||
return True
|
||||
|
||||
def phase_start(self, cr, uid, ids,*args):
|
||||
def set_open(self, cr, uid, ids, *args):
|
||||
self.write(cr, uid, ids, {'state': 'open'})
|
||||
return True
|
||||
|
||||
def phase_pending(self, cr, uid, ids,*args):
|
||||
def set_pending(self, cr, uid, ids,*args):
|
||||
self.write(cr, uid, ids, {'state': 'pending'})
|
||||
return True
|
||||
|
||||
def phase_cancel(self, cr, uid, ids,*args):
|
||||
def set_cancel(self, cr, uid, ids, *args):
|
||||
self.write(cr, uid, ids, {'state': 'cancelled'})
|
||||
return True
|
||||
|
||||
def phase_done(self, cr, uid, ids,*args):
|
||||
def set_done(self, cr, uid, ids, *args):
|
||||
self.write(cr, uid, ids, {'state': 'done'})
|
||||
return True
|
||||
|
||||
|
@ -246,7 +243,7 @@ class project_resource_allocation(osv.osv):
|
|||
_rec_name = 'resource_id'
|
||||
_columns = {
|
||||
'resource_id': fields.many2one('resource.resource', 'Resource', required=True),
|
||||
'phase_id': fields.many2one('project.phase', 'Project Phase',required=True),
|
||||
'phase_id': fields.many2one('project.phase', 'Project Phase', required=True),
|
||||
'useability': fields.float('Useability', help="Useability of this ressource for this project phase in percentage (=50%)"),
|
||||
}
|
||||
_defaults = {
|
||||
|
@ -257,7 +254,7 @@ project_resource_allocation()
|
|||
|
||||
class project(osv.osv):
|
||||
_inherit = "project.project"
|
||||
|
||||
_description = "Project"
|
||||
_columns = {
|
||||
'phase_ids': fields.one2many('project.phase', 'project_id', "Project Phases"),
|
||||
'resource_calendar_id': fields.many2one('resource.calendar', 'Working Time', help="Timetable working hours to adjust the gantt diagram report"),
|
||||
|
@ -267,7 +264,7 @@ project()
|
|||
|
||||
class task(osv.osv):
|
||||
_inherit = "project.task"
|
||||
|
||||
_description = "Task"
|
||||
_columns = {
|
||||
'phase_id': fields.many2one('project.phase', 'Project Phase'),
|
||||
'occupation_rate': fields.float('Occupation Rate', help='The occupation rate fields indicates how much of his time a user is working on a task. A 100% occupation rate means the user works full time on the tasks. The ending date of a task is computed like this: Starting Date + Duration / Occupation Rate.'),
|
||||
|
@ -280,53 +277,58 @@ class task(osv.osv):
|
|||
def onchange_planned(self, cr, uid, ids, project, user_id=False, planned=0.0, effective=0.0, date_start=None, occupation_rate=0.0):
|
||||
result = {}
|
||||
if date_start:
|
||||
resource_pool = self.pool.get('resource.resource')
|
||||
resource_obj = self.pool.get('resource.resource')
|
||||
project_pool = self.pool.get('project.project')
|
||||
hrs = float(planned / float(occupation_rate))
|
||||
calendar_id = project_pool.browse(cr, uid, project).resource_calendar_id.id
|
||||
resource_calendar = self.pool.get('resource.calendar')
|
||||
dt_start = mx.DateTime.strptime(date_start, '%Y-%m-%d %H:%M:%S')
|
||||
resource_id = resource_pool.search(cr, uid, [('user_id','=',user_id)])
|
||||
resource_id = resource_obj.search(cr, uid, [('user_id','=',user_id)])
|
||||
if resource_id:
|
||||
resource_obj = resource_pool.browse(cr, uid, resource_id)[0]
|
||||
hrs = planned / (float(occupation_rate) * resource_obj.time_efficiency)
|
||||
calendar_id = resource_obj.calendar_id.id
|
||||
else:
|
||||
hrs = float(planned / float(occupation_rate))
|
||||
calendar_id = project_pool.browse(cr, uid, project).resource_calendar_id .id
|
||||
resource = resource_obj.browse(cr, uid, resource_id)[0]
|
||||
hrs = planned / (float(occupation_rate) * resource.time_efficiency)
|
||||
if resource.calendar_id.id:
|
||||
calendar_id = resource.calendar_id.id
|
||||
work_times = resource_calendar.interval_get(cr, uid, calendar_id or False, dt_start, hrs or 0.0, resource_id or False)
|
||||
result['date_end'] = work_times[-1][1].strftime('%Y-%m-%d %H:%M:%S')
|
||||
result['remaining_hours'] = planned - effective
|
||||
return {'value' : result}
|
||||
|
||||
def constraint_date_start(self, cr, uid, task, date_end, context=None):
|
||||
# Recursive call for all previous tasks if change in date_start < older time
|
||||
|
||||
resource_cal_pool = self.pool.get('resource.calendar')
|
||||
resource_pool = self.pool.get('resource.resource')
|
||||
def _check_date_start(self, cr, uid, task, date_end, context=None):
|
||||
"""
|
||||
Check And Compute date_end of task if change in date_start < older time.
|
||||
"""
|
||||
resource_calendar_obj = self.pool.get('resource.calendar')
|
||||
resource_obj = self.pool.get('resource.resource')
|
||||
calendar_id = task.project_id.resource_calendar_id.id
|
||||
hours = task.remaining_hours / task.occupation_rate
|
||||
resource_id = resource_pool.search(cr, uid, [('user_id','=',task.user_id.id)])
|
||||
hours = task.planned_hours / task.occupation_rate
|
||||
resource_id = resource_obj.search(cr, uid, [('user_id', '=', task.user_id.id)])
|
||||
if resource_id:
|
||||
resource_obj = resource_pool.browse(cr, uid, resource_id[0])
|
||||
calendar_id = resource_obj.calendar_id.id
|
||||
hours = task.planned_hours / (float(task.occupation_rate) * resource_obj.time_efficiency)
|
||||
work_time = resource_cal_pool.interval_min_get(cr, uid, calendar_id or False, date_end, hours or 0.0, resource_id or False)
|
||||
dt_start = work_time[0][0].strftime('%Y-%m-%d %H:%M:%S')
|
||||
resource = resource_obj.browse(cr, uid, resource_id[0])
|
||||
if resource.calendar_id.id:
|
||||
calendar_id = resource.calendar_id.id
|
||||
hours = task.planned_hours / (float(task.occupation_rate) * resource.time_efficiency)
|
||||
work_times = resource_calendar_obj.interval_min_get(cr, uid, calendar_id or False, date_end, hours or 0.0, resource_id or False)
|
||||
dt_start = work_times[0][0].strftime('%Y-%m-%d %H:%M:%S')
|
||||
self.write(cr, uid, [task.id], {'date_start' : dt_start,'date_end' : date_end.strftime('%Y-%m-%d %H:%M:%S')})
|
||||
|
||||
def constraint_date_end(self, cr, uid, task, date_start, context=None):
|
||||
# Recursive call for all next tasks if change in date_end > older time
|
||||
def _check_date_end(self, cr, uid, task, date_start, context=None):
|
||||
"""
|
||||
Check And Compute date_end of task if change in date_end > older time.
|
||||
"""
|
||||
|
||||
resource_cal_pool = self.pool.get('resource.calendar')
|
||||
resource_pool = self.pool.get('resource.resource')
|
||||
resource_calendar_obj = self.pool.get('resource.calendar')
|
||||
resource_obj = self.pool.get('resource.resource')
|
||||
calendar_id = task.project_id.resource_calendar_id.id
|
||||
hours = task.remaining_hours / task.occupation_rate
|
||||
resource_id = resource_pool.search(cr,uid,[('user_id','=',task.user_id.id)])
|
||||
hours = task.planned_hours / task.occupation_rate
|
||||
resource_id = resource_obj.search(cr,uid,[('user_id', '=', task.user_id.id)])
|
||||
if resource_id:
|
||||
resource_obj = resource_pool.browse(cr, uid, resource_id[0])
|
||||
calendar_id = resource_obj.calendar_id.id
|
||||
hours = task.planned_hours / (float(task.occupation_rate) * resource_obj.time_efficiency)
|
||||
work_time = resource_cal_pool.interval_get(cr, uid, calendar_id or False, date_start, hours or 0.0, resource_id or False)
|
||||
dt_end = work_time[-1][1].strftime('%Y-%m-%d %H:%M:%S')
|
||||
resource = resource_obj.browse(cr, uid, resource_id[0])
|
||||
if resource.calendar_id.id:
|
||||
calendar_id = resource.calendar_id.id
|
||||
hours = task.planned_hours / (float(task.occupation_rate) * resource.time_efficiency)
|
||||
work_times = resource_calendar_obj.interval_get(cr, uid, calendar_id or False, date_start, hours or 0.0, resource_id or False)
|
||||
dt_end = work_times[-1][1].strftime('%Y-%m-%d %H:%M:%S')
|
||||
self.write(cr, uid, [task.id], {'date_start' : date_start.strftime('%Y-%m-%d %H:%M:%S'),'date_end' : dt_end})
|
||||
|
||||
def write(self, cr, uid, ids, vals, context=None):
|
||||
|
@ -337,44 +339,43 @@ class task(osv.osv):
|
|||
if context.get('scheduler',False):
|
||||
return super(task, self).write(cr, uid, ids, vals, context=context)
|
||||
|
||||
# if the task is performed by a resource then its calendar and efficiency also taken
|
||||
# otherwise the project's working calendar considered
|
||||
# Consider calendar and efficiency if the task is performed by a resource
|
||||
# otherwise consider the project's working calendar
|
||||
task_id = ids
|
||||
if isinstance(ids, list):
|
||||
task_id = ids[0]
|
||||
tasks = self.browse(cr, uid, task_id, context=context)
|
||||
resource_cal_pool = self.pool.get('resource.calendar')
|
||||
resource_pool = self.pool.get('resource.resource')
|
||||
calendar_id = tasks.project_id.resource_calendar_id.id
|
||||
hrs = tasks.remaining_hours / tasks.occupation_rate
|
||||
resource_id = resource_pool.search(cr, uid, [('user_id','=',tasks.user_id.id)])
|
||||
task_rec = self.browse(cr, uid, task_id, context=context)
|
||||
resource_calendar_obj = self.pool.get('resource.calendar')
|
||||
resource_obj = self.pool.get('resource.resource')
|
||||
calendar_id = task_rec.project_id.resource_calendar_id.id
|
||||
hrs = task_rec.planned_hours / task_rec.occupation_rate
|
||||
resource_id = resource_obj.search(cr, uid, [('user_id', '=', task_rec.user_id.id)])
|
||||
if resource_id:
|
||||
resource_obj = resource_pool.browse(cr, uid, resource_id[0])
|
||||
calendar_id = resource_obj.calendar_id.id
|
||||
hrs = tasks.planned_hours / (float(tasks.occupation_rate) * resource_obj.time_efficiency)
|
||||
resource = resource_obj.browse(cr, uid, resource_id[0])
|
||||
if resource.calendar_id.id:
|
||||
calendar_id = resource.calendar_id.id
|
||||
hrs = task_rec.planned_hours / (float(task_rec.occupation_rate) * resource.time_efficiency)
|
||||
|
||||
# write method changes the date_start and date_end
|
||||
# for previous and next tasks respectively based on valid condition
|
||||
# Change the date_start and date_end
|
||||
# for previous and next tasks respectively based on valid condition
|
||||
if vals.get('date_start'):
|
||||
if vals['date_start'] < tasks.date_start:
|
||||
dt_start = mx.DateTime.strptime(vals['date_start'],'%Y-%m-%d %H:%M:%S')
|
||||
work_times = resource_cal_pool.interval_get(cr, uid, calendar_id or False, dt_start, hrs or 0.0, resource_id or False)
|
||||
if vals['date_start'] < task_rec.date_start:
|
||||
dt_start = mx.DateTime.strptime(vals['date_start'], '%Y-%m-%d %H:%M:%S')
|
||||
work_times = resource_calendar_obj.interval_get(cr, uid, calendar_id or False, dt_start, hrs or 0.0, resource_id or False)
|
||||
vals['date_end'] = work_times[-1][1].strftime('%Y-%m-%d %H:%M:%S')
|
||||
super(task, self).write(cr, uid, ids, vals, context=context)
|
||||
|
||||
for prv_task in tasks.parent_ids:
|
||||
self.constraint_date_start(cr, uid, prv_task, dt_start)
|
||||
|
||||
for prv_task in task_rec.parent_ids:
|
||||
self._check_date_start(cr, uid, prv_task, dt_start)
|
||||
if vals.get('date_end'):
|
||||
if vals['date_end'] > tasks.date_end:
|
||||
dt_end = mx.DateTime.strptime(vals['date_end'],'%Y-%m-%d %H:%M:%S')
|
||||
hrs = tasks.remaining_hours / tasks.occupation_rate
|
||||
work_times = resource_cal_pool.interval_min_get(cr, uid, calendar_id or False, dt_end, hrs or 0.0, resource_id or False)
|
||||
if vals['date_end'] > task_rec.date_end:
|
||||
dt_end = mx.DateTime.strptime(vals['date_end'], '%Y-%m-%d %H:%M:%S')
|
||||
work_times = resource_calendar_obj.interval_min_get(cr, uid, calendar_id or False, dt_end, hrs or 0.0, resource_id or False)
|
||||
vals['date_start'] = work_times[0][0].strftime('%Y-%m-%d %H:%M:%S')
|
||||
super(task, self).write(cr, uid, ids, vals, context=context)
|
||||
|
||||
for next_task in tasks.child_ids:
|
||||
self.constraint_date_end(cr, uid, next_task, dt_end)
|
||||
for next_task in task_rec.child_ids:
|
||||
self._check_date_end(cr, uid, next_task, dt_end)
|
||||
|
||||
return super(task, self).write(cr, uid, ids, vals, context=context)
|
||||
|
||||
|
|
|
@ -201,6 +201,7 @@
|
|||
</record>
|
||||
|
||||
<!-- Resources -->
|
||||
|
||||
<record id="resource_resource_analyst0" model="resource.resource">
|
||||
<field eval="1.0" name="time_efficiency"/>
|
||||
<field name="user_id" ref="res_users_useranalyst0"/>
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
# ------------------------------------------------------
|
||||
# Project Resource Allocation
|
||||
# ------------------------------------------------------
|
||||
|
||||
<record id="view_project_resource_allocation_form" model="ir.ui.view">
|
||||
<field name="name">project.resource.allocation.form</field>
|
||||
<field name="model">project.resource.allocation</field>
|
||||
|
@ -13,7 +18,7 @@
|
|||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record id="view_project_resource_allocation_list" model="ir.ui.view">
|
||||
<field name="name">project.resource.allocation.list</field>
|
||||
<field name="model">project.resource.allocation</field>
|
||||
|
@ -26,7 +31,11 @@
|
|||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
# ------------------------------------------------------
|
||||
# Project Phase
|
||||
# ------------------------------------------------------
|
||||
|
||||
<record id="view_project_phase_form" model="ir.ui.view">
|
||||
<field name="name">project.phase.form</field>
|
||||
<field name="model">project.phase</field>
|
||||
|
@ -57,11 +66,11 @@
|
|||
</field>
|
||||
<group col="12" colspan="4">
|
||||
<field name="state" select="1"/>
|
||||
<button string="Draft" name="phase_draft" states="open"/>
|
||||
<button string="Start Phase" name="phase_start" states="pending,draft"/>
|
||||
<button string="Cancel" name="phase_cancel" states="draft,open,pending"/>
|
||||
<button string="Done" name="phase_done" states="draft,pending,open"/>
|
||||
<button string="Pending" name="phase_pending" states="open"/>
|
||||
<button string="Draft" name="set_draft" states="open" icon="gtk-indent"/>
|
||||
<button string="Start Phase" name="set_open" states="pending,draft" icon="gtk-execute"/>
|
||||
<button string="Cancel" name="set_cancel" states="draft,open,pending" icon="gtk-cancel"/>
|
||||
<button string="Done" name="set_done" states="draft,pending,open" icon="gtk-jump-to"/>
|
||||
<button string="Pending" name="set_pending" states="open" icon="gtk-media-pause"/>
|
||||
</group>
|
||||
</page>
|
||||
<page string="Other Info">
|
||||
|
@ -128,7 +137,7 @@
|
|||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record id="view_project_phase_list" model="ir.ui.view">
|
||||
<field name="name">project.phase.list</field>
|
||||
<field name="model">project.phase</field>
|
||||
|
@ -185,7 +194,7 @@
|
|||
<field name="project_id" widget="selection"/>
|
||||
<field name="responsible_id">
|
||||
<filter domain="[('responsible_id','=',uid)]" help="My Phase" icon="gtk-execute" default="1"/>
|
||||
</field>
|
||||
</field>
|
||||
<field name="date_start"/>
|
||||
<field name="date_end"/>
|
||||
</group>
|
||||
|
@ -194,7 +203,7 @@
|
|||
<filter string="Project" icon="terp-project" domain="[]" context="{'group_by':'project_id'}"/>
|
||||
<filter string="User" icon="terp-project" domain="[]" context="{'group_by':'responsible_id'}"/>
|
||||
<filter string="Start Date" icon="terp-project" domain="[]" context="{'group_by':'date_start'}"/>
|
||||
</group>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
@ -207,6 +216,10 @@
|
|||
<field name="search_view_id" ref="view_project_phase_search"/>
|
||||
</record>
|
||||
|
||||
# ------------------------------------------------------
|
||||
# Project
|
||||
# ------------------------------------------------------
|
||||
|
||||
<record id="view_phase_project_form1" model="ir.ui.view">
|
||||
<field name="name">phase.project.form1</field>
|
||||
<field name="model">project.project</field>
|
||||
|
@ -220,6 +233,10 @@
|
|||
</field>
|
||||
</record>
|
||||
|
||||
# ------------------------------------------------------
|
||||
# Project Task
|
||||
# ------------------------------------------------------
|
||||
|
||||
<record id="view_project_phase_task_form1" model="ir.ui.view">
|
||||
<field name="name">project.phase.task.form1</field>
|
||||
<field name="model">project.task</field>
|
||||
|
@ -262,9 +279,11 @@
|
|||
view_mode="tree,form"
|
||||
view_type="form" />
|
||||
|
||||
<menuitem action="act_project_phase" id="menu_project_phase" parent="project.menu_project_management" sequence="4"/>
|
||||
# ------------------------------------------------------
|
||||
# Menu Items
|
||||
# ------------------------------------------------------
|
||||
|
||||
<!-- Menu for Resource for Long Term Project -->
|
||||
<menuitem action="act_project_phase" id="menu_project_phase" parent="project.menu_project_management" sequence="4"/>
|
||||
<menuitem icon="terp-project" id="base.menu_main_pm" name="Project Management" sequence="1"/>
|
||||
<menuitem id="menu_pm_resources_project1" name="Resources" parent="base.menu_main_pm" sequence="2"/>
|
||||
<menuitem action="resource.action_resource_resource_tree" id="menu_view_resource" parent="menu_pm_resources_project1" sequence="1"/>
|
||||
|
|
|
@ -10,95 +10,99 @@
|
|||
<record id="act_draft" model="workflow.activity">
|
||||
<field name="wkf_id" ref="wkf_phase"/>
|
||||
<field name="flow_start">True</field>
|
||||
<field name="name">phase_draft</field>
|
||||
<field name="name">set_draft</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">phase_draft()</field>
|
||||
<field name="action">set_draft()</field>
|
||||
</record>
|
||||
|
||||
<record id="act_start_phase" model="workflow.activity">
|
||||
<field name="wkf_id" ref="wkf_phase"/>
|
||||
<field name="name">phase_start</field>
|
||||
<field name="name">set_open</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">phase_start()</field>
|
||||
|
||||
<field name="action">set_open()</field>
|
||||
</record>
|
||||
|
||||
<record id="act_cancel_phase" model="workflow.activity">
|
||||
<field name="wkf_id" ref="wkf_phase"/>
|
||||
<field name="name">phase_cancel</field>
|
||||
<field name="name">set_cancel</field>
|
||||
<field name="flow_stop">True</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">phase_cancel()</field>
|
||||
|
||||
<field name="action">set_cancel()</field>
|
||||
</record>
|
||||
|
||||
<record id="act_phase_pending" model="workflow.activity">
|
||||
<field name="wkf_id" ref="wkf_phase"/>
|
||||
<field name="name">phase_pending</field>
|
||||
<field name="name">set_pending</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">phase_pending()</field>
|
||||
<field name="action">set_pending()</field>
|
||||
</record>
|
||||
|
||||
<record id="act_phase_done" model="workflow.activity">
|
||||
<field name="wkf_id" ref="wkf_phase"/>
|
||||
<field name="name">phase_done</field>
|
||||
<field name="name">set_done</field>
|
||||
<field name="flow_stop">True</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">phase_done()</field>
|
||||
<field name="action">set_done()</field>
|
||||
</record>
|
||||
|
||||
<record id="t0" model="workflow.transition">
|
||||
<field name="act_from" ref="act_draft"/>
|
||||
<field name="act_to" ref="act_start_phase"/>
|
||||
<field name="signal">phase_start</field>
|
||||
<field name="signal">set_open</field>
|
||||
</record>
|
||||
|
||||
<record id="t1" model="workflow.transition">
|
||||
<field name="act_from" ref="act_draft"/>
|
||||
<field name="act_to" ref="act_cancel_phase"/>
|
||||
<field name="signal">phase_cancel</field>
|
||||
<field name="signal">set_cancel</field>
|
||||
</record>
|
||||
|
||||
<record id="t2" model="workflow.transition">
|
||||
<field name="act_from" ref="act_draft"/>
|
||||
<field name="act_to" ref="act_phase_done"/>
|
||||
<field name="signal">phase_done</field>
|
||||
<field name="signal">set_done</field>
|
||||
</record>
|
||||
|
||||
<record id="t3" model="workflow.transition">
|
||||
<field name="act_from" ref="act_start_phase"/>
|
||||
<field name="act_to" ref="act_phase_pending"/>
|
||||
<field name="signal">phase_pending</field>
|
||||
<field name="signal">set_pending</field>
|
||||
</record>
|
||||
|
||||
<record id="t4" model="workflow.transition">
|
||||
<field name="act_from" ref="act_phase_pending"/>
|
||||
<field name="act_to" ref="act_cancel_phase"/>
|
||||
<field name="signal">phase_cancel</field>
|
||||
<field name="signal">set_cancel</field>
|
||||
</record>
|
||||
|
||||
<record id="t5" model="workflow.transition">
|
||||
<field name="act_from" ref="act_phase_pending"/>
|
||||
<field name="act_to" ref="act_draft"/>
|
||||
<field name="signal">phase_draft</field>
|
||||
<field name="signal">set_draft</field>
|
||||
</record>
|
||||
|
||||
<record id="t6" model="workflow.transition">
|
||||
<field name="act_from" ref="act_start_phase"/>
|
||||
<field name="act_to" ref="act_cancel_phase"/>
|
||||
<field name="signal">phase_cancel</field>
|
||||
<field name="act_from" ref="act_phase_pending"/>
|
||||
<field name="act_to" ref="act_start_phase"/>
|
||||
<field name="signal">set_open</field>
|
||||
</record>
|
||||
|
||||
<record id="t7" model="workflow.transition">
|
||||
<field name="act_from" ref="act_start_phase"/>
|
||||
<field name="act_to" ref="act_phase_done"/>
|
||||
<field name="signal">phase_done</field>
|
||||
<field name="act_to" ref="act_cancel_phase"/>
|
||||
<field name="signal">set_cancel</field>
|
||||
</record>
|
||||
|
||||
<record id="t8" model="workflow.transition">
|
||||
<field name="act_from" ref="act_start_phase"/>
|
||||
<field name="act_to" ref="act_phase_done"/>
|
||||
<field name="signal">set_done</field>
|
||||
</record>
|
||||
|
||||
<record id="t9" model="workflow.transition">
|
||||
<field name="act_from" ref="act_start_phase"/>
|
||||
<field name="act_to" ref="act_draft"/>
|
||||
<field name="signal">phase_draft</field>
|
||||
<field name="signal">set_draft</field>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
|
@ -22,7 +22,7 @@
|
|||
import compute_phases_date
|
||||
import compute_tasks_date
|
||||
import schedule_tasks
|
||||
import project_resource
|
||||
import working_calendar
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
|
|
|
@ -23,9 +23,11 @@ import wizard
|
|||
import pooler
|
||||
from tools.translate import _
|
||||
import datetime
|
||||
|
||||
from resource.faces import *
|
||||
from new import classobj
|
||||
import project_resource as proj
|
||||
|
||||
import working_calendar as wkcal
|
||||
|
||||
compute_form = """<?xml version="1.0" ?>
|
||||
<form string="Compute Scheduling of Phases">
|
||||
|
@ -35,42 +37,54 @@ compute_form = """<?xml version="1.0" ?>
|
|||
</form>"""
|
||||
|
||||
compute_fields = {
|
||||
'project_id': {'string':'Project', 'type':'many2one', 'relation': 'project.project'},
|
||||
'project_id': {'string':'Project', 'type':'many2one', 'relation':'project.project'},
|
||||
|
||||
}
|
||||
|
||||
success_msg = """<?xml version="1.0" ?>
|
||||
<form string="Compute Scheduling of Phases">
|
||||
<label string="Phase Scheduling completed successfully."/>
|
||||
</form>"""
|
||||
class wizard_compute_phases(wizard.interface):
|
||||
def _phase_schedule(self, cr, uid, phase, start_date, calendar_id=False, context={}):
|
||||
|
||||
"""Schedule phase with the start date till all the next phases are completed.
|
||||
|
||||
Arguements: start_dsate -- start date for the phase
|
||||
calendar_id -- working calendar of the project
|
||||
|
||||
"""
|
||||
|
||||
def phase_schedule(cr, uid, phase, start_date, calendar_id=False):
|
||||
pool = pooler.get_pool(cr.dbname)
|
||||
phase_pool = pool.get('project.phase')
|
||||
resource_pool = pool.get('resource.resource')
|
||||
uom_pool = pool.get('product.uom')
|
||||
wktime_cal = []
|
||||
resource_cal = False
|
||||
phase_resource = False
|
||||
phase_obj = pool.get('project.phase')
|
||||
resource_obj = pool.get('resource.resource')
|
||||
uom_obj = pool.get('product.uom')
|
||||
phase_resource_obj = False
|
||||
if phase:
|
||||
resource_id = resource_pool.search(cr, uid, [('user_id','=',phase.responsible_id.id)])
|
||||
leaves = []
|
||||
time_efficiency = 1.0
|
||||
resource_id = resource_obj.search(cr, uid, [('user_id', '=', phase.responsible_id.id)])
|
||||
if resource_id:
|
||||
resource_obj = resource_pool.browse(cr, uid, resource_id)[0]
|
||||
leaves = proj.leaves_resource(cr, uid, calendar_id or False , resource_id, resource_obj.calendar_id.id)
|
||||
phase_resource = classobj(str(resource_obj.name), (Resource,), {'__doc__' : resource_obj.name, '__name__' : resource_obj.name, 'vacation' : tuple(leaves), 'efficiency' : resource_obj.time_efficiency})
|
||||
default_uom_id = uom_pool.search(cr, uid, [('name','=','Hour')])[0]
|
||||
avg_hours = uom_pool._compute_qty(cr, uid, phase.product_uom.id, phase.duration, default_uom_id)
|
||||
# Create a new resource object with
|
||||
# all the attributes of the Resource Class
|
||||
resource = resource_obj.browse(cr, uid, resource_id, context=context)[0]
|
||||
time_efficiency = resource.time_efficiency
|
||||
leaves = wkcal.compute_leaves(cr, uid, calendar_id or False , resource_id, resource.calendar_id.id)
|
||||
phase_resource_obj = classobj(str(phase.responsible_id.name), (Resource,),
|
||||
{'__doc__': phase.responsible_id.name,
|
||||
'__name__': phase.responsible_id.name,
|
||||
'vacation': tuple(leaves),
|
||||
'efficiency': time_efficiency
|
||||
})
|
||||
default_uom_id = uom_obj.search(cr, uid, [('name','=','Hour')])[0]
|
||||
avg_hours = uom_obj._compute_qty(cr, uid, phase.product_uom.id, phase.duration, default_uom_id)
|
||||
duration = str(avg_hours) + 'H'
|
||||
|
||||
# Creating a new project for each phase
|
||||
# Create a new project for each phase
|
||||
def Project():
|
||||
start = start_date
|
||||
minimum_time_unit = 1
|
||||
resource = phase_resource
|
||||
# If project has working calendar else the default one would be considered
|
||||
resource = phase_resource_obj
|
||||
# If project has working calendar then that
|
||||
# else the default one would be considered
|
||||
if calendar_id:
|
||||
working_days = proj.compute_working_calendar(cr, uid, calendar_id)
|
||||
vacation = tuple(proj.leaves_resource(cr, uid, calendar_id))
|
||||
working_days = wkcal.compute_working_calendar(cr, uid, calendar_id)
|
||||
vacation = tuple(wkcal.compute_leaves(cr, uid, calendar_id))
|
||||
|
||||
def phase():
|
||||
effort = duration
|
||||
|
@ -78,7 +92,8 @@ def phase_schedule(cr, uid, phase, start_date, calendar_id=False):
|
|||
project = BalancedProject(Project)
|
||||
s_date = project.phase.start.to_datetime()
|
||||
e_date = project.phase.end.to_datetime()
|
||||
# According to constraints on date start and date end on phase recalculation done
|
||||
# Recalculate date_start and date_end
|
||||
# according to constraints on date start and date end on phase
|
||||
if phase.constraint_date_start and str(s_date) < phase.constraint_date_start:
|
||||
start_date = datetime.datetime.strptime(phase.constraint_date_start, '%Y-%m-%d %H:%M:%S')
|
||||
else:
|
||||
|
@ -89,54 +104,59 @@ def phase_schedule(cr, uid, phase, start_date, calendar_id=False):
|
|||
else:
|
||||
end_date = e_date
|
||||
date_start = end_date
|
||||
|
||||
# Writing the dates back
|
||||
phase_pool.write(cr, uid, [phase.id], {'date_start' :start_date.strftime('%Y-%m-%d %H:%M:%S'), 'date_end' :end_date.strftime('%Y-%m-%d %H:%M:%S')}, context={'scheduler' :True})
|
||||
|
||||
# Recursive calling the next phases till all the phases are scheduled
|
||||
# Write the calculated dates back
|
||||
phase_obj.write(cr, uid, [phase.id], {'date_start': start_date.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'date_end': end_date.strftime('%Y-%m-%d %H:%M:%S')},
|
||||
context={'scheduler': True
|
||||
})
|
||||
# Recursive call till all the next phases scheduled
|
||||
for phase in phase.next_phase_ids:
|
||||
if phase.state in ['draft','open','pending']:
|
||||
phase_schedule(cr, uid, phase, date_start, phase.project_id.resource_calendar_id.id or False)
|
||||
self._phase_schedule(cr, uid, phase, date_start, phase.project_id.resource_calendar_id.id or False)
|
||||
else:
|
||||
continue
|
||||
|
||||
#
|
||||
class wizard_compute_phases(wizard.interface):
|
||||
|
||||
def _compute_date(self, cr, uid, data, context):
|
||||
"""
|
||||
Compute the phases for scheduling.
|
||||
"""
|
||||
pool = pooler.get_pool(cr.dbname)
|
||||
project_pool = pool.get('project.project')
|
||||
phase_pool = pool.get('project.phase')
|
||||
|
||||
# if project mentioned
|
||||
if data['form']['project_id']:
|
||||
project_id = project_pool.browse(cr, uid, data['form']['project_id'])
|
||||
phase_ids = phase_pool.search(cr, uid, [('project_id','=',project_id.id), ('state','in',['draft','open','pending']), ('previous_phase_ids','=',False)])
|
||||
|
||||
# else all the draft,open,pending states phases taken
|
||||
else:
|
||||
phase_ids = phase_pool.search(cr, uid,[('state','in',['draft','open','pending']), ('previous_phase_ids','=',False)])
|
||||
|
||||
project_obj = pool.get('project.project')
|
||||
phase_obj = pool.get('project.phase')
|
||||
if data['form']['project_id']: # If project mentioned find its phases
|
||||
project_id = project_obj.browse(cr, uid, data['form']['project_id'], context=context)
|
||||
phase_ids = phase_obj.search(cr, uid, [('project_id', '=', project_id.id),
|
||||
('state', 'in', ['draft', 'open', 'pending']),
|
||||
('previous_phase_ids', '=', False)
|
||||
])
|
||||
else: # Else take all the draft,open,pending states phases
|
||||
phase_ids = phase_obj.search(cr, uid,[('state', 'in', ['draft', 'open', 'pending']),
|
||||
('previous_phase_ids', '=', False)
|
||||
])
|
||||
phase_ids.sort()
|
||||
phase_objs = phase_pool.browse(cr, uid, phase_ids)
|
||||
phase_objs = phase_obj.browse(cr, uid, phase_ids, context=context)
|
||||
for phase in phase_objs:
|
||||
start_date = phase.project_id.date_start
|
||||
if not phase.project_id.date_start:
|
||||
start_date = datetime.datetime.now().strftime("%Y-%m-%d")
|
||||
start_dt = datetime.datetime.strftime((datetime.datetime.strptime(start_date, "%Y-%m-%d")), "%Y-%m-%d %H:%M")
|
||||
calendar_id = phase.project_id.resource_calendar_id.id
|
||||
phase_schedule(cr, uid, phase, start_dt, calendar_id or False)
|
||||
self._phase_schedule(cr, uid, phase, start_dt, calendar_id or False)
|
||||
return {}
|
||||
|
||||
def phases_open_list(self, cr, uid, data, context):
|
||||
def _open_phases_list(self, cr, uid, data, context):
|
||||
"""
|
||||
Return the scheduled phases list.
|
||||
"""
|
||||
mod_obj = pooler.get_pool(cr.dbname).get('ir.model.data')
|
||||
act_obj = pooler.get_pool(cr.dbname).get('ir.actions.act_window')
|
||||
result = mod_obj._get_id(cr, uid, 'project_long_term', 'act_project_phase')
|
||||
id = mod_obj.read(cr, uid, [result], ['res_id'])[0]['res_id']
|
||||
result = act_obj.read(cr, uid, [id], context=context)[0]
|
||||
if data['form']['project_id']:
|
||||
result['domain'] = [('project_id', '=', data['form']['project_id'])]
|
||||
result['domain'] = [('state', 'not in', ['cancelled','done'])]
|
||||
if data['form']['project_id']:
|
||||
result['domain'] = [('project_id', '=', data['form']['project_id']),
|
||||
('state', 'not in', ['cancelled','done'])]
|
||||
return result
|
||||
|
||||
states = {
|
||||
|
@ -150,11 +170,8 @@ class wizard_compute_phases(wizard.interface):
|
|||
},
|
||||
'compute': {
|
||||
'actions': [_compute_date],
|
||||
'result': {'type': 'action', 'action':phases_open_list, 'state':'end'},
|
||||
'result': {'type': 'action', 'action':_open_phases_list, 'state':'end'},
|
||||
},
|
||||
}
|
||||
|
||||
wizard_compute_phases('wizard.compute.phases')
|
||||
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -23,10 +23,12 @@ import wizard
|
|||
import pooler
|
||||
from tools.translate import _
|
||||
import datetime
|
||||
|
||||
from resource.faces import *
|
||||
from new import classobj
|
||||
import operator
|
||||
import project_resource as proj
|
||||
|
||||
import working_calendar as wkcal
|
||||
|
||||
compute_form = """<?xml version="1.0" ?>
|
||||
<form string="Compute Scheduling of Tasks">
|
||||
|
@ -39,44 +41,51 @@ success_msg = """<?xml version="1.0" ?>
|
|||
</form>"""
|
||||
|
||||
compute_fields = {
|
||||
'project_id': {'string':'Project', 'type':'many2one', 'relation': 'project.project', 'required':'True'},
|
||||
'project_id': {'string':'Project', 'type':'many2one', 'relation':'project.project', 'required':'True'},
|
||||
}
|
||||
|
||||
class wizard_compute_tasks(wizard.interface):
|
||||
|
||||
def _compute_date(self, cr, uid, data, context):
|
||||
"""
|
||||
Schedule the tasks according to resource available and priority.
|
||||
"""
|
||||
pool = pooler.get_pool(cr.dbname)
|
||||
project_pool = pool.get('project.project')
|
||||
project_obj = pool.get('project.project')
|
||||
task_pool = pool.get('project.task')
|
||||
resource_pool = pool.get('resource.resource')
|
||||
user_pool = pool.get('res.users')
|
||||
resource_obj = pool.get('resource.resource')
|
||||
user_obj = pool.get('res.users')
|
||||
project_id = data['form']['project_id']
|
||||
project = project_pool.browse(cr, uid, project_id)
|
||||
task_ids = task_pool.search(cr, uid, [('project_id','=',project_id), ('state','in',['draft','open','pending'])])
|
||||
project = project_obj.browse(cr, uid, project_id, context=context)
|
||||
task_ids = task_pool.search(cr, uid, [('project_id', '=', project_id),
|
||||
('state', 'in', ['draft', 'open', 'pending'])
|
||||
])
|
||||
if task_ids:
|
||||
wktime_cal = []
|
||||
task_ids.sort()
|
||||
task_obj = task_pool.browse(cr, uid, task_ids)
|
||||
task_objs = task_pool.browse(cr, uid, task_ids, context=context)
|
||||
calendar_id = project.resource_calendar_id.id
|
||||
start_date = project.date_start
|
||||
if not project.date_start:
|
||||
start_date = datetime.datetime.now().strftime("%Y-%m-%d")
|
||||
date_start = datetime.datetime.strftime(datetime.datetime.strptime(start_date, "%Y-%m-%d"), "%Y-%m-%d %H:%M")
|
||||
|
||||
# To create resources which are the Project Members
|
||||
resource_objs = []
|
||||
for resource in project.members:
|
||||
# Create Resource Class objects which are the Project Members
|
||||
resources = []
|
||||
for user in project.members:
|
||||
leaves = []
|
||||
resource_id = resource_pool.search(cr, uid, [('user_id','=',resource.id)])
|
||||
time_efficiency = 1.0
|
||||
resource_id = resource_obj.search(cr, uid, [('user_id', '=', user.id)])
|
||||
if resource_id:
|
||||
resource_obj = resource_pool.browse(cr, uid, resource_id)[0]
|
||||
leaves = proj.leaves_resource(cr, uid, calendar_id or False , resource_id, resource_obj.calendar_id.id)
|
||||
resource_objs.append(classobj(str(resource.name), (Resource,), {'__doc__' : resource.name, '__name__' : resource.name, 'vacation' : tuple(leaves), 'efficiency' : resource_obj.time_efficiency}))
|
||||
|
||||
priority_dict = {'0' :1000, '1' :800, '2' :500, '3' :300,'4' :100}
|
||||
|
||||
# To create dynamic no of tasks with the resource specified
|
||||
def tasks_resource(j, eff, priorty=500, obj=None):
|
||||
resource = resource_obj.browse(cr, uid, resource_id, context=context)[0]
|
||||
leaves = wkcal.compute_leaves(cr, uid, calendar_id or False , resource_id, resource.calendar_id.id)
|
||||
time_efficiency = resource.time_efficiency
|
||||
resources.append(classobj(str(user.name), (Resource,), {'__doc__': user.name,
|
||||
'__name__': user.name,
|
||||
'vacation': tuple(leaves),
|
||||
'efficiency': time_efficiency
|
||||
}))
|
||||
priority_dict = {'0': 1000, '1': 800, '2': 500, '3': 300,'4': 100}
|
||||
# Create dynamic no of tasks with the resource specified
|
||||
def create_tasks(j, eff, priorty=500, obj=None):
|
||||
def task():
|
||||
"""
|
||||
task is a dynamic method!
|
||||
|
@ -89,43 +98,47 @@ class wizard_compute_tasks(wizard.interface):
|
|||
task.__name__ = "task%d" %j
|
||||
return task
|
||||
|
||||
# Creating the project with all the tasks and resources
|
||||
# Create a 'Faces' project with all the tasks and resources
|
||||
def Project():
|
||||
title = project.name
|
||||
start = date_start
|
||||
resource = reduce(operator.or_, resource_objs)
|
||||
try:
|
||||
resource = reduce(operator.or_, resources)
|
||||
except:
|
||||
raise wizard.except_wizard(_('MemberError'), _('Project must have members assigned !'))
|
||||
minimum_time_unit = 1
|
||||
|
||||
# If project has calendar
|
||||
if calendar_id:
|
||||
working_days = proj.compute_working_calendar(cr, uid, calendar_id)
|
||||
vacation = tuple(proj.leaves_resource(cr, uid, calendar_id))
|
||||
|
||||
# Dynamic Creation of tasks
|
||||
if calendar_id: # If project has calendar
|
||||
working_days = wkcal.compute_working_calendar(cr, uid, calendar_id)
|
||||
vacation = tuple(wkcal.compute_leaves(cr, uid, calendar_id))
|
||||
# Dynamic Creation of tasks
|
||||
i = 0
|
||||
for each_task in task_obj:
|
||||
for each_task in task_objs:
|
||||
hours = str(each_task.planned_hours / each_task.occupation_rate)+ 'H'
|
||||
if each_task.priority in priority_dict.keys():
|
||||
priorty = priority_dict[each_task.priority]
|
||||
if each_task.user_id:
|
||||
for resource_object in resource_objs:
|
||||
if resource_object.__name__ == each_task.user_id.name:
|
||||
task = tasks_resource(i, hours, priorty, resource_object)
|
||||
for resource in resources:
|
||||
if resource.__name__ == each_task.user_id.name:
|
||||
task = create_tasks(i, hours, priorty, resource)
|
||||
else:
|
||||
task = tasks_resource(i, hours, priorty)
|
||||
task = create_tasks(i, hours, priorty)
|
||||
i += 1
|
||||
|
||||
# Writing back the dates
|
||||
project = BalancedProject(Project)
|
||||
loop_no = 0
|
||||
# Write back the computed dates
|
||||
for t in project:
|
||||
s_date = t.start.to_datetime()
|
||||
e_date = t.end.to_datetime()
|
||||
if loop_no == 0:
|
||||
project_pool.write(cr, uid, [project_id], {'date' : e_date})
|
||||
project_obj.write(cr, uid, [project_id], {'date' : e_date})
|
||||
else:
|
||||
user_id = user_pool.search(cr, uid, [('name','=',t.booked_resource[0].__name__)])
|
||||
task_pool.write(cr, uid, [task_obj[loop_no-1].id], {'date_start' :s_date.strftime('%Y-%m-%d %H:%M:%S'), 'date_deadline' :e_date.strftime('%Y-%m-%d %H:%M:%S'), 'user_id' :user_id[0]}, context={'scheduler' :True})
|
||||
user_id = user_obj.search(cr, uid, [('name', '=', t.booked_resource[0].__name__)])
|
||||
task_pool.write(cr, uid, [task_objs[loop_no-1].id], {'date_start': s_date.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'date_deadline': e_date.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'user_id': user_id[0]},
|
||||
context={'scheduler': True
|
||||
})
|
||||
loop_no +=1
|
||||
return {}
|
||||
|
||||
|
@ -137,14 +150,10 @@ class wizard_compute_tasks(wizard.interface):
|
|||
('compute', 'Compute')
|
||||
]},
|
||||
},
|
||||
|
||||
|
||||
'compute': {
|
||||
'actions': [_compute_date],
|
||||
'result': {'type':'form','arch':success_msg,'fields':{}, 'state':[('end', 'Ok')]},
|
||||
}
|
||||
}
|
||||
wizard_compute_tasks('wizard.compute.tasks')
|
||||
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -1,81 +0,0 @@
|
|||
import pooler
|
||||
import datetime
|
||||
|
||||
def timeformat_convert(cr, uid, time_string, context={}):
|
||||
# Function to convert input time string:: 8.5 to output time string 8:30
|
||||
|
||||
split_list = str(time_string).split('.')
|
||||
hour_part = split_list[0]
|
||||
mins_part = split_list[1]
|
||||
round_mins = int(round(float(mins_part) * 60,-2))
|
||||
converted_string = hour_part + ':' + str(round_mins)[0:2]
|
||||
return converted_string
|
||||
|
||||
def leaves_resource(cr, uid, calendar_id, resource_id=False, resource_calendar=False):
|
||||
# To get the leaves for the resource_ids working on phase
|
||||
|
||||
pool = pooler.get_pool(cr.dbname)
|
||||
resource_leaves_pool = pool.get('resource.calendar.leaves')
|
||||
leaves = []
|
||||
if resource_id:
|
||||
resource_leave_ids = resource_leaves_pool.search(cr, uid, ['|', ('calendar_id','=',calendar_id), ('calendar_id','=',resource_calendar), ('resource_id','=',resource_id)])
|
||||
else:
|
||||
resource_leave_ids = resource_leaves_pool.search(cr, uid, [('calendar_id','=',calendar_id), ('resource_id','=',False)])
|
||||
res_leaves = resource_leaves_pool.read(cr, uid, resource_leave_ids, ['date_from', 'date_to'])
|
||||
for leave in range(len(res_leaves)):
|
||||
dt_start = datetime.datetime.strptime(res_leaves[leave]['date_from'], '%Y-%m-%d %H:%M:%S')
|
||||
dt_end = datetime.datetime.strptime(res_leaves[leave]['date_to'], '%Y-%m-%d %H:%M:%S')
|
||||
no = dt_end - dt_start
|
||||
[leaves.append((dt_start + datetime.timedelta(days=x)).strftime('%Y-%m-%d')) for x in range(int(no.days + 1))]
|
||||
leaves.sort()
|
||||
return leaves
|
||||
|
||||
def compute_working_calendar(cr, uid, calendar_id):
|
||||
# To change the format of working calendar to bring it into 'faces' format
|
||||
|
||||
pool = pooler.get_pool(cr.dbname)
|
||||
resource_week_pool = pool.get('resource.calendar.week')
|
||||
time_range = "8:00-8:00"
|
||||
non_working = ""
|
||||
wk = {"0":"mon","1":"tue","2":"wed","3":"thu","4":"fri","5":"sat","6":"sun"}
|
||||
wk_days = {}
|
||||
wk_time = {}
|
||||
wktime_list = []
|
||||
wktime_cal = []
|
||||
week_ids = resource_week_pool.search(cr, uid, [('calendar_id','=',calendar_id)])
|
||||
week_obj = resource_week_pool.read(cr, uid, week_ids, ['dayofweek', 'hour_from', 'hour_to'])
|
||||
|
||||
# Converting time formats into appropriate format required
|
||||
# and creating a list like [('mon', '8:00-12:00'), ('mon', '13:00-18:00')]
|
||||
for week in week_obj:
|
||||
res_str = ""
|
||||
if wk.has_key(week['dayofweek']):
|
||||
day = wk[week['dayofweek']]
|
||||
wk_days[week['dayofweek']] = wk[week['dayofweek']]
|
||||
|
||||
hour_from_str = timeformat_convert(cr, uid, week['hour_from'])
|
||||
hour_to_str = timeformat_convert(cr, uid, week['hour_to'])
|
||||
res_str = hour_from_str + '-' + hour_to_str
|
||||
wktime_list.append((day, res_str))
|
||||
|
||||
# Converting it to format like [('mon', '8:00-12:00', '13:00-18:00')]
|
||||
for item in wktime_list:
|
||||
if wk_time.has_key(item[0]):
|
||||
wk_time[item[0]].append(item[1])
|
||||
else:
|
||||
wk_time[item[0]] = [item[0]]
|
||||
wk_time[item[0]].append(item[1])
|
||||
|
||||
for k,v in wk_time.items():
|
||||
wktime_cal.append(tuple(v))
|
||||
|
||||
# For non working days adding [('tue,wed,fri,sat,sun', '8:00-8:00')]
|
||||
for k,v in wk_days.items():
|
||||
if wk.has_key(k):
|
||||
wk.pop(k)
|
||||
for v in wk.itervalues():
|
||||
non_working += v + ','
|
||||
if non_working:
|
||||
wktime_cal.append((non_working[:-1], time_range))
|
||||
|
||||
return wktime_cal
|
|
@ -23,45 +23,53 @@ import wizard
|
|||
import pooler
|
||||
from tools.translate import _
|
||||
import datetime
|
||||
|
||||
from resource.faces import *
|
||||
from new import classobj
|
||||
import operator
|
||||
import time
|
||||
import project_resource as proj
|
||||
|
||||
import working_calendar as wkcal
|
||||
|
||||
success_msg = """<?xml version="1.0" ?>
|
||||
<form string="Compute Scheduling of Tasks">
|
||||
<label string="Task Scheduling completed successfully."/>
|
||||
</form>"""
|
||||
|
||||
def resource_list(cr, uid, phase):
|
||||
# To create resources which are the Project Members
|
||||
class wizard_schedule_task(wizard.interface):
|
||||
|
||||
def _create_resources(self, cr, uid, phase, context={}):
|
||||
"""
|
||||
Return a list of Resource Class objects for the resources allocated to the phase.
|
||||
"""
|
||||
resource_objs = []
|
||||
for resource in phase.resource_ids:
|
||||
res = resource.resource_id
|
||||
leaves = []
|
||||
resource_eff = res.time_efficiency
|
||||
resource_cal = res.calendar_id.id
|
||||
wktime_cal = proj.compute_working_calendar(cr, uid, resource_cal)
|
||||
leaves = proj.leaves_resource(cr, uid, phase.project_id.resource_calendar_id.id or False , res.id, resource_cal)
|
||||
resource_objs.append(classobj(str(res.user_id.name), (Resource,), {'__doc__' : res.user_id.name, '__name__' : res.user_id.name, 'vacation' : tuple(leaves), 'efficiency' : resource_eff}))
|
||||
if resource_cal:
|
||||
leaves = wkcal.compute_leaves(cr, uid, phase.project_id.resource_calendar_id.id or False, res.id, resource_cal)
|
||||
resource_objs.append(classobj(str(res.user_id.name), (Resource,),
|
||||
{'__doc__': res.user_id.name,
|
||||
'__name__': res.user_id.name,
|
||||
'vacation': tuple(leaves),
|
||||
'efficiency': resource_eff
|
||||
}))
|
||||
return resource_objs
|
||||
|
||||
class wizard_schedule_task(wizard.interface):
|
||||
|
||||
def _compute_date(self, cr, uid, data, context):
|
||||
"""
|
||||
Schedule the tasks according to resource available and priority.
|
||||
"""
|
||||
pool = pooler.get_pool(cr.dbname)
|
||||
phase_pool = pool.get('project.phase')
|
||||
task_pool = pool.get('project.task')
|
||||
user_pool = pool.get('res.users')
|
||||
phase = phase_pool.browse(cr, uid, data['id'])
|
||||
task_ids = map(lambda x : x.id, (filter(lambda x : x.state in ['open','draft','pending'] , phase.task_ids)))
|
||||
|
||||
phase_obj = pool.get('project.phase')
|
||||
task_obj = pool.get('project.task')
|
||||
user_obj = pool.get('res.users')
|
||||
phase = phase_obj.browse(cr, uid, data['id'], context=context)
|
||||
task_ids = map(lambda x : x.id, (filter(lambda x : x.state in ['open', 'draft', 'pending'] , phase.task_ids)))
|
||||
if task_ids:
|
||||
task_ids.sort()
|
||||
tasks = task_pool.browse(cr, uid, task_ids)
|
||||
wktime_cal = []
|
||||
tasks = task_obj.browse(cr, uid, task_ids, context=context)
|
||||
start_date = str(phase.date_start)[:-9]
|
||||
if not phase.date_start:
|
||||
if not phase.project_id.date_start:
|
||||
|
@ -70,11 +78,10 @@ class wizard_schedule_task(wizard.interface):
|
|||
start_date = phase.project_id.date_start
|
||||
date_start = datetime.datetime.strftime(datetime.datetime.strptime(start_date, "%Y-%m-%d"), "%Y-%m-%d %H:%M")
|
||||
calendar_id = phase.project_id.resource_calendar_id.id
|
||||
resource_objs = resource_list(cr, uid, phase)
|
||||
priority_dict = {'0' :1000,'1' :800,'2' :500,'3' :300,'4' :100}
|
||||
|
||||
# To create dynamic no of tasks with the resource specified
|
||||
def tasks_resource(j, eff, priorty=500, obj=False):
|
||||
resources = self._create_resources(cr, uid, phase)
|
||||
priority_dict = {'0': 1000, '1': 800, '2': 500, '3': 300, '4': 100}
|
||||
# Create dynamic no of tasks with the resource specified
|
||||
def create_tasks(j, eff, priorty=500, obj=False):
|
||||
def task():
|
||||
"""
|
||||
task is a dynamic method!
|
||||
|
@ -87,44 +94,47 @@ class wizard_schedule_task(wizard.interface):
|
|||
task.__name__ = "task%d" %j
|
||||
return task
|
||||
|
||||
# Creating the project with all the tasks and resources
|
||||
# Create a 'Faces' project with all the tasks and resources
|
||||
def Project():
|
||||
title = "Test Project"
|
||||
title = "Project"
|
||||
start = date_start
|
||||
resource = reduce(operator.or_, resource_objs)
|
||||
try:
|
||||
resource = reduce(operator.or_, resources)
|
||||
except:
|
||||
raise wizard.except_wizard(_('ResourceError'), _('Phase must have resources assigned !'))
|
||||
minimum_time_unit = 1
|
||||
|
||||
# If project has calendar
|
||||
if calendar_id:
|
||||
working_days = proj.compute_working_calendar(cr, uid, calendar_id)
|
||||
vacation = tuple(proj.leaves_resource(cr, uid, calendar_id))
|
||||
|
||||
# Dynamic Creation of tasks
|
||||
if calendar_id: # If project has working calendar
|
||||
working_days = wkcal.compute_working_calendar(cr, uid, calendar_id)
|
||||
vacation = tuple(wkcal.compute_leaves(cr, uid, calendar_id))
|
||||
# Dynamic creation of tasks
|
||||
i = 0
|
||||
for each_task in tasks:
|
||||
hours = str(each_task.planned_hours / each_task.occupation_rate)+ 'H'
|
||||
if each_task.priority in priority_dict.keys():
|
||||
priorty = priority_dict[each_task.priority]
|
||||
if each_task.user_id:
|
||||
for resource_object in resource_objs:
|
||||
if resource_object.__name__ == each_task.user_id.name:
|
||||
task = tasks_resource(i, hours, priorty, resource_object)
|
||||
for resource in resources:
|
||||
if resource.__name__ == each_task.user_id.name:
|
||||
task = create_tasks(i, hours, priorty, resource)
|
||||
else:
|
||||
task = tasks_resource(i, hours, priorty)
|
||||
task = create_tasks(i, hours, priorty)
|
||||
i += 1
|
||||
|
||||
# Writing back the dates
|
||||
project = BalancedProject(Project)
|
||||
loop_no = 0
|
||||
# Write back the computed dates
|
||||
for t in project:
|
||||
s_date = t.start.to_datetime()
|
||||
e_date = t.end.to_datetime()
|
||||
if loop_no > 0:
|
||||
user_id = user_pool.search(cr, uid, [('name','=',t.booked_resource[0].__name__)])
|
||||
task_pool.write(cr, uid, [tasks[loop_no-1].id], {'date_start' :s_date.strftime('%Y-%m-%d %H:%M:%S'), 'date_deadline' :e_date.strftime('%Y-%m-%d %H:%M:%S'), 'user_id' :user_id[0]}, context={'scheduler' :True})
|
||||
user_id = user_obj.search(cr, uid, [('name', '=', t.booked_resource[0].__name__)])
|
||||
task_obj.write(cr, uid, [tasks[loop_no-1].id], {'date_start': s_date.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'date_deadline': e_date.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'user_id': user_id[0]},
|
||||
context={'scheduler': True
|
||||
})
|
||||
loop_no +=1
|
||||
return {}
|
||||
|
||||
states = {
|
||||
'init': {
|
||||
'actions': [_compute_date],
|
||||
|
@ -132,6 +142,4 @@ class wizard_schedule_task(wizard.interface):
|
|||
}
|
||||
}
|
||||
wizard_schedule_task('phase.schedule.tasks')
|
||||
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import pooler
|
||||
import datetime
|
||||
|
||||
def convert_timeformat(cr, uid, time_string, context={}):
|
||||
""" Convert input time string: 8.5 to output time string 8:30."""
|
||||
|
||||
split_list = str(time_string).split('.')
|
||||
hour_part = split_list[0]
|
||||
mins_part = split_list[1]
|
||||
round_mins = int(round(float(mins_part) * 60,-2))
|
||||
converted_string = hour_part + ':' + str(round_mins)[0:2]
|
||||
return converted_string
|
||||
|
||||
def compute_leaves(cr, uid, calendar_id, resource_id=False, resource_calendar=False):
|
||||
|
||||
"""Compute the leaves from the working calendar of the resource.
|
||||
|
||||
Arguements: calendar_id -- working calendar of the project
|
||||
resource_id -- resource working on phase/task
|
||||
resource_calendar -- working calendar of the resource
|
||||
|
||||
"""
|
||||
|
||||
pool = pooler.get_pool(cr.dbname)
|
||||
resource_calendar_leaves_obj = pool.get('resource.calendar.leaves')
|
||||
leave_list = []
|
||||
if resource_id:
|
||||
leave_ids = resource_calendar_leaves_obj.search(cr, uid, ['|', ('calendar_id', '=', calendar_id),
|
||||
('calendar_id', '=', resource_calendar),
|
||||
('resource_id', '=', resource_id)
|
||||
])
|
||||
else:
|
||||
leave_ids = resource_calendar_leaves_obj.search(cr, uid, [('calendar_id', '=', calendar_id),
|
||||
('resource_id', '=', False)
|
||||
])
|
||||
leaves = resource_calendar_leaves_obj.read(cr, uid, leave_ids, ['date_from', 'date_to'])
|
||||
for i in range(len(leaves)):
|
||||
dt_start = datetime.datetime.strptime(leaves[i]['date_from'], '%Y-%m-%d %H:%M:%S')
|
||||
dt_end = datetime.datetime.strptime(leaves[i]['date_to'], '%Y-%m-%d %H:%M:%S')
|
||||
no = dt_end - dt_start
|
||||
[leave_list.append((dt_start + datetime.timedelta(days=x)).strftime('%Y-%m-%d')) for x in range(int(no.days + 1))]
|
||||
leave_list.sort()
|
||||
return leave_list
|
||||
|
||||
def compute_working_calendar(cr, uid, calendar_id):
|
||||
|
||||
"""Change the format of working calendar from 'Openerp' format to bring it into 'Faces' format.
|
||||
|
||||
Arguement: calendar_id -- working calendar of the project
|
||||
|
||||
"""
|
||||
|
||||
pool = pooler.get_pool(cr.dbname)
|
||||
resource_week_obj = pool.get('resource.calendar.week')
|
||||
time_range = "8:00-8:00"
|
||||
non_working = ""
|
||||
week_days = {"0": "mon", "1": "tue", "2": "wed","3": "thu", "4": "fri", "5": "sat", "6": "sun"}
|
||||
wk_days = {}
|
||||
wk_time = {}
|
||||
wktime_list = []
|
||||
wktime_cal = []
|
||||
week_ids = resource_week_obj.search(cr, uid, [('calendar_id', '=', calendar_id)])
|
||||
weeks = resource_week_obj.read(cr, uid, week_ids, ['dayofweek', 'hour_from', 'hour_to'])
|
||||
# Convert time formats into appropriate format required
|
||||
# and create a list like [('mon', '8:00-12:00'), ('mon', '13:00-18:00')]
|
||||
for week in weeks:
|
||||
res_str = ""
|
||||
if week_days.has_key(week['dayofweek']):
|
||||
day = week_days[week['dayofweek']]
|
||||
wk_days[week['dayofweek']] = week_days[week['dayofweek']]
|
||||
hour_from_str = convert_timeformat(cr, uid, week['hour_from'])
|
||||
hour_to_str = convert_timeformat(cr, uid, week['hour_to'])
|
||||
res_str = hour_from_str + '-' + hour_to_str
|
||||
wktime_list.append((day, res_str))
|
||||
# Convert into format like [('mon', '8:00-12:00', '13:00-18:00')]
|
||||
for item in wktime_list:
|
||||
if wk_time.has_key(item[0]):
|
||||
wk_time[item[0]].append(item[1])
|
||||
else:
|
||||
wk_time[item[0]] = [item[0]]
|
||||
wk_time[item[0]].append(item[1])
|
||||
for k,v in wk_time.items():
|
||||
wktime_cal.append(tuple(v))
|
||||
# Add for the non-working days like: [('sat, sun', '8:00-8:00')]
|
||||
for k,v in wk_days.items():
|
||||
if week_days.has_key(k):
|
||||
week_days.pop(k)
|
||||
for v in week_days.itervalues():
|
||||
non_working += v + ','
|
||||
if non_working:
|
||||
wktime_cal.append((non_working[:-1], time_range))
|
||||
return wktime_cal
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
Loading…
Reference in New Issue