diff --git a/auditlog/models/rule.py b/auditlog/models/rule.py index 1eb482de8..02fd9d290 100644 --- a/auditlog/models/rule.py +++ b/auditlog/models/rule.py @@ -311,6 +311,7 @@ class AuditlogRule(models.Model): def create_fast(self, vals_list, **kwargs): self = self.with_context(auditlog_disabled=True) rule_model = self.env["auditlog.rule"] + vals_list = rule_model._update_vals_list(vals_list) vals_list2 = copy.deepcopy(vals_list) new_records = create_fast.origin(self, vals_list, **kwargs) new_values = {} @@ -715,3 +716,15 @@ class AuditlogRule(models.Model): if act_window: act_window.unlink() return self.write({"state": "draft"}) + + @api.model + def _update_vals_list(self, vals_list): + # Odoo supports empty recordset assignment (while it doesn't handle + # non-empty recordset ¯\_(ツ)_/¯ ), it could be an Odoo issue, but in + # the meanwhile we have to handle this case to avoid errors when using + # ``deepcopy`` to log data. + for vals in vals_list: + for fieldname, fieldvalue in vals.items(): + if isinstance(fieldvalue, models.BaseModel) and not fieldvalue: + vals[fieldname] = False + return vals_list diff --git a/auditlog/tests/test_auditlog.py b/auditlog/tests/test_auditlog.py index 88d5e7c0b..74566f2f6 100644 --- a/auditlog/tests/test_auditlog.py +++ b/auditlog/tests/test_auditlog.py @@ -10,39 +10,17 @@ from odoo.addons.base.models.ir_model import MODULE_UNINSTALL_FLAG class AuditlogCommon(object): def test_LogCreation(self): """First test, caching some data.""" - self.groups_rule.subscribe() - - auditlog_log = self.env["auditlog.log"] group = self.env["res.groups"].create({"name": "testgroup1"}) - self.assertTrue( - auditlog_log.search( + self.assertEqual( + self.env["auditlog.log"].search_count( [ ("model_id", "=", self.groups_model_id), ("method", "=", "create"), ("res_id", "=", group.id), ] - ).ensure_one() - ) - group.write({"name": "Testgroup1"}) - self.assertTrue( - auditlog_log.search( - [ - ("model_id", "=", self.groups_model_id), - ("method", "=", "write"), - ("res_id", "=", group.id), - ] - ).ensure_one() - ) - group.unlink() - self.assertTrue( - auditlog_log.search( - [ - ("model_id", "=", self.groups_model_id), - ("method", "=", "unlink"), - ("res_id", "=", group.id), - ] - ).ensure_one() + ), + 1, ) def test_LogCreation2(self): @@ -193,6 +171,93 @@ class AuditlogCommon(object): if self.groups_rule.capture_record: self.assertTrue(len(log_record.line_ids) > 0) + def test_LogCreation7(self): + """Seventh test: multi-create with different M2O values. + + Check that creation goes as planned (no error coming from ``deepcopy``) + """ + self.groups_rule.subscribe() + + auditlog_log = self.env["auditlog.log"] + cat = self.env["ir.module.category"].create({"name": "Test Category"}) + groups_vals = [ + {"name": "testgroup1"}, + {"name": "testgroup3", "category_id": cat.browse()}, + {"name": "testgroup2", "category_id": False}, + {"name": "testgroup4", "category_id": cat.id}, + ] + groups = self.env["res.groups"].create(groups_vals) + + # Ensure ``category_id`` field has the correct values + expected_ids = [False, False, False, cat.id] + self.assertEqual([g.category_id.id for g in groups], expected_ids) + + # Ensure the correct number of logs have been created + logs = auditlog_log.search( + [ + ("model_id", "=", self.groups_model_id), + ("method", "=", "create"), + ("res_id", "in", groups.ids), + ] + ) + self.assertEqual(len(logs), len(groups)) + + def test_LogUpdate(self): + """Tests write results with different M2O values.""" + self.groups_rule.subscribe() + group = self.env["res.groups"].create({"name": "testgroup1"}) + cat = self.env["ir.module.category"].create({"name": "Test Category"}) + group.write( + { + "name": "Testgroup1", + "category_id": cat.browse(), + } + ) + log1 = self.env["auditlog.log"].search( + [ + ("model_id", "=", self.groups_model_id), + ("method", "=", "write"), + ("res_id", "=", group.id), + ] + ) + self.assertEqual(len(log1), 1) + group.write({"name": "Testgroup2", "category_id": cat.id}) + log2 = self.env["auditlog.log"].search( + [ + ("model_id", "=", self.groups_model_id), + ("method", "=", "write"), + ("res_id", "=", group.id), + ("id", "not in", log1.ids), + ] + ) + self.assertEqual(len(log2), 1) + group.write({"name": "Testgroup3", "category_id": False}) + log3 = self.env["auditlog.log"].search( + [ + ("model_id", "=", self.groups_model_id), + ("method", "=", "write"), + ("res_id", "=", group.id), + ("id", "not in", (log1 + log2).ids), + ] + ) + self.assertEqual(len(log3), 1) + + def test_LogDelete(self): + """Tests unlink results""" + self.groups_rule.subscribe() + group = self.env["res.groups"].create({"name": "testgroup1"}) + group.unlink() + self.assertEqual( + self.env["auditlog.log"].search_count( + [ + ("model_id", "=", self.groups_model_id), + ("method", "=", "unlink"), + ("res_id", "=", group.id), + ] + ), + 1, + ) + class TestAuditlogFull(TransactionCase, AuditlogCommon): def setUp(self):