diff --git a/NEWS.rst b/NEWS.rst index 9540e05eac5371870d651dd0451ff0a5db048507_TkVXUy5yc3Q=..92b9ff4d49ee7a08b8e9f1f1fa11ed8d9c8525ec_TkVXUy5yc3Q= 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -1,6 +1,11 @@ Changelog ========= +16.0.1.2.1 +---------- + +Fix RelationToMany calling undefined fonction. + 16.0.1.2.0 ---------- diff --git a/__manifest__.py b/__manifest__.py index 9540e05eac5371870d651dd0451ff0a5db048507_X19tYW5pZmVzdF9fLnB5..92b9ff4d49ee7a08b8e9f1f1fa11ed8d9c8525ec_X19tYW5pZmVzdF9fLnB5 100644 --- a/__manifest__.py +++ b/__manifest__.py @@ -1,7 +1,7 @@ ############################################################################## # # Converter Odoo module -# Copyright © 2020 XCG Consulting <https://xcg-consulting.fr> +# Copyright © 2020, 2024 XCG Consulting <https://xcg-consulting.fr> # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -22,7 +22,7 @@ "name": "Converter", "license": "AGPL-3", "summary": "Convert odoo records to/from plain data structures.", - "version": "16.0.1.2.0", + "version": "16.0.1.2.1", "category": "Hidden", "author": "XCG Consulting", "website": "https://orbeet.io/", diff --git a/relation.py b/relation.py index 9540e05eac5371870d651dd0451ff0a5db048507_cmVsYXRpb24ucHk=..92b9ff4d49ee7a08b8e9f1f1fa11ed8d9c8525ec_cmVsYXRpb24ucHk= 100644 --- a/relation.py +++ b/relation.py @@ -23,7 +23,7 @@ from odoo import api, models -from .base import ContextBuilder, Converter, Newinstance, Skip, build_context +from .base import ContextBuilder, Converter, NewinstanceType, Skip, build_context from .field import Field _logger = logging.getLogger(__name__) @@ -68,27 +68,9 @@ if not value_present: return {} - post_hook = getattr(self.converter, "post_hook", None) - - if self.converter.is_instance_getter(): - rel_record = self.converter.get_instance(odoo_env, message_value) - - if rel_record is None: - return {self.field_name: None} - - if rel_record is Newinstance: - rel_record = None - - updates = self.converter.message_to_odoo( - odoo_env, phase, message_value, rel_record, value_present - ) - - if updates: - if rel_record: - rel_record.write(updates) - else: - rel_record = odoo_env[self.model_name].create(updates) - - if post_hook: - post_hook(rel_record, message_value) + record = _update_record( + self, odoo_env, phase, message_value, instance, value_present + ) + if record is None: + return {} @@ -94,31 +76,5 @@ - if instance: - field_value = getattr(instance, self.field_name) - - if field_value and field_value.id == rel_record.id: - return {} - return {self.field_name: rel_record.id} - return {self.field_name: rel_record.id} - - else: - field_value = getattr(instance, self.field_name) if instance else None - - updates = self.converter.message_to_odoo( - odoo_env, phase, message_value, field_value, value_present - ) - - if updates: - if field_value: - field_value.write(updates) - if post_hook: - post_hook(field_value, message_value) - return {} - else: - rel_record = odoo_env[self.model_name].create(updates) - if post_hook: - post_hook(rel_record, message_value) - return {self.field_name: rel_record.id} - return {} + return {self.field_name: record.id} class RelationToMany(Field): @@ -176,8 +132,17 @@ return {} field_instances = odoo_env[self.model_name] for value in message_value: - field_instances |= odoo_env["ir.model.data"].browseXref(value) - if instance and getattr(instance, self.field_name) == field_instances: + record = _update_record( + self, odoo_env, phase, value, instance, value_present + ) + if record is not None: + field_instances |= record + + if ( + instance + and not isinstance(instance, NewinstanceType) + and getattr(instance, self.field_name) == field_instances + ): return {} return {self.field_name: [(6, 0, field_instances.ids)]} @@ -236,8 +201,17 @@ return {} field_instances = odoo_env[self.model_name] for value in message_value: - field_instances |= odoo_env["ir.model.data"].browseXref(value) - if instance and getattr(instance, self.field_name) == field_instances: + record = _update_record( + self, odoo_env, phase, value, instance, value_present + ) + if record is not None: + field_instances |= record + + if ( + instance + and not isinstance(instance, NewinstanceType) + and getattr(instance, self.field_name) == field_instances + ): return {} return {self.field_name: [(6, 0, field_instances.ids)]} @@ -256,3 +230,71 @@ else: converter = RelationToOne(name, model_name, converter) return converter + + +def _update_record( + self, + odoo_env: api.Environment, + phase: str, + message_value: Any, + instance: models.BaseModel, + value_present: bool = True, +) -> Any: + """Update or create a single record with the given values. + :param self: the actual converter class. + :param message_value: the message value for one record. + :return: the record id, if any, else None. + """ + post_hook = getattr(self.converter, "post_hook", None) + + if self.converter.is_instance_getter: + rel_record: models.BaseModel | NewinstanceType | None = ( + self.converter.get_instance(odoo_env, message_value) + ) + if rel_record is None: + return None + + if isinstance(rel_record, NewinstanceType): + rel_record = None + + updates = self.converter.message_to_odoo( + odoo_env, phase, message_value, rel_record, value_present + ) + + if updates: + if rel_record: + rel_record.write(updates) + else: + rel_record = odoo_env[self.model_name].create(updates) + if post_hook: + post_hook(rel_record, message_value) + + if instance and not isinstance(instance, NewinstanceType): + field_value = getattr(instance, self.field_name) + if field_value and rel_record.id in field_value.ids: + return None + + return rel_record + else: + field_value = ( + getattr(instance, self.field_name) + if instance and not isinstance(instance, NewinstanceType) + else None + ) + + updates = self.converter.message_to_odoo( + odoo_env, phase, message_value, field_value, value_present + ) + + if updates: + if field_value: + field_value.write(updates) + if post_hook: + post_hook(field_value, message_value) + return None + else: + rel_record = odoo_env[self.model_name].create(updates) + if post_hook: + post_hook(rel_record, message_value) + return rel_record + return None diff --git a/tests/test_relation.py b/tests/test_relation.py index 9540e05eac5371870d651dd0451ff0a5db048507_dGVzdHMvdGVzdF9yZWxhdGlvbi5weQ==..92b9ff4d49ee7a08b8e9f1f1fa11ed8d9c8525ec_dGVzdHMvdGVzdF9yZWxhdGlvbi5weQ== 100644 --- a/tests/test_relation.py +++ b/tests/test_relation.py @@ -17,9 +17,10 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # ############################################################################## +from typing import Any from odoo import tests from odoo.addons.converter import ( Field, Model, @@ -20,9 +21,10 @@ from odoo import tests from odoo.addons.converter import ( Field, Model, + RelationToMany, RelationToOne, Skip, Xref, @@ -117,3 +119,50 @@ message_to_odoo(self.env, message, self.env["res.users"], converter) self.assertEqual(user.partner_id, new_partner) self.assertEqual(new_partner.color, 3) + + def test_many2many_to_odoo(self): + """Ensure multiple sub-objects linked from the main one gets updated + when Odoo receives a message. + """ + + # This converter wraps a user and adds info from its related partner. + converter = Model( + None, + { + "users": RelationToMany( + "user_ids", + "res.users", + Model( + "user", + { + "email": Field("email"), + "xref": Xref("base"), + }, + ), + ), + "xref": Xref("base"), + }, + ) + + partner = self.env.ref("base.main_partner") + self.assertFalse(partner.user_ids) + + # Run our message reception. + message: dict[str, Any] = { + "users": [ + { + "__type__": "user", + "xref": "user_admin", + }, + { + "__type__": "user", + "xref": "user_demo", + }, + ], + "xref": "main_partner", + } + message_to_odoo(self.env, message, self.env["res.partner"], converter) + + # Check the partner's users + self.assertTrue(partner.user_ids) + self.assertEqual(len(partner.user_ids), 2)