diff --git a/README.md b/README.md index 8edc558..fb80ebb 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ addon | version | summary [report_wkhtmltopdf_param](report_wkhtmltopdf_param/) | 2.0.1.0.0| Add new parameters for a paper format to be used by wkhtmltopdf command as arguments. [report_qweb_pdf_watermark](report_qweb_pdf_watermark/) | 2.0.1.0.0| Add watermarks to your QWEB PDF reports [report_xlsx_helper](report_xlsx_helper/) | 2.0.1.0.0| Report xlsx helpers -[report_xlsx](report_xlsx/) | 2.0.1.0.2| Base module to create xlsx report +[report_xlsx](report_xlsx/) | 2.0.1.0.3| Base module to create xlsx report [bi_view_editor](bi_view_editor/) | 2.0.1.0.0| Graphical BI views builder for Odoo [report_qweb_encrypt](report_qweb_encrypt/) | 2.0.1.0.0| Allow to encrypt qweb pdfs [report_qweb_parameter](report_qweb_parameter/) | 2.0.1.0.0| Add new parameters for qweb templates in order to reduce field length and check minimal length diff --git a/report_xlsx/__manifest__.py b/report_xlsx/__manifest__.py index 624d829..c63b1e9 100644 --- a/report_xlsx/__manifest__.py +++ b/report_xlsx/__manifest__.py @@ -6,7 +6,7 @@ "author": "ACSONE SA/NV," "Creu Blanca," "Odoo Community Association (OCA)", "website": "https://gitlab.com/flectra-community/reporting-engine", "category": "Reporting", - "version": "2.0.1.0.2", + "version": "2.0.1.0.3", "development_status": "Production/Stable", "license": "AGPL-3", "external_dependencies": {"python": ["xlsxwriter", "xlrd"]}, diff --git a/report_xlsx/report/report_abstract_xlsx.py b/report_xlsx/report/report_abstract_xlsx.py index 09deb34..15cb4cd 100644 --- a/report_xlsx/report/report_abstract_xlsx.py +++ b/report_xlsx/report/report_abstract_xlsx.py @@ -2,6 +2,7 @@ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). import logging +import re from io import BytesIO from flectra import models @@ -10,6 +11,56 @@ _logger = logging.getLogger(__name__) try: import xlsxwriter + + class PatchedXlsxWorkbook(xlsxwriter.Workbook): + def _check_sheetname(self, sheetname, is_chartsheet=False): + """We want to avoid duplicated sheet names exceptions the same following + the same philosophy that Flectra implements overriding the main library + to avoid the 31 characters limit triming the strings before sending them + to the library. + + In some cases, there's not much control over this as the reports send + automated data and the potential exception is hidden underneath making it + hard to debug the original issue. Even so, different names can become the + same one as their strings are trimmed to those 31 character limit. + + This way, once we come across with a duplicated, we set that final 3 + characters with a sequence that we evaluate on the fly. So for instance: + + - 'Sheet name' will be 'Sheet name~01' + - The next 'Sheet name' will try to rename to 'Sheet name~01' as well and + then that will give us 'Sheet name~02'. + - And the next 'Sheet name' will try to rename to 'Sheet name~01' and then + to 'Sheet name~02' and finally it will be able to 'Sheet name~03'. + - An so on as many times as duplicated sheet names come to the workbook up + to 100 for each sheet name. We set such limit as we don't want to truncate + the strings too much and keeping in mind that this issue don't usually + ocurrs. + """ + try: + return super()._check_sheetname(sheetname, is_chartsheet=is_chartsheet) + except xlsxwriter.exceptions.DuplicateWorksheetName: + pattern = re.compile(r"~[0-9]{2}$") + duplicated_secuence = ( + re.search(pattern, sheetname) and int(sheetname[-2:]) or 0 + ) + # Only up to 100 duplicates + deduplicated_secuence = "~{:02d}".format(duplicated_secuence + 1) + if duplicated_secuence > 99: + raise xlsxwriter.exceptions.DuplicateWorksheetName + if duplicated_secuence: + sheetname = re.sub(pattern, deduplicated_secuence, sheetname) + elif len(sheetname) <= 28: + sheetname += deduplicated_secuence + else: + sheetname = sheetname[:28] + deduplicated_secuence + # Refeed the method until we get an unduplicated name + return self._check_sheetname(sheetname, is_chartsheet=is_chartsheet) + + # "Short string" + + xlsxwriter.Workbook = PatchedXlsxWorkbook + except ImportError: _logger.debug("Can not import xlsxwriter`.")