From aed753e21c2115a6c3021ef0ec61028849b8f446 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Tue, 16 Mar 2010 13:35:13 +0100 Subject: [PATCH 1/2] Don't raise error message about required Integer or Float Integers and Floats have 0 as default value at the database level --- trytond/model/fields/integer.py | 2 +- trytond/model/modelsql.py | 18 +++++++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/trytond/model/fields/integer.py b/trytond/model/fields/integer.py index 415406069c..72f1d08cc1 100644 --- a/trytond/model/fields/integer.py +++ b/trytond/model/fields/integer.py @@ -11,7 +11,7 @@ class Integer(Field): _type = 'integer' -class BigInteger(Field): +class BigInteger(Integer): ''' Define an integer field (``long``). ''' diff --git a/trytond/model/modelsql.py b/trytond/model/modelsql.py index e946fcf389..0a23d0c68e 100644 --- a/trytond/model/modelsql.py +++ b/trytond/model/modelsql.py @@ -355,9 +355,11 @@ class ModelSQL(ModelStorage): for field_name in self._columns: field = self._columns[field_name] # Check required fields - if field.required \ - and not hasattr(field, 'set') \ - and field_name not in ('create_uid', 'create_date'): + if field.required and \ + not hasattr(field, 'set') and \ + not isinstance(field, (fields.Interger, + fields.Float)) and \ + field_name not in ('create_uid', 'create_date'): if not values.get(field_name): self.raise_user_error(cursor2, 'required_field', error_args=self._get_error_args( @@ -876,10 +878,12 @@ class ModelSQL(ModelStorage): continue field = self._columns[field_name] # Check required fields - if field.required \ - and not hasattr(field, 'set') \ - and field_name not in \ - ('create_uid', 'create_date'): + if field.required and \ + not hasattr(field, 'set') and \ + not isinstance(field, (fields.Interger, + fields.Float)) and \ + field_name not in ('create_uid', + 'create_date'): if not values[field_name]: self.raise_user_error(cursor2, 'required_field', From dab0df51de8528e638834947700b2ac23b5cdbf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 17 Mar 2010 11:43:21 +0100 Subject: [PATCH 2/2] export_data return empty value for invisible fields --- CHANGELOG | 1 + trytond/model/modelstorage.py | 82 ++++--- trytond/test/__init__.py | 1 + trytond/test/export_data.py | 58 +++++ trytond/tests/test_exportdata.py | 396 +++++++++++++++++++++++++++++++ trytond/tests/test_tryton.py | 2 + 6 files changed, 509 insertions(+), 31 deletions(-) create mode 100644 trytond/test/export_data.py create mode 100644 trytond/tests/test_exportdata.py diff --git a/CHANGELOG b/CHANGELOG index f44a8ea7f7..b82612209c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +* export_data return empty value for invisible fields * Don't allow to use float in Numeric fields * set/get function on Fields take a list of ids * New interface for Function/Property fields diff --git a/trytond/model/modelstorage.py b/trytond/model/modelstorage.py index a35a761510..f512fea552 100644 --- a/trytond/model/modelstorage.py +++ b/trytond/model/modelstorage.py @@ -496,41 +496,61 @@ class ModelStorage(Model): local_cache=local_cache, context=context) for x in ids), context=context) - def __export_row(self, cursor, user, row, fields_names, context=None): + def __export_row(self, cursor, user, record, fields_names, context=None): lines = [] data = ['' for x in range(len(fields_names))] done = [] for fpos in range(len(fields_names)): - field = fields_names[fpos] - if field: - row2 = row - i = 0 - while i < len(field): - row2 = row2[field[i]] - if not row2: + fields_tree = fields_names[fpos] + if not fields_tree: + continue + value = record + i = 0 + while i < len(fields_tree): + if not isinstance(value, BrowseRecord): + break + field_name = fields_tree[i] + model_obj = self.pool.get(value._model_name) + field = model_obj._columns[field_name] + if field.states and 'invisible' in field.states: + pyson_invisible = PYSONEncoder().encode( + field.states['invisible']) + env = EvalEnvironment(value, model_obj) + env.update(context) + env['current_date'] = datetime.datetime.today() + env['time'] = time + env['context'] = context + env['active_id'] = value.id + invisible = PYSONDecoder(env).decode(pyson_invisible) + if invisible: + value = '' break - if isinstance(row2, (BrowseRecordList, list)): - first = True - fields2 = [(x[:i+1]==field[:i+1] and x[i+1:]) \ - or [] for x in fields_names] - if fields2 in done: - break - done.append(fields2) - for row2 in row2: - lines2 = self.__export_row(cursor, user, row2, - fields2, context) - if first: - for fpos2 in range(len(fields_names)): - if lines2 and lines2[0][fpos2]: - data[fpos2] = lines2[0][fpos2] - lines += lines2[1:] - first = False - else: - lines += lines2 + value = value[field_name] + if isinstance(value, (BrowseRecordList, list)): + first = True + child_fields_names = [(x[:i + 1] == fields_tree[:i + 1] and + x[i + 1:]) or [] for x in fields_names] + if child_fields_names in done: break - i += 1 - if i == len(field): - data[fpos] = row2 or '' + done.append(child_fields_names) + for child_record in value: + child_lines = self.__export_row(cursor, user, + child_record, child_fields_names, context) + if first: + for child_fpos in xrange(len(fields_names)): + if child_lines and child_lines[0][child_fpos]: + data[child_fpos] = \ + child_lines[0][child_fpos] + lines += child_lines[1:] + first = False + else: + lines += child_lines + break + i += 1 + if i == len(fields_tree): + if value is None: + value = '' + data[fpos] = value return [data] + lines def export_data(self, cursor, user, ids, fields_names, context=None): @@ -547,8 +567,8 @@ class ModelStorage(Model): ''' fields_names = [x.split('/') for x in fields_names] datas = [] - for row in self.browse(cursor, user, ids, context): - datas += self.__export_row(cursor, user, row, fields_names, context) + for record in self.browse(cursor, user, ids, context): + datas += self.__export_row(cursor, user, record, fields_names, context) return datas def import_data(self, cursor, user, fields_names, datas, context=None): diff --git a/trytond/test/__init__.py b/trytond/test/__init__.py index f64dce2f96..5fe3bc17f4 100644 --- a/trytond/test/__init__.py +++ b/trytond/test/__init__.py @@ -5,3 +5,4 @@ from test import * from model import * from mptt import * from import_data import * +from export_data import * diff --git a/trytond/test/export_data.py b/trytond/test/export_data.py new file mode 100644 index 0000000000..7483f9f5d9 --- /dev/null +++ b/trytond/test/export_data.py @@ -0,0 +1,58 @@ +#This file is part of Tryton. The COPYRIGHT file at the top level of +#this repository contains the full copyright notices and license terms. +"Test for export_data" +from trytond.model import ModelSQL, fields + + +class ExportDataTarget(ModelSQL): + "Export Data Target" + _name = 'test.export_data.target' + _description = __doc__ + name = fields.Char('Name') + +ExportDataTarget() + + +class ExportData(ModelSQL): + "Export Data" + _name = 'test.export_data' + _description = __doc__ + boolean = fields.Boolean('Boolean') + integer = fields.Integer('Integer') + float = fields.Float('Float') + numeric = fields.Numeric('Numeric') + char = fields.Char('Char') + text = fields.Text('Text') + sha = fields.Sha('Sha') + date = fields.Date('Date') + datetime = fields.DateTime('DateTime') + selection = fields.Selection([ + ('select1', 'Select 1'), + ('select2', 'Select 2'), + ], 'Selection') + many2one = fields.Many2One('test.export_data.target', + 'Many2One') + many2many = fields.Many2Many('test.export_data.relation', + 'many2many', 'target', 'Many2Many') + one2many = fields.One2Many('test.export_data.target', 'one2many', + 'One2Many') + reference = fields.Reference('Reference', [ + ('test.export_data.target', 'Target'), + ]) + +ExportData() + + +class ExportDataTarget(ExportDataTarget): + one2many = fields.Many2One('test.export_data', 'Export Data') + +ExportDataTarget() + +class ExportDataRelation(ModelSQL): + "Export Data Many2Many" + _name = 'test.export_data.relation' + _description = __doc__ + many2many = fields.Many2One('test.export_data', 'Export Data') + target = fields.Many2One('test.export_data.target', 'Target') + +ExportDataRelation() diff --git a/trytond/tests/test_exportdata.py b/trytond/tests/test_exportdata.py new file mode 100644 index 0000000000..7947834909 --- /dev/null +++ b/trytond/tests/test_exportdata.py @@ -0,0 +1,396 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +#This file is part of Tryton. The COPYRIGHT file at the top level of +#this repository contains the full copyright notices and license terms. + +import unittest +from decimal import Decimal +import datetime +from trytond.tests.test_tryton import POOL, DB, USER, CONTEXT, install_module + + +class ExportDataTestCase(unittest.TestCase): + ''' + Test export_data. + ''' + + def setUp(self): + install_module('test') + self.export_data = POOL.get('test.export_data') + self.export_data_target = POOL.get('test.export_data.target') + self.export_data_relation = POOL.get('test.export_data.relation') + + def test0010boolean(self): + ''' + Test boolean. + ''' + cursor = DB.cursor() + + export1_id = self.export_data.create(cursor, USER, { + 'boolean': True, + }, context=CONTEXT) + self.assert_(self.export_data.export_data(cursor, USER, [export1_id], + ['boolean'], context=CONTEXT) == [[True]]) + + export2_id = self.export_data.create(cursor, USER, { + 'boolean': False, + }, context=CONTEXT) + self.assert_(self.export_data.export_data(cursor, USER, [export2_id], + ['boolean'], context=CONTEXT) == [[False]]) + + self.assert_(self.export_data.export_data(cursor, USER, + [export1_id, export2_id], ['boolean'], context=CONTEXT) == + [[True], [False]]) + + cursor.rollback() + cursor.close() + + def test0020integer(self): + ''' + Test integer. + ''' + cursor = DB.cursor() + + export1_id = self.export_data.create(cursor, USER, { + 'integer': 2, + }, context=CONTEXT) + self.assert_(self.export_data.export_data(cursor, USER, [export1_id], + ['integer'], context=CONTEXT) == [[2]]) + + export2_id = self.export_data.create(cursor, USER, { + 'integer': 0, + }, context=CONTEXT) + self.assert_(self.export_data.export_data(cursor, USER, [export2_id], + ['integer'], context=CONTEXT) == [[0]]) + + self.assert_(self.export_data.export_data(cursor, USER, + [export1_id, export2_id], ['integer'], context=CONTEXT) == + [[2], [0]]) + + cursor.rollback() + cursor.close() + + def test0030float(self): + ''' + Test float. + ''' + cursor = DB.cursor() + + export1_id = self.export_data.create(cursor, USER, { + 'float': 1.1, + }, context=CONTEXT) + self.assert_(self.export_data.export_data(cursor, USER, [export1_id], + ['float'], context=CONTEXT) == [[1.1]]) + + export2_id = self.export_data.create(cursor, USER, { + 'float': 0, + }, context=CONTEXT) + self.assert_(self.export_data.export_data(cursor, USER, [export2_id], + ['float'], context=CONTEXT) == [[0]]) + + self.assert_(self.export_data.export_data(cursor, USER, + [export1_id, export2_id], ['float'], context=CONTEXT) == + [[1.1], [0]]) + + cursor.rollback() + cursor.close() + + def test0040numeric(self): + ''' + Test numeric. + ''' + cursor = DB.cursor() + + export1_id = self.export_data.create(cursor, USER, { + 'numeric': Decimal('1.1'), + }, context=CONTEXT) + self.assert_(self.export_data.export_data(cursor, USER, [export1_id], + ['numeric'], context=CONTEXT) == [[Decimal('1.1')]]) + + export2_id = self.export_data.create(cursor, USER, { + 'numeric': Decimal('0'), + }, context=CONTEXT) + self.assert_(self.export_data.export_data(cursor, USER, [export2_id], + ['numeric'], context=CONTEXT) == [[Decimal('0')]]) + + self.assert_(self.export_data.export_data(cursor, USER, + [export1_id, export2_id], ['numeric'], context=CONTEXT) == + [[Decimal('1.1')], [Decimal('0')]]) + + cursor.rollback() + cursor.close() + + def test0050char(self): + ''' + Test char. + ''' + cursor = DB.cursor() + + export1_id = self.export_data.create(cursor, USER, { + 'char': 'test', + }, context=CONTEXT) + self.assert_(self.export_data.export_data(cursor, USER, [export1_id], + ['char'], context=CONTEXT) == [['test']]) + + export2_id = self.export_data.create(cursor, USER, { + 'char': False, + }, context=CONTEXT) + self.assert_(self.export_data.export_data(cursor, USER, [export2_id], + ['char'], context=CONTEXT) == [['']]) + + self.assert_(self.export_data.export_data(cursor, USER, + [export1_id, export2_id], ['char'], context=CONTEXT) == + [['test'], ['']]) + + cursor.rollback() + cursor.close() + + def test0060text(self): + ''' + Test text. + ''' + cursor = DB.cursor() + + export1_id = self.export_data.create(cursor, USER, { + 'text': 'test', + }, context=CONTEXT) + self.assert_(self.export_data.export_data(cursor, USER, [export1_id], + ['text'], context=CONTEXT) == [['test']]) + + export2_id = self.export_data.create(cursor, USER, { + 'text': False, + }, context=CONTEXT) + self.assert_(self.export_data.export_data(cursor, USER, [export2_id], + ['text'], context=CONTEXT) == [['']]) + + self.assert_(self.export_data.export_data(cursor, USER, + [export1_id, export2_id], ['text'], context=CONTEXT) == + [['test'], ['']]) + + cursor.rollback() + cursor.close() + + def test0070sha(self): + ''' + Test sha. + ''' + cursor = DB.cursor() + + export1_id = self.export_data.create(cursor, USER, { + 'sha': 'Test', + }, context=CONTEXT) + self.assert_(self.export_data.export_data(cursor, USER, [export1_id], + ['sha'], context=CONTEXT) == [['640ab2bae07bedc4c163f679a746f7ab7fb5d1fa']]) + + cursor.rollback() + cursor.close() + + def test0080date(self): + ''' + Test date. + ''' + cursor = DB.cursor() + + export1_id = self.export_data.create(cursor, USER, { + 'date': datetime.date(2010, 1, 1), + }, context=CONTEXT) + self.assert_(self.export_data.export_data(cursor, USER, [export1_id], + ['date'], context=CONTEXT) == [[datetime.date(2010, 1, 1)]]) + + export2_id = self.export_data.create(cursor, USER, { + 'date': False, + }, context=CONTEXT) + self.assert_(self.export_data.export_data(cursor, USER, [export2_id], + ['date'], context=CONTEXT) == [['']]) + + self.assert_(self.export_data.export_data(cursor, USER, + [export1_id, export2_id], ['date'], context=CONTEXT) == + [[datetime.date(2010, 1, 1)], ['']]) + + cursor.rollback() + cursor.close() + + def test0090datetime(self): + ''' + Test datetime. + ''' + cursor = DB.cursor() + + export1_id = self.export_data.create(cursor, USER, { + 'datetime': datetime.datetime(2010, 1, 1, 12, 0, 0), + }, context=CONTEXT) + self.assert_(self.export_data.export_data(cursor, USER, [export1_id], + ['datetime'], context=CONTEXT) == [[datetime.datetime(2010, 1, + 1, 12, 0, 0)]]) + + export2_id = self.export_data.create(cursor, USER, { + 'datetime': False, + }, context=CONTEXT) + self.assert_(self.export_data.export_data(cursor, USER, [export2_id], + ['datetime'], context=CONTEXT) == [['']]) + + self.assert_(self.export_data.export_data(cursor, USER, + [export1_id, export2_id], ['datetime'], context=CONTEXT) == + [[datetime.datetime(2010, 1, 1, 12, 0, 0)], ['']]) + + cursor.rollback() + cursor.close() + + def test0100selection(self): + ''' + Test selection. + ''' + cursor = DB.cursor() + + export1_id = self.export_data.create(cursor, USER, { + 'selection': 'select1', + }, context=CONTEXT) + self.assert_(self.export_data.export_data(cursor, USER, [export1_id], + ['selection'], context=CONTEXT) == [['select1']]) + + export2_id = self.export_data.create(cursor, USER, { + 'selection': False, + }, context=CONTEXT) + self.assert_(self.export_data.export_data(cursor, USER, [export2_id], + ['selection'], context=CONTEXT) == [['']]) + + self.assert_(self.export_data.export_data(cursor, USER, + [export1_id, export2_id], ['selection'], context=CONTEXT) == + [['select1'], ['']]) + + cursor.rollback() + cursor.close() + + def test0110many2one(self): + ''' + Test many2one. + ''' + cursor = DB.cursor() + + target_id = self.export_data_target.create(cursor, USER, { + 'name': 'Target Test', + }, context=CONTEXT) + export1_id = self.export_data.create(cursor, USER, { + 'many2one': target_id, + }, context=CONTEXT) + self.assert_(self.export_data.export_data(cursor, USER, [export1_id], + ['many2one/name'], context=CONTEXT) == [['Target Test']]) + + export2_id = self.export_data.create(cursor, USER, { + 'many2one': False, + }, context=CONTEXT) + self.assert_(self.export_data.export_data(cursor, USER, [export2_id], + ['many2one/name'], context=CONTEXT) == [['']]) + + self.assert_(self.export_data.export_data(cursor, USER, + [export1_id, export2_id], ['many2one/name'], context=CONTEXT) == + [['Target Test'], ['']]) + + cursor.rollback() + cursor.close() + + def test0120many2many(self): + ''' + Test many2many. + ''' + cursor = DB.cursor() + + target1_id = self.export_data_target.create(cursor, USER, { + 'name': 'Target 1', + }, context=CONTEXT) + export1_id = self.export_data.create(cursor, USER, { + 'many2many': [('set', [target1_id])], + }, context=CONTEXT) + self.assert_(self.export_data.export_data(cursor, USER, [export1_id], + ['many2many/name'], context=CONTEXT) == [['Target 1']]) + + target2_id = self.export_data_target.create(cursor, USER, { + 'name': 'Target 2', + }, context=CONTEXT) + self.export_data.write(cursor, USER, export1_id, { + 'many2many': [('set', [target1_id, target2_id])], + }, context=CONTEXT) + self.assert_(self.export_data.export_data(cursor, USER, [export1_id], + ['id', 'many2many/name'], context=CONTEXT) == + [[export1_id, 'Target 1'], ['', 'Target 2']]) + + export2_id = self.export_data.create(cursor, USER, { + 'many2many': False, + }, context=CONTEXT) + self.assert_(self.export_data.export_data(cursor, USER, [export2_id], + ['many2many/name'], context=CONTEXT) == [['']]) + + self.assert_(self.export_data.export_data(cursor, USER, + [export1_id, export2_id], ['id', 'many2many/name'], context=CONTEXT) + == [[export1_id, 'Target 1'], ['', 'Target 2'], [export2_id, '']]) + + cursor.rollback() + cursor.close() + + def test0130one2many(self): + ''' + Test one2many. + ''' + cursor = DB.cursor() + + export1_id = self.export_data.create(cursor, USER, {}, context=CONTEXT) + target1_id = self.export_data_target.create(cursor, USER, { + 'name': 'Target 1', + 'one2many': export1_id, + }, context=CONTEXT) + self.assert_(self.export_data.export_data(cursor, USER, [export1_id], + ['one2many/name'], context=CONTEXT) == [['Target 1']]) + + target2_id = self.export_data_target.create(cursor, USER, { + 'name': 'Target 2', + 'one2many': export1_id, + }, context=CONTEXT) + self.assert_(self.export_data.export_data(cursor, USER, [export1_id], + ['id', 'one2many/name'], context=CONTEXT) == + [[export1_id, 'Target 1'], ['', 'Target 2']]) + + export2_id = self.export_data.create(cursor, USER, {}, context=CONTEXT) + self.assert_(self.export_data.export_data(cursor, USER, [export2_id], + ['one2many/name'], context=CONTEXT) == [['']]) + + self.assert_(self.export_data.export_data(cursor, USER, + [export1_id, export2_id], ['id', 'one2many/name'], context=CONTEXT) + == [[export1_id, 'Target 1'], ['', 'Target 2'], [export2_id, '']]) + + cursor.rollback() + cursor.close() + + def test0140reference(self): + ''' + Test reference. + ''' + cursor = DB.cursor() + + target1_id = self.export_data_target.create(cursor, USER, {}, + context=CONTEXT) + export1_id = self.export_data.create(cursor, USER, { + 'reference': 'test.export_data.target,%s' % target1_id, + }, context=CONTEXT) + self.assert_(self.export_data.export_data(cursor, USER, [export1_id], + ['reference'], context=CONTEXT) == + [['test.export_data.target,%s' % target1_id]]) + + export2_id = self.export_data.create(cursor, USER, { + 'reference': False, + }, context=CONTEXT) + self.assert_(self.export_data.export_data(cursor, USER, [export2_id], + ['reference'], context=CONTEXT) == [['']]) + + self.assert_(self.export_data.export_data(cursor, USER, + [export1_id, export2_id], ['reference'], context=CONTEXT) == + [['test.export_data.target,%s' % target1_id], ['']]) + + cursor.rollback() + cursor.close() + +def suite(): + return unittest.TestLoader().loadTestsFromTestCase(ExportDataTestCase) + +if __name__ == '__main__': + suite = suite() + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/trytond/tests/test_tryton.py b/trytond/tests/test_tryton.py index b5dac321ed..c2319cb9e7 100644 --- a/trytond/tests/test_tryton.py +++ b/trytond/tests/test_tryton.py @@ -168,6 +168,8 @@ def all_suite(): suite_.addTests(test_mptt.suite()) import trytond.tests.test_importdata as test_importdata suite_.addTests(test_importdata.suite()) + import trytond.tests.test_exportdata as test_exportdata + suite_.addTests(test_exportdata.suite()) return suite_ def modules_suite():