From 5fc24f84eaf0cfb1d2ce81145eedd49b879d8f2d Mon Sep 17 00:00:00 2001 From: "Mattis Megevand (mmeg)" Date: Tue, 27 Feb 2024 15:33:57 +0100 Subject: [PATCH] [FIX] base_automation: allow multiple automation rules for same trigger Steps to reproduce: - Install base_automation_hr_contract and helpdesk - Setup two automation rules with model "Helpdesk Ticket" and "On incoming message" as trigger - Create a ticket in helpdesk Issues: Traceback is shown because `_message_post` doesn't have the origin attributes. This is due to the closure being wrong as explained here: https://github.com/odoo/odoo/blob/e2ad568e6cd4de2d721149eb76d04f58c8510191/addons/base_automation/models/base_automation.py#L686-L691 Solution: Encapsulate the `_message_post` function definition, so that each times we patch message_post we patch it with a new function. This wasn't the case before since `_message_post` wasn't isolated. This solution is similar to the other method patched. https://github.com/odoo/odoo/blob/e2ad568e6cd4de2d721149eb76d04f58c8510191/addons/base_automation/models/base_automation.py#L711-L734 opw-3758851 closes odoo/odoo#155596 Signed-off-by: Raphael Collet --- .../base_automation/models/base_automation.py | 44 ++++++++++--------- .../test_base_automation/tests/test_flow.py | 28 ++++++++++++ 2 files changed, 51 insertions(+), 21 deletions(-) diff --git a/addons/base_automation/models/base_automation.py b/addons/base_automation/models/base_automation.py index 1d839f90a9f..d52ad54e3bd 100644 --- a/addons/base_automation/models/base_automation.py +++ b/addons/base_automation/models/base_automation.py @@ -810,6 +810,28 @@ class BaseAutomation(models.Model): return base_automation_onchange + def make_message_post(): + def _message_post(self, *args, **kwargs): + message = _message_post.origin(self, *args, **kwargs) + # Don't execute automations for a message emitted during + # the run of automations for a real message + # Don't execute if we know already that a message is only internal + message_sudo = message.sudo().with_context(active_test=False) + if "__action_done" in self.env.context or message_sudo.is_internal or message_sudo.subtype_id.internal: + return message + if message_sudo.message_type in ('notification', 'auto_comment', 'user_notification'): + return message + + # always execute actions when the author is a customer + mail_trigger = "on_message_received" if message_sudo.author_id.partner_share else "on_message_sent" + automations = self.env['base.automation']._get_actions(self, [mail_trigger]) + for automation in automations.with_context(old_values=None): + records = automation._filter_pre(self) + automation._process(records) + + return message + return _message_post + patched_models = defaultdict(set) def patch(model, name, method): @@ -853,27 +875,7 @@ class BaseAutomation(models.Model): self.env.registry.clear_cache('templates') if automation_rule.model_id.is_mail_thread and automation_rule.trigger in MAIL_TRIGGERS: - def _message_post(self, *args, **kwargs): - message = _message_post.origin(self, *args, **kwargs) - # Don't execute automations for a message emitted during - # the run of automations for a real message - # Don't execute if we know already that a message is only internal - message_sudo = message.sudo().with_context(active_test=False) - if "__action_done" in self.env.context or message_sudo.is_internal or message_sudo.subtype_id.internal: - return message - if message_sudo.message_type in ('notification', 'auto_comment', 'user_notification'): - return message - - # always execute actions when the author is a customer - mail_trigger = "on_message_received" if message_sudo.author_id.partner_share else "on_message_sent" - automations = self.env['base.automation']._get_actions(self, [mail_trigger]) - for automation in automations.with_context(old_values=None): - records = automation._filter_pre(self) - automation._process(records) - - return message - - patch(Model, "message_post", _message_post) + patch(Model, "message_post", make_message_post()) def _unregister_hook(self): """ Remove the patches installed by _register_hook() """ diff --git a/addons/test_base_automation/tests/test_flow.py b/addons/test_base_automation/tests/test_flow.py index b52e700677c..99dd4698a7e 100644 --- a/addons/test_base_automation/tests/test_flow.py +++ b/addons/test_base_automation/tests/test_flow.py @@ -1027,6 +1027,34 @@ class TestCompute(common.TransactionCase): obj.message_post(author_id=ext_partner.id, subtype_xmlid="mail.mt_comment") self.assertTrue(obj.active) + def test_multiple_mail_triggers(self): + lead_model = self.env["ir.model"]._get("base.automation.lead.test") + with self.assertRaises(ValidationError): + create_automation(self, trigger="on_message_sent", model_id=lead_model.id) + + lead_thread_model = self.env["ir.model"]._get("base.automation.lead.thread.test") + + create_automation(self, trigger="on_message_sent", model_id=lead_thread_model.id, _actions={ + "state": "object_write", + "update_path": "active", + "update_boolean_value": "false" + }) + create_automation(self, trigger="on_message_sent", model_id=lead_thread_model.id, _actions={ + "state": "object_write", + "evaluation_type": "equation", + "update_path": "name", + "value": "record.name + '!'" + }) + + ext_partner = self.env["res.partner"].create({"name": "ext", "email": "email@server.com"}) + internal_partner = self.env["res.users"].browse(2).partner_id + + obj = self.env["base.automation.lead.thread.test"].create({"name": "test"}) + obj.message_subscribe([ext_partner.id, internal_partner.id]) + + obj.message_post(author_id=internal_partner.id, message_type="comment", subtype_xmlid="mail.mt_comment") + self.assertFalse(obj.active) + self.assertEqual(obj.name, "test!") @common.tagged("post_install", "-at_install") class TestHttp(common.HttpCase):