From 3b29008aef50504860214ce6a9973f1a6be33abe Mon Sep 17 00:00:00 2001 From: "Rvo (Open ERP)" Date: Thu, 18 Feb 2010 15:37:31 +0530 Subject: [PATCH 01/12] [ADD]:added constraints on phases when date_start or date_end field modified,changed interval_min_get method of resource bzr revid: rvo@tinyerp.co.in-20100218100731-cng5emi7udnf0fdf --- addons/project_long_term/project.py | 97 ++++++++++++++++++- .../wizard/compute_phases_date.py | 4 +- addons/resource/__init__.py | 1 + addons/resource/resource.py | 48 +++------ 4 files changed, 109 insertions(+), 41 deletions(-) diff --git a/addons/project_long_term/project.py b/addons/project_long_term/project.py index ffe3ac38842..d8aae8b8506 100644 --- a/addons/project_long_term/project.py +++ b/addons/project_long_term/project.py @@ -20,18 +20,18 @@ ############################################################################## from lxml import etree -from mx import DateTime -from mx.DateTime import now +import mx.DateTime import time from tools.translate import _ - from osv import fields, osv -from tools.translate import _ + class project_phase(osv.osv): _name = "project.phase" _description = "Project Phase" + + def _check_recursion(self,cr,uid,ids): obj_self = self.browse(cr, uid, ids[0]) prev_ids = obj_self.previous_phase_ids @@ -72,6 +72,7 @@ class project_phase(osv.osv): next_ids = next_phase_ids return True + _columns = { 'name': fields.char("Phase Name", size=64, required=True), 'date_start': fields.datetime('Starting Date'), @@ -95,6 +96,94 @@ class project_phase(osv.osv): _constraints = [ (_check_recursion,'Error ! Loops In Phases Not Allowed',['next_phase_ids','previous_phase_ids']) ] + + def timeformat_convert(self,cr, uid, time_string, context={}): + # 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 compute_hours(self,cr,uid,calendar_id,context = None): + # To compute average hours of the working calendar + + resource_week_pool = self.pool.get('resource.calendar.week') + 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']) + hours = [] + hr = 0 + wk_days = [] + for week in week_obj: + if week['dayofweek'] not in wk_days: + wk_days.append(week['dayofweek']) + hour_from_str = self.timeformat_convert(cr,uid,week['hour_from']) + hour_to_str = self.timeformat_convert(cr,uid,week['hour_to']) + hours.append(week['hour_from']) + hours.append(week['hour_to']) + + for hour in range(len(hours)): + if hour%2 ==0: + hr += float(hours[hour+1]) - float(hours[hour]) + return hr/len(wk_days) + + 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') + calendar_id = phase.project_id.resource_calendar_id.id + avg_hours = self.compute_hours(cr,uid,calendar_id) + hours = phase.duration * avg_hours + work_time = resource_cal_pool.interval_min_get(cr, uid, calendar_id or False, date_end, hours or 0.0) + 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')}) + + def constraint_date_end(self,cr,uid,phase,date_start,context=None): + # Recursive call for all next phases if change in date_ebd > older time + + resource_cal_pool = self.pool.get('resource.calendar') + calendar_id = phase.project_id.resource_calendar_id.id + avg_hours = self.compute_hours(cr,uid,calendar_id) + hours = phase.duration * avg_hours + work_time = resource_cal_pool.interval_get(cr, uid, calendar_id or False, date_start, hours or 0.0) + 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}) + + def write(self, cr, uid, ids, vals,context=None): + phase = self.browse(cr,uid,ids[0]) + resource_cal_pool = self.pool.get('resource.calendar') + calendar_id = phase.project_id.resource_calendar_id.id + avg_hours = self.compute_hours(cr,uid,calendar_id) + + if not context: + context = {} +# write method changes 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') + hrs = phase.duration * avg_hours + work_times = resource_cal_pool.interval_get(cr, uid, calendar_id or False, dt_start, hrs or 0.0) + 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) + + 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') + hrs = phase.duration * avg_hours + work_times = resource_cal_pool.interval_min_get(cr, uid, calendar_id or False, dt_end, hrs or 0.0) + 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) + return super(project_phase, self).write(cr, uid, ids, vals, context=context) + project_phase() class project_resource_allocation(osv.osv): diff --git a/addons/project_long_term/wizard/compute_phases_date.py b/addons/project_long_term/wizard/compute_phases_date.py index 2d4cf9fd70e..7778de6bfb0 100644 --- a/addons/project_long_term/wizard/compute_phases_date.py +++ b/addons/project_long_term/wizard/compute_phases_date.py @@ -175,7 +175,6 @@ class wizard_compute_phases(wizard.interface): def phase_schedule(cr,uid,phase,start_date,avg_hour = 0.0): if phase: - # To get resources and the duration for the phase resources_list = resource_list(cr,uid,phase) if not avg_hour: @@ -211,9 +210,8 @@ class wizard_compute_phases(wizard.interface): end_date = e_date # Writing the dates back - phase_pool.write(cr,uid,[phase.id],{'date_start':start_date,'date_end':end_date}) + 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')}) date_start = end_date - # Recursive calling the next phases till all the phases are scheduled for phase in phase.next_phase_ids: phase_schedule(cr,uid,phase,date_start) diff --git a/addons/resource/__init__.py b/addons/resource/__init__.py index 2aa86a90c65..77485b4f121 100755 --- a/addons/resource/__init__.py +++ b/addons/resource/__init__.py @@ -20,6 +20,7 @@ ############################################################################## import resource +import faces # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/resource/resource.py b/addons/resource/resource.py index 7ac36e5e7bd..aac885c2898 100755 --- a/addons/resource/resource.py +++ b/addons/resource/resource.py @@ -36,50 +36,30 @@ class resource_calendar(osv.osv): _defaults = { 'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'resource.calendar', c) } - def interval_min_get(self, cr, uid, id, dt_from, hours , resource=0): - dt_leave = [] + def interval_min_get(self, cr, uid, id, dt_from, hours): if not id: return [(dt_from-mx.DateTime.RelativeDateTime(hours=int(hours)*3), dt_from)] - if resource: - resource_leave_ids = self.pool.get('resource.calendar.leaves').search(cr,uid,[('resource_id','=',resource)]) - if resource_leave_ids: - res_leaves = self.pool.get('resource.calendar.leaves').read(cr,uid,resource_leave_ids,['date_from','date_to']) - for i in range(len(res_leaves)): - dtf = mx.DateTime.strptime(res_leaves[i]['date_from'],'%Y-%m-%d %H:%M:%S') - dtt = mx.DateTime.strptime(res_leaves[i]['date_to'],'%Y-%m-%d %H:%M:%S') - leave_days = ((dtt - dtf).days) + 1 - for x in range(int(leave_days)): - dt_leave.append((dtf + mx.DateTime.RelativeDateTime(days=x)).strftime('%Y-%m-%d')) - dt_leave.sort() 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 resource_calendar_week where dayofweek='%s' and calendar_id=%s order by hour_from", (dt_from.day_of_week,id)) + cr.execute("select hour_from,hour_to from resource_calendar_week where dayofweek='%s' and calendar_id=%s order by hour_from desc", (dt_from.day_of_week,id)) for (hour_from,hour_to) in cr.fetchall(): - if (hour_to>current_hour) and (todo>0): - m = max(hour_from, current_hour) - if (hour_to-m)>todo: - hour_to = m+todo - d1 = mx.DateTime.DateTime(dt_from.year,dt_from.month,dt_from.day,int(math.floor(m)),int((m%1) * 60)) - d2 = mx.DateTime.DateTime(dt_from.year,dt_from.month,dt_from.day,int(math.floor(hour_to)),int((hour_to%1) * 60)) - dt1 = d1.strftime('%Y-%m-%d') - dt2 = d2.strftime('%Y-%m-%d') - for i in range(len(dt_leave)): - if dt1 == dt_leave[i]: - dt_from += mx.DateTime.RelativeDateTime(days=1) - d1 = mx.DateTime.DateTime(dt_from.year,dt_from.month,dt_from.day,int(math.floor(m)),int((m%1) * 60)) - d2 = mx.DateTime.DateTime(dt_from.year,dt_from.month,dt_from.day,int(math.floor(hour_to)),int((hour_to%1) * 60)) - dt1 = d1.strftime('%Y-%m-%d') - dt2 = d2.strftime('%Y-%m-%d') - result.append((d1, d2)) - current_hour = hour_to - todo -= (hour_to - m) - dt_from += mx.DateTime.RelativeDateTime(days=1) - current_hour = 0 + if (hour_from0): + m = min(hour_to, current_hour) + if (m-hour_from)>todo: + hour_from = m-todo + d1 = mx.DateTime.DateTime(dt_from.year,dt_from.month,dt_from.day,int(math.floor(hour_from)),int((hour_from%1) * 60)) + d2 = mx.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 -= mx.DateTime.RelativeDateTime(days=1) + current_hour = 24 maxrecur -= 1 + result.reverse() return result def interval_get(self, cr, uid, id, dt_from, hours, resource=0, byday=True): From 7300add4a7d90f8d4fae3a897dd8add4902f6751 Mon Sep 17 00:00:00 2001 From: "Rvo (Open ERP)" Date: Thu, 18 Feb 2010 18:43:18 +0530 Subject: [PATCH 02/12] [ADD]:added constraints on parent_ids and child_ids of tasks when date_start and date_end fields modified,improved on_change method on tasks bzr revid: rvo@tinyerp.co.in-20100218131318-mu80ki61iuazu06c --- addons/project/project.py | 65 +++++++++++++++++++-- addons/project/wizard/compute_tasks_date.py | 2 +- 2 files changed, 62 insertions(+), 5 deletions(-) diff --git a/addons/project/project.py b/addons/project/project.py index a57f782ef10..b2490a4eab7 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -259,13 +259,16 @@ class task(osv.osv): result = {} for res in self.browse(cr, uid, ids): if date_start and planned: + dt_start = mx.DateTime.strptime(date_start,'%Y-%m-%d %H:%M:%S') resource_id = self.pool.get('resource.resource').search(cr,uid,[('user_id','=',res.user_id.id)]) if resource_id: resource_obj = self.pool.get('resource.resource').browse(cr,uid,resource_id)[0] - d = mx.DateTime.strptime(date_start,'%Y-%m-%d %H:%M:%S') - hrs = (planned)/(occupation_rate) - work_times = self.pool.get('resource.calendar').interval_get(cr, uid, resource_obj.calendar_id.id or False, d, hrs or 0.0, resource_obj.id) - result['date_end'] = work_times[-1][1].strftime('%Y-%m-%d %H:%M:%S') + hrs = planned/(occupation_rate * resource_obj.time_efficiency) + work_times = self.pool.get('resource.calendar').interval_get(cr, uid, resource_obj.calendar_id.id or False, dt_start, hrs or 0.0, resource_obj.id) + else: + hrs = planned / occupation_rate + work_times = self.pool.get('resource.calendar').interval_get(cr, uid, res.project_id.resource_calendar_id .id or False, dt_start, hrs or 0.0) + result['date_end'] = work_times[-1][1].strftime('%Y-%m-%d %H:%M:%S') result['remaining_hours'] = planned-effective return {'value':result} @@ -472,6 +475,60 @@ class task(osv.osv): self.write(cr, uid, typ.id, {'type': index and types[index-1] or False}) return True + 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') + calendar_id = task.project_id.resource_calendar_id.id + hours = task.remaining_hours / task.occupation_rate + work_time = resource_cal_pool.interval_min_get(cr, uid, calendar_id or False, date_end, hours or 0.0) + dt_start = work_time[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 + + resource_cal_pool = self.pool.get('resource.calendar') + calendar_id = task.project_id.resource_calendar_id.id + hours = task.remaining_hours / task.occupation_rate + work_time = resource_cal_pool.interval_get(cr, uid, calendar_id or False, date_start, hours or 0.0) + dt_end = work_time[-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): + tasks = self.browse(cr,uid,ids[0]) + resource_cal_pool = self.pool.get('resource.calendar') + calendar_id = tasks.project_id.resource_calendar_id.id + + if not context: + context = {} +# write method changes 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') + hrs = tasks.remaining_hours / tasks.occupation_rate + work_times = resource_cal_pool.interval_get(cr, uid, calendar_id or False, dt_start, hrs or 0.0) + 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) + + 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) + 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) + return super(task, self).write(cr, uid, ids, vals, context=context) + + task() class project_work(osv.osv): diff --git a/addons/project/wizard/compute_tasks_date.py b/addons/project/wizard/compute_tasks_date.py index 5dc7c9ae007..c44fb902e87 100644 --- a/addons/project/wizard/compute_tasks_date.py +++ b/addons/project/wizard/compute_tasks_date.py @@ -216,7 +216,7 @@ class wizard_compute_tasks(wizard.interface): project_pool.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,'date_deadline':e_date,'user_id':user_id[0]}) + 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]}) loop_no +=1 return {} From 53b12ce0303b35162b0a127e906b73bde6b90989 Mon Sep 17 00:00:00 2001 From: "Rvo (Open ERP)" Date: Thu, 18 Feb 2010 18:47:03 +0530 Subject: [PATCH 03/12] removed print statement bzr revid: rvo@tinyerp.co.in-20100218131703-kofy55mbudtwgjtn --- addons/project_long_term/wizard/compute_phases_date.py | 1 - 1 file changed, 1 deletion(-) diff --git a/addons/project_long_term/wizard/compute_phases_date.py b/addons/project_long_term/wizard/compute_phases_date.py index 7778de6bfb0..8cd3b59c2db 100644 --- a/addons/project_long_term/wizard/compute_phases_date.py +++ b/addons/project_long_term/wizard/compute_phases_date.py @@ -195,7 +195,6 @@ class wizard_compute_phases(wizard.interface): resource = reduce(operator.or_,resources_list) project = BalancedProject(Project) - print 'Project Phase Start & End:::',project.phase.name,project.phase.booked_resource,project.phase.start.to_datetime(),project.phase.end.to_datetime() s_date = project.phase.start.to_datetime() e_date = project.phase.end.to_datetime() From 09c358d92dba91cc7abaafdfc152cc4cc6c44a04 Mon Sep 17 00:00:00 2001 From: "Rvo (Open ERP)" Date: Fri, 19 Feb 2010 15:56:24 +0530 Subject: [PATCH 04/12] [IMP]:changes in interval_get and minimum_interval_get,added required fields in resource.resource bzr revid: rvo@tinyerp.co.in-20100219102624-22psr47e8xk9beo5 --- addons/hr/hr_demo.xml | 1 + addons/resource/resource.py | 90 +++++++++++++++++++++++-------------- 2 files changed, 57 insertions(+), 34 deletions(-) diff --git a/addons/hr/hr_demo.xml b/addons/hr/hr_demo.xml index fc01109d86c..e4ac2a97872 100644 --- a/addons/hr/hr_demo.xml +++ b/addons/hr/hr_demo.xml @@ -10,6 +10,7 @@ Fabien Pinckaers + diff --git a/addons/resource/resource.py b/addons/resource/resource.py index aac885c2898..df58b322e46 100755 --- a/addons/resource/resource.py +++ b/addons/resource/resource.py @@ -36,9 +36,23 @@ class resource_calendar(osv.osv): _defaults = { 'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'resource.calendar', c) } - def interval_min_get(self, cr, uid, id, dt_from, hours): + + def interval_min_get(self, cr, uid, id, dt_from, hours,resource=None): + dt_leave = [] if not id: return [(dt_from-mx.DateTime.RelativeDateTime(hours=int(hours)*3), dt_from)] + if resource: + resource_leave_ids = self.pool.get('resource.calendar.leaves').search(cr,uid,['|',('resource_id','=',resource.id),('resource_id','=',False)]) + else: + resource_leave_ids = self.pool.get('resource.calendar.leaves').search(cr,uid,[('resource_id','=',False)]) + res_leaves = self.pool.get('resource.calendar.leaves').read(cr,uid,resource_leave_ids,['date_from','date_to']) + for leave in res_leaves: + dtf = mx.DateTime.strptime(leave['date_from'],'%Y-%m-%d %H:%M:%S') + dtt = mx.DateTime.strptime(leave['date_to'],'%Y-%m-%d %H:%M:%S') + no = dtt - dtf + [dt_leave.append((dtf + mx.DateTime.RelativeDateTime(days=x)).strftime('%Y-%m-%d')) for x in range(int(no.days + 1))] + dt_leave.sort() + dt_leave.reverse() todo = hours cycle = 0 result = [] @@ -47,37 +61,45 @@ class resource_calendar(osv.osv): while (todo>0) and maxrecur: cr.execute("select hour_from,hour_to from resource_calendar_week where dayofweek='%s' and calendar_id=%s order by hour_from desc", (dt_from.day_of_week,id)) for (hour_from,hour_to) in cr.fetchall(): + leave_flag = False if (hour_from0): m = min(hour_to, current_hour) if (m-hour_from)>todo: hour_from = m-todo - d1 = mx.DateTime.DateTime(dt_from.year,dt_from.month,dt_from.day,int(math.floor(hour_from)),int((hour_from%1) * 60)) - d2 = mx.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_check = dt_from.strftime('%Y-%m-%d') + for leave in dt_leave: + if dt_check == leave: + dt_check = mx.DateTime.strptime(dt_check,"%Y-%m-%d") - mx.DateTime.RelativeDateTime(days=1) + leave_flag = True + if leave_flag: + break + else: + d1 = mx.DateTime.DateTime(dt_from.year,dt_from.month,dt_from.day,int(math.floor(hour_from)),int((hour_from%1) * 60)) + d2 = mx.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 -= mx.DateTime.RelativeDateTime(days=1) current_hour = 24 maxrecur -= 1 result.reverse() return result - def interval_get(self, cr, uid, id, dt_from, hours, resource=0, byday=True): + def interval_get(self, cr, uid, id, dt_from, hours, resource=None, byday=True): dt_leave = [] if not id: return [(dt_from,dt_from+mx.DateTime.RelativeDateTime(hours=int(hours)*3))] if resource: - resource_leave_ids = self.pool.get('resource.calendar.leaves').search(cr,uid,[('resource_id','=',resource)]) - if resource_leave_ids: - res_leaves = self.pool.get('resource.calendar.leaves').read(cr,uid,resource_leave_ids,['date_from','date_to']) - for i in range(len(res_leaves)): - dtf = mx.DateTime.strptime(res_leaves[i]['date_from'],'%Y-%m-%d %H:%M:%S') - dtt = mx.DateTime.strptime(res_leaves[i]['date_to'],'%Y-%m-%d %H:%M:%S') - no = dtt - dtf - leave_days = no.days + 1 - for x in range(int(leave_days)): - dt_leave.append((dtf + mx.DateTime.RelativeDateTime(days=x)).strftime('%Y-%m-%d')) - dt_leave.sort() + resource_leave_ids = self.pool.get('resource.calendar.leaves').search(cr,uid,['|',('resource_id','=',resource.id),('resource_id','=',False) ]) + else: + resource_leave_ids = self.pool.get('resource.calendar.leaves').search(cr,uid,[('resource_id','=',False)]) + res_leaves = self.pool.get('resource.calendar.leaves').read(cr,uid,resource_leave_ids,['date_from','date_to']) + for leave in res_leaves: + dtf = mx.DateTime.strptime(leave['date_from'],'%Y-%m-%d %H:%M:%S') + dtt = mx.DateTime.strptime(leave['date_to'],'%Y-%m-%d %H:%M:%S') + no = dtt - dtf + [dt_leave.append((dtf + mx.DateTime.RelativeDateTime(days=x)).strftime('%Y-%m-%d')) for x in range(int(no.days + 1))] + dt_leave.sort() todo = hours cycle = 0 result = [] @@ -86,24 +108,24 @@ class resource_calendar(osv.osv): while (todo>0) and maxrecur: cr.execute("select hour_from,hour_to from resource_calendar_week where dayofweek='%s' and calendar_id=%s order by hour_from", (dt_from.day_of_week,id)) for (hour_from,hour_to) in cr.fetchall(): + leave_flag = False if (hour_to>current_hour) and (todo>0): m = max(hour_from, current_hour) if (hour_to-m)>todo: hour_to = m+todo - d1 = mx.DateTime.DateTime(dt_from.year,dt_from.month,dt_from.day,int(math.floor(m)),int((m%1) * 60)) - d2 = mx.DateTime.DateTime(dt_from.year,dt_from.month,dt_from.day,int(math.floor(hour_to)),int((hour_to%1) * 60)) - dt1 = d1.strftime('%Y-%m-%d') - dt2 = d2.strftime('%Y-%m-%d') - for i in range(len(dt_leave)): - if dt1 == dt_leave[i]: - dt_from += mx.DateTime.RelativeDateTime(days=1) - d1 = mx.DateTime.DateTime(dt_from.year,dt_from.month,dt_from.day,int(math.floor(m)),int((m%1) * 60)) - d2 = mx.DateTime.DateTime(dt_from.year,dt_from.month,dt_from.day,int(math.floor(hour_to)),int((hour_to%1) * 60)) - dt1 = d1.strftime('%Y-%m-%d') - dt2 = d2.strftime('%Y-%m-%d') - result.append((d1, d2)) - current_hour = hour_to - todo -= (hour_to - m) + dt_check = dt_from.strftime('%Y-%m-%d') + for leave in dt_leave: + if dt_check == leave: + dt_check = mx.DateTime.strptime(dt_check,"%Y-%m-%d") + mx.DateTime.RelativeDateTime(days=1) + leave_flag = True + if leave_flag: + break + else: + d1 = mx.DateTime.DateTime(dt_from.year,dt_from.month,dt_from.day,int(math.floor(m)),int((m%1) * 60)) + d2 = mx.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 += mx.DateTime.RelativeDateTime(days=1) current_hour = 0 maxrecur -= 1 @@ -135,8 +157,8 @@ class resource_resource(osv.osv): 'company_id' : fields.many2one('res.company', 'Company', required=True), 'resource_type': fields.selection([('user','Human'),('material','Material')], 'Resource Type', required=True), 'user_id' : fields.many2one('res.users', 'User',help='Related user name for the resource to manage its access.'), - 'time_efficiency' : fields.float('Efficiency factor', size=8, help="This field depict the efficiency of the resource to complete tasks. e.g resource put alone on a phase of 5 days with 5 tasks assigned to him, will show a load of 100% for this phase by default, but if we put a efficency of 200%, then his load will only be 50%."), - 'calendar_id' : fields.many2one("resource.calendar", "Working time", help="Define the schedule of resource"), + 'time_efficiency' : fields.float('Efficiency factor', size=8, required=True,help="This field depict the efficiency of the resource to complete tasks. e.g resource put alone on a phase of 5 days with 5 tasks assigned to him, will show a load of 100% for this phase by default, but if we put a efficency of 200%, then his load will only be 50%."), + 'calendar_id' : fields.many2one("resource.calendar", "Working time",required=True, help="Define the schedule of resource"), } _defaults = { 'resource_type' : lambda *a: 'user', From eabe16de50d796e35a8c6cfe9b17cfba163cc6b6 Mon Sep 17 00:00:00 2001 From: "Rvo (Open ERP)" Date: Fri, 19 Feb 2010 15:58:28 +0530 Subject: [PATCH 05/12] minor changes bzr revid: rvo@tinyerp.co.in-20100219102828-hrehhv0aacv8conw --- addons/project/project.py | 2 +- addons/project/wizard/compute_tasks_date.py | 21 ++++++------ addons/project_long_term/project.py | 3 +- .../wizard/compute_phases_date.py | 32 ++++++++----------- 4 files changed, 25 insertions(+), 33 deletions(-) diff --git a/addons/project/project.py b/addons/project/project.py index 841a688272c..85af339160a 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -265,7 +265,7 @@ class task(osv.osv): if resource_id: resource_obj = self.pool.get('resource.resource').browse(cr,uid,resource_id)[0] hrs = planned/(occupation_rate * resource_obj.time_efficiency) - work_times = self.pool.get('resource.calendar').interval_get(cr, uid, resource_obj.calendar_id.id or False, dt_start, hrs or 0.0, resource_obj.id) + work_times = self.pool.get('resource.calendar').interval_get(cr, uid, resource_obj.calendar_id.id or False, dt_start, hrs or 0.0, resource_obj) else: hrs = planned / occupation_rate work_times = self.pool.get('resource.calendar').interval_get(cr, uid, res.project_id.resource_calendar_id .id or False, dt_start, hrs or 0.0) diff --git a/addons/project/wizard/compute_tasks_date.py b/addons/project/wizard/compute_tasks_date.py index c44fb902e87..c52f0b5f079 100644 --- a/addons/project/wizard/compute_tasks_date.py +++ b/addons/project/wizard/compute_tasks_date.py @@ -61,15 +61,13 @@ def leaves_resource(cr,uid,id): resource_leaves_pool = pool.get('resource.calendar.leaves') resource_leave_ids = resource_leaves_pool.search(cr,uid,[('resource_id','=',id)]) leaves = [] - if resource_leave_ids: - 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 - leave_days = no.days + 1 - [leaves.append((dt_start + datetime.timedelta(days=x)).strftime('%Y-%m-%d')) for x in range(int(leave_days))] - leaves.sort() + 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 class wizard_compute_tasks(wizard.interface): @@ -148,9 +146,8 @@ class wizard_compute_tasks(wizard.interface): 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 - leave_days = no.days + 1 - [leaves.append((dt_start + datetime.timedelta(days=x)).strftime('%Y-%m-%d')) for x in range(int(leave_days))] - leaves.sort() + [leaves.append((dt_start + datetime.timedelta(days=x)).strftime('%Y-%m-%d')) for x in range(int(no.days + 1))] + leaves.sort() # To create resources which are the Project Members resource = project.members diff --git a/addons/project_long_term/project.py b/addons/project_long_term/project.py index d8aae8b8506..e50b41713b1 100644 --- a/addons/project_long_term/project.py +++ b/addons/project_long_term/project.py @@ -140,7 +140,7 @@ class project_phase(osv.osv): 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_ebd > older time + # Recursive call for all next phases if change in date_end > older time resource_cal_pool = self.pool.get('resource.calendar') calendar_id = phase.project_id.resource_calendar_id.id @@ -191,7 +191,6 @@ class project_resource_allocation(osv.osv): _description = 'Project Resource Allocation' _rec_name = 'resource_id' _columns = { -# 'name': fields.char('Name',size = 64), 'resource_id': fields.many2one('resource.resource', 'Resource', 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%)"), diff --git a/addons/project_long_term/wizard/compute_phases_date.py b/addons/project_long_term/wizard/compute_phases_date.py index 8cd3b59c2db..ff31fec1251 100644 --- a/addons/project_long_term/wizard/compute_phases_date.py +++ b/addons/project_long_term/wizard/compute_phases_date.py @@ -61,15 +61,13 @@ def leaves_resource(cr,uid,id): resource_leaves_pool = pool.get('resource.calendar.leaves') resource_leave_ids = resource_leaves_pool.search(cr,uid,[('resource_id','=',id)]) leaves = [] - if resource_leave_ids: - 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 - leave_days = no.days + 1 - [leaves.append((dt_start + datetime.timedelta(days=x)).strftime('%Y-%m-%d')) for x in range(int(leave_days))] - leaves.sort() + 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 resource_list(cr,uid,obj): @@ -162,14 +160,12 @@ class wizard_compute_phases(wizard.interface): # If project working calendar has any leaves resource_leave_ids = resource_leaves_pool.search(cr,uid,[('calendar_id','=',calendar_id)]) - if resource_leave_ids: - 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 - leave_days = no.days + 1 - [leaves.append((dt_start + datetime.timedelta(days=x)).strftime('%Y-%m-%d')) for x in range(int(leave_days))] + 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() @@ -184,7 +180,7 @@ class wizard_compute_phases(wizard.interface): # Creating a new project for each phase def Project(): start = start_date - + minimum_time_unit = 1 # If project has working calendar else the default one would be considered if wktime_cal: working_days = wktime_cal From 04a579d34d01531ca54dbf93e5330b108d09aecd Mon Sep 17 00:00:00 2001 From: "Rvo (Open ERP)" Date: Thu, 25 Feb 2010 11:31:27 +0530 Subject: [PATCH 06/12] [ADD]:fields:responsible_id,sequence on phase bzr revid: rvo@tinyerp.co.in-20100225060127-163r7s9v40jo08xf --- addons/project_long_term/project.py | 127 ++++++---------------- addons/project_long_term/project_view.xml | 49 +++++++-- 2 files changed, 71 insertions(+), 105 deletions(-) diff --git a/addons/project_long_term/project.py b/addons/project_long_term/project.py index e50b41713b1..83838d4721c 100644 --- a/addons/project_long_term/project.py +++ b/addons/project_long_term/project.py @@ -31,7 +31,6 @@ class project_phase(osv.osv): _description = "Project Phase" - def _check_recursion(self,cr,uid,ids): obj_self = self.browse(cr, uid, ids[0]) prev_ids = obj_self.previous_phase_ids @@ -51,7 +50,7 @@ class project_phase(osv.osv): #iter prev_ids while prev_ids: - cr.execute('select distinct prv_phase_id from project_phase_previous_rel where phase_id in ('+','.join(map(str, prev_ids))+')') + cr.execute('select distinct next_phase_id from project_phase_rel where prv_phase_id in ('+','.join(map(str, prev_ids))+')') prv_phase_ids = filter(None, map(lambda x: x[0], cr.fetchall())) if obj_self.id in prv_phase_ids: return False @@ -62,7 +61,7 @@ class project_phase(osv.osv): #iter next_ids while next_ids: - cr.execute('select distinct next_phase_id from project_phase_next_rel where phase_id in ('+','.join(map(str, next_ids))+')') + cr.execute('select distinct prv_phase_id from project_phase_rel where next_phase_id in ('+','.join(map(str, next_ids))+')') next_phase_ids = filter(None, map(lambda x: x[0], cr.fetchall())) if obj_self.id in next_phase_ids: return False @@ -80,109 +79,51 @@ class project_phase(osv.osv): 'constraint_date_start': fields.datetime('Constraint Starting Date'), 'constraint_date_end': fields.datetime('Constraint End Date'), 'project_id': fields.many2one('project.project', 'Project', required=True), - 'next_phase_ids': fields.many2many('project.phase', 'project_phase_next_rel', 'phase_id', 'next_phase_id', 'Next Phases'), - 'previous_phase_ids': fields.many2many('project.phase', 'project_phase_previous_rel', 'phase_id', 'prv_phase_id', 'Previous Phases'), - 'duration': fields.float('Duration'), - 'product_uom': fields.many2one('product.uom', 'Duration UoM', help="UoM (Unit of Measure) is the unit of measurement for Duration"), + '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"), '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, + 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']) - ] +# _constraints = [ +# (_check_recursion,'Error ! Loops In Phases Not Allowed',['next_phase_ids','previous_phase_ids']) +# ] - def timeformat_convert(self,cr, uid, time_string, context={}): - # 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 phase_draft(self, cr, uid, ids,*args): + self.write(cr, uid, ids, {'state': 'draft'}) + return True - def compute_hours(self,cr,uid,calendar_id,context = None): - # To compute average hours of the working calendar + def phase_start(self, cr, uid, ids,*args): + self.write(cr, uid, ids, {'state': 'open'}) + return True - resource_week_pool = self.pool.get('resource.calendar.week') - 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']) - hours = [] - hr = 0 - wk_days = [] - for week in week_obj: - if week['dayofweek'] not in wk_days: - wk_days.append(week['dayofweek']) - hour_from_str = self.timeformat_convert(cr,uid,week['hour_from']) - hour_to_str = self.timeformat_convert(cr,uid,week['hour_to']) - hours.append(week['hour_from']) - hours.append(week['hour_to']) + def phase_pending(self, cr, uid, ids,*args): + self.write(cr, uid, ids, {'state': 'pending'}) + return True - for hour in range(len(hours)): - if hour%2 ==0: - hr += float(hours[hour+1]) - float(hours[hour]) - return hr/len(wk_days) + def phase_cancel(self, cr, uid, ids,*args): + self.write(cr, uid, ids, {'state': 'cancelled'}) + return True - 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') - calendar_id = phase.project_id.resource_calendar_id.id - avg_hours = self.compute_hours(cr,uid,calendar_id) - hours = phase.duration * avg_hours - work_time = resource_cal_pool.interval_min_get(cr, uid, calendar_id or False, date_end, hours or 0.0) - 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')}) - - 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') - calendar_id = phase.project_id.resource_calendar_id.id - avg_hours = self.compute_hours(cr,uid,calendar_id) - hours = phase.duration * avg_hours - work_time = resource_cal_pool.interval_get(cr, uid, calendar_id or False, date_start, hours or 0.0) - 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}) - - def write(self, cr, uid, ids, vals,context=None): - phase = self.browse(cr,uid,ids[0]) - resource_cal_pool = self.pool.get('resource.calendar') - calendar_id = phase.project_id.resource_calendar_id.id - avg_hours = self.compute_hours(cr,uid,calendar_id) - - if not context: - context = {} -# write method changes 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') - hrs = phase.duration * avg_hours - work_times = resource_cal_pool.interval_get(cr, uid, calendar_id or False, dt_start, hrs or 0.0) - 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) - - 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') - hrs = phase.duration * avg_hours - work_times = resource_cal_pool.interval_min_get(cr, uid, calendar_id or False, dt_end, hrs or 0.0) - 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) - return super(project_phase, self).write(cr, uid, ids, vals, context=context) + def phase_done(self, cr, uid, ids,*args): + self.write(cr, uid, ids, {'state': 'done'}) + return True project_phase() @@ -192,7 +133,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 = { diff --git a/addons/project_long_term/project_view.xml b/addons/project_long_term/project_view.xml index b2219f8f281..d7213754b4a 100644 --- a/addons/project_long_term/project_view.xml +++ b/addons/project_long_term/project_view.xml @@ -19,9 +19,8 @@ tree - + - @@ -34,29 +33,55 @@
- + + - - - - - - - + - + + + + + + + + + + + + +