From 09c7ec59ce1f78865d080a2c2b0ddd7e299d45de Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Thu, 23 Mar 2017 16:02:55 +0100 Subject: [PATCH] [FIX] base_action_rule: prevent double execution only in recursive calls (#15863) --- addons/base_action_rule/base_action_rule.py | 13 ++++--- .../base_action_rule_demo.xml | 34 +++++++++++++++++++ addons/base_action_rule/test_models.py | 13 +++++++ .../tests/test_base_action_rule.py | 24 +++++++++++++ 4 files changed, 80 insertions(+), 4 deletions(-) diff --git a/addons/base_action_rule/base_action_rule.py b/addons/base_action_rule/base_action_rule.py index 1255dfb3be0..1278cc09604 100644 --- a/addons/base_action_rule/base_action_rule.py +++ b/addons/base_action_rule/base_action_rule.py @@ -175,13 +175,18 @@ class base_action_rule(osv.osv): @openerp.api.multi def _process(self, records): """ Process action ``self`` on the ``records`` that have not been done yet. """ - # filter out the records on which self has already been done, then mark - # remaining records as done (to avoid recursive processing) + # filter out the records on which self has already been done action_done = self._context['__action_done'] - records -= action_done.setdefault(self, records.browse()) + records_done = action_done.get(self, records.browse()) + records -= records_done if not records: return - action_done[self] |= records + + # mark the remaining records as done (to avoid recursive processing) + action_done = dict(action_done) + action_done[self] = records_done + records + self = self.with_context(__action_done=action_done) + records = records.with_context(__action_done=action_done) # modify records values = {} diff --git a/addons/base_action_rule/base_action_rule_demo.xml b/addons/base_action_rule/base_action_rule_demo.xml index c626cd41a3a..fe629e16a17 100644 --- a/addons/base_action_rule/base_action_rule_demo.xml +++ b/addons/base_action_rule/base_action_rule_demo.xml @@ -44,11 +44,45 @@ [('customer', '=', True)] + + Test Server Action + + +record = model.browse(context['active_id']) +if 'partner_id' in context['old_values'][record.id]: + record.write({'state': 'draft'}) + + + + + Test recursive rule + on_write + + + + Test rule on secondary record on_create + + + Test Server Action Context + + +record = model.browse(context['active_id']) +if 'user_id' in context['old_values'][record.id]: + record.write({'is_assigned_to_admin': (record.user_id.id == 1)}) + + + + + Test rule on write check context + on_write + + + diff --git a/addons/base_action_rule/test_models.py b/addons/base_action_rule/test_models.py index 2bfb568d762..697f868aab6 100644 --- a/addons/base_action_rule/test_models.py +++ b/addons/base_action_rule/test_models.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +from dateutil import relativedelta + import openerp from openerp.osv import fields, osv from openerp import api @@ -33,6 +35,17 @@ class lead_test(osv.Model): } customer = openerp.fields.Boolean(related='partner_id.customer', readonly=True, store=True) + priority = openerp.fields.Boolean() + deadline = openerp.fields.Boolean(compute='_compute_deadline', store=True) + is_assigned_to_admin = openerp.fields.Boolean(string='Assigned to admin user') + + @api.depends('priority') + def _compute_deadline(self): + for record in self: + if not record.priority: + record.deadline = False + else: + record.deadline = openerp.fields.Datetime.from_string(record.create_date) + relativedelta.relativedelta(days=3) @api.cr_uid_ids_context def message_post(self, cr, uid, thread_id, body='', subject=None, message_type='notification', subtype=None, parent_id=False, attachments=None, context=None, **kwargs): diff --git a/addons/base_action_rule/tests/test_base_action_rule.py b/addons/base_action_rule/tests/test_base_action_rule.py index b6e7d880246..673efbcc37d 100644 --- a/addons/base_action_rule/tests/test_base_action_rule.py +++ b/addons/base_action_rule/tests/test_base_action_rule.py @@ -92,6 +92,30 @@ class base_action_rule_test(common.TransactionCase): self.assertTrue(lead.customer) self.assertEqual(lead.user_id, self.user_demo) + def test_11_recomputed_field(self): + """ + Check that a rule is executed whenever a field is recomputed and the + context contains the target field + """ + partner = self.env.ref('base.res_partner_1') + lead = self.create_lead(state='draft', partner_id=partner.id) + self.assertFalse(lead.deadline, 'There should not be a deadline defined') + # change priority and user; this triggers deadline recomputation, and + # the server action should set the boolean field to True + lead.write({'priority': True, 'user_id': self.user_admin.id}) + self.assertTrue(lead.deadline, 'Deadline should be defined') + self.assertTrue(lead.is_assigned_to_admin, 'Lead should be assigned to admin') + + def test_12_recursive(self): + """ Check that a rule is executed recursively by a secondary change. """ + lead = self.create_lead(state='open') + self.assertEqual(lead.state, 'open') + self.assertEqual(lead.user_id, self.user_admin) + # change partner; this should trigger the rule that modifies the state + partner = self.env.ref('base.res_partner_1') + lead.write({'partner_id': partner.id}) + self.assertEqual(lead.state, 'draft') + def test_20_direct_line(self): """ Check that a rule is executed after creating a line record.