[MERGE] forward port of branch saas-5 up to 9363bc9
[odoo/odoo.git] / addons / base_action_rule / base_action_rule.py
index e74e719..864d283 100644 (file)
@@ -54,10 +54,10 @@ class base_action_rule(osv.osv):
     _order = 'sequence'
 
     _columns = {
-        'name':  fields.char('Rule Name', size=64, required=True),
+        'name':  fields.char('Rule Name', required=True),
         'model_id': fields.many2one('ir.model', 'Related Document Model',
             required=True, domain=[('osv_memory', '=', False)]),
-        'model': fields.related('model_id', 'model', type="char", size=256, string='Model'),
+        'model': fields.related('model_id', 'model', type="char", string='Model'),
         'create_date': fields.datetime('Create Date', readonly=1),
         'active': fields.boolean('Active',
             help="When unchecked, the rule is hidden and will not be executed."),
@@ -96,7 +96,7 @@ class base_action_rule(osv.osv):
             ondelete='restrict',
             domain="[('model_id', '=', model_id.model)]",
             help="If present, this condition must be satisfied before executing the action rule."),
-        'last_run': fields.datetime('Last Run', readonly=1),
+        'last_run': fields.datetime('Last Run', readonly=1, copy=False),
     }
 
     _defaults = {
@@ -152,68 +152,79 @@ class base_action_rule(osv.osv):
 
         return True
 
-    def _wrap_create(self, old_create, model):
-        """ Return a wrapper around `old_create` calling both `old_create` and
-            `_process`, in that order.
-        """
-        def create(cr, uid, vals, context=None, **kwargs):
-            # avoid loops or cascading actions
-            if context and context.get('action'):
-                return old_create(cr, uid, vals, context=context)
-
-            context = dict(context or {}, action=True)
-            new_id = old_create(cr, uid, vals, context=context, **kwargs)
-
-            # retrieve the action rules to run on creation
-            action_dom = [('model', '=', model), ('kind', 'in', ['on_create', 'on_create_or_write'])]
-            action_ids = self.search(cr, uid, action_dom, context=context)
-
-            # check postconditions, and execute actions on the records that satisfy them
-            for action in self.browse(cr, uid, action_ids, context=context):
-                if self._filter(cr, uid, action, action.filter_id, [new_id], context=context):
-                    self._process(cr, uid, action, [new_id], context=context)
-            return new_id
-
-        return create
-
-    def _wrap_write(self, old_write, model):
-        """ Return a wrapper around `old_write` calling both `old_write` and
-            `_process`, in that order.
-        """
-        def write(cr, uid, ids, vals, context=None, **kwargs):
-            # avoid loops or cascading actions
-            if context and context.get('action'):
-                return old_write(cr, uid, ids, vals, context=context, **kwargs)
-
-            context = dict(context or {}, action=True)
-            ids = [ids] if isinstance(ids, (int, long, str)) else ids
-
-            # retrieve the action rules to run on update
-            action_dom = [('model', '=', model), ('kind', 'in', ['on_write', 'on_create_or_write'])]
-            action_ids = self.search(cr, uid, action_dom, context=context)
-            actions = self.browse(cr, uid, action_ids, context=context)
-
-            # check preconditions
-            pre_ids = {}
-            for action in actions:
-                pre_ids[action] = self._filter(cr, uid, action, action.filter_pre_id, ids, context=context)
-
-            # execute write
-            old_write(cr, uid, ids, vals, context=context, **kwargs)
-
-            # check postconditions, and execute actions on the records that satisfy them
-            for action in actions:
-                post_ids = self._filter(cr, uid, action, action.filter_id, pre_ids[action], context=context)
-                if post_ids:
-                    self._process(cr, uid, action, post_ids, context=context)
-            return True
-
-        return write
-
     def _register_hook(self, cr, ids=None):
         """ Wrap the methods `create` and `write` of the models specified by
             the rules given by `ids` (or all existing rules if `ids` is `None`.)
         """
+        #
+        # Note: the patched methods create and write must be defined inside
+        # another function, otherwise their closure may be wrong. For instance,
+        # the function create refers to the outer variable 'create', which you
+        # expect to be bound to create itself. But that expectation is wrong if
+        # create is defined inside a loop; in that case, the variable 'create'
+        # is bound to the last function defined by the loop.
+        #
+
+        def make_create():
+            """ instanciate a create method that processes action rules """
+            def create(self, cr, uid, vals, context=None, **kwargs):
+                # avoid loops or cascading actions
+                if context and context.get('action'):
+                    return create.origin(self, cr, uid, vals, context=context)
+
+                # call original method with a modified context
+                context = dict(context or {}, action=True)
+                new_id = create.origin(self, cr, uid, vals, context=context, **kwargs)
+
+                # as it is a new record, we do not consider the actions that have a prefilter
+                action_model = self.pool.get('base.action.rule')
+                action_dom = [('model', '=', self._name),
+                              ('kind', 'in', ['on_create', 'on_create_or_write'])]
+                action_ids = action_model.search(cr, uid, action_dom, context=context)
+
+                # check postconditions, and execute actions on the records that satisfy them
+                for action in action_model.browse(cr, uid, action_ids, context=context):
+                    if action_model._filter(cr, uid, action, action.filter_id, [new_id], context=context):
+                        action_model._process(cr, uid, action, [new_id], context=context)
+                return new_id
+
+            return create
+
+        def make_write():
+            """ instanciate a write method that processes action rules """
+            def write(self, cr, uid, ids, vals, context=None, **kwargs):
+                # avoid loops or cascading actions
+                if context and context.get('action'):
+                    return write.origin(self, cr, uid, ids, vals, context=context)
+
+                # modify context
+                context = dict(context or {}, action=True)
+                ids = [ids] if isinstance(ids, (int, long, str)) else ids
+
+                # retrieve the action rules to possibly execute
+                action_model = self.pool.get('base.action.rule')
+                action_dom = [('model', '=', self._name),
+                              ('kind', 'in', ['on_write', 'on_create_or_write'])]
+                action_ids = action_model.search(cr, uid, action_dom, context=context)
+                actions = action_model.browse(cr, uid, action_ids, context=context)
+
+                # check preconditions
+                pre_ids = {}
+                for action in actions:
+                    pre_ids[action] = action_model._filter(cr, uid, action, action.filter_pre_id, ids, context=context)
+
+                # call original method
+                write.origin(self, cr, uid, ids, vals, context=context, **kwargs)
+
+                # check postconditions, and execute actions on the records that satisfy them
+                for action in actions:
+                    post_ids = action_model._filter(cr, uid, action, action.filter_id, pre_ids[action], context=context)
+                    if post_ids:
+                        action_model._process(cr, uid, action, post_ids, context=context)
+                return True
+
+            return write
+
         updated = False
         if ids is None:
             ids = self.search(cr, SUPERUSER_ID, [])
@@ -221,10 +232,12 @@ class base_action_rule(osv.osv):
             model = action_rule.model_id.model
             model_obj = self.pool[model]
             if not hasattr(model_obj, 'base_action_ruled'):
-                model_obj.create = self._wrap_create(model_obj.create, model)
-                model_obj.write = self._wrap_write(model_obj.write, model)
+                # monkey-patch methods create and write
+                model_obj._patch_method('create', make_create())
+                model_obj._patch_method('write', make_write())
                 model_obj.base_action_ruled = True
                 updated = True
+
         return updated
 
     def _update_cron(self, cr, uid, context=None):
@@ -299,7 +312,7 @@ class base_action_rule(osv.osv):
                 if 'lang' not in ctx:
                     # Filters might be language-sensitive, attempt to reuse creator lang
                     # as we are usually running this as super-user in background
-                    [filter_meta] = action.filter_id.perm_read()
+                    [filter_meta] = action.filter_id.get_metadata()
                     user_id = filter_meta['write_uid'] and filter_meta['write_uid'][0] or \
                                     filter_meta['create_uid'][0]
                     ctx['lang'] = self.pool['res.users'].browse(cr, uid, user_id).lang