[MERGE] Merged with fix from need_action branch

bzr revid: tde@openerp.com-20120403074906-1m4yjhz60i9f5tsk
bzr revid: tde@openerp.com-20120403075529-nyp3tq6xafig088w
This commit is contained in:
Thibault Delavallée 2012-04-03 09:55:29 +02:00
commit e12dff66de
4 changed files with 43 additions and 53 deletions

View File

@ -4,25 +4,26 @@ Need action mechanism
base.needaction 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 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 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 ask this user to perform an action. Records are given as references, a list of tuples (model_name, record_id).
- ``needaction_get_record_ids``: for a given model_name 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_record_ids``: for a given model_name 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)
A menu in Settings/Users has been added to allow having a quick look to need_action_user_ids.
- ``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)
Menu modification
+++++++++++++++++
This revision adds two functional fields to ``ir.ui.menu`` model :
- ``uses_needaction``: boolean field. If the menu entry is related to a act_window action, and 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 related model uses the need action mechanism, this field gives the number of actions the current user has to perform.
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.
Those fields are functional, because they must be recalculated for each user, and each time menus are displayed. ``needaction_uid_ctr`` takes into account the domain of the action, in order to display accurate numbers.
@ -37,9 +38,9 @@ In your ``foo`` module, you want to specify that when it is in state ``confirmed
_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):
# set the list void by default
result[foo_obj.id] = []
# if foo_obj is confirmed: manager is required to perform an action
if foo_obj.state == 'confirmed':

View File

@ -20,12 +20,13 @@
##############################################################################
import openerp.pooler as pooler
from operator import itemgetter
from osv import osv, fields
from tools.translate import _
class ir_needaction_users_rel(osv.osv):
'''
base_needaction_users_rel holds data related to the needaction
ir_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
@ -36,7 +37,7 @@ class ir_needaction_users_rel(osv.osv):
_name = 'ir.needaction_users_rel'
_description = 'Needaction relationship table'
_rec_name = 'id'
_order = 'res_model asc'
_order = 'id desc'
_columns = {
'res_model': fields.char('Related Document Model', size=128,
select=1, required=True),
@ -54,7 +55,7 @@ class ir_needaction(osv.osv):
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.
users asked to 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
@ -70,16 +71,16 @@ class ir_needaction(osv.osv):
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 ask this user to perform an action. Records
are given as references, a list of tuples (model_name, record_id).
- ``needaction_get_record_ids``: for a given model_name 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).
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)
'''
_name = 'ir.needaction'
_description = 'Need action of users on records API'
@ -91,14 +92,12 @@ class ir_needaction(osv.osv):
# need action relationship management
#------------------------------------------------------
def _get_user_ids(self, cr, uid, ids, context=None):
"""Given ids of model self._name, find the user_ids that have an action to perform"""
def _get_users(self, cr, uid, ids, context=None):
if context is None:
context = {}
needact_obj = self.pool.get('ir.needaction_users_rel')
needact_ids = needact_obj.search(cr, uid, [('res_model', '=', self._nanme), ('res_id', 'in', ids)], context=context)
needacts = needact_obj.read(cr, uid, needact_ids, context=context)
return [needact['user_id'] for needact in needacts]
needact_ids = needact_obj.search(cr, uid, [('res_model', '=', self._name), ('res_id', 'in', ids)], context=context)
return needact_obj.read(cr, uid, needact_ids, context=context)
def _link_users(self, cr, uid, ids, user_ids, context=None):
"""Given ids of model self._name, add user_ids to the relationship table"""
@ -120,8 +119,10 @@ class ir_needaction(osv.osv):
def _update_users(self, cr, uid, ids, user_ids, context=None):
"""Given ids of model self._name, update their entries in the relationship table to user_ids"""
if context is None:
context = {}
# read current records
cur_needact_objs = self._get_users(cr, uid, ids, context=None)
if len(cur_needact_objs) == len(user_ids) and all([cur_needact_obj['user_id'] in user_ids for cur_needact_obj in cur_needact_objs]):
return True
# unlink old records
self._unlink_users(cr, uid, ids, context=context)
# link new records
@ -142,7 +143,7 @@ class ir_needaction(osv.osv):
obj_id = super(ir_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)
self._link_users(cr, uid, [obj_id], needaction_user_ids[obj_id], context=context)
return obj_id
def write(self, cr, uid, ids, values, context=None):
@ -168,17 +169,6 @@ class ir_needaction(osv.osv):
# Need action API
#------------------------------------------------------
@classmethod
def needaction_get_user_ids(cls, cr, uid, model_name, user_id, context=None):
"""Given a model
get the user_ids that have to perform at least one action"""
if context is None:
context = {}
need_act_obj = pooler.get_pool(cr.dbname).get('ir.needaction_users_rel')
need_act_ids = need_act_obj.search(cr, uid, [('res_model', '=', model)], context=context)
need_acts = need_act_obj.read(cr, uid, need_act_ids, context=context)
return list(set([need_act['user_id'] for need_act in need_acts]))
@classmethod
def needaction_get_record_ids(cls, cr, uid, model_name, user_id, limit=80, context=None):
"""Given a model and a user_id
@ -187,7 +177,7 @@ class ir_needaction(osv.osv):
context = {}
need_act_obj = pooler.get_pool(cr.dbname).get('ir.needaction_users_rel')
need_act_ids = need_act_obj.search(cr, uid, [('res_model', '=', model_name), ('user_id', '=', user_id)], limit=limit, context=context)
return [need_act['res_id'] for need_act in need_act_obj.read(cr, uid, need_act_ids, context=context)]
return map(itemgetter('res_id'), need_act_obj.read(cr, uid, need_act_ids, context=context))
@classmethod
def needaction_get_action_count(cls, cr, uid, model_name, user_id, limit=80, context=None):
@ -200,8 +190,7 @@ class ir_needaction(osv.osv):
@classmethod
def needaction_get_record_references(cls, cr, uid, user_id, offset=None, limit=None, order=None, context=None):
"""General method
For a given user_id, get all the records that asks this user to
"""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."""
@ -209,9 +198,7 @@ class ir_needaction(osv.osv):
context = {}
need_act_obj = pooler.get_pool(cr.dbname).get('ir.needaction_users_rel')
need_act_ids = need_act_obj.search(cr, uid, [('user_id', '=', user_id)], offset=offset, limit=limit, order=order, context=context)
need_acts = need_act_obj.browse(cr, uid, need_act_ids, context=context)
record_references = [(need_act.res_model, need_act.res_id) for need_act in need_acts]
return record_references
need_acts = need_act_obj.read(cr, uid, need_act_ids, context=context)
return map(itemgetter('res_model', 'id'), need_acts)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -264,9 +264,10 @@ 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 = osv.osv.get_needaction_info(cr, uid, menu.action.res_model, uid, domain=menu.action.domain, context=context)
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]
res[menu.id]['needaction_record_ids'] = menu_needaction_res[2]
return res
_columns = {
@ -285,8 +286,9 @@ class ir_ui_menu(osv.osv):
'web_icon_hover':fields.char('Web Icon File (hover)', size=128),
'web_icon_data': fields.function(_get_image_icon, string='Web Icon Image', type='binary', readonly=True, store=True, multi='icon'),
'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='Related model uses the need action mechanism', type='boolean', help='If the menu entry is related to a act_window action, and 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 related model uses the need action mechanism, this field gives the number of actions the current user has to perform.', multi='_get_needaction'),
'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=[

View File

@ -715,9 +715,9 @@ class BaseModel(object):
@staticmethod
def get_needaction_info(cr, uid, model_name, user_id, limit=None, order=None, domain=False, context=None):
"""Base method for needaction mechanism
- see base.needaction for actual implementation
- see ir.needaction for actual implementation
- if the model uses the need action mechanism
(hasattr(model_obj, 'needaction_get_record_ids'):
(hasattr(model_obj, 'needaction_get_record_ids')):
- get the record ids on which the user has actions to perform
- evaluate the menu domain
- compose a new domain: menu domain, limited to ids of
@ -731,16 +731,16 @@ class BaseModel(object):
"""
model_obj = pooler.get_pool(cr.dbname).get(model_name)
if hasattr(model_obj, 'needaction_get_record_ids'):
ids = model_obj.needaction_get_record_ids(cr, uid, model_name, user_id, context=context)
ids = model_obj.needaction_get_record_ids(cr, uid, model_name, user_id, limit=8096, context=context)
if not ids:
return [True, 0]
return (True, 0, [])
if domain:
new_domain = eval(domain) + [('id', 'in', ids)]
else:
new_domain = [('ids', 'in', ids)]
return [True, model_obj.search(cr, uid, new_domain, limit=limit, order=order, count=True)]
new_domain = [('id', 'in', ids)]
return (True, model_obj.search(cr, uid, new_domain, limit=limit, order=order, count=True), ids)
else:
return [False, 0]
return (False, 0, [])
def view_init(self, cr, uid, fields_list, context=None):
"""Override this method to do specific things when a view on the object is opened."""