From 522de0125cd5764220957637ee8f647dc910ef8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 18 Apr 2012 10:47:37 +0200 Subject: [PATCH 01/16] [REVIEW] needaction: remove unnecessary bzr revid: tde@openerp.com-20120418084737-jxvoe5jf02xalq8r --- openerp/addons/base/ir/ir_needaction.py | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/openerp/addons/base/ir/ir_needaction.py b/openerp/addons/base/ir/ir_needaction.py index 3fcd56affae..6dd0b73e82c 100644 --- a/openerp/addons/base/ir/ir_needaction.py +++ b/openerp/addons/base/ir/ir_needaction.py @@ -49,15 +49,11 @@ class ir_needaction_users(osv.osv): def _get_users(self, cr, uid, res_ids, res_model, context=None): """Given res_ids of res_model, get user_ids present in table""" - if context is None: - context = {} needact_ids = self.search(cr, uid, [('res_model', '=', res_model), ('res_id', 'in', res_ids)], context=context) return map(itemgetter('res_id'), self.read(cr, uid, needact_ids, context=context)) def create_users(self, cr, uid, res_ids, res_model, user_ids, context=None): """Given res_ids of res_model, add user_ids to the relationship table""" - if context is None: - context = {} for res_id in res_ids: for user_id in user_ids: self.create(cr, uid, {'res_model': res_model, 'res_id': res_id, 'user_id': user_id}, context=context) @@ -65,8 +61,6 @@ class ir_needaction_users(osv.osv): def unlink_users(self, cr, uid, res_ids, res_model, context=None): """Given res_ids of res_model, delete all entries in the relationship table""" - if context is None: - context = {} to_del_ids = self.search(cr, uid, [('res_model', '=', res_model), ('res_id', 'in', res_ids)], context=context) return self.unlink(cr, uid, to_del_ids, context=context) @@ -131,8 +125,6 @@ class ir_needaction_mixin(osv.osv): return dict.fromkeys(ids, []) def create(self, cr, uid, values, context=None): - if context is None: - context = {} needact_table_obj = self.pool.get('ir.needaction_users') # perform create obj_id = super(ir_needaction_mixin, self).create(cr, uid, values, context=context) @@ -142,8 +134,6 @@ class ir_needaction_mixin(osv.osv): return obj_id def write(self, cr, uid, ids, values, context=None): - if context is None: - context = {} needact_table_obj = self.pool.get('ir.needaction_users') # perform write write_res = super(ir_needaction_mixin, self).write(cr, uid, ids, values, context=context) @@ -154,8 +144,6 @@ class ir_needaction_mixin(osv.osv): return write_res def unlink(self, cr, uid, ids, context=None): - if context is None: - context = {} # unlink user_ids needact_table_obj = self.pool.get('ir.needaction_users') needact_table_obj.unlink_users(cr, uid, ids, self._name, context=context) @@ -169,8 +157,6 @@ class ir_needaction_mixin(osv.osv): def needaction_get_record_ids(self, cr, uid, user_id, limit=80, context=None): """Given the current model and a user_id get the number of actions it has to perform""" - if context is None: - context = {} needact_table_obj = self.pool.get('ir.needaction_users') needact_table_ids = needact_table_obj.search(cr, uid, [('res_model', '=', self._name), ('user_id', '=', user_id)], limit=limit, context=context) return map(itemgetter('res_id'), needact_table_obj.read(cr, uid, needact_table_ids, context=context)) @@ -178,8 +164,6 @@ class ir_needaction_mixin(osv.osv): def needaction_get_action_count(self, cr, uid, user_id, limit=80, context=None): """Given the current model and a user_id get the number of actions it has to perform""" - if context is None: - context = {} needact_table_obj = self.pool.get('ir.needaction_users') return needact_table_obj.search(cr, uid, [('res_model', '=', self._name), ('user_id', '=', user_id)], limit=limit, count=True, context=context) @@ -188,8 +172,6 @@ class ir_needaction_mixin(osv.osv): perform an action. Records are given as references, a list of tuples (model_name, record_id). This method is trans-model.""" - if context is None: - context = {} needact_table_obj = self.pool.get('ir.needaction_users') needact_table_ids = needact_table_obj.search(cr, uid, [('user_id', '=', user_id)], offset=offset, limit=limit, order=order, context=context) needact_records = needact_table_obj.read(cr, uid, needact_table_ids, context=context) From 7ef99096b0b84b0eb338ba55fcce14b3f9b5142c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 18 Apr 2012 10:59:45 +0200 Subject: [PATCH 02/16] [RFEVIEW] Cleaned code, based on ODO/VMT review. bzr revid: tde@openerp.com-20120418085945-jlr7hbzrpju3yv2m --- openerp/addons/base/ir/ir_needaction.py | 52 +++++++++---------- .../addons/base/security/ir.model.access.csv | 2 +- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/openerp/addons/base/ir/ir_needaction.py b/openerp/addons/base/ir/ir_needaction.py index 6dd0b73e82c..7f246dc282a 100644 --- a/openerp/addons/base/ir/ir_needaction.py +++ b/openerp/addons/base/ir/ir_needaction.py @@ -24,17 +24,18 @@ from operator import itemgetter from osv import osv, fields from tools.translate import _ -class ir_needaction_users(osv.osv): +class ir_needaction_users_rel(osv.osv): ''' - ir_needaction_users holds data related to the needaction - mechanism inside OpenERP. A needaction is characterized by: + ir_needaction_users_rel holds data related to the needaction + mechanism inside OpenERP. A row in this model is characterized by: - res_model: model of the record requiring an action - res_id: ID of the record requiring an action - user_id: foreign key to the res.users table, to the user that has to perform the action - ''' + This model can be seen as a many2many, linking (res_model, res_id) to + users (those whose attention is required on the record).''' - _name = 'ir.needaction_users' + _name = 'ir.ir_needaction_users_rel' _description = 'Needaction relationship table' _rec_name = 'id' _order = 'id desc' @@ -49,8 +50,8 @@ class ir_needaction_users(osv.osv): def _get_users(self, cr, uid, res_ids, res_model, context=None): """Given res_ids of res_model, get user_ids present in table""" - needact_ids = self.search(cr, uid, [('res_model', '=', res_model), ('res_id', 'in', res_ids)], context=context) - return map(itemgetter('res_id'), self.read(cr, uid, needact_ids, context=context)) + rel_ids = self.search(cr, uid, [('res_model', '=', res_model), ('res_id', 'in', res_ids)], context=context) + return map(itemgetter('res_id'), self.read(cr, uid, rel_ids, context=context)) def create_users(self, cr, uid, res_ids, res_model, user_ids, context=None): """Given res_ids of res_model, add user_ids to the relationship table""" @@ -88,11 +89,11 @@ class ir_needaction_mixin(osv.osv): This class wraps a class (ir.needaction_users) that behaves like a many2many field. However, no field is added to the model - inheriting from base.needaction. The mixin class manages the low-level + inheriting from this mixin class. This class handles the low-level considerations of updating relationships. Every change made on the record calls a method that updates the relationships. - Objects using the need_action feature should override the + Objects using the 'need_action' feature should override the ``get_needaction_user_ids`` method. This methods returns a dictionary whose keys are record ids, and values a list of user ids, like in a many2many relationship. Therefore by defining only one method, @@ -112,7 +113,7 @@ class ir_needaction_mixin(osv.osv): are given as references, a list of tuples (model_name, record_id) ''' _name = 'ir.needaction_mixin' - _description = 'Need action of users on records API' + _description = '"Need action" mixin' #------------------------------------------------------ # Addon API @@ -125,56 +126,55 @@ class ir_needaction_mixin(osv.osv): return dict.fromkeys(ids, []) def create(self, cr, uid, values, context=None): - needact_table_obj = self.pool.get('ir.needaction_users') + rel_obj = self.pool.get('ir.needaction_users') # perform create obj_id = super(ir_needaction_mixin, self).create(cr, uid, values, context=context) # link user_ids needaction_user_ids = self.get_needaction_user_ids(cr, uid, [obj_id], context=context) - needact_table_obj.create_users(cr, uid, [obj_id], self._name, needaction_user_ids[obj_id], context=context) + rel_obj.create_users(cr, uid, [obj_id], self._name, needaction_user_ids[obj_id], context=context) return obj_id def write(self, cr, uid, ids, values, context=None): - needact_table_obj = self.pool.get('ir.needaction_users') + rel_obj = self.pool.get('ir.needaction_users') # perform write write_res = super(ir_needaction_mixin, self).write(cr, uid, ids, values, context=context) # get and update user_ids needaction_user_ids = self.get_needaction_user_ids(cr, uid, ids, context=context) for id in ids: - needact_table_obj.update_users(cr, uid, [id], self._name, needaction_user_ids[id], context=context) + rel_obj.update_users(cr, uid, [id], self._name, needaction_user_ids[id], context=context) return write_res def unlink(self, cr, uid, ids, context=None): # unlink user_ids - needact_table_obj = self.pool.get('ir.needaction_users') - needact_table_obj.unlink_users(cr, uid, ids, self._name, context=context) + rel_obj = self.pool.get('ir.needaction_users') + rel_obj.unlink_users(cr, uid, ids, self._name, context=context) # perform unlink return super(ir_needaction_mixin, self).unlink(cr, uid, ids, context=context) #------------------------------------------------------ - # Need action API + # "Need action" API #------------------------------------------------------ def needaction_get_record_ids(self, cr, uid, user_id, limit=80, context=None): """Given the current model and a user_id get the number of actions it has to perform""" - needact_table_obj = self.pool.get('ir.needaction_users') - needact_table_ids = needact_table_obj.search(cr, uid, [('res_model', '=', self._name), ('user_id', '=', user_id)], limit=limit, context=context) - return map(itemgetter('res_id'), needact_table_obj.read(cr, uid, needact_table_ids, context=context)) + rel_obj = self.pool.get('ir.needaction_users') + needact_table_ids = rel_obj.search(cr, uid, [('res_model', '=', self._name), ('user_id', '=', user_id)], limit=limit, context=context) + return map(itemgetter('res_id'), rel_obj.read(cr, uid, needact_table_ids, context=context)) def needaction_get_action_count(self, cr, uid, user_id, limit=80, context=None): """Given the current model and a user_id get the number of actions it has to perform""" - needact_table_obj = self.pool.get('ir.needaction_users') - return needact_table_obj.search(cr, uid, [('res_model', '=', self._name), ('user_id', '=', user_id)], limit=limit, count=True, context=context) + rel_obj = self.pool.get('ir.needaction_users') + return rel_obj.search(cr, uid, [('res_model', '=', self._name), ('user_id', '=', user_id)], limit=limit, count=True, context=context) def needaction_get_record_references(self, cr, uid, user_id, offset=None, limit=None, order=None, context=None): """For a given user_id, get all the records that asks this user to perform an action. Records are given as references, a list of tuples (model_name, record_id). This method is trans-model.""" - needact_table_obj = self.pool.get('ir.needaction_users') - needact_table_ids = needact_table_obj.search(cr, uid, [('user_id', '=', user_id)], offset=offset, limit=limit, order=order, context=context) - needact_records = needact_table_obj.read(cr, uid, needact_table_ids, context=context) - return map(itemgetter('res_model', 'id'), needact_records) + rel_obj = self.pool.get('ir.needaction_users') + needact_table_ids = rel_obj.search(cr, uid, [('user_id', '=', user_id)], offset=offset, limit=limit, order=order, context=context) + return map(itemgetter('res_model', 'id'), rel_obj.read(cr, uid, needact_table_ids, context=context)) # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/addons/base/security/ir.model.access.csv b/openerp/addons/base/security/ir.model.access.csv index ac05b726341..8b906ba48a5 100644 --- a/openerp/addons/base/security/ir.model.access.csv +++ b/openerp/addons/base/security/ir.model.access.csv @@ -121,6 +121,6 @@ "access_ir_mail_server_all","ir_mail_server","model_ir_mail_server",,1,0,0,0 "access_ir_actions_todo_category","ir_actions_todo_category","model_ir_actions_todo_category","group_system",1,1,1,1 "access_ir_actions_client","ir_actions_client all","model_ir_actions_client",,1,0,0,0 -"access_ir_needaction_users","ir_needaction_users","model_ir_needaction_users",,1,1,1,1 +"access_ir_needaction_users_rel","ir_needaction_users_rel","model_ir_needaction_users_rel",,1,1,1,1 "access_ir_needaction_mixin","ir_needaction_mixin","model_ir_needaction_mixin",,1,1,1,1 From 94210b26a27cb32c0a00bcb578b44bca138eeec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 18 Apr 2012 11:01:41 +0200 Subject: [PATCH 03/16] [REVIEW] [DOC] Updated documentation. bzr revid: tde@openerp.com-20120418090141-ho1205rt0omyxo4k --- doc/api/need_action_specs.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/api/need_action_specs.rst b/doc/api/need_action_specs.rst index c9f32dd60c4..88787ae4c44 100644 --- a/doc/api/need_action_specs.rst +++ b/doc/api/need_action_specs.rst @@ -1,14 +1,14 @@ Need action mechanism ===================== -ir.needaction mixin class -++++++++++++++++++++++++++ +ir.needaction_mixin mixin class ++++++++++++++++++++++++++++++++ This revision adds a mixin class for objects using the need action feature. Need action feature can be used by objects willing to be able to signal that an action is required on a particular record. If in the business logic an action must be performed by somebody, for instance validation by a manager, this mechanism allows to set a list of users asked to perform an action. -This class wraps a class (ir.needaction_users) that behaves like a many2many field. However, no field is added to the model inheriting from base.needaction. The mixin class manages the low-level considerations of updating relationships. Every change made on the record calls a method that updates the relationships. +This class wraps a class (ir.needaction_users) that behaves like a many2many field. However, no field is added to the model inheriting from ir.needaction_mixin. The mixin class manages the low-level considerations of updating relationships. Every change made on the record calls a method that updates the relationships. Objects using the need_action feature should override the ``get_needaction_user_ids`` method. This methods returns a dictionary whose keys are record ids, and values a list of user ids, like in a many2many relationship. Therefore by defining only one method, you can specify if an action is required by defining the users that have to do it, in every possible situation. @@ -30,12 +30,12 @@ Those fields are functional, because they depend on the user and must therefore Addon implementation example ++++++++++++++++++++++++++++ -In your ``foo`` module, you want to specify that when it is in state ``confirmed``, it has to be validated by a manager, given by the field ``manager_id``. After making ``foo`` inheriting from ``base.needaction``, you override the ``get_needaction_user_ids`` method: +In your ``foo`` module, you want to specify that when it is in state ``confirmed``, it has to be validated by a manager, given by the field ``manager_id``. After making ``foo`` inheriting from ``ir.needaction_mixin``, you override the ``get_needaction_user_ids`` method: :: [...] - _inherit = [base.needaction] + _inherit = [`ir.needaction_mixin] [...] def get_needaction_user_ids(self, cr, uid, ids, context=None): result = dict.fromkeys(ids) From c1a4f1f8db6e3161ae61a8358e023c281dc3fed5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 18 Apr 2012 11:07:35 +0200 Subject: [PATCH 04/16] [IMP] Updated code (see odo/vmt review). Also removed record_ids storage in menus, to avoid passing too much data to the client. bzr revid: tde@openerp.com-20120418090735-e5ewk3wcf7bp0bvu --- openerp/addons/base/ir/ir_needaction.py | 15 ++++++++------- openerp/addons/base/ir/ir_ui_menu.py | 8 +++----- openerp/osv/orm.py | 10 ++++++---- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/openerp/addons/base/ir/ir_needaction.py b/openerp/addons/base/ir/ir_needaction.py index 7f246dc282a..c408c189939 100644 --- a/openerp/addons/base/ir/ir_needaction.py +++ b/openerp/addons/base/ir/ir_needaction.py @@ -51,7 +51,7 @@ class ir_needaction_users_rel(osv.osv): def _get_users(self, cr, uid, res_ids, res_model, context=None): """Given res_ids of res_model, get user_ids present in table""" rel_ids = self.search(cr, uid, [('res_model', '=', res_model), ('res_id', 'in', res_ids)], context=context) - return map(itemgetter('res_id'), self.read(cr, uid, rel_ids, context=context)) + return list(set(map(itemgetter('user_id'), self.read(cr, uid, rel_ids, ['user_id'], context=context)))) def create_users(self, cr, uid, res_ids, res_model, user_ids, context=None): """Given res_ids of res_model, add user_ids to the relationship table""" @@ -69,7 +69,7 @@ class ir_needaction_users_rel(osv.osv): """Given res_ids of res_model, update their entries in the relationship table to user_ids""" # read current records cur_users = self._get_users(cr, uid, res_ids, res_model, context=context) - if len(cur_users) == len(user_ids) and all([cur_user in user_ids for cur_user in cur_users]): + if len(cur_users) == len(user_ids) and all(cur_user in user_ids for cur_user in cur_users): return True # unlink old records self.unlink_users(cr, uid, res_ids, res_model, context=context) @@ -157,10 +157,11 @@ class ir_needaction_mixin(osv.osv): def needaction_get_record_ids(self, cr, uid, user_id, limit=80, context=None): """Given the current model and a user_id - get the number of actions it has to perform""" + return the record ids that require the user to perform an + action""" rel_obj = self.pool.get('ir.needaction_users') - needact_table_ids = rel_obj.search(cr, uid, [('res_model', '=', self._name), ('user_id', '=', user_id)], limit=limit, context=context) - return map(itemgetter('res_id'), rel_obj.read(cr, uid, needact_table_ids, context=context)) + rel_ids = rel_obj.search(cr, uid, [('res_model', '=', self._name), ('user_id', '=', user_id)], limit=limit, context=context) + return map(itemgetter('res_id'), rel_obj.read(cr, uid, rel_ids, ['res_id'], context=context)) def needaction_get_action_count(self, cr, uid, user_id, limit=80, context=None): """Given the current model and a user_id @@ -174,7 +175,7 @@ class ir_needaction_mixin(osv.osv): tuples (model_name, record_id). This method is trans-model.""" rel_obj = self.pool.get('ir.needaction_users') - needact_table_ids = rel_obj.search(cr, uid, [('user_id', '=', user_id)], offset=offset, limit=limit, order=order, context=context) - return map(itemgetter('res_model', 'id'), rel_obj.read(cr, uid, needact_table_ids, context=context)) + rel_ids = rel_obj.search(cr, uid, [('user_id', '=', user_id)], offset=offset, limit=limit, order=order, context=context) + return map(itemgetter('res_model', 'res_id'), rel_obj.read(cr, uid, rel_ids, ['res_model', 'res_id'], context=context)) # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/addons/base/ir/ir_ui_menu.py b/openerp/addons/base/ir/ir_ui_menu.py index 1f3f2e0f181..0de4ff74c11 100644 --- a/openerp/addons/base/ir/ir_ui_menu.py +++ b/openerp/addons/base/ir/ir_ui_menu.py @@ -258,19 +258,17 @@ class ir_ui_menu(osv.osv): def _get_needaction(self, cr, uid, ids, field_names, args, context=None): if context is None: context = {} - res = dict.fromkeys(ids) + res = {} for menu in self.browse(cr, uid, ids, context=context): res[menu.id] = {} if menu.action and menu.action.type == 'ir.actions.act_window' and menu.action.res_model: - menu_needaction_res = self.pool.get(menu.action.res_model).get_needaction_info(cr, uid, uid, domain=menu.action.domain, context=context) + menu_needaction_res = self.pool.get(menu.action.res_model)._get_needaction_info(cr, uid, uid, domain=menu.action.domain, context=context) # TODO: find the addon that causes a bug on runbot, not on local if not isinstance(menu_needaction_res[1], (int, long)): menu_needaction_res[1] = 0 else: - menu_needaction_res = [False, 0, ()] + menu_needaction_res = [False, 0] res[menu.id]['needaction_enabled'] = menu_needaction_res[0] res[menu.id]['needaction_counter'] = menu_needaction_res[1] - # not used currently, therefore set to a void list - res[menu.id]['needaction_record_ids'] = [] return res _columns = { diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index 313c96a9fb4..bdf78fd3567 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -4866,7 +4866,7 @@ class BaseModel(object): get_xml_id = get_external_id _get_xml_ids = _get_external_ids - def get_needaction_info(self, cr, uid, user_id, limit=None, order=None, domain=False, context=None): + def _get_needaction_info(self, cr, uid, user_id, limit=None, order=None, domain=False, context=None): """Base method for needaction mechanism - see ir.needaction for actual implementation - if the model uses the need action mechanism @@ -4883,16 +4883,18 @@ class BaseModel(object): :return: [uses_needaction=True/False, needaction_uid_ctr=%d] """ if hasattr(self, 'needaction_get_record_ids'): + # Arbitrary limit, but still much lower thant infinity, to avoid + # getting too much data. ids = self.needaction_get_record_ids(cr, uid, user_id, limit=8192, context=context) if not ids: - return [True, 0, []] + return [True, 0] if domain: new_domain = eval(domain, locals_dict={'uid': user_id}) + [('id', 'in', ids)] else: new_domain = [('id', 'in', ids)] - return [True, self.search(cr, uid, new_domain, limit=limit, order=order, count=True, context=context), ids] + return [True, self.search(cr, uid, new_domain, limit=limit, order=order, count=True, context=context)] else: - return [False, 0, []] + return [False, 0] # Transience def is_transient(self): From aca2b4874639b5adbe13e6471b8d9a0d5f59651d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 18 Apr 2012 11:12:59 +0200 Subject: [PATCH 05/16] [FIX] Propagated name change from ir_needaction_users to ir_needaction_users_rel. bzr revid: tde@openerp.com-20120418091259-cnhae6rsc8dqlmkw --- openerp/addons/base/ir/ir_needaction.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/openerp/addons/base/ir/ir_needaction.py b/openerp/addons/base/ir/ir_needaction.py index c408c189939..2ea81870c68 100644 --- a/openerp/addons/base/ir/ir_needaction.py +++ b/openerp/addons/base/ir/ir_needaction.py @@ -87,7 +87,7 @@ class ir_needaction_mixin(osv.osv): validation by a manager, this mechanism allows to set a list of users asked to perform an action. - This class wraps a class (ir.needaction_users) that behaves + This class wraps a class (ir.ir_needaction_users_rel) that behaves like a many2many field. However, no field is added to the model inheriting from this mixin class. This class handles the low-level considerations of updating relationships. Every change made on the @@ -126,7 +126,7 @@ class ir_needaction_mixin(osv.osv): return dict.fromkeys(ids, []) def create(self, cr, uid, values, context=None): - rel_obj = self.pool.get('ir.needaction_users') + rel_obj = self.pool.get('ir.ir_needaction_users_rel') # perform create obj_id = super(ir_needaction_mixin, self).create(cr, uid, values, context=context) # link user_ids @@ -135,7 +135,7 @@ class ir_needaction_mixin(osv.osv): return obj_id def write(self, cr, uid, ids, values, context=None): - rel_obj = self.pool.get('ir.needaction_users') + rel_obj = self.pool.get('ir.ir_needaction_users_rel') # perform write write_res = super(ir_needaction_mixin, self).write(cr, uid, ids, values, context=context) # get and update user_ids @@ -146,7 +146,7 @@ class ir_needaction_mixin(osv.osv): def unlink(self, cr, uid, ids, context=None): # unlink user_ids - rel_obj = self.pool.get('ir.needaction_users') + rel_obj = self.pool.get('ir.ir_needaction_users_rel') rel_obj.unlink_users(cr, uid, ids, self._name, context=context) # perform unlink return super(ir_needaction_mixin, self).unlink(cr, uid, ids, context=context) @@ -159,14 +159,14 @@ class ir_needaction_mixin(osv.osv): """Given the current model and a user_id return the record ids that require the user to perform an action""" - rel_obj = self.pool.get('ir.needaction_users') + rel_obj = self.pool.get('ir.ir_needaction_users_rel') rel_ids = rel_obj.search(cr, uid, [('res_model', '=', self._name), ('user_id', '=', user_id)], limit=limit, context=context) return map(itemgetter('res_id'), rel_obj.read(cr, uid, rel_ids, ['res_id'], context=context)) def needaction_get_action_count(self, cr, uid, user_id, limit=80, context=None): """Given the current model and a user_id get the number of actions it has to perform""" - rel_obj = self.pool.get('ir.needaction_users') + rel_obj = self.pool.get('ir.ir_needaction_users_rel') return rel_obj.search(cr, uid, [('res_model', '=', self._name), ('user_id', '=', user_id)], limit=limit, count=True, context=context) def needaction_get_record_references(self, cr, uid, user_id, offset=None, limit=None, order=None, context=None): @@ -174,7 +174,7 @@ class ir_needaction_mixin(osv.osv): perform an action. Records are given as references, a list of tuples (model_name, record_id). This method is trans-model.""" - rel_obj = self.pool.get('ir.needaction_users') + rel_obj = self.pool.get('ir.ir_needaction_users_rel') rel_ids = rel_obj.search(cr, uid, [('user_id', '=', user_id)], offset=offset, limit=limit, order=order, context=context) return map(itemgetter('res_model', 'res_id'), rel_obj.read(cr, uid, rel_ids, ['res_model', 'res_id'], context=context)) From 7b073bd3861d83fee01e987b151eeef88c8dd314 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 18 Apr 2012 11:13:57 +0200 Subject: [PATCH 06/16] [FIX] Propagated name change from ir_needaction_users to ir_needaction_users_rel. bzr revid: tde@openerp.com-20120418091357-w3364qw5e0fyfjkj --- openerp/addons/base/ir/ir.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/openerp/addons/base/ir/ir.xml b/openerp/addons/base/ir/ir.xml index ccec381f1d1..e66182bae39 100644 --- a/openerp/addons/base/ir/ir.xml +++ b/openerp/addons/base/ir/ir.xml @@ -662,9 +662,9 @@ - - ir.needaction_users.tree - ir.needaction_users + + ir.needaction_users_rel.tree + ir.needaction_users_rel tree 10 @@ -676,14 +676,14 @@ - + Need action relationships - ir.needaction_users + ir.needaction_users_rel form tree,form - + Date: Wed, 18 Apr 2012 11:23:15 +0200 Subject: [PATCH 07/16] [FIX] Fixed wrong model name. bzr revid: tde@openerp.com-20120418092315-c35p0dtazgkgd9ae --- openerp/addons/base/ir/ir_needaction.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/openerp/addons/base/ir/ir_needaction.py b/openerp/addons/base/ir/ir_needaction.py index 2ea81870c68..6dd9cdfdc54 100644 --- a/openerp/addons/base/ir/ir_needaction.py +++ b/openerp/addons/base/ir/ir_needaction.py @@ -35,7 +35,7 @@ class ir_needaction_users_rel(osv.osv): This model can be seen as a many2many, linking (res_model, res_id) to users (those whose attention is required on the record).''' - _name = 'ir.ir_needaction_users_rel' + _name = 'ir.needaction_users_rel' _description = 'Needaction relationship table' _rec_name = 'id' _order = 'id desc' @@ -126,7 +126,7 @@ class ir_needaction_mixin(osv.osv): return dict.fromkeys(ids, []) def create(self, cr, uid, values, context=None): - rel_obj = self.pool.get('ir.ir_needaction_users_rel') + rel_obj = self.pool.get('ir.needaction_users_rel') # perform create obj_id = super(ir_needaction_mixin, self).create(cr, uid, values, context=context) # link user_ids @@ -135,7 +135,7 @@ class ir_needaction_mixin(osv.osv): return obj_id def write(self, cr, uid, ids, values, context=None): - rel_obj = self.pool.get('ir.ir_needaction_users_rel') + rel_obj = self.pool.get('ir.needaction_users_rel') # perform write write_res = super(ir_needaction_mixin, self).write(cr, uid, ids, values, context=context) # get and update user_ids @@ -146,7 +146,7 @@ class ir_needaction_mixin(osv.osv): def unlink(self, cr, uid, ids, context=None): # unlink user_ids - rel_obj = self.pool.get('ir.ir_needaction_users_rel') + rel_obj = self.pool.get('ir.needaction_users_rel') rel_obj.unlink_users(cr, uid, ids, self._name, context=context) # perform unlink return super(ir_needaction_mixin, self).unlink(cr, uid, ids, context=context) @@ -159,14 +159,14 @@ class ir_needaction_mixin(osv.osv): """Given the current model and a user_id return the record ids that require the user to perform an action""" - rel_obj = self.pool.get('ir.ir_needaction_users_rel') + rel_obj = self.pool.get('ir.needaction_users_rel') rel_ids = rel_obj.search(cr, uid, [('res_model', '=', self._name), ('user_id', '=', user_id)], limit=limit, context=context) return map(itemgetter('res_id'), rel_obj.read(cr, uid, rel_ids, ['res_id'], context=context)) def needaction_get_action_count(self, cr, uid, user_id, limit=80, context=None): """Given the current model and a user_id get the number of actions it has to perform""" - rel_obj = self.pool.get('ir.ir_needaction_users_rel') + rel_obj = self.pool.get('ir.needaction_users_rel') return rel_obj.search(cr, uid, [('res_model', '=', self._name), ('user_id', '=', user_id)], limit=limit, count=True, context=context) def needaction_get_record_references(self, cr, uid, user_id, offset=None, limit=None, order=None, context=None): @@ -174,7 +174,7 @@ class ir_needaction_mixin(osv.osv): perform an action. Records are given as references, a list of tuples (model_name, record_id). This method is trans-model.""" - rel_obj = self.pool.get('ir.ir_needaction_users_rel') + rel_obj = self.pool.get('ir.needaction_users_rel') rel_ids = rel_obj.search(cr, uid, [('user_id', '=', user_id)], offset=offset, limit=limit, order=order, context=context) return map(itemgetter('res_model', 'res_id'), rel_obj.read(cr, uid, rel_ids, ['res_model', 'res_id'], context=context)) From 22adea444c6614243ce280a795931ce91460ddc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 18 Apr 2012 11:41:01 +0200 Subject: [PATCH 08/16] [ADD] Added a 'needaction_pending' field: this states whether the record has an action pending. bzr revid: tde@openerp.com-20120418094101-o0a9lcpux8b6d75u --- openerp/addons/base/ir/ir_needaction.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/openerp/addons/base/ir/ir_needaction.py b/openerp/addons/base/ir/ir_needaction.py index 6dd9cdfdc54..7270eb2ce54 100644 --- a/openerp/addons/base/ir/ir_needaction.py +++ b/openerp/addons/base/ir/ir_needaction.py @@ -115,6 +115,21 @@ class ir_needaction_mixin(osv.osv): _name = 'ir.needaction_mixin' _description = '"Need action" mixin' + def get_needaction_pending(self, cr, uid, ids, name, arg, context=None): + res = {} + rel_obj = self.pool.get('ir.needaction_users_rel') + for id in ids: + res[id] = rel_obj.search(cr, uid, [('res_model', '=', self._name), ('res_id', '=', id), ('user_id', '=', uid)], limit=1, count=True, context=context) > 0 + return res + + _columns = { + 'needaction_pending': fields.function(get_needaction_pending, type='boolean', + string='Need action pending', + help='If True, this field states that users have to perform an action. \ + This field comes from the needaction mechanism. Please refer \ + to the ir.needaction_mixin class.'), + } + #------------------------------------------------------ # Addon API #------------------------------------------------------ From 9012e82d774ce96d0fafaa41b56e39dd3b4ab6c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 18 Apr 2012 11:45:54 +0200 Subject: [PATCH 09/16] [IMP] 'needaction_pending' field: not necessary to read a table as the information is already available in object. bzr revid: tde@openerp.com-20120418094554-n5p2wf599tbmhizi --- openerp/addons/base/ir/ir_needaction.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openerp/addons/base/ir/ir_needaction.py b/openerp/addons/base/ir/ir_needaction.py index 7270eb2ce54..7ed9762f8db 100644 --- a/openerp/addons/base/ir/ir_needaction.py +++ b/openerp/addons/base/ir/ir_needaction.py @@ -117,9 +117,9 @@ class ir_needaction_mixin(osv.osv): def get_needaction_pending(self, cr, uid, ids, name, arg, context=None): res = {} - rel_obj = self.pool.get('ir.needaction_users_rel') + needaction_user_ids = self.get_needaction_user_ids(cr, uid, ids, context=context) for id in ids: - res[id] = rel_obj.search(cr, uid, [('res_model', '=', self._name), ('res_id', '=', id), ('user_id', '=', uid)], limit=1, count=True, context=context) > 0 + res[id] = uid in needaction_user_ids[id] return res _columns = { From 49f785c74ec6e6aa5df27439e8bf8860e23df58d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 18 Apr 2012 11:58:54 +0200 Subject: [PATCH 10/16] [DOC] Improved doc, adding reference to the needaction_pending field. bzr revid: tde@openerp.com-20120418095854-29o6qupaoi741imr --- doc/api/need_action_specs.rst | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/doc/api/need_action_specs.rst b/doc/api/need_action_specs.rst index 88787ae4c44..de1b75a5fb1 100644 --- a/doc/api/need_action_specs.rst +++ b/doc/api/need_action_specs.rst @@ -1,6 +1,8 @@ Need action mechanism ===================== +.. versionadded:: openobject-server.4124 + ir.needaction_mixin mixin class +++++++++++++++++++++++++++++++ @@ -15,15 +17,37 @@ Objects using the need_action feature should override the ``get_needaction_user_ This class also offers several global services,: - ``needaction_get_record_ids``: for the current model and uid, get all record ids that ask this user to perform an action. This mechanism is used for instance to display the number of pending actions in menus, such as Leads (12) - ``needaction_get_action_count``: as ``needaction_get_record_ids`` but returns only the number of action, not the ids (performs a search with count=True) - - ``needaction_get_user_record_references``: for a given uid, get all the records that ask this user to perform an action. Records are given as references, a list of tuples (model_name, record_id) + - ``needaction_get_user_record_references``: for a given uid, get all the records that ask this user to perform an action. Records are given as references, a list of tuples (model_name, record_id). + +.. versionadded:: openobject-server.XXXX + +This revision of the needaction_mixin mechanism slighty modifies the class behavior. The ``ir_needaction_mixin`` class now adds a function field on model inheriting from the class. This field allows to state whether a given record has a needaction for the current user. This is usefull if you want to customize views according to the needaction feature. For example, you may want to set records in bold in a list view if the current user has an action to perform on the record. This makes the class not a pure abstract class, but allows to easily use the action information. The field definition is:: + + + def get_needaction_pending(self, cr, uid, ids, name, arg, context=None): + res = {} + needaction_user_ids = self.get_needaction_user_ids(cr, uid, ids, context=context) + for id in ids: + res[id] = uid in needaction_user_ids[id] + return res + + _columns = { + 'needaction_pending': fields.function(get_needaction_pending, type='boolean', + string='Need action pending', + help='If True, this field states that users have to perform an action. \ + This field comes from the needaction mechanism. Please refer \ + to the ir.needaction_mixin class.'), + } Menu modification +++++++++++++++++ +.. versionchanged:: openobject-server.XXXX + This revision adds three functional fields to ``ir.ui.menu`` model : - ``uses_needaction``: boolean field. If the menu entry action is an act_window action, and if this action is related to a model that uses the need_action mechanism, this field is set to true. Otherwise, it is false. - ``needaction_uid_ctr``: integer field. If the target model uses the need action mechanism, this field gives the number of actions the current user has to perform. - - ``needaction_record_ids``: many2many field. If the target model uses the need action mechanism, this field holds the ids of the record requesting the user to perform an action. + - ``needaction_record_ids``: many2many field. If the target model uses the need action mechanism, this field holds the ids of the record requesting the user to perform an action. **This field has been removed on version XXXX**. Those fields are functional, because they depend on the user and must therefore be computed at every refresh, each time menus are displayed. The use of the need action mechanism is done by taking into account the action domain in order to display accurate results. When computing the value of the functional fields, the ids of records asking the user to perform an action is concatenated to the action domain. A counting search is then performed on the model, giving back the number of action the users has to perform, limited to the domain of the action. From a787430ba943af3a7e7e71b3ddcb0c76d00d5d1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 18 Apr 2012 13:10:16 +0200 Subject: [PATCH 11/16] [DOC] Small doc update. bzr revid: tde@openerp.com-20120418111016-fgmvnq85esyhvw87 --- doc/api/need_action_specs.rst | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/doc/api/need_action_specs.rst b/doc/api/need_action_specs.rst index de1b75a5fb1..111c156ac79 100644 --- a/doc/api/need_action_specs.rst +++ b/doc/api/need_action_specs.rst @@ -1,16 +1,18 @@ Need action mechanism ===================== -.. versionadded:: openobject-server.4124 +.. versionadded:: 7.0 -ir.needaction_mixin mixin class -+++++++++++++++++++++++++++++++ +ir.needaction_mixin class ++++++++++++++++++++++++++ + +.. versionadded:: openobject-server.4124 This revision adds a mixin class for objects using the need action feature. Need action feature can be used by objects willing to be able to signal that an action is required on a particular record. If in the business logic an action must be performed by somebody, for instance validation by a manager, this mechanism allows to set a list of users asked to perform an action. -This class wraps a class (ir.needaction_users) that behaves like a many2many field. However, no field is added to the model inheriting from ir.needaction_mixin. The mixin class manages the low-level considerations of updating relationships. Every change made on the record calls a method that updates the relationships. +This class wraps a class (ir.ir_needaction_users_rel) that behaves like a many2many field. However, no field is added to the model inheriting from ir.needaction_mixin. The mixin class manages the low-level considerations of updating relationships. Every change made on the record calls a method that updates the relationships. Objects using the need_action feature should override the ``get_needaction_user_ids`` method. This methods returns a dictionary whose keys are record ids, and values a list of user ids, like in a many2many relationship. Therefore by defining only one method, you can specify if an action is required by defining the users that have to do it, in every possible situation. @@ -39,6 +41,23 @@ This revision of the needaction_mixin mechanism slighty modifies the class behav to the ir.needaction_mixin class.'), } +ir.needaction_users_rel class ++++++++++++++++++++++++++++++ + +.. versionadded:: openobject-server.4124 + +This class essentially wraps a database table that behaves like a many2many. +It holds data related to the needaction mechanism inside OpenERP. A row +in this model is characterized by: + + - ``res_model``: model of the record requiring an action + - ``res_id``: ID of the record requiring an action + - ``user_id``: foreign key to the res.users table, to the user that + has to perform the action + +This model can be seen as a many2many, linking (res_model, res_id) to +users (those whose attention is required on the record) + Menu modification +++++++++++++++++ From 8d858cf0b02e4e9a053ac3c4dd1438afe1c6278a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 18 Apr 2012 13:12:12 +0200 Subject: [PATCH 12/16] [TMP] Uncommit temp fix. bzr revid: tde@openerp.com-20120418111212-j035lajdu2g045kf --- openerp/addons/base/ir/ir_ui_menu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openerp/addons/base/ir/ir_ui_menu.py b/openerp/addons/base/ir/ir_ui_menu.py index 0de4ff74c11..e861f8231ca 100644 --- a/openerp/addons/base/ir/ir_ui_menu.py +++ b/openerp/addons/base/ir/ir_ui_menu.py @@ -264,7 +264,7 @@ class ir_ui_menu(osv.osv): if menu.action and menu.action.type == 'ir.actions.act_window' and menu.action.res_model: menu_needaction_res = self.pool.get(menu.action.res_model)._get_needaction_info(cr, uid, uid, domain=menu.action.domain, context=context) # TODO: find the addon that causes a bug on runbot, not on local - if not isinstance(menu_needaction_res[1], (int, long)): menu_needaction_res[1] = 0 + #if not isinstance(menu_needaction_res[1], (int, long)): menu_needaction_res[1] = 0 else: menu_needaction_res = [False, 0] res[menu.id]['needaction_enabled'] = menu_needaction_res[0] From 78883771bdb16b9d2c4c926162fc1d5dfab0e068 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 18 Apr 2012 13:25:43 +0200 Subject: [PATCH 13/16] [DOC] Added comments in code. bzr revid: tde@openerp.com-20120418112543-g5jbcn2h5mgrbxtc --- openerp/addons/base/ir/ir_needaction.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/openerp/addons/base/ir/ir_needaction.py b/openerp/addons/base/ir/ir_needaction.py index 7ed9762f8db..cb6d29d1715 100644 --- a/openerp/addons/base/ir/ir_needaction.py +++ b/openerp/addons/base/ir/ir_needaction.py @@ -111,7 +111,15 @@ class ir_needaction_mixin(osv.osv): - ``needaction_get_user_record_references``: for a given uid, get all the records that ask this user to perform an action. Records are given as references, a list of tuples (model_name, record_id) - ''' + + The ``ir_needaction_mixin`` class adds a function field on model inheriting + from the class. This field allows to state whether a given record has + a needaction for the current user. This is usefull if you want to customize + views according to the needaction feature. For example, you may want to + set records in bold in a list view if the current user has an action to + perform on the record. This makes the class not a pure abstract class, + but allows to easily use the action information.''' + _name = 'ir.needaction_mixin' _description = '"Need action" mixin' From 3da67c851fa3333215dfa9afb8faa2be032dac11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 18 Apr 2012 13:37:06 +0200 Subject: [PATCH 14/16] [REM] Removed unused field in menus. BTW it solves a GTk client crash. bzr revid: tde@openerp.com-20120418113706-raesdy1tak5dwkwo --- openerp/addons/base/ir/ir_ui_menu.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openerp/addons/base/ir/ir_ui_menu.py b/openerp/addons/base/ir/ir_ui_menu.py index e861f8231ca..f73496cb230 100644 --- a/openerp/addons/base/ir/ir_ui_menu.py +++ b/openerp/addons/base/ir/ir_ui_menu.py @@ -289,7 +289,6 @@ class ir_ui_menu(osv.osv): 'web_icon_hover_data':fields.function(_get_image_icon, string='Web Icon Image (hover)', type='binary', readonly=True, store=True, multi='icon'), 'needaction_enabled': fields.function(_get_needaction, string='Target model uses the need action mechanism', type='boolean', help='If the menu entry action is an act_window action, and if this action is related to a model that uses the need_action mechanism, this field is set to true. Otherwise, it is false.', multi='_get_needaction'), 'needaction_counter': fields.function(_get_needaction, string='Number of actions the user has to perform', type='integer', help='If the target model uses the need action mechanism, this field gives the number of actions the current user has to perform.', multi='_get_needaction'), - 'needaction_record_ids': fields.function(_get_needaction, string='Ids of records requesting an action from the user', type='many2many', help='If the target model uses the need action mechanism, this field holds the ids of the record requesting the user to perform an action.', multi='_get_needaction'), 'action': fields.function(_action, fnct_inv=_action_inv, type='reference', string='Action', selection=[ From d029e2734c65624083eeb593a9684e35d31e7b47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 18 Apr 2012 13:37:48 +0200 Subject: [PATCH 15/16] [DOC] Small doc update. bzr revid: tde@openerp.com-20120418113748-vsdtsype2l94wbl5 --- doc/api/need_action_specs.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api/need_action_specs.rst b/doc/api/need_action_specs.rst index 111c156ac79..a3a8ba04bf0 100644 --- a/doc/api/need_action_specs.rst +++ b/doc/api/need_action_specs.rst @@ -66,7 +66,7 @@ Menu modification This revision adds three functional fields to ``ir.ui.menu`` model : - ``uses_needaction``: boolean field. If the menu entry action is an act_window action, and if this action is related to a model that uses the need_action mechanism, this field is set to true. Otherwise, it is false. - ``needaction_uid_ctr``: integer field. If the target model uses the need action mechanism, this field gives the number of actions the current user has to perform. - - ``needaction_record_ids``: many2many field. If the target model uses the need action mechanism, this field holds the ids of the record requesting the user to perform an action. **This field has been removed on version XXXX**. + - **REMOVED** ``needaction_record_ids``: many2many field. If the target model uses the need action mechanism, this field holds the ids of the record requesting the user to perform an action. **This field has been removed on version XXXX**. Those fields are functional, because they depend on the user and must therefore be computed at every refresh, each time menus are displayed. The use of the need action mechanism is done by taking into account the action domain in order to display accurate results. When computing the value of the functional fields, the ids of records asking the user to perform an action is concatenated to the action domain. A counting search is then performed on the model, giving back the number of action the users has to perform, limited to the domain of the action. From 110fbacb2a085fe26ec3032b5e1e0edf93ebc8e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 18 Apr 2012 13:50:50 +0200 Subject: [PATCH 16/16] [REM] Removed comment and now unnecessary code. bzr revid: tde@openerp.com-20120418115050-jwtyqg9iahakyu13 --- openerp/addons/base/ir/ir_ui_menu.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/openerp/addons/base/ir/ir_ui_menu.py b/openerp/addons/base/ir/ir_ui_menu.py index f73496cb230..15d250a7489 100644 --- a/openerp/addons/base/ir/ir_ui_menu.py +++ b/openerp/addons/base/ir/ir_ui_menu.py @@ -263,8 +263,6 @@ class ir_ui_menu(osv.osv): res[menu.id] = {} if menu.action and menu.action.type == 'ir.actions.act_window' and menu.action.res_model: menu_needaction_res = self.pool.get(menu.action.res_model)._get_needaction_info(cr, uid, uid, domain=menu.action.domain, context=context) - # TODO: find the addon that causes a bug on runbot, not on local - #if not isinstance(menu_needaction_res[1], (int, long)): menu_needaction_res[1] = 0 else: menu_needaction_res = [False, 0] res[menu.id]['needaction_enabled'] = menu_needaction_res[0]