From d143acdd0aeca09d2c46af12426da67506fda21b Mon Sep 17 00:00:00 2001 From: Laurent Defert Date: Tue, 19 Nov 2013 11:04:33 +0100 Subject: [PATCH] Add a 'copy' action to One2Many and Many2Many's set method issue3341 review1271002 --- CHANGELOG | 1 + doc/ref/models/fields.rst | 4 +++ trytond/model/fields/many2many.py | 15 +++++++++ trytond/model/fields/one2many.py | 11 +++++++ trytond/model/modelstorage.py | 31 +++--------------- trytond/tests/test_fields.py | 54 +++++++++++++++++++++++++++++-- 6 files changed, 88 insertions(+), 28 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index dfb192df84..f676408d5b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +* Add a "copy" action to One2Many and Many2Many's set method * Force UTC as timezone (migration script available on tryton-tools) * Add relation_field for many2one diff --git a/doc/ref/models/fields.rst b/doc/ref/models/fields.rst index 451392fe12..b3ac9ffb6b 100644 --- a/doc/ref/models/fields.rst +++ b/doc/ref/models/fields.rst @@ -511,6 +511,10 @@ This field accepts as written value a list of tuples like this: - ``('set'[, ids, ...])``: it will link only the target ids to this record. + - ``('copy', ids[, {: value, ...}])``: it will copy the target + ids to this record. Optional field names and values may be added to + override some of the fields of the copied records. + :class:`One2Many` has some extra required arguments: .. attribute:: One2Many.model_name diff --git a/trytond/model/fields/many2many.py b/trytond/model/fields/many2many.py index 8a94044714..b6020f6342 100644 --- a/trytond/model/fields/many2many.py +++ b/trytond/model/fields/many2many.py @@ -108,6 +108,7 @@ class Many2Many(Field): (``add``, ````), (``unlink_all``), (``set``, ````) + (``copy``, ````, ``[{: value}, ...]``) ''' pool = Pool() if not values: @@ -215,6 +216,20 @@ class Many2Many(Field): }) if to_create: Relation.create(to_create) + elif act[0] == 'copy': + target_ids = list(act[1]) + to_create = [] + default = None + if len(act) > 2: + default = act[2] + for new in Target.copy(target_ids, default=default): + for record_id in ids: + to_create.append({ + self.origin: field_value(record_id), + self.target: new.id, + }) + if to_create: + Relation.create(to_create) else: raise Exception('Bad arguments') diff --git a/trytond/model/fields/one2many.py b/trytond/model/fields/one2many.py index bb9f451eac..4f7ba7f484 100644 --- a/trytond/model/fields/one2many.py +++ b/trytond/model/fields/one2many.py @@ -114,6 +114,7 @@ class One2Many(Field): (``add``, ````), (``unlink_all``), (``set``, ````) + (``copy``, ````, ``[{: value}, ...]``) ''' if not values: return @@ -196,6 +197,16 @@ class One2Many(Field): Target.write(Target.browse(target_ids), { self.field: field_value(record_id), }) + elif act[0] == 'copy': + copy_ids = map(int, act[1]) + + if len(act) > 2: + default = act[2].copy() + else: + default = {} + for record_id in ids: + default[self.field] = field_value(record_id) + Target.copy(copy_ids, default=default) else: raise Exception('Bad arguments') diff --git a/trytond/model/modelstorage.py b/trytond/model/modelstorage.py index 806db4948e..48bc9347e6 100644 --- a/trytond/model/modelstorage.py +++ b/trytond/model/modelstorage.py @@ -245,7 +245,6 @@ class ModelStorage(Model): def convert_data(field_defs, data): data = data.copy() - data_o2m = {} for field_name in field_defs: ftype = field_defs[field_name]['type'] @@ -271,46 +270,26 @@ class ModelStorage(Model): pass elif ftype in ('one2many',): if data[field_name]: - data_o2m[field_name] = data[field_name] - data[field_name] = None + data[field_name] = [('copy', data[field_name])] elif ftype == 'many2many': if data[field_name]: data[field_name] = [('set', data[field_name])] if 'id' in data: del data['id'] - return data, data_o2m + return data - new_ids = {} fields_names = [n for n, f in cls._fields.iteritems() if (not isinstance(f, fields.Function) or isinstance(f, fields.Property))] ids = map(int, records) datas = cls.read(ids, fields_names=fields_names) field_defs = cls.fields_get(fields_names=fields_names) - data_ids = [] to_create = [] - o2m_to_create = [] for data in datas: - data_ids.append(data['id']) - data, data_o2m = convert_data(field_defs, data) + data = convert_data(field_defs, data) to_create.append(data) - o2m_to_create.append(data_o2m) new_records = cls.create(to_create) - for data_id, new_record, data_o2m in izip(data_ids, new_records, - o2m_to_create): - new_ids[data_id] = new_record.id - for field_name in data_o2m: - Relation = pool.get( - field_defs[field_name]['relation']) - relation_field = field_defs[field_name]['relation_field'] - if relation_field: - field = Relation._fields[relation_field] - if field._type == 'reference': - value = str(new_record) - else: - value = new_record.id - Relation.copy(Relation.browse(data_o2m[field_name]), - default={relation_field: value}) + new_ids = dict(izip(ids, map(int, new_records))) fields_translate = {} for field_name, field in field_defs.iteritems(): @@ -332,7 +311,7 @@ class ModelStorage(Model): fields_names=fields_translate.keys() + ['id']) for data in datas: data_id = data['id'] - data, _ = convert_data(fields_translate, data) + data = convert_data(fields_translate, data) cls.write([cls(new_ids[data_id])], data) return cls.browse(new_ids.values()) diff --git a/trytond/tests/test_fields.py b/trytond/tests/test_fields.py index 0d7ae44f4a..49e686aaec 100644 --- a/trytond/tests/test_fields.py +++ b/trytond/tests/test_fields.py @@ -2528,7 +2528,32 @@ class FieldsTestCase(unittest.TestCase): one2many.write([one2many1], { 'targets': [ - ('delete', [target2.id]), + ('copy', [target1.id], {'name': 'copy1'}), + ], + }) + targets = one2many_target.search([ + ('id', 'not in', [target1.id, target2.id]), + ]) + self.assertEqual(len(targets), 1) + self.assertEqual(targets[0].name, 'copy1') + + one2many.write([one2many1], { + 'targets': [ + ('copy', [target2.id]), + ], + }) + self.assertEqual(len(one2many1.targets), 4) + targets = one2many_target.search([ + ('id', 'not in', [target1.id, target2.id]), + ]) + self.assertEqual(len(targets), 2) + names = set([target.name for target in targets]) + self.assertEqual(names, set(('copy1', 'target2'))) + + copy_ids = [target.id for target in targets] + one2many.write([one2many1], { + 'targets': [ + ('delete', [target2.id] + copy_ids), ], }) self.assertEqual(one2many1.targets, (target1,)) @@ -2710,7 +2735,32 @@ class FieldsTestCase(unittest.TestCase): many2many.write([many2many1], { 'targets': [ - ('delete', [target2.id]), + ('copy', [target1.id], {'name': 'copy1'}), + ], + }) + targets = many2many_target.search([ + ('id', 'not in', [target1.id, target2.id]), + ]) + self.assertEqual(len(targets), 1) + self.assertEqual(targets[0].name, 'copy1') + + many2many.write([many2many1], { + 'targets': [ + ('copy', [target2.id]), + ], + }) + self.assertEqual(len(many2many1.targets), 4) + targets = many2many_target.search([ + ('id', 'not in', [target1.id, target2.id]), + ]) + self.assertEqual(len(targets), 2) + names = set([target.name for target in targets]) + self.assertEqual(names, set(('copy1', 'target2'))) + + copy_ids = [target.id for target in targets] + many2many.write([many2many1], { + 'targets': [ + ('delete', [target2.id] + copy_ids), ], }) self.assertEqual(many2many1.targets, (target1,))