From fced1b3a2b7536f4c40665db78d1b0acfe66321e Mon Sep 17 00:00:00 2001 From: "Pedro M. Baeza" Date: Wed, 4 Feb 2026 17:59:03 +0100 Subject: [PATCH] [IMP] onchange_helper: Support one2many fields in onchanges There are one2many fields that are computed, like for example `sale.order.line~agent_ids` in module `sale_commission`. When another module like `sale_product_set` uses this module to populate the order lines, you get this error: ``` odoo.sql_db: bad query: INSERT INTO "sale_order_line_agent" ("id", "create_date", "create_uid", "object_id", "write_date", "write_uid") VALUES (nextval('sale_order_line_agent_id_seq'), ...) RETURNING id odoo-1 | ERROR: null value in column "agent_id" of relation "sale_order_line_agent" violates not-null constraint ``` And this is because the commands that are passed consists on just `(0, 0, {})`. This commit fixes acting in 2 places: - Populating correctly the onchange_spec to get those fields. - Manipulating afterwards o2m fields the same way it's done with the first level to clean many2one values from `(id, display_name)` to just the id. TT60729 --- onchange_helper/models/base.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/onchange_helper/models/base.py b/onchange_helper/models/base.py index c087ddc8c2f..943536384be 100644 --- a/onchange_helper/models/base.py +++ b/onchange_helper/models/base.py @@ -15,7 +15,16 @@ def _get_new_values(self, record, on_change_result): for fieldname, value in vals.items(): if fieldname not in record: column = self._fields[fieldname] - if value and column.type == "many2one": + if column.type == "one2many": + submodel = self.env[column.comodel_name] + for vals in value: + if vals[0] == 0: + # CREATE command + for elem in vals[2]: + subcolumn = submodel._fields[elem] + if subcolumn.type == "many2one" and vals[2][elem]: + vals[2][elem] = vals[2][elem][0] + elif value and column.type == "many2one": value = value[0] # many2one are tuple (id, name) new_values[fieldname] = value return new_values @@ -33,7 +42,12 @@ def play_onchanges(self, values, onchange_fields): # _onchange_spec() will return onchange fields from the default view # we need all fields in the dict even the empty ones # otherwise 'onchange()' will not apply changes to them - onchange_specs = {field_name: "1" for field_name, field in self._fields.items()} + onchange_specs = {} + for field_name, field in self._fields.items(): + onchange_specs[field_name] = "1" + if field.type == "one2many": + for sub_field_name in self.env[field.comodel_name]._fields.keys(): + onchange_specs[f"{field_name}.{sub_field_name}"] = "1" all_values = values.copy() # If self is a record (play onchange on existing record) # we take the value of the field