Project management improvements
bzr revid: hda@tinyerp.com-20100226132006-lomo55f42zug2o1n
This commit is contained in:
commit
3f969c76a6
|
@ -10,6 +10,7 @@
|
|||
|
||||
<record id="employee1" model="hr.employee">
|
||||
<field name="name">Fabien Pinckaers</field>
|
||||
<field name="calendar_id" ref="timesheet_group1"/>
|
||||
<!--<field name="regime">45</field>-->
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<!--<field name="holiday_max">25</field>-->
|
||||
|
|
|
@ -256,17 +256,23 @@ class task(osv.osv):
|
|||
res[task.id]['delay_hours'] = res[task.id]['total_hours'] - task.planned_hours
|
||||
return res
|
||||
|
||||
def onchange_planned(self, cr, uid, ids, planned=0.0, effective=0.0, date_start=None,occupation_rate=0.0):
|
||||
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 = {}
|
||||
for res in self.browse(cr, uid, ids):
|
||||
if date_start and planned:
|
||||
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')
|
||||
if date_start:
|
||||
resource_pool = self.pool.get('resource.resource')
|
||||
project_pool = self.pool.get('project.project')
|
||||
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)])
|
||||
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 / occupation_rate)
|
||||
calendar_id = project_pool.browse(cr,uid,project).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}
|
||||
|
||||
|
@ -473,6 +479,83 @@ 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')
|
||||
resource_pool = 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)])
|
||||
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')
|
||||
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')
|
||||
resource_pool = 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)])
|
||||
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')
|
||||
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):
|
||||
|
||||
if not context:
|
||||
context = {}
|
||||
|
||||
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
|
||||
tasks = self.browse(cr,uid,ids[0])
|
||||
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)])
|
||||
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)
|
||||
|
||||
# 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')
|
||||
work_times = resource_cal_pool.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)
|
||||
|
||||
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)
|
||||
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):
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
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
|
|
@ -247,7 +247,7 @@
|
|||
name="planned_hours"
|
||||
widget="float_time"
|
||||
attrs="{'readonly':[('state','!=','draft')]}"
|
||||
on_change="onchange_planned(planned_hours,effective_hours,date_start,occupation_rate)"/>
|
||||
on_change="onchange_planned(project_id,user_id,planned_hours,effective_hours,date_start,occupation_rate)"/>
|
||||
<field name="remaining_hours" select="2" widget="float_time" attrs="{'readonly':[('state','!=','draft')]}" colspan="2"/>
|
||||
<button name="%(action_config_compute_remaining)d" string="Review" type="action" colspan="1" target="new" states="open,pending" icon="gtk-edit"/>
|
||||
</group>
|
||||
|
|
|
@ -26,12 +26,11 @@ import datetime
|
|||
from resource.faces import *
|
||||
from new import classobj
|
||||
import operator
|
||||
import time
|
||||
import project.project_resource as proj
|
||||
|
||||
compute_form = """<?xml version="1.0" ?>
|
||||
<form string="Compute Scheduling of Tasks">
|
||||
<field name="project_id" colspan="4"/>
|
||||
<field name= "date_from" colspan="4"/>
|
||||
</form>"""
|
||||
|
||||
success_msg = """<?xml version="1.0" ?>
|
||||
|
@ -41,132 +40,39 @@ success_msg = """<?xml version="1.0" ?>
|
|||
|
||||
compute_fields = {
|
||||
'project_id': {'string':'Project', 'type':'many2one', 'relation': 'project.project', 'required':'True'},
|
||||
'date_from': {'string':"Start date",'type':'datetime','required':'True' ,'default': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S')},
|
||||
}
|
||||
|
||||
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,id):
|
||||
# To get the leaves for the members working on project
|
||||
|
||||
pool = pooler.get_pool(cr.dbname)
|
||||
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()
|
||||
return leaves
|
||||
|
||||
class wizard_compute_tasks(wizard.interface):
|
||||
|
||||
|
||||
def _compute_date(self, cr, uid, data, context):
|
||||
pool = pooler.get_pool(cr.dbname)
|
||||
project_pool = pool.get('project.project')
|
||||
task_pool = pool.get('project.task')
|
||||
resource_pool = pool.get('resource.resource')
|
||||
resource_leaves_pool = pool.get('resource.calendar.leaves')
|
||||
resource_week_pool = pool.get('resource.calendar.week')
|
||||
user_pool = 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)])
|
||||
task_ids = task_pool.search(cr,uid,[('project_id','=',project_id),('state','in',['draft','open','pending'])])
|
||||
if task_ids:
|
||||
wktime_cal = []
|
||||
leaves = []
|
||||
task_ids.sort()
|
||||
task_obj = task_pool.browse(cr,uid,task_ids)
|
||||
task_1 = task_obj[0]
|
||||
date_start = datetime.datetime.strftime(datetime.datetime.strptime(data['form']['date_from'],"%Y-%m-%d %H:%M:%S"),"%Y-%m-%d %H:%M")
|
||||
calendar_id = project.resource_calendar_id.id
|
||||
|
||||
# If project has a working calendar then that would be used otherwise
|
||||
# the default faces calendar would be used
|
||||
if calendar_id:
|
||||
resource_leave_ids = resource_leaves_pool.search(cr,uid,[('calendar_id','=',calendar_id)])
|
||||
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 = []
|
||||
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))
|
||||
|
||||
# If project working calendar has any 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()
|
||||
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 = project.members
|
||||
resource_objs = []
|
||||
for no in range(len(resource)):
|
||||
for resource in project.members:
|
||||
leaves = []
|
||||
resource_eff = 1.00
|
||||
resource_id = resource_pool.search(cr,uid,[('user_id','=',resource[no].id)])
|
||||
resource_id = resource_pool.search(cr,uid,[('user_id','=',resource.id)])
|
||||
if resource_id:
|
||||
# Getting the efficiency for specific resource
|
||||
resource_eff = resource_pool.browse(cr,uid,resource_id)[0].time_efficiency
|
||||
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}))
|
||||
|
||||
# Getting list of leaves for specific resource
|
||||
if wktime_cal:
|
||||
leaves = leaves_resource(cr,uid,resource_id)
|
||||
resource_objs.append(classobj(str(resource[no].name),(Resource,),{'__doc__':resource[no].name,'__name__':resource[no].name,'vacation':tuple(leaves),'efficiency':resource_eff}))
|
||||
priority_dict = {'0':1000,'1':800,'2':500,'3':300,'4':100}
|
||||
|
||||
# To create dynamic no of tasks with the resource specified
|
||||
|
@ -191,22 +97,25 @@ class wizard_compute_tasks(wizard.interface):
|
|||
minimum_time_unit = 1
|
||||
|
||||
# If project has calendar
|
||||
if wktime_cal:
|
||||
working_days = wktime_cal
|
||||
vacation = tuple(leaves)
|
||||
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
|
||||
for i in range(len(task_obj)):
|
||||
hours = str(task_obj[i].remaining_hours / task_obj[i].occupation_rate)+ 'H'
|
||||
if task_obj[i].priority in priority_dict.keys():
|
||||
priorty = priority_dict[task_obj[i].priority]
|
||||
if task_obj[i].user_id:
|
||||
i = 0
|
||||
for each_task in task_obj:
|
||||
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__ == task_obj[i].user_id.name:
|
||||
if resource_object.__name__ == each_task.user_id.name:
|
||||
task = tasks_resource(i,hours,priorty,resource_object)
|
||||
else:
|
||||
task = tasks_resource(i,hours,priorty)
|
||||
i += 1
|
||||
|
||||
# Writing back the dates
|
||||
project = BalancedProject(Project)
|
||||
loop_no = 0
|
||||
for t in project:
|
||||
|
@ -216,9 +125,8 @@ 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 {}
|
||||
|
||||
states = {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
|
@ -15,7 +15,7 @@
|
|||
# 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/>.
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
|
@ -32,9 +32,10 @@
|
|||
"init_xml" : [],
|
||||
"demo_xml" : [#"project_demo.xml"
|
||||
],
|
||||
"update_xml": [
|
||||
"update_xml": [
|
||||
"project_wizard.xml" ,
|
||||
"project_view.xml",
|
||||
"project_wizard.xml"
|
||||
"project_phase_workflow.xml"
|
||||
],
|
||||
'installable': True,
|
||||
'active': False,
|
||||
|
|
|
@ -20,18 +20,17 @@
|
|||
##############################################################################
|
||||
|
||||
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
|
||||
|
@ -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 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()))
|
||||
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 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()))
|
||||
if obj_self.id in next_phase_ids:
|
||||
return False
|
||||
|
@ -72,6 +71,14 @@ class project_phase(osv.osv):
|
|||
next_ids = next_phase_ids
|
||||
return True
|
||||
|
||||
def _check_dates(self, cr, uid, ids):
|
||||
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
|
||||
|
||||
|
||||
_columns = {
|
||||
'name': fields.char("Phase Name", size=64, required=True),
|
||||
'date_start': fields.datetime('Starting Date'),
|
||||
|
@ -79,22 +86,143 @@ 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'])
|
||||
(_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'])
|
||||
]
|
||||
|
||||
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)
|
||||
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':[]}}
|
||||
|
||||
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')
|
||||
calendar_id = phase.project_id.resource_calendar_id.id
|
||||
if phase.responsible_id.id:
|
||||
resource_id = resource_pool.search(cr,uid,[('user_id','=',phase.responsible_id.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')})
|
||||
|
||||
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')
|
||||
calendar_id = phase.project_id.resource_calendar_id.id
|
||||
if phase.responsible_id.id:
|
||||
resource_id = resource_pool.search(cr,uid,[('user_id','=',phase.responsible_id.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})
|
||||
|
||||
def write(self, cr, uid, ids, vals,context=None):
|
||||
|
||||
if not context:
|
||||
context = {}
|
||||
|
||||
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
|
||||
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_id = False
|
||||
calendar_id = phase.project_id.resource_calendar_id.id
|
||||
|
||||
if phase.responsible_id.id:
|
||||
resource_id = resource_pool.search(cr,uid,[('user_id','=',phase.responsible_id.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
|
||||
|
||||
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)
|
||||
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')
|
||||
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)
|
||||
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_draft(self, cr, uid, ids,*args):
|
||||
self.write(cr, uid, ids, {'state': 'draft'})
|
||||
return True
|
||||
|
||||
def phase_start(self, cr, uid, ids,*args):
|
||||
self.write(cr, uid, ids, {'state': 'open'})
|
||||
return True
|
||||
|
||||
def phase_pending(self, cr, uid, ids,*args):
|
||||
self.write(cr, uid, ids, {'state': 'pending'})
|
||||
return True
|
||||
|
||||
def phase_cancel(self, cr, uid, ids,*args):
|
||||
self.write(cr, uid, ids, {'state': 'cancelled'})
|
||||
return True
|
||||
|
||||
def phase_done(self, cr, uid, ids,*args):
|
||||
self.write(cr, uid, ids, {'state': 'done'})
|
||||
return True
|
||||
|
||||
project_phase()
|
||||
|
||||
class project_resource_allocation(osv.osv):
|
||||
|
@ -102,9 +230,8 @@ 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),
|
||||
'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 = {
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<record id="wkf_phase" model="workflow">
|
||||
<field name="name">project.phase.wkf</field>
|
||||
<field name="osv">project.phase</field>
|
||||
<field name="on_create">True</field>
|
||||
</record>
|
||||
|
||||
<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="kind">function</field>
|
||||
<field name="action">phase_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="kind">function</field>
|
||||
<field name="action">phase_start()</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="flow_stop">True</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">phase_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="kind">function</field>
|
||||
<field name="action">phase_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="flow_stop">True</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">phase_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>
|
||||
</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>
|
||||
</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>
|
||||
</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>
|
||||
</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>
|
||||
</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>
|
||||
</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>
|
||||
</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>
|
||||
</record>
|
||||
|
||||
<record id="t8" model="workflow.transition">
|
||||
<field name="act_from" ref="act_start_phase"/>
|
||||
<field name="act_to" ref="act_draft"/>
|
||||
<field name="signal">phase_draft</field>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
|
@ -19,9 +19,8 @@
|
|||
<field name="type">tree</field>
|
||||
<field name="priority" eval="5"/>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Project Resource Allocation">
|
||||
<tree editable="bottom" string="Project Resource Allocation">
|
||||
<field name="resource_id"/>
|
||||
<field name="phase_id"/>
|
||||
<field name="useability"/>
|
||||
</tree>
|
||||
</field>
|
||||
|
@ -34,29 +33,56 @@
|
|||
<form string="Project Phase">
|
||||
<group colspan="6" col="6">
|
||||
<field name="name" select="1"/>
|
||||
<field name="project_id"/>
|
||||
<field name="project_id" on_change="onchange_project(project_id)"/>
|
||||
<field name="responsible_id"/>
|
||||
<field name="date_start"/>
|
||||
<field name="date_end"/>
|
||||
<field name="constraint_date_start"/>
|
||||
<field name="constraint_date_end"/>
|
||||
<field name="duration"/>
|
||||
<field name="product_uom"/>
|
||||
</group>
|
||||
<notebook colspan="4">
|
||||
<page string="Task Detail">
|
||||
<separator colspan="4" string="Project's Tasks"/>
|
||||
<field colspan="4" name="task_ids" nolabel="1"/>
|
||||
</page>
|
||||
<page string="Resource Allocation Detail">
|
||||
<page string="Resource Allocation Detail">
|
||||
<separator colspan="4" string="Resource Allocation"/>
|
||||
<field colspan="4" name="resource_ids" nolabel="1"/>
|
||||
<field colspan="4" name="resource_ids" nolabel="1">
|
||||
<tree editable="bottom" string="Project Resource Allocation">
|
||||
<field name="resource_id" context="{'project_id':parent.project_id}"/>
|
||||
<field name="useability"/>
|
||||
</tree>
|
||||
<form string="Project Resource Allocation">
|
||||
<field name="resource_id" context="{'project_id':parent.project_id}"/>
|
||||
<field name="useability"/>
|
||||
</form>
|
||||
</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"/>
|
||||
</group>
|
||||
</page>
|
||||
<page string="Other Info">
|
||||
<group colspan="2" col="2">
|
||||
<separator string="Constraints" colspan="2"/>
|
||||
<field name="constraint_date_start"/>
|
||||
<field name="constraint_date_end"/>
|
||||
</group>
|
||||
<group colspan="2" col="2">
|
||||
<separator string="Scheduling" colspan="2"/>
|
||||
<field name="sequence"/>
|
||||
</group>
|
||||
<separator colspan="4" string="Next Phases"/>
|
||||
<field colspan="4" name="next_phase_ids" nolabel="1"/>
|
||||
<separator colspan="4" string="Previous Phases"/>
|
||||
<field colspan="4" name="previous_phase_ids" nolabel="1"/>
|
||||
</page>
|
||||
<page string="Task Detail">
|
||||
<separator colspan="4" string="Project's Tasks"/>
|
||||
<field colspan="4" name="task_ids" nolabel="1"/>
|
||||
|
||||
<button name="%(wizard_schedule_task)d" string="Schedule Tasks" type="action" icon="gtk-jump-to"/>
|
||||
</page>
|
||||
</notebook>
|
||||
<newline/>
|
||||
</form>
|
||||
|
@ -92,7 +118,7 @@
|
|||
res_model="project.task"
|
||||
src_model="project.phase"
|
||||
view_mode="tree,form"
|
||||
domain="[('phase_id','=',active_id)]"/>
|
||||
domain="[('phase_id','=',active_id)]"/>
|
||||
|
||||
<menuitem action="act_project_phase" id="menu_project_phase" parent="project.menu_project_management" sequence="4"/>
|
||||
|
||||
|
|
|
@ -5,11 +5,13 @@
|
|||
<wizard id="wizard_delegate_task" menu="False" model="project.task" name="project.task.delegate" string="Delegate Task"/>
|
||||
<wizard id="wizard_attachment_task" model="project.task" name="project.task.attachment" string="All Attachments"/> -->
|
||||
<wizard id="wizard_compute_phase" menu="False" model="project.phase" name="wizard.compute.phases" string="Compute Phase Scheduling"/>
|
||||
<menuitem icon="terp-project" id="menu_main" name="Project Management"/>
|
||||
<!--<menuitem icon="terp-project" id="menu_main" name="Project Management"/>-->
|
||||
<wizard id="wizard_schedule_task" menu="False" model="project.phase" name="phase.schedule.tasks" string="Schedule Tasks"/>
|
||||
<menuitem id="base.menu_pm_planning" name="Planning" parent="base.menu_main" sequence="3"/>
|
||||
<menuitem
|
||||
action="wizard_compute_phase"
|
||||
id="menu_wizard_compute_phase"
|
||||
parent="menu_main"
|
||||
parent="base.menu_pm_planning"
|
||||
type="wizard"
|
||||
sequence="1"/>
|
||||
</data>
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
##############################################################################
|
||||
|
||||
import compute_phases_date
|
||||
import schedule_tasks
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
|
|
|
@ -25,17 +25,17 @@ from tools.translate import _
|
|||
import datetime
|
||||
from resource.faces import *
|
||||
from new import classobj
|
||||
import operator
|
||||
import project.project_resource as proj
|
||||
|
||||
compute_form = """<?xml version="1.0" ?>
|
||||
<form string="Compute Scheduling of Phases">
|
||||
|
||||
<field name="phase_id" colspan="4"/>
|
||||
<field name="project_id" colspan="4"/>
|
||||
|
||||
</form>"""
|
||||
|
||||
compute_fields = {
|
||||
'phase_id': {'string':'Phase', 'type':'many2one', 'relation': 'project.phase', 'required':'True'},
|
||||
'project_id': {'string':'Project', 'type':'many2one', 'relation': 'project.project'},
|
||||
|
||||
}
|
||||
|
||||
|
@ -44,188 +44,91 @@ success_msg = """<?xml version="1.0" ?>
|
|||
<label string="Phase Scheduling completed successfully."/>
|
||||
</form>"""
|
||||
|
||||
def timeformat_convert(cr, uid, time_string, context={}):
|
||||
# Function to convert input time string:: 8.5 to output time string 8:30
|
||||
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
|
||||
if phase:
|
||||
resource_id = resource_pool.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)
|
||||
duration = str(avg_hours) + 'H'
|
||||
|
||||
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
|
||||
# Creating 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
|
||||
if calendar_id:
|
||||
working_days = proj.compute_working_calendar(cr,uid,calendar_id)
|
||||
vacation = tuple(proj.leaves_resource(cr,uid,calendar_id))
|
||||
|
||||
def leaves_resource(cr,uid,id):
|
||||
# To get the leaves for the resource_ids working on phase
|
||||
def phase():
|
||||
effort = duration
|
||||
|
||||
pool = pooler.get_pool(cr.dbname)
|
||||
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()
|
||||
return leaves
|
||||
project = BalancedProject(Project)
|
||||
s_date = project.phase.start.to_datetime()
|
||||
e_date = project.phase.end.to_datetime()
|
||||
|
||||
def resource_list(cr,uid,obj):
|
||||
# To get the resource_ids working on phase
|
||||
# According to constraints on date start and date end on phase recalculation done
|
||||
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:
|
||||
start_date = s_date
|
||||
if phase.constraint_date_end and str(e_date) > phase.constraint_date_end:
|
||||
end_date= datetime.datetime.strptime(phase.constraint_date_end,'%Y-%m-%d %H:%M:%S')
|
||||
date_start = phase.constraint_date_end[:-3]
|
||||
else:
|
||||
end_date = e_date
|
||||
date_start = end_date
|
||||
|
||||
pool = pooler.get_pool(cr.dbname)
|
||||
resource_pool = pool.get('resource.resource')
|
||||
resources = obj.resource_ids
|
||||
resource_objs = []
|
||||
leaves = []
|
||||
calendar_id = obj.project_id.resource_calendar_id
|
||||
for no in range(len(resources)):
|
||||
resource_id = resource_pool.search(cr,uid,[('id','=',resources[no].resource_id.id)])
|
||||
if resource_id and calendar_id:
|
||||
# Getting list of leaves for specific resource
|
||||
leaves = leaves_resource(cr,uid,resource_id)
|
||||
# Creating the faces.Resource object with resource specific efficiency and vacation
|
||||
resource_objs.append(classobj(str(resources[no].resource_id.name),(Resource,),{'__doc__':resources[no].resource_id.name,'__name__':resources[no].resource_id.name,'efficiency':resources[no].useability/100,'vacation':tuple(leaves)}))
|
||||
return resource_objs
|
||||
# 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
|
||||
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)
|
||||
else:
|
||||
continue
|
||||
|
||||
#
|
||||
class wizard_compute_phases(wizard.interface):
|
||||
|
||||
def _compute_date(self, cr, uid, data, context):
|
||||
pool = pooler.get_pool(cr.dbname)
|
||||
project_pool = pool.get('project.project')
|
||||
phase_pool = pool.get('project.phase')
|
||||
resource_pool = pool.get('resource.resource')
|
||||
resource_leaves_pool = pool.get('resource.calendar.leaves')
|
||||
resource_week_pool = pool.get('resource.calendar.week')
|
||||
avg_hr = 0.0
|
||||
wktime_cal = []
|
||||
leaves = []
|
||||
phase_id = data['form']['phase_id']
|
||||
phase = phase_pool.browse(cr,uid,phase_id)
|
||||
calendar_id = phase.project_id.resource_calendar_id.id
|
||||
|
||||
# If project has a working calendar then that would be used otherwise
|
||||
# the default faces calendar would be used
|
||||
if calendar_id:
|
||||
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 = []
|
||||
hours = []
|
||||
hr = 0
|
||||
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'])
|
||||
# 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)])
|
||||
|
||||
# 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
|
||||
hours.append(week['hour_from'])
|
||||
hours.append(week['hour_to'])
|
||||
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 hour in range(len(hours)):
|
||||
if hour%2 ==0:
|
||||
hr += float(hours[hour+1]) - float(hours[hour])
|
||||
avg_hr = hr/len(wktime_cal)
|
||||
|
||||
# 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))
|
||||
|
||||
# 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))]
|
||||
leaves.sort()
|
||||
|
||||
|
||||
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:
|
||||
avg_hour = 8.0
|
||||
man_days = str(phase.duration * avg_hour) + 'H'
|
||||
|
||||
# Creating a new project for each phase
|
||||
def Project():
|
||||
start = start_date
|
||||
|
||||
# If project has working calendar else the default one would be considered
|
||||
if wktime_cal:
|
||||
working_days = wktime_cal
|
||||
vacation = tuple(leaves)
|
||||
|
||||
def phase():
|
||||
effort = man_days
|
||||
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()
|
||||
|
||||
# According to constraints on date start and date end on phase recalculation done
|
||||
if phase.constraint_date_start and str(s_date) < phase.constraint_date_start:
|
||||
start_date = phase.constraint_date_start
|
||||
else:
|
||||
start_date = s_date
|
||||
if phase.constraint_date_end and str(e_date) > phase.constraint_date_end:
|
||||
end_date = phase.constraint_date_end[:-3]
|
||||
else:
|
||||
end_date = e_date
|
||||
|
||||
# Writing the dates back
|
||||
phase_pool.write(cr,uid,[phase.id],{'date_start':start_date,'date_end':end_date})
|
||||
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)
|
||||
|
||||
# Phase Scheduling starts from here with the call to phase_schedule method
|
||||
start_dt = datetime.datetime.strftime((datetime.datetime.strptime(phase.project_id.date_start,"%Y-%m-%d")),"%Y-%m-%d %H:%M")
|
||||
if avg_hr:
|
||||
phase_schedule(cr,uid,phase,start_dt,avg_hr)
|
||||
# else all the draft,open,pending states phases taken
|
||||
else:
|
||||
phase_schedule(cr,uid,phase,start_dt)
|
||||
phase_ids = phase_pool.search(cr,uid,[('state','in',['draft','open','pending']),('previous_phase_ids','=',False)])
|
||||
|
||||
phase_ids.sort()
|
||||
phase_objs = phase_pool.browse(cr,uid,phase_ids)
|
||||
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)
|
||||
return {}
|
||||
|
||||
states = {
|
||||
'init': {
|
||||
'actions': [],
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
# -*- 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 wizard
|
||||
import pooler
|
||||
from tools.translate import _
|
||||
import datetime
|
||||
from resource.faces import *
|
||||
from new import classobj
|
||||
import operator
|
||||
import time
|
||||
import project.project_resource as proj
|
||||
|
||||
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
|
||||
|
||||
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}))
|
||||
return resource_objs
|
||||
|
||||
class wizard_schedule_task(wizard.interface):
|
||||
|
||||
def _compute_date(self, cr, uid, data, context):
|
||||
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)))
|
||||
|
||||
if task_ids:
|
||||
task_ids.sort()
|
||||
tasks = task_pool.browse(cr,uid,task_ids)
|
||||
wktime_cal = []
|
||||
start_date = str(phase.date_start)[:-9]
|
||||
if not phase.date_start:
|
||||
if not phase.project_id.date_start:
|
||||
start_date = datetime.datetime.now().strftime("%Y-%m-%d")
|
||||
else:
|
||||
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):
|
||||
def task():
|
||||
"""
|
||||
task is a dynamic method!
|
||||
"""
|
||||
effort = eff
|
||||
if obj:
|
||||
resource = obj
|
||||
priority = priorty
|
||||
task.__doc__ = "TaskNO%d" %j
|
||||
task.__name__ = "task%d" %j
|
||||
return task
|
||||
|
||||
# Creating the project with all the tasks and resources
|
||||
def Project():
|
||||
title = "Test Project"
|
||||
start = date_start
|
||||
resource = reduce(operator.or_,resource_objs)
|
||||
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
|
||||
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)
|
||||
else:
|
||||
task = tasks_resource(i,hours,priorty)
|
||||
i += 1
|
||||
|
||||
# Writing back the dates
|
||||
project = BalancedProject(Project)
|
||||
loop_no = 0
|
||||
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})
|
||||
loop_no +=1
|
||||
return {}
|
||||
|
||||
states = {
|
||||
'init': {
|
||||
'actions': [_compute_date],
|
||||
'result': {'type':'form','arch':success_msg,'fields':{}, 'state':[('end', 'Ok')]},
|
||||
}
|
||||
}
|
||||
wizard_schedule_task('phase.schedule.tasks')
|
||||
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
@ -20,6 +20,7 @@
|
|||
##############################################################################
|
||||
|
||||
import resource
|
||||
import faces
|
||||
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -36,68 +36,67 @@ 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):
|
||||
|
||||
def interval_min_get(self, cr, uid, id, dt_from, hours,resource=False):
|
||||
resource_cal_leaves = self.pool.get('resource.calendar.leaves')
|
||||
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)])
|
||||
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()
|
||||
resource_leave_ids = resource_cal_leaves.search(cr,uid,[('calendar_id','=',id),'|',('resource_id','=',False),('resource_id','=',resource)])
|
||||
res_leaves = resource_cal_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 = []
|
||||
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')
|
||||
leave_flag = False
|
||||
if (hour_from<current_hour) and (todo>0):
|
||||
m = min(hour_to, current_hour)
|
||||
if (m-hour_from)>todo:
|
||||
hour_from = m-todo
|
||||
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_to
|
||||
todo -= (hour_to - m)
|
||||
dt_from += mx.DateTime.RelativeDateTime(days=1)
|
||||
current_hour = 0
|
||||
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=False, byday=True):
|
||||
resource_cal_leaves = self.pool.get('resource.calendar.leaves')
|
||||
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 = resource_cal_leaves.search(cr,uid,[('calendar_id','=',id),'|',('resource_id','=',False),('resource_id','=',resource)])
|
||||
res_leaves = resource_cal_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()
|
||||
print 'dtLevae',dt_leave
|
||||
todo = hours
|
||||
cycle = 0
|
||||
result = []
|
||||
|
@ -106,24 +105,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
|
||||
|
@ -155,8 +154,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',
|
||||
|
@ -164,6 +163,18 @@ class resource_resource(osv.osv):
|
|||
'active' : lambda *a: True,
|
||||
'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'resource.resource', c)
|
||||
}
|
||||
|
||||
def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False):
|
||||
if context is None:
|
||||
context = {}
|
||||
|
||||
if context.get('project_id',False):
|
||||
project_pool = self.pool.get('project.project')
|
||||
project_rec = project_pool.browse(cr,uid,context['project_id'])
|
||||
user_ids = [user_id.id for user_id in project_rec.members]
|
||||
args.append(('user_id','in',user_ids))
|
||||
|
||||
return super(resource_resource, self).search(cr, uid, args, offset, limit,order, context, count)
|
||||
resource_resource()
|
||||
|
||||
class resource_calendar_leaves(osv.osv):
|
||||
|
@ -187,5 +198,14 @@ class resource_calendar_leaves(osv.osv):
|
|||
_constraints = [
|
||||
(check_dates, 'Error! leave start-date must be lower then leave end-date.', ['date_from', 'date_to'])
|
||||
]
|
||||
|
||||
def onchange_resource(self,cr,uid,ids,resource):
|
||||
result = {}
|
||||
if resource:
|
||||
resource_pool = self.pool.get('resource.resource')
|
||||
result['calendar_id'] = resource_pool.browse(cr,uid,resource).calendar_id.id
|
||||
return {'value':result}
|
||||
return {'value':{'calendar_id':[]}}
|
||||
|
||||
resource_calendar_leaves()
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -61,7 +61,6 @@
|
|||
<field name="arch" type="xml">
|
||||
<calendar color="resource_id" date_start="date_from" string="Resource">
|
||||
<field name="name"/>
|
||||
|
||||
</calendar>
|
||||
</field>
|
||||
</record>
|
||||
|
@ -135,7 +134,7 @@
|
|||
<field name="calendar_id" />
|
||||
<field name="date_from" />
|
||||
<field name="date_to" />
|
||||
<field name="resource_id" />
|
||||
<field name="resource_id" on_change="onchange_resource(resource_id)"/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
|
Loading…
Reference in New Issue