[FIX] base_automation: differentiate between creating and writing

Steps to reproduce:
-------------------
[A] Create an automated action with:
- Model: Tasks (project.task)
- Trigger: On save
- When updating: ID (to mimic "on_create" trigger)
- Execute code:
```py
record.message_post(body="This is a message from automated action (fields triggers: ['id'])")
```

[B] Create a server action (contextual action) with:
- Type: execute code
- Model: Tasks (project.task)
- code:
```py
record.write({})
```

[C] Test:
- create a new task --> message has been posted (OK)
- write some values on the task --> no message has been posted (OK)
- run the server action --> message has been posted (KO)

Issue:
------
No message should be posted as the ID field has not been modified.

Cause:
------
`if not self._context.get('old_values')` is considered as `True`
because we have an empty dict.
In fact, when we make a write on a record, old_values will be always a dict.

```py
old_values = {
    old_vals.pop('id'): old_vals
    for old_vals in (records.read(list(vals)) if vals else [])
}
```

Solution:
---------
Old values are not defined in the context during a create.
We can compare `self._context.get('old_values')` with `None`
to differentiate between creating and writing on records.

opw-3736068

closes odoo/odoo#155832

Signed-off-by: Rémy Voet (ryv) <ryv@odoo.com>
This commit is contained in:
Thomas Lefebvre (thle)
2024-04-16 14:12:46 +00:00
parent 6f44b78954
commit 5fe2866cc4
2 changed files with 44 additions and 1 deletions
@@ -680,7 +680,7 @@ class BaseAutomation(models.Model):
# all fields are implicit triggers
return True
if not self._context.get('old_values'):
if self._context.get('old_values') is None:
# this is a create: all fields are considered modified
return True
@@ -112,3 +112,46 @@ class TestAutomation(TransactionCaseWithUserDemo):
# simulate a onchange call on name
result = self_portal.onchange({}, [], {"name": {}, "active": {}})
self.assertEqual(result["value"]["active"], False)
def test_04_on_create_or_write_differentiate(self):
"""
The purpose is to differentiate create and empty write.
"""
model = self.env.ref("base.model_res_partner")
model_field_id = self.env['ir.model.fields'].search([('model', '=', model.model), ('name', '=', 'id')], limit=1)
automation = self.env["base.automation"].create({
"name": "Test automated action",
"trigger": "on_create_or_write",
"model_id": model.id,
"trigger_field_ids": [Command.set([model_field_id.id])],
})
action = self.env["ir.actions.server"].create({
"name": "Modify name",
"base_automation_id": automation.id,
"model_id": model.id,
"state": "code",
"code": "record.write({'name': 'Modified Name'})"
})
action.flush_recordset()
automation.write({"action_server_ids": [Command.link(action.id)]})
# action cached was cached with admin, force CacheMiss
automation.env.clear()
server_action = self.env["ir.actions.server"].create({
"name": "Empty write",
"model_id": model.id,
"state": "code",
"code": "record.write({})"
})
partner = self.env[model.model].create({'name': 'Test Name'})
self.assertEqual(partner.name, 'Modified Name', 'The automatic action must be performed')
partner.name = 'Reset Name'
self.assertEqual(partner.name, 'Reset Name', 'The automatic action must not be performed')
context = {
'active_model': model.model,
'active_id': partner.id,
}
server_action.with_context(context).run()
self.assertEqual(partner.name, 'Reset Name', 'The automatic action must not be performed')