From f33ede7c6fc5c1321644a906f74bd2a9e5a15e2b Mon Sep 17 00:00:00 2001 From: Jigar Amin - OpenERP Date: Fri, 12 Aug 2011 15:47:42 +0530 Subject: [PATCH 01/11] [IMP] Project time shoudl consider it;s parents also bzr revid: jam@tinyerp.com-20110812101742-njb0418if0uhgbbw --- addons/project/project.py | 39 +++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/addons/project/project.py b/addons/project/project.py index b1461da424c..c474bb620ba 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -80,6 +80,16 @@ class project(osv.osv): pricelist_id = pricelist.get('property_product_pricelist', False) and pricelist.get('property_product_pricelist')[0] or False return {'value':{'contact_id': addr['contact'], 'pricelist_id': pricelist_id}} + def get_childs(self, cr, uid, ids, context=None): + cr.execute("""SELECT id FROM project_project WHERE analytic_account_id IN ( + SELECT id FROM account_analytic_account WHERE parent_id = ( + SELECT id FROM account_analytic_account WHERE id = ( + SELECT analytic_account_id FROM project_project WHERE id = %s + ) + ) + )"""%(ids)) + return cr.fetchall() + def _progress_rate(self, cr, uid, ids, names, arg, context=None): res = {}.fromkeys(ids, 0.0) if not ids: @@ -93,14 +103,31 @@ class project(osv.osv): 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())) + progress = dict(map(lambda x: (x[0], (x[1] or 0.0 ,x[2] or 0.0 ,x[3] or 0.0 ,x[4] or 0.0)), 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)) + childs = self.get_childs( cr, uid, project.id, context) + project_ids = [project.id] + if childs: + project_ids += [ child[0] for child in childs ] 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 + 'planned_hours':0.0, + 'effective_hours': 0.0, + 'total_hours': 0.0, + 'progress_rate':0.0 + } + planned_hours, effective_hours, total_hours= 0.0, 0.0,0.0 + for child in project_ids: + s = progress.get(child, (0.0,0.0,0.0,0.0)) + planned_hours, effective_hours, total_hours = planned_hours+s[0] or 0.0, effective_hours+s[2] or 0.0, total_hours+s[1] or 0.0 + if planned_hours == 0: + rnd = 0.0 + else: + rnd = round((effective_hours/planned_hours)*100,2) or 0.0 + res[project.id] = { + 'planned_hours': res.get(project.id).get('planned_hours')+ planned_hours, + 'effective_hours': res.get(project.id).get('effective_hours')+ effective_hours, + 'total_hours': res.get(project.id).get('total_hours')+ total_hours, + 'progress_rate': rnd } return res From aad408531416354fafbedce2ff883382bce2a51d Mon Sep 17 00:00:00 2001 From: Jigar Amin - OpenERP Date: Fri, 12 Aug 2011 16:03:54 +0530 Subject: [PATCH 02/11] imporved injection sql bzr revid: jam@tinyerp.com-20110812103354-0mo9szu5u7wzr3zn --- addons/project/project.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/addons/project/project.py b/addons/project/project.py index c474bb620ba..762ad6add8c 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -82,12 +82,12 @@ class project(osv.osv): def get_childs(self, cr, uid, ids, context=None): cr.execute("""SELECT id FROM project_project WHERE analytic_account_id IN ( - SELECT id FROM account_analytic_account WHERE parent_id = ( - SELECT id FROM account_analytic_account WHERE id = ( - SELECT analytic_account_id FROM project_project WHERE id = %s - ) - ) - )"""%(ids)) + SELECT id FROM account_analytic_account WHERE parent_id = ( + SELECT analytic_account_id FROM project_project project + LEFT JOIN account_analytic_account account ON account.id = project.analytic_account_id + WHERE project.id = %s + ) + )"""%(ids)) return cr.fetchall() def _progress_rate(self, cr, uid, ids, names, arg, context=None): From 65051f36952efe2997b9d5e6e306ce657ec34739 Mon Sep 17 00:00:00 2001 From: Jigar Amin - OpenERP Date: Tue, 27 Sep 2011 15:55:14 +0530 Subject: [PATCH 03/11] [FIX] project - returnof the store bzr revid: jam@tinyerp.com-20110927102514-rge1jer11yyznttx --- addons/project/project.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/addons/project/project.py b/addons/project/project.py index 136008aef79..1b728b538d1 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -133,7 +133,12 @@ class project(osv.osv): 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 + if task.project_id: + result[task.project_id.id] = True + if task.project_id.parent_id: + cr.execute('''SELECT id FROM project_project WHERE analytic_account_id = '%s' '''%task.project_id.parent_id.id) + for parent in cr.fetchall(): + result[parent[0]] = True return result.keys() def _get_project_work(self, cr, uid, ids, context=None): From 1a4a2b4dd8feb0e7d861eae0656b60e0d6e4cdcf Mon Sep 17 00:00:00 2001 From: Jigar Amin - OpenERP Date: Wed, 28 Sep 2011 18:30:33 +0530 Subject: [PATCH 04/11] [FIX] project - calcaulting parent child caluesonchnage of task and parent_id bzr revid: jam@tinyerp.com-20110928130033-el7n8edu0erp95b6 --- addons/project/project.py | 81 ++++++++++++++++++++++++++------------- 1 file changed, 54 insertions(+), 27 deletions(-) diff --git a/addons/project/project.py b/addons/project/project.py index 1b728b538d1..99ec9df8db5 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -79,20 +79,37 @@ class project(osv.osv): pricelist_id = pricelist.get('property_product_pricelist', False) and pricelist.get('property_product_pricelist')[0] or False return {'value':{'contact_id': addr['contact'], 'pricelist_id': pricelist_id}} - def get_childs(self, cr, uid, ids, context=None): + def _get_childs(self, cr, uid, ids, childs,context=None): cr.execute("""SELECT id FROM project_project WHERE analytic_account_id IN ( SELECT id FROM account_analytic_account WHERE parent_id = ( SELECT analytic_account_id FROM project_project project LEFT JOIN account_analytic_account account ON account.id = project.analytic_account_id WHERE project.id = %s ) - )"""%(ids)) - return cr.fetchall() + )"""%(ids)) + for child in cr.fetchall(): + if child: + if child[0] not in childs: childs.append(child[0]) + self._get_childs( cr, uid, child[0], childs,context) + return childs + + + def _get_parents(self, cr, uid, ids, parents,context=None): + for project in self.read(cr, uid, ids, ['id', 'parent_id'],context): + if project.get('parent_id'): + cr.execute('''SELECT id FROM project_project WHERE analytic_account_id = '%s' '''%project.get('parent_id')[0]) + for child in cr.fetchall(): + if child[0] not in parents: parents.append(child[0]) + child_rec= self.read(cr, uid, child[0], ['id', 'parent_id'],context) + if child_rec.get('parent_id'): + parents = self._get_parents(cr, uid, [child[0]], parents,context) + return parents def _progress_rate(self, cr, uid, ids, names, arg, context=None): res = {}.fromkeys(ids, 0.0) if not ids: return res + parents = self._get_parents(cr, uid, ids, ids,context) cr.execute('''SELECT project_id, sum(planned_hours), sum(total_hours), sum(effective_hours), SUM(remaining_hours) FROM @@ -103,31 +120,41 @@ class project(osv.osv): GROUP BY project_id''', (tuple(ids),)) progress = dict(map(lambda x: (x[0], (x[1] or 0.0 ,x[2] or 0.0 ,x[3] or 0.0 ,x[4] or 0.0)), cr.fetchall())) - for project in self.browse(cr, uid, ids, context=context): - childs = self.get_childs( cr, uid, project.id, context) - project_ids = [project.id] - if childs: - project_ids += [ child[0] for child in childs ] + for project in self.browse(cr, uid, parents, context=context): + childs = [] + childs = self._get_childs(cr, uid, project.id, childs,context) + s = progress.get(project.id, (0.0,0.0,0.0,0.0)) res[project.id] = { - 'planned_hours':0.0, - 'effective_hours': 0.0, - 'total_hours': 0.0, - 'progress_rate':0.0 + '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 } planned_hours, effective_hours, total_hours= 0.0, 0.0,0.0 - for child in project_ids: - s = progress.get(child, (0.0,0.0,0.0,0.0)) - planned_hours, effective_hours, total_hours = planned_hours+s[0] or 0.0, effective_hours+s[2] or 0.0, total_hours+s[1] or 0.0 - if planned_hours == 0: - rnd = 0.0 - else: - rnd = round((effective_hours/planned_hours)*100,2) or 0.0 - res[project.id] = { - 'planned_hours': res.get(project.id).get('planned_hours')+ planned_hours, - 'effective_hours': res.get(project.id).get('effective_hours')+ effective_hours, - 'total_hours': res.get(project.id).get('total_hours')+ total_hours, - 'progress_rate': rnd - } + if childs: + 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(childs),)) + progress2 = dict(map(lambda x: (x[0], (x[1] or 0.0 ,x[2] or 0.0 ,x[3] or 0.0 ,x[4] or 0.0)), cr.fetchall())) + for child in childs: + s2 = progress2.get(child, (0.0,0.0,0.0,0.0)) + planned_hours, effective_hours, total_hours = planned_hours+s2[0], effective_hours+s2[2] , total_hours+s2[1] + if planned_hours == 0: + rnd = 0.0 + else: + rnd = round((effective_hours/planned_hours)*100,2) or 0.0 + res[project.id] = { + 'planned_hours': res.get(project.id).get('planned_hours')+ planned_hours, + 'effective_hours': res.get(project.id).get('effective_hours')+ effective_hours, + 'total_hours': res.get(project.id).get('total_hours')+ total_hours, + 'progress_rate': res.get(project.id).get('progress_rate')+ rnd + } return res def _get_project_task(self, cr, uid, ids, context=None): @@ -166,13 +193,13 @@ 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.project': (lambda self, cr, uid, ids, c={}: ids, ['tasks', 'parent_id'], 10), 'project.task': (_get_project_task, ['planned_hours', 'effective_hours', 'remaining_hours', 'total_hours', 'progress', 'delay_hours','state'], 10), }), '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."), '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.project': (lambda self, cr, uid, ids, c={}: ids, ['tasks','parent_id'], 10), 'project.task': (_get_project_task, ['planned_hours', 'effective_hours', 'remaining_hours', 'total_hours', 'progress', 'delay_hours','state'], 10), }), '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."), From 66f7f0ac91b9419c959e19a927bd2e91630692ca Mon Sep 17 00:00:00 2001 From: Jigar Amin - OpenERP Date: Mon, 3 Oct 2011 11:44:54 +0530 Subject: [PATCH 05/11] [FIX] project added child in fields.function trigger bzr revid: jam@tinyerp.com-20111003061454-aoakxovqrw9uxtl8 --- addons/project/project.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/project/project.py b/addons/project/project.py index e8c1d96be89..8b13955b803 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -193,13 +193,13 @@ 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', 'parent_id'], 10), + 'project.project': (lambda self, cr, uid, ids, c={}: ids, ['tasks', 'parent_id', 'child_ids'], 10), 'project.task': (_get_project_task, ['planned_hours', 'effective_hours', 'remaining_hours', 'total_hours', 'progress', 'delay_hours','state'], 10), }), '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."), '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','parent_id'], 10), + 'project.project': (lambda self, cr, uid, ids, c={}: ids, ['tasks','parent_id', 'child_ids'], 10), 'project.task': (_get_project_task, ['planned_hours', 'effective_hours', 'remaining_hours', 'total_hours', 'progress', 'delay_hours','state'], 10), }), '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."), From eb0dfa2555ed939468d32e230d9f19850657371b Mon Sep 17 00:00:00 2001 From: Jigar Amin - OpenERP Date: Mon, 3 Oct 2011 15:17:19 +0530 Subject: [PATCH 06/11] [FIX] cide usabality bzr revid: jam@tinyerp.com-20111003094719-c3tepdje2wd81xyb --- addons/project/project.py | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/addons/project/project.py b/addons/project/project.py index 8b13955b803..1fb828c78d4 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -88,9 +88,8 @@ class project(osv.osv): ) )"""%(ids)) for child in cr.fetchall(): - if child: - if child[0] not in childs: childs.append(child[0]) - self._get_childs( cr, uid, child[0], childs,context) + if child[0] not in childs: childs.append(child[0]) + self._get_childs( cr, uid, child[0], childs,context) return childs @@ -130,7 +129,7 @@ class project(osv.osv): 'total_hours': s[1], 'progress_rate': s[1] and round(100.0*s[2]/s[1],2) or 0.0 } - planned_hours, effective_hours, total_hours= 0.0, 0.0,0.0 + if childs: cr.execute('''SELECT project_id, sum(planned_hours), sum(total_hours), sum(effective_hours), SUM(remaining_hours) @@ -141,19 +140,18 @@ class project(osv.osv): state<>'cancelled' GROUP BY project_id''', (tuple(childs),)) - progress2 = dict(map(lambda x: (x[0], (x[1] or 0.0 ,x[2] or 0.0 ,x[3] or 0.0 ,x[4] or 0.0)), cr.fetchall())) + child_progress = dict(map(lambda x: (x[0], (x[1] or 0.0 ,x[2] or 0.0 ,x[3] or 0.0 ,x[4] or 0.0)), cr.fetchall())) + planned_hours, effective_hours, total_hours, rnd= 0.0, 0.0,0.0, 0.0 for child in childs: - s2 = progress2.get(child, (0.0,0.0,0.0,0.0)) - planned_hours, effective_hours, total_hours = planned_hours+s2[0], effective_hours+s2[2] , total_hours+s2[1] - if planned_hours == 0: - rnd = 0.0 - else: - rnd = round((effective_hours/planned_hours)*100,2) or 0.0 + ch_vals = child_progress.get(child, (0.0,0.0,0.0,0.0)) + planned_hours, effective_hours, total_hours = planned_hours+ch_vals[0], effective_hours+ch_vals[2] , total_hours+ch_vals[1] + if res.get(project.id).get('planned_hours')+ planned_hours > 0: + rnd = round(( res.get(project.id).get('effective_hours')+effective_hours)/(res.get(project.id).get('planned_hours')+ planned_hours)*100,2) or 0.0 res[project.id] = { 'planned_hours': res.get(project.id).get('planned_hours')+ planned_hours, 'effective_hours': res.get(project.id).get('effective_hours')+ effective_hours, 'total_hours': res.get(project.id).get('total_hours')+ total_hours, - 'progress_rate': res.get(project.id).get('progress_rate')+ rnd + 'progress_rate': rnd } return res From b839539b83dcc7f2eee9f4885186a64f6fe4e056 Mon Sep 17 00:00:00 2001 From: Jigar Amin - OpenERP Date: Wed, 30 Nov 2011 16:38:52 +0530 Subject: [PATCH 07/11] [FIX] ActiveId Ref on New Task When from Project>Tasks act_window and Query Code refectoring bzr revid: jam@tinyerp.com-20111130110852-ox1y0dfnvvmc44yx --- addons/project/project.py | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/addons/project/project.py b/addons/project/project.py index 9b588e88659..466c937ac6f 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -109,20 +109,18 @@ class project(osv.osv): parents = self._get_parents(cr, uid, [child[0]], parents,context) return parents + def _get_project(self,cr, ids): + 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),)) + return cr + def _progress_rate(self, cr, uid, ids, names, arg, context=None): res = {}.fromkeys(ids, 0.0) if not ids: return res parents = self._get_parents(cr, uid, ids, ids,context) - 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),)) + cr = self._get_project(cr,ids) progress = dict(map(lambda x: (x[0], (x[1] or 0.0 ,x[2] or 0.0 ,x[3] or 0.0 ,x[4] or 0.0)), cr.fetchall())) for project in self.browse(cr, uid, parents, context=context): childs = [] @@ -136,15 +134,7 @@ class project(osv.osv): } if childs: - 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(childs),)) + cr = self._get_project(cr, childs) child_progress = dict(map(lambda x: (x[0], (x[1] or 0.0 ,x[2] or 0.0 ,x[3] or 0.0 ,x[4] or 0.0)), cr.fetchall())) planned_hours, effective_hours, total_hours, rnd= 0.0, 0.0,0.0, 0.0 for child in childs: @@ -588,8 +578,8 @@ class task(osv.osv): def _default_project(self, cr, uid, context=None): if context is None: context = {} - if 'project_id' in context and context['project_id']: - return int(context['project_id']) + if 'default_project_id' in context and context['default_project_id']: + return int(context['default_project_id']) return False def duplicate_task(self, cr, uid, map_ids, context=None): From 287c11d94ddaefaf23fb81c19397cb95be864cda Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Mon, 6 Feb 2012 11:36:55 +0100 Subject: [PATCH 08/11] [IMP] project: fix dependencies of function fields progress_rate, etc. bzr revid: rco@openerp.com-20120206103655-u1ba98f49xa2ifj8 --- addons/project/project.py | 127 ++++++++++++++++---------------------- 1 file changed, 52 insertions(+), 75 deletions(-) diff --git a/addons/project/project.py b/addons/project/project.py index 6f773b93908..a25299f42ee 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -84,83 +84,60 @@ class project(osv.osv): val['pricelist_id'] = pricelist_id return {'value': val} - def _get_childs(self, cr, uid, ids, childs,context=None): - cr.execute("""SELECT id FROM project_project WHERE analytic_account_id IN ( - SELECT id FROM account_analytic_account WHERE parent_id = ( - SELECT analytic_account_id FROM project_project project - LEFT JOIN account_analytic_account account ON account.id = project.analytic_account_id - WHERE project.id = %s - ) - )"""%(ids)) - for child in cr.fetchall(): - if child[0] not in childs: childs.append(child[0]) - self._get_childs( cr, uid, child[0], childs,context) - return childs + 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 = map(lambda t: t[0], cr.fetchall()) + res.update(ids) + return list(res) - def _get_parents(self, cr, uid, ids, parents,context=None): - for project in self.read(cr, uid, ids, ['id', 'parent_id'],context): - if project.get('parent_id'): - cr.execute('''SELECT id FROM project_project WHERE analytic_account_id = '%s' '''%project.get('parent_id')[0]) - for child in cr.fetchall(): - if child[0] not in parents: parents.append(child[0]) - child_rec= self.read(cr, uid, child[0], ['id', 'parent_id'],context) - if child_rec.get('parent_id'): - parents = self._get_parents(cr, uid, [child[0]], parents,context) - return parents - - def _get_project(self,cr, ids): - 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),)) - return cr + def _get_project_and_children(self, cr, uid, ids, context=None): + """ return the project ids and all their children projects """ + res = set(ids) + while ids: + cr.execute(""" + SELECT project.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),)) + ids = map(lambda t: t[0], cr.fetchall()) + res.update(ids) + return list(res) def _progress_rate(self, cr, uid, ids, names, arg, context=None): - res = {}.fromkeys(ids, 0.0) - if not ids: - return res - parents = self._get_parents(cr, uid, ids, ids,context) - cr = self._get_project(cr,ids) - progress = dict(map(lambda x: (x[0], (x[1] or 0.0 ,x[2] or 0.0 ,x[3] or 0.0 ,x[4] or 0.0)), cr.fetchall())) - for project in self.browse(cr, uid, parents, context=context): - childs = [] - childs = self._get_childs(cr, uid, project.id, childs,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 - } - - if childs: - cr = self._get_project(cr, childs) - child_progress = dict(map(lambda x: (x[0], (x[1] or 0.0 ,x[2] or 0.0 ,x[3] or 0.0 ,x[4] or 0.0)), cr.fetchall())) - planned_hours, effective_hours, total_hours, rnd= 0.0, 0.0,0.0, 0.0 - for child in childs: - ch_vals = child_progress.get(child, (0.0,0.0,0.0,0.0)) - planned_hours, effective_hours, total_hours = planned_hours+ch_vals[0], effective_hours+ch_vals[2] , total_hours+ch_vals[1] - if res.get(project.id).get('planned_hours')+ planned_hours > 0: - rnd = round(( res.get(project.id).get('effective_hours')+effective_hours)/(res.get(project.id).get('planned_hours')+ planned_hours)*100,2) or 0.0 - res[project.id] = { - 'planned_hours': res.get(project.id).get('planned_hours')+ planned_hours, - 'effective_hours': res.get(project.id).get('effective_hours')+ effective_hours, - 'total_hours': res.get(project.id).get('total_hours')+ total_hours, - 'progress_rate': rnd - } + res = {} + for id in ids: + project_ids = self._get_project_and_children(cr, uid, [id], context) + cr.execute("""SELECT + COALESCE(SUM(planned_hours), 0.0) AS planned_hours, + COALESCE(SUM(total_hours), 0.0) AS total_hours, + COALESCE(SUM(effective_hours), 0.0) AS effective_hours + FROM project_task WHERE project_id IN %s AND state <> 'cancelled' + """, (tuple(project_ids),)) + r = cr.dictfetchone() + if r['total_hours']: + r['progress_rate'] = round(100.0 * r['effective_hours'] / r['total_hours'], 2) + else: + r['progress_rate'] = 100.0 + res[id] = r + print ">>> _progress_rate(%s) from projects %s\n>>> %s" % (id, project_ids, res[id]) 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 - if task.project_id.parent_id: - cr.execute('''SELECT id FROM project_project WHERE analytic_account_id = '%s' '''%task.project_id.parent_id.id) - for parent in cr.fetchall(): - result[parent[0]] = True - return result.keys() - def unlink(self, cr, uid, ids, *args, **kwargs): for proj in self.browse(cr, uid, ids): if proj.tasks: @@ -180,15 +157,15 @@ 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', 'parent_id', 'child_ids'], 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', 'effective_hours', 'remaining_hours', 'total_hours', 'progress', 'delay_hours','state'], 10), }), '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','parent_id', 'child_ids'], 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', 'effective_hours', 'remaining_hours', 'total_hours', 'progress', 'delay_hours','state'], 10), }), '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."), '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)]}), From ba4e2135167a6d9b3908c96d315b2e36a913a91d Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Mon, 6 Feb 2012 12:29:40 +0100 Subject: [PATCH 09/11] [IMP] project: optimize function field progress_rate of project.project bzr revid: rco@openerp.com-20120206112940-mnvl3bvhyn2vn17c --- addons/project/project.py | 51 ++++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/addons/project/project.py b/addons/project/project.py index a25299f42ee..5a0f6d52f64 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -105,37 +105,48 @@ class project(osv.osv): return list(res) def _get_project_and_children(self, cr, uid, ids, context=None): - """ return the project ids and all their children projects """ - res = set(ids) + """ 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 + 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),)) - ids = map(lambda t: t[0], cr.fetchall()) - res.update(ids) - return list(res) + dic = dict(cr.fetchall()) + res.update(dic) + ids = dic.keys() + return res def _progress_rate(self, cr, uid, ids, names, arg, context=None): - res = {} + 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: - project_ids = self._get_project_and_children(cr, uid, [id], context) - cr.execute("""SELECT - COALESCE(SUM(planned_hours), 0.0) AS planned_hours, - COALESCE(SUM(total_hours), 0.0) AS total_hours, - COALESCE(SUM(effective_hours), 0.0) AS effective_hours - FROM project_task WHERE project_id IN %s AND state <> 'cancelled' - """, (tuple(project_ids),)) - r = cr.dictfetchone() - if r['total_hours']: - r['progress_rate'] = round(100.0 * r['effective_hours'] / r['total_hours'], 2) + if res[id]['total_hours']: + res[id]['progress_rate'] = round(100.0 * res[id]['effective_hours'] / res[id]['total_hours'], 2) else: - r['progress_rate'] = 100.0 - res[id] = r - print ">>> _progress_rate(%s) from projects %s\n>>> %s" % (id, project_ids, res[id]) + res[id]['progress_rate'] = 100.0 return res def unlink(self, cr, uid, ids, *args, **kwargs): From 9f1dc2bc02fd354eac0398e84227873d1ee1fa39 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Mon, 6 Feb 2012 14:06:31 +0100 Subject: [PATCH 10/11] [FIX] project: value of field progress on project.project bzr revid: rco@openerp.com-20120206130631-43np82er01p9kd2i --- addons/project/project.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/project/project.py b/addons/project/project.py index 5a0f6d52f64..b2ac4cc86c1 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -146,7 +146,7 @@ class project(osv.osv): 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'] = 100.0 + res[id]['progress_rate'] = 0.0 return res def unlink(self, cr, uid, ids, *args, **kwargs): From 457ba47cf6181218b08d82e8fd6d72e7374366ae Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Mon, 6 Feb 2012 14:08:04 +0100 Subject: [PATCH 11/11] [IMP] project: store fields effective_hours and progress_rate, and fix their store dependencies bzr revid: rco@openerp.com-20120206130804-87wq126btdzqt91e --- addons/project/project.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/addons/project/project.py b/addons/project/project.py index b2ac4cc86c1..52633fb449a 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -169,16 +169,24 @@ class project(osv.osv): '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': (_get_project_and_parents, ['tasks', 'parent_id', 'child_ids'], 10), - 'project.task': (_get_projects_from_tasks, ['planned_hours', 'effective_hours', 'remaining_hours', 'total_hours', 'progress', 'delay_hours','state'], 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': (_get_project_and_parents, ['tasks','parent_id', 'child_ids'], 10), - 'project.task': (_get_projects_from_tasks, ['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)]}),