7e67ba581a1b0d29da870e15c0c9b1b14241127a
[odoo/odoo.git] / openerp / tests / addons / test_impex / tests / test_export.py
1 # -*- coding: utf-8 -*-
2 import itertools
3 import openerp.modules.registry
4 import openerp
5
6 from openerp.tests import common
7
8
9 class CreatorCase(common.TransactionCase):
10     model_name = False
11
12     def __init__(self, *args, **kwargs):
13         super(CreatorCase, self).__init__(*args, **kwargs)
14         self.model = None
15
16     def setUp(self):
17         super(CreatorCase, self).setUp()
18         self.model = self.registry(self.model_name)
19     def make(self, value):
20         id = self.model.create(self.cr, openerp.SUPERUSER_ID, {'value': value})
21         return self.model.browse(self.cr, openerp.SUPERUSER_ID, [id])[0]
22     def export(self, value, fields=('value',), context=None):
23         record = self.make(value)
24         return self.model._BaseModel__export_row(
25             self.cr, openerp.SUPERUSER_ID, record,
26             [f.split('/') for f in fields],
27             context=context)
28
29 class test_boolean_field(CreatorCase):
30     model_name = 'export.boolean'
31
32     def test_true(self):
33         self.assertEqual(
34             self.export(True),
35             [[u'True']])
36     def test_false(self):
37         """ ``False`` value to boolean fields is unique in being exported as a
38         (unicode) string, not a boolean
39         """
40         self.assertEqual(
41             self.export(False),
42             [[u'False']])
43
44 class test_integer_field(CreatorCase):
45     model_name = 'export.integer'
46
47     def test_empty(self):
48         self.assertEqual(self.model.search(self.cr, openerp.SUPERUSER_ID, []), [],
49                          "Test model should have no records")
50     def test_0(self):
51         self.assertEqual(
52             self.export(0),
53             [[False]])
54
55     def test_basic_value(self):
56         self.assertEqual(
57             self.export(42),
58             [[u'42']])
59
60     def test_negative(self):
61         self.assertEqual(
62             self.export(-32),
63             [[u'-32']])
64
65     def test_huge(self):
66         self.assertEqual(
67             self.export(2**31-1),
68             [[unicode(2**31-1)]])
69
70 class test_float_field(CreatorCase):
71     model_name = 'export.float'
72
73     def test_0(self):
74         self.assertEqual(
75             self.export(0.0),
76             [[False]])
77
78     def test_epsilon(self):
79         self.assertEqual(
80             self.export(0.000000000027),
81             [[u'2.7e-11']])
82
83     def test_negative(self):
84         self.assertEqual(
85             self.export(-2.42),
86             [[u'-2.42']])
87
88     def test_positive(self):
89         self.assertEqual(
90             self.export(47.36),
91             [[u'47.36']])
92
93     def test_big(self):
94         self.assertEqual(
95             self.export(87654321.4678),
96             [[u'87654321.4678']])
97
98 class test_decimal_field(CreatorCase):
99     model_name = 'export.decimal'
100
101     def test_0(self):
102         self.assertEqual(
103             self.export(0.0),
104             [[False]])
105
106     def test_epsilon(self):
107         """ epsilon gets sliced to 0 due to precision
108         """
109         self.assertEqual(
110             self.export(0.000000000027),
111             [[False]])
112
113     def test_negative(self):
114         self.assertEqual(
115             self.export(-2.42),
116             [[u'-2.42']])
117
118     def test_positive(self):
119         self.assertEqual(
120             self.export(47.36),
121             [[u'47.36']])
122
123     def test_big(self):
124         self.assertEqual(
125             self.export(87654321.4678), [[u'87654321.468']])
126
127 class test_string_field(CreatorCase):
128     model_name = 'export.string.bounded'
129
130     def test_empty(self):
131         self.assertEqual(
132             self.export(""),
133             [[False]])
134     def test_within_bounds(self):
135         self.assertEqual(
136             self.export("foobar"),
137             [[u"foobar"]])
138     def test_out_of_bounds(self):
139         self.assertEqual(
140             self.export("C for Sinking, "
141                         "Java for Drinking, "
142                         "Smalltalk for Thinking. "
143                         "...and Power to the Penguin!"),
144             [[u"C for Sinking, J"]])
145
146 class test_unbound_string_field(CreatorCase):
147     model_name = 'export.string'
148
149     def test_empty(self):
150         self.assertEqual(
151             self.export(""),
152             [[False]])
153     def test_small(self):
154         self.assertEqual(
155             self.export("foobar"),
156             [[u"foobar"]])
157     def test_big(self):
158         self.assertEqual(
159             self.export("We flew down weekly to meet with IBM, but they "
160                         "thought the way to measure software was the amount "
161                         "of code we wrote, when really the better the "
162                         "software, the fewer lines of code."),
163             [[u"We flew down weekly to meet with IBM, but they thought the "
164               u"way to measure software was the amount of code we wrote, "
165               u"when really the better the software, the fewer lines of "
166               u"code."]])
167
168 class test_text(CreatorCase):
169     model_name = 'export.text'
170
171     def test_empty(self):
172         self.assertEqual(
173             self.export(""),
174             [[False]])
175     def test_small(self):
176         self.assertEqual(
177             self.export("foobar"),
178             [[u"foobar"]])
179     def test_big(self):
180         self.assertEqual(
181             self.export("So, `bind' is `let' and monadic programming is"
182                         " equivalent to programming in the A-normal form. That"
183                         " is indeed all there is to monads"),
184             [[u"So, `bind' is `let' and monadic programming is equivalent to"
185               u" programming in the A-normal form. That is indeed all there"
186               u" is to monads"]])
187
188 class test_date(CreatorCase):
189     model_name = 'export.date'
190
191     def test_empty(self):
192         self.assertEqual(
193             self.export(False),
194             [[False]])
195     def test_basic(self):
196         self.assertEqual(
197             self.export('2011-11-07'),
198             [[u'2011-11-07']])
199
200 class test_datetime(CreatorCase):
201     model_name = 'export.datetime'
202
203     def test_empty(self):
204         self.assertEqual(
205             self.export(False),
206             [[False]])
207     def test_basic(self):
208         self.assertEqual(
209             self.export('2011-11-07 21:05:48'),
210             [[u'2011-11-07 21:05:48']])
211     def test_tz(self):
212         """ Export ignores the timezone and always exports to UTC
213
214         .. note:: on the other hand, export uses user lang for name_get
215         """
216         # NOTE: ignores user timezone, always exports to UTC
217         self.assertEqual(
218             self.export('2011-11-07 21:05:48', context={'tz': 'Pacific/Norfolk'}),
219             [[u'2011-11-07 21:05:48']])
220
221 class test_selection(CreatorCase):
222     model_name = 'export.selection'
223     translations_fr = [
224         ("Qux", "toto"),
225         ("Bar", "titi"),
226         ("Foo", "tete"),
227     ]
228
229     def test_empty(self):
230         self.assertEqual(
231             self.export(False),
232             [[False]])
233
234     def test_value(self):
235         """ selections export the *label* for their value
236         """
237         self.assertEqual(
238             self.export(2),
239             [[u"Bar"]])
240
241     def test_localized_export(self):
242         self.registry('res.lang').create(self.cr, openerp.SUPERUSER_ID, {
243             'name': u'Français',
244             'code': 'fr_FR',
245             'translatable': True,
246             'date_format': '%d.%m.%Y',
247             'decimal_point': ',',
248             'thousand_sep': ' ',
249         })
250         Translations = self.registry('ir.translation')
251         for source, value in self.translations_fr:
252             Translations.create(self.cr, openerp.SUPERUSER_ID, {
253                 'name': 'export.selection,value',
254                 'lang': 'fr_FR',
255                 'type': 'selection',
256                 'src': source,
257                 'value': value
258             })
259         self.assertEqual(
260             self.export(2, context={'lang': 'fr_FR'}),
261             [[u'Bar']])
262
263 class test_selection_function(CreatorCase):
264     model_name = 'export.selection.function'
265
266     def test_empty(self):
267         self.assertEqual(
268             self.export(False),
269             [[False]])
270
271     def test_value(self):
272         # FIXME: selection functions export the *value* itself
273         self.assertEqual(
274             self.export(1),
275             [[u'1']])
276         self.assertEqual(
277             self.export(3),
278             [[u'3']])
279         # fucking hell
280         self.assertEqual(
281             self.export(0),
282             [[False]])
283
284 class test_m2o(CreatorCase):
285     model_name = 'export.many2one'
286
287     def test_empty(self):
288         self.assertEqual(
289             self.export(False),
290             [[False]])
291     def test_basic(self):
292         """ Exported value is the name_get of the related object
293         """
294         integer_id = self.registry('export.integer').create(
295             self.cr, openerp.SUPERUSER_ID, {'value': 42})
296         name = dict(self.registry('export.integer').name_get(
297             self.cr, openerp.SUPERUSER_ID,[integer_id]))[integer_id]
298         self.assertEqual(
299             self.export(integer_id),
300             [[name]])
301     def test_path(self):
302         """ Can recursively export fields of m2o via path
303         """
304         integer_id = self.registry('export.integer').create(
305             self.cr, openerp.SUPERUSER_ID, {'value': 42})
306         self.assertEqual(
307             self.export(integer_id, fields=['value/.id', 'value/value']),
308             [[unicode(integer_id), u'42']])
309     def test_external_id(self):
310         integer_id = self.registry('export.integer').create(
311             self.cr, openerp.SUPERUSER_ID, {'value': 42})
312         # __export__.$class.$id
313         external_id = u'__export__.export_many2one_%d' % integer_id
314         self.assertEqual(
315             self.export(integer_id, fields=['value/id']),
316             [[external_id]])
317
318 class test_o2m(CreatorCase):
319     model_name = 'export.one2many'
320     commands = [
321         (0, False, {'value': 4, 'str': 'record1'}),
322         (0, False, {'value': 42, 'str': 'record2'}),
323         (0, False, {'value': 36, 'str': 'record3'}),
324         (0, False, {'value': 4, 'str': 'record4'}),
325         (0, False, {'value': 13, 'str': 'record5'}),
326     ]
327     names = [
328         u'export.one2many.child:%d' % d['value']
329         for c, _, d in commands
330     ]
331
332     def test_empty(self):
333         self.assertEqual(
334             self.export(False),
335             [[False]])
336
337     def test_single(self):
338         self.assertEqual(
339             self.export([(0, False, {'value': 42})]),
340             # name_get result
341             [[u'export.one2many.child:42']])
342
343     def test_single_subfield(self):
344         self.assertEqual(
345             self.export([(0, False, {'value': 42})],
346                         fields=['value', 'value/value']),
347             [[u'export.one2many.child:42', u'42']])
348
349     def test_integrate_one_in_parent(self):
350         self.assertEqual(
351             self.export([(0, False, {'value': 42})],
352                         fields=['const', 'value/value']),
353             [[u'4', u'42']])
354
355     def test_multiple_records(self):
356         self.assertEqual(
357             self.export(self.commands, fields=['const', 'value/value']),
358             [
359                 [u'4', u'4'],
360                 [u'', u'42'],
361                 [u'', u'36'],
362                 [u'', u'4'],
363                 [u'', u'13'],
364             ])
365
366     def test_multiple_records_name(self):
367         self.assertEqual(
368             self.export(self.commands, fields=['const', 'value']),
369             [[
370                 u'4', u','.join(self.names)
371             ]])
372
373     def test_multiple_records_id(self):
374         export = self.export(self.commands, fields=['const', 'value/.id'])
375         O2M_c = self.registry('export.one2many.child')
376         ids = O2M_c.browse(self.cr, openerp.SUPERUSER_ID,
377                            O2M_c.search(self.cr, openerp.SUPERUSER_ID, []))
378         self.assertEqual(
379             export,
380             [
381                 ['4', str(ids[0].id)],
382                 ['', str(ids[1].id)],
383                 ['', str(ids[2].id)],
384                 ['', str(ids[3].id)],
385                 ['', str(ids[4].id)],
386             ])
387
388     def test_multiple_records_with_name_before(self):
389         self.assertEqual(
390             self.export(self.commands, fields=['const', 'value', 'value/value']),
391             [[ # exports sub-fields of very first o2m
392                 u'4', u','.join(self.names), u'4'
393             ]])
394
395     def test_multiple_records_with_name_after(self):
396         self.assertEqual(
397             self.export(self.commands, fields=['const', 'value/value', 'value']),
398             [ # completely ignores name_get request
399                 [u'4', u'4', ''],
400                 ['', u'42', ''],
401                 ['', u'36', ''],
402                 ['', u'4', ''],
403                 ['', u'13', ''],
404             ])
405
406     def test_multiple_subfields_neighbour(self):
407         self.assertEqual(
408             self.export(self.commands, fields=['const', 'value/str','value/value']),
409             [
410                 [u'4', u'record1', u'4'],
411                 ['', u'record2', u'42'],
412                 ['', u'record3', u'36'],
413                 ['', u'record4', u'4'],
414                 ['', u'record5', u'13'],
415             ])
416
417     def test_multiple_subfields_separated(self):
418         self.assertEqual(
419             self.export(self.commands, fields=['value/str', 'const', 'value/value']),
420             [
421                 [u'record1', u'4', u'4'],
422                 [u'record2', '', u'42'],
423                 [u'record3', '', u'36'],
424                 [u'record4', '', u'4'],
425                 [u'record5', '', u'13'],
426             ])
427
428 class test_o2m_multiple(CreatorCase):
429     model_name = 'export.one2many.multiple'
430
431     def make(self, value=None, **values):
432         if value is not None: values['value'] = value
433         id = self.model.create(self.cr, openerp.SUPERUSER_ID, values)
434         return self.model.browse(self.cr, openerp.SUPERUSER_ID, [id])[0]
435     def export(self, value=None, fields=('child1', 'child2',), context=None, **values):
436         record = self.make(value, **values)
437         return self.model._BaseModel__export_row(
438             self.cr, openerp.SUPERUSER_ID, record,
439             [f.split('/') for f in fields],
440             context=context)
441
442     def test_empty(self):
443         self.assertEqual(
444             self.export(child1=False, child2=False),
445             [[False, False]])
446
447     def test_single_per_side(self):
448         self.assertEqual(
449             self.export(child1=False, child2=[(0, False, {'value': 42})]),
450             [[False, u'export.one2many.child.2:42']])
451
452         self.assertEqual(
453             self.export(child1=[(0, False, {'value': 43})], child2=False),
454             [[u'export.one2many.child.1:43', False]])
455
456         self.assertEqual(
457             self.export(child1=[(0, False, {'value': 43})],
458                         child2=[(0, False, {'value': 42})]),
459             [[u'export.one2many.child.1:43', u'export.one2many.child.2:42']])
460
461     def test_single_integrate_subfield(self):
462         fields = ['const', 'child1/value', 'child2/value']
463         self.assertEqual(
464             self.export(child1=False, child2=[(0, False, {'value': 42})],
465                         fields=fields),
466             [[u'36', False, u'42']])
467
468         self.assertEqual(
469             self.export(child1=[(0, False, {'value': 43})], child2=False,
470                         fields=fields),
471             [[u'36', u'43', False]])
472
473         self.assertEqual(
474             self.export(child1=[(0, False, {'value': 43})],
475                         child2=[(0, False, {'value': 42})],
476                         fields=fields),
477             [[u'36', u'43', u'42']])
478
479     def test_multiple(self):
480         """ With two "concurrent" o2ms, exports the first line combined, then
481         exports the rows for the first o2m, then the rows for the second o2m.
482         """
483         fields = ['const', 'child1/value', 'child2/value']
484         child1 = [(0, False, {'value': v, 'str': 'record%.02d' % index})
485                   for index, v in zip(itertools.count(), [4, 42, 36, 4, 13])]
486         child2 = [(0, False, {'value': v, 'str': 'record%.02d' % index})
487                   for index, v in zip(itertools.count(10), [8, 12, 8, 55, 33, 13])]
488
489         self.assertEqual(
490             self.export(child1=child1, child2=False, fields=fields),
491             [
492                 [u'36', u'4', False],
493                 ['', u'42', ''],
494                 ['', u'36', ''],
495                 ['', u'4', ''],
496                 ['', u'13', ''],
497             ])
498         self.assertEqual(
499             self.export(child1=False, child2=child2, fields=fields),
500             [
501                 [u'36', False, u'8'],
502                 ['', '', u'12'],
503                 ['', '', u'8'],
504                 ['', '', u'55'],
505                 ['', '', u'33'],
506                 ['', '', u'13'],
507             ])
508         self.assertEqual(
509             self.export(child1=child1, child2=child2, fields=fields),
510             [
511                 [u'36', u'4', u'8'],
512                 ['', u'42', ''],
513                 ['', u'36', ''],
514                 ['', u'4', ''],
515                 ['', u'13', ''],
516                 ['', '', u'12'],
517                 ['', '', u'8'],
518                 ['', '', u'55'],
519                 ['', '', u'33'],
520                 ['', '', u'13'],
521             ])
522
523 class test_m2m(CreatorCase):
524     model_name = 'export.many2many'
525     commands = [
526         (0, False, {'value': 4, 'str': 'record000'}),
527         (0, False, {'value': 42, 'str': 'record001'}),
528         (0, False, {'value': 36, 'str': 'record010'}),
529         (0, False, {'value': 4, 'str': 'record011'}),
530         (0, False, {'value': 13, 'str': 'record100'}),
531     ]
532     names = [
533         u'export.many2many.other:%d' % d['value']
534         for c, _, d in commands
535     ]
536
537     def test_empty(self):
538         self.assertEqual(
539             self.export(False),
540             [[False]])
541
542     def test_single(self):
543         self.assertEqual(
544             self.export([(0, False, {'value': 42})]),
545             # name_get result
546             [[u'export.many2many.other:42']])
547
548     def test_single_subfield(self):
549         self.assertEqual(
550             self.export([(0, False, {'value': 42})],
551                         fields=['value', 'value/value']),
552             [[u'export.many2many.other:42', u'42']])
553
554     def test_integrate_one_in_parent(self):
555         self.assertEqual(
556             self.export([(0, False, {'value': 42})],
557                         fields=['const', 'value/value']),
558             [[u'4', u'42']])
559
560     def test_multiple_records(self):
561         self.assertEqual(
562             self.export(self.commands, fields=['const', 'value/value']),
563             [
564                 [u'4', u'4'],
565                 [u'', u'42'],
566                 [u'', u'36'],
567                 [u'', u'4'],
568                 [u'', u'13'],
569             ])
570
571     def test_multiple_records_name(self):
572         self.assertEqual(
573             self.export(self.commands, fields=['const', 'value']),
574             [[ # FIXME: hardcoded comma, import uses config.csv_internal_sep
575                # resolution: remove configurable csv_internal_sep
576                 u'4', u','.join(self.names)
577             ]])
578
579     # essentially same as o2m, so boring
580
581 class test_function(CreatorCase):
582     model_name = 'export.function'
583
584     def test_value(self):
585         """ Exports value normally returned by accessing the function field
586         """
587         self.assertEqual(
588             self.export(42),
589             [[u'3']])