From 4571448bd0d0f9237151cfe92106a52363da6a66 Mon Sep 17 00:00:00 2001 From: "Jiten (OpenERP)" Date: Thu, 19 Jul 2012 15:52:04 +0530 Subject: [PATCH 01/85] [IMP] Start to improve DB management looks same as other views of apps. bzr revid: jra@tinyerp.com-20120719102204-3eg2fmjdweouyjxl --- addons/web/static/src/xml/base.xml | 363 +++++++++++++++++------------ 1 file changed, 216 insertions(+), 147 deletions(-) diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 70b0cd85cc5..50cec55014f 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -115,153 +115,222 @@ -
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CREATE DATABASE
- -
-
-
- - - - - - - - - - - - - - - -
DROP DATABASE
- - -
-
-
- - - - - - - - - - - - - - - - -
BACKUP DATABASE
- - -
-
-
- - - - - - - - - - - - - - - - - - - -
RESTORE DATABASE
-
-
- - - - - - - - - - - - - - - - - - - -
CHANGE MASTER PASSWORD
-
-
-
+ + + + + + + + + +
+ + Back to Login + +
+ + d.url = '/' + (window.location.search || ''); + + +
+
+
Database Management
+ +
+
+ +
+
+ + + + + + + + + + + +
+

+ + Database Management + +

