diff --git a/doc/api/need_action_specs.rst b/doc/api/need_action_specs.rst new file mode 100644 index 00000000000..5cffe05be2c --- /dev/null +++ b/doc/api/need_action_specs.rst @@ -0,0 +1,34 @@ +Need action mixin class +======================= + +This revision adds a mixin class for objects using the need action feature. Need action mechanism can be used by objects that have 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 field with the user_id of the user requested to perform the action. + +This class wraps a table (base.needaction_users_rel) 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. + +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. + +This class also offers several global services,: + - ``needaction_get_user_record_references``: for a given uid, 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 mechanism is used for instance to display the number of pending actions in menus, such as Leads (12). + +A menu in Settings/Users has been added to allows having a quick look to need_action_user_ids. + +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: + +:: + + [...] + _inherit = [base.needaction] + [...] + def get_needaction_user_ids(self, cr, uid, ids, context=None): + # set the list void by default + result = dict.fromkeys(ids, []) + for foo_obj in self.browse(cr, uid, ids, context=context): + # if foo_obj is confirmed: manager is required to perform an action + if foo_obj.state == 'confirmed': + result[foo_obj.id] = [foo_obj.manager_id] + return result diff --git a/doc/index.rst.inc b/doc/index.rst.inc index d208bf82eb1..80873a696ec 100644 --- a/doc/index.rst.inc +++ b/doc/index.rst.inc @@ -14,3 +14,4 @@ New feature merges :maxdepth: 1 api/user_img_specs + api/need_action_specs diff --git a/openerp/addons/base/__init__.py b/openerp/addons/base/__init__.py index 16a61521fb4..af551e60c1e 100644 --- a/openerp/addons/base/__init__.py +++ b/openerp/addons/base/__init__.py @@ -25,6 +25,7 @@ import res import publisher_warranty import report import test +import base_needaction # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/addons/base/__openerp__.py b/openerp/addons/base/__openerp__.py index 802a0510e28..67920c7239a 100644 --- a/openerp/addons/base/__openerp__.py +++ b/openerp/addons/base/__openerp__.py @@ -40,6 +40,7 @@ ], 'update_xml': [ 'base_update.xml', + 'base_needaction_view.xml', 'ir/wizard/wizard_menu_view.xml', 'ir/ir.xml', 'ir/ir_config_parameter_view.xml', diff --git a/openerp/addons/base/base_needaction.py b/openerp/addons/base/base_needaction.py new file mode 100644 index 00000000000..62a36162e89 --- /dev/null +++ b/openerp/addons/base/base_needaction.py @@ -0,0 +1,174 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2009-Today OpenERP SA () +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see +# +############################################################################## + +from osv import osv, fields +from tools.translate import _ + +class base_needaction_users_rel(osv.osv): + ''' + base_needaction_users_rel holds data related to the needaction + mechanism inside OpenERP. A needaction 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 + ''' + + _name = 'base.needaction_users_rel' + _rec_name = 'id' + _order = 'res_model asc' + _columns = { + 'res_model': fields.char('Related Document Model', size=128, + select=1, required=True), + 'res_id': fields.integer('Related Document ID', + select=1, required=True), + 'user_id': fields.many2one('res.users', string='Related User ID', + ondelete='cascade', select=1, required=True), + } + + +class base_needaction(osv.osv): + '''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 ot perform an action. + + This class wraps a table (base.needaction_users_rel) 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. + + 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. + + This class also offers several global services,: + - ``needaction_get_user_record_references``: for a given uid, 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 mechanism is used for instance to display the number of pending + actions in menus, such as Leads (12). + ''' + _name = 'base.needaction' + _description = 'Need action mechanism' + + _columns = { + } + + #------------------------------------------------------ + # need action relationship management + #------------------------------------------------------ + + def _link_users(self, cr, uid, ids, user_ids, context=None): + if context is None: + context = {} + needact_rel_obj = self.pool.get('base.needaction_users_rel') + for id in ids: + for user_id in user_ids: + needact_rel_obj.create(cr, uid, {'res_model': self._name, 'res_id': id, 'user_id': user_id}, context=context) + return True + + def _unlink_users(self, cr, uid, ids, context=None): + if context is None: + context = {} + needact_rel_obj = self.pool.get('base.needaction_users_rel') + to_del_ids = needact_rel_obj.search(cr, uid, [('res_model', '=', self._name), ('res_id', 'in', ids)], context=context) + return needact_rel_obj.unlink(cr, uid, to_del_ids, context=context) + + def _update_users(self, cr, uid, ids, user_ids, context=None): + if context is None: + context = {} + # unlink old records + self._unlink_users(cr, uid, ids, context=context) + # link new records + for res_id in ids: + self._link_users(cr, uid, ids, user_ids, context=context) + return True + + #------------------------------------------------------ + # Addon API + #------------------------------------------------------ + + def get_needaction_user_ids(self, cr, uid, ids, context=None): + result = dict.fromkeys(ids, []) + return result + + def create(self, cr, uid, values, context=None): + if context is None: + context = {} + # perform create + obj_id = super(base_needaction, self).create(cr, uid, values, context=context) + # link user_ids + needaction_user_ids = self.get_needaction_user_ids(cr, uid, [obj_id], context=context) + self._update_users(cr, uid, [obj_id], needaction_user_ids[obj_id], context=context) + return obj_id + + def write(self, cr, uid, ids, values, context=None): + if context is None: + context = {} + # perform write + write_res = super(base_needaction, 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: + self._update_users(cr, uid, [id], needaction_user_ids[id], context=context) + return write_res + + def unlink(self, cr, uid, ids, context=None): + if context is None: + context = {} + # unlink user_ids + self._unlink_users(cr, uid, ids, context=context) + # perform unlink + return super(base_needaction, self).unlink(cr, uid, ids, context=context) + + #------------------------------------------------------ + # General API + #------------------------------------------------------ + + def needaction_get_user_needaction_ids(self, cr, uid, user_id, offset=0, limit=None, order=None, count=False, context=None): + if context is None: + context = {} + needact_rel_obj = self.pool.get('base.needaction_users_rel') + search_res = needact_rel_obj.search(cr, uid, [('user_id', '=', user_id)], offset=offset, limit=limit, order=order, count=count, context=context) + return search_res + + def needaction_get_user_record_references(self, cr, uid, user_id, offset=0, limit=None, order=None, context=None): + '''for a given uid, 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).''' + if context is None: + context = {} + needact_rel_obj = self.pool.get('base.needaction_users_rel') + needact_obj_ids = self.get_user_needaction_ids(cr, uid, user_id, offset=offset, limit=limit, order=order, context=context) + needact_objs = needact_rel_obj.browse(cr, uid, needact_obj_ids, context=context) + record_references = [(needact_obj.res_model, needact_obj.res_id) for needact_obj in needact_objs] + return record_references + + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/addons/base/base_needaction_view.xml b/openerp/addons/base/base_needaction_view.xml new file mode 100644 index 00000000000..84f7612cac9 --- /dev/null +++ b/openerp/addons/base/base_needaction_view.xml @@ -0,0 +1,29 @@ + + + + + + base.needaction_users_rel.tree + base.needaction_users_rel + tree + 10 + + + + + + + + + + + Need action relationships + base.needaction_users_rel + form + tree,form + + + + + + diff --git a/openerp/addons/base/res/__init__.py b/openerp/addons/base/res/__init__.py index 8bfaba2fe24..2b4da739f54 100644 --- a/openerp/addons/base/res/__init__.py +++ b/openerp/addons/base/res/__init__.py @@ -34,7 +34,6 @@ import res_request import res_lang import res_log import res_widget -import res_needaction import ir_property import wizard diff --git a/openerp/addons/base/res/res_needaction.py b/openerp/addons/base/res/res_needaction.py deleted file mode 100644 index 529444b6e4e..00000000000 --- a/openerp/addons/base/res/res_needaction.py +++ /dev/null @@ -1,67 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2009-Today OpenERP SA () -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see -# -############################################################################## - -import time -from osv import osv, fields -from tools.translate import _ - -class res_needaction(osv.osv): - '''Mixin class for object implementing a need_action mechanism - - Need action mechanism can be used by objects wanting 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 add have a field - linking to the user requested to perform the action. - - Technically, this class adds a need_action_user_id field; when - set to false, no action is required; when an user_id is set, - this user has an action to perform. Setting an user_id is done - through redefining the get_needaction_user_id method, that - stores the need_action_user_id function fields. - - This mechanism is used for instance to display the number of pending action - in menus. - ''' - _name = 'res.needaction' - _description = 'Need action Engine' - - def get_needaction_user_id(self, cr, uid, ids, name, arg, context=None): - if context is None: - context = {} - result = {} - for obj in self.browse(cr, uid, ids, context=context): - result[obj.id] = False - return result - - ''' Wrapper: in 6.1 the reference to a method is given to a function - field, not the function name. Inheritance is therefore not directly - possible.''' - def get_needaction_user_id_wrapper(self, cr, uid, ids, name, arg, context=None): - return self.get_needaction_user_id(cr, uid, ids, name, arg, context=context) - - _columns = { - 'need_action_user_id': fields.function(get_needaction_user_id_wrapper, - type='many2one', relation='res.users', store=True, - select=1, string='User'), - } - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: