diff --git a/openerp/addons/base/ir/ir.xml b/openerp/addons/base/ir/ir.xml
index fac2946e204..c554a8896aa 100644
--- a/openerp/addons/base/ir/ir.xml
+++ b/openerp/addons/base/ir/ir.xml
@@ -556,30 +556,6 @@
-
-
- ir.needaction_users_rel.tree
- ir.needaction_users_rel
- 10
-
-
-
-
-
-
-
-
-
-
- Need action relationships
- ir.needaction_users_rel
- form
- tree,form
-
-
-
-
-
ir.ui.view
diff --git a/openerp/addons/base/ir/ir_needaction.py b/openerp/addons/base/ir/ir_needaction.py
index 0f28c33ded9..9112d60c1f4 100644
--- a/openerp/addons/base/ir/ir_needaction.py
+++ b/openerp/addons/base/ir/ir_needaction.py
@@ -19,180 +19,49 @@
#
##############################################################################
-import openerp.pooler as pooler
-from operator import itemgetter
from osv import osv, fields
-from tools.translate import _
-
-class ir_needaction_users_rel(osv.Model):
- ''' 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_rel'
- _description = 'Needaction relationship table'
- _rec_name = 'id'
- _order = 'id desc'
- _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',
- ondelete='cascade', select=1, required=True),
- }
-
- 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 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"""
- 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)
- return True
-
- 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"""
- 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)
-
- def update_users(self, cr, uid, res_ids, res_model, user_ids, context=None):
- """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):
- return True
- # unlink old records
- self.unlink_users(cr, uid, res_ids, res_model, context=context)
- # link new records
- self.create_users(cr, uid, res_ids, res_model, user_ids, context=context)
- return True
-
class ir_needaction_mixin(osv.Model):
'''Mixin class for objects using the need action feature.
-
- Need action feature can be used by objects having 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
+
+ Need action feature can be used by objects having 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.ir_needaction_users_rel) that
- behaves like a many2many field. 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
- ``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_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)
+
+ Objects using the 'need_action' feature should override the
+ ``needaction_domain_get`` method. This methods returns a
+ domain to filter records requiring an action for a specific user.
+
+ This class also offers several global services:
- ``needaction_get_action_count``: as ``needaction_get_record_ids``
- but returns only the number of action, not the ids (performs a
+ but returns only the number of action, not the ids (performs a
search with count=True)
+ '''
- The ``ir_needaction_mixin`` class adds a calculated field
- ``needaction_pending``. This function 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. '''
-
_name = 'ir.needaction_mixin'
- _description = 'Need action mixin'
-
- 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
+ _needaction = True
- def search_needaction_pending(self, cr, uid, self_again, field_name, criterion, context=None):
- ids = self.needaction_get_record_ids(
- cr, uid, uid, limit=1024, context=context)
- return [('id', 'in', ids)]
-
- _columns = {
- 'needaction_pending': fields.function(
- get_needaction_pending, type='boolean',
- fnct_search=search_needaction_pending,
- string='Need action pending',
- help="If True, this field states that users have to perform an " \
- "action This field comes from the ir.needaction_mixin class."),
- }
-
#------------------------------------------------------
# Addon API
#------------------------------------------------------
-
- def get_needaction_user_ids(self, cr, uid, ids, context=None):
- """ Returns the user_ids that have to perform an action
- :return: dict { record_id: [user_ids], }
+
+ def _needaction_domain_get(self, cr, uid, context=None):
+ """ Returns the domain to filter records that require an action
+ :return: domain or False is no need action
"""
- return dict((id,list()) for id in ids)
-
- def create(self, cr, uid, values, context=None):
- 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
- needaction_user_ids = self.get_needaction_user_ids(cr, uid, [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):
- 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
- needaction_user_ids = self.get_needaction_user_ids(cr, uid, ids, context=context)
- for id in ids:
- 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
- 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)
-
+ return False
+
#------------------------------------------------------
# "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
- return the record ids that require the user to perform an
- action"""
- 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):
+
+ def _needaction_count(self, cr, uid, domain=[], 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')
- return rel_obj.search(cr, uid, [('res_model', '=', self._name), ('user_id', '=', user_id)], limit=limit, count=True, context=context)
+ dom = self._needaction_domain_get(cr, uid, context=context)
+ if dom is False:
+ return 0
+ return self.search(cr, uid, domain+dom, context=context, count=True)
-# 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 77c0c4d738a..f5e4ee2ba05 100644
--- a/openerp/addons/base/ir/ir_ui_menu.py
+++ b/openerp/addons/base/ir/ir_ui_menu.py
@@ -255,23 +255,24 @@ class ir_ui_menu(osv.osv):
return res
- def _get_needaction_info(self, cr, uid, id, domain=[], context={}):
- return [False, 0]
-
def _get_needaction(self, cr, uid, ids, field_names, args, context=None):
if context is None:
context = {}
res = {}
for menu in self.browse(cr, uid, ids, context=context):
+ res[menu.id] = {}
+ res[menu.id]['needaction_enabled'] = False
+ res[menu.id]['needaction_counter'] = False
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)
- else:
- menu_needaction_res = [False, 0]
- res[menu.id]['needaction_enabled'] = menu_needaction_res[0]
- res[menu.id]['needaction_counter'] = menu_needaction_res[1]
+ obj = self.pool.get(menu.action.res_model)
+ if obj._needaction:
+ res[menu.id]['needaction_enabled'] = obj._needaction
+ # check domain and context: should we evaluate the domain ?
+ # and add context of the action ?
+ res[menu.id]['needaction_counter'] = obj._needaction_count(cr, uid, menu.action.domain, context=context)
return res
-
+
_columns = {
'name': fields.char('Menu', size=64, required=True, translate=True),
'sequence': fields.integer('Sequence'),
diff --git a/openerp/addons/base/security/ir.model.access.csv b/openerp/addons/base/security/ir.model.access.csv
index d49aa18db8c..b0218e977bc 100644
--- a/openerp/addons/base/security/ir.model.access.csv
+++ b/openerp/addons/base/security/ir.model.access.csv
@@ -118,6 +118,5 @@
"access_ir_config_parameter","ir_config_parameter","model_ir_config_parameter",,1,0,0,0
"access_ir_mail_server_all","ir_mail_server","model_ir_mail_server",,1,0,0,0
"access_ir_actions_client","ir_actions_client all","model_ir_actions_client",,1,0,0,0
-"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
diff --git a/openerp/osv/fields.py b/openerp/osv/fields.py
index 5a47601f8a5..da025845759 100644
--- a/openerp/osv/fields.py
+++ b/openerp/osv/fields.py
@@ -514,7 +514,10 @@ class one2many(_column):
for id in ids:
res[id] = []
- ids2 = obj.pool.get(self._obj).search(cr, user, self._domain + [(self._fields_id, 'in', ids)], limit=self._limit, context=context)
+ dom = self._domain
+ if isinstance(self._domain, type(lambda: None)):
+ dom = self._domain(obj)
+ ids2 = obj.pool.get(self._obj).search(cr, user, dom + [(self._fields_id, 'in', ids)], limit=self._limit, context=context)
for r in obj.pool.get(self._obj)._read_flat(cr, user, ids2, [self._fields_id], context=context, load='_classic_write'):
if r[self._fields_id] in res:
res[r[self._fields_id]].append(r['id'])
@@ -556,7 +559,10 @@ class one2many(_column):
reverse_rel = obj._all_columns.get(self._fields_id)
assert reverse_rel, 'Trying to unlink the content of a o2m but the pointed model does not have a m2o'
# if the o2m has a static domain we must respect it when unlinking
- extra_domain = self._domain if isinstance(getattr(self, '_domain', None), list) else []
+ dom = self._domain
+ if isinstance(self._domain, type(lambda: None)):
+ dom = self._domain(obj)
+ extra_domain = dom or []
ids_to_unlink = obj.search(cr, user, [(self._fields_id,'=',id)] + extra_domain, context=context)
# If the model has cascade deletion, we delete the rows because it is the intended behavior,
# otherwise we only nullify the reverse foreign key column.
@@ -574,7 +580,10 @@ class one2many(_column):
return result
def search(self, cr, obj, args, name, value, offset=0, limit=None, uid=None, operator='like', context=None):
- return obj.pool.get(self._obj).name_search(cr, uid, value, self._domain, operator, context=context,limit=limit)
+ dom = self._domain
+ if isinstance(self._domain, type(lambda: None)):
+ dom = self._domain(obj)
+ return obj.pool.get(self._obj).name_search(cr, uid, value, dom, operator, context=context,limit=limit)
@classmethod
diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py
index 7c8085ea8ed..368a37f455a 100644
--- a/openerp/osv/orm.py
+++ b/openerp/osv/orm.py
@@ -666,6 +666,7 @@ class BaseModel(object):
_order = 'id'
_sequence = None
_description = None
+ _needaction = False
# dict of {field:method}, with method returning the name_get of records
# to include in the _read_group, if grouped on this field
@@ -4960,37 +4961,7 @@ class BaseModel(object):
# backwards compatibility
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):
- """Base method for needaction mechanism
- - see ir.needaction for actual implementation
- - if the model uses the need action mechanism
- (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
- records requesting an action
- - count the number of records maching that domain, that
- is the number of actions the user has to perform
- - this method returns default values
- :param: model_name: the name of the model (ex: hr.holidays)
- :param: user_id: the id of user
- :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]
- 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)]
- else:
- return [False, 0]
-
+
# Transience
def is_transient(self):
""" Return whether the model is transient.