changeset 41bae0ca6718 in trytond:default details: https://hg.tryton.org/trytond?cmd=changeset&node=41bae0ca6718 description: Skip default values when copying records
This allows users with no write access to a field to copy records if their values have not been modified. issue10536 review336541002 diffstat: CHANGELOG | 1 + trytond/model/modelstorage.py | 15 ++++++-- trytond/tests/copy_.py | 11 ++++++ trytond/tests/test_copy.py | 74 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 97 insertions(+), 4 deletions(-) diffs (185 lines): diff -r 05235e67b280 -r 41bae0ca6718 CHANGELOG --- a/CHANGELOG Mon Oct 11 11:52:42 2021 +0200 +++ b/CHANGELOG Mon Oct 11 15:14:57 2021 +0200 @@ -1,3 +1,4 @@ +* Skip default values when copying records * Use global cache for Function fields in readonly transactions * Add format method to DictSchemaMixin * Allow modify record name on the reports diff -r 05235e67b280 -r 41bae0ca6718 trytond/model/modelstorage.py --- a/trytond/model/modelstorage.py Mon Oct 11 11:52:42 2021 +0200 +++ b/trytond/model/modelstorage.py Mon Oct 11 15:14:57 2021 +0200 @@ -370,7 +370,7 @@ for name, value in default.items() if name.startswith(prefix)} - def convert_data(field_defs, origin): + def convert_data(field_defs, origin, default_values): data = origin.copy() for field_name in field_defs: ftype = field_defs[field_name]['type'] @@ -383,13 +383,16 @@ 'write_uid', ): del data[field_name] + continue if field_name in default: if callable(default[field_name]): data[field_name] = default[field_name](origin) else: data[field_name] = default[field_name] - if (isinstance(field, fields.Function) + if data[field_name] == default_values.get(field_name): + del data[field_name] + elif (isinstance(field, fields.Function) and not isinstance(field, fields.MultiValue)): del data[field_name] elif ftype in ('many2one', 'one2one'): @@ -433,9 +436,10 @@ ids = list(map(int, records)) values = {d['id']: d for d in cls.read(ids, fields_names=fields_names)} field_defs = cls.fields_get(fields_names=fields_names) + default_values = cls.default_get(fields_names, with_rec_name=False) to_create = [] for id_ in ids: - data = convert_data(field_defs, values[id_]) + data = convert_data(field_defs, values[id_], default_values) to_create.append(data) new_records = cls.create(to_create) @@ -459,12 +463,15 @@ # should be the same. with Transaction().set_context(language=lang.code, fuzzy_translation=False): + default_values = cls.default_get( + fields_names, with_rec_name=False) values = cls.read(ids, fields_names=fields_names) to_write = [] for data in values: to_write.append(id2new_records[data['id']]) to_write.append( - convert_data(fields_translate, data)) + convert_data( + fields_translate, data, default_values)) cls.write(*to_write) return new_records diff -r 05235e67b280 -r 41bae0ca6718 trytond/tests/copy_.py --- a/trytond/tests/copy_.py Mon Oct 11 11:52:42 2021 +0200 +++ b/trytond/tests/copy_.py Mon Oct 11 15:14:57 2021 +0200 @@ -101,6 +101,16 @@ file_id = fields.Char("Binary ID") +class CopyAccess(ModelSQL): + "Copy with Access" + __name__ = 'test.copy.access' + name = fields.Char("Name") + + @classmethod + def default_name(cls): + return "Default" + + class CopyTranslate(ModelSQL): "Copy Translate" __name__ = 'test.copy.translate' @@ -121,5 +131,6 @@ CopyMany2ManyReferenceTarget, CopyMany2ManyReferenceRelation, CopyBinary, + CopyAccess, CopyTranslate, module=module, type_='model') diff -r 05235e67b280 -r 41bae0ca6718 trytond/tests/test_copy.py --- a/trytond/tests/test_copy.py Mon Oct 11 11:52:42 2021 +0200 +++ b/trytond/tests/test_copy.py Mon Oct 11 15:14:57 2021 +0200 @@ -4,6 +4,7 @@ import unittest from trytond.model import fields +from trytond.model.exceptions import AccessError from trytond.tests.test_tryton import activate_module, with_transaction from trytond.transaction import Transaction from trytond.pool import Pool @@ -198,6 +199,79 @@ self.assertEqual(record_copy.file_id, record.file_id) self.assertEqual(record_copy.binary_id, record.binary_id) + @with_transaction(context={'_check_access': True}) + def test_no_acccess_copy_with_custom_value(self): + "Test copying field with no access and custom value" + pool = Pool() + Field = pool.get('ir.model.field') + FieldAccess = pool.get('ir.model.field.access') + TestAccess = pool.get('test.copy.access') + + record, = TestAccess.create([{'name': 'foo'}]) + + field, = Field.search([ + ('model.model', '=', 'test.copy.access'), + ('name', '=', 'name'), + ]) + FieldAccess.create([{ + 'field': field.id, + 'group': None, + 'perm_read': True, + 'perm_write': False, + }]) + + with self.assertRaises(AccessError): + new_record, = TestAccess.copy([record]) + + @with_transaction(context={'_check_access': True}) + def test_no_acccess_copy_with_default(self): + "Test copying field with no access but default value" + pool = Pool() + Field = pool.get('ir.model.field') + FieldAccess = pool.get('ir.model.field.access') + TestAccess = pool.get('test.copy.access') + + field, = Field.search([ + ('model.model', '=', 'test.copy.access'), + ('name', '=', 'name'), + ]) + FieldAccess.create([{ + 'field': field.id, + 'group': None, + 'perm_read': True, + 'perm_write': False, + }]) + + record, = TestAccess.create([{}]) + self.assertEqual(record.name, "Default") + new_record, = TestAccess.copy([record]) + self.assertEqual(new_record.name, "Default") + + @with_transaction(context={'_check_access': True}) + def test_no_acccess_copy_with_defaults(self): + "Test copying field with no access and defaults" + pool = Pool() + Field = pool.get('ir.model.field') + FieldAccess = pool.get('ir.model.field.access') + TestAccess = pool.get('test.copy.access') + + record, = TestAccess.create([{}]) + + field, = Field.search([ + ('model.model', '=', 'test.copy.access'), + ('name', '=', 'name'), + ]) + FieldAccess.create([{ + 'field': field.id, + 'group': None, + 'perm_read': True, + 'perm_write': False, + }]) + + with self.assertRaises(AccessError): + new_record, = TestAccess.copy( + [record], default={'name': 'nondefault'}) + class CopyTranslationTestCase(TranslationTestCase): "Test copy translation"