'Patch models that should trigger action rules based on creation,
modification, deletion of records and form onchanges.'

@api.model_cr
def _register_hook(self):

    def make_create():
        '    Instanciate    a    create    method    that    processes    action    rules.    '
        @api.model
        def create(self, vals, **kw):
            actions = self.env['base.action.rule']._get_actions(self, ['on_create', 'on_create_or_write'])
            record = create.origin(self.with_env(actions.env), vals, **kw)
            for action in actions.with_context(old_values=None):
                action._process(action._filter_post(record))
            return record.with_env(self.env)
        return create
    def make_write():
        '    Instanciate    a    _write    method    that    processes    action    rules.    '
        @api.multi
        def _write(self, vals, **kw):
            actions = self.env['base.action.rule']._get_actions(self, ['on_write', 'on_create_or_write'])
            records = self.with_env(actions.env)
            pre = {action: action._filter_pre(records) for action in actions}
            old_values = {old_vals.pop('id'): old_vals for old_vals in records.read(list(vals))}
            _write.origin(records, vals, **kw)
            for action in actions.with_context(old_values=old_values):
                action._process(action._filter_post(pre[action]))
            return True
        return _write
    def make_unlink():
        '    Instanciate    an    unlink    method    that    processes    action    rules.    '
        @api.multi
        def unlink(self, **kwargs):
            actions = self.env['base.action.rule']._get_actions(self, ['on_unlink'])
            records = self.with_env(actions.env)
            for action in actions:
                action._process(action._filter_post(records))
            return unlink.origin(self, **kwargs)
        return unlink
    def make_onchange(action_rule_id):
        '    Instanciate    an    onchange    method    for    the    given    action    rule.    '
        def base_action_rule_onchange(self):
            action_rule = self.env['base.action.rule'].browse(action_rule_id)
            result = {}
            for server_action in action_rule.server_action_ids.with_context(active_model=self._name, onchange_self=self):
                res = server_action.run()
                if res:
                    if ('value' in res):
                        res['value'].pop('id', None)
                        self.update({key: val for (key, val) in res['value'].iteritems() if (key in self._fields)})
                    if ('domain' in res):
                        result.setdefault('domain', {}).update(res['domain'])
                    if ('warning' in res):
                        result['warning'] = res['warning']
            return result
        return base_action_rule_onchange
    patched_models = defaultdict(set)
    def patch(model, name, method):
        '    Patch    method    `name`    on    `model`,    unless    it    has    been    patched    already.    '
        if (model not in patched_models[name]):
            patched_models[name].add(model)
            model._patch_method(name, method)
    for action_rule in self.with_context({}).search([]):
        Model = self.env[action_rule.model]
        if (action_rule.kind == 'on_create'):
            patch(Model, 'create', make_create())
        elif (action_rule.kind == 'on_create_or_write'):
            patch(Model, 'create', make_create())
            patch(Model, '_write', make_write())
        elif (action_rule.kind == 'on_write'):
            patch(Model, '_write', make_write())
        elif (action_rule.kind == 'on_unlink'):
            patch(Model, 'unlink', make_unlink())
        elif (action_rule.kind == 'on_change'):
            method = make_onchange(action_rule.id)
            for field_name in action_rule.on_change_fields.split(','):
                Model._onchange_methods[field_name.strip()].append(method)
