From 567f02338b5c7e30d51fc9f0ec5d630d3cee7449 Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Tue, 12 Oct 2010 17:13:30 +0530 Subject: [PATCH] [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 --- addons/project/project.py | 82 +++++++++++++++++++++++++++++----- addons/project/test/test_project.yml | 17 ------- 2 files changed, 70 insertions(+), 29 deletions(-) diff --git a/addons/project/project.py b/addons/project/project.py index 27bbb2f..25e2421 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -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'), diff --git a/addons/project/test/test_project.yml b/addons/project/test/test_project.yml index d57cc27..bc65ab6 100644 --- a/addons/project/test/test_project.yml +++ b/addons/project/test/test_project.yml @@ -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' - -- 1.7.10.4