[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:
parent
2498fc7273
commit
567f02338b
|
@ -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'),
|
||||
|
|
|
@ -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'
|
||||
-
|
||||
|
|
Loading…
Reference in New Issue