Imagine the following situation: an automated action A is triggered when some stored computed field F has a certain value. When a record is created and no value is given for field F, then the automated action A may be run twice: once when evaluating A's domain forces the computation of F, and once again because A's domain is satisfied. The implementation already uses context flags to reflect which automated actions have already been run, in order to avoid automated actions to be run recursively. The fix consists in enabling those context flags to be shared among the evaluation of the domain and processing of the automated actions. opw-3731182 closes odoo/odoo#157272 Signed-off-by: Raphael Collet <rco@odoo.com> Co-authored-by: Nasreddin Boulif (bon) <bon@odoo.com>
1186 lines
49 KiB
Python
1186 lines
49 KiB
Python
# # -*- coding: utf-8 -*-
|
|
# # Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
import json
|
|
from unittest.mock import patch
|
|
import sys
|
|
|
|
from odoo.tools import mute_logger
|
|
from odoo.addons.base.tests.common import TransactionCaseWithUserDemo
|
|
from odoo.tests import common, tagged
|
|
from odoo.exceptions import AccessError, ValidationError
|
|
from odoo import Command
|
|
|
|
|
|
def create_automation(self, **kwargs):
|
|
"""
|
|
Create a transient automation with the given data and actions
|
|
The created automation is cleaned up at the end of the calling test
|
|
"""
|
|
vals = {'name': 'Automation'}
|
|
vals.update(kwargs)
|
|
actions_data = vals.pop('_actions', [])
|
|
if not isinstance(actions_data, list):
|
|
actions_data = [actions_data]
|
|
automation_id = self.env['base.automation'].create(vals)
|
|
action_ids = self.env['ir.actions.server'].create(
|
|
[
|
|
{
|
|
'name': 'Action',
|
|
'base_automation_id': automation_id.id,
|
|
'model_id': automation_id.model_id.id,
|
|
'usage': 'base_automation',
|
|
**action,
|
|
}
|
|
for action in actions_data
|
|
]
|
|
)
|
|
automation_id.write({'action_server_ids': [Command.set(action_ids.ids)]})
|
|
self.addCleanup(automation_id.unlink)
|
|
return automation_id
|
|
|
|
|
|
@tagged('post_install', '-at_install')
|
|
class BaseAutomationTest(TransactionCaseWithUserDemo):
|
|
def setUp(self):
|
|
super(BaseAutomationTest, self).setUp()
|
|
self.user_root = self.env.ref('base.user_root')
|
|
self.user_admin = self.env.ref('base.user_admin')
|
|
self.lead_model = self.env.ref('test_base_automation.model_base_automation_lead_test')
|
|
self.project_model = self.env.ref('test_base_automation.model_test_base_automation_project')
|
|
self.test_mail_template_automation = self.env['mail.template'].create(
|
|
{
|
|
'name': 'Template Automation',
|
|
'model_id': self.env['ir.model']._get_id("base.automation.lead.thread.test"),
|
|
'body_html': """<div>Email automation</div>""",
|
|
}
|
|
)
|
|
self.res_partner_1 = self.env['res.partner'].create({'name': 'My Partner'})
|
|
|
|
def create_lead(self, **kwargs):
|
|
vals = {
|
|
'name': "Lead Test",
|
|
'user_id': self.user_root.id,
|
|
}
|
|
vals.update(kwargs)
|
|
lead = self.env['base.automation.lead.test'].create(vals)
|
|
self.addCleanup(lead.unlink)
|
|
return lead
|
|
|
|
def create_line(self, **kwargs):
|
|
vals = {
|
|
'name': 'Line Test',
|
|
'user_id': self.user_root.id,
|
|
}
|
|
vals.update(kwargs)
|
|
line = self.env['base.automation.line.test'].create(vals)
|
|
self.addCleanup(line.unlink)
|
|
return line
|
|
|
|
def create_project(self, **kwargs):
|
|
vals = {'name': 'Project Test'}
|
|
vals.update(kwargs)
|
|
project = self.env['test_base_automation.project'].create(vals)
|
|
self.addCleanup(project.unlink)
|
|
return project
|
|
|
|
def create_stage(self, **kwargs):
|
|
vals = {'name': 'Stage Test'}
|
|
vals.update(kwargs)
|
|
stage = self.env['test_base_automation.stage'].create(vals)
|
|
self.addCleanup(stage.unlink)
|
|
return stage
|
|
|
|
def create_tag(self, **kwargs):
|
|
vals = {'name': 'Tag Test'}
|
|
vals.update(kwargs)
|
|
tag = self.env['test_base_automation.tag'].create(vals)
|
|
self.addCleanup(tag.unlink)
|
|
return tag
|
|
|
|
def test_000_on_create_or_write(self):
|
|
"""
|
|
Test case: on save, simple case
|
|
- trigger: on_create_or_write
|
|
"""
|
|
# --- Without the automation ---
|
|
lead = self.create_lead()
|
|
self.assertEqual(lead.state, 'draft')
|
|
self.assertEqual(lead.user_id, self.user_root)
|
|
|
|
# --- With the automation ---
|
|
create_automation(
|
|
self,
|
|
model_id=self.lead_model.id,
|
|
trigger='on_create_or_write',
|
|
_actions={'state': 'code', 'code': "record.write({'user_id': %s})" % (self.user_demo.id)},
|
|
)
|
|
|
|
# Write a lead should trigger the automation
|
|
lead.write({'state': 'open'})
|
|
self.assertEqual(lead.state, 'open')
|
|
self.assertEqual(lead.user_id, self.user_demo)
|
|
|
|
# Create a lead should trigger the automation
|
|
lead2 = self.create_lead()
|
|
self.assertEqual(lead2.state, 'draft')
|
|
self.assertEqual(lead2.user_id, self.user_demo)
|
|
|
|
def test_001_on_create_or_write(self):
|
|
"""
|
|
Test case: on save, with filter_domain
|
|
- trigger: on_create_or_write
|
|
- apply when: state is 'draft'
|
|
"""
|
|
create_automation(
|
|
self,
|
|
model_id=self.lead_model.id,
|
|
trigger='on_create_or_write',
|
|
filter_domain="[('state', '=', 'draft')]",
|
|
_actions={'state': 'code', 'code': "record.write({'user_id': %s})" % (self.user_demo.id)},
|
|
)
|
|
|
|
# Create a lead with state=open should not trigger the automation
|
|
lead = self.create_lead(state='open')
|
|
self.assertEqual(lead.state, 'open')
|
|
self.assertEqual(lead.user_id, self.user_root)
|
|
|
|
# Write a lead to state=draft should trigger the automation
|
|
lead.write({'state': 'draft'})
|
|
self.assertEqual(lead.state, 'draft')
|
|
self.assertEqual(lead.user_id, self.user_demo)
|
|
|
|
# Create a lead with state=draft should trigger the automation
|
|
lead_2 = self.create_lead()
|
|
self.assertEqual(lead_2.state, 'draft')
|
|
self.assertEqual(lead_2.user_id, self.user_demo)
|
|
|
|
def test_002_on_create_or_write(self):
|
|
"""
|
|
Test case: on save, with filter_pre_domain and filter_domain
|
|
- trigger: on_create_or_write
|
|
- before update filter: state is 'open'
|
|
- apply when: state is 'done'
|
|
"""
|
|
create_automation(
|
|
self,
|
|
model_id=self.lead_model.id,
|
|
trigger='on_create_or_write',
|
|
filter_pre_domain="[('state', '=', 'open')]",
|
|
filter_domain="[('state', '=', 'done')]",
|
|
_actions={'state': 'code', 'code': "record.write({'user_id': %s})" % (self.user_demo.id)},
|
|
)
|
|
|
|
# Create a lead with state=open should not trigger the automation
|
|
lead = self.create_lead(state='open')
|
|
self.assertEqual(lead.state, 'open')
|
|
self.assertEqual(lead.user_id, self.user_root)
|
|
|
|
# Write a lead to state=pending THEN to state=done should not trigger the automation
|
|
lead.write({'state': 'pending'})
|
|
self.assertEqual(lead.state, 'pending')
|
|
self.assertEqual(lead.user_id, self.user_root)
|
|
lead.write({'state': 'done'})
|
|
self.assertEqual(lead.state, 'done')
|
|
self.assertEqual(lead.user_id, self.user_root)
|
|
|
|
# Write a lead from state=open to state=done should trigger the automation
|
|
lead.write({'state': 'open'})
|
|
self.assertEqual(lead.state, 'open')
|
|
self.assertEqual(lead.user_id, self.user_root)
|
|
lead.write({'state': 'done'})
|
|
self.assertEqual(lead.state, 'done')
|
|
self.assertEqual(lead.user_id, self.user_demo)
|
|
|
|
# Create a lead with state=open then write it to state=done should trigger the automation
|
|
lead_2 = self.create_lead(state='open')
|
|
self.assertEqual(lead_2.state, 'open')
|
|
self.assertEqual(lead_2.user_id, self.user_root)
|
|
lead_2.write({'state': 'done'})
|
|
self.assertEqual(lead_2.state, 'done')
|
|
self.assertEqual(lead_2.user_id, self.user_demo)
|
|
|
|
# Create a lead with state=done should trigger the automation,
|
|
# as verifying the filter_pre_domain does not make sense on create
|
|
lead_3 = self.create_lead(state='done')
|
|
self.assertEqual(lead_3.state, 'done')
|
|
self.assertEqual(lead_3.user_id, self.user_demo)
|
|
|
|
def test_003_on_create_or_write(self):
|
|
""" Check that the on_create_or_write trigger works as expected with trigger fields. """
|
|
lead_state_field = self.env.ref('test_base_automation.field_base_automation_lead_test__state')
|
|
automation = create_automation(
|
|
self,
|
|
model_id=self.lead_model.id,
|
|
trigger='on_create_or_write',
|
|
trigger_field_ids=[Command.link(lead_state_field.id)],
|
|
_actions={
|
|
'state': 'code',
|
|
'code': """
|
|
if env.context.get('old_values', None): # on write only
|
|
record = model.browse(env.context['active_id'])
|
|
record['name'] = record.name + 'X'""",
|
|
},
|
|
)
|
|
|
|
lead = self.create_lead(name="X")
|
|
lead.priority = True
|
|
partner1 = self.res_partner_1
|
|
lead.partner_id = partner1
|
|
self.assertEqual(lead.name, 'X', "No update until now.")
|
|
|
|
lead.state = 'open'
|
|
self.assertEqual(lead.name, 'XX', "One update should have happened.")
|
|
lead.state = 'done'
|
|
self.assertEqual(lead.name, 'XXX', "One update should have happened.")
|
|
lead.state = 'done'
|
|
self.assertEqual(lead.name, 'XXX', "No update should have happened.")
|
|
lead.state = 'cancel'
|
|
self.assertEqual(lead.name, 'XXXX', "One update should have happened.")
|
|
|
|
# change the rule to trigger on partner_id
|
|
lead_partner_id_field = self.env.ref('test_base_automation.field_base_automation_lead_test__partner_id')
|
|
automation.write({'trigger_field_ids': [Command.set([lead_partner_id_field.id])]})
|
|
|
|
partner2 = self.env['res.partner'].create({'name': 'A new partner'})
|
|
lead.name = 'X'
|
|
lead.state = 'open'
|
|
self.assertEqual(lead.name, 'X', "No update should have happened.")
|
|
lead.partner_id = partner2
|
|
self.assertEqual(lead.name, 'XX', "One update should have happened.")
|
|
lead.partner_id = partner2
|
|
self.assertEqual(lead.name, 'XX', "No update should have happened.")
|
|
lead.partner_id = partner1
|
|
self.assertEqual(lead.name, 'XXX', "One update should have happened.")
|
|
lead.partner_id = partner1
|
|
self.assertEqual(lead.name, 'XXX', "No update should have happened.")
|
|
|
|
def test_010_recompute(self):
|
|
"""
|
|
Test case: automation is applied whenever a field is recomputed
|
|
after a change on another model.
|
|
- trigger: on_create_or_write
|
|
- apply when: employee is True
|
|
"""
|
|
partner = self.res_partner_1
|
|
partner.write({'employee': False})
|
|
|
|
create_automation(
|
|
self,
|
|
model_id=self.lead_model.id,
|
|
trigger='on_create_or_write',
|
|
filter_domain="[('employee', '=', True)]",
|
|
_actions={'state': 'code', 'code': "record.write({'user_id': %s})" % (self.user_demo.id)},
|
|
)
|
|
|
|
lead = self.create_lead(partner_id=partner.id)
|
|
self.assertEqual(lead.partner_id, partner)
|
|
self.assertEqual(lead.employee, False)
|
|
self.assertEqual(lead.user_id, self.user_root)
|
|
|
|
# change partner, recompute on lead should trigger the rule
|
|
partner.write({'employee': True})
|
|
self.env.flush_all() # ensures the recomputation is done
|
|
self.assertEqual(lead.partner_id, partner)
|
|
self.assertEqual(lead.employee, True)
|
|
self.assertEqual(lead.user_id, self.user_demo)
|
|
|
|
def test_011_recompute(self):
|
|
"""
|
|
Test case: automation is applied whenever a field is recomputed.
|
|
The context contains the target field.
|
|
- trigger: on_create_or_write
|
|
"""
|
|
create_automation(
|
|
self,
|
|
model_id=self.lead_model.id,
|
|
trigger='on_create_or_write',
|
|
_actions={
|
|
'state': 'code',
|
|
'code': """
|
|
if env.context.get('old_values', None): # on write
|
|
if 'user_id' in env.context['old_values'][record.id]:
|
|
record.write({'is_assigned_to_admin': (record.user_id.id == 1)})""",
|
|
},
|
|
)
|
|
|
|
partner = self.res_partner_1
|
|
lead = self.create_lead(state='draft', partner_id=partner.id)
|
|
self.assertEqual(lead.deadline, False)
|
|
self.assertEqual(lead.is_assigned_to_admin, False)
|
|
|
|
# change priority and user; this triggers deadline recomputation, and
|
|
# the server action should set is_assigned_to_admin field to True
|
|
lead.write({'priority': True, 'user_id': self.user_root.id})
|
|
self.assertNotEqual(lead.deadline, False)
|
|
self.assertEqual(lead.is_assigned_to_admin, True)
|
|
|
|
def test_012_recompute(self):
|
|
"""
|
|
Test case: automation is applied whenever a field is recomputed.
|
|
- trigger: on_create_or_write
|
|
- if updating fields: [deadline]
|
|
"""
|
|
active_field = self.env.ref("test_base_automation.field_base_automation_lead_test__active")
|
|
create_automation(
|
|
self,
|
|
model_id=self.lead_model.id,
|
|
trigger='on_create_or_write',
|
|
trigger_field_ids=[Command.link(active_field.id)],
|
|
_actions={
|
|
'state': 'code',
|
|
'code': """
|
|
if not env.context.get('old_values', None): # on create
|
|
record.write({'state': 'open'})
|
|
else:
|
|
record.write({'priority': not record.priority})""",
|
|
},
|
|
)
|
|
|
|
lead = self.create_lead(state='draft', priority=False)
|
|
self.assertEqual(lead.state, 'open') # the rule has set the state to open on create
|
|
self.assertEqual(lead.priority, False)
|
|
|
|
# change state; the rule should not be triggered
|
|
lead.write({'state': 'pending'})
|
|
self.assertEqual(lead.state, 'pending')
|
|
self.assertEqual(lead.priority, False)
|
|
|
|
# change active; the rule should be triggered
|
|
lead.write({'active': False})
|
|
self.assertEqual(lead.state, 'pending')
|
|
self.assertEqual(lead.priority, True)
|
|
|
|
# change active again; the rule should still be triggered
|
|
lead.write({'active': True})
|
|
self.assertEqual(lead.state, 'pending')
|
|
self.assertEqual(lead.priority, False)
|
|
|
|
def test_013_recompute(self):
|
|
"""
|
|
Test case: automation is applied whenever a field is recomputed
|
|
- trigger: on_create_or_write
|
|
- if updating fields: [deadline]
|
|
- before update filter: deadline is not set
|
|
- apply when: deadline is set
|
|
"""
|
|
deadline_field = self.env.ref("test_base_automation.field_base_automation_lead_test__deadline")
|
|
create_automation(
|
|
self,
|
|
model_id=self.env['ir.model']._get_id('base.automation.lead.thread.test'),
|
|
trigger='on_create_or_write',
|
|
trigger_field_ids=[Command.link(deadline_field.id)],
|
|
filter_pre_domain="[('deadline', '=', False)]",
|
|
filter_domain="[('deadline', '!=', False)]",
|
|
_actions={
|
|
'state': 'mail_post',
|
|
'mail_post_method': 'email',
|
|
'template_id': self.test_mail_template_automation.id,
|
|
},
|
|
)
|
|
|
|
send_mail_count = 0
|
|
|
|
def _patched_send_mail(*args, **kwargs):
|
|
nonlocal send_mail_count
|
|
send_mail_count += 1
|
|
|
|
patcher = patch('odoo.addons.mail.models.mail_template.MailTemplate.send_mail', _patched_send_mail)
|
|
self.startPatcher(patcher)
|
|
|
|
lead = self.env['base.automation.lead.thread.test'].create({
|
|
'name': "Lead Test",
|
|
'user_id': self.user_root.id,
|
|
})
|
|
self.addCleanup(lead.unlink)
|
|
self.assertEqual(lead.priority, False)
|
|
self.assertEqual(lead.deadline, False)
|
|
self.assertEqual(send_mail_count, 0)
|
|
|
|
lead.write({'priority': True})
|
|
self.assertEqual(lead.priority, True)
|
|
self.assertNotEqual(lead.deadline, False)
|
|
self.assertEqual(send_mail_count, 1)
|
|
|
|
def test_020_recursive(self):
|
|
""" Check that a rule is executed recursively by a secondary change. """
|
|
create_automation(
|
|
self,
|
|
model_id=self.lead_model.id,
|
|
trigger='on_create_or_write',
|
|
_actions={
|
|
'state': 'code',
|
|
'code': """
|
|
if env.context.get('old_values', None): # on write
|
|
if 'partner_id' in env.context['old_values'][record.id]:
|
|
record.write({'state': 'draft'})""",
|
|
},
|
|
)
|
|
create_automation(
|
|
self,
|
|
model_id=self.lead_model.id,
|
|
trigger='on_create_or_write',
|
|
filter_domain="[('state', '=', 'draft')]",
|
|
_actions={'state': 'code', 'code': "record.write({'user_id': %s})" % (self.user_demo.id)},
|
|
)
|
|
|
|
lead = self.create_lead(state='open')
|
|
self.assertEqual(lead.state, 'open')
|
|
self.assertEqual(lead.user_id, self.user_root)
|
|
|
|
# change partner; this should trigger the rule that modifies the state
|
|
# and then the rule that modifies the user
|
|
partner = self.res_partner_1
|
|
lead.write({'partner_id': partner.id})
|
|
self.assertEqual(lead.state, 'draft')
|
|
self.assertEqual(lead.user_id, self.user_demo)
|
|
|
|
def test_021_recursive(self):
|
|
""" Check what it does with a recursive infinite loop """
|
|
automations = [
|
|
create_automation(
|
|
self,
|
|
model_id=self.lead_model.id,
|
|
trigger='on_create_or_write',
|
|
filter_domain="[('state', '=', 'draft')]",
|
|
_actions={'state': 'code', 'code': "record.write({'state': 'pending'})"},
|
|
),
|
|
create_automation(
|
|
self,
|
|
model_id=self.lead_model.id,
|
|
trigger='on_create_or_write',
|
|
filter_domain="[('state', '=', 'pending')]",
|
|
_actions={'state': 'code', 'code': "record.write({'state': 'open'})"},
|
|
),
|
|
create_automation(
|
|
self,
|
|
model_id=self.lead_model.id,
|
|
trigger='on_create_or_write',
|
|
filter_domain="[('state', '=', 'open')]",
|
|
_actions={'state': 'code', 'code': "record.write({'state': 'done'})"},
|
|
),
|
|
create_automation(
|
|
self,
|
|
model_id=self.lead_model.id,
|
|
trigger='on_create_or_write',
|
|
filter_domain="[('state', '=', 'done')]",
|
|
_actions={'state': 'code', 'code': "record.write({'state': 'draft'})"},
|
|
),
|
|
]
|
|
|
|
def _patch(*args, **kwargs):
|
|
self.assertEqual(args[0], automations.pop(0))
|
|
|
|
patcher = patch('odoo.addons.base_automation.models.base_automation.BaseAutomation._process', _patch)
|
|
self.startPatcher(patcher)
|
|
|
|
lead = self.create_lead(state='draft')
|
|
self.assertEqual(lead.state, 'draft')
|
|
self.assertEqual(len(automations), 0) # all automations have been processed # CHECK if proper assertion ?
|
|
|
|
def test_030_submodel(self):
|
|
""" Check that a rule on a submodel is executed when the parent is modified. """
|
|
# --- Without the automations ---
|
|
line = self.create_line()
|
|
self.assertEqual(line.user_id, self.user_root)
|
|
|
|
lead = self.create_lead(line_ids=[(0, 0, {'name': 'Line', 'user_id': self.user_root.id})])
|
|
self.assertEqual(lead.user_id, self.user_root)
|
|
self.assertEqual(lead.line_ids.user_id, self.user_root)
|
|
|
|
# --- With the automations ---
|
|
comodel = self.env.ref('test_base_automation.model_base_automation_line_test')
|
|
create_automation(
|
|
self,
|
|
model_id=self.lead_model.id,
|
|
trigger='on_create_or_write',
|
|
_actions={'state': 'code', 'code': "record.write({'user_id': %s})" % (self.user_demo.id)},
|
|
)
|
|
create_automation(
|
|
self,
|
|
model_id=comodel.id,
|
|
trigger='on_create_or_write',
|
|
_actions={'state': 'code', 'code': "record.write({'user_id': %s})" % (self.user_demo.id)},
|
|
)
|
|
|
|
line = self.create_line(user_id=self.user_root.id)
|
|
self.assertEqual(line.user_id, self.user_demo) # rule on secondary model
|
|
|
|
lead = self.create_lead(line_ids=[(0, 0, {'name': 'Line', 'user_id': self.user_root.id})])
|
|
self.assertEqual(lead.user_id, self.user_demo) # rule on primary model
|
|
self.assertEqual(lead.line_ids.user_id, self.user_demo) # rule on secondary model
|
|
|
|
def test_040_modelwithoutaccess(self):
|
|
"""
|
|
Ensure a domain on a M2O without user access doesn't fail.
|
|
We create a base automation with a filter on a model the user haven't access to
|
|
- create a group
|
|
- restrict acl to this group and set only admin in it
|
|
- create base.automation with a filter
|
|
- create a record in the restricted model in admin
|
|
- create a record in the non restricted model in demo
|
|
"""
|
|
Model = self.env['base.automation.link.test']
|
|
model_id = self.env.ref('test_base_automation.model_base_automation_link_test')
|
|
Comodel = self.env['base.automation.linked.test']
|
|
comodel_access = self.env.ref('test_base_automation.access_base_automation_linked_test')
|
|
comodel_access.group_id = self.env['res.groups'].create({
|
|
'name': "Access to base.automation.linked.test",
|
|
"users": [Command.link(self.user_admin.id)],
|
|
})
|
|
|
|
# sanity check: user demo has no access to the comodel of 'linked_id'
|
|
with self.assertRaises(AccessError):
|
|
Comodel.with_user(self.user_demo).check_access_rights('read')
|
|
|
|
# check base automation with filter that performs Comodel.search()
|
|
create_automation(
|
|
self,
|
|
model_id=model_id.id,
|
|
trigger='on_create_or_write',
|
|
filter_pre_domain="[('linked_id.another_field', '=', 'something')]",
|
|
_actions={'state': 'code', 'code': 'action = [rec.name for rec in records]'},
|
|
)
|
|
Comodel.create([
|
|
{'name': 'a first record', 'another_field': 'something'},
|
|
{'name': 'another record', 'another_field': 'something different'},
|
|
])
|
|
rec1 = Model.create({'name': 'a record'})
|
|
rec1.write({'name': 'a first record'})
|
|
rec2 = Model.with_user(self.user_demo).create({'name': 'another record'})
|
|
rec2.write({'name': 'another value'})
|
|
|
|
# check base automation with filter that performs Comodel.name_search()
|
|
create_automation(
|
|
self,
|
|
model_id=model_id.id,
|
|
trigger='on_create_or_write',
|
|
filter_pre_domain="[('linked_id', '=', 'whatever')]",
|
|
_actions={'state': 'code', 'code': 'action = [rec.name for rec in records]'},
|
|
)
|
|
rec3 = Model.create({'name': 'a random record'})
|
|
rec3.write({'name': 'a first record'})
|
|
rec4 = Model.with_user(self.user_demo).create({'name': 'again another record'})
|
|
rec4.write({'name': 'another value'})
|
|
|
|
def test_050_on_create_or_write_with_create_record(self):
|
|
create_automation(
|
|
self,
|
|
model_id=self.lead_model.id,
|
|
trigger='on_create_or_write',
|
|
_actions={
|
|
'state': 'object_create',
|
|
'crud_model_id': self.project_model.id,
|
|
'value': 'foo',
|
|
},
|
|
)
|
|
lead = self.create_lead()
|
|
search_result = self.env['test_base_automation.project'].name_search('foo')
|
|
self.assertEqual(len(search_result), 1, 'One record on the project model should have been created')
|
|
|
|
lead.write({'name': 'renamed lead'})
|
|
search_result = self.env['test_base_automation.project'].name_search('foo')
|
|
self.assertEqual(len(search_result), 2, 'Another record on the project model should have been created')
|
|
|
|
# ----------------------------
|
|
# The following does not work properly as it is a known
|
|
# limitation of the implementation since at least 14.0
|
|
# -> AssertionError: 4 != 3 : Another record on the secondary model should have been created
|
|
|
|
# # write on a field that is a dependency of another computed field
|
|
# lead.write({'priority': True})
|
|
# search_result = self.env['test_base_automation.project'].name_search('foo')
|
|
# self.assertEqual(len(search_result), 3, 'Another record on the secondary model should have been created')
|
|
# ----------------------------
|
|
|
|
def test_060_on_stage_set(self):
|
|
stage_field = self.env['ir.model.fields'].search([
|
|
('model_id', '=', self.project_model.id),
|
|
('name', '=', 'stage_id'),
|
|
])
|
|
stage1 = self.create_stage()
|
|
stage2 = self.create_stage()
|
|
create_automation(
|
|
self,
|
|
model_id=self.project_model.id,
|
|
trigger='on_stage_set',
|
|
trigger_field_ids=[stage_field.id],
|
|
filter_domain="[('stage_id', '=', %s)]" % stage1.id,
|
|
_actions={'state': 'code', 'code': "record.write({'name': record.name + '!'})"},
|
|
)
|
|
project = self.create_project()
|
|
self.assertEqual(project.name, 'Project Test')
|
|
project.write({'stage_id': stage1.id})
|
|
self.assertEqual(project.name, 'Project Test!')
|
|
project.write({'stage_id': stage1.id})
|
|
self.assertEqual(project.name, 'Project Test!')
|
|
project.write({'stage_id': stage2.id})
|
|
self.assertEqual(project.name, 'Project Test!')
|
|
project.write({'stage_id': False})
|
|
self.assertEqual(project.name, 'Project Test!')
|
|
project.write({'stage_id': stage1.id})
|
|
self.assertEqual(project.name, 'Project Test!!')
|
|
|
|
def test_070_on_user_set(self):
|
|
user_field = self.env['ir.model.fields'].search([
|
|
('model_id', '=', self.lead_model.id),
|
|
('name', '=', 'user_id'),
|
|
])
|
|
create_automation(
|
|
self,
|
|
model_id=self.lead_model.id,
|
|
trigger='on_user_set',
|
|
trigger_field_ids=[user_field.id],
|
|
filter_domain="[('user_id', '!=', False)]",
|
|
_actions={'state': 'code', 'code': "record.write({'name': record.name + '!'})"},
|
|
)
|
|
|
|
lead = self.create_lead()
|
|
self.assertEqual(lead.name, 'Lead Test!')
|
|
lead.write({'user_id': self.user_demo.id})
|
|
self.assertEqual(lead.name, 'Lead Test!!')
|
|
lead.write({'user_id': self.user_demo.id})
|
|
self.assertEqual(lead.name, 'Lead Test!!')
|
|
lead.write({'user_id': self.user_admin.id})
|
|
self.assertEqual(lead.name, 'Lead Test!!!')
|
|
lead.write({'user_id': False})
|
|
self.assertEqual(lead.name, 'Lead Test!!!')
|
|
lead.write({'user_id': self.user_demo.id})
|
|
self.assertEqual(lead.name, 'Lead Test!!!!')
|
|
|
|
def test_071_on_user_set(self):
|
|
# same test as above but with the user_ids many2many on a project
|
|
user_field = self.env['ir.model.fields'].search([
|
|
('model_id', '=', self.project_model.id),
|
|
('name', '=', 'user_ids'),
|
|
])
|
|
create_automation(
|
|
self,
|
|
model_id=self.project_model.id,
|
|
trigger='on_user_set',
|
|
trigger_field_ids=[user_field.id],
|
|
filter_domain="[('user_ids', '!=', False)]",
|
|
_actions={'state': 'code', 'code': "record.write({'name': record.name + '!'})"},
|
|
)
|
|
|
|
project = self.create_project()
|
|
self.assertEqual(project.name, 'Project Test')
|
|
project.write({'user_ids': [Command.set([self.user_demo.id])]})
|
|
self.assertEqual(project.name, 'Project Test!')
|
|
project.write({'user_ids': [Command.set([self.user_demo.id])]})
|
|
self.assertEqual(project.name, 'Project Test!')
|
|
project.write({'user_ids': [Command.link(self.user_admin.id)]})
|
|
self.assertEqual(project.name, 'Project Test!!')
|
|
# Unlinking a user while there are still other users does trigger the automation
|
|
# This behavior could be changed in the future but needs a bit of investigation
|
|
project.write({'user_ids': [Command.unlink(self.user_admin.id)]})
|
|
self.assertEqual(project.name, 'Project Test!!!')
|
|
project.write({'user_ids': [Command.set([])]})
|
|
self.assertEqual(project.name, 'Project Test!!!')
|
|
project.write({'user_ids': [Command.set([self.user_demo.id])]})
|
|
self.assertEqual(project.name, 'Project Test!!!!')
|
|
|
|
def test_080_on_tag_set(self):
|
|
tag_field = self.env['ir.model.fields'].search([
|
|
('model_id', '=', self.project_model.id),
|
|
('name', '=', 'tag_ids'),
|
|
])
|
|
tag1 = self.create_tag()
|
|
create_automation(
|
|
self,
|
|
model_id=self.project_model.id,
|
|
trigger='on_tag_set',
|
|
trigger_field_ids=[tag_field.id],
|
|
filter_pre_domain="[('tag_ids', 'not in', [%s])]" % tag1.id,
|
|
filter_domain="[('tag_ids', 'in', [%s])]" % tag1.id,
|
|
_actions={'state': 'code', 'code': "record.write({'name': record.name + '!'})"},
|
|
)
|
|
project = self.create_project()
|
|
self.assertEqual(project.name, 'Project Test')
|
|
project.write({'tag_ids': [Command.set([tag1.id])]})
|
|
self.assertEqual(project.name, 'Project Test!')
|
|
project.write({'tag_ids': [Command.set([tag1.id])]})
|
|
self.assertEqual(project.name, 'Project Test!')
|
|
|
|
tag2 = self.create_tag()
|
|
project.write({'tag_ids': [Command.link(tag2.id)]})
|
|
self.assertEqual(project.name, 'Project Test!')
|
|
project.write({'tag_ids': [Command.clear()]})
|
|
self.assertEqual(project.name, 'Project Test!')
|
|
project.write({'tag_ids': [Command.set([tag2.id])]})
|
|
self.assertEqual(project.name, 'Project Test!')
|
|
project.write({'tag_ids': [Command.link(tag1.id)]})
|
|
self.assertEqual(project.name, 'Project Test!!')
|
|
|
|
def test_090_on_state_set(self):
|
|
state_field = self.env['ir.model.fields'].search([
|
|
('model_id', '=', self.lead_model.id),
|
|
('name', '=', 'state'),
|
|
])
|
|
|
|
create_automation(
|
|
self,
|
|
model_id=self.lead_model.id,
|
|
trigger='on_state_set',
|
|
trigger_field_ids=[state_field.id],
|
|
filter_domain="[('state', '=', 'done')]",
|
|
_actions={'state': 'code', 'code': "record.write({'name': record.name + '!'})"},
|
|
)
|
|
|
|
lead = self.create_lead()
|
|
self.assertEqual(lead.name, 'Lead Test')
|
|
lead.write({'state': 'open'})
|
|
self.assertEqual(lead.name, 'Lead Test')
|
|
lead.write({'state': 'done'})
|
|
self.assertEqual(lead.name, 'Lead Test!')
|
|
lead.write({'state': 'done'})
|
|
self.assertEqual(lead.name, 'Lead Test!')
|
|
lead.write({'state': 'open'})
|
|
self.assertEqual(lead.name, 'Lead Test!')
|
|
lead.write({'state': 'done'})
|
|
self.assertEqual(lead.name, 'Lead Test!!')
|
|
|
|
def test_100_on_priority_set(self):
|
|
priority_field = self.env['ir.model.fields'].search([
|
|
('model_id', '=', self.project_model.id),
|
|
('name', '=', 'priority'),
|
|
])
|
|
create_automation(
|
|
self,
|
|
model_id=self.project_model.id,
|
|
trigger='on_priority_set',
|
|
trigger_field_ids=[priority_field.id],
|
|
filter_domain="[('priority', '=', '2')]",
|
|
_actions={'state': 'code', 'code': "record.write({'name': record.name + '!'})"},
|
|
)
|
|
project = self.create_project()
|
|
self.assertEqual(project.name, 'Project Test')
|
|
self.assertEqual(project.priority, '1')
|
|
project.write({'priority': '0'})
|
|
self.assertEqual(project.name, 'Project Test')
|
|
project.write({'priority': '2'})
|
|
self.assertEqual(project.name, 'Project Test!')
|
|
project.write({'priority': '2'})
|
|
self.assertEqual(project.name, 'Project Test!')
|
|
project.write({'priority': '0'})
|
|
self.assertEqual(project.name, 'Project Test!')
|
|
project.write({'priority': '2'})
|
|
self.assertEqual(project.name, 'Project Test!!')
|
|
|
|
def test_110_on_archive(self):
|
|
active_field = self.env['ir.model.fields'].search([
|
|
('model_id', '=', self.lead_model.id),
|
|
('name', '=', 'active'),
|
|
])
|
|
create_automation(
|
|
self,
|
|
model_id=self.lead_model.id,
|
|
trigger='on_archive',
|
|
trigger_field_ids=[active_field.id],
|
|
filter_domain="[('active', '=', False)]",
|
|
_actions={'state': 'code', 'code': "record.write({'name': record.name + '!'})"},
|
|
)
|
|
lead = self.create_lead()
|
|
self.assertEqual(lead.name, 'Lead Test')
|
|
lead.write({'active': False})
|
|
self.assertEqual(lead.name, 'Lead Test!')
|
|
lead.write({'active': True})
|
|
self.assertEqual(lead.name, 'Lead Test!')
|
|
lead.write({'active': False})
|
|
self.assertEqual(lead.name, 'Lead Test!!')
|
|
lead.write({'active': False})
|
|
self.assertEqual(lead.name, 'Lead Test!!')
|
|
|
|
def test_110_on_unarchive(self):
|
|
active_field = self.env['ir.model.fields'].search([
|
|
('model_id', '=', self.lead_model.id),
|
|
('name', '=', 'active'),
|
|
])
|
|
create_automation(
|
|
self,
|
|
model_id=self.lead_model.id,
|
|
trigger='on_unarchive',
|
|
trigger_field_ids=[active_field.id],
|
|
filter_domain="[('active', '=', True)]",
|
|
_actions={'state': 'object_write', 'evaluation_type': 'equation', 'update_path': 'name', 'value': "record.name + '!'"},
|
|
)
|
|
lead = self.create_lead()
|
|
self.assertEqual(lead.name, 'Lead Test')
|
|
lead.write({'active': False})
|
|
self.assertEqual(lead.name, 'Lead Test')
|
|
lead.write({'active': True})
|
|
self.assertEqual(lead.name, 'Lead Test!')
|
|
lead.write({'active': False})
|
|
self.assertEqual(lead.name, 'Lead Test!')
|
|
lead.write({'active': True})
|
|
self.assertEqual(lead.name, 'Lead Test!!')
|
|
lead.write({'active': True})
|
|
self.assertEqual(lead.name, 'Lead Test!!')
|
|
|
|
def test_120_on_change(self):
|
|
Model = self.env.get(self.lead_model.model)
|
|
lead_name_field = self.env['ir.model.fields'].search([
|
|
('model_id', '=', self.lead_model.id),
|
|
('name', '=', 'name'),
|
|
])
|
|
self.assertEqual(lead_name_field.name in Model._onchange_methods, False)
|
|
create_automation(
|
|
self,
|
|
model_id=self.lead_model.id,
|
|
trigger='on_change',
|
|
on_change_field_ids=[lead_name_field.id],
|
|
_actions={'state': 'code', 'code': ""},
|
|
)
|
|
self.assertEqual(lead_name_field.name in Model._onchange_methods, True)
|
|
|
|
def test_130_on_unlink(self):
|
|
automation = create_automation(
|
|
self,
|
|
model_id=self.lead_model.id,
|
|
trigger='on_unlink',
|
|
_actions={'state': 'code', 'code': "record.write({'name': record.name + '!'})"},
|
|
)
|
|
|
|
called_count = 0
|
|
|
|
def _patch(*args, **kwargs):
|
|
nonlocal called_count
|
|
called_count += 1
|
|
self.assertEqual(args[0], automation)
|
|
|
|
patcher = patch('odoo.addons.base_automation.models.base_automation.BaseAutomation._process', _patch)
|
|
self.startPatcher(patcher)
|
|
|
|
lead = self.create_lead()
|
|
self.assertEqual(called_count, 0)
|
|
lead.unlink()
|
|
self.assertEqual(called_count, 1)
|
|
|
|
def test_004_check_method(self):
|
|
model = self.env["ir.model"]._get("base.automation.lead.test")
|
|
TIME_TRIGGERS = [
|
|
'on_time',
|
|
'on_time_created',
|
|
'on_time_updated',
|
|
]
|
|
self.env["base.automation"].search([('trigger', 'in', TIME_TRIGGERS)]).active = False
|
|
|
|
automation = self.env["base.automation"].create({
|
|
"name": "Cron BaseAuto",
|
|
"trigger": "on_time",
|
|
"model_id": model.id,
|
|
})
|
|
self.assertFalse(automation.last_run)
|
|
self.env["base.automation"]._check(False)
|
|
self.assertTrue(automation.last_run)
|
|
|
|
|
|
@common.tagged('post_install', '-at_install')
|
|
class TestCompute(common.TransactionCase):
|
|
def test_inversion(self):
|
|
""" If a stored field B depends on A, an update to the trigger for A
|
|
should trigger the recomputaton of A, then B.
|
|
|
|
However if a search() is performed during the computation of A
|
|
??? and _order is affected ??? a flush will be triggered, forcing the
|
|
computation of B, based on the previous A.
|
|
|
|
This happens if a rule has a non-empty filter_pre_domain, even if
|
|
it's an empty list (``'[]'`` as opposed to ``False``).
|
|
"""
|
|
company1 = self.env['res.partner'].create({
|
|
'name': "Gorofy",
|
|
'is_company': True,
|
|
})
|
|
company2 = self.env['res.partner'].create({
|
|
'name': "Awiclo",
|
|
'is_company': True
|
|
})
|
|
r = self.env['res.partner'].create({
|
|
'name': 'Bob',
|
|
'is_company': False,
|
|
'parent_id': company1.id
|
|
})
|
|
self.assertEqual(r.display_name, 'Gorofy, Bob')
|
|
r.parent_id = company2
|
|
self.assertEqual(r.display_name, 'Awiclo, Bob')
|
|
|
|
create_automation(
|
|
self,
|
|
model_id=self.env.ref('base.model_res_partner').id,
|
|
filter_pre_domain=False,
|
|
trigger='on_create_or_write',
|
|
_actions={'state': 'code'}, # no-op action
|
|
)
|
|
r.parent_id = company1
|
|
self.assertEqual(r.display_name, 'Gorofy, Bob')
|
|
|
|
create_automation(
|
|
self,
|
|
model_id=self.env.ref('base.model_res_partner').id,
|
|
filter_pre_domain='[]',
|
|
trigger='on_create_or_write',
|
|
_actions={'state': 'code'}, # no-op action
|
|
)
|
|
r.parent_id = company2
|
|
self.assertEqual(r.display_name, 'Awiclo, Bob')
|
|
|
|
def test_recursion(self):
|
|
project = self.env['test_base_automation.project'].create({})
|
|
|
|
# this action is executed every time a task is assigned to project
|
|
create_automation(
|
|
self,
|
|
model_id=self.env.ref('test_base_automation.model_test_base_automation_task').id,
|
|
trigger='on_create_or_write',
|
|
filter_domain=repr([('project_id', '=', project.id)]),
|
|
_actions={'state': 'code'}, # no-op action
|
|
)
|
|
|
|
# create one task in project with 10 subtasks; all the subtasks are
|
|
# automatically assigned to project, too
|
|
task = self.env['test_base_automation.task'].create({'project_id': project.id})
|
|
subtasks = task.create([{'parent_id': task.id} for _ in range(10)])
|
|
subtasks.flush_model()
|
|
|
|
# This test checks what happens when a stored recursive computed field
|
|
# is marked to compute on many records, and automation rules are
|
|
# triggered depending on that field. In this case, we trigger the
|
|
# recomputation of 'project_id' on 'subtasks' by deleting their parent
|
|
# task.
|
|
#
|
|
# An issue occurs when the domain of automation rules is evaluated by
|
|
# method search(), because the latter flushes the fields to search on,
|
|
# which are also the ones being recomputed. Combined with the fact
|
|
# that recursive fields are not computed in batch, this leads to a huge
|
|
# amount of recursive calls between the automation rule and flush().
|
|
#
|
|
# The execution of task.unlink() looks like this:
|
|
# - mark 'project_id' to compute on subtasks
|
|
# - delete task
|
|
# - flush()
|
|
# - recompute 'project_id' on subtask1
|
|
# - call compute on subtask1
|
|
# - in action, search([('id', 'in', subtask1.ids), ('project_id', '=', pid)])
|
|
# - flush(['id', 'project_id'])
|
|
# - recompute 'project_id' on subtask2
|
|
# - call compute on subtask2
|
|
# - in action search([('id', 'in', subtask2.ids), ('project_id', '=', pid)])
|
|
# - flush(['id', 'project_id'])
|
|
# - recompute 'project_id' on subtask3
|
|
# - call compute on subtask3
|
|
# - in action, search([('id', 'in', subtask3.ids), ('project_id', '=', pid)])
|
|
# - flush(['id', 'project_id'])
|
|
# - recompute 'project_id' on subtask4
|
|
# ...
|
|
limit = sys.getrecursionlimit()
|
|
try:
|
|
sys.setrecursionlimit(100)
|
|
task.unlink()
|
|
finally:
|
|
sys.setrecursionlimit(limit)
|
|
|
|
def test_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")
|
|
automation = create_automation(self, trigger="on_message_sent", model_id=lead_thread_model.id, _actions={
|
|
"state": "object_write",
|
|
"update_path": "active",
|
|
"update_boolean_value": "false"
|
|
})
|
|
|
|
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)
|
|
|
|
obj.active = True
|
|
obj.message_post(author_id=internal_partner.id, subtype_xmlid="mail.mt_comment")
|
|
self.assertTrue(obj.active)
|
|
|
|
obj.message_post(author_id=ext_partner.id, message_type="comment")
|
|
self.assertTrue(obj.active)
|
|
|
|
obj.message_post(author_id=internal_partner.id, message_type="comment")
|
|
self.assertTrue(obj.active)
|
|
obj.message_post(author_id=internal_partner.id, subtype_xmlid="mail.mt_comment", message_type="comment")
|
|
self.assertFalse(obj.active)
|
|
|
|
automation.trigger = "on_message_received"
|
|
obj.active = True
|
|
obj.message_post(author_id=internal_partner.id, subtype_xmlid="mail.mt_comment", message_type="comment")
|
|
self.assertTrue(obj.active)
|
|
|
|
obj.message_post(author_id=ext_partner.id, message_type="comment")
|
|
self.assertTrue(obj.active)
|
|
|
|
obj.message_post(author_id=ext_partner.id, subtype_xmlid="mail.mt_comment", message_type="comment")
|
|
self.assertFalse(obj.active)
|
|
|
|
obj.active = True
|
|
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!")
|
|
|
|
def test_compute_on_create(self):
|
|
lead_model = self.env['ir.model']._get('base.automation.lead.test')
|
|
stage_field = self.env['ir.model.fields']._get('base.automation.lead.test', 'stage_id')
|
|
new_stage = self.env['test_base_automation.stage'].create({'name': 'New'})
|
|
|
|
create_automation(
|
|
self,
|
|
model_id=lead_model.id,
|
|
trigger='on_stage_set',
|
|
trigger_field_ids=[stage_field.id],
|
|
_actions={
|
|
'state': 'object_create',
|
|
'crud_model_id': self.env['ir.model']._get('res.partner').id,
|
|
'value': "Test Partner Automation",
|
|
},
|
|
filter_domain=repr([('stage_id', '=', new_stage.id)]),
|
|
)
|
|
|
|
# Tricky case: the record is created with 'stage_id' being false, and
|
|
# the field is marked for recomputation. The field is then recomputed
|
|
# while evaluating 'filter_domain', which causes the execution of the
|
|
# automation. And as the domain is satisfied, the automation is
|
|
# processed again, but it must detect that it has just been run!
|
|
self.env['base.automation.lead.test'].create({
|
|
'name': 'Test Lead',
|
|
})
|
|
|
|
# check that the automation has been run once
|
|
partner_count = self.env['res.partner'].search_count([('name', '=', 'Test Partner Automation')])
|
|
self.assertEqual(partner_count, 1, "Only one partner should have been created")
|
|
|
|
|
|
@common.tagged("post_install", "-at_install")
|
|
class TestHttp(common.HttpCase):
|
|
def test_webhook_trigger(self):
|
|
model = self.env["ir.model"]._get("base.automation.linked.test")
|
|
record_getter = "model.search([('name', '=', payload['name'])]) if payload.get('name') else None"
|
|
automation = create_automation(self, trigger="on_webhook", model_id=model.id, record_getter=record_getter, _actions={
|
|
"state": "object_write",
|
|
"update_path": "another_field",
|
|
"value": "written"
|
|
})
|
|
|
|
obj = self.env[model.model].create({"name": "some name"})
|
|
response = self.url_open(automation.url, data=json.dumps({"name": "some name"}))
|
|
self.assertEqual(response.json(), {"status": "ok"})
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertEqual(obj.another_field, "written")
|
|
|
|
obj.another_field = False
|
|
with mute_logger("odoo.addons.base_automation.models.base_automation"):
|
|
response = self.url_open(automation.url, data=json.dumps({}))
|
|
self.assertEqual(response.json(), {"status": "error"})
|
|
self.assertEqual(response.status_code, 500)
|
|
self.assertEqual(obj.another_field, False)
|
|
|
|
response = self.url_open("/web/hook/0123456789", data=json.dumps({"name": "some name"}))
|
|
self.assertEqual(response.json(), {"status": "error"})
|
|
self.assertEqual(response.status_code, 404)
|
|
|
|
def test_payload_in_action_server(self):
|
|
model = self.env["ir.model"]._get("base.automation.linked.test")
|
|
record_getter = "model.search([('name', '=', payload['name'])]) if payload.get('name') else None"
|
|
automation = create_automation(self, trigger="on_webhook", model_id=model.id, record_getter=record_getter, _actions={
|
|
"state": "code",
|
|
"code": "record.write({'another_field': json.dumps(payload)})"
|
|
})
|
|
|
|
obj = self.env[model.model].create({"name": "some name"})
|
|
self.url_open(automation.url, data=json.dumps({"name": "some name", "test_key": "test_value"}), headers={"Content-Type": "application/json"})
|
|
self.assertEqual(json.loads(obj.another_field), {
|
|
"name": "some name",
|
|
"test_key": "test_value",
|
|
})
|
|
|
|
obj.another_field = ""
|
|
self.url_open(automation.url + "?test_param=test_value&name=some%20name")
|
|
self.assertEqual(json.loads(obj.another_field), {
|
|
"name": "some name",
|
|
"test_param": "test_value",
|
|
})
|
|
|
|
def test_webhook_send_and_receive(self):
|
|
model = self.env["ir.model"]._get("base.automation.linked.test")
|
|
obj = self.env[model.model].create({"name": "some name"})
|
|
|
|
automation_receiver = create_automation(self, trigger="on_webhook", model_id=model.id, _actions={
|
|
"state": "code",
|
|
"code": "record.write({'another_field': json.dumps(payload)})"
|
|
})
|
|
name_field_id = self.env.ref("test_base_automation.field_base_automation_linked_test__name")
|
|
automation_sender = create_automation(self, trigger="on_write", model_id=model.id, trigger_field_ids=[(6, 0, [name_field_id.id])], _actions={
|
|
"state": "webhook",
|
|
"webhook_url": automation_receiver.url,
|
|
})
|
|
|
|
obj.name = "new_name"
|
|
self.cr.flush()
|
|
self.cr.clear()
|
|
self.assertEqual(json.loads(obj.another_field), {
|
|
'_action': f'Send Webhook Notification(#{automation_sender.action_server_ids[0].id})',
|
|
"_id": obj.id,
|
|
"_model": obj._name,
|
|
})
|
|
|
|
def test_on_change_get_views_cache(self):
|
|
model_name = "base.automation.lead.test"
|
|
my_view = self.env["ir.ui.view"].create({
|
|
"name": "My View",
|
|
"model": model_name,
|
|
"type": "form",
|
|
"arch": "<form><field name='active'/></form>",
|
|
})
|
|
self.assertEqual(
|
|
self.env[model_name].get_view(my_view.id)["arch"],
|
|
'<form><field name="active"/></form>'
|
|
)
|
|
model = self.env["ir.model"]._get(model_name)
|
|
active_field = self.env["ir.model.fields"]._get(model_name, "active")
|
|
create_automation(self, trigger="on_change", model_id=model.id, on_change_field_ids=[Command.set([active_field.id])], _actions={
|
|
"state": "code",
|
|
"code": "",
|
|
})
|
|
self.assertEqual(
|
|
self.env[model_name].get_view(my_view.id)["arch"],
|
|
'<form><field name="active" on_change="1"/></form>'
|
|
)
|