+
+
+
+
+
+
+
+
+
+
+
+ +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CREATE DATABASE
+ +
+
+
+ + + + + + + + + + + + + + + +
DROP DATABASE
+ + +
+
+
+ + + + + + + + + + + + + + + + +
BACKUP DATABASE
+ + +
+
+
+ + + + + + + + + + + + + + + + + + + +
RESTORE DATABASE
+
+
+ + + + + + + + + + + + + + + + + + + +
CHANGE MASTER PASSWORD
+
+
+
+ +
+
+
+
+
+
+
+
+
+ +
From fc36345ca0730cf8cbd197d9177bc884764188f2 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 23 Jul 2012 17:03:06 +0200 Subject: [PATCH 02/85] [ADD] test cases for export of simple fields bzr revid: xmo@openerp.com-20120723150306-6zgtr9ho0d3ud78u --- openerp/modules/__init__.py | 6 +- openerp/osv/orm.py | 6 +- openerp/tests/__init__.py | 11 +- openerp/tests/common.py | 5 +- openerp/tests/test_export.py | 265 +++++++++++++++++++++++++++++++++++ 5 files changed, 276 insertions(+), 17 deletions(-) create mode 100644 openerp/tests/test_export.py diff --git a/openerp/modules/__init__.py b/openerp/modules/__init__.py index f28c397bb23..56265e353a2 100644 --- a/openerp/modules/__init__.py +++ b/openerp/modules/__init__.py @@ -24,11 +24,7 @@ """ -import openerp.modules.db -import openerp.modules.graph -import openerp.modules.loading -import openerp.modules.migration -import openerp.modules.module +from . import db, graph, loading, migration, module, registry # TODO temporarily expose those things from openerp.modules.module import \ diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index 78487e7641c..02a13a41dcb 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -4179,11 +4179,7 @@ class BaseModel(object): # Try-except added to filter the creation of those records whose filds are readonly. # Example : any dashboard which has all the fields readonly.(due to Views(database views)) - try: - cr.execute("SELECT nextval('"+self._sequence+"')") - except: - raise except_orm(_('UserError'), - _('You cannot perform this operation. New Record Creation is not allowed for this object as this object is for reporting purpose.')) + cr.execute("SELECT nextval('"+self._sequence+"')") id_new = cr.fetchone()[0] for table in tocreate: diff --git a/openerp/tests/__init__.py b/openerp/tests/__init__.py index 913c5c759c9..7b30adece79 100644 --- a/openerp/tests/__init__.py +++ b/openerp/tests/__init__.py @@ -8,18 +8,17 @@ Tests can be explicitely added to the `fast_suite` or `checks` lists or not. See the :ref:`test-framework` section in the :ref:`features` list. """ -import test_expression -import test_ir_sequence -import test_orm -import test_uninstall +from . import test_expression, test_ir_sequence, test_orm,\ + test_uninstall, test_export fast_suite = [ test_ir_sequence, - ] +] checks = [ test_expression, test_orm, - ] + test_export, +] # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/tests/common.py b/openerp/tests/common.py index ef5b0586136..b1af4338312 100644 --- a/openerp/tests/common.py +++ b/openerp/tests/common.py @@ -39,13 +39,16 @@ class TransactionCase(unittest2.TestCase): """ def setUp(self): - self.cr = openerp.modules.registry.RegistryManager.get(DB).db.cursor() + self.cr = self.cursor() self.uid = openerp.SUPERUSER_ID def tearDown(self): self.cr.rollback() self.cr.close() + def cursor(self): + return openerp.modules.registry.RegistryManager.get(DB).db.cursor() + def registry(self, model): return openerp.modules.registry.RegistryManager.get(DB)[model] diff --git a/openerp/tests/test_export.py b/openerp/tests/test_export.py new file mode 100644 index 00000000000..b1b51a608dc --- /dev/null +++ b/openerp/tests/test_export.py @@ -0,0 +1,265 @@ +# -*- coding: utf-8 -*- +import psycopg2 + +import openerp.modules.registry +import openerp +from openerp.osv import orm, fields + +from . import common + +models = [ + ('boolean', fields.boolean()), + ('integer', fields.integer()), + ('float', fields.float()), + ('decimal', fields.float(digits=(16, 3))), + ('string.bounded', fields.char('unknown', size=16)), + ('string', fields.char('unknown', size=None)), + ('date', fields.date()), + ('datetime', fields.datetime()), + ('text', fields.text()), + ('selection', fields.selection([(1, "Foo"), (2, "Bar"), (3, "Qux")])), + # TODO: m2o, o2m, m2m + # TODO: function? + # TODO: related? + # TODO: reference? +] +for name, field in models: + attrs = { + '_name': 'export.%s' % name, + '_module': 'base', + '_columns': { + 'value': field + } + } + NewModel = type( + 'Export%s' % ''.join(section.capitalize() for section in name.split('.')), + (orm.Model,), + attrs) + +def setUpModule(): + openerp.tools.config['update'] = dict(base=1) + openerp.modules.registry.RegistryManager.new( + common.DB, update_module=True) + +class CreatorCase(common.TransactionCase): + model_name = False + + def __init__(self, *args, **kwargs): + super(CreatorCase, self).__init__(*args, **kwargs) + self.model = None + + def setUp(self): + super(CreatorCase, self).setUp() + self.model = self.registry(self.model_name) + def make(self, value): + id = self.model.create(self.cr, openerp.SUPERUSER_ID, {'value': value}) + return self.model.browse(self.cr, openerp.SUPERUSER_ID, [id])[0] + def export(self, value, context=None): + record = self.make(value) + return self.model._BaseModel__export_row( + self.cr, openerp.SUPERUSER_ID, record, [["value"]], context=context) + +class test_boolean_field(CreatorCase): + model_name = 'export.boolean' + + def test_true(self): + self.assertEqual( + self.export(True), + [[u'True']]) + def test_false(self): + """ ``False`` value to boolean fields is unique in being exported as a + (unicode) string, not a boolean + """ + self.assertEqual( + self.export(False), + [[u'False']]) + +class test_integer_field(CreatorCase): + model_name = 'export.integer' + + def test_empty(self): + self.assertEqual(self.model.search(self.cr, openerp.SUPERUSER_ID, []), [], + "Test model should have no records") + def test_0(self): + self.assertEqual( + self.export(0), + [[False]]) + + def test_basic_value(self): + self.assertEqual( + self.export(42), + [[u'42']]) + + def test_negative(self): + self.assertEqual( + self.export(-32), + [[u'-32']]) + + def test_huge(self): + self.assertEqual( + self.export(2**31-1), + [[unicode(2**31-1)]]) + +class test_float_field(CreatorCase): + model_name = 'export.float' + + def test_0(self): + self.assertEqual( + self.export(0.0), + [[False]]) + + def test_epsilon(self): + self.assertEqual( + self.export(0.000000000027), + [[u'2.7e-11']]) + + def test_negative(self): + self.assertEqual( + self.export(-2.42), + [[u'-2.42']]) + + def test_positive(self): + self.assertEqual( + self.export(47.36), + [[u'47.36']]) + + def test_big(self): + self.assertEqual( + self.export(87654321.4678), + [[u'87654321.4678']]) + +class test_decimal_field(CreatorCase): + model_name = 'export.decimal' + + def test_0(self): + self.assertEqual( + self.export(0.0), + [[False]]) + + def test_epsilon(self): + """ epsilon gets sliced to 0 due to precision + """ + self.assertEqual( + self.export(0.000000000027), + [[False]]) + + def test_negative(self): + self.assertEqual( + self.export(-2.42), + [[u'-2.42']]) + + def test_positive(self): + self.assertEqual( + self.export(47.36), + [[u'47.36']]) + + def test_big(self): + self.assertEqual( + self.export(87654321.4678), [[u'87654321.468']]) + +class test_string_field(CreatorCase): + model_name = 'export.string.bounded' + + def test_empty(self): + self.assertEqual( + self.export(""), + [[False]]) + def test_within_bounds(self): + self.assertEqual( + self.export("foobar"), + [[u"foobar"]]) + def test_out_of_bounds(self): + self.assertEqual( + self.export("C for Sinking, " + "Java for Drinking, " + "Smalltalk for Thinking. " + "...and Power to the Penguin!"), + [[u"C for Sinking, J"]]) + +class test_unbound_string_field(CreatorCase): + model_name = 'export.string' + + def test_empty(self): + self.assertEqual( + self.export(""), + [[False]]) + def test_small(self): + self.assertEqual( + self.export("foobar"), + [[u"foobar"]]) + def test_big(self): + self.assertEqual( + self.export("We flew down weekly to meet with IBM, but they " + "thought the way to measure software was the amount " + "of code we wrote, when really the better the " + "software, the fewer lines of code."), + [[u"We flew down weekly to meet with IBM, but they thought the " + u"way to measure software was the amount of code we wrote, " + u"when really the better the software, the fewer lines of " + u"code."]]) + +class test_text(CreatorCase): + model_name = 'export.text' + + def test_empty(self): + self.assertEqual( + self.export(""), + [[False]]) + def test_small(self): + self.assertEqual( + self.export("foobar"), + [[u"foobar"]]) + def test_big(self): + self.assertEqual( + self.export("So, `bind' is `let' and monadic programming is" + " equivalent to programming in the A-normal form. That" + " is indeed all there is to monads"), + [[u"So, `bind' is `let' and monadic programming is equivalent to" + u" programming in the A-normal form. That is indeed all there" + u" is to monads"]]) + +class test_date(CreatorCase): + model_name = 'export.date' + + def test_empty(self): + self.assertEqual( + self.export(False), + [[False]]) + def test_basic(self): + self.assertEqual( + self.export('2011-11-07'), + [[u'2011-11-07']]) + +class test_datetime(CreatorCase): + model_name = 'export.datetime' + + def test_empty(self): + self.assertEqual( + self.export(False), + [[False]]) + def test_basic(self): + self.assertEqual( + self.export('2011-11-07 21:05:48'), + [[u'2011-11-07 21:05:48']]) + def test_tz(self): + """ Export ignores the timezone and always exports to UTC + """ + self.assertEqual( + self.export('2011-11-07 21:05:48', {'tz': 'Pacific/Norfolk'}), + [[u'2011-11-07 21:05:48']]) + +class test_selection(CreatorCase): + model_name = 'export.selection' + + def test_empty(self): + self.assertEqual( + self.export(False), + [[False]]) + + def test_value(self): + """ selections export the *label* for their value + """ + self.assertEqual( + self.export(2), + [[u"Bar"]]) + From feb9efed4dd58dc3bbb20b58ee49b269d726f4e3 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 23 Jul 2012 17:38:26 +0200 Subject: [PATCH 03/85] [IMP] prepare export test cases for relational fields bzr revid: xmo@openerp.com-20120723153826-hu7cwhj8uv6w0xu7 --- openerp/tests/test_export.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/openerp/tests/test_export.py b/openerp/tests/test_export.py index b1b51a608dc..401a90fd635 100644 --- a/openerp/tests/test_export.py +++ b/openerp/tests/test_export.py @@ -54,10 +54,12 @@ class CreatorCase(common.TransactionCase): def make(self, value): id = self.model.create(self.cr, openerp.SUPERUSER_ID, {'value': value}) return self.model.browse(self.cr, openerp.SUPERUSER_ID, [id])[0] - def export(self, value, context=None): + def export(self, value, fields=('value',), context=None): record = self.make(value) return self.model._BaseModel__export_row( - self.cr, openerp.SUPERUSER_ID, record, [["value"]], context=context) + self.cr, openerp.SUPERUSER_ID, record, + [f.split('/') for f in fields], + context=context) class test_boolean_field(CreatorCase): model_name = 'export.boolean' @@ -245,7 +247,7 @@ class test_datetime(CreatorCase): """ Export ignores the timezone and always exports to UTC """ self.assertEqual( - self.export('2011-11-07 21:05:48', {'tz': 'Pacific/Norfolk'}), + self.export('2011-11-07 21:05:48', context={'tz': 'Pacific/Norfolk'}), [[u'2011-11-07 21:05:48']]) class test_selection(CreatorCase): From 5850df630c87ab2474fec8827af64c99d184e122 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 23 Jul 2012 18:00:41 +0200 Subject: [PATCH 04/85] [ADD] m2o field export tests bzr revid: xmo@openerp.com-20120723160041-o6w24ia2kwnqu19z --- openerp/tests/test_export.py | 41 +++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/openerp/tests/test_export.py b/openerp/tests/test_export.py index 401a90fd635..61c71510f42 100644 --- a/openerp/tests/test_export.py +++ b/openerp/tests/test_export.py @@ -18,6 +18,8 @@ models = [ ('datetime', fields.datetime()), ('text', fields.text()), ('selection', fields.selection([(1, "Foo"), (2, "Bar"), (3, "Qux")])), + # just relate to an integer + ('many2one', fields.many2one('export.integer')), # TODO: m2o, o2m, m2m # TODO: function? # TODO: related? @@ -29,7 +31,11 @@ for name, field in models: '_module': 'base', '_columns': { 'value': field - } + }, + 'name_get': (lambda self, cr, uid, ids, context=None: + [(record.id, "%s:%s" % (self._name, record.value)) + for record in self.browse(cr, uid, ids, context=context)]) + } NewModel = type( 'Export%s' % ''.join(section.capitalize() for section in name.split('.')), @@ -265,3 +271,36 @@ class test_selection(CreatorCase): self.export(2), [[u"Bar"]]) +class test_m2o(CreatorCase): + model_name = 'export.many2one' + + def test_empty(self): + self.assertEqual( + self.export(False), + [[False]]) + def test_basic(self): + """ Exported value is the name_get of the related object + """ + integer_id = self.registry('export.integer').create( + self.cr, openerp.SUPERUSER_ID, {'value': 42}) + name = dict(self.registry('export.integer').name_get( + self.cr, openerp.SUPERUSER_ID,[integer_id]))[integer_id] + self.assertEqual( + self.export(integer_id), + [[name]]) + def test_path(self): + """ Can recursively export fields of m2o via path + """ + integer_id = self.registry('export.integer').create( + self.cr, openerp.SUPERUSER_ID, {'value': 42}) + self.assertEqual( + self.export(integer_id, fields=['value/.id', 'value/value']), + [[unicode(integer_id), u'42']]) + def test_external_id(self): + integer_id = self.registry('export.integer').create( + self.cr, openerp.SUPERUSER_ID, {'value': 42}) + # __export__.$class.$id + external_id = u'__export__.export_many2one_%d' % integer_id + self.assertEqual( + self.export(integer_id, fields=['value/id']), + [[external_id]]) From e15e32feb2d7b50ca5aa9c0f96b066df203ed45c Mon Sep 17 00:00:00 2001 From: "Jiten (OpenERP)" Date: Tue, 24 Jul 2012 11:48:14 +0530 Subject: [PATCH 05/85] [IMP] Remove form sheet width and background. bzr revid: jra@tinyerp.com-20120724061814-d0y5ujh3q13e5faj --- addons/web/static/src/xml/base.xml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 07e92ffa915..b8ca09b1186 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -174,8 +174,6 @@
-
-
@@ -315,8 +313,6 @@ -
-
From 02109d24e244037483d86f33e0532a0b455deca6 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 24 Jul 2012 12:27:28 +0200 Subject: [PATCH 06/85] [ADD] tests for o2m field export bzr revid: xmo@openerp.com-20120724102728-g7noucvda19k7k5q --- openerp/tests/test_export.py | 119 ++++++++++++++++++++++++++++++++++- 1 file changed, 117 insertions(+), 2 deletions(-) diff --git a/openerp/tests/test_export.py b/openerp/tests/test_export.py index 61c71510f42..c46ef53837e 100644 --- a/openerp/tests/test_export.py +++ b/openerp/tests/test_export.py @@ -20,7 +20,8 @@ models = [ ('selection', fields.selection([(1, "Foo"), (2, "Bar"), (3, "Qux")])), # just relate to an integer ('many2one', fields.many2one('export.integer')), - # TODO: m2o, o2m, m2m + ('one2many', fields.one2many('export.one2many.child', 'parent_id')), + # TODO: m2m # TODO: function? # TODO: related? # TODO: reference? @@ -30,18 +31,35 @@ for name, field in models: '_name': 'export.%s' % name, '_module': 'base', '_columns': { + 'const': fields.integer(), 'value': field }, + '_defaults': {'const': 4}, 'name_get': (lambda self, cr, uid, ids, context=None: [(record.id, "%s:%s" % (self._name, record.value)) for record in self.browse(cr, uid, ids, context=context)]) - } NewModel = type( 'Export%s' % ''.join(section.capitalize() for section in name.split('.')), (orm.Model,), attrs) +class One2ManyChild(orm.Model): + _name = 'export.one2many.child' + _module = 'base' + # FIXME: orm.py:1161, fix to name_get on m2o field + _rec_name = 'value' + + _columns = { + 'parent_id': fields.many2one('export.one2many'), + 'str': fields.char('unknown', size=None), + 'value': fields.integer() + } + def name_get(self, cr, uid, ids, context=None): + return [(record.id, "%s:%s" % (self._name, record.value)) + for record in self.browse(cr, uid, ids, context=context)] + + def setUpModule(): openerp.tools.config['update'] = dict(base=1) openerp.modules.registry.RegistryManager.new( @@ -251,6 +269,8 @@ class test_datetime(CreatorCase): [[u'2011-11-07 21:05:48']]) def test_tz(self): """ Export ignores the timezone and always exports to UTC + + .. note:: on the other hand, export uses user lang for name_get """ self.assertEqual( self.export('2011-11-07 21:05:48', context={'tz': 'Pacific/Norfolk'}), @@ -304,3 +324,98 @@ class test_m2o(CreatorCase): self.assertEqual( self.export(integer_id, fields=['value/id']), [[external_id]]) + +class test_o2m(CreatorCase): + model_name = 'export.one2many' + commands = [ + (0, False, {'value': 4, 'str': 'record1'}), + (0, False, {'value': 42, 'str': 'record2'}), + (0, False, {'value': 36, 'str': 'record3'}), + (0, False, {'value': 4, 'str': 'record4'}), + (0, False, {'value': 13, 'str': 'record5'}), + ] + names = [ + u'export.one2many.child:%d' % d['value'] + for c, _, d in commands + ] + + def test_empty(self): + self.assertEqual( + self.export(False), + [[False]]) + + def test_single(self): + self.assertEqual( + self.export([(0, False, {'value': 42})]), + # name_get result + [[u'export.one2many.child:42']]) + + def test_single_subfield(self): + self.assertEqual( + self.export([(0, False, {'value': 42})], + fields=['value', 'value/value']), + [[u'export.one2many.child:42', u'42']]) + + def test_integrate_one_in_parent(self): + self.assertEqual( + self.export([(0, False, {'value': 42})], + fields=['const', 'value/value']), + [[u'4', u'42']]) + + def test_multiple_records(self): + self.assertEqual( + self.export(self.commands, fields=['const', 'value/value']), + [ + [u'4', u'4'], + [u'', u'42'], + [u'', u'36'], + [u'', u'4'], + [u'', u'13'], + ]) + + def test_multiple_records_name(self): + self.assertEqual( + self.export(self.commands, fields=['const', 'value']), + [[ + u'4', u','.join(self.names) + ]]) + + def test_multiple_records_with_name_before(self): + self.assertEqual( + self.export(self.commands, fields=['const', 'value', 'value/value']), + [[ # exports sub-fields of very first o2m + u'4', u','.join(self.names), u'4' + ]]) + + def test_multiple_records_with_name_before(self): + self.assertEqual( + self.export(self.commands, fields=['const', 'value/value', 'value']), + [ # completely ignores name_get request + [u'4', u'4', ''], + [u'', u'42', ''], + [u'', u'36', ''], + [u'', u'4', ''], + [u'', u'13', ''], + ]) + + def test_multiple_subfields_neighbour(self): + self.assertEqual( + self.export(self.commands, fields=['const', 'value/str','value/value']), + [ + [u'4', u'record1', u'4'], + ['', u'record2', u'42'], + ['', u'record3', u'36'], + ['', u'record4', u'4'], + ['', u'record5', u'13'], + ]) + + def test_multiple_subfields_separated(self): + self.assertEqual( + self.export(self.commands, fields=['value/str', 'const', 'value/value']), + [ + [u'record1', u'4', u'4'], + [u'record2', '', u'42'], + [u'record3', '', u'36'], + [u'record4', '', u'4'], + [u'record5', '', u'13'], + ]) From f3c315cf10e2fb766586e2c8c9e03e7b228c800b Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 24 Jul 2012 12:57:56 +0200 Subject: [PATCH 07/85] [ADD] some m2m tests bzr revid: xmo@openerp.com-20120724105756-swxo1l4tahqjopzd --- openerp/tests/test_export.py | 80 +++++++++++++++++++++++++++++++++--- 1 file changed, 75 insertions(+), 5 deletions(-) diff --git a/openerp/tests/test_export.py b/openerp/tests/test_export.py index c46ef53837e..030466dc674 100644 --- a/openerp/tests/test_export.py +++ b/openerp/tests/test_export.py @@ -21,7 +21,7 @@ models = [ # just relate to an integer ('many2one', fields.many2one('export.integer')), ('one2many', fields.one2many('export.one2many.child', 'parent_id')), - # TODO: m2m + ('many2many', fields.many2many('export.many2many.other')) # TODO: function? # TODO: related? # TODO: reference? @@ -59,6 +59,19 @@ class One2ManyChild(orm.Model): return [(record.id, "%s:%s" % (self._name, record.value)) for record in self.browse(cr, uid, ids, context=context)] +class Many2ManyChild(orm.Model): + _name = 'export.many2many.other' + _module = 'base' + # FIXME: orm.py:1161, fix to name_get on m2o field + _rec_name = 'value' + + _columns = { + 'str': fields.char('unknown', size=None), + 'value': fields.integer() + } + def name_get(self, cr, uid, ids, context=None): + return [(record.id, "%s:%s" % (self._name, record.value)) + for record in self.browse(cr, uid, ids, context=context)] def setUpModule(): openerp.tools.config['update'] = dict(base=1) @@ -392,10 +405,10 @@ class test_o2m(CreatorCase): self.export(self.commands, fields=['const', 'value/value', 'value']), [ # completely ignores name_get request [u'4', u'4', ''], - [u'', u'42', ''], - [u'', u'36', ''], - [u'', u'4', ''], - [u'', u'13', ''], + ['', u'42', ''], + ['', u'36', ''], + ['', u'4', ''], + ['', u'13', ''], ]) def test_multiple_subfields_neighbour(self): @@ -419,3 +432,60 @@ class test_o2m(CreatorCase): [u'record4', '', u'4'], [u'record5', '', u'13'], ]) + +class test_m2m(CreatorCase): + model_name = 'export.many2many' + commands = [ + (0, False, {'value': 4, 'str': 'record000'}), + (0, False, {'value': 42, 'str': 'record001'}), + (0, False, {'value': 36, 'str': 'record010'}), + (0, False, {'value': 4, 'str': 'record011'}), + (0, False, {'value': 13, 'str': 'record100'}), + ] + names = [ + u'export.many2many.other:%d' % d['value'] + for c, _, d in commands + ] + + def test_empty(self): + self.assertEqual( + self.export(False), + [[False]]) + + def test_single(self): + self.assertEqual( + self.export([(0, False, {'value': 42})]), + # name_get result + [[u'export.many2many.other:42']]) + + def test_single_subfield(self): + self.assertEqual( + self.export([(0, False, {'value': 42})], + fields=['value', 'value/value']), + [[u'export.many2many.other:42', u'42']]) + + def test_integrate_one_in_parent(self): + self.assertEqual( + self.export([(0, False, {'value': 42})], + fields=['const', 'value/value']), + [[u'4', u'42']]) + + def test_multiple_records(self): + self.assertEqual( + self.export(self.commands, fields=['const', 'value/value']), + [ + [u'4', u'4'], + [u'', u'42'], + [u'', u'36'], + [u'', u'4'], + [u'', u'13'], + ]) + + def test_multiple_records_name(self): + self.assertEqual( + self.export(self.commands, fields=['const', 'value']), + [[ + u'4', u','.join(self.names) + ]]) + + # essentially same as o2m, so boring From 53c82f4086e2fd2a029106fbbcc95beaad53c250 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 26 Jul 2012 12:08:04 +0200 Subject: [PATCH 08/85] [TEST] export of multiple o2m fields in the same object bzr revid: xmo@openerp.com-20120726100804-ggsjwco93k9d5bqw --- openerp/tests/export_models.py | 102 ++++++++++++++++++++ openerp/tests/test_export.py | 167 +++++++++++++++++++-------------- 2 files changed, 200 insertions(+), 69 deletions(-) create mode 100644 openerp/tests/export_models.py diff --git a/openerp/tests/export_models.py b/openerp/tests/export_models.py new file mode 100644 index 00000000000..d54c73ac4ad --- /dev/null +++ b/openerp/tests/export_models.py @@ -0,0 +1,102 @@ +# -*- coding: utf-8 -*- +from openerp.osv import orm, fields + +models = [ + ('boolean', fields.boolean()), + ('integer', fields.integer()), + ('float', fields.float()), + ('decimal', fields.float(digits=(16, 3))), + ('string.bounded', fields.char('unknown', size=16)), + ('string', fields.char('unknown', size=None)), + ('date', fields.date()), + ('datetime', fields.datetime()), + ('text', fields.text()), + ('selection', fields.selection([(1, "Foo"), (2, "Bar"), (3, "Qux")])), + # just relate to an integer + ('many2one', fields.many2one('export.integer')), + ('one2many', fields.one2many('export.one2many.child', 'parent_id')), + ('many2many', fields.many2many('export.many2many.other')) + # TODO: function? + # TODO: related? + # TODO: reference? +] +for name, field in models: + attrs = { + '_name': 'export.%s' % name, + '_module': 'base', + '_columns': { + 'const': fields.integer(), + 'value': field + }, + '_defaults': {'const': 4}, + 'name_get': (lambda self, cr, uid, ids, context=None: + [(record.id, "%s:%s" % (self._name, record.value)) + for record in self.browse(cr, uid, ids, context=context)]) + } + NewModel = type( + 'Export%s' % ''.join(section.capitalize() for section in name.split('.')), + (orm.Model,), + attrs) + +class One2ManyChild(orm.Model): + _name = 'export.one2many.child' + _module = 'base' + # FIXME: orm.py:1161, fix to name_get on m2o field + _rec_name = 'value' + + _columns = { + 'parent_id': fields.many2one('export.one2many'), + 'str': fields.char('unknown', size=None), + 'value': fields.integer() + } + def name_get(self, cr, uid, ids, context=None): + return [(record.id, "%s:%s" % (self._name, record.value)) + for record in self.browse(cr, uid, ids, context=context)] + +class One2ManyMultiple(orm.Model): + _name = 'export.one2many.multiple' + _module = 'base' + + _columns = { + 'const': fields.integer(), + 'child1': fields.one2many('export.one2many.child.1', 'parent_id'), + 'child2': fields.one2many('export.one2many.child.2', 'parent_id'), + } + _defaults = { 'const': 36 } + +class One2ManyChildMultiple(orm.Model): + _name = 'export.one2many.multiple.child' + _module = 'base' + # FIXME: orm.py:1161, fix to name_get on m2o field + _rec_name = 'value' + + _columns = { + 'parent_id': fields.many2one('export.one2many.multiple'), + 'str': fields.char('unknown', size=None), + 'value': fields.integer() + } + def name_get(self, cr, uid, ids, context=None): + return [(record.id, "%s:%s" % (self._name, record.value)) + for record in self.browse(cr, uid, ids, context=context)] +class One2ManyChild1(orm.Model): + _name = 'export.one2many.child.1' + _module = 'base' + _inherit = 'export.one2many.multiple.child' +class One2ManyChild2(orm.Model): + _name = 'export.one2many.child.2' + _module = 'base' + _inherit = 'export.one2many.multiple.child' + +class Many2ManyChild(orm.Model): + _name = 'export.many2many.other' + _module = 'base' + # FIXME: orm.py:1161, fix to name_get on m2o field + _rec_name = 'value' + + _columns = { + 'str': fields.char('unknown', size=None), + 'value': fields.integer() + } + def name_get(self, cr, uid, ids, context=None): + return [(record.id, "%s:%s" % (self._name, record.value)) + for record in self.browse(cr, uid, ids, context=context)] diff --git a/openerp/tests/test_export.py b/openerp/tests/test_export.py index 030466dc674..ea21cd36a8a 100644 --- a/openerp/tests/test_export.py +++ b/openerp/tests/test_export.py @@ -1,77 +1,10 @@ # -*- coding: utf-8 -*- -import psycopg2 - +import itertools import openerp.modules.registry import openerp -from openerp.osv import orm, fields -from . import common +from . import common, export_models -models = [ - ('boolean', fields.boolean()), - ('integer', fields.integer()), - ('float', fields.float()), - ('decimal', fields.float(digits=(16, 3))), - ('string.bounded', fields.char('unknown', size=16)), - ('string', fields.char('unknown', size=None)), - ('date', fields.date()), - ('datetime', fields.datetime()), - ('text', fields.text()), - ('selection', fields.selection([(1, "Foo"), (2, "Bar"), (3, "Qux")])), - # just relate to an integer - ('many2one', fields.many2one('export.integer')), - ('one2many', fields.one2many('export.one2many.child', 'parent_id')), - ('many2many', fields.many2many('export.many2many.other')) - # TODO: function? - # TODO: related? - # TODO: reference? -] -for name, field in models: - attrs = { - '_name': 'export.%s' % name, - '_module': 'base', - '_columns': { - 'const': fields.integer(), - 'value': field - }, - '_defaults': {'const': 4}, - 'name_get': (lambda self, cr, uid, ids, context=None: - [(record.id, "%s:%s" % (self._name, record.value)) - for record in self.browse(cr, uid, ids, context=context)]) - } - NewModel = type( - 'Export%s' % ''.join(section.capitalize() for section in name.split('.')), - (orm.Model,), - attrs) - -class One2ManyChild(orm.Model): - _name = 'export.one2many.child' - _module = 'base' - # FIXME: orm.py:1161, fix to name_get on m2o field - _rec_name = 'value' - - _columns = { - 'parent_id': fields.many2one('export.one2many'), - 'str': fields.char('unknown', size=None), - 'value': fields.integer() - } - def name_get(self, cr, uid, ids, context=None): - return [(record.id, "%s:%s" % (self._name, record.value)) - for record in self.browse(cr, uid, ids, context=context)] - -class Many2ManyChild(orm.Model): - _name = 'export.many2many.other' - _module = 'base' - # FIXME: orm.py:1161, fix to name_get on m2o field - _rec_name = 'value' - - _columns = { - 'str': fields.char('unknown', size=None), - 'value': fields.integer() - } - def name_get(self, cr, uid, ids, context=None): - return [(record.id, "%s:%s" % (self._name, record.value)) - for record in self.browse(cr, uid, ids, context=context)] def setUpModule(): openerp.tools.config['update'] = dict(base=1) @@ -433,6 +366,102 @@ class test_o2m(CreatorCase): [u'record5', '', u'13'], ]) +# todo: test with multiple o2m fields and exporting all +class test_o2m_multiple(CreatorCase): + model_name = 'export.one2many.multiple' + + def make(self, value=None, **values): + if value is not None: values['value'] = value + id = self.model.create(self.cr, openerp.SUPERUSER_ID, values) + return self.model.browse(self.cr, openerp.SUPERUSER_ID, [id])[0] + def export(self, value=None, fields=('child1', 'child2',), context=None, **values): + record = self.make(value, **values) + return self.model._BaseModel__export_row( + self.cr, openerp.SUPERUSER_ID, record, + [f.split('/') for f in fields], + context=context) + + def test_empty(self): + self.assertEqual( + self.export(child1=False, child2=False), + [[False, False]]) + + def test_single_per_side(self): + self.assertEqual( + self.export(child1=False, child2=[(0, False, {'value': 42})]), + [[False, u'export.one2many.child.2:42']]) + + self.assertEqual( + self.export(child1=[(0, False, {'value': 43})], child2=False), + [[u'export.one2many.child.1:43', False]]) + + self.assertEqual( + self.export(child1=[(0, False, {'value': 43})], + child2=[(0, False, {'value': 42})]), + [[u'export.one2many.child.1:43', u'export.one2many.child.2:42']]) + + def test_single_integrate_subfield(self): + fields = ['const', 'child1/value', 'child2/value'] + self.assertEqual( + self.export(child1=False, child2=[(0, False, {'value': 42})], + fields=fields), + [[u'36', False, u'42']]) + + self.assertEqual( + self.export(child1=[(0, False, {'value': 43})], child2=False, + fields=fields), + [[u'36', u'43', False]]) + + self.assertEqual( + self.export(child1=[(0, False, {'value': 43})], + child2=[(0, False, {'value': 42})], + fields=fields), + [[u'36', u'43', u'42']]) + + def test_multiple(self): + """ With two "concurrent" o2ms, exports the first line combined, then + exports the rows for the first o2m, then the rows for the second o2m. + """ + fields = ['const', 'child1/value', 'child2/value'] + child1 = [(0, False, {'value': v, 'str': 'record%.02d' % index}) + for index, v in zip(itertools.count(), [4, 42, 36, 4, 13])] + child2 = [(0, False, {'value': v, 'str': 'record%.02d' % index}) + for index, v in zip(itertools.count(10), [8, 12, 8, 55, 33, 13])] + + self.assertEqual( + self.export(child1=child1, child2=False, fields=fields), + [ + [u'36', u'4', False], + ['', u'42', ''], + ['', u'36', ''], + ['', u'4', ''], + ['', u'13', ''], + ]) + self.assertEqual( + self.export(child1=False, child2=child2, fields=fields), + [ + [u'36', False, u'8'], + ['', '', u'12'], + ['', '', u'8'], + ['', '', u'55'], + ['', '', u'33'], + ['', '', u'13'], + ]) + self.assertEqual( + self.export(child1=child1, child2=child2, fields=fields), + [ + [u'36', u'4', u'8'], + ['', u'42', ''], + ['', u'36', ''], + ['', u'4', ''], + ['', u'13', ''], + ['', '', u'12'], + ['', '', u'8'], + ['', '', u'55'], + ['', '', u'33'], + ['', '', u'13'], + ]) + class test_m2m(CreatorCase): model_name = 'export.many2many' commands = [ From 73f3f24fb4564e9edb7bf4ddede1ccf46aaa94e7 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 30 Jul 2012 12:32:14 +0200 Subject: [PATCH 09/85] [ADD] tests for selection[function] and function fields export bzr revid: xmo@openerp.com-20120730103214-t4s9g0z3sl1x05b3 --- openerp/tests/export_models.py | 19 +++++++++++++++---- openerp/tests/test_export.py | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/openerp/tests/export_models.py b/openerp/tests/export_models.py index d54c73ac4ad..2f85ab473e8 100644 --- a/openerp/tests/export_models.py +++ b/openerp/tests/export_models.py @@ -1,6 +1,16 @@ # -*- coding: utf-8 -*- from openerp.osv import orm, fields +def selection_fn(obj, cr, uid, context=None): + return list(enumerate(["Corge", "Grault", "Wheee", "Moog"])) + +def function_fn(model, cr, uid, ids, field_name, arg, context): + return dict((id, 3) for id in ids) +def function_fn_write(model, cr, uid, id, field_name, field_value, fnct_inv_arg, context): + """ just so CreatorCase.export can be used + """ + pass + models = [ ('boolean', fields.boolean()), ('integer', fields.integer()), @@ -12,13 +22,14 @@ models = [ ('datetime', fields.datetime()), ('text', fields.text()), ('selection', fields.selection([(1, "Foo"), (2, "Bar"), (3, "Qux")])), + ('selection.function', fields.selection(selection_fn)), # just relate to an integer ('many2one', fields.many2one('export.integer')), ('one2many', fields.one2many('export.one2many.child', 'parent_id')), - ('many2many', fields.many2many('export.many2many.other')) - # TODO: function? - # TODO: related? - # TODO: reference? + ('many2many', fields.many2many('export.many2many.other')), + ('function', fields.function(function_fn, fnct_inv=function_fn_write, type="integer")), + # related: specialization of fields.function, should work the same way + # TODO: reference ] for name, field in models: attrs = { diff --git a/openerp/tests/test_export.py b/openerp/tests/test_export.py index ea21cd36a8a..ee4405db75d 100644 --- a/openerp/tests/test_export.py +++ b/openerp/tests/test_export.py @@ -7,7 +7,7 @@ from . import common, export_models def setUpModule(): - openerp.tools.config['update'] = dict(base=1) + openerp.tools.config['update'] = {'base': 1} openerp.modules.registry.RegistryManager.new( common.DB, update_module=True) @@ -237,6 +237,28 @@ class test_selection(CreatorCase): self.export(2), [[u"Bar"]]) +class test_selection_function(CreatorCase): + model_name = 'export.selection.function' + + def test_empty(self): + self.assertEqual( + self.export(False), + [[False]]) + + def test_value(self): + """ selection functions export the *value* itself + """ + self.assertEqual( + self.export(1), + [[u'1']]) + self.assertEqual( + self.export(3), + [[u'3']]) + # fucking hell + self.assertEqual( + self.export(0), + [[False]]) + class test_m2o(CreatorCase): model_name = 'export.many2one' @@ -518,3 +540,13 @@ class test_m2m(CreatorCase): ]]) # essentially same as o2m, so boring + +class test_function(CreatorCase): + model_name = 'export.function' + + def test_value(self): + """ Exports value normally returned by accessing the function field + """ + self.assertEqual( + self.export(42), + [[u'3']]) From 64910eaebad74019b4d7e0c58621a8487f2c007e Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 30 Jul 2012 13:11:05 +0200 Subject: [PATCH 10/85] [ADD] boolean field import tests bzr revid: xmo@openerp.com-20120730111105-k6q8xop0ru71l0dj --- openerp/tests/__init__.py | 3 +- openerp/tests/test_import.py | 95 ++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 openerp/tests/test_import.py diff --git a/openerp/tests/__init__.py b/openerp/tests/__init__.py index 7b30adece79..4b8fa798aea 100644 --- a/openerp/tests/__init__.py +++ b/openerp/tests/__init__.py @@ -9,7 +9,7 @@ See the :ref:`test-framework` section in the :ref:`features` list. """ from . import test_expression, test_ir_sequence, test_orm,\ - test_uninstall, test_export + test_uninstall, test_export, test_import fast_suite = [ test_ir_sequence, @@ -19,6 +19,7 @@ checks = [ test_expression, test_orm, test_export, + test_import, ] # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/tests/test_import.py b/openerp/tests/test_import.py new file mode 100644 index 00000000000..4c5af087b2c --- /dev/null +++ b/openerp/tests/test_import.py @@ -0,0 +1,95 @@ +# -*- coding: utf-8 -*- +import openerp.modules.registry +import openerp + +from . import common, export_models + +def ok(n): + """ Successful import of ``n`` records + + :param int n: number of records which should have been imported + """ + return n, 0, 0, 0 + +def values(seq): + return [item['value'] for item in seq] + +def setupModule(): + openerp.tools.config['update'] = {'base': 1} + openerp.modules.registry.RegistryManager.new( + common.DB, update_module=True) + +class ImporterCase(common.TransactionCase): + model_name = False + + def __init__(self, *args, **kwargs): + super(ImporterCase, self).__init__(*args, **kwargs) + self.model = None + + def setUp(self): + super(ImporterCase, self).setUp() + self.model = self.registry(self.model_name) + + def import_(self, fields, rows, context=None): + return self.model.import_data( + self.cr, openerp.SUPERUSER_ID, fields, rows, context=context) + def read(self, fields=('value',), domain=(), context=None): + return self.model.read( + self.cr, openerp.SUPERUSER_ID, + self.model.search(self.cr, openerp.SUPERUSER_ID, domain, context=context), + fields=fields, context=context) + +class test_boolean_field(ImporterCase): + model_name = 'export.boolean' + + def test_empty(self): + self.assertEqual( + self.import_(['value'], []), + ok(0)) + + def test_exported(self): + self.assertEqual( + self.import_(['value'], [ + ['False'], + ['True'], + ]), + ok(2)) + records = self.read() + self.assertItemsEqual([ + False, + True, + ], values(records)) + + def test_falses(self): + self.assertEqual( + self.import_(['value'], [ + [u'0'], + [u'off'], + [u'false'], + [u'FALSE'], + [u'OFF'], + ]), + ok(5)) + self.assertItemsEqual([ + False, + False, + False, + False, + False, + ], + values(self.read())) + + def test_trues(self): + self.assertEqual( + self.import_(['value'], [ + ['no'], + ['None'], + ['nil'], + ['()'], + ['f'], + ['#f'], + ]), + ok(6)) + self.assertItemsEqual( + [True] * 6, + values(self.read())) From 59193b0c0fdcffb05e1a25a01f083bbf5b8d924d Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 30 Jul 2012 13:36:33 +0200 Subject: [PATCH 11/85] [ADD] empty record/field case to booleans bzr revid: xmo@openerp.com-20120730113633-tauioxm941k0iaqn --- openerp/tests/test_import.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/openerp/tests/test_import.py b/openerp/tests/test_import.py index 4c5af087b2c..e29cc8a3ad9 100644 --- a/openerp/tests/test_import.py +++ b/openerp/tests/test_import.py @@ -55,7 +55,7 @@ class test_boolean_field(ImporterCase): ]), ok(2)) records = self.read() - self.assertItemsEqual([ + self.assertEqual([ False, True, ], values(records)) @@ -68,9 +68,11 @@ class test_boolean_field(ImporterCase): [u'false'], [u'FALSE'], [u'OFF'], + [u''], ]), - ok(5)) - self.assertItemsEqual([ + ok(6)) + self.assertEqual([ + False, False, False, False, @@ -90,6 +92,7 @@ class test_boolean_field(ImporterCase): ['#f'], ]), ok(6)) - self.assertItemsEqual( + self.assertEqual( [True] * 6, values(self.read())) + From 6af9bb41598dfdc03721fe228fb4c5f972335361 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 30 Jul 2012 14:22:43 +0200 Subject: [PATCH 12/85] [TEST] integers import bzr revid: xmo@openerp.com-20120730122243-wjpalm6ovwuk6k5c --- openerp/tests/test_import.py | 88 +++++++++++++++++++++++++++++++++++- 1 file changed, 86 insertions(+), 2 deletions(-) diff --git a/openerp/tests/test_import.py b/openerp/tests/test_import.py index e29cc8a3ad9..c38517a6f07 100644 --- a/openerp/tests/test_import.py +++ b/openerp/tests/test_import.py @@ -11,6 +11,18 @@ def ok(n): """ return n, 0, 0, 0 +def error(row, message, record=None, **kwargs): + """ Failed import of the record ``record`` at line ``row``, with the error + message ``message`` + + :param str message: + :param dict record: + """ + return ( + -1, dict(record or {}, **kwargs), + "Line %d : %s\n" % (row, message), + '') + def values(seq): return [item['value'] for item in seq] @@ -90,9 +102,81 @@ class test_boolean_field(ImporterCase): ['()'], ['f'], ['#f'], + # Problem: OpenOffice (and probably excel) output localized booleans + ['VRAI'], ]), - ok(6)) + ok(7)) self.assertEqual( - [True] * 6, + [True] * 7, values(self.read())) +class test_integer_field(ImporterCase): + model_name = 'export.integer' + + def test_none(self): + self.assertEqual( + self.import_(['value'], []), + ok(0)) + + def test_empty(self): + self.assertEqual( + self.import_(['value'], [['']]), + ok(1)) + self.assertEqual( + [False], + values(self.read())) + + def test_zero(self): + self.assertEqual( + self.import_(['value'], [['0']]), + ok(1)) + self.assertEqual( + self.import_(['value'], [['-0']]), + ok(1)) + self.assertEqual([False, False], values(self.read())) + + def test_positives(self): + self.assertEqual( + self.import_(['value'], [ + ['1'], + ['42'], + [str(2**31-1)], + ['12345678'] + ]), + ok(4)) + self.assertEqual([ + 1, 42, 2**31-1, 12345678 + ], values(self.read())) + + def test_negatives(self): + self.assertEqual( + self.import_(['value'], [ + ['-1'], + ['-42'], + [str(-(2**31 - 1))], + [str(-(2**31))], + ['-12345678'] + ]), + ok(5)) + self.assertEqual([ + -1, -42, -(2**31 - 1), -(2**31), -12345678 + ], values(self.read())) + + def test_out_of_range(self): + self.assertEqual( + self.import_(['value'], [[str(2**31)]]), + error(1, "integer out of range", value=2**31)) + # auto-rollbacks if error is in process_liness, but not during + # ir.model.data write. Can differentiate because former ends lines + # error lines with "!" + self.cr.rollback() + self.assertEqual( + self.import_(['value'], [[str(-2**32)]]), + error(1, "integer out of range", value=-2**32)) + + + def test_nonsense(self): + # dafuq? why does that one raise an error? + self.assertRaises( + ValueError, + self.import_, ['value'], [['zorglub']]) From 1cf264cfd4ae468653f5aade0786f522a459bf58 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 30 Jul 2012 16:28:51 +0200 Subject: [PATCH 13/85] [TEST] char and text fields bzr revid: xmo@openerp.com-20120730142851-6854x99l4sxmoc0s --- openerp/tests/test_import.py | 125 +++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/openerp/tests/test_import.py b/openerp/tests/test_import.py index c38517a6f07..7fef60876e0 100644 --- a/openerp/tests/test_import.py +++ b/openerp/tests/test_import.py @@ -180,3 +180,128 @@ class test_integer_field(ImporterCase): self.assertRaises( ValueError, self.import_, ['value'], [['zorglub']]) + +class test_float_field(ImporterCase): + model_name = 'export.float' + def test_none(self): + self.assertEqual( + self.import_(['value'], []), + ok(0)) + + def test_empty(self): + self.assertEqual( + self.import_(['value'], [['']]), + ok(1)) + self.assertEqual( + [False], + values(self.read())) + + def test_zero(self): + self.assertEqual( + self.import_(['value'], [['0']]), + ok(1)) + self.assertEqual( + self.import_(['value'], [['-0']]), + ok(1)) + self.assertEqual([False, False], values(self.read())) + + def test_positives(self): + self.assertEqual( + self.import_(['value'], [ + ['1'], + ['42'], + [str(2**31-1)], + ['12345678'], + [str(2**33)], + ['0.000001'], + ]), + ok(6)) + self.assertEqual([ + 1, 42, 2**31-1, 12345678, 2.0**33, .000001 + ], values(self.read())) + + def test_negatives(self): + self.assertEqual( + self.import_(['value'], [ + ['-1'], + ['-42'], + [str(-2**31 + 1)], + [str(-2**31)], + ['-12345678'], + [str(-2**33)], + ['-0.000001'], + ]), + ok(7)) + self.assertEqual([ + -1, -42, -(2**31 - 1), -(2**31), -12345678, -2.0**33, -.000001 + ], values(self.read())) + + def test_nonsense(self): + self.assertRaises( + ValueError, + self.import_, ['value'], [['foobar']]) + +class test_string_field(ImporterCase): + model_name = 'export.string.bounded' + + def test_empty(self): + self.assertEqual( + self.import_(['value'], [['']]), + ok(1)) + self.assertEqual([False], values(self.read())) + + def test_imported(self): + self.assertEqual( + self.import_(['value'], [ + [u'foobar'], + [u'foobarbaz'], + [u'Með suð í eyrum við spilum endalaust'], + [u"People 'get' types. They use them all the time. Telling " + u"someone he can't pound a nail with a banana doesn't much " + u"surprise him."] + ]), + ok(4)) + self.assertEqual([ + u"foobar", + u"foobarbaz", + u"Með suð í eyrum ", + u"People 'get' typ", + ], values(self.read())) + +class test_unbound_string_field(ImporterCase): + model_name = 'export.string' + + def test_imported(self): + self.assertEqual( + self.import_(['value'], [ + [u'í dag viðrar vel til loftárása'], + # ackbar.jpg + [u"If they ask you about fun, you tell them – fun is a filthy" + u" parasite"] + ]), + ok(2)) + self.assertEqual([ + u"í dag viðrar vel til loftárása", + u"If they ask you about fun, you tell them – fun is a filthy parasite" + ], values(self.read())) + +class test_text(ImporterCase): + model_name = 'export.text' + + def test_empty(self): + self.assertEqual( + self.import_(['value'], [['']]), + ok(1)) + self.assertEqual([False], values(self.read())) + + def test_imported(self): + s = (u"Breiðskífa er notað um útgefna hljómplötu sem inniheldur " + u"stúdíóupptökur frá einum flytjanda. Breiðskífur eru oftast " + u"milli 25-80 mínútur og er lengd þeirra oft miðuð við 33⅓ " + u"snúninga 12 tommu vínylplötur (sem geta verið allt að 30 mín " + u"hvor hlið).\n\nBreiðskífur eru stundum tvöfaldar og eru þær þá" + u" gefnar út á tveimur geisladiskum eða tveimur vínylplötum.") + self.assertEqual( + self.import_(['value'], [[s]]), + ok(1)) + self.assertEqual([s], values(self.read())) From 5b5148b925e5750036257c33426faa41c5f61486 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 30 Jul 2012 17:28:44 +0200 Subject: [PATCH 14/85] [TEST] selection fields import bzr revid: xmo@openerp.com-20120730152844-xob3ezlw0qpb1kxc --- openerp/tests/test_export.py | 2 ++ openerp/tests/test_import.py | 49 +++++++++++++++++++++++++++++++++--- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/openerp/tests/test_export.py b/openerp/tests/test_export.py index ee4405db75d..01d0fbc7726 100644 --- a/openerp/tests/test_export.py +++ b/openerp/tests/test_export.py @@ -237,6 +237,8 @@ class test_selection(CreatorCase): self.export(2), [[u"Bar"]]) + # TODO: localized export! + class test_selection_function(CreatorCase): model_name = 'export.selection.function' diff --git a/openerp/tests/test_import.py b/openerp/tests/test_import.py index 7fef60876e0..9c817236cf2 100644 --- a/openerp/tests/test_import.py +++ b/openerp/tests/test_import.py @@ -20,7 +20,7 @@ def error(row, message, record=None, **kwargs): """ return ( -1, dict(record or {}, **kwargs), - "Line %d : %s\n" % (row, message), + "Line %d : %s" % (row, message), '') def values(seq): @@ -165,14 +165,14 @@ class test_integer_field(ImporterCase): def test_out_of_range(self): self.assertEqual( self.import_(['value'], [[str(2**31)]]), - error(1, "integer out of range", value=2**31)) + error(1, "integer out of range\n", value=2**31)) # auto-rollbacks if error is in process_liness, but not during # ir.model.data write. Can differentiate because former ends lines # error lines with "!" self.cr.rollback() self.assertEqual( self.import_(['value'], [[str(-2**32)]]), - error(1, "integer out of range", value=-2**32)) + error(1, "integer out of range\n", value=-2**32)) def test_nonsense(self): @@ -305,3 +305,46 @@ class test_text(ImporterCase): self.import_(['value'], [[s]]), ok(1)) self.assertEqual([s], values(self.read())) + +class test_selection(ImporterCase): + model_name = 'export.selection' + + def test_imported(self): + self.assertEqual( + self.import_(['value'], [ + ['Qux'], + ['Bar'], + ['Foo'], + [2], + ]), + ok(4)) + self.assertEqual([3, 2, 1, 2], values(self.read())) + + def test_invalid(self): + self.assertEqual( + self.import_(['value'], [['Baz']]), + error(1, "Key/value 'Baz' not found in selection field 'value'", + # what the fuck? + value=False)) + +class test_selection_function(ImporterCase): + model_name = 'export.selection.function' + + def test_imported(self): + """ By what bloody magic does that thing work? + + => import uses fields_get, so translates import label (may or may not + be good news) *and* serializes the selection function to reverse + it: import does not actually know that the selection field uses a + function + """ + # TODO: localized import + self.assertEqual( + self.import_(['value'], [ + [3], + ["Grault"], + ]), + ok(2)) + self.assertEqual( + ['3', '1'], + values(self.read())) From 496177a9b1a8fbd517bf8faf453b8ac04a1e996c Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 30 Jul 2012 17:30:17 +0200 Subject: [PATCH 15/85] [TEST] incorrect value for selection field import bzr revid: xmo@openerp.com-20120730153017-iywveudrq5h0ykef --- openerp/tests/test_import.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openerp/tests/test_import.py b/openerp/tests/test_import.py index 9c817236cf2..c87f65c4c6e 100644 --- a/openerp/tests/test_import.py +++ b/openerp/tests/test_import.py @@ -326,6 +326,11 @@ class test_selection(ImporterCase): error(1, "Key/value 'Baz' not found in selection field 'value'", # what the fuck? value=False)) + self.cr.rollback() + self.assertEqual( + self.import_(['value'], [[42]]), + error(1, "Key/value '42' not found in selection field 'value'", + value=False)) class test_selection_function(ImporterCase): model_name = 'export.selection.function' From f8822ae313e380f72f5aff23b7f61d2f73939764 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 30 Jul 2012 18:23:18 +0200 Subject: [PATCH 16/85] [TEST] m2o import bzr revid: xmo@openerp.com-20120730162318-qwxncgvizrifgxyh --- openerp/tests/export_models.py | 8 ++- openerp/tests/test_import.py | 106 +++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+), 1 deletion(-) diff --git a/openerp/tests/export_models.py b/openerp/tests/export_models.py index 2f85ab473e8..277814808b4 100644 --- a/openerp/tests/export_models.py +++ b/openerp/tests/export_models.py @@ -42,7 +42,13 @@ for name, field in models: '_defaults': {'const': 4}, 'name_get': (lambda self, cr, uid, ids, context=None: [(record.id, "%s:%s" % (self._name, record.value)) - for record in self.browse(cr, uid, ids, context=context)]) + for record in self.browse(cr, uid, ids, context=context)]), + 'name_search': (lambda self, cr, uid, name, operator, context=None: + self.name_get(cr, uid, + self.search(cr, uid, [['value', operator, int(name.split(':')[1])]]) + , context=context) + if isinstance(name, basestring) and name.split(':')[0] == self._name + else []) } NewModel = type( 'Export%s' % ''.join(section.capitalize() for section in name.split('.')), diff --git a/openerp/tests/test_import.py b/openerp/tests/test_import.py index c87f65c4c6e..90e14375540 100644 --- a/openerp/tests/test_import.py +++ b/openerp/tests/test_import.py @@ -50,6 +50,12 @@ class ImporterCase(common.TransactionCase): self.cr, openerp.SUPERUSER_ID, self.model.search(self.cr, openerp.SUPERUSER_ID, domain, context=context), fields=fields, context=context) + def browse(self, domain=(), context=None): + return self.model.browse( + self.cr, openerp.SUPERUSER_ID, + self.model.search(self.cr, openerp.SUPERUSER_ID, domain, context=context), + context=context) + class test_boolean_field(ImporterCase): model_name = 'export.boolean' @@ -353,3 +359,103 @@ class test_selection_function(ImporterCase): self.assertEqual( ['3', '1'], values(self.read())) + +class test_o2m(ImporterCase): + model_name = 'export.many2one' + + def test_by_name(self): + # create integer objects + integer_id1 = self.registry('export.integer').create( + self.cr, openerp.SUPERUSER_ID, {'value': 42}) + integer_id2 = self.registry('export.integer').create( + self.cr, openerp.SUPERUSER_ID, {'value': 36}) + # get its name + name1 = dict(self.registry('export.integer').name_get( + self.cr, openerp.SUPERUSER_ID,[integer_id1]))[integer_id1] + name2 = dict(self.registry('export.integer').name_get( + self.cr, openerp.SUPERUSER_ID,[integer_id2]))[integer_id2] + + self.assertEqual( + self.import_(['value'], [ + # import by name_get + [name1], + [name1], + [name2], + ]), + ok(3)) + # correct ids assigned to corresponding records + self.assertEqual([ + (integer_id1, name1), + (integer_id1, name1), + (integer_id2, name2),], + values(self.read())) + + # TODO: test import by xid + + def test_by_id(self): + integer_id = self.registry('export.integer').create( + self.cr, openerp.SUPERUSER_ID, {'value': 42}) + self.assertEqual( + self.import_(['value/.id'], [[integer_id]]), + ok(1)) + b = self.browse() + self.assertEqual(42, b[0].value.value) + + def test_by_names(self): + integer_id1 = self.registry('export.integer').create( + self.cr, openerp.SUPERUSER_ID, {'value': 42}) + integer_id2 = self.registry('export.integer').create( + self.cr, openerp.SUPERUSER_ID, {'value': 42}) + name1 = dict(self.registry('export.integer').name_get( + self.cr, openerp.SUPERUSER_ID,[integer_id1]))[integer_id1] + name2 = dict(self.registry('export.integer').name_get( + self.cr, openerp.SUPERUSER_ID,[integer_id2]))[integer_id2] + # names should be the same + self.assertEqual(name1, name2) + + self.assertEqual( + self.import_(['value'], [[name2]]), + ok(1)) + # FIXME: is it really normal import does not care for name_search collisions? + self.assertEqual([ + (integer_id1, name1) + ], values(self.read())) + + def test_fail_by_implicit_id(self): + """ Can't implicitly import records by id + """ + # create integer objects + integer_id1 = self.registry('export.integer').create( + self.cr, openerp.SUPERUSER_ID, {'value': 42}) + integer_id2 = self.registry('export.integer').create( + self.cr, openerp.SUPERUSER_ID, {'value': 36}) + + self.assertRaises( + ValueError, # Because name_search all the things. Fallback schmallback + self.import_, ['value'], [ + # import by id, without specifying it + [integer_id1], + [integer_id2], + [integer_id1], + ]) + + def test_sub_field(self): + """ Does not implicitly create the record, does not warn that you can't + import m2o subfields (at all)... + """ + self.assertRaises( + ValueError, # No record found for 42, name_searches the bloody thing + self.import_, ['value/value'], [['42']]) + + def test_fail_noids(self): + self.assertRaises( + ValueError, + self.import_, ['value'], [['nameisnoexist:3']]) + self.cr.rollback() + self.assertRaises( + ValueError, + self.import_, ['value/id'], [['noxidhere']]), + self.cr.rollback() + self.assertRaises( + Exception, # FIXME: Why can't you be a ValueError like everybody else? + self.import_, ['value/.id'], [[66]]) From a3542213bc80ab9247e70a4f1353da6e36dd8266 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 30 Jul 2012 18:53:30 +0200 Subject: [PATCH 17/85] [TEST] ids handling on main record bzr revid: xmo@openerp.com-20120730165330-lm4l1yzfjduajb1p --- openerp/tests/test_import.py | 37 ++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/openerp/tests/test_import.py b/openerp/tests/test_import.py index 90e14375540..2305928a12e 100644 --- a/openerp/tests/test_import.py +++ b/openerp/tests/test_import.py @@ -56,6 +56,38 @@ class ImporterCase(common.TransactionCase): self.model.search(self.cr, openerp.SUPERUSER_ID, domain, context=context), context=context) +class test_ids_stuff(ImporterCase): + model_name = 'export.integer' + + def test_create_with_id(self): + self.assertRaises( + Exception, # dammit + self.import_, ['.id', 'value'], [['42', '36']]) + def test_create_with_xid(self): + self.assertEqual( + self.import_(['id', 'value'], [['somexmlid', '42']]), + ok(1)) + # TODO: get xid back, check that it is correct? + + def test_update_with_id(self): + id = self.model.create(self.cr, openerp.SUPERUSER_ID, {'value': 36}) + self.assertEqual( + 36, + self.model.browse(self.cr, openerp.SUPERUSER_ID, id).value) + + self.assertEqual( + self.import_(['.id', 'value'], [[str(id), '42']]), + ok(1)) + self.assertEqual( + [42], # updated value to imported + values(self.read())) + + def test_update_with_xid(self): + self.import_(['id', 'value'], [['somexmlid', '36']]) + self.assertEqual([36], values(self.read())) + + self.import_(['id', 'value'], [['somexmlid', '1234567']]) + self.assertEqual([1234567], values(self.read())) class test_boolean_field(ImporterCase): model_name = 'export.boolean' @@ -459,3 +491,8 @@ class test_o2m(ImporterCase): self.assertRaises( Exception, # FIXME: Why can't you be a ValueError like everybody else? self.import_, ['value/.id'], [[66]]) +# TODO: M2M +# TODO: O2M + +# function, related, reference: written to db as-is... +# => function uses @type for value coercion/conversion From 6bad3e84fb1093b3e9c1a3c99bea3cc19fd95e2e Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 31 Jul 2012 12:39:08 +0200 Subject: [PATCH 18/85] [TEST] m2o import link by xid bzr revid: xmo@openerp.com-20120731103908-p1apmrx3ifnodz0f --- openerp/tests/test_import.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/openerp/tests/test_import.py b/openerp/tests/test_import.py index 2305928a12e..0b0d8fe4d0d 100644 --- a/openerp/tests/test_import.py +++ b/openerp/tests/test_import.py @@ -392,7 +392,7 @@ class test_selection_function(ImporterCase): ['3', '1'], values(self.read())) -class test_o2m(ImporterCase): +class test_m2o(ImporterCase): model_name = 'export.many2one' def test_by_name(self): @@ -422,7 +422,21 @@ class test_o2m(ImporterCase): (integer_id2, name2),], values(self.read())) - # TODO: test import by xid + def test_by_xid(self): + integer_id = self.registry('export.integer').create( + self.cr, openerp.SUPERUSER_ID, {'value': 42}) + self.registry('ir.model.data').create(self.cr, openerp.SUPERUSER_ID, { + 'name': 'export-integer-value', + 'model': 'export.integer', + 'res_id': integer_id, + 'module': 'test-export' + }) + + self.assertEqual( + self.import_(['value/id'], [['test-export.export-integer-value']]), + ok(1)) + b = self.browse() + self.assertEqual(42, b[0].value.value) def test_by_id(self): integer_id = self.registry('export.integer').create( @@ -491,7 +505,9 @@ class test_o2m(ImporterCase): self.assertRaises( Exception, # FIXME: Why can't you be a ValueError like everybody else? self.import_, ['value/.id'], [[66]]) + # TODO: M2M + # TODO: O2M # function, related, reference: written to db as-is... From ac647909bf97d6caf2ddc74b4daad6cea7752645 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 31 Jul 2012 15:54:49 +0200 Subject: [PATCH 19/85] [TEST] m2m import bzr revid: xmo@openerp.com-20120731135449-i07ex1zswepen3wt --- openerp/tests/export_models.py | 6 ++ openerp/tests/test_export.py | 2 +- openerp/tests/test_import.py | 135 ++++++++++++++++++++++++++++++--- 3 files changed, 132 insertions(+), 11 deletions(-) diff --git a/openerp/tests/export_models.py b/openerp/tests/export_models.py index 277814808b4..3bb5cf0c6d2 100644 --- a/openerp/tests/export_models.py +++ b/openerp/tests/export_models.py @@ -117,3 +117,9 @@ class Many2ManyChild(orm.Model): def name_get(self, cr, uid, ids, context=None): return [(record.id, "%s:%s" % (self._name, record.value)) for record in self.browse(cr, uid, ids, context=context)] + def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=100): + return (self.name_get(cr, user, + self.search(cr, user, [['value', operator, int(name.split(':')[1])]]) + , context=context) + if isinstance(name, basestring) and name.split(':')[0] == self._name + else []) diff --git a/openerp/tests/test_export.py b/openerp/tests/test_export.py index 01d0fbc7726..d693de01993 100644 --- a/openerp/tests/test_export.py +++ b/openerp/tests/test_export.py @@ -537,7 +537,7 @@ class test_m2m(CreatorCase): def test_multiple_records_name(self): self.assertEqual( self.export(self.commands, fields=['const', 'value']), - [[ + [[ # FIXME: hardcoded comma, import uses config.csv_internal_sep u'4', u','.join(self.names) ]]) diff --git a/openerp/tests/test_import.py b/openerp/tests/test_import.py index 0b0d8fe4d0d..b275db520c8 100644 --- a/openerp/tests/test_import.py +++ b/openerp/tests/test_import.py @@ -56,6 +56,30 @@ class ImporterCase(common.TransactionCase): self.model.search(self.cr, openerp.SUPERUSER_ID, domain, context=context), context=context) + def xid(self, record): + ModelData = self.registry('ir.model.data') + + ids = ModelData.search( + self.cr, openerp.SUPERUSER_ID, + [('model', '=', record._table_name), ('res_id', '=', record.id)]) + if ids: + d = ModelData.read( + self.cr, openerp.SUPERUSER_ID, ids, ['name', 'module'])[0] + if d['module']: + return '%s.%s' % (d['module'], d['name']) + return d['name'] + + name = dict(record.name_get())[record.id] + # fix dotted name_get results, otherwise xid lookups blow up + name = name.replace('.', '-') + ModelData.create(self.cr, openerp.SUPERUSER_ID, { + 'name': name, + 'model': record._table_name, + 'res_id': record.id, + 'module': '__test__' + }) + return '__test__.' + name + class test_ids_stuff(ImporterCase): model_name = 'export.integer' @@ -67,7 +91,9 @@ class test_ids_stuff(ImporterCase): self.assertEqual( self.import_(['id', 'value'], [['somexmlid', '42']]), ok(1)) - # TODO: get xid back, check that it is correct? + self.assertEqual( + 'somexmlid', + self.xid(self.browse()[0])) def test_update_with_id(self): id = self.model.create(self.cr, openerp.SUPERUSER_ID, {'value': 36}) @@ -423,17 +449,14 @@ class test_m2o(ImporterCase): values(self.read())) def test_by_xid(self): - integer_id = self.registry('export.integer').create( + ExportInteger = self.registry('export.integer') + integer_id = ExportInteger.create( self.cr, openerp.SUPERUSER_ID, {'value': 42}) - self.registry('ir.model.data').create(self.cr, openerp.SUPERUSER_ID, { - 'name': 'export-integer-value', - 'model': 'export.integer', - 'res_id': integer_id, - 'module': 'test-export' - }) + xid = self.xid(ExportInteger.browse( + self.cr, openerp.SUPERUSER_ID, [integer_id])[0]) self.assertEqual( - self.import_(['value/id'], [['test-export.export-integer-value']]), + self.import_(['value/id'], [[xid]]), ok(1)) b = self.browse() self.assertEqual(42, b[0].value.value) @@ -506,7 +529,99 @@ class test_m2o(ImporterCase): Exception, # FIXME: Why can't you be a ValueError like everybody else? self.import_, ['value/.id'], [[66]]) -# TODO: M2M +class test_m2m(ImporterCase): + model_name = 'export.many2many' + + # apparently, one and only thing which works is a + # csv_internal_sep-separated list of ids, xids, or names (depending if + # m2m/.id, m2m/id or m2m[/anythingelse] + def test_ids(self): + id1 = self.registry('export.many2many.other').create( + self.cr, openerp.SUPERUSER_ID, {'value': 3, 'str': 'record0'}) + id2 = self.registry('export.many2many.other').create( + self.cr, openerp.SUPERUSER_ID, {'value': 44, 'str': 'record1'}) + id3 = self.registry('export.many2many.other').create( + self.cr, openerp.SUPERUSER_ID, {'value': 84, 'str': 'record2'}) + id4 = self.registry('export.many2many.other').create( + self.cr, openerp.SUPERUSER_ID, {'value': 9, 'str': 'record3'}) + id5 = self.registry('export.many2many.other').create( + self.cr, openerp.SUPERUSER_ID, {'value': 99, 'str': 'record4'}) + + self.assertEqual( + self.import_(['value/.id'], [ + ['%d,%d' % (id1, id2)], + ['%d,%d,%d' % (id1, id3, id4)], + ['%d,%d,%d' % (id1, id2, id3)], + ['%d' % id5] + ]), + ok(4)) + ids = lambda records: [record.id for record in records] + + b = self.browse() + self.assertEqual(ids(b[0].value), [id1, id2]) + self.assertEqual(values(b[0].value), [3, 44]) + + self.assertEqual(ids(b[2].value), [id1, id2, id3]) + self.assertEqual(values(b[2].value), [3, 44, 84]) + + def test_noids(self): + try: + self.import_(['value/.id'], [['42']]) + self.fail("Should have raised an exception") + except Exception, e: + self.assertIs(type(e), Exception, + "test should be fixed on exception subclass") + + def test_xids(self): + M2O_o = self.registry('export.many2many.other') + id1 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 3, 'str': 'record0'}) + id2 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 44, 'str': 'record1'}) + id3 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 84, 'str': 'record2'}) + id4 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 9, 'str': 'record3'}) + records = M2O_o.browse(self.cr, openerp.SUPERUSER_ID, [id1, id2, id3, id4]) + + self.assertEqual( + self.import_(['value/id'], [ + ['%s,%s' % (self.xid(records[0]), self.xid(records[1]))], + ['%s' % self.xid(records[3])], + ['%s,%s' % (self.xid(records[2]), self.xid(records[1]))], + ]), + ok(3)) + + b = self.browse() + self.assertEqual(values(b[0].value), [3, 44]) + self.assertEqual(values(b[2].value), [44, 84]) + def test_noxids(self): + self.assertRaises( + ValueError, + self.import_, ['value/id'], [['noxidforthat']]) + + def test_names(self): + M2O_o = self.registry('export.many2many.other') + id1 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 3, 'str': 'record0'}) + id2 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 44, 'str': 'record1'}) + id3 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 84, 'str': 'record2'}) + id4 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 9, 'str': 'record3'}) + records = M2O_o.browse(self.cr, openerp.SUPERUSER_ID, [id1, id2, id3, id4]) + + name = lambda record: dict(record.name_get())[record.id] + + self.assertEqual( + self.import_(['value'], [ + ['%s,%s' % (name(records[1]), name(records[2]))], + ['%s,%s,%s' % (name(records[0]), name(records[1]), name(records[2]))], + ['%s,%s' % (name(records[0]), name(records[3]))], + ]), + ok(3)) + + b = self.browse() + self.assertEqual(values(b[1].value), [3, 44, 84]) + self.assertEqual(values(b[2].value), [3, 9]) + + def test_nonames(self): + self.assertRaises( + ValueError, + self.import_, ['value'], [['wherethem2mhavenonames']]) # TODO: O2M From 29e6a77fef10ebe06017bb55b492f8b004a0f80e Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 31 Jul 2012 16:46:06 +0200 Subject: [PATCH 20/85] [TEST] localized import/export of selection fields bzr revid: xmo@openerp.com-20120731144606-ceax7lk3rfi7zumk --- openerp/tests/test_export.py | 29 +++++++++++++- openerp/tests/test_import.py | 74 +++++++++++++++++++++++++++++++++++- 2 files changed, 100 insertions(+), 3 deletions(-) diff --git a/openerp/tests/test_export.py b/openerp/tests/test_export.py index d693de01993..d60e5a09dcb 100644 --- a/openerp/tests/test_export.py +++ b/openerp/tests/test_export.py @@ -224,6 +224,11 @@ class test_datetime(CreatorCase): class test_selection(CreatorCase): model_name = 'export.selection' + translations_fr = [ + ("Qux", "toto"), + ("Bar", "titi"), + ("Foo", "tete"), + ] def test_empty(self): self.assertEqual( @@ -237,7 +242,28 @@ class test_selection(CreatorCase): self.export(2), [[u"Bar"]]) - # TODO: localized export! + def test_localized_export(self): + self.registry('res.lang').create(self.cr, openerp.SUPERUSER_ID, { + 'name': u'Français', + 'code': 'fr_FR', + 'translatable': True, + 'date_format': '%d.%m.%Y', + 'decimal_point': ',', + 'thousand_sep': ' ', + }) + Translations = self.registry('ir.translation') + for source, value in self.translations_fr: + Translations.create(self.cr, openerp.SUPERUSER_ID, { + 'name': 'export.selection,value', + 'lang': 'fr_FR', + 'type': 'selection', + 'src': source, + 'value': value + }) + # FIXME: can't import an exported selection field label if lang != en_US + self.assertEqual( + self.export(2, context={'lang': 'fr_FR'}), + [[u'Bar']]) class test_selection_function(CreatorCase): model_name = 'export.selection.function' @@ -390,7 +416,6 @@ class test_o2m(CreatorCase): [u'record5', '', u'13'], ]) -# todo: test with multiple o2m fields and exporting all class test_o2m_multiple(CreatorCase): model_name = 'export.one2many.multiple' diff --git a/openerp/tests/test_import.py b/openerp/tests/test_import.py index b275db520c8..7422ffd1a7a 100644 --- a/openerp/tests/test_import.py +++ b/openerp/tests/test_import.py @@ -372,6 +372,11 @@ class test_text(ImporterCase): class test_selection(ImporterCase): model_name = 'export.selection' + translations_fr = [ + ("Qux", "toto"), + ("Bar", "titi"), + ("Foo", "tete"), + ] def test_imported(self): self.assertEqual( @@ -384,6 +389,38 @@ class test_selection(ImporterCase): ok(4)) self.assertEqual([3, 2, 1, 2], values(self.read())) + def test_imported_translated(self): + self.registry('res.lang').create(self.cr, openerp.SUPERUSER_ID, { + 'name': u'Français', + 'code': 'fr_FR', + 'translatable': True, + 'date_format': '%d.%m.%Y', + 'decimal_point': ',', + 'thousand_sep': ' ', + }) + Translations = self.registry('ir.translation') + for source, value in self.translations_fr: + Translations.create(self.cr, openerp.SUPERUSER_ID, { + 'name': 'export.selection,value', + 'lang': 'fr_FR', + 'type': 'selection', + 'src': source, + 'value': value + }) + + self.assertEqual( + self.import_(['value'], [ + ['toto'], + ['tete'], + ['titi'], + ], context={'lang': 'fr_FR'}), + ok(3)) + self.assertEqual([3, 1, 2], values(self.read())) + self.assertEqual( + self.import_(['value'], [['Foo']], context={'lang': 'fr_FR'}), + error(1, "Key/value 'Foo' not found in selection field 'value'", + value=False)) + def test_invalid(self): self.assertEqual( self.import_(['value'], [['Baz']]), @@ -398,6 +435,12 @@ class test_selection(ImporterCase): class test_selection_function(ImporterCase): model_name = 'export.selection.function' + translations_fr = [ + ("Corge", "toto"), + ("Grault", "titi"), + ("Whee", "tete"), + ("Moog", "tutu"), + ] def test_imported(self): """ By what bloody magic does that thing work? @@ -407,7 +450,6 @@ class test_selection_function(ImporterCase): it: import does not actually know that the selection field uses a function """ - # TODO: localized import self.assertEqual( self.import_(['value'], [ [3], @@ -418,6 +460,36 @@ class test_selection_function(ImporterCase): ['3', '1'], values(self.read())) + def test_translated(self): + self.registry('res.lang').create(self.cr, openerp.SUPERUSER_ID, { + 'name': u'Français', + 'code': 'fr_FR', + 'translatable': True, + 'date_format': '%d.%m.%Y', + 'decimal_point': ',', + 'thousand_sep': ' ', + }) + Translations = self.registry('ir.translation') + for source, value in self.translations_fr: + Translations.create(self.cr, openerp.SUPERUSER_ID, { + 'name': 'export.selection,value', + 'lang': 'fr_FR', + 'type': 'selection', + 'src': source, + 'value': value + }) + # FIXME: Fucking hell + self.assertEqual( + self.import_(['value'], [ + ['toto'], + ['tete'], + ], context={'lang': 'fr_FR'}), + error(1, "Key/value 'toto' not found in selection field 'value'", + value=False)) + self.assertEqual( + self.import_(['value'], [['Wheee']], context={'lang': 'fr_FR'}), + ok(1)) + class test_m2o(ImporterCase): model_name = 'export.many2one' From e1c94f32212ed3d74b3aa84cc805fe614562e17a Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 31 Jul 2012 17:27:47 +0200 Subject: [PATCH 21/85] [TEST] importing m2m fields to an existing object with m2m values bzr revid: xmo@openerp.com-20120731152747-beg72fx2150upvay --- openerp/tests/test_import.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/openerp/tests/test_import.py b/openerp/tests/test_import.py index 7422ffd1a7a..c2534caa929 100644 --- a/openerp/tests/test_import.py +++ b/openerp/tests/test_import.py @@ -695,7 +695,28 @@ class test_m2m(ImporterCase): ValueError, self.import_, ['value'], [['wherethem2mhavenonames']]) -# TODO: O2M + def test_import_to_existing(self): + M2O_o = self.registry('export.many2many.other') + id1 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 3, 'str': 'record0'}) + id2 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 44, 'str': 'record1'}) + id3 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 84, 'str': 'record2'}) + id4 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 9, 'str': 'record3'}) + + xid = 'myxid' + self.assertEqual( + self.import_(['id', 'value/.id'], [[xid, '%d,%d' % (id1, id2)]]), + ok(1)) + self.assertEqual( + self.import_(['id', 'value/.id'], [[xid, '%d,%d' % (id3, id4)]]), + ok(1)) + + b = self.browse() + self.assertEqual(len(b), 1) + # TODO: replacement of existing m2m values is correct? + self.assertEqual(values(b[0].value), [84, 9]) + +class test_o2m(ImporterCase): + model_name = 'export.one2many' # function, related, reference: written to db as-is... # => function uses @type for value coercion/conversion From c4a60df691a0822258489105b71018ff6b3d73c6 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 31 Jul 2012 18:36:31 +0200 Subject: [PATCH 22/85] [TEST] o2m db-id export and import bzr revid: xmo@openerp.com-20120731163631-n97nxph9gm2f6kdp --- openerp/tests/test_export.py | 17 +++++- openerp/tests/test_import.py | 113 ++++++++++++++++++++++++++++++++++- 2 files changed, 127 insertions(+), 3 deletions(-) diff --git a/openerp/tests/test_export.py b/openerp/tests/test_export.py index d60e5a09dcb..ed47b580365 100644 --- a/openerp/tests/test_export.py +++ b/openerp/tests/test_export.py @@ -376,6 +376,21 @@ class test_o2m(CreatorCase): u'4', u','.join(self.names) ]]) + def test_multiple_records_id(self): + export = self.export(self.commands, fields=['const', 'value/.id']) + O2M_c = self.registry('export.one2many.child') + ids = O2M_c.browse(self.cr, openerp.SUPERUSER_ID, + O2M_c.search(self.cr, openerp.SUPERUSER_ID, [])) + self.assertEqual( + export, + [ + ['4', str(ids[0].id)], + ['', str(ids[1].id)], + ['', str(ids[2].id)], + ['', str(ids[3].id)], + ['', str(ids[4].id)], + ]) + def test_multiple_records_with_name_before(self): self.assertEqual( self.export(self.commands, fields=['const', 'value', 'value/value']), @@ -383,7 +398,7 @@ class test_o2m(CreatorCase): u'4', u','.join(self.names), u'4' ]]) - def test_multiple_records_with_name_before(self): + def test_multiple_records_with_name_after(self): self.assertEqual( self.export(self.commands, fields=['const', 'value/value', 'value']), [ # completely ignores name_get request diff --git a/openerp/tests/test_import.py b/openerp/tests/test_import.py index c2534caa929..c23d74d080a 100644 --- a/openerp/tests/test_import.py +++ b/openerp/tests/test_import.py @@ -23,8 +23,8 @@ def error(row, message, record=None, **kwargs): "Line %d : %s" % (row, message), '') -def values(seq): - return [item['value'] for item in seq] +def values(seq, field='value'): + return [item[field] for item in seq] def setupModule(): openerp.tools.config['update'] = {'base': 1} @@ -718,5 +718,114 @@ class test_m2m(ImporterCase): class test_o2m(ImporterCase): model_name = 'export.one2many' + def test_single(self): + self.assertEqual( + self.import_(['const', 'value/value'], [ + ['5', '63'] + ]), + ok(1)) + + (b,) = self.browse() + self.assertEqual(b.const, 5) + self.assertEqual(values(b.value), [63]) + + def test_multicore(self): + self.assertEqual( + self.import_(['const', 'value/value'], [ + ['5', '63'], + ['6', '64'], + ]), + ok(2)) + + b1, b2 = self.browse() + self.assertEqual(b1.const, 5) + self.assertEqual(values(b1.value), [63]) + self.assertEqual(b2.const, 6) + self.assertEqual(values(b2.value), [64]) + + def test_multisub(self): + self.assertEqual( + self.import_(['const', 'value/value'], [ + ['5', '63'], + ['', '64'], + ['', '65'], + ['', '66'], + ]), + ok(4)) + + (b,) = self.browse() + self.assertEqual(values(b.value), [63, 64, 65, 66]) + + def test_multi_subfields(self): + self.assertEqual( + self.import_(['value/str', 'const', 'value/value'], [ + ['this', '5', '63'], + ['is', '', '64'], + ['the', '', '65'], + ['rhythm', '', '66'], + ]), + ok(4)) + + (b,) = self.browse() + self.assertEqual(values(b.value), [63, 64, 65, 66]) + self.assertEqual( + values(b.value, 'str'), + 'this is the rhythm'.split()) + + def test_link(self): + id1 = self.registry('export.one2many.child').create(self.cr, openerp.SUPERUSER_ID, { + 'str': 'Bf', 'value': 109 + }) + id2 = self.registry('export.one2many.child').create(self.cr, openerp.SUPERUSER_ID, { + 'str': 'Me', 'value': 262 + }) + + self.assertEqual( + self.import_(['const', 'value/.id'], [ + ['42', str(id1)], + ['', str(id2)], + ]), + ok(2)) + + # No record values alongside id => o2m resolution skipped altogether, + # creates 2 records + b, b1 = self.browse() + self.assertEqual(b.const, 42) + self.assertEqual(values(b.value), []) + self.assertEqual(b1.const, 4) + self.assertEqual(values(b1.value), []) + + def test_link_2(self): + O2M_c = self.registry('export.one2many.child') + id1 = O2M_c.create(self.cr, openerp.SUPERUSER_ID, { + 'str': 'Bf', 'value': 109 + }) + id2 = O2M_c.create(self.cr, openerp.SUPERUSER_ID, { + 'str': 'Me', 'value': 262 + }) + + self.assertEqual( + self.import_(['const', 'value/.id', 'value/value'], [ + ['42', str(id1), '1'], + ['', str(id2), '2'], + ]), + ok(2)) + + (b,) = self.browse() + # if an id (db or xid) is provided, expectations that objects are + # *already* linked and emits UPDATE (1, id, {}). + # Noid => CREATE (0, ?, {}) + # TODO: xid ignored aside from getting corresponding db id? + self.assertEqual(b.const, 42) + self.assertEqual(values(b.value), []) + + # FIXME: updates somebody else's records? + self.assertEqual( + O2M_c.read(self.cr, openerp.SUPERUSER_ID, id1), + {'id': id1, 'str': 'Bf', 'value': 1, 'parent_id': False}) + self.assertEqual( + O2M_c.read(self.cr, openerp.SUPERUSER_ID, id2), + {'id': id2, 'str': 'Me', 'value': 2, 'parent_id': False}) + # function, related, reference: written to db as-is... # => function uses @type for value coercion/conversion From 3f1ab77e2858659388426b5148b8c91c71c6bc3a Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 1 Aug 2012 09:08:03 +0200 Subject: [PATCH 23/85] [TEST] o2m importing of multiple fields bzr revid: xmo@openerp.com-20120801070803-4sgvemuiifrhe61s --- openerp/tests/test_import.py | 66 +++++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/openerp/tests/test_import.py b/openerp/tests/test_import.py index c23d74d080a..08c52dd902c 100644 --- a/openerp/tests/test_import.py +++ b/openerp/tests/test_import.py @@ -772,6 +772,8 @@ class test_o2m(ImporterCase): values(b.value, 'str'), 'this is the rhythm'.split()) + # TODO: failing inline LINK_TO + def test_link(self): id1 = self.registry('export.one2many.child').create(self.cr, openerp.SUPERUSER_ID, { 'str': 'Bf', 'value': 109 @@ -788,7 +790,8 @@ class test_o2m(ImporterCase): ok(2)) # No record values alongside id => o2m resolution skipped altogether, - # creates 2 records + # creates 2 records => remove/don't import columns sideshow columns, + # get completely different semantics b, b1 = self.browse() self.assertEqual(b.const, 42) self.assertEqual(values(b.value), []) @@ -827,5 +830,66 @@ class test_o2m(ImporterCase): O2M_c.read(self.cr, openerp.SUPERUSER_ID, id2), {'id': id2, 'str': 'Me', 'value': 2, 'parent_id': False}) + # TODO: name_get? + +class test_o2m_multiple(ImporterCase): + model_name = 'export.one2many.multiple' + + def test_multi_mixed(self): + self.assertEqual( + self.import_(['const', 'child1/value', 'child2/value'], [ + ['5', '11', '21'], + ['', '12', '22'], + ['', '13', '23'], + ['', '14', ''], + ]), + ok(4)) + # Oh yeah, that's the stuff + (b, b1, b2) = self.browse() + self.assertEqual(values(b.child1), [11]) + self.assertEqual(values(b.child2), [21]) + + self.assertEqual(values(b1.child1), [12]) + self.assertEqual(values(b1.child2), [22]) + + self.assertEqual(values(b2.child1), [13, 14]) + self.assertEqual(values(b2.child2), [23]) + + def test_multi(self): + self.assertEqual( + self.import_(['const', 'child1/value', 'child2/value'], [ + ['5', '11', '21'], + ['', '12', ''], + ['', '13', ''], + ['', '14', ''], + ['', '', '22'], + ['', '', '23'], + ]), + ok(6)) + # What the actual fuck? + (b, b1) = self.browse() + self.assertEqual(values(b.child1), [11, 12, 13, 14]) + self.assertEqual(values(b.child2), [21]) + self.assertEqual(values(b1.child2), [22, 23]) + + def test_multi_fullsplit(self): + self.assertEqual( + self.import_(['const', 'child1/value', 'child2/value'], [ + ['5', '11', ''], + ['', '12', ''], + ['', '13', ''], + ['', '14', ''], + ['', '', '21'], + ['', '', '22'], + ['', '', '23'], + ]), + ok(7)) + # oh wow + (b, b1) = self.browse() + self.assertEqual(b.const, 5) + self.assertEqual(values(b.child1), [11, 12, 13, 14]) + self.assertEqual(b1.const, 36) + self.assertEqual(values(b1.child2), [21, 22, 23]) + # function, related, reference: written to db as-is... # => function uses @type for value coercion/conversion From e3dc73880280fca68d43eb4352c8d56c3e47debc Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 1 Aug 2012 10:21:08 +0200 Subject: [PATCH 24/85] [TEST] failure of o2m relinking via inline id (as exported) bzr revid: xmo@openerp.com-20120801082108-vkind3ocwsqgd0bv --- openerp/tests/test_import.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/openerp/tests/test_import.py b/openerp/tests/test_import.py index 08c52dd902c..87e7ae9c350 100644 --- a/openerp/tests/test_import.py +++ b/openerp/tests/test_import.py @@ -772,7 +772,24 @@ class test_o2m(ImporterCase): values(b.value, 'str'), 'this is the rhythm'.split()) - # TODO: failing inline LINK_TO + def test_link_inline(self): + id1 = self.registry('export.one2many.child').create(self.cr, openerp.SUPERUSER_ID, { + 'str': 'Bf', 'value': 109 + }) + id2 = self.registry('export.one2many.child').create(self.cr, openerp.SUPERUSER_ID, { + 'str': 'Me', 'value': 262 + }) + + try: + self.import_(['const', 'value/.id'], [ + ['42', '%d,%d' % (id1, id2)] + ]) + except ValueError, e: + # should be Exception(Database ID doesn't exist: export.one2many.child : $id1,$id2) + self.assertIs(type(e), ValueError) + self.assertEqual( + e.args[0], + "invalid literal for int() with base 10: '%d,%d'" % (id1, id2)) def test_link(self): id1 = self.registry('export.one2many.child').create(self.cr, openerp.SUPERUSER_ID, { @@ -830,8 +847,6 @@ class test_o2m(ImporterCase): O2M_c.read(self.cr, openerp.SUPERUSER_ID, id2), {'id': id2, 'str': 'Me', 'value': 2, 'parent_id': False}) - # TODO: name_get? - class test_o2m_multiple(ImporterCase): model_name = 'export.one2many.multiple' From 471ef96edf890014df766a0ee956fe190f4e5480 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 1 Aug 2012 11:44:34 +0200 Subject: [PATCH 25/85] [FIX] some notes and comments in import/export tests bzr revid: xmo@openerp.com-20120801094434-t23wa2343utnevh6 --- openerp/tests/test_export.py | 5 ++--- openerp/tests/test_import.py | 21 +++++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/openerp/tests/test_export.py b/openerp/tests/test_export.py index ed47b580365..e545fe24c61 100644 --- a/openerp/tests/test_export.py +++ b/openerp/tests/test_export.py @@ -218,6 +218,7 @@ class test_datetime(CreatorCase): .. note:: on the other hand, export uses user lang for name_get """ + # NOTE: ignores user timezone, always exports to UTC self.assertEqual( self.export('2011-11-07 21:05:48', context={'tz': 'Pacific/Norfolk'}), [[u'2011-11-07 21:05:48']]) @@ -260,7 +261,6 @@ class test_selection(CreatorCase): 'src': source, 'value': value }) - # FIXME: can't import an exported selection field label if lang != en_US self.assertEqual( self.export(2, context={'lang': 'fr_FR'}), [[u'Bar']]) @@ -274,8 +274,7 @@ class test_selection_function(CreatorCase): [[False]]) def test_value(self): - """ selection functions export the *value* itself - """ + # FIXME: selection functions export the *value* itself self.assertEqual( self.export(1), [[u'1']]) diff --git a/openerp/tests/test_import.py b/openerp/tests/test_import.py index 87e7ae9c350..de2553427e0 100644 --- a/openerp/tests/test_import.py +++ b/openerp/tests/test_import.py @@ -240,7 +240,7 @@ class test_integer_field(ImporterCase): def test_nonsense(self): - # dafuq? why does that one raise an error? + # FIXME: shit error reporting, exceptions half the time, messages the other half self.assertRaises( ValueError, self.import_, ['value'], [['zorglub']]) @@ -384,7 +384,7 @@ class test_selection(ImporterCase): ['Qux'], ['Bar'], ['Foo'], - [2], + ['2'], ]), ok(4)) self.assertEqual([3, 2, 1, 2], values(self.read())) @@ -408,6 +408,8 @@ class test_selection(ImporterCase): 'value': value }) + # FIXME: can't import an exported selection field label if lang != en_US + # (see test_export.test_selection.test_localized_export) self.assertEqual( self.import_(['value'], [ ['toto'], @@ -443,16 +445,14 @@ class test_selection_function(ImporterCase): ] def test_imported(self): - """ By what bloody magic does that thing work? - - => import uses fields_get, so translates import label (may or may not - be good news) *and* serializes the selection function to reverse - it: import does not actually know that the selection field uses a - function + """ import uses fields_get, so translates import label (may or may not + be good news) *and* serializes the selection function to reverse it: + import does not actually know that the selection field uses a function """ + # NOTE: conflict between a value and a label => ? self.assertEqual( self.import_(['value'], [ - [3], + ['3'], ["Grault"], ]), ok(2)) @@ -461,6 +461,8 @@ class test_selection_function(ImporterCase): values(self.read())) def test_translated(self): + """ Expects output of selection function returns translated labels + """ self.registry('res.lang').create(self.cr, openerp.SUPERUSER_ID, { 'name': u'Français', 'code': 'fr_FR', @@ -478,7 +480,6 @@ class test_selection_function(ImporterCase): 'src': source, 'value': value }) - # FIXME: Fucking hell self.assertEqual( self.import_(['value'], [ ['toto'], From 788b1bdb5b2809c44fb814b4ebf67bd23efa6f32 Mon Sep 17 00:00:00 2001 From: Antony Lesuisse Date: Fri, 3 Aug 2012 17:46:41 +0200 Subject: [PATCH 26/85] [REM] correctly rename and remove wiki bzr revid: al@openerp.com-20120803154641-ocbydlqt7ppjecwn --- addons/{wiki => document_page}/__init__.py | 2 +- addons/{wiki => document_page}/__openerp__.py | 38 +- .../data/wiki_main.xml | 0 .../data/wiki_quickstart.xml | 0 .../document_page.py} | 151 +++--- addons/document_page/document_page_data.xml | 41 ++ addons/document_page/document_page_demo.xml | 105 ++++ .../document_page/document_page_sequence.xml | 16 + .../document_page_view.xml} | 228 ++++----- addons/document_page/i18n/ar.po | 398 +++++++++++++++ addons/{wiki => document_page}/i18n/bg.po | 4 +- addons/{wiki => document_page}/i18n/bs.po | 4 +- addons/{wiki => document_page}/i18n/ca.po | 4 +- addons/{wiki => document_page}/i18n/cs.po | 4 +- addons/{wiki => document_page}/i18n/da.po | 4 +- addons/{wiki => document_page}/i18n/de.po | 4 +- addons/document_page/i18n/document_page.pot | 423 ++++++++++++++++ addons/{wiki => document_page}/i18n/el.po | 4 +- addons/{wiki => document_page}/i18n/es.po | 4 +- addons/{wiki => document_page}/i18n/es_AR.po | 4 +- addons/{wiki => document_page}/i18n/es_CR.po | 6 +- addons/{wiki => document_page}/i18n/es_MX.po | 0 addons/{wiki => document_page}/i18n/es_VE.po | 0 addons/{wiki => document_page}/i18n/et.po | 4 +- addons/{wiki => document_page}/i18n/fi.po | 4 +- addons/{wiki => document_page}/i18n/fr.po | 6 +- addons/{wiki => document_page}/i18n/gl.po | 4 +- addons/{wiki => document_page}/i18n/hr.po | 4 +- addons/{wiki => document_page}/i18n/hu.po | 4 +- addons/{wiki => document_page}/i18n/id.po | 4 +- addons/{wiki => document_page}/i18n/it.po | 10 +- addons/{wiki => document_page}/i18n/ja.po | 4 +- addons/{wiki => document_page}/i18n/ko.po | 4 +- addons/{wiki => document_page}/i18n/lt.po | 4 +- addons/{wiki => document_page}/i18n/lv.po | 4 +- addons/{wiki => document_page}/i18n/mn.po | 4 +- addons/{wiki => document_page}/i18n/nb.po | 6 +- addons/{wiki => document_page}/i18n/nl.po | 4 +- addons/{wiki => document_page}/i18n/nl_BE.po | 4 +- addons/{wiki => document_page}/i18n/pl.po | 54 +-- addons/{wiki => document_page}/i18n/pt.po | 48 +- addons/{wiki => document_page}/i18n/pt_BR.po | 4 +- addons/{wiki => document_page}/i18n/ro.po | 112 ++--- addons/{wiki => document_page}/i18n/ru.po | 4 +- addons/{wiki => document_page}/i18n/sk.po | 4 +- addons/{wiki => document_page}/i18n/sl.po | 4 +- addons/{wiki => document_page}/i18n/sq.po | 4 +- addons/{wiki => document_page}/i18n/sr.po | 4 +- .../{wiki => document_page}/i18n/sr@latin.po | 4 +- addons/{wiki => document_page}/i18n/sv.po | 72 ++- addons/{wiki => document_page}/i18n/tlh.po | 4 +- addons/{wiki => document_page}/i18n/tr.po | 4 +- addons/{wiki => document_page}/i18n/uk.po | 4 +- addons/{wiki => document_page}/i18n/vi.po | 4 +- addons/{wiki => document_page}/i18n/zh_CN.po | 4 +- addons/{wiki => document_page}/i18n/zh_TW.po | 4 +- .../security/document_page_security.xml} | 0 .../security/ir.model.access.csv | 8 + .../document_page/security/wiki_security.xml | 12 + .../static/src/css/document_page.css | 5 + .../static/src/js/document_page.js | 18 + .../static/src/lib/wiky/Readme.md | 0 .../static/src/lib/wiky/autogit.sh | 0 .../static/src/lib/wiky/index.html | 0 .../static/src/lib/wiky/input_complete | 0 .../static/src/lib/wiky/jquery-1.4.2.min.js | 0 .../static/src/lib/wiky/wiky.css | 0 .../static/src/lib/wiky/wiky.js | 0 .../test/document_page_test00.yml | 71 +++ .../{wiki => document_page}/web/__init__.py | 0 .../web/controllers/__init__.py | 0 .../web/controllers/wiki.py | 0 .../{wiki => document_page}/web/locales/bg.po | 0 .../{wiki => document_page}/web/locales/ca.po | 0 .../{wiki => document_page}/web/locales/da.po | 0 .../{wiki => document_page}/web/locales/de.po | 0 .../locales/de_DE/LC_MESSAGES/javascript.po | 0 .../web/locales/de_DE/LC_MESSAGES/messages.po | 0 .../web/locales/es_CR.po | 0 .../locales/es_ES/LC_MESSAGES/javascript.po | 0 .../web/locales/es_ES/LC_MESSAGES/messages.po | 0 .../web/locales/es_PY.po | 0 .../{wiki => document_page}/web/locales/fr.po | 0 .../locales/fr_FR/LC_MESSAGES/javascript.po | 0 .../web/locales/fr_FR/LC_MESSAGES/messages.po | 0 .../{wiki => document_page}/web/locales/gl.po | 0 .../web/locales/javascript.pot | 0 .../web/locales/messages.pot | 0 .../{wiki => document_page}/web/locales/ru.po | 0 .../{wiki => document_page}/web/locales/sk.po | 0 .../{wiki => document_page}/web/locales/tr.po | 0 .../web/static/css/wiki.css | 0 .../web/widgets/__init__.py | 0 .../web/widgets/rss/__init__.py | 0 .../web/widgets/rss/feedparser.py | 0 .../web/widgets/templates/wiki.mako | 0 .../web/widgets/wiki.py | 0 .../web/widgets/wikimarkup/__init__.py | 0 .../wizard/__init__.py | 8 +- .../wizard/document_page_create_menu.py} | 55 ++- .../document_page_create_menu_view.xml} | 8 +- .../wizard/document_page_make_index.py | 83 ++++ .../wizard/document_page_make_index_view.xml} | 11 +- .../wizard/document_page_open.py} | 19 +- .../wizard/document_page_open_view.xml} | 8 +- .../wizard/document_page_show_diff.py} | 6 +- .../wizard/document_page_show_diff_view.xml} | 6 +- addons/wiki/i18n/ar.po | 435 ----------------- addons/wiki/i18n/wiki.pot | 454 ------------------ addons/wiki/security/ir.model.access.csv | 8 - addons/wiki/static/src/js/wiki.js | 13 - addons/wiki/test/wiki_test00.yml | 81 ---- addons/wiki/wiki_demo.xml | 8 - addons/wiki/wizard/wiki_make_index.py | 100 ---- addons/wiki_faq/__init__.py | 22 - addons/wiki_faq/__openerp__.py | 43 -- addons/wiki_faq/i18n/ar.po | 32 -- addons/wiki_faq/i18n/bg.po | 30 -- addons/wiki_faq/i18n/ca.po | 33 -- addons/wiki_faq/i18n/cs.po | 32 -- addons/wiki_faq/i18n/da.po | 32 -- addons/wiki_faq/i18n/de.po | 32 -- addons/wiki_faq/i18n/el.po | 32 -- addons/wiki_faq/i18n/es.po | 32 -- addons/wiki_faq/i18n/es_CR.po | 34 -- addons/wiki_faq/i18n/es_MX.po | 32 -- addons/wiki_faq/i18n/es_VE.po | 32 -- addons/wiki_faq/i18n/fi.po | 32 -- addons/wiki_faq/i18n/fr.po | 31 -- addons/wiki_faq/i18n/gl.po | 32 -- addons/wiki_faq/i18n/hr.po | 32 -- addons/wiki_faq/i18n/hu.po | 32 -- addons/wiki_faq/i18n/it.po | 32 -- addons/wiki_faq/i18n/ja.po | 32 -- addons/wiki_faq/i18n/lv.po | 32 -- addons/wiki_faq/i18n/mn.po | 32 -- addons/wiki_faq/i18n/nl.po | 32 -- addons/wiki_faq/i18n/oc.po | 32 -- addons/wiki_faq/i18n/pt.po | 32 -- addons/wiki_faq/i18n/pt_BR.po | 32 -- addons/wiki_faq/i18n/ro.po | 32 -- addons/wiki_faq/i18n/ru.po | 32 -- addons/wiki_faq/i18n/sk.po | 32 -- addons/wiki_faq/i18n/sl.po | 32 -- addons/wiki_faq/i18n/sr@latin.po | 32 -- addons/wiki_faq/i18n/sv.po | 32 -- addons/wiki_faq/i18n/tr.po | 32 -- addons/wiki_faq/i18n/wiki_faq.pot | 28 -- addons/wiki_faq/i18n/zh_CN.po | 32 -- addons/wiki_faq/wiki_faq.xml | 95 ---- addons/wiki_quality_manual/__init__.py | 22 - addons/wiki_quality_manual/__openerp__.py | 44 -- addons/wiki_quality_manual/i18n/ar.po | 32 -- addons/wiki_quality_manual/i18n/bg.po | 32 -- addons/wiki_quality_manual/i18n/ca.po | 32 -- addons/wiki_quality_manual/i18n/cs.po | 32 -- addons/wiki_quality_manual/i18n/da.po | 32 -- addons/wiki_quality_manual/i18n/de.po | 32 -- addons/wiki_quality_manual/i18n/es.po | 32 -- addons/wiki_quality_manual/i18n/es_CR.po | 34 -- addons/wiki_quality_manual/i18n/es_MX.po | 32 -- addons/wiki_quality_manual/i18n/es_VE.po | 32 -- addons/wiki_quality_manual/i18n/fi.po | 32 -- addons/wiki_quality_manual/i18n/fr.po | 31 -- addons/wiki_quality_manual/i18n/gl.po | 32 -- addons/wiki_quality_manual/i18n/hr.po | 32 -- addons/wiki_quality_manual/i18n/hu.po | 32 -- addons/wiki_quality_manual/i18n/it.po | 34 -- addons/wiki_quality_manual/i18n/ja.po | 32 -- addons/wiki_quality_manual/i18n/lv.po | 32 -- addons/wiki_quality_manual/i18n/mn.po | 32 -- addons/wiki_quality_manual/i18n/nl.po | 32 -- addons/wiki_quality_manual/i18n/oc.po | 32 -- addons/wiki_quality_manual/i18n/pt.po | 32 -- addons/wiki_quality_manual/i18n/pt_BR.po | 32 -- addons/wiki_quality_manual/i18n/ro.po | 32 -- addons/wiki_quality_manual/i18n/ru.po | 32 -- addons/wiki_quality_manual/i18n/sk.po | 32 -- addons/wiki_quality_manual/i18n/sl.po | 32 -- addons/wiki_quality_manual/i18n/sr@latin.po | 32 -- addons/wiki_quality_manual/i18n/sv.po | 32 -- addons/wiki_quality_manual/i18n/tr.po | 32 -- .../i18n/wiki_quality_manual.pot | 28 -- addons/wiki_quality_manual/i18n/zh_CN.po | 32 -- .../wiki_quality_manual.xml | 167 ------- addons/wiki_sale_faq/__init__.py | 22 - addons/wiki_sale_faq/__openerp__.py | 49 -- addons/wiki_sale_faq/i18n/ar.po | 63 --- addons/wiki_sale_faq/i18n/bg.po | 78 --- addons/wiki_sale_faq/i18n/ca.po | 78 --- addons/wiki_sale_faq/i18n/da.po | 63 --- addons/wiki_sale_faq/i18n/de.po | 75 --- addons/wiki_sale_faq/i18n/es.po | 79 --- addons/wiki_sale_faq/i18n/es_CR.po | 81 ---- addons/wiki_sale_faq/i18n/es_MX.po | 83 ---- addons/wiki_sale_faq/i18n/es_VE.po | 83 ---- addons/wiki_sale_faq/i18n/fi.po | 66 --- addons/wiki_sale_faq/i18n/fr.po | 78 --- addons/wiki_sale_faq/i18n/gl.po | 77 --- addons/wiki_sale_faq/i18n/hr.po | 77 --- addons/wiki_sale_faq/i18n/hu.po | 66 --- addons/wiki_sale_faq/i18n/it.po | 78 --- addons/wiki_sale_faq/i18n/ja.po | 62 --- addons/wiki_sale_faq/i18n/lv.po | 77 --- addons/wiki_sale_faq/i18n/mn.po | 66 --- addons/wiki_sale_faq/i18n/nl.po | 77 --- addons/wiki_sale_faq/i18n/oc.po | 59 --- addons/wiki_sale_faq/i18n/pl.po | 78 --- addons/wiki_sale_faq/i18n/pt.po | 66 --- addons/wiki_sale_faq/i18n/pt_BR.po | 77 --- addons/wiki_sale_faq/i18n/ro.po | 78 --- addons/wiki_sale_faq/i18n/ru.po | 77 --- addons/wiki_sale_faq/i18n/sk.po | 77 --- addons/wiki_sale_faq/i18n/sl.po | 77 --- addons/wiki_sale_faq/i18n/sr@latin.po | 77 --- addons/wiki_sale_faq/i18n/sv.po | 77 --- addons/wiki_sale_faq/i18n/tr.po | 77 --- addons/wiki_sale_faq/i18n/wiki_sale_faq.pot | 44 -- addons/wiki_sale_faq/i18n/zh_CN.po | 71 --- addons/wiki_sale_faq/wiki_sale_faq_data.xml | 29 -- addons/wiki_sale_faq/wiki_sale_faq_view.xml | 41 -- 221 files changed, 1703 insertions(+), 6521 deletions(-) rename addons/{wiki => document_page}/__init__.py (98%) rename addons/{wiki => document_page}/__openerp__.py (58%) rename addons/{wiki => document_page}/data/wiki_main.xml (100%) rename addons/{wiki => document_page}/data/wiki_quickstart.xml (100%) rename addons/{wiki/wiki.py => document_page/document_page.py} (60%) create mode 100644 addons/document_page/document_page_data.xml create mode 100644 addons/document_page/document_page_demo.xml create mode 100644 addons/document_page/document_page_sequence.xml rename addons/{wiki/wiki_view.xml => document_page/document_page_view.xml} (50%) create mode 100644 addons/document_page/i18n/ar.po rename addons/{wiki => document_page}/i18n/bg.po (99%) rename addons/{wiki => document_page}/i18n/bs.po (98%) rename addons/{wiki => document_page}/i18n/ca.po (99%) rename addons/{wiki => document_page}/i18n/cs.po (99%) rename addons/{wiki => document_page}/i18n/da.po (99%) rename addons/{wiki => document_page}/i18n/de.po (99%) create mode 100644 addons/document_page/i18n/document_page.pot rename addons/{wiki => document_page}/i18n/el.po (99%) rename addons/{wiki => document_page}/i18n/es.po (99%) rename addons/{wiki => document_page}/i18n/es_AR.po (99%) rename addons/{wiki => document_page}/i18n/es_CR.po (98%) rename addons/{wiki => document_page}/i18n/es_MX.po (100%) rename addons/{wiki => document_page}/i18n/es_VE.po (100%) rename addons/{wiki => document_page}/i18n/et.po (99%) rename addons/{wiki => document_page}/i18n/fi.po (99%) rename addons/{wiki => document_page}/i18n/fr.po (98%) rename addons/{wiki => document_page}/i18n/gl.po (99%) rename addons/{wiki => document_page}/i18n/hr.po (99%) rename addons/{wiki => document_page}/i18n/hu.po (99%) rename addons/{wiki => document_page}/i18n/id.po (98%) rename addons/{wiki => document_page}/i18n/it.po (97%) rename addons/{wiki => document_page}/i18n/ja.po (99%) rename addons/{wiki => document_page}/i18n/ko.po (99%) rename addons/{wiki => document_page}/i18n/lt.po (99%) rename addons/{wiki => document_page}/i18n/lv.po (99%) rename addons/{wiki => document_page}/i18n/mn.po (99%) rename addons/{wiki => document_page}/i18n/nb.po (98%) rename addons/{wiki => document_page}/i18n/nl.po (99%) rename addons/{wiki => document_page}/i18n/nl_BE.po (99%) rename addons/{wiki => document_page}/i18n/pl.po (92%) rename addons/{wiki => document_page}/i18n/pt.po (92%) rename addons/{wiki => document_page}/i18n/pt_BR.po (99%) rename addons/{wiki => document_page}/i18n/ro.po (81%) rename addons/{wiki => document_page}/i18n/ru.po (99%) rename addons/{wiki => document_page}/i18n/sk.po (99%) rename addons/{wiki => document_page}/i18n/sl.po (99%) rename addons/{wiki => document_page}/i18n/sq.po (99%) rename addons/{wiki => document_page}/i18n/sr.po (99%) rename addons/{wiki => document_page}/i18n/sr@latin.po (99%) rename addons/{wiki => document_page}/i18n/sv.po (88%) rename addons/{wiki => document_page}/i18n/tlh.po (98%) rename addons/{wiki => document_page}/i18n/tr.po (99%) rename addons/{wiki => document_page}/i18n/uk.po (99%) rename addons/{wiki => document_page}/i18n/vi.po (99%) rename addons/{wiki => document_page}/i18n/zh_CN.po (99%) rename addons/{wiki => document_page}/i18n/zh_TW.po (98%) rename addons/{wiki/security/wiki_security.xml => document_page/security/document_page_security.xml} (100%) create mode 100644 addons/document_page/security/ir.model.access.csv create mode 100644 addons/document_page/security/wiki_security.xml create mode 100644 addons/document_page/static/src/css/document_page.css create mode 100644 addons/document_page/static/src/js/document_page.js rename addons/{wiki => document_page}/static/src/lib/wiky/Readme.md (100%) rename addons/{wiki => document_page}/static/src/lib/wiky/autogit.sh (100%) rename addons/{wiki => document_page}/static/src/lib/wiky/index.html (100%) rename addons/{wiki => document_page}/static/src/lib/wiky/input_complete (100%) rename addons/{wiki => document_page}/static/src/lib/wiky/jquery-1.4.2.min.js (100%) rename addons/{wiki => document_page}/static/src/lib/wiky/wiky.css (100%) rename addons/{wiki => document_page}/static/src/lib/wiky/wiky.js (100%) create mode 100644 addons/document_page/test/document_page_test00.yml rename addons/{wiki => document_page}/web/__init__.py (100%) rename addons/{wiki => document_page}/web/controllers/__init__.py (100%) rename addons/{wiki => document_page}/web/controllers/wiki.py (100%) rename addons/{wiki => document_page}/web/locales/bg.po (100%) rename addons/{wiki => document_page}/web/locales/ca.po (100%) rename addons/{wiki => document_page}/web/locales/da.po (100%) rename addons/{wiki => document_page}/web/locales/de.po (100%) rename addons/{wiki => document_page}/web/locales/de_DE/LC_MESSAGES/javascript.po (100%) rename addons/{wiki => document_page}/web/locales/de_DE/LC_MESSAGES/messages.po (100%) rename addons/{wiki => document_page}/web/locales/es_CR.po (100%) rename addons/{wiki => document_page}/web/locales/es_ES/LC_MESSAGES/javascript.po (100%) rename addons/{wiki => document_page}/web/locales/es_ES/LC_MESSAGES/messages.po (100%) rename addons/{wiki => document_page}/web/locales/es_PY.po (100%) rename addons/{wiki => document_page}/web/locales/fr.po (100%) rename addons/{wiki => document_page}/web/locales/fr_FR/LC_MESSAGES/javascript.po (100%) rename addons/{wiki => document_page}/web/locales/fr_FR/LC_MESSAGES/messages.po (100%) rename addons/{wiki => document_page}/web/locales/gl.po (100%) rename addons/{wiki => document_page}/web/locales/javascript.pot (100%) rename addons/{wiki => document_page}/web/locales/messages.pot (100%) rename addons/{wiki => document_page}/web/locales/ru.po (100%) rename addons/{wiki => document_page}/web/locales/sk.po (100%) rename addons/{wiki => document_page}/web/locales/tr.po (100%) rename addons/{wiki => document_page}/web/static/css/wiki.css (100%) rename addons/{wiki => document_page}/web/widgets/__init__.py (100%) rename addons/{wiki => document_page}/web/widgets/rss/__init__.py (100%) rename addons/{wiki => document_page}/web/widgets/rss/feedparser.py (100%) rename addons/{wiki => document_page}/web/widgets/templates/wiki.mako (100%) rename addons/{wiki => document_page}/web/widgets/wiki.py (100%) rename addons/{wiki => document_page}/web/widgets/wikimarkup/__init__.py (100%) rename addons/{wiki => document_page}/wizard/__init__.py (89%) rename addons/{wiki/wizard/wiki_create_menu.py => document_page/wizard/document_page_create_menu.py} (63%) rename addons/{wiki/wizard/wiki_create_menu_view.xml => document_page/wizard/document_page_create_menu_view.xml} (78%) create mode 100644 addons/document_page/wizard/document_page_make_index.py rename addons/{wiki/wizard/wiki_make_index_view.xml => document_page/wizard/document_page_make_index_view.xml} (75%) rename addons/{wiki/wizard/wiki_wiki_page_open.py => document_page/wizard/document_page_open.py} (80%) rename addons/{wiki/wizard/wiki_wiki_page_open_view.xml => document_page/wizard/document_page_open_view.xml} (75%) rename addons/{wiki/wizard/wiki_show_diff.py => document_page/wizard/document_page_show_diff.py} (91%) rename addons/{wiki/wizard/wiki_show_diff_view.xml => document_page/wizard/document_page_show_diff_view.xml} (83%) delete mode 100644 addons/wiki/i18n/ar.po delete mode 100644 addons/wiki/i18n/wiki.pot delete mode 100644 addons/wiki/security/ir.model.access.csv delete mode 100644 addons/wiki/static/src/js/wiki.js delete mode 100644 addons/wiki/test/wiki_test00.yml delete mode 100644 addons/wiki/wiki_demo.xml delete mode 100644 addons/wiki/wizard/wiki_make_index.py delete mode 100644 addons/wiki_faq/__init__.py delete mode 100644 addons/wiki_faq/__openerp__.py delete mode 100644 addons/wiki_faq/i18n/ar.po delete mode 100644 addons/wiki_faq/i18n/bg.po delete mode 100644 addons/wiki_faq/i18n/ca.po delete mode 100644 addons/wiki_faq/i18n/cs.po delete mode 100644 addons/wiki_faq/i18n/da.po delete mode 100644 addons/wiki_faq/i18n/de.po delete mode 100644 addons/wiki_faq/i18n/el.po delete mode 100644 addons/wiki_faq/i18n/es.po delete mode 100644 addons/wiki_faq/i18n/es_CR.po delete mode 100644 addons/wiki_faq/i18n/es_MX.po delete mode 100644 addons/wiki_faq/i18n/es_VE.po delete mode 100644 addons/wiki_faq/i18n/fi.po delete mode 100644 addons/wiki_faq/i18n/fr.po delete mode 100644 addons/wiki_faq/i18n/gl.po delete mode 100644 addons/wiki_faq/i18n/hr.po delete mode 100644 addons/wiki_faq/i18n/hu.po delete mode 100644 addons/wiki_faq/i18n/it.po delete mode 100644 addons/wiki_faq/i18n/ja.po delete mode 100644 addons/wiki_faq/i18n/lv.po delete mode 100644 addons/wiki_faq/i18n/mn.po delete mode 100644 addons/wiki_faq/i18n/nl.po delete mode 100644 addons/wiki_faq/i18n/oc.po delete mode 100644 addons/wiki_faq/i18n/pt.po delete mode 100644 addons/wiki_faq/i18n/pt_BR.po delete mode 100644 addons/wiki_faq/i18n/ro.po delete mode 100644 addons/wiki_faq/i18n/ru.po delete mode 100644 addons/wiki_faq/i18n/sk.po delete mode 100644 addons/wiki_faq/i18n/sl.po delete mode 100644 addons/wiki_faq/i18n/sr@latin.po delete mode 100644 addons/wiki_faq/i18n/sv.po delete mode 100644 addons/wiki_faq/i18n/tr.po delete mode 100644 addons/wiki_faq/i18n/wiki_faq.pot delete mode 100644 addons/wiki_faq/i18n/zh_CN.po delete mode 100644 addons/wiki_faq/wiki_faq.xml delete mode 100644 addons/wiki_quality_manual/__init__.py delete mode 100644 addons/wiki_quality_manual/__openerp__.py delete mode 100644 addons/wiki_quality_manual/i18n/ar.po delete mode 100644 addons/wiki_quality_manual/i18n/bg.po delete mode 100644 addons/wiki_quality_manual/i18n/ca.po delete mode 100644 addons/wiki_quality_manual/i18n/cs.po delete mode 100644 addons/wiki_quality_manual/i18n/da.po delete mode 100644 addons/wiki_quality_manual/i18n/de.po delete mode 100644 addons/wiki_quality_manual/i18n/es.po delete mode 100644 addons/wiki_quality_manual/i18n/es_CR.po delete mode 100644 addons/wiki_quality_manual/i18n/es_MX.po delete mode 100644 addons/wiki_quality_manual/i18n/es_VE.po delete mode 100644 addons/wiki_quality_manual/i18n/fi.po delete mode 100644 addons/wiki_quality_manual/i18n/fr.po delete mode 100644 addons/wiki_quality_manual/i18n/gl.po delete mode 100644 addons/wiki_quality_manual/i18n/hr.po delete mode 100644 addons/wiki_quality_manual/i18n/hu.po delete mode 100644 addons/wiki_quality_manual/i18n/it.po delete mode 100644 addons/wiki_quality_manual/i18n/ja.po delete mode 100644 addons/wiki_quality_manual/i18n/lv.po delete mode 100644 addons/wiki_quality_manual/i18n/mn.po delete mode 100644 addons/wiki_quality_manual/i18n/nl.po delete mode 100644 addons/wiki_quality_manual/i18n/oc.po delete mode 100644 addons/wiki_quality_manual/i18n/pt.po delete mode 100644 addons/wiki_quality_manual/i18n/pt_BR.po delete mode 100644 addons/wiki_quality_manual/i18n/ro.po delete mode 100644 addons/wiki_quality_manual/i18n/ru.po delete mode 100644 addons/wiki_quality_manual/i18n/sk.po delete mode 100644 addons/wiki_quality_manual/i18n/sl.po delete mode 100644 addons/wiki_quality_manual/i18n/sr@latin.po delete mode 100644 addons/wiki_quality_manual/i18n/sv.po delete mode 100644 addons/wiki_quality_manual/i18n/tr.po delete mode 100644 addons/wiki_quality_manual/i18n/wiki_quality_manual.pot delete mode 100644 addons/wiki_quality_manual/i18n/zh_CN.po delete mode 100644 addons/wiki_quality_manual/wiki_quality_manual.xml delete mode 100644 addons/wiki_sale_faq/__init__.py delete mode 100644 addons/wiki_sale_faq/__openerp__.py delete mode 100644 addons/wiki_sale_faq/i18n/ar.po delete mode 100644 addons/wiki_sale_faq/i18n/bg.po delete mode 100644 addons/wiki_sale_faq/i18n/ca.po delete mode 100644 addons/wiki_sale_faq/i18n/da.po delete mode 100644 addons/wiki_sale_faq/i18n/de.po delete mode 100644 addons/wiki_sale_faq/i18n/es.po delete mode 100644 addons/wiki_sale_faq/i18n/es_CR.po delete mode 100644 addons/wiki_sale_faq/i18n/es_MX.po delete mode 100644 addons/wiki_sale_faq/i18n/es_VE.po delete mode 100644 addons/wiki_sale_faq/i18n/fi.po delete mode 100644 addons/wiki_sale_faq/i18n/fr.po delete mode 100644 addons/wiki_sale_faq/i18n/gl.po delete mode 100644 addons/wiki_sale_faq/i18n/hr.po delete mode 100644 addons/wiki_sale_faq/i18n/hu.po delete mode 100644 addons/wiki_sale_faq/i18n/it.po delete mode 100644 addons/wiki_sale_faq/i18n/ja.po delete mode 100644 addons/wiki_sale_faq/i18n/lv.po delete mode 100644 addons/wiki_sale_faq/i18n/mn.po delete mode 100644 addons/wiki_sale_faq/i18n/nl.po delete mode 100644 addons/wiki_sale_faq/i18n/oc.po delete mode 100644 addons/wiki_sale_faq/i18n/pl.po delete mode 100644 addons/wiki_sale_faq/i18n/pt.po delete mode 100644 addons/wiki_sale_faq/i18n/pt_BR.po delete mode 100644 addons/wiki_sale_faq/i18n/ro.po delete mode 100644 addons/wiki_sale_faq/i18n/ru.po delete mode 100644 addons/wiki_sale_faq/i18n/sk.po delete mode 100644 addons/wiki_sale_faq/i18n/sl.po delete mode 100644 addons/wiki_sale_faq/i18n/sr@latin.po delete mode 100644 addons/wiki_sale_faq/i18n/sv.po delete mode 100644 addons/wiki_sale_faq/i18n/tr.po delete mode 100644 addons/wiki_sale_faq/i18n/wiki_sale_faq.pot delete mode 100644 addons/wiki_sale_faq/i18n/zh_CN.po delete mode 100644 addons/wiki_sale_faq/wiki_sale_faq_data.xml delete mode 100644 addons/wiki_sale_faq/wiki_sale_faq_view.xml diff --git a/addons/wiki/__init__.py b/addons/document_page/__init__.py similarity index 98% rename from addons/wiki/__init__.py rename to addons/document_page/__init__.py index f52d0c2eb50..7ce5f4b6474 100644 --- a/addons/wiki/__init__.py +++ b/addons/document_page/__init__.py @@ -19,7 +19,7 @@ # ############################################################################## -import wiki +import document_page import wizard # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/wiki/__openerp__.py b/addons/document_page/__openerp__.py similarity index 58% rename from addons/wiki/__openerp__.py rename to addons/document_page/__openerp__.py index 4c413b6050b..f9cb3c58331 100644 --- a/addons/wiki/__openerp__.py +++ b/addons/document_page/__openerp__.py @@ -20,38 +20,44 @@ ############################################################################## { - 'name': 'Wiki', + 'name': 'Document Page', 'version': '1.0.1', 'category': 'Knowledge Management', 'description': """ -The base module to manage documents(wiki). +The base module to manage documents(document_page). ========================================== Keep track of Wiki groups, pages, and history. """, 'author': ['OpenERP SA', 'Axelor'], 'website': 'http://openerp.com', - 'depends': ['knowledge'], - 'web_depends': ['widget_wiki'], + 'depends': ['knowledge','document','crm'], + 'web_depends': ['widget_document_page'], 'init_xml': [], 'update_xml': [ - 'wizard/wiki_wiki_page_open_view.xml', - 'wizard/wiki_create_menu_view.xml', - 'wizard/wiki_make_index_view.xml', - 'wizard/wiki_show_diff_view.xml', - 'wiki_view.xml', - 'data/wiki_quickstart.xml', - 'data/wiki_main.xml', - 'security/wiki_security.xml', + 'data/document_page_sale_faq_data.xml', + 'wizard/document_page_page_open_view.xml', + 'wizard/document_page_create_menu_view.xml', + 'wizard/document_page_show_diff_view.xml', + 'wizard/document_page_make_index_view.xml', + 'document_page_view.xml', + 'document_page_sequence.xml', + 'document_page_sale_faq_view.xml', + 'data/document_page_quickstart.xml', + 'data/document_page_main.xml', + 'security/document_page_security.xml', 'security/ir.model.access.csv' ], - 'demo_xml': ['wiki_demo.xml'], - 'test': ['test/wiki_test00.yml'], + + 'demo_xml': ['document_page_demo.xml'], + 'test': ['test/document_page_test00.yml'], 'installable': True, 'auto_install': False, 'certificate': '0086363630317', 'web': True, - 'images': ['images/create_index.jpeg','images/page_history.jpeg','images/wiki_groups.jpeg','images/wiki_pages.jpeg'], - 'js': ['static/src/lib/wiky/wiky.js', 'static/src/js/wiki.js'], + 'images': ['images/create_index.jpeg','images/page_history.jpeg','images/document_page_groups.jpeg','images/document_page_pages.jpeg','document_page_type_internal_faq.jpeg','page_history.jpeg','sale_document.jpeg','wiki_pages.jpeg','wiki_pages_quality_manual.jpeg'], + 'js': ['static/src/lib/wiky/wiky.js', 'static/src/js/document_page.js'], + 'css' : [ + "static/src/css/document_page.css"], } # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/wiki/data/wiki_main.xml b/addons/document_page/data/wiki_main.xml similarity index 100% rename from addons/wiki/data/wiki_main.xml rename to addons/document_page/data/wiki_main.xml diff --git a/addons/wiki/data/wiki_quickstart.xml b/addons/document_page/data/wiki_quickstart.xml similarity index 100% rename from addons/wiki/data/wiki_quickstart.xml rename to addons/document_page/data/wiki_quickstart.xml diff --git a/addons/wiki/wiki.py b/addons/document_page/document_page.py similarity index 60% rename from addons/wiki/wiki.py rename to addons/document_page/document_page.py index fa1dca8e95a..5a0a6a261af 100644 --- a/addons/wiki/wiki.py +++ b/addons/document_page/document_page.py @@ -25,29 +25,28 @@ from tools.translate import _ import difflib import tools -class wiki_wiki(osv.osv): - """ wiki """ - _name = "wiki.wiki" +class document_page(osv.osv): + """ document.page """ + _name = "document.page" -wiki_wiki() +document_page() -class wiki_group(osv.osv): - """ Wiki Groups """ +class document_page_type(osv.osv): + """ document page type """ - _name = "wiki.groups" - _description = "Wiki Groups" + _name = "document.page.type" + _description = "Document page type" _order = 'name' _columns = { - 'name':fields.char('Wiki Group', size=256, select=True, required=True), - 'page_ids':fields.one2many('wiki.wiki', 'group_id', 'Pages'), + 'name':fields.char('Document Page Type', size=256, select=True, required=True), + 'page_ids':fields.one2many('document.page', 'parent_id', 'Pages'), 'notes':fields.text("Description"), 'create_date':fields.datetime("Created Date", select=True), - 'template': fields.text('Wiki Template'), - 'section': fields.boolean("Make Section ?"), + 'content_template': fields.text('Document Page Template'), 'method':fields.selection([('list', 'List'), ('page', 'Home Page'), \ ('tree', 'Tree')], 'Display Method', help="Define the default behaviour of the menu created on this group"), - 'home':fields.many2one('wiki.wiki', 'Home Page', help="Required to select home page if display method is Home Page"), + 'home':fields.many2one('document.page', 'Home Page', help="Required to select home page if display method is Home Page"), 'menu_id': fields.many2one('ir.ui.menu', "Menu", readonly=True), } @@ -55,13 +54,13 @@ class wiki_group(osv.osv): 'method': lambda *a: 'page', } - def open_wiki_page(self, cr, uid, ids, context=None): + def open_document_page(self, cr, uid, ids, context=None): - """ Opens Wiki Page of Group + """ Opens document Page of Group @param cr: the current row, from the database cursor, @param uid: the current user’s ID for security checks, - @param ids: List of open wiki group’s IDs - @return: dictionay of open wiki window on give group id + @param ids: List of open document group’s IDs + @return: dictionay of open Document window on give group id """ if type(ids) in (int,long,): ids = [ids] @@ -71,110 +70,112 @@ class wiki_group(osv.osv): if not group_id: return {} value = { - 'name': 'Wiki Page', + 'name': 'Document Page', 'view_type': 'form', 'view_mode': 'form,tree', - 'res_model': 'wiki.wiki', + 'res_model': 'document.page', 'view_id': False, 'type': 'ir.actions.act_window', 'nodestroy': True, } group = self.browse(cr, uid, group_id, context=context) - value['domain'] = "[('group_id','=',%d)]" % (group.id) + value['domain'] = "[('parent_id','=',%d)]" % (group.id) if group.method == 'page': value['res_id'] = group.home.id elif group.method == 'list': value['view_type'] = 'form' value['view_mode'] = 'tree,form' elif group.method == 'tree': - view_id = self.pool.get('ir.ui.view').search(cr, uid, [('name', '=', 'wiki.wiki.tree.children')]) + view_id = self.pool.get('ir.ui.view').search(cr, uid, [('name', '=', 'document.page.tree.children')]) value['view_id'] = view_id - value['domain'] = [('group_id', '=', group.id), ('parent_id', '=', False)] + value['domain'] = [('parent_id', '=', group.id)] value['view_type'] = 'tree' + value['view_mode'] = 'tree' return value -wiki_group() +document_page_type() -class wiki_wiki2(osv.osv): - """ Wiki Page """ - - _inherit = "wiki.wiki" - _description = "Wiki Page" - _order = 'section,create_date desc' +class document_page2(osv.osv): + """ Document Page """ + _inherit = "document.page" + _description = "Document Page" + _order = 'create_date desc' + _columns = { 'name': fields.char('Title', size=256, select=True, required=True), 'write_uid': fields.many2one('res.users', "Last Contributor", select=True), - 'text_area': fields.text("Content"), + 'content': fields.text("Content"), + 'index': fields.char('Index', size=256), 'create_uid': fields.many2one('res.users', 'Author', select=True, readonly=True), 'create_date': fields.datetime("Created on", select=True, readonly=True), + 'content_template': fields.text('Document Template'), 'write_date': fields.datetime("Modification Date", select=True, readonly=True), 'tags': fields.char('Keywords', size=1024, select=True), - 'history_id': fields.one2many('wiki.wiki.history', 'wiki_id', 'History Lines'), + 'history_ids': fields.one2many('document.page.history', 'document_id', 'History Lines'), 'minor_edit': fields.boolean('Minor edit', select=True), - 'summary': fields.char('Summary', size=256), - 'section': fields.char('Section', size=32, help="Use page section code like 1.2.1", select=True), - 'group_id': fields.many2one('wiki.groups', 'Wiki Group', select=1, ondelete='set null', - help="Topic, also called Wiki Group"), - 'toc': fields.boolean('Table of Contents', - help="Indicates that this pages have a table of contents or not"), + 'edit_summary': fields.char('Summary', size=256), + 'type':fields.selection([('normal','Content Page'), ('index','Index Page')], 'Type', help="Define the type of the document"), 'review': fields.boolean('Needs Review', select=True, help="Indicates that this page should be reviewed, raising the attention of other contributors"), - 'parent_id': fields.many2one('wiki.wiki', 'Parent Page', help="Allows you to link with the other page with in the current topic"), - 'child_ids': fields.one2many('wiki.wiki', 'parent_id', 'Child Pages'), + 'parent_id': fields.many2one('document.page.type', 'Parent Page', select=1 , ondelete='set null', help="Allows you to link with the topic"), + 'child_ids': fields.one2many('document.page', 'parent_id', 'Child Pages'), } _defaults = { - 'toc': True, + 'type':'normal', 'review': True, 'minor_edit': True, + 'index' : 1, } - def onchange_group_id(self, cr, uid, ids, group_id, content, context=None): + + + def onchange_parent_id(self, cr, uid, ids, parent_id, content, context=None): """ @param cr: the current row, from the database cursor, @param uid: the current user’s ID for security checks, - @param ids: List of wiki page’s IDs - @return: dictionay of open wiki page on give page section """ + @param ids: List of document page’s IDs + @return: dictionay of open document page on give page section """ - if (not group_id) or content: + if (not parent_id) or content: return {} - grp = self.pool.get('wiki.groups').browse(cr, uid, group_id, context=context) - section = '0' - for page in grp.page_ids: - if page.section: section = page.section - s = section.split('.') - template = grp.template + grp = self.pool.get('document.page.type').browse(cr, uid, parent_id, context=context) + template = grp.content_template try: s[-1] = str(int(s[-1])+1) except: pass - section = '.'.join(s) return { 'value':{ - 'text_area': template, - 'section': section + 'content': template, } } + def onchange_content(self, cr, uid, ids, content, context=None): + + if content: + return {'value':{'summary': content}} + return {} + def copy_data(self, cr, uid, id, default=None, context=None): """ @param cr: the current row, from the database cursor, @param uid: the current user’s ID for security checks, - @param id: Give wiki page's ID """ + @param id: Give document page's ID """ - return super(wiki_wiki2, self).copy_data(cr, uid, id, {'wiki_id': False}, context) + return super(document_page2, self).copy_data(cr, uid, id, {'document_id': False}, context) def create_history(self, cr, uid, ids, vals, context=None): history_id = False - history = self.pool.get('wiki.wiki.history') - if vals.get('text_area'): + history = self.pool.get('document.page.history') + if vals.get('content'): res = { 'minor_edit': vals.get('minor_edit', True), - 'text_area': vals.get('text_area', ''), + 'content': vals.get('content', ''), 'write_uid': uid, - 'wiki_id': ids[0], - 'summary':vals.get('summary', '') + 'document_id': ids[0], + 'summary':vals.get('edit_summary', '') } history_id = history.create(cr, uid, res) return history_id @@ -183,37 +184,37 @@ class wiki_wiki2(osv.osv): """ @param cr: the current row, from the database cursor, @param uid: the current user’s ID for security checks, """ - wiki_id = super(wiki_wiki2, self).create(cr, uid, + document_id = super(document_page2, self).create(cr, uid, vals, context) - self.create_history(cr, uid, [wiki_id], vals, context) - return wiki_id + self.create_history(cr, uid, [document_id], vals, context) + return document_id def write(self, cr, uid, ids, vals, context=None): """ @param cr: the current row, from the database cursor, @param uid: the current user’s ID for security checks, """ - result = super(wiki_wiki2, self).write(cr, uid, ids, vals, context) + result = super(document_page2, self).write(cr, uid, ids, vals, context) self.create_history(cr, uid, ids, vals, context) return result -wiki_wiki2() +document_page2() -class wiki_history(osv.osv): - """ Wiki History """ +class document_page_history(osv.osv): + """ Document Page History """ - _name = "wiki.wiki.history" - _description = "Wiki History" + _name = "document.page.history" + _description = "Document Page History" _rec_name = "summary" _order = 'id DESC' _columns = { 'create_date': fields.datetime("Date", select=True), - 'text_area': fields.text("Text area"), + 'content': fields.text("Content"), 'minor_edit': fields.boolean('This is a major edit ?', select=True), 'summary': fields.char('Summary', size=256, select=True), 'write_uid': fields.many2one('res.users', "Modify By", select=True), - 'wiki_id': fields.many2one('wiki.wiki', 'Wiki Id', select=True) + 'document_id': fields.many2one('document.page', 'Document Page', select=True) } _defaults = { @@ -225,9 +226,9 @@ class wiki_history(osv.osv): """ @param cr: the current row, from the database cursor, @param uid: the current user’s ID for security checks, """ - history_pool = self.pool.get('wiki.wiki.history') - text1 = history_pool.read(cr, uid, [v1], ['text_area'])[0]['text_area'] - text2 = history_pool.read(cr, uid, [v2], ['text_area'])[0]['text_area'] + history_pool = self.pool.get('document.page.history') + text1 = history_pool.read(cr, uid, [v1], ['content'])[0]['content'] + text2 = history_pool.read(cr, uid, [v2], ['content'])[0]['content'] line1 = line2 = '' if text1: line1 = tools.ustr(text1.splitlines(1)) @@ -238,6 +239,6 @@ class wiki_history(osv.osv): diff = difflib.HtmlDiff() return diff.make_file(line1, line2, "Revision-%s" % (v1), "Revision-%s" % (v2), context=False) -wiki_history() +document_page_history() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/document_page/document_page_data.xml b/addons/document_page/document_page_data.xml new file mode 100644 index 00000000000..1cef65694d8 --- /dev/null +++ b/addons/document_page/document_page_data.xml @@ -0,0 +1,41 @@ + + + + + The OpenERP wiki + help, quick start, wiki, formatting + 0 + 1 + Initial Page + ==The OpenERP wiki== + +[[File:http://www.openerp.com/sites/all/themes/openerp/logo.png OpenERP]] + +The OpenERP wiki allows you to manage your enterprise's contents using wiki +restructured texts. This module provides a collaborative way to manage internal +FAQs, quality manuals, technical references, etc. + +==Keypoints== +* Same formating style than MediaWiki, +* Any number of wiki group for different purposes, +* Detailed history on all pages, +* Integrated with the document management system. + +==Why you should use the OpenERP integrated wiki than a separate wiki system ?== +* Allows links to any document of the system, +* Uses the access controls of OpenERP for uniq access rights management, +* Use it to describe projects, tasks, products, +* Integrated with customer portal to provide restricted external accesses, +* Linked to users processes for quality manuals. + +==To get more information== +* [[Basic Wiki Editing]] +* [[Wiki Documentation]] +* [http://openerp.com The OpenERP website] + + + + + + + diff --git a/addons/document_page/document_page_demo.xml b/addons/document_page/document_page_demo.xml new file mode 100644 index 00000000000..90f56f86d63 --- /dev/null +++ b/addons/document_page/document_page_demo.xml @@ -0,0 +1,105 @@ + + + + + + + + + OpenERP 6.1. Functional Demo + 0 + 6 + Initial Page + ==OpenERP 6.1. Functional Demo== + +The news is out, OpenERP's latest version 6.1. is here. +It's more user-friendly, even more business oriented and efficient to manage your company + +How to discover the latest version 6.1.? + +Demo : [http://demo.openerp.com] +Online: [http://openerp.com/online] +Download: [http://openerp.com/downloads] + +We have also put together a functional demo that presents 6.1. Watch this video to learn directly from us what OpenERP 6.1. can do for you. Share it in your company, with your clients and implement it now for your business. + +==Watch on Youtube!== + +[[Video:http://www.youtube.com/embed/7jES2jxKMso ]] + + + + + + Personalise Dashboards + 0 + 7 + Initial Page + ==Personalise your OpenERP dashboards== +You like OpenERP, but feel like you want to personalise it more? Now, OpenERP goes a step further and lets you customize your dashboard. +How? you will ask. Thanks to a new feature that allows you to customize your dashboard by adding new boards of any search view. + +Let's say you are not a big fan of the default dashboard and you feel like the view provided are not the most relevant to you. So, now you can remove the board you find useless and customize your own one. + +==How is it done?== + +Step 1: access one search view + +Step 2: apply the filter you want to see at each connection to the application (eg. on sales, manufacturing, etc) + +Step 3: add it into the dashboard in the same space where you can save the filter + +Step 4: choose the application you want it visible on and the name of the array + +Look at this simple example below from Purchase, where I want to put on the application's dashboard "Purchases to Approve". After I access the search view and apply the filter for "Purchases to Approve", I can add it immediately to my Purchase dashboard. + + +[[File:http://www.openerp.com/sites/default/files/fileattach/dashboard2_1(1).png ]] + +In less than a minute, the search view is visible on the dashboard + +[[File:http://www.openerp.com/sites/default/files/fileattach/dashboard2_2.png ]] + +Of course, you are free to delete what you don't need or like, but just in case you change your mind there is a reset button to return to the default view. + + + + + Touchscreen Point of Sale + 0 + 8 + Initial Page + ==The new OpenERP touchscreen Point of Sale== + +The brand new OpenERP touchscreen point of sale available with 6.1 allows you to manage your shop sales very easily. +It's fully web based so that you don't have to install or deploy any software and all the sales shops can be easily consolidated. +It works in connected and disconnected modes so that you can continue to sell if you lose your internet connection. + +[[File:http://www.openerp.com/sites/default/files/fileattach/POS(2).png ]] + +==Here's a summary of its main features and benefits:== + +100% WEB based + +available for any touchscreen device (ipod, ipad, any tablet)mobile (with portable devices) + +no installation required +Packed as a standard OpenERP module + +no synchronization needed, completely integrated +all OpenERP available, use OpenERP for backend consolidate shops almost in real time +Work offline, with no server connection + +continue working even when your connection is down +if you close your browser, data won't be lost + +fully web based with a clean interface smart interface +You have different options to select your products. You can do it through the barcode reader, just browse through the categories you have put in place (ie. drinks, snacks, meals, etc.), or text search in case neither of the other options work for you. +If you need to use the POS for your restaurant, for example, your employees can record at the same time multiple tickets without having to wait to do one transaction at a time. Along, to facilitate payment, the application allows multiple payment methods. + +The POS application is so simple and accessible to use that your shop or restaurant will never need any other tool to manage orders. Due to its smart and user-friendly interface you don't need any training to learn how to use it. Think of it as an out-of-the-box solution to boost your business' productivity. + + + + + \ No newline at end of file diff --git a/addons/document_page/document_page_sequence.xml b/addons/document_page/document_page_sequence.xml new file mode 100644 index 00000000000..1305ccd5678 --- /dev/null +++ b/addons/document_page/document_page_sequence.xml @@ -0,0 +1,16 @@ + + + + + Document Page + document.page + + + Document Page sequence + document.page + 1 + 1 + + + + diff --git a/addons/wiki/wiki_view.xml b/addons/document_page/document_page_view.xml similarity index 50% rename from addons/wiki/wiki_view.xml rename to addons/document_page/document_page_view.xml index d0d4c35a80d..74d6f0b8719 100644 --- a/addons/wiki/wiki_view.xml +++ b/addons/document_page/document_page_view.xml @@ -1,27 +1,24 @@ + - - + - - - + - wiki.groups.tree - wiki.groups + document.page.type.tree + document.page.type tree - + @@ -29,76 +26,77 @@ - wiki.groups.form - wiki.groups + document_page_type.form + document.page.type form -
+ + +
+
- + - - + -
+
- - + +
diff --git a/addons/account_analytic_default/account_analytic_default_view.xml b/addons/account_analytic_default/account_analytic_default_view.xml index 3e2ba0fe5f3..0bc0cd0c3bb 100644 --- a/addons/account_analytic_default/account_analytic_default_view.xml +++ b/addons/account_analytic_default/account_analytic_default_view.xml @@ -31,7 +31,7 @@ - + diff --git a/addons/account_asset/account_asset_view.xml b/addons/account_asset/account_asset_view.xml index cdc772ad65d..50c0443b02a 100644 --- a/addons/account_asset/account_asset_view.xml +++ b/addons/account_asset/account_asset_view.xml @@ -254,7 +254,7 @@ - + diff --git a/addons/account_coda/account_coda_view.xml b/addons/account_coda/account_coda_view.xml index 07f397c663f..b4b8e7796c2 100644 --- a/addons/account_coda/account_coda_view.xml +++ b/addons/account_coda/account_coda_view.xml @@ -249,7 +249,7 @@ - + diff --git a/addons/account_payment/account_payment_view.xml b/addons/account_payment/account_payment_view.xml index 5395bcafb17..dd9a78b261a 100644 --- a/addons/account_payment/account_payment_view.xml +++ b/addons/account_payment/account_payment_view.xml @@ -120,7 +120,7 @@ - + diff --git a/addons/analytic_user_function/analytic_user_function_view.xml b/addons/analytic_user_function/analytic_user_function_view.xml index 9c6e89d1bda..036fe7c29ff 100644 --- a/addons/analytic_user_function/analytic_user_function_view.xml +++ b/addons/analytic_user_function/analytic_user_function_view.xml @@ -90,7 +90,7 @@ - + @@ -114,7 +114,7 @@ - + diff --git a/addons/audittrail/audittrail_view.xml b/addons/audittrail/audittrail_view.xml index 65eca09a9a1..55c97760544 100644 --- a/addons/audittrail/audittrail_view.xml +++ b/addons/audittrail/audittrail_view.xml @@ -30,7 +30,7 @@ - + @@ -97,7 +97,7 @@ - + diff --git a/addons/base_calendar/base_calendar_view.xml b/addons/base_calendar/base_calendar_view.xml index d48795173ed..151d64f6370 100644 --- a/addons/base_calendar/base_calendar_view.xml +++ b/addons/base_calendar/base_calendar_view.xml @@ -34,7 +34,7 @@ - + @@ -222,7 +222,7 @@ - + diff --git a/addons/base_calendar/crm_meeting_view.xml b/addons/base_calendar/crm_meeting_view.xml index d3371ad602a..96a13c4bcc6 100644 --- a/addons/base_calendar/crm_meeting_view.xml +++ b/addons/base_calendar/crm_meeting_view.xml @@ -99,7 +99,7 @@ - + @@ -232,7 +232,7 @@ - + @@ -277,7 +277,7 @@ - + diff --git a/addons/caldav/caldav_view.xml b/addons/caldav/caldav_view.xml index a45a2231738..eb89469ca19 100644 --- a/addons/caldav/caldav_view.xml +++ b/addons/caldav/caldav_view.xml @@ -10,7 +10,7 @@
- + @@ -84,7 +84,7 @@ - + diff --git a/addons/crm/crm_lead_view.xml b/addons/crm/crm_lead_view.xml index dd2ef7bd9e8..cf2466c4f13 100644 --- a/addons/crm/crm_lead_view.xml +++ b/addons/crm/crm_lead_view.xml @@ -166,15 +166,15 @@ - - + + - + @@ -496,7 +496,7 @@ - + @@ -526,10 +526,10 @@
- +
- +
@@ -537,7 +537,7 @@
diff --git a/addons/crm/wizard/crm_phonecall_to_phonecall_view.xml b/addons/crm/wizard/crm_phonecall_to_phonecall_view.xml index 4fb54d731d4..dd8006c7990 100644 --- a/addons/crm/wizard/crm_phonecall_to_phonecall_view.xml +++ b/addons/crm/wizard/crm_phonecall_to_phonecall_view.xml @@ -15,7 +15,7 @@ - +
diff --git a/addons/crm_claim/crm_claim_view.xml b/addons/crm_claim/crm_claim_view.xml index da3d20424a7..930c08408cd 100644 --- a/addons/crm_claim/crm_claim_view.xml +++ b/addons/crm_claim/crm_claim_view.xml @@ -131,7 +131,7 @@ - + diff --git a/addons/crm_helpdesk/crm_helpdesk_view.xml b/addons/crm_helpdesk/crm_helpdesk_view.xml index c23ca566f9f..0fb0cbf825f 100644 --- a/addons/crm_helpdesk/crm_helpdesk_view.xml +++ b/addons/crm_helpdesk/crm_helpdesk_view.xml @@ -50,7 +50,7 @@ - + diff --git a/addons/crm_partner_assign/wizard/crm_forward_to_partner_view.xml b/addons/crm_partner_assign/wizard/crm_forward_to_partner_view.xml index 8c89297f8ec..f15f104f1c1 100644 --- a/addons/crm_partner_assign/wizard/crm_forward_to_partner_view.xml +++ b/addons/crm_partner_assign/wizard/crm_forward_to_partner_view.xml @@ -2,27 +2,26 @@ - crm_lead_forward_to_partner crm.lead.forward.to.partner form
- + - + + on_change="on_change_email(user_id)"/> - + - + @@ -44,7 +43,7 @@
@@ -60,7 +59,7 @@
- + + /> diff --git a/addons/document/document_view.xml b/addons/document/document_view.xml index 3a9963c56c2..92d07751e24 100644 --- a/addons/document/document_view.xml +++ b/addons/document/document_view.xml @@ -1,3 +1,4 @@ + @@ -11,7 +12,7 @@ - + @@ -73,7 +74,7 @@ - +
@@ -235,7 +236,7 @@ - + diff --git a/addons/event/event_view.xml b/addons/event/event_view.xml index 02f996c6aac..f3a0c5b321e 100644 --- a/addons/event/event_view.xml +++ b/addons/event/event_view.xml @@ -477,7 +477,7 @@ - + diff --git a/addons/hr/hr_view.xml b/addons/hr/hr_view.xml index ff789c55c5a..5d34d428726 100644 --- a/addons/hr/hr_view.xml +++ b/addons/hr/hr_view.xml @@ -43,7 +43,7 @@ - + diff --git a/addons/hr_evaluation/hr_evaluation_view.xml b/addons/hr_evaluation/hr_evaluation_view.xml index 70a272009fc..6d54e6278b1 100644 --- a/addons/hr_evaluation/hr_evaluation_view.xml +++ b/addons/hr_evaluation/hr_evaluation_view.xml @@ -180,7 +180,7 @@ - + @@ -289,7 +289,7 @@ - + diff --git a/addons/hr_recruitment/hr_recruitment_view.xml b/addons/hr_recruitment/hr_recruitment_view.xml index cfeac11ebff..d72db953fd1 100644 --- a/addons/hr_recruitment/hr_recruitment_view.xml +++ b/addons/hr_recruitment/hr_recruitment_view.xml @@ -144,7 +144,7 @@ - +
diff --git a/addons/point_of_sale/point_of_sale.py b/addons/point_of_sale/point_of_sale.py index aa152ad94d3..152329ad2fd 100644 --- a/addons/point_of_sale/point_of_sale.py +++ b/addons/point_of_sale/point_of_sale.py @@ -156,6 +156,15 @@ class pos_session(osv.osv): ('closed', 'Closed & Posted'), ] + def _compute_cash_journal_id(self, cr, uid, ids, fieldnames, args, context=None): + result = dict.fromkeys(ids, False) + for record in self.browse(cr, uid, ids, context=context): + for st in record.statement_ids: + if st.journal_id.type == 'cash': + result[record.id] = st.journal_id.id + break + return result + def _compute_cash_register_id(self, cr, uid, ids, fieldnames, args, context=None): result = dict.fromkeys(ids, False) for record in self.browse(cr, uid, ids, context=context): @@ -211,6 +220,9 @@ class pos_session(osv.osv): required=True, readonly=True, select=1), + 'cash_journal_id' : fields.function(_compute_cash_journal_id, method=True, + type='many2one', relation='account.journal', + string='Cash Journal', store=True), 'cash_register_id' : fields.function(_compute_cash_register_id, method=True, type='many2one', relation='account.bank.statement', string='Cash Register', store=True), diff --git a/addons/point_of_sale/point_of_sale_view.xml b/addons/point_of_sale/point_of_sale_view.xml index 9e8db366563..1168faa21ac 100644 --- a/addons/point_of_sale/point_of_sale_view.xml +++ b/addons/point_of_sale/point_of_sale_view.xml @@ -18,16 +18,14 @@ form
-
-
+