diff --git a/mis_builder/models/mis_report_instance.py b/mis_builder/models/mis_report_instance.py index 070ffe1a5..bbb9268e5 100644 --- a/mis_builder/models/mis_report_instance.py +++ b/mis_builder/models/mis_report_instance.py @@ -793,7 +793,6 @@ def print_pdf(self): ) def export_xls(self): - self.ensure_one() return self.env.ref("mis_builder.xls_export").report_action( self, data=dict(dummy=True) ) # required to propagate context @@ -1023,3 +1022,10 @@ def _compute_user_can_edit_annotation(self): self.user_can_edit_annotation = self.env.user.has_group( "mis_builder.group_edit_annotation" ) + + def _get_xlsx_report_name(self): + self.ensure_one() + return "{} - {}".format( + self.name, + ", ".join([a.name for a in self.query_company_ids]), + ) diff --git a/mis_builder/report/mis_report_instance_xlsx.py b/mis_builder/report/mis_report_instance_xlsx.py index 26309918e..78e00fc98 100644 --- a/mis_builder/report/mis_report_instance_xlsx.py +++ b/mis_builder/report/mis_report_instance_xlsx.py @@ -34,17 +34,18 @@ def _mis_builder_add_annotation(self, sheet, cell, row_pos, col_pos, notes): if cell and (annotation := notes.get(cell.cell_id, {}).get("text")): sheet.write_comment(row_pos, col_pos, annotation) - def generate_xlsx_report(self, workbook, data, objects): + def _get_worksheet_name(self, mis_instance): + return mis_instance._get_xlsx_report_name()[:31] + + def _generate_xlsx_one_report(self, workbook, mis_instance): # get the computed result of the report - matrix = objects._compute_matrix() - notes = objects.get_notes_by_cell_id() + matrix = mis_instance._compute_matrix() + notes = mis_instance.get_notes_by_cell_id() style_obj = self.env["mis.report.style"] # create worksheet - report_name = "{} - {}".format( - objects[0].name, ", ".join([a.name for a in objects[0].query_company_ids]) - ) - sheet = workbook.add_worksheet(report_name[:31]) + worksheet_name = self._get_worksheet_name(mis_instance) + sheet = workbook.add_worksheet(worksheet_name) row_pos = 0 col_pos = 0 # width of the labels column @@ -57,13 +58,14 @@ def generate_xlsx_report(self, workbook, data, objects): header_format = workbook.add_format( {"bold": True, "align": "center", "bg_color": "#F0EEEE"} ) + report_name = mis_instance._get_xlsx_report_name() sheet.write(row_pos, 0, report_name, bold) row_pos += 2 # filters - filter_descriptions = objects.get_filter_descriptions() + filter_descriptions = mis_instance.get_filter_descriptions() if filter_descriptions: - for filter_description in objects.get_filter_descriptions(): + for filter_description in mis_instance.get_filter_descriptions(): sheet.write(row_pos, 0, filter_description) row_pos += 1 row_pos += 1 @@ -88,7 +90,9 @@ def generate_xlsx_report(self, workbook, data, objects): else: sheet.write(row_pos, col_pos, label, header_format) col_width[col_pos] = max( - col_width[col_pos], len(col.label or ""), len(col.description or "") + col_width[col_pos], + len(col.label or ""), + len(col.description or ""), ) col_pos += col.colspan row_pos += 1 @@ -184,3 +188,9 @@ def generate_xlsx_report(self, workbook, data, objects): min_col_pos = min(col_width.keys()) max_col_pos = max(col_width.keys()) sheet.set_column(min_col_pos, max_col_pos, data_col_width * COL_WIDTH) + + return sheet + + def generate_xlsx_report(self, workbook, data, objects): + for instance in objects: + self._generate_xlsx_one_report(workbook, instance) diff --git a/mis_builder/report/mis_report_instance_xlsx.xml b/mis_builder/report/mis_report_instance_xlsx.xml index 6059075b3..d6427196c 100644 --- a/mis_builder/report/mis_report_instance_xlsx.xml +++ b/mis_builder/report/mis_report_instance_xlsx.xml @@ -8,4 +8,15 @@ xlsx mis_report_instance + + + Export XLS + + + list + code + + action = records.export_xls() + + diff --git a/mis_builder/tests/common.py b/mis_builder/tests/common.py index 5725cfd6c..f5547da8d 100644 --- a/mis_builder/tests/common.py +++ b/mis_builder/tests/common.py @@ -1,10 +1,17 @@ # Copyright 2017 ACSONE SA/NV () # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). - import doctest +import io +import logging + +import openpyxl +from odoo import api from odoo.tests import BaseCase, tagged +_logger = logging.getLogger(__name__) +_test_logger = logging.getLogger("odoo.tests") + def _zip(iter1, iter2): i = 0 @@ -63,3 +70,35 @@ def load_tests(loader, tests, ignore): return tests return load_tests + + +def try_xlsx_report( + cr, uid, rname, ids, data=None, context=None, our_module=None, report_type=None +): + """Try to render a report with contents of ids + + This function should also check for common pitfalls of reports. + + this is inspired from the odoo.tools.test_reports.try_report specialiazed to xlsx + """ + if context is None: + context = {} + _test_logger.info(" - Trying %s.create(%r)", rname, ids) + + env = api.Environment(cr, uid, context) + + res_data, res_format = env["ir.actions.report"]._render(rname, ids, data=data) + + if not res_data: + raise ValueError(f"Report {rname} produced an empty result!") + + if res_format == "xlsx": + return openpyxl.load_workbook(io.BytesIO(res_data), data_only=True) + else: + _logger.warning( + 'Report %s produced a "%s" chunk, cannot examine it', rname, res_format + ) + return False + + _test_logger.info(" + Report %s produced correctly.", rname) + return True diff --git a/mis_builder/tests/test_mis_report_instance.py b/mis_builder/tests/test_mis_report_instance.py index 37acf04e7..59bdb2b65 100644 --- a/mis_builder/tests/test_mis_report_instance.py +++ b/mis_builder/tests/test_mis_report_instance.py @@ -6,6 +6,7 @@ from ..models.accounting_none import AccountingNone from ..models.mis_report import TYPE_STR, SubKPITupleLengthError, SubKPIUnknownTypeError +from .common import try_xlsx_report class TestMisReportInstance(common.HttpCase): @@ -355,6 +356,27 @@ def setUp(self): ) ) + # create a duplicate of first instance with different period + self.report_instance_4 = self.env["mis.report.instance"].create( + dict( + name="test instance", + report_id=self.report.id, + company_id=self.env.ref("base.main_company").id, + period_ids=[ + ( + 0, + 0, + dict( + name="p2", + mode="fix", + manual_date_from="2015-01-01", + manual_date_to="2015-12-31", + ), + ), + ], + ) + ) + def test_compute(self): matrix = self.report_instance._compute_matrix() for row in matrix.iter_rows(): @@ -564,19 +586,25 @@ def test_qweb(self): def test_xlsx(self): self.report_instance.export_xls() # get action - with self.assertLogs("odoo.tools.test_reports", level="WARNING") as log_catcher: - test_reports.try_report( - self.env.cr, - self.env.uid, - "mis_builder.mis_report_instance_xlsx", - [self.report_instance.id], - report_type="xlsx", - ) - self.assertIn( - 'Report mis_builder.mis_report_instance_xlsx produced a "xlsx" chunk, ' - "cannot examine it", - log_catcher.output[0], + excel_workbook = try_xlsx_report( + self.env.cr, + self.env.uid, + "mis_builder.mis_report_instance_xlsx", + [self.report_instance.id], + report_type="xlsx", + ) + self.assertEqual(len(excel_workbook.sheetnames), 1) + + def test_xlsx_multiple_instances(self): + self.report_instance.export_xls() # get action + excel_workbook = try_xlsx_report( + self.env.cr, + self.env.uid, + "mis_builder.mis_report_instance_xlsx", + [self.report_instance.id, self.report_instance_4.id], + report_type="xlsx", ) + self.assertEqual(len(excel_workbook.sheetnames), 2) def test_get_kpis_by_account_id(self): account_ids = (