details:   https://code.tryton.org/tryton/commit/80db193bd3ac
branch:    default
user:      Cédric Krier <[email protected]>
date:      Tue Feb 24 11:35:00 2026 +0100
description:
        Add model and field Many2One on translations
diffstat:

 trytond/trytond/ir/translation.py            |  98 ++++++++++++++++++++++++----
 trytond/trytond/ir/view/translation_list.xml |   3 +-
 2 files changed, 86 insertions(+), 15 deletions(-)

diffs (149 lines):

diff -r 6af5c937a3ce -r 80db193bd3ac trytond/trytond/ir/translation.py
--- a/trytond/trytond/ir/translation.py Tue Feb 24 11:34:42 2026 +0100
+++ b/trytond/trytond/ir/translation.py Tue Feb 24 11:35:00 2026 +0100
@@ -14,6 +14,7 @@
 from sql.aggregate import Max
 from sql.conditionals import Case
 from sql.functions import CharLength, Position, Substring
+from sql.operators import Concat
 
 import trytond.config as config
 from trytond.cache import Cache
@@ -67,6 +68,11 @@
         fields.fmany2one(
             'overriding_module_ref', 'overriding_module', 'ir.module,name',
             "Overriding Module", readonly=True),
+        fields.fmany2one(
+            'model_ref', 'model', 'ir.model,name', "Model", readonly=True),
+        fields.fmany2one(
+            'field_ref', 'field,model', 'ir.model.field,name,model', "Field",
+            readonly=True),
         ModelSQL, ModelView):
     __name__ = "ir.translation"
 
@@ -96,8 +102,10 @@
 
     module = fields.Char('Module', readonly=True)
     fuzzy = fields.Boolean('Fuzzy')
-    model = fields.Function(fields.Char('Model'), 'get_model',
-            searcher='search_model')
+    model = fields.Function(
+        fields.Char("Model"), 'get_model', searcher='search_model')
+    field = fields.Function(
+        fields.Char("Field"), 'get_field', searcher='search_field')
     overriding_module = fields.Char('Overriding Module', readonly=True)
     _translation_cache = Cache('ir.translation', context=False)
     _translation_report_cache = Cache(
@@ -285,6 +293,80 @@
         return self.name.split(',')[0]
 
     @classmethod
+    def search_model(cls, name, clause):
+        table = cls.__table__()
+        _, operator, value = clause
+        Operator = fields.SQL_OPERATORS[operator]
+        return [('id', 'in', table.select(table.id,
+                    where=Operator(Substring(table.name, 1,
+                            Case((
+                                    Position(',', table.name) > 0,
+                                    Position(',', table.name) - 1),
+                                else_=CharLength(table.name))), value)))]
+
+    @classmethod
+    def domain_model_ref(cls, clause, tables):
+        pool = Pool()
+        IrModel = pool.get('ir.model')
+        table, _ = tables[None]
+        if 'model_ref' not in tables:
+            ir_model = IrModel.__table__()
+            tables['model_ref'] = {
+                None: (ir_model, ir_model.name == Substring(table.name, 1,
+                        Case((
+                                Position(',', table.name) > 0,
+                                Position(',', table.name) - 1),
+                            else_=CharLength(table.name)))),
+                }
+        nested = clause[0][len('model_ref') + 1:]
+        if not nested:
+            if isinstance(clause[2], str):
+                nested = 'rec_name'
+            else:
+                nested = 'id'
+        domain = [(nested, *clause[1:])]
+        tables, clause = IrModel.search_domain(
+            domain, tables=tables['model_ref'])
+        return clause
+
+    def get_field(self, name):
+        return self.name.split(',', 1)[1] if ',' in self.name else None
+
+    @classmethod
+    def search_field(cls, name, clause):
+        table = cls.__table__()
+        _, operator, value = clause
+        Operator = fields.SQL_OPERATORS[operator]
+        return [('id', 'in', table.select(table.id,
+                    where=Operator(Substring(table.name,
+                            Case((
+                                    Position(',', table.name) > 0,
+                                    Position(',', table.name) - 1),
+                                else_=CharLength(table.name))), value)))]
+
+    @classmethod
+    def domain_field_ref(cls, clause, tables):
+        pool = Pool()
+        Field = pool.get('ir.model.field')
+        table, _ = tables[None]
+        if 'field_ref' not in tables:
+            field = Field.__table__()
+            tables['field_ref'] = {
+                None: (field, (table.name == Concat(Concat(
+                            field.model, ','), field.name))),
+                }
+        nested = clause[0][len('model_ref') + 1:]
+        if not nested:
+            if isinstance(clause[2], str):
+                nested = 'rec_name'
+            else:
+                nested = 'id'
+        domain = [(nested, *clause[1:])]
+        tables, clause = Field.search_domain(
+            domain, tables=tables['field_ref'])
+        return clause
+
+    @classmethod
     def search_rec_name(cls, name, clause):
         clause = tuple(clause)
         if clause[1].startswith('!') or clause[1].startswith('not '):
@@ -298,18 +380,6 @@
             ]
 
     @classmethod
-    def search_model(cls, name, clause):
-        table = cls.__table__()
-        _, operator, value = clause
-        Operator = fields.SQL_OPERATORS[operator]
-        return [('id', 'in', table.select(table.id,
-                    where=Operator(Substring(table.name, 1,
-                            Case((
-                                    Position(',', table.name) > 0,
-                                    Position(',', table.name) - 1),
-                                else_=CharLength(table.name))), value)))]
-
-    @classmethod
     def get_language(cls):
         language = Transaction().language
         result = cls._get_language_cache.get(language)
diff -r 6af5c937a3ce -r 80db193bd3ac 
trytond/trytond/ir/view/translation_list.xml
--- a/trytond/trytond/ir/view/translation_list.xml      Tue Feb 24 11:34:42 
2026 +0100
+++ b/trytond/trytond/ir/view/translation_list.xml      Tue Feb 24 11:35:00 
2026 +0100
@@ -6,7 +6,8 @@
     <field name="src_plural" widget="char" expand="2" optional="1"/>
     <field name="value" widget="char" expand="2"/>
     <field name="fuzzy"/>
-    <field name="name" expand="1" optional="1"/>
+    <field name="model_ref" expand="1" optional="1"/>
+    <field name="field_ref" expand="1" optional="1"/>
     <field name="lang"/>
     <field name="type" optional="1"/>
     <field name="res_id" optional="1"/>

Reply via email to