[FIX] web: number format in exported group header for float values

Exporting data incorrectly formats the float values of group headers

Steps to reproduce:
1. Install Planning
2. Open Planning and trigger the list view
3. Remove the default filter and add a group_by on employees
4. Export the data
5. The file produced doesn't have the same format for Allocated Hours in
   the group headers and in the line details

Solution:
Create formats for float and monetary values using the user's
preferences in decimal separator and decimal precision. For monetary
format, we use the biggest decimal precision used in the company
currencies.

opw-2864273

closes odoo/odoo#98813

X-original-commit: 718e8eea7862ad307479190336baf5b5e7092ae4
Signed-off-by: Julien Castiaux <juc@odoo.com>
Signed-off-by: Guillaume Merlin (megu) <megu@odoo.com>
This commit is contained in:
MerlinGuillaume
2022-08-25 04:21:14 +02:00
parent 6dc0d6976d
commit 719e028640
2 changed files with 38 additions and 35 deletions
+22 -16
View File
@@ -36,7 +36,12 @@ class XlsxCreatorCase(common.HttpCase):
}
def _mock_write(self, row, column, value, style=None):
self.worksheet[row, column] = str(value)
if isinstance(value, float):
decimal_places = style.num_format[::-1].find('.')
style_format = "{:." + str(decimal_places) + "f}"
self.worksheet[row, column] = style_format.format(value)
else:
self.worksheet[row, column] = str(value)
def make(self, values, context=None):
return self.model.with_context(**(context or {})).create(values)
@@ -71,6 +76,7 @@ class XlsxCreatorCase(common.HttpCase):
@tagged('-at_install', 'post_install')
class TestGroupedExport(XlsxCreatorCase):
model_name = 'export.group_operator'
# pylint: disable=bad-whitespace
def test_int_sum_max(self):
values = [
@@ -117,12 +123,12 @@ class TestGroupedExport(XlsxCreatorCase):
['Int Sum' ,'Float Min'],
['10 (2)' ,'111.00'],
[' 111.0 (1)','111.00'],
['10' ,'111.0'],
['10' ,'111.00'],
[' 222.0 (1)','222.00'],
['10' ,'222.0'],
['10' ,'222.00'],
['20 (1)' ,'333.00'],
[' 333.0 (1)','333.00'],
['20' ,'333.0'],
['20' ,'333.00'],
])
def test_float_avg(self):
@@ -137,12 +143,12 @@ class TestGroupedExport(XlsxCreatorCase):
['Int Sum' ,'Float Avg'],
['10 (2)' ,'150.00'],
[' 100.0 (1)','100.00'],
['10' ,'100.0'],
['10' ,'100.00'],
[' 200.0 (1)','200.00'],
['10' ,'200.0'],
['10' ,'200.00'],
['20 (1)' ,'300.00'],
[' 300.0 (1)','300.00'],
['20' ,'300.0'],
['20' ,'300.00'],
])
def test_float_avg_nested(self):
@@ -159,12 +165,12 @@ class TestGroupedExport(XlsxCreatorCase):
['10 (3)' ,'300.00'],
[' 20 (1)' ,'600.00'],
[' 600.0 (1)','600.00'],
['10' ,'600.0'],
['10' ,'600.00'],
[' 30 (2)' ,'150.00'],
[' 100.0 (1)','100.00'],
['10' ,'100.0'],
['10' ,'100.00'],
[' 200.0 (1)','200.00'],
['10' ,'200.0'],
['10' ,'200.00'],
])
def test_float_avg_nested_no_value(self):
@@ -181,11 +187,11 @@ class TestGroupedExport(XlsxCreatorCase):
['10 (3)' ,'0.00'],
[' 20 (1)' ,'0.00'],
[' Undefined (1)','0.00'],
['10' ,'0.0'],
['10' ,'0.00'],
[' 30 (2)' ,'0.00'],
[' Undefined (2)','0.00'],
['10' ,'0.0'],
['10' ,'0.0'],
['10' ,'0.00'],
['10' ,'0.00'],
])
def test_date_max(self):
@@ -359,11 +365,11 @@ class TestGroupedExport(XlsxCreatorCase):
['Int Sum', 'Float Monetary'],
['1 (1)', '60739.200'],
[' 60739.2 (1)', '60739.200'],
['1', '60739.2'],
['1', '60739.20'],
['2 (1)', '2.000'],
[' 2.0 (1)', '2.000'],
['2', '2.0'],
['2', '2.00'],
['3 (1)', '1000.000'],
[' 1000.0 (1)', '1000.000'],
['3', '1000.0'],
['3', '1000.00'],
])
+16 -19
View File
@@ -16,8 +16,8 @@ import odoo.modules.registry
from odoo import http
from odoo.exceptions import UserError
from odoo.http import content_disposition, request
from odoo.tools import float_repr, lazy_property, osutil, pycompat
from odoo.tools.misc import xlsxwriter
from odoo.tools import lazy_property, osutil, pycompat
from odoo.tools.misc import xlsxwriter, get_lang
from odoo.tools.translate import _
@@ -183,6 +183,11 @@ class ExportXlsxWriter:
self.datetime_style = self.workbook.add_format({'text_wrap': True, 'num_format': 'yyyy-mm-dd hh:mm:ss'})
self.worksheet = self.workbook.add_worksheet()
self.value = False
decimal_separator = get_lang(request.env).decimal_point
self.float_format = f'0{decimal_separator}00'
decimal_places = [res['decimal_places'] for res in
request.env['res.currency'].search_read([], ['decimal_places'])]
self.monetary_format = f'0{decimal_separator}{max(decimal_places or [2]) * "0"}'
if row_count > self.worksheet.xls_rowmax:
raise UserError(_('There are too many rows (%s rows, limit: %s) to export as Excel 2007-2013 (.xlsx) format. Consider splitting the export.') % (row_count, self.worksheet.xls_rowmax))
@@ -230,6 +235,8 @@ class ExportXlsxWriter:
cell_style = self.datetime_style
elif isinstance(cell_value, datetime.date):
cell_style = self.date_style
elif isinstance(cell_value, float):
cell_style.set_num_format(self.float_format)
self.write(row, column, cell_value, cell_style)
@@ -264,26 +271,16 @@ class GroupExportXlsxWriter(ExportXlsxWriter):
label = '%s%s (%s)' % (' ' * group_depth, label, group.count)
self.write(row, column, label, self.header_bold_style)
if any(f.get('type') == 'monetary' for f in self.fields[1:]):
decimal_places = [res['decimal_places'] for res in group._model.env['res.currency'].search_read([], ['decimal_places'])]
decimal_places = max(decimal_places) if decimal_places else 2
for field in self.fields[1:]: # No aggregates allowed in the first column because of the group title
column += 1
aggregated_value = aggregates.get(field['name'])
# Float fields may not be displayed properly because of float
# representation issue with non stored fields or with values
# that, even stored, cannot be rounded properly and it is not
# acceptable to display useless digits (i.e. monetary)
#
# non stored field -> we force 2 digits
# stored monetary -> we force max digits of installed currencies
if isinstance(aggregated_value, float):
if field.get('type') == 'monetary':
aggregated_value = float_repr(aggregated_value, decimal_places)
elif not field.get('store'):
aggregated_value = float_repr(aggregated_value, 2)
self.write(row, column, str(aggregated_value if aggregated_value is not None else ''), self.header_bold_style)
if field.get('type') == 'monetary':
self.header_bold_style.set_num_format(self.monetary_format)
elif field.get('type') == 'float':
self.header_bold_style.set_num_format(self.float_format)
else:
aggregated_value = str(aggregated_value if aggregated_value is not None else '')
self.write(row, column, aggregated_value, self.header_bold_style)
return row + 1, 0