[FIX] project: calculate planned hours, effective hours, total hours and progress bar when tasks and tasks work modified

bzr revid: hmo@tinyerp.com-20101012114330-37n22hl9qmczwmfm
This commit is contained in:
Harry (OpenERP) 2010-10-12 17:13:30 +05:30
parent 2498fc7273
commit 567f02338b
2 changed files with 70 additions and 29 deletions

View File

@ -129,7 +129,7 @@ class project(osv.osv):
state<>'cancelled'
GROUP BY
project_id''',(tuple(all_projects),))
progress = dict(map(lambda x: (x[0], (x[1], x[2], x[3])), cr.fetchall()))
progress = dict(map(lambda x: (x[0], (x[1] or 0.0, x[2] or 0.0, x[3] or 0.0)), cr.fetchall()))
user_uom, def_uom = self._get_user_and_default_uom_ids(cr, uid)
for project in self.browse(cr, uid, par_child_projects.keys(), context=context):
@ -145,12 +145,10 @@ class project(osv.osv):
s[0] = uom_obj._compute_qty(cr, uid, user_uom, s[0], def_uom)
s[1] = uom_obj._compute_qty(cr, uid, user_uom, s[1], def_uom)
s[2] = uom_obj._compute_qty(cr, uid, user_uom, s[2], def_uom)
if project.state == 'close':
progress_rate = 100.0
else:
progress_rate = s[1] and round(min(100.0 * s[2] / s[1], 99.99), 2)
res[project.id] = {
'planned_hours': s[0],
'effective_hours': s[2],
@ -159,6 +157,23 @@ class project(osv.osv):
}
return res
def _get_project_task(self, cr, uid, ids, context=None):
if context is None:
context = {}
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 _get_project_work(self, cr, uid, ids, context=None):
if context is None:
context = {}
result = {}
for work in self.pool.get('project.task.work').browse(cr, uid, ids, context=context):
if work.task_id and work.task_id.project_id: result[work.task_id.project_id.id] = True
return result.keys()
def unlink(self, cr, uid, ids, *args, **kwargs):
for proj in self.browse(cr, uid, ids):
if proj.tasks:
@ -174,10 +189,30 @@ class project(osv.osv):
'warn_manager': fields.boolean('Warn Manager', help="If you check this field, the project manager will receive a request each time a task is completed by his team.", states={'close':[('readonly',True)], 'cancelled':[('readonly',True)]}),
'members': fields.many2many('res.users', 'project_user_rel', 'project_id', 'uid', 'Project Members', help="Project's member. Not used in any computation, just for information purpose.", states={'close':[('readonly',True)], 'cancelled':[('readonly',True)]}),
'tasks': fields.one2many('project.task', 'project_id', "Project tasks"),
'planned_hours': fields.function(_progress_rate, multi="progress", method=True, string='Planned Time', help="Sum of planned hours of all tasks related to this project and its child projects.", store=True),
'effective_hours': fields.function(_progress_rate, multi="progress", method=True, string='Time Spent', help="Sum of spent hours of all tasks related to this project and its child projects.", store=True),
'total_hours': fields.function(_progress_rate, multi="progress", method=True, string='Total Time', help="Sum of total hours of all tasks related to this project and its child projects.", store=True),
'progress_rate': fields.function(_progress_rate, multi="progress", method=True, string='Progress', type='float', group_operator="avg", help="Percent of tasks closed according to the total of tasks todo.", store=True),
'planned_hours': fields.function(_progress_rate, multi="progress", method=True, 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'], 10),
'project.task.work': (_get_project_work, ['hours'], 10),
}),
'effective_hours': fields.function(_progress_rate, multi="progress", method=True, string='Time Spent', help="Sum of spent 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'], 10),
'project.task.work': (_get_project_work, ['hours'], 10),
}),
'total_hours': fields.function(_progress_rate, multi="progress", method=True, 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'], 10),
'project.task.work': (_get_project_work, ['hours'], 10),
}),
'progress_rate': fields.function(_progress_rate, multi="progress", method=True, string='Progress', type='float', group_operator="avg", help="Percent of tasks closed according to the total of tasks todo.",
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'], 10),
'project.task.work': (_get_project_work, ['hours'], 10),
}),
'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)]}),
@ -414,6 +449,14 @@ class task(osv.osv):
res[task.id] = False
return res
def _get_task(self, cr, uid, ids, context=None):
if context is None:
context = {}
result = {}
for work in self.pool.get('project.task.work').browse(cr, uid, ids, context=context):
if work.task_id: result[work.task_id.id] = True
return result.keys()
_columns = {
'active': fields.function(_is_template, method=True, store=True, string='Not a Template Task', type='boolean', help="This field is computed automatically and have the same behavior than the boolean 'active' field: if the task is linked to a template or unactivated project, it will be hidden unless specifically asked."),
'name': fields.char('Task Summary', size=128, required=True),
@ -433,12 +476,27 @@ class task(osv.osv):
'child_ids': fields.many2many('project.task', 'project_task_parent_rel', 'parent_id', 'task_id', 'Delegated Tasks'),
'notes': fields.text('Notes'),
'planned_hours': fields.float('Planned Hours', help='Estimated time to do the task, usually set by the project manager when the task is in draft state.'),
'effective_hours': fields.function(_hours_get, method=True, string='Hours Spent', multi='hours', store=True, help="Computed using the sum of the task work done."),
'effective_hours': fields.function(_hours_get, method=True, string='Hours Spent', multi='hours', help="Computed using the sum of the task work done.",
store = {
'project.task': (lambda self, cr, uid, ids, c={}: ids, ['work_ids'], 10),
'project.task.work': (_get_task, ['hours'], 10),
}),
'remaining_hours': fields.float('Remaining Hours', digits=(16,2), help="Total remaining time, can be re-estimated periodically by the assignee of the task."),
'total_hours': fields.function(_hours_get, method=True, string='Total Hours', multi='hours', store=True, help="Computed as: Time Spent + Remaining Time."),
'progress': fields.function(_hours_get, method=True, string='Progress (%)', multi='hours', group_operator="avg", store=True, help="Computed as: Time Spent / Total Time."),
'delay_hours': fields.function(_hours_get, method=True, string='Delay Hours', multi='hours', store=True, help="Computed as difference of the time estimated by the project manager and the real time to close the task."),
'total_hours': fields.function(_hours_get, method=True, string='Total Hours', multi='hours', help="Computed as: Time Spent + Remaining Time.",
store = {
'project.task': (lambda self, cr, uid, ids, c={}: ids, ['work_ids'], 10),
'project.task.work': (_get_task, ['hours'], 10),
}),
'progress': fields.function(_hours_get, method=True, string='Progress (%)', multi='hours', group_operator="avg", help="Computed as: Time Spent / Total Time.",
store = {
'project.task': (lambda self, cr, uid, ids, c={}: ids, ['work_ids'], 10),
'project.task.work': (_get_task, ['hours'], 10),
}),
'delay_hours': fields.function(_hours_get, method=True, string='Delay Hours', multi='hours', help="Computed as difference of the time estimated by the project manager and the real time to close the task.",
store = {
'project.task': (lambda self, cr, uid, ids, c={}: ids, ['work_ids'], 10),
'project.task.work': (_get_task, ['hours'], 10),
}),
'user_id': fields.many2one('res.users', 'Assigned to'),
'delegated_user_id': fields.related('child_ids', 'user_id', type='many2one', relation='res.users', string='Delegated To'),
'partner_id': fields.many2one('res.partner', 'Partner'),

View File

@ -136,7 +136,6 @@
- planned_hours == 30
- remaining_hours == 30
- delay_hours == 0.0
- total_hours == 30
- effective_hours == 0.0
-
Make a work task entry 'Training on OpenERP modules, models and classes' of 10 hours
@ -153,8 +152,6 @@
!assert {model: project.task, id: project_task_technicaltraining0, severity: error, string: After work task of 10 hours effective_hours must be equal to 10}:
- remaining_hours == 20
- effective_hours == 10.0
- total_hours == effective_hours + remaining_hours
- delay_hours == total_hours - planned_hours
-
Make a work task entry 'Training on OpenERP xml views' of 10 hours
-
@ -170,8 +167,6 @@
!assert {model: project.task, id: project_task_technicaltraining0, severity: error, string: After one more work task of 10 hours effective_hours must be equal to 20}:
- remaining_hours == 10
- effective_hours == 20.0
- total_hours == effective_hours + remaining_hours
- delay_hours == total_hours - planned_hours
-
Make a work task entry 'Training on workflows' of 10 hours
-
@ -187,8 +182,6 @@
!assert {model: project.task, id: project_task_technicaltraining0, severity: error, string: After one more work task of 10 hours effective_hours must be equal to 30}:
- remaining_hours == 0
- effective_hours == 30.0
- total_hours == effective_hours + remaining_hours
- delay_hours == total_hours - planned_hours
-
Set remaining hours of 10 hours for reevaluating the task
-
@ -210,8 +203,6 @@
- planned_hours == 30
- remaining_hours == 10.0
- effective_hours == 30.0
- total_hours == effective_hours + remaining_hours
- delay_hours == total_hours - planned_hours
-
Make a work task entry 'Training on reports and wizards' of 10 hours
-
@ -228,8 +219,6 @@
- planned_hours == 30
- remaining_hours == 0
- effective_hours == 40.0
- total_hours == effective_hours + remaining_hours
- delay_hours == total_hours - planned_hours
-
Close the task
-
@ -265,8 +254,6 @@
- planned_hours == 30
- remaining_hours == 10.0
- effective_hours == 40.0
- total_hours == effective_hours + remaining_hours
- delay_hours == total_hours - planned_hours
-
Make a work task entry 'Training on yml' of 5 hours
-
@ -283,8 +270,6 @@
- planned_hours == 30
- remaining_hours == 5.0
- effective_hours == 45.0
- total_hours == effective_hours + remaining_hours
- delay_hours == total_hours - planned_hours
-
Close the task
-
@ -302,8 +287,6 @@
- planned_hours == 30
- remaining_hours == 0.0
- effective_hours == 45.0
- total_hours == effective_hours + remaining_hours
- delay_hours == total_hours - planned_hours
-
Close project 'OpenERP Training Programme'
-