diff --git a/trytond/trytond/ir/__init__.py b/trytond/trytond/ir/__init__.py
index 77afb9c8399..820ca9690de 100644
--- a/trytond/trytond/ir/__init__.py
+++ b/trytond/trytond/ir/__init__.py
@@ -12,7 +12,8 @@
def register():
Pool.register(
- configuration.Configuration,
+ configuration.Configuration, # Must be registered first, see
+ # `__register__` implementation
translation.Translation,
translation.TranslationSetStart,
translation.TranslationSetSucceed,
diff --git a/trytond/trytond/ir/configuration.py b/trytond/trytond/ir/configuration.py
index b1569cd46a6..00d6c898284 100644
--- a/trytond/trytond/ir/configuration.py
+++ b/trytond/trytond/ir/configuration.py
@@ -3,6 +3,7 @@
from trytond.cache import Cache
from trytond.config import config
from trytond.model import ModelSingleton, ModelSQL, fields
+from trytond.transaction import Transaction
class Configuration(ModelSingleton, ModelSQL):
@@ -12,6 +13,15 @@ class Configuration(ModelSingleton, ModelSQL):
hostname = fields.Char("Hostname", strip=False)
_get_language_cache = Cache('ir_configuration.get_language')
+ @classmethod
+ def __register__(cls, module_name):
+ # This migration must be done before any translation creation takes
+ # place
+ cursor = Transaction().connection.cursor()
+ cursor.execute(
+ "ALTER TABLE ir_translation ALTER COLUMN res_id DROP NOT NULL")
+ super().__register__(module_name)
+
@staticmethod
def default_language():
return config.get('database', 'language')
diff --git a/trytond/trytond/ir/message.xml b/trytond/trytond/ir/message.xml
index 2baecf7c3a5..f925358790b 100644
--- a/trytond/trytond/ir/message.xml
+++ b/trytond/trytond/ir/message.xml
@@ -408,5 +408,11 @@ this repository contains the full copyright notices and license terms. -->
%(field)s (model name)
+
+ The translation name must be unique by language and type when it's not related to a record.
+
+
+ The translation name must be unique by language, type and source when it's not related to a record.
+
diff --git a/trytond/trytond/ir/translation.py b/trytond/trytond/ir/translation.py
index 115c6ed7d47..d4cb197eca2 100644
--- a/trytond/trytond/ir/translation.py
+++ b/trytond/trytond/ir/translation.py
@@ -61,7 +61,7 @@ class Translation(ModelSQL, ModelView):
__name__ = "ir.translation"
name = fields.Char('Field Name', required=True)
- res_id = fields.Integer('Resource ID', required=True)
+ res_id = fields.Integer('Resource ID', domain=[('res_id', '>=', 0)])
lang = fields.Selection('get_language', string='Language')
type = fields.Selection(TRANSLATION_TYPE, string='Type',
required=True)
@@ -120,6 +120,11 @@ def __register__(cls, module_name):
table.drop_constraint('translation_md5_uniq')
table.drop_column('src_md5')
+ # Migration from 7.2
+ cursor.execute(*ir_translation.update(
+ [ir_translation.res_id], [Null],
+ where=(ir_translation.res_id == -1)))
+
super(Translation, cls).__register__(module_name)
# Migration from 3.8: rename odt type in report
@@ -162,7 +167,7 @@ def register_model(cls, model, module_name):
'value', 'module', 'fuzzy', 'res_id')],
[[
name, INTERNAL_LANG, 'model', src,
- '', module_name, False, -1]]))
+ '', module_name, False, Null]]))
else:
cursor.execute(*ir_translation.update(
[ir_translation.src],
@@ -210,7 +215,7 @@ def insert(field, type, name, string):
*ir_translation.insert(columns,
[[
name, INTERNAL_LANG, type, val,
- '', module_name, False, -1]]))
+ '', module_name, False, Null]]))
inserted = True
for field_name, field in model._fields.items():
@@ -251,7 +256,7 @@ def update_insert_button(state_name, button):
trans_name, INTERNAL_LANG,
'wizard_button', button.string,
'', module_name,
- False, -1]]))
+ False, Null]]))
elif trans_buttons[trans_name] != button.string:
cursor.execute(*ir_translation.update(
[ir_translation.src],
@@ -269,10 +274,6 @@ def update_insert_button(state_name, button):
def default_fuzzy():
return False
- @staticmethod
- def default_res_id():
- return -1
-
def get_model(self, name):
return self.name.split(',')[0]
@@ -614,7 +615,7 @@ def get_sources(cls, args):
('value', '!=', ''),
('value', '!=', None),
('fuzzy', '=', False),
- ('res_id', '=', -1),
+ ('res_id', '=', None),
]
if source is not None:
clause.append(('src', '=', source))
@@ -655,7 +656,7 @@ def get_report(cls, report_name, text):
('value', '!=', ''),
('value', '!=', None),
('fuzzy', '=', False),
- ('res_id', '=', -1),
+ ('res_id', '=', None),
], order=[('module', 'DESC')])
for translation in translations:
cache.setdefault(translation.src, translation.value)
@@ -799,7 +800,7 @@ def override_translation(ressource_id, new_translation):
])
res_id = model_data.db_id
else:
- res_id = -1
+ res_id = None
with Transaction().set_context(module=res_id_module):
domain = [
('name', '=', new_translation.name),
@@ -864,14 +865,16 @@ def override_translation(ressource_id, new_translation):
if (model in fs_id2prop
and res_id in fs_id2prop[model]):
res_id, noupdate = fs_id2prop[model][res_id]
+ elif res_id:
+ continue
if res_id:
try:
res_id = int(res_id)
except ValueError:
- res_id = None
- if not res_id:
- res_id = -1
+ continue
+ else:
+ res_id = None
translation.res_id = res_id
key = translation.unique_key
@@ -954,7 +957,7 @@ def translation_export(cls, lang, module):
'name': translation.name,
}
res_id = translation.res_id
- if res_id >= 0:
+ if res_id:
model, _ = translation.name.split(',')
if model in db_id2fs_id:
res_id = db_id2fs_id[model].get(res_id)
@@ -1135,7 +1138,7 @@ def set_report(self):
report.report_name, INTERNAL_LANG,
'report', string,
'', module,
- False, -1]]))
+ False, Null]]))
for (report_name, module), strings in report_strings.items():
query = translation.delete(
where=(translation.name == report_name)
@@ -1234,7 +1237,7 @@ def set_view(self):
view.model, INTERNAL_LANG,
'view', string,
'', view.module,
- False, -1]]))
+ False, Null]]))
if strings:
cursor.execute(*translation.delete(
where=(translation.name == view.model)
@@ -1301,7 +1304,7 @@ def _clean_model(translation):
Model = pool.get(model_name)
except KeyError:
return True
- if translation.res_id >= 0:
+ if translation.res_id:
if field_name not in Model._fields:
return True
field = Model._fields[field_name]
@@ -1413,7 +1416,7 @@ def transition_clean(self):
to_delete.append(translation.id)
else:
keys.add(key)
- if translation.type == 'model' and translation.res_id >= 0:
+ if translation.type == 'model' and translation.res_id:
model_name, _ = translation.name.split(',', 1)
records[model_name][translation.res_id].add(translation.id)
@@ -1606,7 +1609,7 @@ def do_update(self, action):
where=(translation.name == row['name'])
& (translation.type == row['type'])
& (translation.lang == lang)
- & (translation.res_id == (row['res_id'] or -1))
+ & (translation.res_id == row['res_id'])
& (translation.module == row['module'])))
cursor.execute(*translation.select(
diff --git a/trytond/trytond/tests/test_model.py b/trytond/trytond/tests/test_model.py
index ef1babffe62..2d1ab3148c2 100644
--- a/trytond/trytond/tests/test_model.py
+++ b/trytond/trytond/tests/test_model.py
@@ -326,7 +326,6 @@ def test_fields_get(self):
'lang': self.other_language,
'src': "Name",
'name': 'test.model,name',
- 'res_id': -1,
'value': "Nom",
'type': 'field',
}])