summaryrefslogtreecommitdiff
path: root/addons/test_xlsx_export
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/test_xlsx_export
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/test_xlsx_export')
-rw-r--r--addons/test_xlsx_export/__init__.py1
-rw-r--r--addons/test_xlsx_export/__manifest__.py13
-rw-r--r--addons/test_xlsx_export/ir.model.access.csv4
-rw-r--r--addons/test_xlsx_export/models.py36
-rw-r--r--addons/test_xlsx_export/tests/__init__.py4
-rw-r--r--addons/test_xlsx_export/tests/test_export.py371
6 files changed, 429 insertions, 0 deletions
diff --git a/addons/test_xlsx_export/__init__.py b/addons/test_xlsx_export/__init__.py
new file mode 100644
index 00000000..0650744f
--- /dev/null
+++ b/addons/test_xlsx_export/__init__.py
@@ -0,0 +1 @@
+from . import models
diff --git a/addons/test_xlsx_export/__manifest__.py b/addons/test_xlsx_export/__manifest__.py
new file mode 100644
index 00000000..df22ffcd
--- /dev/null
+++ b/addons/test_xlsx_export/__manifest__.py
@@ -0,0 +1,13 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+{
+ 'name': 'test xlsx export',
+ 'version': '0.1',
+ 'category': 'Hidden/Tests',
+ 'description': """A module to test xlsx export.""",
+ 'depends': ['web', 'test_mail'],
+ 'data': ['ir.model.access.csv'],
+ 'installable': True,
+ 'auto_install': False,
+ 'license': 'LGPL-3',
+}
diff --git a/addons/test_xlsx_export/ir.model.access.csv b/addons/test_xlsx_export/ir.model.access.csv
new file mode 100644
index 00000000..cb43b225
--- /dev/null
+++ b/addons/test_xlsx_export/ir.model.access.csv
@@ -0,0 +1,4 @@
+"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
+access_export_group_operator,access_export_group_operator,model_export_group_operator,,1,1,1,1
+access_export_group_operator_one2many,access_export_group_operator_one2many,model_export_group_operator_one2many,,1,1,1,1
+access_export_integer,access_export_integer,model_export_integer,,1,1,1,1
diff --git a/addons/test_xlsx_export/models.py b/addons/test_xlsx_export/models.py
new file mode 100644
index 00000000..830917c0
--- /dev/null
+++ b/addons/test_xlsx_export/models.py
@@ -0,0 +1,36 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import api, fields, models
+
+class NewModel(models.Model):
+ _name = 'export.integer'
+ _description = 'Export: Integer'
+
+ value = fields.Integer(default=4)
+
+ def name_get(self):
+ return [(record.id, "%s:%s" % (self._name, record.value)) for record in self]
+
+class GroupOperator(models.Model):
+ _name = 'export.group_operator'
+ _description = 'Export Group Operator'
+
+ int_sum = fields.Integer(group_operator='sum')
+ int_max = fields.Integer(group_operator='max')
+ float_min = fields.Float(group_operator='min')
+ float_avg = fields.Float(group_operator='avg')
+ float_monetary = fields.Monetary(currency_field='currency_id',group_operator='sum')
+ currency_id = fields.Many2one('res.currency')
+ date_max = fields.Date(group_operator='max')
+ bool_and = fields.Boolean(group_operator='bool_and')
+ bool_or = fields.Boolean(group_operator='bool_or')
+ many2one = fields.Many2one('export.integer')
+ one2many = fields.One2many('export.group_operator.one2many', 'parent_id')
+
+class GroupOperatorO2M(models.Model):
+ _name = 'export.group_operator.one2many'
+ _description = 'Export Group Operator One2Many'
+
+ parent_id = fields.Many2one('export.group_operator')
+ value = fields.Integer()
diff --git a/addons/test_xlsx_export/tests/__init__.py b/addons/test_xlsx_export/tests/__init__.py
new file mode 100644
index 00000000..ff41d3a8
--- /dev/null
+++ b/addons/test_xlsx_export/tests/__init__.py
@@ -0,0 +1,4 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+from . import test_export
+
diff --git a/addons/test_xlsx_export/tests/test_export.py b/addons/test_xlsx_export/tests/test_export.py
new file mode 100644
index 00000000..0ee3a05d
--- /dev/null
+++ b/addons/test_xlsx_export/tests/test_export.py
@@ -0,0 +1,371 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+import json
+from datetime import date
+from unittest.mock import patch
+
+from odoo import http
+from odoo.tests import common, tagged
+from odoo.addons.web.controllers.main import ExportXlsxWriter
+from odoo.addons.mail.tests.common import mail_new_test_user
+
+
+class XlsxCreatorCase(common.HttpCase):
+ model_name = False
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.model = None
+
+ def setUp(self):
+ super().setUp()
+ self.model = self.env[self.model_name]
+
+ mail_new_test_user(self.env, login='fof', password='123456789', groups='base.group_user,base.group_allow_export')
+ self.session = self.authenticate('fof', '123456789')
+
+ self.worksheet = {} # mock worksheet
+
+ self.default_params = {
+ 'domain': [],
+ 'fields': [{'name': field.name, 'label': field.string} for field in self.model._fields.values()],
+ 'groupby': [],
+ 'ids': False,
+ 'import_compat': False,
+ 'model': self.model._name,
+ }
+
+ def _mock_write(self, row, column, value, style=None):
+ self.worksheet[row, column] = str(value)
+
+ def make(self, values, context=None):
+ return self.model.with_context(**(context or {})).create(values)
+
+ def export(self, values, fields=[], params={}, context=None):
+ self.worksheet = {}
+ self.make(values, context=context)
+
+ if fields and 'fields' not in params:
+ params['fields'] = [{
+ 'name': self.model._fields[f].name,
+ 'label': self.model._fields[f].string,
+ 'type': self.model._fields[f].type,
+ } for f in fields]
+
+ with patch.object(ExportXlsxWriter, 'write', self._mock_write):
+ self.url_open('/web/export/xlsx', data={
+ 'data': json.dumps(dict(self.default_params, **params)),
+ 'token': 'dummy',
+ 'csrf_token': http.WebRequest.csrf_token(self),
+ })
+ return self.worksheet
+
+ def assertExportEqual(self, value, expected):
+ for row in range(len(expected)):
+ for column in range(len(expected[row])):
+ cell_value = value.pop((row, column), '')
+ expected_value = expected[row][column]
+ self.assertEqual(cell_value, expected_value, "Cell %s, %s have a wrong value" % (row, column))
+ self.assertFalse(value, "There are unexpected cells in the export")
+
+
+@tagged('-at_install', 'post_install')
+class TestGroupedExport(XlsxCreatorCase):
+ model_name = 'export.group_operator'
+
+ def test_int_sum_max(self):
+ values = [
+ {'int_sum': 10, 'int_max': 20},
+ {'int_sum': 10, 'int_max': 50},
+ {'int_sum': 20,'int_max': 30},
+ ]
+ export = self.export(values, fields=['int_sum', 'int_max'], params={'groupby': ['int_sum', 'int_max']})
+ self.assertExportEqual(export, [
+ ['Int Sum' ,'Int Max'],
+ ['10 (2)' ,'50'],
+ [' 20 (1)' ,'20'],
+ ['10' ,'20'],
+ [' 50 (1)' ,'50'],
+ ['10' ,'50'],
+ ['20 (1)' ,'30'],
+ [' 30 (1)' ,'30'],
+ ['20' ,'30'],
+ ])
+
+ export = self.export([], fields=['int_max', 'int_sum'], params={'groupby': ['int_sum', 'int_max']})
+
+ self.assertExportEqual(export, [
+ ['Int Max' ,'Int Sum'],
+ ['10 (2)' ,'20'],
+ [' 20 (1)' ,'10'],
+ ['20' ,'10'],
+ [' 50 (1)' ,'10'],
+ ['50' ,'10'],
+ ['20 (1)' ,'20'],
+ [' 30 (1)' ,'20'],
+ ['30' ,'20'],
+ ])
+
+ def test_float_min(self):
+ values = [
+ {'int_sum': 10, 'float_min': 111.0},
+ {'int_sum': 10, 'float_min': 222.0},
+ {'int_sum': 20, 'float_min': 333.0},
+ ]
+ export = self.export(values, fields=['int_sum', 'float_min'], params={'groupby': ['int_sum', 'float_min']})
+
+ self.assertExportEqual(export, [
+ ['Int Sum' ,'Float Min'],
+ ['10 (2)' ,'111.00'],
+ [' 111.0 (1)','111.00'],
+ ['10' ,'111.0'],
+ [' 222.0 (1)','222.00'],
+ ['10' ,'222.0'],
+ ['20 (1)' ,'333.00'],
+ [' 333.0 (1)','333.00'],
+ ['20' ,'333.0'],
+ ])
+
+ def test_float_avg(self):
+ values = [
+ {'int_sum': 10, 'float_avg': 100.0},
+ {'int_sum': 10, 'float_avg': 200.0},
+ {'int_sum': 20, 'float_avg': 300.0},
+ ]
+ export = self.export(values, fields=['int_sum', 'float_avg'], params={'groupby': ['int_sum', 'float_avg']})
+
+ self.assertExportEqual(export, [
+ ['Int Sum' ,'Float Avg'],
+ ['10 (2)' ,'150.00'],
+ [' 100.0 (1)','100.00'],
+ ['10' ,'100.0'],
+ [' 200.0 (1)','200.00'],
+ ['10' ,'200.0'],
+ ['20 (1)' ,'300.00'],
+ [' 300.0 (1)','300.00'],
+ ['20' ,'300.0'],
+ ])
+
+ def test_float_avg_nested(self):
+ """ With more than one nested level (avg aggregation) """
+ values = [
+ {'int_sum': 10, 'int_max': 30, 'float_avg': 100.0},
+ {'int_sum': 10, 'int_max': 30, 'float_avg': 200.0},
+ {'int_sum': 10, 'int_max': 20, 'float_avg': 600.0},
+ ]
+ export = self.export(values, fields=['int_sum', 'float_avg'], params={'groupby': ['int_sum', 'int_max', 'float_avg']})
+
+ self.assertExportEqual(export, [
+ ['Int Sum' ,'Float Avg'],
+ ['10 (3)' ,'300.00'],
+ [' 20 (1)' ,'600.00'],
+ [' 600.0 (1)','600.00'],
+ ['10' ,'600.0'],
+ [' 30 (2)' ,'150.00'],
+ [' 100.0 (1)','100.00'],
+ ['10' ,'100.0'],
+ [' 200.0 (1)','200.00'],
+ ['10' ,'200.0'],
+ ])
+
+ def test_float_avg_nested_no_value(self):
+ """ With more than one nested level (avg aggregation is done on 0, not False) """
+ values = [
+ {'int_sum': 10, 'int_max': 20, 'float_avg': False},
+ {'int_sum': 10, 'int_max': 30, 'float_avg': False},
+ {'int_sum': 10, 'int_max': 30, 'float_avg': False},
+ ]
+ export = self.export(values, fields=['int_sum', 'float_avg'], params={'groupby': ['int_sum', 'int_max', 'float_avg']})
+
+ self.assertExportEqual(export, [
+ ['Int Sum' ,'Float Avg'],
+ ['10 (3)' ,'0.00'],
+ [' 20 (1)' ,'0.00'],
+ [' Undefined (1)','0.00'],
+ ['10' ,'0.0'],
+ [' 30 (2)' ,'0.00'],
+ [' Undefined (2)','0.00'],
+ ['10' ,'0.0'],
+ ['10' ,'0.0'],
+ ])
+
+ def test_date_max(self):
+ values = [
+ {'int_sum': 10, 'date_max': date(2019, 1, 1)},
+ {'int_sum': 10, 'date_max': date(2000, 1, 1)},
+ {'int_sum': 20, 'date_max': date(1980, 1, 1)},
+ ]
+ export = self.export(values, fields=['int_sum', 'date_max'], params={'groupby': ['int_sum', 'date_max:month']})
+
+ self.assertExportEqual(export, [
+ ['Int Sum' ,'Date Max'],
+ ['10 (2)' ,'2019-01-01'],
+ [' January 2000 (1)' ,'2000-01-01'],
+ ['10' ,'2000-01-01'],
+ [' January 2019 (1)' ,'2019-01-01'],
+ ['10' ,'2019-01-01'],
+ ['20 (1)' ,'1980-01-01'],
+ [' January 1980 (1)' ,'1980-01-01'],
+ ['20' ,'1980-01-01'],
+ ])
+
+ def test_bool_and(self):
+ values = [
+ {'int_sum': 10, 'bool_and': True},
+ {'int_sum': 10, 'bool_and': True},
+ {'int_sum': 20, 'bool_and': True},
+ {'int_sum': 20, 'bool_and': False},
+ ]
+ export = self.export(values, fields=['int_sum', 'bool_and'], params={'groupby': ['int_sum', 'bool_and']})
+
+ self.assertExportEqual(export, [
+ ['Int Sum' ,'Bool And'],
+ ['10 (2)' ,'True'],
+ [' True (2)' ,'True'],
+ ['10' ,'True'],
+ ['10' ,'True'],
+ ['20 (2)' ,'False'],
+ [' False (1)' ,'False'],
+ ['20' ,'False'],
+ [' True (1)' ,'True'],
+ ['20' ,'True'],
+ ])
+
+ def test_bool_or(self):
+ values = [
+ {'int_sum': 10, 'bool_or': True},
+ {'int_sum': 10, 'bool_or': False},
+ {'int_sum': 20, 'bool_or': False},
+ {'int_sum': 20, 'bool_or': False},
+ ]
+ export = self.export(values, fields=['int_sum', 'bool_or'], params={'groupby': ['int_sum', 'bool_or']})
+
+ self.assertExportEqual(export, [
+ ['Int Sum' ,'Bool Or'],
+ ['10 (2)' ,'True'],
+ [' False (1)' ,'False'],
+ ['10' ,'False'],
+ [' True (1)' ,'True'],
+ ['10' ,'True'],
+ ['20 (2)' ,'False'],
+ [' False (2)' ,'False'],
+ ['20' ,'False'],
+ ['20' ,'False'],
+ ])
+
+ def test_many2one(self):
+ values = [
+ {'int_sum': 10, 'many2one': self.env['export.integer'].create({}).id},
+ {'int_sum': 10},
+ ]
+ export = self.export(values, fields=['int_sum', 'many2one'], params={'groupby': ['int_sum', 'many2one']})
+
+ self.assertExportEqual(export, [
+ ['Int Sum' ,'Many2One'],
+ ['10 (2)' ,''],
+ [' export.integer:4 (1)' ,''],
+ ['10' ,'export.integer:4'],
+ [' Undefined (1)' ,''],
+ ['10' ,'False'],
+ ])
+
+ def test_nested_records(self):
+ """
+ aggregated values currently not supported for nested record export, but it should not crash
+ e.g. export 'many2one/const'
+ """
+ values = [{'int_sum': 10,
+ 'date_max': date(2019, 1, 1),
+ 'many2one': self.env['export.integer'].create({}).id,
+ }, {
+ 'int_sum': 10,
+ 'date_max': date(2000, 1, 1),
+ 'many2one': self.env['export.integer'].create({}).id,
+ },]
+ export = self.export(values,
+ params={
+ 'groupby': ['int_sum', 'date_max:month'],
+ 'fields': [
+ {'name': 'int_sum', 'label': 'Int Sum'},
+ {'name': 'date_max', 'label': 'Date Max'},
+ {'name': 'many2one/value', 'label': 'Many2One/Value'},
+ ]
+ })
+
+ self.assertExportEqual(export, [
+ ['Int Sum' ,'Date Max' ,'Many2One/Value'],
+ ['10 (2)' ,'2019-01-01' ,''],
+ [' January 2000 (1)' ,'2000-01-01' ,''],
+ ['10' ,'2000-01-01' ,'4'],
+ [' January 2019 (1)' ,'2019-01-01' ,''],
+ ['10' ,'2019-01-01' ,'4'],
+ ])
+
+ def test_one2many(self):
+ values = [{
+ 'int_sum': 10,
+ 'one2many': [
+ (0, 0, {'value': 8}),
+ (0, 0, {'value': 9}),
+ ],
+ }]
+ export = self.export(values,
+ params={
+ 'groupby': ['int_sum',],
+ 'fields': [
+ {'name': 'int_sum', 'label': 'Int Sum'},
+ {'name': 'one2many/value', 'label': 'One2many/Value'},
+ ]
+ })
+ self.assertExportEqual(export, [
+ ['Int Sum' ,'One2many/Value'],
+ ['10 (1)' ,''],
+ ['10' ,'8'],
+ ['' ,'9'],
+ ])
+
+ def test_unset_date_values(self):
+ values = [
+ {'int_sum': 10, 'date_max': date(2019, 1, 1)},
+ {'int_sum': 10, 'date_max': False},
+ ]
+ # Group and aggregate by date, but date fields are not set for all records
+ export = self.export(values, fields=['int_sum', 'date_max'], params={'groupby': ['int_sum', 'date_max:month']})
+
+ self.assertExportEqual(export, [
+ ['Int Sum' ,'Date Max'],
+ ['10 (2)' ,'2019-01-01'],
+ [' January 2019 (1)' ,'2019-01-01'],
+ ['10' ,'2019-01-01'],
+ [' Undefined (1)' ,''],
+ ['10' ,''],
+ ])
+
+ def test_float_representation(self):
+ currency = self.env['res.currency'].create({
+ 'name': "bottlecap",
+ 'symbol': "b",
+ 'rounding': 0.001,
+ 'decimal_places': 3,
+ })
+
+ values = [
+ {'int_sum': 1, 'currency_id': currency.id, 'float_monetary': 60739.2000000004},
+ {'int_sum': 2, 'currency_id': currency.id, 'float_monetary': 2.0},
+ {'int_sum': 3, 'currency_id': currency.id, 'float_monetary': 999.9995999},
+ ]
+ export = self.export(values, fields=['int_sum', 'float_monetary'], params={'groupby': ['int_sum', 'float_monetary']})
+
+ self.assertExportEqual(export, [
+ ['Int Sum', 'Float Monetary'],
+ ['1 (1)','60739.200'],
+ [' 60739.2 (1)','60739.200'],
+ ['1','60739.2'],
+ ['2 (1)','2.000'],
+ [' 2.0 (1)','2.000'],
+ ['2','2.0'],
+ ['3 (1)','1000.000'],
+ [' 1000.0 (1)','1000.000'],
+ ['3','1000.0'],
+ ])