[MERGE] lp:823838 (project: make project hours include children projects hours)
bzr revid: rco@openerp.com-20120207092623-3b7qajyfny3o2b0m
This commit is contained in:
commit
4db8d52f74
|
@ -84,35 +84,70 @@ class project(osv.osv):
|
|||
val['pricelist_id'] = pricelist_id
|
||||
return {'value': val}
|
||||
|
||||
def _progress_rate(self, cr, uid, ids, names, arg, context=None):
|
||||
res = {}.fromkeys(ids, 0.0)
|
||||
if not ids:
|
||||
return res
|
||||
cr.execute('''SELECT
|
||||
project_id, sum(planned_hours), sum(total_hours), sum(effective_hours), SUM(remaining_hours)
|
||||
FROM
|
||||
project_task
|
||||
WHERE
|
||||
project_id in %s AND
|
||||
state<>'cancelled'
|
||||
GROUP BY
|
||||
project_id''', (tuple(ids),))
|
||||
progress = dict(map(lambda x: (x[0], (x[1],x[2],x[3],x[4])), cr.fetchall()))
|
||||
for project in self.browse(cr, uid, ids, context=context):
|
||||
s = progress.get(project.id, (0.0,0.0,0.0,0.0))
|
||||
res[project.id] = {
|
||||
'planned_hours': s[0],
|
||||
'effective_hours': s[2],
|
||||
'total_hours': s[1],
|
||||
'progress_rate': s[1] and round(100.0*s[2]/s[1],2) or 0.0
|
||||
}
|
||||
def _get_projects_from_tasks(self, cr, uid, task_ids, context=None):
|
||||
tasks = self.pool.get('project.task').browse(cr, uid, task_ids, context=context)
|
||||
project_ids = [task.project_id.id for task in tasks]
|
||||
return self.pool.get('project.project')._get_project_and_parents(cr, uid, project_ids, context)
|
||||
|
||||
def _get_project_and_parents(self, cr, uid, ids, context=None):
|
||||
""" return the project ids and all their parent projects """
|
||||
res = set(ids)
|
||||
while ids:
|
||||
cr.execute("""
|
||||
SELECT DISTINCT parent.id
|
||||
FROM project_project project, project_project parent, account_analytic_account account
|
||||
WHERE project.analytic_account_id = account.id
|
||||
AND parent.analytic_account_id = account.parent_id
|
||||
AND project.id IN %s
|
||||
""", (tuple(ids),))
|
||||
ids = [t[0] for t in cr.fetchall()]
|
||||
res.update(ids)
|
||||
return list(res)
|
||||
|
||||
def _get_project_and_children(self, cr, uid, ids, context=None):
|
||||
""" retrieve all children projects of project ids;
|
||||
return a dictionary mapping each project to its parent project (or None)
|
||||
"""
|
||||
res = dict.fromkeys(ids, None)
|
||||
while ids:
|
||||
cr.execute("""
|
||||
SELECT project.id, parent.id
|
||||
FROM project_project project, project_project parent, account_analytic_account account
|
||||
WHERE project.analytic_account_id = account.id
|
||||
AND parent.analytic_account_id = account.parent_id
|
||||
AND parent.id IN %s
|
||||
""", (tuple(ids),))
|
||||
dic = dict(cr.fetchall())
|
||||
res.update(dic)
|
||||
ids = dic.keys()
|
||||
return res
|
||||
|
||||
def _get_project_task(self, cr, uid, ids, context=None):
|
||||
result = {}
|
||||
for task in self.pool.get('project.task').browse(cr, uid, ids, context=context):
|
||||
if task.project_id: result[task.project_id.id] = True
|
||||
return result.keys()
|
||||
def _progress_rate(self, cr, uid, ids, names, arg, context=None):
|
||||
child_parent = self._get_project_and_children(cr, uid, ids, context)
|
||||
# compute planned_hours, total_hours, effective_hours specific to each project
|
||||
cr.execute("""
|
||||
SELECT project_id, COALESCE(SUM(planned_hours), 0.0),
|
||||
COALESCE(SUM(total_hours), 0.0), COALESCE(SUM(effective_hours), 0.0)
|
||||
FROM project_task WHERE project_id IN %s AND state <> 'cancelled'
|
||||
GROUP BY project_id
|
||||
""", (tuple(child_parent.keys()),))
|
||||
# aggregate results into res
|
||||
res = dict([(id, {'planned_hours':0.0,'total_hours':0.0,'effective_hours':0.0}) for id in ids])
|
||||
for id, planned, total, effective in cr.fetchall():
|
||||
# add the values specific to id to all parent projects of id in the result
|
||||
while id:
|
||||
if id in ids:
|
||||
res[id]['planned_hours'] += planned
|
||||
res[id]['total_hours'] += total
|
||||
res[id]['effective_hours'] += effective
|
||||
id = child_parent[id]
|
||||
# compute progress rates
|
||||
for id in ids:
|
||||
if res[id]['total_hours']:
|
||||
res[id]['progress_rate'] = round(100.0 * res[id]['effective_hours'] / res[id]['total_hours'], 2)
|
||||
else:
|
||||
res[id]['progress_rate'] = 0.0
|
||||
return res
|
||||
|
||||
def unlink(self, cr, uid, ids, *args, **kwargs):
|
||||
for proj in self.browse(cr, uid, ids):
|
||||
|
@ -133,17 +168,25 @@ class project(osv.osv):
|
|||
'tasks': fields.one2many('project.task', 'project_id', "Project tasks"),
|
||||
'planned_hours': fields.function(_progress_rate, multi="progress", string='Planned Time', help="Sum of planned hours of all tasks related to this project and its child projects.",
|
||||
store = {
|
||||
'project.project': (lambda self, cr, uid, ids, c={}: ids, ['tasks'], 10),
|
||||
'project.task': (_get_project_task, ['planned_hours', 'effective_hours', 'remaining_hours', 'total_hours', 'progress', 'delay_hours','state'], 10),
|
||||
'project.project': (_get_project_and_parents, ['tasks', 'parent_id', 'child_ids'], 10),
|
||||
'project.task': (_get_projects_from_tasks, ['planned_hours', 'remaining_hours', 'work_ids', 'state'], 20),
|
||||
}),
|
||||
'effective_hours': fields.function(_progress_rate, multi="progress", string='Time Spent', help="Sum of spent hours of all tasks related to this project and its child projects.",
|
||||
store = {
|
||||
'project.project': (_get_project_and_parents, ['tasks', 'parent_id', 'child_ids'], 10),
|
||||
'project.task': (_get_projects_from_tasks, ['planned_hours', 'remaining_hours', 'work_ids', 'state'], 20),
|
||||
}),
|
||||
'effective_hours': fields.function(_progress_rate, multi="progress", string='Time Spent', help="Sum of spent hours of all tasks related to this project and its child projects."),
|
||||
'resource_calendar_id': fields.many2one('resource.calendar', 'Working Time', help="Timetable working hours to adjust the gantt diagram report", states={'close':[('readonly',True)]} ),
|
||||
'total_hours': fields.function(_progress_rate, multi="progress", string='Total Time', help="Sum of total hours of all tasks related to this project and its child projects.",
|
||||
store = {
|
||||
'project.project': (lambda self, cr, uid, ids, c={}: ids, ['tasks'], 10),
|
||||
'project.task': (_get_project_task, ['planned_hours', 'effective_hours', 'remaining_hours', 'total_hours', 'progress', 'delay_hours','state'], 10),
|
||||
'project.project': (_get_project_and_parents, ['tasks', 'parent_id', 'child_ids'], 10),
|
||||
'project.task': (_get_projects_from_tasks, ['planned_hours', 'remaining_hours', 'work_ids', 'state'], 20),
|
||||
}),
|
||||
'progress_rate': fields.function(_progress_rate, multi="progress", string='Progress', type='float', group_operator="avg", help="Percent of tasks closed according to the total of tasks todo."),
|
||||
'progress_rate': fields.function(_progress_rate, multi="progress", string='Progress', type='float', group_operator="avg", help="Percent of tasks closed according to the total of tasks todo.",
|
||||
store = {
|
||||
'project.project': (_get_project_and_parents, ['tasks', 'parent_id', 'child_ids'], 10),
|
||||
'project.task': (_get_projects_from_tasks, ['planned_hours', 'remaining_hours', 'work_ids', 'state'], 20),
|
||||
}),
|
||||
'resource_calendar_id': fields.many2one('resource.calendar', 'Working Time', help="Timetable working hours to adjust the gantt diagram report", states={'close':[('readonly',True)]} ),
|
||||
'warn_customer': fields.boolean('Warn Partner', help="If you check this, the user will have a popup when closing a task that propose a message to send by email to the customer.", states={'close':[('readonly',True)], 'cancelled':[('readonly',True)]}),
|
||||
'warn_header': fields.text('Mail Header', help="Header added at the beginning of the email for the warning message sent to the customer when a task is closed.", states={'close':[('readonly',True)], 'cancelled':[('readonly',True)]}),
|
||||
'warn_footer': fields.text('Mail Footer', help="Footer added at the beginning of the email for the warning message sent to the customer when a task is closed.", states={'close':[('readonly',True)], 'cancelled':[('readonly',True)]}),
|
||||
|
|
Loading…
Reference in New Issue