Merge PR #958 into 18.0

Signed-off-by pedrobaeza
This commit is contained in:
OCA-git-bot
2025-02-10 18:47:39 +00:00
25 changed files with 2396 additions and 0 deletions

View File

@@ -0,0 +1,127 @@
===================
Report xlsx helpers
===================
..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:df481003a65f02d5bf2edcc90f4238033b81c3fa5120209c2e36e25b1cdabb77
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Mature-brightgreen.png
:target: https://odoo-community.org/page/development-status
:alt: Mature
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Freporting--engine-lightgray.png?logo=github
:target: https://github.com/OCA/reporting-engine/tree/18.0/report_xlsx_helper
:alt: OCA/reporting-engine
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/reporting-engine-18-0/reporting-engine-18-0-report_xlsx_helper
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/reporting-engine&target_branch=18.0
:alt: Try me on Runboat
|badge1| |badge2| |badge3| |badge4| |badge5|
This module provides a set of tools to facilitate the creation of excel
reports with format xlsx.
**Table of contents**
.. contents::
:local:
Installation
============
This module requires report_xlsx version 13.0.1.0.0 or higher.
Usage
=====
In order to create an Excel report you can define a report of type
'xlsx' in a static or dynamic way:
- Static syntax: cf. ``account_move_line_report_xls`` for an example.
- Dynamic syntax: cf. ``report_xlsx_helper_demo`` for an example
The ``AbstractReportXlsx`` class contains a number of attributes and
methods to facilitate the creation excel reports in Odoo.
- Cell types
string, number, boolean, datetime.
- Cell formats
The predefined cell formats result in a consistent look and feel of
the Odoo Excel reports.
- Cell formulas
Cell formulas can be easily added with the help of the
``_rowcol_to_cell()`` method.
- Excel templates
It is possible to define Excel templates which can be adapted by
'inherited' modules. Download the ``account_move_line_report_xls``
module from http://apps.odoo.com as example.
- Excel with multiple sheets
Download the ``account_asset_management_xls`` module from
http://apps.odoo.com as example.
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/OCA/reporting-engine/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/reporting-engine/issues/new?body=module:%20report_xlsx_helper%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues.
Credits
=======
Authors
-------
* Noviat
Contributors
------------
- Luc De Meyer <luc.demeyer@noviat.com>
- Rattapong Chokmasermkul <rattapongc@ecosoft.co.th>
- Saran Lim. <saranl@ecosoft.co.th>
- `Sinerkia Innovación y Desarrollo S.L. <https://www.sinerkia.com>`__:
- Luis Pomar
Maintainers
-----------
This module is maintained by the OCA.
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
This module is part of the `OCA/reporting-engine <https://github.com/OCA/reporting-engine/tree/18.0/report_xlsx_helper>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View File

@@ -0,0 +1,3 @@
from . import controllers
from . import models
from . import report

View File

@@ -0,0 +1,14 @@
# Copyright 2009-2019 Noviat.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
"name": "Report xlsx helpers",
"author": "Noviat, Odoo Community Association (OCA)",
"website": "https://github.com/OCA/reporting-engine",
"category": "Reporting",
"version": "18.0.1.0.0",
"license": "AGPL-3",
"depends": ["report_xlsx"],
"development_status": "Mature",
"installable": True,
}

View File

@@ -0,0 +1 @@
from . import main

View File

@@ -0,0 +1,51 @@
# Copyright 2009-2018 Noviat.
# License AGPL-3.0 or later (https://www.gnuorg/licenses/agpl.html).
import json
from odoo.http import content_disposition, request, route
from odoo.addons.report_xlsx.controllers.main import ReportController
class ReportController(ReportController):
@route(
[
"/report/<converter>/<reportname>",
"/report/<converter>/<reportname>/<docids>",
],
type="http",
auth="user",
website=True,
)
def report_routes(self, reportname, docids=None, converter=None, **data):
report = request.env["ir.actions.report"]._get_report_from_name(reportname)
if converter == "xlsx" and not report:
context = dict(request.env.context)
if docids:
docids = [int(i) for i in docids.split(",")]
if data.get("options"):
data.update(json.loads(data.pop("options")))
if data.get("context"):
data["context"] = json.loads(data["context"])
context.update(data["context"])
context["report_name"] = reportname
xlsx = report.with_context(**context)._render_xlsx(
reportname, docids, data=data
)[0]
report_file = context.get("report_file")
if not report_file:
active_model = context.get("active_model", "export")
report_file = active_model.replace(".", "_")
xlsxhttpheaders = [
(
"Content-Type",
"application/vnd.openxmlformats-"
"officedocument.spreadsheetml.sheet",
),
("Content-Length", len(xlsx)),
("Content-Disposition", content_disposition(report_file + ".xlsx")),
]
return request.make_response(xlsx, headers=xlsxhttpheaders)
return super().report_routes(reportname, docids, converter, **data)

View File

@@ -0,0 +1,129 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * report_xlsx_helper
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 15.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2022-06-15 18:05+0000\n"
"Last-Translator: jabelchi <jabelchi@gmail.com>\n"
"Language-Team: none\n"
"Language: ca\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.3.2\n"
#. module: report_xlsx_helper
#. odoo-python
#: code:addons/report_xlsx_helper/report/report_xlsx_abstract.py:0
#, python-format
msgid ""
"%(__name__)s, _write_line : programming error detected while processing "
"col_specs_section %(col_specs_section)s, column %(col)s"
msgstr ""
"%(__name__)s, _write_line : error de programació detectat en processar "
"col_specs_section %(col_specs_section)s, columna %(col)s"
#. module: report_xlsx_helper
#. odoo-python
#: code:addons/report_xlsx_helper/models/ir_actions_report.py:0
#, python-format
msgid "%s model was not found"
msgstr "No s'ha trobat el model %s"
#. module: report_xlsx_helper
#. odoo-python
#: code:addons/report_xlsx_helper/report/report_xlsx_abstract.py:0
#, python-format
msgid ", cellvalue %s"
msgstr ""
#. module: report_xlsx_helper
#: model:ir.model,name:report_xlsx_helper.model_report_report_xlsx_abstract
#, fuzzy
msgid "Abstract XLSX Report"
msgstr "Informe XLSX abstracte"
#. module: report_xlsx_helper
#. odoo-python
#: code:addons/report_xlsx_helper/report/report_xlsx_abstract.py:0
#, python-format
msgid ""
"Programming Error:\n"
"\n"
"Excel Sheet name '%(name)s' contains unsupported special characters: "
"'%(special_chars)s'."
msgstr ""
"Error de programació:\n"
"\n"
"El full Excel amb nom '%(name)s' contè caràcters especials no soportats: "
"'%(special_chars)s'."
#. module: report_xlsx_helper
#. odoo-python
#: code:addons/report_xlsx_helper/report/report_xlsx_abstract.py:0
#, python-format
msgid ""
"Programming Error:\n"
"\n"
"Excel Sheet name '%(name)s' should not exceed %(max_chars)s characters."
msgstr ""
"Error de programació:\n"
"\n"
"El full d'Excel de nom '%(name)s' no hauria d'excedir els %(max_chars)s "
"caràcters."
#. module: report_xlsx_helper
#. odoo-python
#: code:addons/report_xlsx_helper/report/report_xlsx_abstract.py:0
#, python-format
msgid ""
"Programming Error:\n"
"\n"
"The '%s' column is not defined in the worksheet column specifications."
msgstr ""
"Error de programació:\n"
"\n"
"La columna '%s' no està definida a les especificacions de columna del full "
"de càlcul."
#. module: report_xlsx_helper
#. odoo-python
#: code:addons/report_xlsx_helper/report/report_xlsx_abstract.py:0
#, python-format
msgid ""
"Programming Error:\n"
"\n"
"The '%s' column is not defined the worksheet column specifications."
msgstr ""
"Error de programació:\n"
"\n"
"La columna '%s' no està definida a les especificacions de columna del full "
"de càlcul."
#. module: report_xlsx_helper
#. odoo-python
#: code:addons/report_xlsx_helper/report/report_xlsx_abstract.py:0
#, python-format
msgid ""
"Programming Error:\n"
"\n"
"The 'title' parameter is mandatory when calling the '_write_ws_title' method."
msgstr ""
"Error de programació:\n"
"\n"
"El paràmetre 'títol' és obligatori si es crida el mètode '_write_ws_title'."
#. module: report_xlsx_helper
#: model:ir.model,name:report_xlsx_helper.model_ir_actions_report
msgid "Report Action"
msgstr "Acció d'informe"
#. module: report_xlsx_helper
#: model:ir.model,name:report_xlsx_helper.model_report_report_xlsx_helper_test_partner_xlsx
#, fuzzy
msgid "Test Partner XLSX Report"
msgstr "Informe XLSX partner prova"

View File

@@ -0,0 +1,165 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * report_xlsx_helper
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 14.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2023-10-15 16:37+0000\n"
"Last-Translator: Ivorra78 <informatica@totmaterial.es>\n"
"Language-Team: none\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.17\n"
#. module: report_xlsx_helper
#. odoo-python
#: code:addons/report_xlsx_helper/report/report_xlsx_abstract.py:0
#, python-format
msgid ""
"%(__name__)s, _write_line : programming error detected while processing "
"col_specs_section %(col_specs_section)s, column %(col)s"
msgstr ""
"%(__name__)s, _write_line : error de programación detectado al procesar "
"col_specs_section %(col_specs_section)s, columna %(col)s"
#. module: report_xlsx_helper
#. odoo-python
#: code:addons/report_xlsx_helper/models/ir_actions_report.py:0
#, python-format
msgid "%s model was not found"
msgstr "%s modelo no fue encontrado"
#. module: report_xlsx_helper
#. odoo-python
#: code:addons/report_xlsx_helper/report/report_xlsx_abstract.py:0
#, python-format
msgid ", cellvalue %s"
msgstr ", valor de celda %s"
#. module: report_xlsx_helper
#: model:ir.model,name:report_xlsx_helper.model_report_report_xlsx_abstract
msgid "Abstract XLSX Report"
msgstr "Informe XLSX en abstracto"
#. module: report_xlsx_helper
#. odoo-python
#: code:addons/report_xlsx_helper/report/report_xlsx_abstract.py:0
#, python-format
msgid ""
"Programming Error:\n"
"\n"
"Excel Sheet name '%(name)s' contains unsupported special characters: "
"'%(special_chars)s'."
msgstr ""
"Error de programación:\n"
"\n"
"El nombre de la hoja Excel '%(name)s' contiene caracteres especiales no "
"admitidos: '%(special_chars)s'."
#. module: report_xlsx_helper
#. odoo-python
#: code:addons/report_xlsx_helper/report/report_xlsx_abstract.py:0
#, python-format
msgid ""
"Programming Error:\n"
"\n"
"Excel Sheet name '%(name)s' should not exceed %(max_chars)s characters."
msgstr ""
"Error de programación:\n"
"\n"
"El nombre de la hoja Excel '%(name)s' no debe exceder %(max_chars)s "
"caracteres."
#. module: report_xlsx_helper
#. odoo-python
#: code:addons/report_xlsx_helper/report/report_xlsx_abstract.py:0
#, python-format
msgid ""
"Programming Error:\n"
"\n"
"The '%s' column is not defined in the worksheet column specifications."
msgstr ""
"Error de programación:\n"
"\n"
"La columna '%s' no está definida en las especificaciones de columna de la "
"hoja de cálculo."
#. module: report_xlsx_helper
#. odoo-python
#: code:addons/report_xlsx_helper/report/report_xlsx_abstract.py:0
#, python-format
msgid ""
"Programming Error:\n"
"\n"
"The '%s' column is not defined the worksheet column specifications."
msgstr ""
"Error de programación:\n"
"\n"
"La columna '%s' no está definida en las especificaciones de columna de la "
"hoja de cálculo."
#. module: report_xlsx_helper
#. odoo-python
#: code:addons/report_xlsx_helper/report/report_xlsx_abstract.py:0
#, python-format
msgid ""
"Programming Error:\n"
"\n"
"The 'title' parameter is mandatory when calling the '_write_ws_title' method."
msgstr ""
"Error de programación:\n"
"\n"
"El parámetro 'title' es obligatorio al llamar al método '_write_ws_title'."
#. module: report_xlsx_helper
#: model:ir.model,name:report_xlsx_helper.model_ir_actions_report
msgid "Report Action"
msgstr "Informar Acción"
#. module: report_xlsx_helper
#: model:ir.model,name:report_xlsx_helper.model_report_report_xlsx_helper_test_partner_xlsx
msgid "Test Partner XLSX Report"
msgstr "Informe XLSX del socio de prueba"
#, python-format
#~ msgid ""
#~ "%s, _write_line : programming error detected while processing "
#~ "col_specs_section %s, column %s"
#~ msgstr ""
#~ "%s, _write_line : detectado error procesando col_specs_section %s, column "
#~ "%s"
#~ msgid "Display Name"
#~ msgstr "Mostrar Nombre"
#~ msgid "ID"
#~ msgstr "ID (identificación)"
#~ msgid "Last Modified on"
#~ msgstr "Última Modificación el"
#, python-format
#~ msgid ""
#~ "Programming Error:\n"
#~ "\n"
#~ "Excel Sheet name '%s' contains unsupported special characters: '%s'."
#~ msgstr ""
#~ "Error de programación:\n"
#~ "\n"
#~ "El nombre de la hoja Excel '%s' contiene caracteres especiales no "
#~ "admitidos: '%s'."
#, python-format
#~ msgid ""
#~ "Programming Error:\n"
#~ "\n"
#~ "Excel Sheet name '%s' should not exceed %s characters."
#~ msgstr ""
#~ "Error de programación:\n"
#~ "\n"
#~ "El nombre de la hoja Excel '%s' no debe superar los %s caracteres."

View File

@@ -0,0 +1,103 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * report_xlsx_helper
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 17.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
#. module: report_xlsx_helper
#. odoo-python
#: code:addons/report_xlsx_helper/report/report_xlsx_abstract.py:0
#, python-format
msgid ""
"%(__name__)s, _write_line : programming error detected while processing "
"col_specs_section %(col_specs_section)s, column %(col)s"
msgstr ""
#. module: report_xlsx_helper
#. odoo-python
#: code:addons/report_xlsx_helper/models/ir_actions_report.py:0
#, python-format
msgid "%s model was not found"
msgstr ""
#. module: report_xlsx_helper
#. odoo-python
#: code:addons/report_xlsx_helper/report/report_xlsx_abstract.py:0
#, python-format
msgid ", cellvalue %s"
msgstr ""
#. module: report_xlsx_helper
#: model:ir.model,name:report_xlsx_helper.model_report_report_xlsx_abstract
msgid "Abstract XLSX Report"
msgstr ""
#. module: report_xlsx_helper
#. odoo-python
#: code:addons/report_xlsx_helper/report/report_xlsx_abstract.py:0
#, python-format
msgid ""
"Programming Error:\n"
"\n"
"Excel Sheet name '%(name)s' contains unsupported special characters: '%(special_chars)s'."
msgstr ""
#. module: report_xlsx_helper
#. odoo-python
#: code:addons/report_xlsx_helper/report/report_xlsx_abstract.py:0
#, python-format
msgid ""
"Programming Error:\n"
"\n"
"Excel Sheet name '%(name)s' should not exceed %(max_chars)s characters."
msgstr ""
#. module: report_xlsx_helper
#. odoo-python
#: code:addons/report_xlsx_helper/report/report_xlsx_abstract.py:0
#, python-format
msgid ""
"Programming Error:\n"
"\n"
"The '%s' column is not defined in the worksheet column specifications."
msgstr ""
#. module: report_xlsx_helper
#. odoo-python
#: code:addons/report_xlsx_helper/report/report_xlsx_abstract.py:0
#, python-format
msgid ""
"Programming Error:\n"
"\n"
"The '%s' column is not defined the worksheet column specifications."
msgstr ""
#. module: report_xlsx_helper
#. odoo-python
#: code:addons/report_xlsx_helper/report/report_xlsx_abstract.py:0
#, python-format
msgid ""
"Programming Error:\n"
"\n"
"The 'title' parameter is mandatory when calling the '_write_ws_title' method."
msgstr ""
#. module: report_xlsx_helper
#: model:ir.model,name:report_xlsx_helper.model_ir_actions_report
msgid "Report Action"
msgstr ""
#. module: report_xlsx_helper
#: model:ir.model,name:report_xlsx_helper.model_report_report_xlsx_helper_test_partner_xlsx
msgid "Test Partner XLSX Report"
msgstr ""

View File

@@ -0,0 +1,127 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * report_xlsx_helper
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 17.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2024-02-12 10:46+0000\n"
"Last-Translator: mymage <stefano.consolaro@mymage.it>\n"
"Language-Team: none\n"
"Language: it\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.17\n"
#. module: report_xlsx_helper
#. odoo-python
#: code:addons/report_xlsx_helper/report/report_xlsx_abstract.py:0
#, python-format
msgid ""
"%(__name__)s, _write_line : programming error detected while processing "
"col_specs_section %(col_specs_section)s, column %(col)s"
msgstr ""
"%(__name__)s, _write_line : rilevato errore di programmazione "
"nell'elaborazione di col_specs_section %(col_specs_section)s, colonna %(col)s"
#. module: report_xlsx_helper
#. odoo-python
#: code:addons/report_xlsx_helper/models/ir_actions_report.py:0
#, python-format
msgid "%s model was not found"
msgstr "Il modello %s non è stato trovato"
#. module: report_xlsx_helper
#. odoo-python
#: code:addons/report_xlsx_helper/report/report_xlsx_abstract.py:0
#, python-format
msgid ", cellvalue %s"
msgstr ", valore cella %s"
#. module: report_xlsx_helper
#: model:ir.model,name:report_xlsx_helper.model_report_report_xlsx_abstract
msgid "Abstract XLSX Report"
msgstr "Report XLSX astratto"
#. module: report_xlsx_helper
#. odoo-python
#: code:addons/report_xlsx_helper/report/report_xlsx_abstract.py:0
#, python-format
msgid ""
"Programming Error:\n"
"\n"
"Excel Sheet name '%(name)s' contains unsupported special characters: '%(special_chars)s'."
msgstr ""
"Errore programmazione:\n"
"\n"
"Il foglio Excel nome '%(name)s' contene caratteri non supportati: "
"'%(special_chars)s'."
#. module: report_xlsx_helper
#. odoo-python
#: code:addons/report_xlsx_helper/report/report_xlsx_abstract.py:0
#, python-format
msgid ""
"Programming Error:\n"
"\n"
"Excel Sheet name '%(name)s' should not exceed %(max_chars)s characters."
msgstr ""
"Errore programmazione:\n"
"\n"
"Il foglio Excel nome '%(name)s' non dovrebbe superare i %(max_chars)s "
"caratteri."
#. module: report_xlsx_helper
#. odoo-python
#: code:addons/report_xlsx_helper/report/report_xlsx_abstract.py:0
#, python-format
msgid ""
"Programming Error:\n"
"\n"
"The '%s' column is not defined in the worksheet column specifications."
msgstr ""
"Errore programmazione:\n"
"\n"
"La colonna '%s' non è definita nelle specifiche delle colonne del foglio di "
"lavoro."
#. module: report_xlsx_helper
#. odoo-python
#: code:addons/report_xlsx_helper/report/report_xlsx_abstract.py:0
#, python-format
msgid ""
"Programming Error:\n"
"\n"
"The '%s' column is not defined the worksheet column specifications."
msgstr ""
"Errore programmazione:\n"
"\n"
"La colonna '%s' non è definita nelle specifiche delle colonne del foglio di "
"lavoro."
#. module: report_xlsx_helper
#. odoo-python
#: code:addons/report_xlsx_helper/report/report_xlsx_abstract.py:0
#, python-format
msgid ""
"Programming Error:\n"
"\n"
"The 'title' parameter is mandatory when calling the '_write_ws_title' method."
msgstr ""
"Errore programmazione:\n"
"\n"
"Il parametro 'title' è obbligatorio quando si chiama il metodo "
"'_write_ws_title'."
#. module: report_xlsx_helper
#: model:ir.model,name:report_xlsx_helper.model_ir_actions_report
msgid "Report Action"
msgstr "Azione resoconto"
#. module: report_xlsx_helper
#: model:ir.model,name:report_xlsx_helper.model_report_report_xlsx_helper_test_partner_xlsx
msgid "Test Partner XLSX Report"
msgstr "Test resoconto XLSX partner"

View File

@@ -0,0 +1,102 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * report_xlsx_helper
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 17.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: report_xlsx_helper
#. odoo-python
#: code:addons/report_xlsx_helper/report/report_xlsx_abstract.py:0
#, python-format
msgid ""
"%(__name__)s, _write_line : programming error detected while processing "
"col_specs_section %(col_specs_section)s, column %(col)s"
msgstr ""
#. module: report_xlsx_helper
#. odoo-python
#: code:addons/report_xlsx_helper/models/ir_actions_report.py:0
#, python-format
msgid "%s model was not found"
msgstr ""
#. module: report_xlsx_helper
#. odoo-python
#: code:addons/report_xlsx_helper/report/report_xlsx_abstract.py:0
#, python-format
msgid ", cellvalue %s"
msgstr ""
#. module: report_xlsx_helper
#: model:ir.model,name:report_xlsx_helper.model_report_report_xlsx_abstract
msgid "Abstract XLSX Report"
msgstr ""
#. module: report_xlsx_helper
#. odoo-python
#: code:addons/report_xlsx_helper/report/report_xlsx_abstract.py:0
#, python-format
msgid ""
"Programming Error:\n"
"\n"
"Excel Sheet name '%(name)s' contains unsupported special characters: '%(special_chars)s'."
msgstr ""
#. module: report_xlsx_helper
#. odoo-python
#: code:addons/report_xlsx_helper/report/report_xlsx_abstract.py:0
#, python-format
msgid ""
"Programming Error:\n"
"\n"
"Excel Sheet name '%(name)s' should not exceed %(max_chars)s characters."
msgstr ""
#. module: report_xlsx_helper
#. odoo-python
#: code:addons/report_xlsx_helper/report/report_xlsx_abstract.py:0
#, python-format
msgid ""
"Programming Error:\n"
"\n"
"The '%s' column is not defined in the worksheet column specifications."
msgstr ""
#. module: report_xlsx_helper
#. odoo-python
#: code:addons/report_xlsx_helper/report/report_xlsx_abstract.py:0
#, python-format
msgid ""
"Programming Error:\n"
"\n"
"The '%s' column is not defined the worksheet column specifications."
msgstr ""
#. module: report_xlsx_helper
#. odoo-python
#: code:addons/report_xlsx_helper/report/report_xlsx_abstract.py:0
#, python-format
msgid ""
"Programming Error:\n"
"\n"
"The 'title' parameter is mandatory when calling the '_write_ws_title' method."
msgstr ""
#. module: report_xlsx_helper
#: model:ir.model,name:report_xlsx_helper.model_ir_actions_report
msgid "Report Action"
msgstr ""
#. module: report_xlsx_helper
#: model:ir.model,name:report_xlsx_helper.model_report_report_xlsx_helper_test_partner_xlsx
msgid "Test Partner XLSX Report"
msgstr ""

View File

@@ -0,0 +1 @@
from . import ir_actions_report

View File

@@ -0,0 +1,19 @@
# Copyright 2009-2018 Noviat.
# License AGPL-3.0 or later (https://www.gnuorg/licenses/agpl.html).
from odoo import _, api, models
from odoo.exceptions import UserError
class IrActionsReport(models.Model):
_inherit = "ir.actions.report"
@api.model
def _render_xlsx(self, report_ref, docids, data):
if not self and self.env.context.get("report_name"):
report_model_name = "report.{}".format(self.env.context["report_name"])
report_model = self.env.get(report_model_name)
if report_model is None:
raise UserError(_("%s model was not found") % report_model_name)
return report_model.create_xlsx_report(docids, data)
return super()._render_xlsx(report_ref, docids, data)

View File

@@ -0,0 +1,3 @@
[build-system]
requires = ["whool"]
build-backend = "whool.buildapi"

View File

@@ -0,0 +1,8 @@
- Luc De Meyer \<<luc.demeyer@noviat.com>\>
- Rattapong Chokmasermkul \<<rattapongc@ecosoft.co.th>\>
- Saran Lim. \<<saranl@ecosoft.co.th>\>
- [Sinerkia Innovación y Desarrollo S.L.](https://www.sinerkia.com):
- Luis Pomar

View File

@@ -0,0 +1,2 @@
This module provides a set of tools to facilitate the creation of excel
reports with format xlsx.

View File

@@ -0,0 +1 @@
This module requires report_xlsx version 13.0.1.0.0 or higher.

View File

@@ -0,0 +1,33 @@
In order to create an Excel report you can define a report of type
'xlsx' in a static or dynamic way:
- Static syntax: cf. `account_move_line_report_xls` for an example.
- Dynamic syntax: cf. `report_xlsx_helper_demo` for an example
The `AbstractReportXlsx` class contains a number of attributes and
methods to facilitate the creation excel reports in Odoo.
- Cell types
string, number, boolean, datetime.
- Cell formats
The predefined cell formats result in a consistent look and feel of
the Odoo Excel reports.
- Cell formulas
Cell formulas can be easily added with the help of the
`_rowcol_to_cell()` method.
- Excel templates
It is possible to define Excel templates which can be adapted by
'inherited' modules. Download the `account_move_line_report_xls`
module from <http://apps.odoo.com> as example.
- Excel with multiple sheets
Download the `account_asset_management_xls` module from
<http://apps.odoo.com> as example.

View File

@@ -0,0 +1,3 @@
from . import report_xlsx_format
from . import report_xlsx_abstract
from . import test_partner_report_xlsx

View File

@@ -0,0 +1,770 @@
# Copyright 2009-2018 Noviat.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import re
from datetime import date, datetime
from types import CodeType
from xlsxwriter.utility import xl_rowcol_to_cell
from odoo import _, fields, models
from odoo.exceptions import UserError
from .report_xlsx_format import FORMATS, XLS_HEADERS
class ReportXlsxAbstract(models.AbstractModel):
_inherit = "report.report_xlsx.abstract"
def generate_xlsx_report(self, workbook, data, objects):
self._define_formats(workbook)
for ws_params in self._get_ws_params(workbook, data, objects):
ws_name = ws_params.get("ws_name")
ws_name = self._check_ws_name(ws_name)
ws = workbook.add_worksheet(ws_name)
generate_ws_method = getattr(self, ws_params["generate_ws_method"])
generate_ws_method(workbook, ws, ws_params, data, objects)
def _check_ws_name(self, name, sanitize=True):
pattern = re.compile(r"[/\\*\[\]:?]") # invalid characters: /\*[]:?
max_chars = 31
if sanitize:
# we could drop these two lines since a similar
# sanitize is done in tools.misc PatchedXlsxWorkbook
name = pattern.sub("", name)
name = name[:max_chars]
else:
if len(name) > max_chars:
raise UserError(
_(
"Programming Error:\n\n"
"Excel Sheet name '%(name)s' should not exceed %(max_chars)s "
"characters."
)
% {"name": name, "max_chars": max_chars}
)
special_chars = pattern.findall(name)
if special_chars:
raise UserError(
_(
"Programming Error:\n\n"
"Excel Sheet name '%(name)s' contains unsupported special "
"characters: '%(special_chars)s'."
)
% {"name": name, "special_chars": special_chars}
)
return name
def _get_ws_params(self, workbook, data, objects):
"""
Return list of dictionaries with parameters for the
worksheets.
Keywords:
- 'generate_ws_method': mandatory
- 'ws_name': name of the worksheet
- 'title': title of the worksheet
- 'wanted_list': list of column names
- 'col_specs': cf. XXX
The 'generate_ws_method' must be present in your report
and contain the logic to generate the content of the worksheet.
"""
return []
def _define_xls_headers(self, workbook):
"""
Predefined worksheet headers/footers.
"""
hf_params = {
"font_size": 8,
"font_style": "I", # B: Bold, I: Italic, U: Underline
}
XLS_HEADERS["xls_headers"] = {"standard": ""}
report_date = fields.Datetime.context_timestamp(
self.env.user, datetime.now()
).strftime("%Y-%m-%d %H:%M")
XLS_HEADERS["xls_footers"] = {
"standard": (
"&L&%(font_size)s&%(font_style)s"
+ report_date
+ "&R&%(font_size)s&%(font_style)s&P / &N"
)
% hf_params
}
def _define_formats(self, workbook):
"""
This section contains a number of pre-defined formats.
It is recommended to use these in order to have a
consistent look & feel between your XLSX reports.
"""
self._define_xls_headers(workbook)
border_grey = "#D3D3D3"
border = {"border": True, "border_color": border_grey}
theader = dict(border, bold=True)
bg_grey = "#CCCCCC"
bg_yellow = "#FFFFCC"
bg_blue = "#CCFFFF"
num_format = "#,##0.00"
num_format_conditional = f"{num_format};[Red]-{num_format};{num_format}"
pct_format = "#,##0.00%"
pct_format_conditional = f"{pct_format};[Red]-{pct_format};{pct_format}"
int_format = "#,##0"
int_format_conditional = f"{int_format};[Red]-{int_format};{int_format}"
date_format = "YYYY-MM-DD"
theader_grey = dict(theader, bg_color=bg_grey)
theader_yellow = dict(theader, bg_color=bg_yellow)
theader_blue = dict(theader, bg_color=bg_blue)
# format for worksheet title
FORMATS["format_ws_title"] = workbook.add_format(
{"bold": True, "font_size": 14}
)
# no border formats
FORMATS["format_left"] = workbook.add_format({"align": "left"})
FORMATS["format_center"] = workbook.add_format({"align": "center"})
FORMATS["format_right"] = workbook.add_format({"align": "right"})
FORMATS["format_amount_left"] = workbook.add_format(
{"align": "left", "num_format": num_format}
)
FORMATS["format_amount_center"] = workbook.add_format(
{"align": "center", "num_format": num_format}
)
FORMATS["format_amount_right"] = workbook.add_format(
{"align": "right", "num_format": num_format}
)
FORMATS["format_amount_conditional_left"] = workbook.add_format(
{"align": "left", "num_format": num_format_conditional}
)
FORMATS["format_amount_conditional_center"] = workbook.add_format(
{"align": "center", "num_format": num_format_conditional}
)
FORMATS["format_amount_conditional_right"] = workbook.add_format(
{"align": "right", "num_format": num_format_conditional}
)
FORMATS["format_percent_left"] = workbook.add_format(
{"align": "left", "num_format": pct_format}
)
FORMATS["format_percent_center"] = workbook.add_format(
{"align": "center", "num_format": pct_format}
)
FORMATS["format_percent_right"] = workbook.add_format(
{"align": "right", "num_format": pct_format}
)
FORMATS["format_percent_conditional_left"] = workbook.add_format(
{"align": "left", "num_format": pct_format_conditional}
)
FORMATS["format_percent_conditional_center"] = workbook.add_format(
{"align": "center", "num_format": pct_format_conditional}
)
FORMATS["format_percent_conditional_right"] = workbook.add_format(
{"align": "right", "num_format": pct_format_conditional}
)
FORMATS["format_integer_left"] = workbook.add_format(
{"align": "left", "num_format": int_format}
)
FORMATS["format_integer_center"] = workbook.add_format(
{"align": "center", "num_format": int_format}
)
FORMATS["format_integer_right"] = workbook.add_format(
{"align": "right", "num_format": int_format}
)
FORMATS["format_integer_conditional_left"] = workbook.add_format(
{"align": "right", "num_format": int_format_conditional}
)
FORMATS["format_integer_conditional_center"] = workbook.add_format(
{"align": "center", "num_format": int_format_conditional}
)
FORMATS["format_integer_conditional_right"] = workbook.add_format(
{"align": "right", "num_format": int_format_conditional}
)
FORMATS["format_date_left"] = workbook.add_format(
{"align": "left", "num_format": date_format}
)
FORMATS["format_date_center"] = workbook.add_format(
{"align": "center", "num_format": date_format}
)
FORMATS["format_date_right"] = workbook.add_format(
{"align": "right", "num_format": date_format}
)
FORMATS["format_left_bold"] = workbook.add_format(
{"align": "left", "bold": True}
)
FORMATS["format_center_bold"] = workbook.add_format(
{"align": "center", "bold": True}
)
FORMATS["format_right_bold"] = workbook.add_format(
{"align": "right", "bold": True}
)
FORMATS["format_amount_left_bold"] = workbook.add_format(
{"align": "left", "bold": True, "num_format": num_format}
)
FORMATS["format_amount_center_bold"] = workbook.add_format(
{"align": "center", "bold": True, "num_format": num_format}
)
FORMATS["format_amount_right_bold"] = workbook.add_format(
{"align": "right", "bold": True, "num_format": num_format}
)
FORMATS["format_amount_conditional_left_bold"] = workbook.add_format(
{"align": "left", "bold": True, "num_format": num_format_conditional}
)
FORMATS["format_amount_conditional_center_bold"] = workbook.add_format(
{"align": "center", "bold": True, "num_format": num_format_conditional}
)
FORMATS["format_amount_conditional_right_bold"] = workbook.add_format(
{"align": "right", "bold": True, "num_format": num_format_conditional}
)
FORMATS["format_percent_left_bold"] = workbook.add_format(
{"align": "left", "bold": True, "num_format": pct_format}
)
FORMATS["format_percent_center_bold"] = workbook.add_format(
{"align": "center", "bold": True, "num_format": pct_format}
)
FORMATS["format_percent_right_bold"] = workbook.add_format(
{"align": "right", "bold": True, "num_format": pct_format}
)
FORMATS["format_percent_conditional_left_bold"] = workbook.add_format(
{"align": "left", "bold": True, "num_format": pct_format_conditional}
)
FORMATS["format_percent_conditional_center_bold"] = workbook.add_format(
{"align": "center", "bold": True, "num_format": pct_format_conditional}
)
FORMATS["format_percent_conditional_right_bold"] = workbook.add_format(
{"align": "right", "bold": True, "num_format": pct_format_conditional}
)
FORMATS["format_integer_left_bold"] = workbook.add_format(
{"align": "left", "bold": True, "num_format": int_format}
)
FORMATS["format_integer_center_bold"] = workbook.add_format(
{"align": "center", "bold": True, "num_format": int_format}
)
FORMATS["format_integer_right_bold"] = workbook.add_format(
{"align": "right", "bold": True, "num_format": int_format}
)
FORMATS["format_integer_conditional_left_bold"] = workbook.add_format(
{"align": "left", "bold": True, "num_format": int_format_conditional}
)
FORMATS["format_integer_conditional_center_bold"] = workbook.add_format(
{"align": "center", "bold": True, "num_format": int_format_conditional}
)
FORMATS["format_integer_conditional_right_bold"] = workbook.add_format(
{"align": "right", "bold": True, "num_format": int_format_conditional}
)
FORMATS["format_date_left_bold"] = workbook.add_format(
{"align": "left", "bold": True, "num_format": date_format}
)
FORMATS["format_date_center_bold"] = workbook.add_format(
{"align": "center", "bold": True, "num_format": date_format}
)
FORMATS["format_date_right_bold"] = workbook.add_format(
{"align": "right", "bold": True, "num_format": date_format}
)
# formats for worksheet table column headers
FORMATS["format_theader_grey_left"] = workbook.add_format(theader_grey)
FORMATS["format_theader_grey_center"] = workbook.add_format(
dict(theader_grey, align="center")
)
FORMATS["format_theader_grey_right"] = workbook.add_format(
dict(theader_grey, align="right")
)
FORMATS["format_theader_grey_amount_left"] = workbook.add_format(
dict(theader_grey, num_format=num_format, align="left")
)
FORMATS["format_theader_grey_amount_center"] = workbook.add_format(
dict(theader_grey, num_format=num_format, align="center")
)
FORMATS["format_theader_grey_amount_right"] = workbook.add_format(
dict(theader_grey, num_format=num_format, align="right")
)
FORMATS["format_theader_grey_amount_conditional_left"] = workbook.add_format(
dict(theader_grey, num_format=num_format_conditional, align="left")
)
FORMATS["format_theader_grey_amount_conditional_center"] = workbook.add_format(
dict(theader_grey, num_format=num_format_conditional, align="center")
)
FORMATS["format_theader_grey_amount_conditional_right"] = workbook.add_format(
dict(theader_grey, num_format=num_format_conditional, align="right")
)
FORMATS["format_theader_grey_percent_left"] = workbook.add_format(
dict(theader_grey, num_format=pct_format, align="left")
)
FORMATS["format_theader_grey_percent_center"] = workbook.add_format(
dict(theader_grey, num_format=pct_format, align="center")
)
FORMATS["format_theader_grey_percent_right"] = workbook.add_format(
dict(theader_grey, num_format=pct_format, align="right")
)
FORMATS["format_theader_grey_percent_conditional_left"] = workbook.add_format(
dict(theader_grey, num_format=pct_format_conditional, align="left")
)
FORMATS["format_theader_grey_percent_conditional_center"] = workbook.add_format(
dict(theader_grey, num_format=pct_format_conditional, align="center")
)
FORMATS["format_theader_grey_percent_conditional_right"] = workbook.add_format(
dict(theader_grey, num_format=pct_format_conditional, align="right")
)
FORMATS["format_theader_grey_integer_left"] = workbook.add_format(
dict(theader_grey, num_format=int_format, align="left")
)
FORMATS["format_theader_grey_integer_center"] = workbook.add_format(
dict(theader_grey, num_format=int_format, align="center")
)
FORMATS["format_theader_grey_integer_right"] = workbook.add_format(
dict(theader_grey, num_format=int_format, align="right")
)
FORMATS["format_theader_grey_integer_conditional_left"] = workbook.add_format(
dict(theader_grey, num_format=int_format_conditional, align="left")
)
FORMATS["format_theader_grey_integer_conditional_center"] = workbook.add_format(
dict(theader_grey, num_format=int_format_conditional, align="center")
)
FORMATS["format_theader_grey_integer_conditional_right"] = workbook.add_format(
dict(theader_grey, num_format=int_format_conditional, align="right")
)
FORMATS["format_theader_yellow_left"] = workbook.add_format(theader_yellow)
FORMATS["format_theader_yellow_center"] = workbook.add_format(
dict(theader_yellow, align="center")
)
FORMATS["format_theader_yellow_right"] = workbook.add_format(
dict(theader_yellow, align="right")
)
FORMATS["format_theader_yellow_amount_left"] = workbook.add_format(
dict(theader_yellow, num_format=num_format, align="left")
)
FORMATS["format_theader_yellow_amount_center"] = workbook.add_format(
dict(theader_yellow, num_format=num_format, align="center")
)
FORMATS["format_theader_yellow_amount_right"] = workbook.add_format(
dict(theader_yellow, num_format=num_format, align="right")
)
FORMATS["format_theader_yellow_amount_conditional_left"] = workbook.add_format(
dict(theader_yellow, num_format=num_format_conditional, align="left")
)
FORMATS["format_theader_yellow_amount_conditional_center"] = (
workbook.add_format(
dict(theader_yellow, num_format=num_format_conditional, align="center")
)
)
FORMATS["format_theader_yellow_amount_conditional_right"] = workbook.add_format(
dict(theader_yellow, num_format=num_format_conditional, align="right")
)
FORMATS["format_theader_yellow_percent_left"] = workbook.add_format(
dict(theader_yellow, num_format=pct_format, align="left")
)
FORMATS["format_theader_yellow_percent_center"] = workbook.add_format(
dict(theader_yellow, num_format=pct_format, align="center")
)
FORMATS["format_theader_yellow_percent_right"] = workbook.add_format(
dict(theader_yellow, num_format=pct_format, align="right")
)
FORMATS["format_theader_yellow_percent_conditional_left"] = workbook.add_format(
dict(theader_yellow, num_format=pct_format_conditional, align="left")
)
FORMATS["format_theader_yellow_percent_conditional_center"] = (
workbook.add_format(
dict(theader_yellow, num_format=pct_format_conditional, align="center")
)
)
FORMATS["format_theader_yellow_percent_conditional_right"] = (
workbook.add_format(
dict(theader_yellow, num_format=pct_format_conditional, align="right")
)
)
FORMATS["format_theader_yellow_integer_left"] = workbook.add_format(
dict(theader_yellow, num_format=int_format, align="left")
)
FORMATS["format_theader_yellow_integer_center"] = workbook.add_format(
dict(theader_yellow, num_format=int_format, align="center")
)
FORMATS["format_theader_yellow_integer_right"] = workbook.add_format(
dict(theader_yellow, num_format=int_format, align="right")
)
FORMATS["format_theader_yellow_integer_conditional_left"] = workbook.add_format(
dict(theader_yellow, num_format=int_format_conditional, align="left")
)
FORMATS["format_theader_yellow_integer_conditional_center"] = (
workbook.add_format(
dict(theader_yellow, num_format=int_format_conditional, align="center")
)
)
FORMATS["format_theader_yellow_integer_conditional_right"] = (
workbook.add_format(
dict(theader_yellow, num_format=int_format_conditional, align="right")
)
)
FORMATS["format_theader_blue_left"] = workbook.add_format(theader_blue)
FORMATS["format_theader_blue_center"] = workbook.add_format(
dict(theader_blue, align="center")
)
FORMATS["format_theader_blue_right"] = workbook.add_format(
dict(theader_blue, align="right")
)
FORMATS["format_theader_blue_amount_left"] = workbook.add_format(
dict(theader_blue, num_format=num_format, align="left")
)
FORMATS["format_theader_blue_amount_center"] = workbook.add_format(
dict(theader_blue, num_format=num_format, align="center")
)
FORMATS["format_theader_blue_amount_right"] = workbook.add_format(
dict(theader_blue, num_format=num_format, align="right")
)
FORMATS["format_theader_blue_amount_conditional_left"] = workbook.add_format(
dict(theader_blue, num_format=num_format_conditional, align="left")
)
FORMATS["format_theader_blue_amount_conditional_center"] = workbook.add_format(
dict(theader_blue, num_format=num_format_conditional, align="center")
)
FORMATS["format_theader_blue_amount_conditional_right"] = workbook.add_format(
dict(theader_blue, num_format=num_format_conditional, align="right")
)
FORMATS["format_theader_blue_percent_left"] = workbook.add_format(
dict(theader_blue, num_format=pct_format, align="left")
)
FORMATS["format_theader_blue_percent_center"] = workbook.add_format(
dict(theader_blue, num_format=pct_format, align="center")
)
FORMATS["format_theader_blue_percent_right"] = workbook.add_format(
dict(theader_blue, num_format=pct_format, align="right")
)
FORMATS["format_theader_blue_percent_conditional_left"] = workbook.add_format(
dict(theader_blue, num_format=pct_format_conditional, align="left")
)
FORMATS["format_theader_blue_percent_conditional_center"] = workbook.add_format(
dict(theader_blue, num_format=pct_format_conditional, align="center")
)
FORMATS["format_theader_blue_percent_conditional_right"] = workbook.add_format(
dict(theader_blue, num_format=pct_format_conditional, align="right")
)
FORMATS["format_theader_blue_integer_left"] = workbook.add_format(
dict(theader_blue, num_format=int_format, align="left")
)
FORMATS["format_theader_blue_integer_center"] = workbook.add_format(
dict(theader_blue, num_format=int_format, align="center")
)
FORMATS["format_theader_blue_integer_right"] = workbook.add_format(
dict(theader_blue, num_format=int_format, align="right")
)
FORMATS["format_theader_blue_integer_conditional_left"] = workbook.add_format(
dict(theader_blue, num_format=int_format_conditional, align="left")
)
FORMATS["format_theader_blue_integer_conditional_center"] = workbook.add_format(
dict(theader_blue, num_format=int_format_conditional, align="center")
)
FORMATS["format_theader_blue_integer_conditional_right"] = workbook.add_format(
dict(theader_blue, num_format=int_format_conditional, align="right")
)
# formats for worksheet table cells
FORMATS["format_tcell_left"] = workbook.add_format(dict(border, align="left"))
FORMATS["format_tcell_center"] = workbook.add_format(
dict(border, align="center")
)
FORMATS["format_tcell_right"] = workbook.add_format(dict(border, align="right"))
FORMATS["format_tcell_amount_left"] = workbook.add_format(
dict(border, num_format=num_format, align="left")
)
FORMATS["format_tcell_amount_center"] = workbook.add_format(
dict(border, num_format=num_format, align="center")
)
FORMATS["format_tcell_amount_right"] = workbook.add_format(
dict(border, num_format=num_format, align="right")
)
FORMATS["format_tcell_amount_conditional_left"] = workbook.add_format(
dict(border, num_format=num_format_conditional, align="left")
)
FORMATS["format_tcell_amount_conditional_center"] = workbook.add_format(
dict(border, num_format=num_format_conditional, align="center")
)
FORMATS["format_tcell_amount_conditional_right"] = workbook.add_format(
dict(border, num_format=num_format_conditional, align="right")
)
FORMATS["format_tcell_percent_left"] = workbook.add_format(
dict(border, num_format=pct_format, align="left")
)
FORMATS["format_tcell_percent_center"] = workbook.add_format(
dict(border, num_format=pct_format, align="center")
)
FORMATS["format_tcell_percent_right"] = workbook.add_format(
dict(border, num_format=pct_format, align="right")
)
FORMATS["format_tcell_percent_conditional_left"] = workbook.add_format(
dict(border, num_format=pct_format_conditional, align="left")
)
FORMATS["format_tcell_percent_conditional_center"] = workbook.add_format(
dict(border, num_format=pct_format_conditional, align="center")
)
FORMATS["format_tcell_percent_conditional_right"] = workbook.add_format(
dict(border, num_format=pct_format_conditional, align="right")
)
FORMATS["format_tcell_integer_left"] = workbook.add_format(
dict(border, num_format=int_format, align="left")
)
FORMATS["format_tcell_integer_center"] = workbook.add_format(
dict(border, num_format=int_format, align="center")
)
FORMATS["format_tcell_integer_right"] = workbook.add_format(
dict(border, num_format=int_format, align="right")
)
FORMATS["format_tcell_integer_conditional_left"] = workbook.add_format(
dict(border, num_format=int_format_conditional, align="left")
)
FORMATS["format_tcell_integer_conditional_center"] = workbook.add_format(
dict(border, num_format=int_format_conditional, align="center")
)
FORMATS["format_tcell_integer_conditional_right"] = workbook.add_format(
dict(border, num_format=int_format_conditional, align="right")
)
FORMATS["format_tcell_date_left"] = workbook.add_format(
dict(border, num_format=date_format, align="left")
)
FORMATS["format_tcell_date_center"] = workbook.add_format(
dict(border, num_format=date_format, align="center")
)
FORMATS["format_tcell_date_right"] = workbook.add_format(
dict(border, num_format=date_format, align="right")
)
FORMATS["format_tcell_left_bold"] = workbook.add_format(
dict(border, align="left", bold=True)
)
FORMATS["format_tcell_center_bold"] = workbook.add_format(
dict(border, align="center", bold=True)
)
FORMATS["format_tcell_right_bold"] = workbook.add_format(
dict(border, align="right", bold=True)
)
FORMATS["format_tcell_amount_left_bold"] = workbook.add_format(
dict(border, num_format=num_format, align="left", bold=True)
)
FORMATS["format_tcell_amount_center_bold"] = workbook.add_format(
dict(border, num_format=num_format, align="center", bold=True)
)
FORMATS["format_tcell_amount_right_bold"] = workbook.add_format(
dict(border, num_format=num_format, align="right", bold=True)
)
FORMATS["format_tcell_amount_conditional_left_bold"] = workbook.add_format(
dict(border, num_format=num_format_conditional, align="left", bold=True)
)
FORMATS["format_tcell_amount_conditional_center_bold"] = workbook.add_format(
dict(border, num_format=num_format_conditional, align="center", bold=True)
)
FORMATS["format_tcell_amount_conditional_right_bold"] = workbook.add_format(
dict(border, num_format=num_format_conditional, align="right", bold=True)
)
FORMATS["format_tcell_percent_left_bold"] = workbook.add_format(
dict(border, num_format=pct_format, align="left", bold=True)
)
FORMATS["format_tcell_percent_center_bold"] = workbook.add_format(
dict(border, num_format=pct_format, align="center", bold=True)
)
FORMATS["format_tcell_percent_right_bold"] = workbook.add_format(
dict(border, num_format=pct_format, align="right", bold=True)
)
FORMATS["format_tcell_percent_conditional_left_bold"] = workbook.add_format(
dict(border, num_format=pct_format_conditional, align="left", bold=True)
)
FORMATS["format_tcell_percent_conditional_center_bold"] = workbook.add_format(
dict(border, num_format=pct_format_conditional, align="center", bold=True)
)
FORMATS["format_tcell_percent_conditional_right_bold"] = workbook.add_format(
dict(border, num_format=pct_format_conditional, align="right", bold=True)
)
FORMATS["format_tcell_integer_left_bold"] = workbook.add_format(
dict(border, num_format=int_format, align="left", bold=True)
)
FORMATS["format_tcell_integer_center_bold"] = workbook.add_format(
dict(border, num_format=int_format, align="center", bold=True)
)
FORMATS["format_tcell_integer_right_bold"] = workbook.add_format(
dict(border, num_format=int_format, align="right", bold=True)
)
FORMATS["format_tcell_integer_conditional_left_bold"] = workbook.add_format(
dict(border, num_format=int_format_conditional, align="left", bold=True)
)
FORMATS["format_tcell_integer_conditional_center_bold"] = workbook.add_format(
dict(border, num_format=int_format_conditional, align="center", bold=True)
)
FORMATS["format_tcell_integer_conditional_right_bold"] = workbook.add_format(
dict(border, num_format=int_format_conditional, align="right", bold=True)
)
FORMATS["format_tcell_date_left_bold"] = workbook.add_format(
dict(border, num_format=date_format, align="left", bold=True)
)
FORMATS["format_tcell_date_center_bold"] = workbook.add_format(
dict(border, num_format=date_format, align="center", bold=True)
)
FORMATS["format_tcell_date_right_bold"] = workbook.add_format(
dict(border, num_format=date_format, align="right", bold=True)
)
def _set_column_width(self, ws, ws_params):
"""
Set width for all columns included in the 'wanted_list'.
"""
col_specs = ws_params.get("col_specs")
wl = ws_params.get("wanted_list") or []
for pos, col in enumerate(wl):
if col not in col_specs:
raise UserError(
_(
"Programming Error:\n\n"
"The '%s' column is not defined in the worksheet "
"column specifications."
)
% col
)
ws.set_column(pos, pos, col_specs[col]["width"])
def _write_ws_title(self, ws, row_pos, ws_params, merge_range=False):
"""
Helper function to ensure consistent title formats
troughout all worksheets.
Requires 'title' keyword in ws_params.
"""
title = ws_params.get("title")
if not title:
raise UserError(
_(
"Programming Error:\n\n"
"The 'title' parameter is mandatory "
"when calling the '_write_ws_title' method."
)
)
if merge_range:
wl = ws_params.get("wanted_list")
if wl and len(wl) > 1:
ws.merge_range(
row_pos, 0, row_pos, len(wl) - 1, title, FORMATS["format_ws_title"]
)
else:
ws.write_string(row_pos, 0, title, FORMATS["format_ws_title"])
return row_pos + 2
def _write_line(
self,
ws,
row_pos,
ws_params,
col_specs_section=None,
render_space=None,
default_format=None,
col_specs="col_specs",
wanted_list="wanted_list",
):
"""
Write a line with all columns included in the 'wanted_list'.
Use the entry defined by the col_specs_section.
An empty cell will be written if no col_specs_section entry
for a column.
"""
col_specs = ws_params.get(col_specs)
wl = ws_params.get(wanted_list) or []
pos = 0
for col in wl:
if col not in col_specs:
raise UserError(
_(
"Programming Error:\n\n"
"The '%s' column is not defined the worksheet "
"column specifications."
)
% col
)
colspan = col_specs[col].get("colspan") or 1
cell_spec = col_specs[col].get(col_specs_section) or {}
if not cell_spec:
cell_value = None
cell_type = "blank"
cell_format = default_format
else:
cell_value = cell_spec.get("value")
if isinstance(cell_value, CodeType):
cell_value = self._eval(cell_value, render_space)
cell_type = cell_spec.get("type")
cell_format = cell_spec.get("format") or default_format
if not cell_type:
# test bool first since isinstance(val, int) returns
# True when type(val) is bool
if isinstance(cell_value, bool):
cell_type = "boolean"
elif isinstance(cell_value, str):
cell_type = "string"
elif isinstance(cell_value, int | float):
cell_type = "number"
elif isinstance(cell_value, datetime):
cell_type = "datetime"
elif isinstance(cell_value, date):
cell_value = datetime.combine(cell_value, datetime.min.time())
cell_type = "datetime"
else:
if not cell_value:
cell_type = "blank"
else:
msg = _(
"%(__name__)s, _write_line : programming error "
"detected while processing "
"col_specs_section %(col_specs_section)s, "
"column %(col)s"
) % {
"__name__": __name__,
"col_specs_section": col_specs_section,
"col": col,
}
if cell_value:
msg += _(", cellvalue %s") % cell_value
raise UserError(msg)
colspan = cell_spec.get("colspan") or colspan
args_pos = [row_pos, pos]
args_data = [cell_value]
if cell_format:
if isinstance(cell_format, CodeType):
cell_format = self._eval(cell_format, render_space)
args_data.append(cell_format)
self._apply_formula_quirk(args_data, cell_type, cell_format)
if colspan > 1:
args_pos += [row_pos, pos + colspan - 1]
args = args_pos + args_data
ws.merge_range(*args)
else:
ws_method = getattr(ws, f"write_{cell_type}")
args = args_pos + args_data
ws_method(*args)
pos += colspan
return row_pos + 1
@staticmethod
def _apply_formula_quirk(args_data, cell_type, cell_format):
"""Insert empty value to force LibreOffice to recompute the value"""
if cell_type == "formula":
if not cell_format:
# Insert positional argument for missing format
args_data.append(None)
args_data.append("")
@staticmethod
def _render(code):
return compile(code, "<string>", "eval")
@staticmethod
def _eval(val, render_space):
if not render_space:
render_space = {}
if "datetime" not in render_space:
render_space["datetime"] = datetime
# the use of eval is not a security thread as long as the
# col_specs template is defined in a python module
return eval(val, render_space) # pylint: disable=W0123,W8112
@staticmethod
def _rowcol_to_cell(row, col, row_abs=False, col_abs=False):
return xl_rowcol_to_cell(row, col, row_abs=row_abs, col_abs=col_abs)

View File

@@ -0,0 +1,164 @@
# Global Formatting
XLS_HEADERS = {"xls_headers": "", "xls_footers": ""}
FORMATS = {
"format_ws_title": False,
"format_left": False,
"format_center": False,
"format_right": False,
"format_amount_left": False,
"format_amount_center": False,
"format_amount_right": False,
"format_amount_conditional_left": False,
"format_amount_conditional_center": False,
"format_amount_conditional_right": False,
"format_percent_left": False,
"format_percent_center": False,
"format_percent_right": False,
"format_percent_conditional_left": False,
"format_percent_conditional_center": False,
"format_percent_conditional_right": False,
"format_integer_left": False,
"format_integer_center": False,
"format_integer_right": False,
"format_integer_conditional_left": False,
"format_integer_conditional_center": False,
"format_integer_conditional_right": False,
"format_date_left": False,
"format_date_center": False,
"format_date_right": False,
"format_left_bold": False,
"format_center_bold": False,
"format_right_bold": False,
"format_amount_left_bold": False,
"format_amount_center_bold": False,
"format_amount_right_bold": False,
"format_amount_conditional_left_bold": False,
"format_amount_conditional_center_bold": False,
"format_amount_conditional_right_bold": False,
"format_percent_left_bold": False,
"format_percent_center_bold": False,
"format_percent_right_bold": False,
"format_percent_conditional_left_bold": False,
"format_percent_conditional_center_bold": False,
"format_percent_conditional_right_bold": False,
"format_integer_left_bold": False,
"format_integer_center_bold": False,
"format_integer_right_bold": False,
"format_integer_conditional_left_bold": False,
"format_integer_conditional_center_bold": False,
"format_integer_conditional_right_bold": False,
"format_date_left_bold": False,
"format_date_center_bold": False,
"format_date_right_bold": False,
"format_theader_grey_left": False,
"format_theader_grey_center": False,
"format_theader_grey_right": False,
"format_theader_grey_amount_left": False,
"format_theader_grey_amount_center": False,
"format_theader_grey_amount_right": False,
"format_theader_grey_amount_conditional_left": False,
"format_theader_grey_amount_conditional_center": False,
"format_theader_grey_amount_conditional_right": False,
"format_theader_grey_percent_left": False,
"format_theader_grey_percent_center": False,
"format_theader_grey_percent_right": False,
"format_theader_grey_percent_conditional_left": False,
"format_theader_grey_percent_conditional_center": False,
"format_theader_grey_percent_conditional_right": False,
"format_theader_grey_integer_left": False,
"format_theader_grey_integer_center": False,
"format_theader_grey_integer_right": False,
"format_theader_grey_integer_conditional_left": False,
"format_theader_grey_integer_conditional_center": False,
"format_theader_grey_integer_conditional_right": False,
"format_theader_yellow_left": False,
"format_theader_yellow_center": False,
"format_theader_yellow_right": False,
"format_theader_yellow_amount_left": False,
"format_theader_yellow_amount_center": False,
"format_theader_yellow_amount_right": False,
"format_theader_yellow_amount_conditional_left": False,
"format_theader_yellow_amount_conditional_center": False,
"format_theader_yellow_amount_conditional_right": False,
"format_theader_yellow_percent_left": False,
"format_theader_yellow_percent_center": False,
"format_theader_yellow_percent_right": False,
"format_theader_yellow_percent_conditional_left": False,
"format_theader_yellow_percent_conditional_center": False,
"format_theader_yellow_percent_conditional_right": False,
"format_theader_yellow_integer_left": False,
"format_theader_yellow_integer_center": False,
"format_theader_yellow_integer_right": False,
"format_theader_yellow_integer_conditional_left": False,
"format_theader_yellow_integer_conditional_center": False,
"format_theader_yellow_integer_conditional_right": False,
"format_theader_blue_left": False,
"format_theader_blue_center": False,
"format_theader_blue_right": False,
"format_theader_blue_amount_left": False,
"format_theader_blue_amount_center": False,
"format_theader_blue_amount_right": False,
"format_theader_blue_amount_conditional_left": False,
"format_theader_blue_amount_conditional_center": False,
"format_theader_blue_amount_conditional_right": False,
"format_theader_blue_percent_left": False,
"format_theader_blue_percent_center": False,
"format_theader_blue_percent_right": False,
"format_theader_blue_percent_conditional_left": False,
"format_theader_blue_percent_conditional_center": False,
"format_theader_blue_percent_conditional_right": False,
"format_theader_blue_integer_left": False,
"format_theader_blue_integer_center": False,
"format_theader_blue_integer_right": False,
"format_theader_blue_integer_conditional_left": False,
"format_theader_blue_integer_conditional_center": False,
"format_theader_blue_integer_conditional_right": False,
"format_tcell_left": False,
"format_tcell_center": False,
"format_tcell_right": False,
"format_tcell_amount_left": False,
"format_tcell_amount_center": False,
"format_tcell_amount_right": False,
"format_tcell_amount_conditional_left": False,
"format_tcell_amount_conditional_center": False,
"format_tcell_amount_conditional_right": False,
"format_tcell_percent_left": False,
"format_tcell_percent_center": False,
"format_tcell_percent_right": False,
"format_tcell_percent_conditional_left": False,
"format_tcell_percent_conditional_center": False,
"format_tcell_percent_conditional_right": False,
"format_tcell_integer_left": False,
"format_tcell_integer_center": False,
"format_tcell_integer_right": False,
"format_tcell_integer_conditional_left": False,
"format_tcell_integer_conditional_center": False,
"format_tcell_integer_conditional_right": False,
"format_tcell_date_left": False,
"format_tcell_date_center": False,
"format_tcell_date_right": False,
"format_tcell_left_bold": False,
"format_tcell_center_bold": False,
"format_tcell_right_bold": False,
"format_tcell_amount_left_bold": False,
"format_tcell_amount_center_bold": False,
"format_tcell_amount_right_bold": False,
"format_tcell_amount_conditional_left_bold": False,
"format_tcell_amount_conditional_center_bold": False,
"format_tcell_amount_conditional_right_bold": False,
"format_tcell_percent_left_bold": False,
"format_tcell_percent_center_bold": False,
"format_tcell_percent_right_bold": False,
"format_tcell_percent_conditional_left_bold": False,
"format_tcell_percent_conditional_center_bold": False,
"format_tcell_percent_conditional_right_bold": False,
"format_tcell_integer_left_bold": False,
"format_tcell_integer_center_bold": False,
"format_tcell_integer_right_bold": False,
"format_tcell_integer_conditional_left_bold": False,
"format_tcell_integer_conditional_center_bold": False,
"format_tcell_integer_conditional_right_bold": False,
"format_tcell_date_left_bold": False,
"format_tcell_date_center_bold": False,
"format_tcell_date_right_bold": False,
}

View File

@@ -0,0 +1,76 @@
# Copyright 2009-2019 Noviat.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import models
from .report_xlsx_format import FORMATS, XLS_HEADERS
# TODO:
# make PR to move this class as well as the report_xlsx test class
# to the tests folder (requires dynamic update Odoo registry when
# running unit tests.
class TestPartnerXlsx(models.AbstractModel):
_name = "report.report_xlsx_helper.test_partner_xlsx"
_inherit = "report.report_xlsx.abstract"
_description = "Test Partner XLSX Report"
def _get_ws_params(self, wb, data, partners):
partner_template = {
"name": {
"header": {"value": "Name"},
"data": {"value": self._render("partner.name")},
"width": 20,
},
"number_of_contacts": {
"header": {"value": "# Contacts"},
"data": {"value": self._render("len(partner.child_ids)")},
"width": 10,
},
"create_date": {
"header": {"value": "Creation Date"},
"data": {
"value": self._render("partner.create_date"),
"format": FORMATS["format_date_right"],
},
"width": 13,
},
}
ws_params = {
"ws_name": "Partners",
"generate_ws_method": "_partner_report",
"title": "Partners",
"wanted_list": [k for k in partner_template],
"col_specs": partner_template,
}
return [ws_params]
def _partner_report(self, workbook, ws, ws_params, data, partners):
ws.set_portrait()
ws.fit_to_pages(1, 0)
ws.set_header(XLS_HEADERS["xls_headers"]["standard"])
ws.set_footer(XLS_HEADERS["xls_footers"]["standard"])
self._set_column_width(ws, ws_params)
row_pos = 0
row_pos = self._write_ws_title(ws, row_pos, ws_params)
row_pos = self._write_line(
ws,
row_pos,
ws_params,
col_specs_section="header",
default_format=FORMATS["format_theader_yellow_left"],
)
ws.freeze_panes(row_pos, 0)
for partner in partners:
row_pos = self._write_line(
ws,
row_pos,
ws_params,
col_specs_section="data",
render_space={"partner": partner},
default_format=FORMATS["format_tcell_left"],
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@@ -0,0 +1,469 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
<title>Report xlsx helpers</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
Despite the name, some widely supported CSS2 features are used.
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
.subscript {
vertical-align: sub;
font-size: smaller }
.superscript {
vertical-align: super;
font-size: smaller }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title, .code .error {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left, table.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right, table.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
.align-top {
vertical-align: top }
.align-middle {
vertical-align: middle }
.align-bottom {
vertical-align: bottom }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: gray; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic, pre.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
/* "booktabs" style (no vertical lines) */
table.docutils.booktabs {
border: 0px;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.docutils.booktabs * {
border: 0px;
}
table.docutils.booktabs th {
border-bottom: thin solid;
text-align: left;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document" id="report-xlsx-helpers">
<h1 class="title">Report xlsx helpers</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:df481003a65f02d5bf2edcc90f4238033b81c3fa5120209c2e36e25b1cdabb77
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Mature" src="https://img.shields.io/badge/maturity-Mature-brightgreen.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/reporting-engine/tree/18.0/report_xlsx_helper"><img alt="OCA/reporting-engine" src="https://img.shields.io/badge/github-OCA%2Freporting--engine-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/reporting-engine-18-0/reporting-engine-18-0-report_xlsx_helper"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/reporting-engine&amp;target_branch=18.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>This module provides a set of tools to facilitate the creation of excel
reports with format xlsx.</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#installation" id="toc-entry-1">Installation</a></li>
<li><a class="reference internal" href="#usage" id="toc-entry-2">Usage</a></li>
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-3">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="toc-entry-4">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="toc-entry-5">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="toc-entry-6">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="toc-entry-7">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="installation">
<h1><a class="toc-backref" href="#toc-entry-1">Installation</a></h1>
<p>This module requires report_xlsx version 13.0.1.0.0 or higher.</p>
</div>
<div class="section" id="usage">
<h1><a class="toc-backref" href="#toc-entry-2">Usage</a></h1>
<p>In order to create an Excel report you can define a report of type
xlsx in a static or dynamic way:</p>
<ul class="simple">
<li>Static syntax: cf. <tt class="docutils literal">account_move_line_report_xls</tt> for an example.</li>
<li>Dynamic syntax: cf. <tt class="docutils literal">report_xlsx_helper_demo</tt> for an example</li>
</ul>
<p>The <tt class="docutils literal">AbstractReportXlsx</tt> class contains a number of attributes and
methods to facilitate the creation excel reports in Odoo.</p>
<ul>
<li><p class="first">Cell types</p>
<p>string, number, boolean, datetime.</p>
</li>
<li><p class="first">Cell formats</p>
<p>The predefined cell formats result in a consistent look and feel of
the Odoo Excel reports.</p>
</li>
<li><p class="first">Cell formulas</p>
<p>Cell formulas can be easily added with the help of the
<tt class="docutils literal">_rowcol_to_cell()</tt> method.</p>
</li>
<li><p class="first">Excel templates</p>
<p>It is possible to define Excel templates which can be adapted by
inherited modules. Download the <tt class="docutils literal">account_move_line_report_xls</tt>
module from <a class="reference external" href="http://apps.odoo.com">http://apps.odoo.com</a> as example.</p>
</li>
<li><p class="first">Excel with multiple sheets</p>
<p>Download the <tt class="docutils literal">account_asset_management_xls</tt> module from
<a class="reference external" href="http://apps.odoo.com">http://apps.odoo.com</a> as example.</p>
</li>
</ul>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#toc-entry-3">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/reporting-engine/issues">GitHub Issues</a>.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OCA/reporting-engine/issues/new?body=module:%20report_xlsx_helper%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h1><a class="toc-backref" href="#toc-entry-4">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#toc-entry-5">Authors</a></h2>
<ul class="simple">
<li>Noviat</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#toc-entry-6">Contributors</a></h2>
<ul class="simple">
<li>Luc De Meyer &lt;<a class="reference external" href="mailto:luc.demeyer&#64;noviat.com">luc.demeyer&#64;noviat.com</a>&gt;</li>
<li>Rattapong Chokmasermkul &lt;<a class="reference external" href="mailto:rattapongc&#64;ecosoft.co.th">rattapongc&#64;ecosoft.co.th</a>&gt;</li>
<li>Saran Lim. &lt;<a class="reference external" href="mailto:saranl&#64;ecosoft.co.th">saranl&#64;ecosoft.co.th</a>&gt;</li>
<li><a class="reference external" href="https://www.sinerkia.com">Sinerkia Innovación y Desarrollo S.L.</a>:<ul>
<li>Luis Pomar</li>
</ul>
</li>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#toc-entry-7">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org">
<img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" />
</a>
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.</p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/reporting-engine/tree/18.0/report_xlsx_helper">OCA/reporting-engine</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1 @@
from . import test_report_xlsx_helper

View File

@@ -0,0 +1,24 @@
# Copyright 2009-2019 Noviat.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo.tests.common import TransactionCase
class TestReportXlsxHelper(TransactionCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
p1 = cls.env.ref("base.res_partner_1")
p2 = cls.env.ref("base.res_partner_2")
cls.partners = p1 + p2
ctx = {
"report_name": "report_xlsx_helper.test_partner_xlsx",
"active_model": "res.partner",
"active_ids": cls.partners.ids,
}
cls.report = cls.env["ir.actions.report"].with_context(**ctx)
def test_report_xlsx_helper(self):
report_xls = self.report._render_xlsx(None, None, None)
self.assertEqual(report_xls[1], "xlsx")