diff --git a/onchange_helper/models/models.py b/onchange_helper/models/models.py index 2584ffe55..ab2a2fba7 100644 --- a/onchange_helper/models/models.py +++ b/onchange_helper/models/models.py @@ -79,6 +79,12 @@ class Base(models.AbstractModel): result = [(5,)] for record in value: vals = {} + # We have to check first if the record already exists + # (only in case of M2M). + if field.type == "many2many" and not isinstance( + record.id, models.NewId): + result.append((4, record.id)) + continue for name in record._cache: if name in models.LOG_ACCESS_COLUMNS: continue diff --git a/onchange_helper/tests/test_onchange.py b/onchange_helper/tests/test_onchange.py index 101a997de..46f7cc646 100644 --- a/onchange_helper/tests/test_onchange.py +++ b/onchange_helper/tests/test_onchange.py @@ -3,13 +3,72 @@ # Copyright 2019 ACSONE SA/NV # @author Sébastien BEAU # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - import mock import odoo.tests.common as common +from odoo import api, models + + +class ResPartnerTester(models.Model): + _inherit = "res.partner" + _name = "res.partner" + + @api.onchange("name") + def _onchange_name_test(self): + """ + When the name change, assign every existing partner category + on the partner. + This onchange is used to test behavior of this module for M2M fields. + :return: + """ + self.category_id = self.env['res.partner.category'].search([], limit=2) class TestOnchange(common.TransactionCase): + def _init_test_model(self, model_cls): + model_cls._build_model(self.registry, self.cr) + model = self.env[model_cls._name].with_context(todo=[]) + model._prepare_setup() + model._setup_base(partial=False) + model._setup_fields(partial=False) + model._setup_complete() + model._auto_init() + model.init() + model._auto_end() + return model + + def setUp(self): + super(TestOnchange, self).setUp() + self.registry.enter_test_mode() + self.old_cursor = self.cr + self.cr = self.registry.cursor() + self.env = api.Environment(self.cr, self.uid, {}) + self.test_model = self._init_test_model(ResPartnerTester) + + def tearDown(self): + self.registry.leave_test_mode() + self.cr = self.old_cursor + self.env = api.Environment(self.cr, self.uid, {}) + super(TestOnchange, self).tearDown() + + def test_playing_onchange_m2m(self): + """ + Test if the onchange fill correctly M2M fields. + :return: + """ + values = { + "name": "Balthazar Melchior Gaspard", + } + expected_categs = self.env['res.partner.category'].search([], limit=2) + # We should have some categs for this test + self.assertTrue(expected_categs) + # We have to ensure categs are into cache. So just load the name. + expected_categs.mapped("name") + expected_result = [(5,)] + expected_result.extend([(4, c.id) for c in expected_categs]) + result = self.env['res.partner'].play_onchanges(values, values.keys()) + self.assertEqual(result["category_id"], expected_result) + def test_playing_onchange_on_model(self): res_partner = self.env["res.partner"] with mock.patch.object(