diff --git a/account_move_line_report_xls/README.rst b/account_move_line_report_xls/README.rst new file mode 100644 index 00000000..0ce69ca0 --- /dev/null +++ b/account_move_line_report_xls/README.rst @@ -0,0 +1,91 @@ +.. image:: https://img.shields.io/badge/license-AGPL--3-blue.png + :target: https://www.gnu.org/licenses/agpl + :alt: License: AGPL-3 + +========================== +Journal Items Excel Export +========================== + +This module extends the functionality of the journal items +('account.move.line') list view and allow you to export the selected lines. + +Installation +============ + +To install this module, you need also the **report_xlsx_helper** +module located in: + +https://github.com/OCA/reporting-engine + +Usage +===== + +To use this module, you need to: + +* go to the list view of the journal items +* select the lines you wish to export +* click on the button on top to export + +The Excel export can be tailored to your exact needs via the following methods +of the 'account.move.line' object: + +* **_report_xls_fields** + + Add/drop columns or change order from the list of columns that are defined + in the Excel template. + + The following fields are defined in the Excel template: + + move, name, date, journal, period, partner, account, + date_maturity, debit, credit, balance, + reconcile, reconcile_partial, analytic_account, + ref, partner_ref, tax_code, tax_amount, amount_residual, + amount_currency, currency_name, company_currency, + amount_residual_currency, product, product_ref', product_uom, quantity, + statement, invoice, narration, blocked + +* **_report_xls_template** + + Change/extend the Excel template. + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/91/11.0 + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues +`_. In case of trouble, please +check there if your issue has already been reported. If you spotted it first, +help us smashing it by providing a detailed and welcomed feedback. + +Credits +======= + +Author +------------ +* Luc De Meyer, Noviat + +Contributors +------------ +* Guillaume Auger + +Icon +---- +* https://openclipart.org/detail/38353/58294main-The.Brain.in.Space-page-91-spreadsheet-by-rejon + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +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. + +To contribute to this module, please visit http://odoo-community.org. diff --git a/account_move_line_report_xls/__init__.py b/account_move_line_report_xls/__init__.py new file mode 100644 index 00000000..bb05a243 --- /dev/null +++ b/account_move_line_report_xls/__init__.py @@ -0,0 +1,8 @@ +from . import models +try: + from . import report +except ImportError: + import logging + logging.getLogger('odoo.module').\ + warning('''report_xlsx_helper not available in addons path. + account_move_line_report_xls will not be usable''') diff --git a/account_move_line_report_xls/__manifest__.py b/account_move_line_report_xls/__manifest__.py new file mode 100644 index 00000000..f33e118f --- /dev/null +++ b/account_move_line_report_xls/__manifest__.py @@ -0,0 +1,17 @@ +# Copyright 2009-2018 Noviat. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + 'name': 'Account Move Line XLSX export', + 'version': '11.0.1.0.0', + 'license': 'AGPL-3', + 'author': 'Noviat,' + 'Odoo Community Association (OCA)', + 'category': 'Accounting & Finance', + 'summary': 'Journal Items Excel export', + 'depends': ['account', 'report_xlsx_helper'], + 'data': [ + 'report/account_move_line_xlsx.xml', + ], + 'installable': True, +} diff --git a/account_move_line_report_xls/i18n/fr.po b/account_move_line_report_xls/i18n/fr.po new file mode 100644 index 00000000..0e8e8639 --- /dev/null +++ b/account_move_line_report_xls/i18n/fr.po @@ -0,0 +1,181 @@ +# French translation of Odoo. +# This file contains the translation of the following modules: +# * account_move_line_report_xls +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 8.0\n" +"Report-Msgid-Bugs-To: support@noviat.com\n" +"POT-Creation-Date: 2016-05-16 12:31:16.568000\n" +"PO-Revision-Date: 2016-05-16 12:31:16.568000\n" +"Last-Translator: Luc De Meyer (Noviat nv/sa)\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Journal Items" +msgstr "Écritures comptables" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Entry" +msgstr "Écriture" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Name" +msgstr "Nom" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Reference" +msgstr "Référence" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Effective Date" +msgstr "Date" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Period" +msgstr "Période" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Partner" +msgstr "Partenaire" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Partner Reference" +msgstr "Réf. Partenaire" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Account" +msgstr "Compte" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Maturity Date" +msgstr "Date d'échéance" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Debit" +msgstr "Débit" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Credit" +msgstr "Crédit" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Balance" +msgstr "Solde" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Rec." +msgstr "Let." + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Part. Rec." +msgstr "Let. Part." + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Tax Code" +msgstr "Case TVA" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Tax/Base Amount" +msgstr "Montant TVA/Base" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Am. Currency" +msgstr "Montant devise" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Curr." +msgstr "Dev." + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Journal" +msgstr "Journal" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Comp. Curr." +msgstr "Dev. Soc." + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Analytic Account" +msgstr "Compte analytique" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Analytic Account Reference" +msgstr "Référence compte analytique" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Product" +msgstr "Article" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Product Reference" +msgstr "Réf. Article" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Unit of Measure" +msgstr "Unité de mesure" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Qty" +msgstr "Qty" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Statement" +msgstr "Relevé" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Invoice" +msgstr "Facture" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Residual Amount" +msgstr "Montant résiduel" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Res. Am. in Curr." +msgstr "Montant résiduel en dev." + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Notes" +msgstr "Notes" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Lit." +msgstr "Lit." + diff --git a/account_move_line_report_xls/i18n/nl.po b/account_move_line_report_xls/i18n/nl.po new file mode 100644 index 00000000..fed06d5c --- /dev/null +++ b/account_move_line_report_xls/i18n/nl.po @@ -0,0 +1,181 @@ +# Dutch translation of Odoo. +# This file contains the translation of the following modules: +# * account_move_line_report_xls +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 8.0\n" +"Report-Msgid-Bugs-To: support@noviat.com\n" +"POT-Creation-Date: 2016-05-16 12:31:16.564000\n" +"PO-Revision-Date: 2016-05-16 12:31:16.564000\n" +"Last-Translator: Luc De Meyer (Noviat nv/sa)\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Journal Items" +msgstr "Boekingsregels" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Entry" +msgstr "Boeking" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Name" +msgstr "Naam" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Reference" +msgstr "Referentie" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Effective Date" +msgstr "Datum" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Period" +msgstr "Periode" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Partner" +msgstr "Partner" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Partner Reference" +msgstr "Ref. Partner" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Account" +msgstr "Rekening" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Maturity Date" +msgstr "Vervaldatum" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Debit" +msgstr "Debet" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Credit" +msgstr "Credit" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Balance" +msgstr "Saldo" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Rec." +msgstr "Rec." + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Part. Rec." +msgstr "Rec. Part." + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Tax Code" +msgstr "BTW vak" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Tax/Base Amount" +msgstr "Bedrag BTW/Mvh" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Am. Currency" +msgstr "Bedrag valuta" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Curr." +msgstr "Val." + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Journal" +msgstr "Dagboek" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Comp. Curr." +msgstr "Bedr. Val." + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Analytic Account" +msgstr "Kostenplaats" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Analytic Account Reference" +msgstr "Kostenplaats referentie" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Product" +msgstr "Product" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Product Reference" +msgstr "Ref. Product" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Unit of Measure" +msgstr "Maateenheid" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Qty" +msgstr "HvH" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Statement" +msgstr "Uitreksel" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Invoice" +msgstr "Factuur" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Residual Amount" +msgstr "Restbedrag" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Res. Am. in Curr." +msgstr "Restbedrag in val." + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Notes" +msgstr "Notities" + +#. module: account_move_line_report_xls +#: report:move.line.list.xls:0 +msgid "Lit." +msgstr "Lit." + diff --git a/account_move_line_report_xls/models/__init__.py b/account_move_line_report_xls/models/__init__.py new file mode 100644 index 00000000..8795b3be --- /dev/null +++ b/account_move_line_report_xls/models/__init__.py @@ -0,0 +1 @@ +from . import account_move_line diff --git a/account_move_line_report_xls/models/account_move_line.py b/account_move_line_report_xls/models/account_move_line.py new file mode 100644 index 00000000..3539d09b --- /dev/null +++ b/account_move_line_report_xls/models/account_move_line.py @@ -0,0 +1,48 @@ +# Copyright 2009-2018 Noviat. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, models +from odoo.addons.report_xlsx_helper.report.report_xlsx_abstract \ + import ReportXlsxAbstract +_render = ReportXlsxAbstract._render + + +class AccountMoveLine(models.Model): + _inherit = 'account.move.line' + + # Change list in custom module e.g. to add/drop columns or change order + @api.model + def _report_xlsx_fields(self): + return [ + 'move', 'name', 'date', 'journal', 'partner', 'account', + 'date_maturity', 'debit', 'credit', 'balance', + 'full_reconcile', 'reconcile_amount', + # 'analytic_account_name', 'analytic_account', + # 'ref', 'partner_ref', + # 'amount_residual', 'amount_currency', 'currency_name', + # 'company_currency', 'amount_residual_currency', + # 'product', 'product_ref', 'product_uom', 'quantity', + # 'statement', 'invoice', 'narration', 'blocked', + # 'id', 'matched_debit_ids', 'matched_credit_ids', + ] + + # Change/Add Template entries + @api.model + def _report_xlsx_template(self): + """ + Template updates, e.g. + + my_change = { + 'move': { + 'header': { + 'value': 'My Move Title', + }, + 'lines': { + 'value': _render("line.move_id.name or ''"), + }, + 'width': 20, + }, + } + return my_change + """ + return {} diff --git a/account_move_line_report_xls/report/__init__.py b/account_move_line_report_xls/report/__init__.py new file mode 100644 index 00000000..dc9f47cd --- /dev/null +++ b/account_move_line_report_xls/report/__init__.py @@ -0,0 +1 @@ +from . import account_move_line_xlsx diff --git a/account_move_line_report_xls/report/account_move_line_xlsx.py b/account_move_line_report_xls/report/account_move_line_xlsx.py new file mode 100644 index 00000000..c107c329 --- /dev/null +++ b/account_move_line_report_xls/report/account_move_line_xlsx.py @@ -0,0 +1,444 @@ +# Copyright 2009-2018 Noviat +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import logging + +from odoo import models +from odoo.tools.translate import translate + +_logger = logging.getLogger(__name__) + +IR_TRANSLATION_NAME = 'move.line.list.xls' + + +class AccountMoveLineXlsx(models.AbstractModel): + _name = 'report.account_move_line_report_xls.account_move_line_xlsx' + _inherit = 'report.report_xlsx.abstract' + + def _(self, src): + lang = self.env.context.get('lang', 'en_US') + val = translate( + self.env.cr, IR_TRANSLATION_NAME, 'report', lang, src) or src + return val + + def _get_ws_params(self, workbook, data, amls): + + # XLSX Template + col_specs = { + 'move': { + 'header': { + 'value': self._('Entry'), + }, + 'lines': { + 'value': self._render("line.move_id.name"), + }, + 'width': 20, + }, + 'name': { + 'header': { + 'value': self._('Name'), + }, + 'lines': { + 'value': self._render("line.name"), + }, + 'width': 42, + }, + 'ref': { + 'header': { + 'value': self._('Reference'), + }, + 'lines': { + 'value': self._render("line.ref"), + }, + 'width': 42, + }, + 'date': { + 'header': { + 'value': self._('Effective Date'), + }, + 'lines': { + 'value': self._render( + "datetime.strptime(line.date, '%Y-%m-%d')"), + 'format': self.format_tcell_date_left, + }, + 'width': 13, + }, + 'partner': { + 'header': { + 'value': self._('Partner'), + }, + 'lines': { + 'value': self._render( + "line.partner_id and line.partner_id.name"), + }, + 'width': 36, + }, + 'partner_ref': { + 'header': { + 'value': self._('Partner Reference'), + }, + 'lines': { + 'value': self._render( + "line.partner_id and line.partner_id.ref"), + }, + 'width': 36, + }, + 'account': { + 'header': { + 'value': self._('Account'), + }, + 'lines': { + 'value': self._render( + "line.account_id.code"), + }, + 'width': 12, + }, + 'date_maturity': { + 'header': { + 'value': self._('Maturity Date'), + }, + 'lines': { + 'value': self._render( + "datetime.strptime(line.date_maturity,'%Y-%m-%d')"), + 'format': self.format_tcell_date_left, + }, + 'width': 13, + }, + 'debit': { + 'header': { + 'value': self._('Debit'), + 'format': self.format_theader_yellow_right, + }, + 'lines': { + 'value': self._render("line.debit"), + 'format': self.format_tcell_amount_right, + }, + 'totals': { + 'type': 'formula', + 'value': self._render("debit_formula"), + 'format': self.format_theader_yellow_amount_right, + }, + 'width': 18, + }, + 'credit': { + 'header': { + 'value': self._('Credit'), + 'format': self.format_theader_yellow_right, + }, + 'lines': { + 'value': self._render("line.credit"), + 'format': self.format_tcell_amount_right, + }, + 'totals': { + 'type': 'formula', + 'value': self._render("credit_formula"), + 'format': self.format_theader_yellow_amount_right, + }, + 'width': 18, + }, + 'balance': { + 'header': { + 'value': self._('Balance'), + 'format': self.format_theader_yellow_right, + }, + 'lines': { + 'value': self._render("line.balance"), + 'format': self.format_tcell_amount_right, + }, + 'totals': { + 'type': 'formula', + 'value': self._render("bal_formula"), + 'format': self.format_theader_yellow_amount_right, + }, + 'width': 18, + }, + 'full_reconcile': { + 'header': { + 'value': self._('Rec.'), + 'format': self.format_theader_yellow_center, + }, + 'lines': { + 'value': self._render( + "line.full_reconcile_id " + "and line.full_reconcile_id.name"), + 'format': self.format_tcell_center, + }, + 'width': 12, + }, + 'reconcile_amount': { + 'header': { + 'value': self._('Reconcile Amount'), + }, + 'lines': { + 'value': self._render( + "line.full_reconcile_id and line.balance or " + "(sum(line.matched_credit_ids.mapped('amount')) - " + "sum(line.matched_debit_ids.mapped('amount')))"), + 'format': self.format_tcell_amount_right, + }, + 'width': 12, + }, + 'matched_debit_ids': { + 'header': { + 'value': self._('Matched Debits'), + }, + 'lines': { + 'value': self._render( + "line.matched_debit_ids " + "and str([x.debit_move_id.id " + "for x in line.matched_debit_ids])"), + }, + 'width': 20, + }, + 'matched_credit_ids': { + 'header': { + 'value': self._('Matched Credits'), + }, + 'lines': { + 'value': self._render( + "line.matched_credit_ids " + "and str([x.credit_move_id.id " + "for x in line.matched_credit_ids])"), + }, + 'width': 20, + }, + 'amount_currency': { + 'header': { + 'value': self._('Am. Currency'), + 'format': self.format_theader_yellow_right, + }, + 'lines': { + 'value': self._render("line.amount_currency"), + 'format': self.format_tcell_amount_right, + }, + 'width': 18, + }, + 'currency_name': { + 'header': { + 'value': self._('Curr.'), + 'format': self.format_theader_yellow_center, + }, + 'lines': { + 'value': self._render( + "line.currency_id and line.currency_id.name"), + 'format': self.format_tcell_center, + }, + 'width': 6, + }, + 'journal': { + 'header': { + 'value': self._('Journal'), + }, + 'lines': { + 'value': self._render("line.journal_id.code"), + }, + 'width': 12, + }, + 'company_currency': { + 'header': { + 'value': self._('Comp. Curr.'), + 'format': self.format_theader_yellow_center, + }, + 'lines': { + 'value': self._render( + "line.company_id.currency_id.name"), + 'format': self.format_tcell_center, + }, + 'width': 10, + }, + 'analytic_account': { + 'header': { + 'value': self._('Analytic Account Reference'), + }, + 'lines': { + 'value': self._render( + "line.analytic_account_id " + "and line.analytic_account_id.code"), + }, + 'width': 36, + }, + 'analytic_account_name': { + 'header': { + 'value': self._('Analytic Account'), + }, + 'lines': { + 'value': self._render( + "line.analytic_account_id " + "and line.analytic_account_id.name"), + }, + 'width': 36, + }, + 'product': { + 'header': { + 'value': self._('Product'), + }, + 'lines': { + 'value': self._render( + "line.product_id and line.product_id.name"), + }, + 'width': 36, + }, + 'product_ref': { + 'header': { + 'value': self._('Product Reference'), + }, + 'lines': { + 'value': self._render( + "line.product_id and line.product_id.default_code " + "or ''"), + }, + 'width': 36, + }, + 'product_uom': { + 'header': { + 'value': self._('Unit of Measure'), + }, + 'lines': { + 'value': self._render( + "line.product_uom_id and line.product_uom_id.name"), + }, + 'width': 20, + }, + 'quantity': { + 'header': { + 'value': self._('Qty'), + 'format': self.format_theader_yellow_right, + }, + 'lines': { + 'value': self._render("line.quantity"), + 'format': self.format_tcell_amount_right, + }, + 'width': 8, + }, + 'statement': { + 'header': { + 'value': self._('Statement'), + }, + 'lines': { + 'value': self._render( + "line.statement_id and line.statement_id.name"), + }, + 'width': 20, + }, + 'invoice': { + 'header': { + 'value': self._('Invoice'), + }, + 'lines': { + 'value': self._render( + "line.invoice_id and line.invoice_id.number"), + }, + 'width': 20, + }, + 'amount_residual': { + 'header': { + 'value': self._('Residual Amount'), + 'format': self.format_theader_yellow_right, + }, + 'lines': { + 'value': self._render("line.amount_residual"), + 'format': self.format_tcell_amount_right, + }, + 'width': 18, + }, + 'amount_residual_currency': { + 'header': { + 'value': self._('Res. Am. in Curr.'), + 'format': self.format_theader_yellow_right, + }, + 'lines': { + 'value': self._render("line.amount_residual_currency"), + 'format': self.format_tcell_amount_right, + }, + 'width': 18, + }, + 'narration': { + 'header': { + 'value': self._('Notes'), + }, + 'lines': { + 'value': self._render("line.move_id.narration or ''"), + }, + 'width': 42, + }, + 'blocked': { + 'header': { + 'value': self._('Lit.'), + 'format': self.format_theader_yellow_center, + }, + 'lines': { + 'value': self._render("line.blocked and 'x' or ''"), + 'format': self.format_tcell_center, + }, + 'width': 4, + }, + 'id': { + 'header': { + 'value': self._('Id'), + 'format': self.format_theader_yellow_right, + }, + 'lines': { + 'value': self._render("line.id"), + 'format': self.format_tcell_integer_right, + }, + 'width': 12, + }, + } + col_specs.update(self.env['account.move.line']._report_xlsx_template()) + wanted_list = self.env['account.move.line']._report_xlsx_fields() + title = self._("Journal Items") + + return [{ + 'ws_name': title, + 'generate_ws_method': '_amls_export', + 'title': title, + 'wanted_list': wanted_list, + 'col_specs': col_specs, + }] + + def _amls_export(self, workbook, ws, ws_params, data, amls): + + ws.set_landscape() + ws.fit_to_pages(1, 0) + ws.set_header(self.xls_headers['standard']) + ws.set_footer(self.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=self.format_theader_yellow_left) + + ws.freeze_panes(row_pos, 0) + + wanted_list = ws_params['wanted_list'] + debit_pos = 'debit' in wanted_list and wanted_list.index('debit') + credit_pos = 'credit' in wanted_list and wanted_list.index('credit') + + for line in amls: + row_pos = self._write_line( + ws, row_pos, ws_params, col_specs_section='lines', + render_space={'line': line}, + default_format=self.format_tcell_left) + + aml_cnt = len(amls) + debit_start = self._rowcol_to_cell(row_pos - aml_cnt, debit_pos) + debit_stop = self._rowcol_to_cell(row_pos - 1, debit_pos) + debit_formula = 'SUM(%s:%s)' % (debit_start, debit_stop) + credit_start = self._rowcol_to_cell(row_pos - aml_cnt, credit_pos) + credit_stop = self._rowcol_to_cell(row_pos - 1, credit_pos) + credit_formula = 'SUM(%s:%s)' % (credit_start, credit_stop) + debit_cell = self._rowcol_to_cell(row_pos, debit_pos) + credit_cell = self._rowcol_to_cell(row_pos, credit_pos) + bal_formula = debit_cell + '-' + credit_cell + row_pos = self._write_line( + ws, row_pos, ws_params, col_specs_section='totals', + render_space={ + 'debit_formula': debit_formula, + 'credit_formula': credit_formula, + 'bal_formula': bal_formula, + }, + default_format=self.format_theader_yellow_left) diff --git a/account_move_line_report_xls/report/account_move_line_xlsx.xml b/account_move_line_report_xls/report/account_move_line_xlsx.xml new file mode 100644 index 00000000..3f55dffb --- /dev/null +++ b/account_move_line_report_xls/report/account_move_line_xlsx.xml @@ -0,0 +1,15 @@ + + + + + Export Selected Lines + account.move.line + ir.actions.report + action + + account_move_line_report_xls.account_move_line_xlsx + account_move_line + xlsx + + + diff --git a/account_move_line_report_xls/static/description/icon.png b/account_move_line_report_xls/static/description/icon.png new file mode 100644 index 00000000..9fede7eb Binary files /dev/null and b/account_move_line_report_xls/static/description/icon.png differ diff --git a/account_move_line_report_xls/static/description/icon.svg b/account_move_line_report_xls/static/description/icon.svg new file mode 100644 index 00000000..32210f51 --- /dev/null +++ b/account_move_line_report_xls/static/description/icon.svg @@ -0,0 +1,320 @@ + + + + + + + + spain_provinces + + + + spain + geography + + + + + sherrera + + + + + sherrera + + + + + sherrera + + + + image/svg+xml + + + en + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/account_move_line_report_xls/static/description/index.html b/account_move_line_report_xls/static/description/index.html new file mode 100644 index 00000000..ed6e2b8e --- /dev/null +++ b/account_move_line_report_xls/static/description/index.html @@ -0,0 +1,108 @@ + + +
+
+
+

Journal Items Excel export

+
+
+
+ +
+
+
+

+ This module extends the functionality of the journal items ('account.move.line') list view and allow you to export the selected lines. +

+
+
+
+ +
+
+
+

Installation & Usage

+

+ To install this module, you need also the report_xls module located in: +
https://github.com/OCA/reporting-engine +

+

+ To use this module, you need to: +

    +
  • go to the list view of the journal items
  • +
  • select the lines you wish to export
  • +
  • click on the button on top to export
  • +
+

+

+ The Excel export can be tailored to your exact needs via the following methods of the 'account.move.line' object: +

    +
  • + _report_xlsx_fields +
    + Add/drop columns or change order from the list of columns that are defined in the Excel template. +
    + The following fields are available: +
    + + move, name, date, journal, partner, account, +
    + date_maturity, debit, credit, balance, +
    + reconcile, analytic_account, +
    + ref, partner_ref, amount_residual, +
    + amount_currency, currency_name, company_currency, +
    + amount_residual_currency, product, product_ref', product_uom, quantity, +
    + statement, invoice, narration, blocked +
    +
    +
    +
  • +
+

+

+

    +
  • + _report_xlsx_template +
    + Change/extend the Excel template. +
    +
  • +
+

+
+
+
+ +
+
+
+

Maintainer

+
+
+

+ This module is maintained by the OCA. +

+

+ 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. +

+

+ To contribute to this module, please visit http://odoo-community.org. +

+
+ +
+
+ +
diff --git a/account_move_line_report_xls/static/description/journal_items.png b/account_move_line_report_xls/static/description/journal_items.png new file mode 100644 index 00000000..0dcacd2b Binary files /dev/null and b/account_move_line_report_xls/static/description/journal_items.png differ diff --git a/account_move_line_report_xls/static/description/oca_logo.png b/account_move_line_report_xls/static/description/oca_logo.png new file mode 100644 index 00000000..ef62eaf9 Binary files /dev/null and b/account_move_line_report_xls/static/description/oca_logo.png differ diff --git a/account_move_line_report_xls/tests/__init__.py b/account_move_line_report_xls/tests/__init__.py new file mode 100644 index 00000000..f1626769 --- /dev/null +++ b/account_move_line_report_xls/tests/__init__.py @@ -0,0 +1 @@ +from . import test_aml_report_xlsx diff --git a/account_move_line_report_xls/tests/test_aml_report_xlsx.py b/account_move_line_report_xls/tests/test_aml_report_xlsx.py new file mode 100644 index 00000000..462e6dec --- /dev/null +++ b/account_move_line_report_xls/tests/test_aml_report_xlsx.py @@ -0,0 +1,36 @@ +# Copyright 2009-2018 Noviat. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo.tests.common import TransactionCase + + +class TestAmlReportXlsx(TransactionCase): + + def setUp(self): + super(TestAmlReportXlsx, self).setUp() + self.report = self.env.ref( + 'account_move_line_report_xls.action_account_move_line_xlsx') + sale_journal = self.env['account.journal'].search( + [('type', '=', 'sale')])[0] + ar = self.env['account.account'].search( + [('internal_type', '=', 'receivable')])[0] + aml_vals = [ + {'name': 'debit', + 'debit': 100, + 'account_id': ar.id, + }, + {'name': 'credit', + 'credit': 100, + 'account_id': ar.id, + }, + ] + am = self.env['account.move'].create({ + 'name': 'test', + 'journal_id': sale_journal.id, + 'line_ids': [(0, 0, x) for x in aml_vals], + }) + self.amls = am.line_ids + + def test_aml_report_xlsx(self): + report_xls = self.report.render_xlsx(self.amls.ids, None) + self.assertEqual(report_xls[1], 'xlsx') diff --git a/report_xlsx_helper/README.rst b/report_xlsx_helper/README.rst new file mode 100644 index 00000000..7f368768 --- /dev/null +++ b/report_xlsx_helper/README.rst @@ -0,0 +1,88 @@ +.. image:: https://img.shields.io/badge/license-AGPL--3-blue.png + :target: https://www.gnu.org/licenses/agpl + :alt: License: AGPL-3 + +=========================== +Excel report engine helpers +=========================== + +This module provides a set of tools to facilitate the creation of excel reports with format xlsx. + +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. + +Installation +============ + +This module requires report_xlsx version 11.0.1.03 or higher. + +Configuration and Usage +======================= + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/143/11.0 + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues +`_. In case of trouble, please +check there if your issue has already been reported. If you spotted it first, +help us smashing it by providing a detailed and welcomed feedback. + +Credits +======= + +Contributors +------------ + +* Luc De Meyer + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +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. + +To contribute to this module, please visit http://odoo-community.org. diff --git a/report_xlsx_helper/__init__.py b/report_xlsx_helper/__init__.py new file mode 100644 index 00000000..9b6fa04e --- /dev/null +++ b/report_xlsx_helper/__init__.py @@ -0,0 +1,3 @@ +from . import controllers +from . import models +from . import report diff --git a/report_xlsx_helper/__manifest__.py b/report_xlsx_helper/__manifest__.py new file mode 100644 index 00000000..afd22481 --- /dev/null +++ b/report_xlsx_helper/__manifest__.py @@ -0,0 +1,16 @@ +# Copyright 2009-2018 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': '11.0.1.0.0', + 'license': 'AGPL-3', + 'depends': [ + 'report_xlsx', + ], + 'installable': True, +} diff --git a/report_xlsx_helper/controllers/__init__.py b/report_xlsx_helper/controllers/__init__.py new file mode 100644 index 00000000..12a7e529 --- /dev/null +++ b/report_xlsx_helper/controllers/__init__.py @@ -0,0 +1 @@ +from . import main diff --git a/report_xlsx_helper/controllers/main.py b/report_xlsx_helper/controllers/main.py new file mode 100644 index 00000000..abaf974d --- /dev/null +++ b/report_xlsx_helper/controllers/main.py @@ -0,0 +1,54 @@ +# Copyright 2009-2018 Noviat. +# License AGPL-3.0 or later (https://www.gnuorg/licenses/agpl.html). + +import json + +from odoo.addons.report_xlsx.controllers.main import ReportController +from odoo.http import content_disposition, route, request + + +class ReportController(ReportController): + + @route([ + '/report//', + '/report///', + ], 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'): + # Ignore 'lang' here, because the context in data is the one + # from the webclient *but* if the user explicitely wants to + # change the lang, this mechanism overwrites it. + data['context'] = json.loads(data['context']) + if data['context'].get('lang'): + del data['context']['lang'] + context.update(data['context']) + context['report_name'] = reportname + + xlsx = report.with_context(context).render_xlsx( + 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(ReportController, self).report_routes( + reportname, docids, converter, **data) diff --git a/report_xlsx_helper/i18n/report_xlsx_helper.pot b/report_xlsx_helper/i18n/report_xlsx_helper.pot new file mode 100644 index 00000000..81aed537 --- /dev/null +++ b/report_xlsx_helper/i18n/report_xlsx_helper.pot @@ -0,0 +1,67 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * report_xlsx_helper +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.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 +#: code:addons/report_xlsx_helper/report/abstract_report_xlsx.py:520 +#, python-format +msgid "%s, _write_line : programming error detected while processing col_specs_section %s, column %s" +msgstr "" + +#. module: report_xlsx_helper +#: code:addons/report_xlsx_helper/report/abstract_report_xlsx.py:526 +#, python-format +msgid ", cellvalue %s" +msgstr "" + +#. module: report_xlsx_helper +#: code:addons/report_xlsx_helper/report/abstract_report_xlsx.py:52 +#, python-format +msgid "Programming Error:\n" +"\n" +"Excel Sheet name '%s' contains unsupported special characters: '%s'." +msgstr "" + +#. module: report_xlsx_helper +#: code:addons/report_xlsx_helper/report/abstract_report_xlsx.py:46 +#, python-format +msgid "Programming Error:\n" +"\n" +"Excel Sheet name '%s' should not exceed %s characters." +msgstr "" + +#. module: report_xlsx_helper +#: code:addons/report_xlsx_helper/report/abstract_report_xlsx.py:448 +#, python-format +msgid "Programming Error:\n" +"\n" +"The '%s' column is not defined in the worksheet column specifications." +msgstr "" + +#. module: report_xlsx_helper +#: code:addons/report_xlsx_helper/report/abstract_report_xlsx.py:489 +#, python-format +msgid "Programming Error:\n" +"\n" +"The '%s' column is not defined the worksheet column specifications." +msgstr "" + +#. module: report_xlsx_helper +#: code:addons/report_xlsx_helper/report/abstract_report_xlsx.py:462 +#, python-format +msgid "Programming Error:\n" +"\n" +"The 'title' parameter is mandatory when calling the '_write_ws_title' method." +msgstr "" + diff --git a/report_xlsx_helper/models/__init__.py b/report_xlsx_helper/models/__init__.py new file mode 100644 index 00000000..a248cf21 --- /dev/null +++ b/report_xlsx_helper/models/__init__.py @@ -0,0 +1 @@ +from . import ir_actions_report diff --git a/report_xlsx_helper/models/ir_actions_report.py b/report_xlsx_helper/models/ir_actions_report.py new file mode 100644 index 00000000..81db27d0 --- /dev/null +++ b/report_xlsx_helper/models/ir_actions_report.py @@ -0,0 +1,21 @@ +# 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, 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(IrActionsReport, self).render_xlsx(docids, data) diff --git a/report_xlsx_helper/report/__init__.py b/report_xlsx_helper/report/__init__.py new file mode 100644 index 00000000..3222e9d5 --- /dev/null +++ b/report_xlsx_helper/report/__init__.py @@ -0,0 +1,2 @@ +from . import report_xlsx_abstract +from . import test_partner_report_xlsx diff --git a/report_xlsx_helper/report/report_xlsx_abstract.py b/report_xlsx_helper/report/report_xlsx_abstract.py new file mode 100644 index 00000000..b93cf2a4 --- /dev/null +++ b/report_xlsx_helper/report/report_xlsx_abstract.py @@ -0,0 +1,551 @@ +# Copyright 2009-2018 Noviat. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from datetime import datetime +import re +from types import CodeType +from xlsxwriter.utility import xl_rowcol_to_cell + +from odoo import fields, models, _ +from odoo.exceptions import UserError + + +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 '%s' should not exceed %s characters." + ) % (name, max_chars)) + special_chars = pattern.findall(name) + if special_chars: + raise UserError(_( + "Programming Error:\n\n" + "Excel Sheet name '%s' contains unsupported special " + "characters: '%s'." + ) % (name, 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 + } + self.xls_headers = { + 'standard': '' + } + report_date = fields.Datetime.context_timestamp( + self.env.user, datetime.now()).strftime('%Y-%m-%d %H:%M') + self.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_yellow = '#FFFFCC' + bg_blue = '#CCFFFF' + num_format = '#,##0.00' + num_format_conditional = '{0};[Red]-{0};{0}'.format(num_format) + pct_format = '#,##0.00%' + pct_format_conditional = '{0};[Red]-{0};{0}'.format(pct_format) + int_format = '#,##0' + int_format_conditional = '{0};[Red]-{0};{0}'.format(int_format) + date_format = 'YYYY-MM-DD' + theader_yellow = dict(theader, bg_color=bg_yellow) + theader_blue = dict(theader, bg_color=bg_blue) + + # format for worksheet title + self.format_ws_title = workbook.add_format( + {'bold': True, 'font_size': 14}) + + # no border formats + self.format_left = workbook.add_format({'align': 'left'}) + self.format_center = workbook.add_format({'align': 'center'}) + self.format_right = workbook.add_format({'align': 'right'}) + self.format_amount_left = workbook.add_format( + {'align': 'left', 'num_format': num_format}) + self.format_amount_center = workbook.add_format( + {'align': 'center', 'num_format': num_format}) + self.format_amount_right = workbook.add_format( + {'align': 'right', 'num_format': num_format}) + self.format_amount_conditional_left = workbook.add_format( + {'align': 'left', 'num_format': num_format_conditional}) + self.format_amount_conditional_center = workbook.add_format( + {'align': 'center', 'num_format': num_format_conditional}) + self.format_amount_conditional_right = workbook.add_format( + {'align': 'right', 'num_format': num_format_conditional}) + self.format_percent_left = workbook.add_format( + {'align': 'left', 'num_format': pct_format}) + self.format_percent_center = workbook.add_format( + {'align': 'center', 'num_format': pct_format}) + self.format_percent_right = workbook.add_format( + {'align': 'right', 'num_format': pct_format}) + self.format_percent_conditional_left = workbook.add_format( + {'align': 'left', 'num_format': pct_format_conditional}) + self.format_percent_conditional_center = workbook.add_format( + {'align': 'center', 'num_format': pct_format_conditional}) + self.format_percent_conditional_right = workbook.add_format( + {'align': 'right', 'num_format': pct_format_conditional}) + self.format_integer_left = workbook.add_format( + {'align': 'left', 'num_format': int_format}) + self.format_integer_center = workbook.add_format( + {'align': 'center', 'num_format': int_format}) + self.format_integer_right = workbook.add_format( + {'align': 'right', 'num_format': int_format}) + self.format_integer_conditional_left = workbook.add_format( + {'align': 'right', 'num_format': int_format_conditional}) + self.format_integer_conditional_center = workbook.add_format( + {'align': 'center', 'num_format': int_format_conditional}) + self.format_integer_conditional_right = workbook.add_format( + {'align': 'right', 'num_format': int_format_conditional}) + self.format_date_left = workbook.add_format( + {'align': 'left', 'num_format': date_format}) + self.format_date_center = workbook.add_format( + {'align': 'center', 'num_format': date_format}) + self.format_date_right = workbook.add_format( + {'align': 'right', 'num_format': date_format}) + + self.format_left_bold = workbook.add_format( + {'align': 'left', 'bold': True}) + self.format_center_bold = workbook.add_format( + {'align': 'center', 'bold': True}) + self.format_right_bold = workbook.add_format( + {'align': 'right', 'bold': True}) + self.format_amount_left_bold = workbook.add_format( + {'align': 'left', 'bold': True, 'num_format': num_format}) + self.format_amount_center_bold = workbook.add_format( + {'align': 'center', 'bold': True, 'num_format': num_format}) + self.format_amount_right_bold = workbook.add_format( + {'align': 'right', 'bold': True, 'num_format': num_format}) + self.format_amount_conditional_left_bold = workbook.add_format( + {'align': 'left', 'bold': True, + 'num_format': num_format_conditional}) + self.format_amount_conditional_center_bold = workbook.add_format( + {'align': 'center', 'bold': True, + 'num_format': num_format_conditional}) + self.format_amount_conditional_right_bold = workbook.add_format( + {'align': 'right', 'bold': True, + 'num_format': num_format_conditional}) + self.format_percent_left_bold = workbook.add_format( + {'align': 'left', 'bold': True, 'num_format': pct_format}) + self.format_percent_center_bold = workbook.add_format( + {'align': 'center', 'bold': True, 'num_format': pct_format}) + self.format_percent_right_bold = workbook.add_format( + {'align': 'right', 'bold': True, 'num_format': pct_format}) + self.format_percent_conditional_left_bold = workbook.add_format( + {'align': 'left', 'bold': True, + 'num_format': pct_format_conditional}) + self.format_percent_conditional_center_bold = workbook.add_format( + {'align': 'center', 'bold': True, + 'num_format': pct_format_conditional}) + self.format_percent_conditional_right_bold = workbook.add_format( + {'align': 'right', 'bold': True, + 'num_format': pct_format_conditional}) + self.format_integer_left_bold = workbook.add_format( + {'align': 'left', 'bold': True, 'num_format': int_format}) + self.format_integer_center_bold = workbook.add_format( + {'align': 'center', 'bold': True, 'num_format': int_format}) + self.format_integer_right_bold = workbook.add_format( + {'align': 'right', 'bold': True, 'num_format': int_format}) + self.format_integer_conditional_left_bold = workbook.add_format( + {'align': 'left', 'bold': True, + 'num_format': int_format_conditional}) + self.format_integer_conditional_center_bold = workbook.add_format( + {'align': 'center', 'bold': True, + 'num_format': int_format_conditional}) + self.format_integer_conditional_right_bold = workbook.add_format( + {'align': 'right', 'bold': True, + 'num_format': int_format_conditional}) + self.format_date_left_bold = workbook.add_format( + {'align': 'left', 'bold': True, 'num_format': date_format}) + self.format_date_center_bold = workbook.add_format( + {'align': 'center', 'bold': True, 'num_format': date_format}) + self.format_date_right_bold = workbook.add_format( + {'align': 'right', 'bold': True, 'num_format': date_format}) + + # formats for worksheet table column headers + self.format_theader_yellow_left = workbook.add_format(theader_yellow) + self.format_theader_yellow_center = workbook.add_format( + dict(theader_yellow, align='center')) + self.format_theader_yellow_right = workbook.add_format( + dict(theader_yellow, align='right')) + self.format_theader_yellow_amount_left = workbook.add_format( + dict(theader_yellow, num_format=num_format, align='left')) + self.format_theader_yellow_amount_center = workbook.add_format( + dict(theader_yellow, num_format=num_format, align='center')) + self.format_theader_yellow_amount_right = workbook.add_format( + dict(theader_yellow, num_format=num_format, align='right')) + + self.format_theader_yellow_amount_conditional_left = workbook.\ + add_format(dict(theader_yellow, num_format=num_format_conditional, + align='left')) + self.format_theader_yellow_amount_conditional_center = workbook.\ + add_format(dict(theader_yellow, num_format=num_format_conditional, + align='center')) + self.format_theader_yellow_amount_conditional_right = workbook.\ + add_format(dict(theader_yellow, num_format=num_format_conditional, + align='right')) + self.format_theader_yellow_percent_left = workbook.add_format( + dict(theader_yellow, num_format=pct_format, align='left')) + self.format_theader_yellow_percent_center = workbook.add_format( + dict(theader_yellow, num_format=pct_format, align='center')) + self.format_theader_yellow_percent_right = workbook.add_format( + dict(theader_yellow, num_format=pct_format, align='right')) + self.format_theader_yellow_percent_conditional_left = workbook.\ + add_format(dict(theader_yellow, num_format=pct_format_conditional, + align='left')) + self.format_theader_yellow_percent_conditional_center = workbook.\ + add_format(dict(theader_yellow, num_format=pct_format_conditional, + align='center')) + self.format_theader_yellow_percent_conditional_right = workbook.\ + add_format(dict(theader_yellow, num_format=pct_format_conditional, + align='right')) + self.format_theader_yellow_integer_left = workbook.add_format( + dict(theader_yellow, num_format=int_format, align='left')) + self.format_theader_yellow_integer_center = workbook.add_format( + dict(theader_yellow, num_format=int_format, align='center')) + self.format_theader_yellow_integer_right = workbook.add_format( + dict(theader_yellow, num_format=int_format, align='right')) + self.format_theader_yellow_integer_conditional_left = workbook.\ + add_format(dict(theader_yellow, num_format=int_format_conditional, + align='left')) + self.format_theader_yellow_integer_conditional_center = workbook.\ + add_format(dict(theader_yellow, num_format=int_format_conditional, + align='center')) + self.format_theader_yellow_integer_conditional_right = workbook.\ + add_format(dict(theader_yellow, num_format=int_format_conditional, + align='right')) + + self.format_theader_blue_left = workbook.add_format(theader_blue) + self.format_theader_blue_center = workbook.add_format( + dict(theader_blue, align='center')) + self.format_theader_blue_right = workbook.add_format( + dict(theader_blue, align='right')) + self.format_theader_blue_amount_left = workbook.add_format( + dict(theader_blue, num_format=num_format, align='left')) + self.format_theader_blue_amount_center = workbook.add_format( + dict(theader_blue, num_format=num_format, align='center')) + self.format_theader_blue_amount_right = workbook.add_format( + dict(theader_blue, num_format=num_format, align='right')) + self.format_theader_blue_amount_conditional_left = workbook.\ + add_format(dict(theader_blue, num_format=num_format_conditional, + align='left')) + self.format_theader_blue_amount_conditional_center = workbook.\ + add_format(dict(theader_blue, num_format=num_format_conditional, + align='center')) + self.format_theader_blue_amount_conditional_right = workbook.\ + add_format(dict(theader_blue, num_format=num_format_conditional, + align='right')) + self.format_theader_blue_percent_left = workbook.add_format( + dict(theader_blue, num_format=pct_format, align='left')) + self.format_theader_blue_percent_center = workbook.add_format( + dict(theader_blue, num_format=pct_format, align='center')) + self.format_theader_blue_percent_right = workbook.add_format( + dict(theader_blue, num_format=pct_format, align='right')) + self.format_theader_blue_percent_conditional_left = workbook.\ + add_format(dict(theader_blue, num_format=pct_format_conditional, + align='left')) + self.format_theader_blue_percent_conditional_center = workbook.\ + add_format(dict(theader_blue, num_format=pct_format_conditional, + align='center')) + self.format_theader_blue_percent_conditional_right = workbook.\ + add_format(dict(theader_blue, num_format=pct_format_conditional, + align='right')) + self.format_theader_blue_integer_left = workbook.add_format( + dict(theader_blue, num_format=int_format, align='left')) + self.format_theader_blue_integer_center = workbook.add_format( + dict(theader_blue, num_format=int_format, align='center')) + self.format_theader_blue_integer_right = workbook.add_format( + dict(theader_blue, num_format=int_format, align='right')) + self.format_theader_blue_integer_conditional_left = workbook.\ + add_format(dict(theader_blue, num_format=int_format_conditional, + align='left')) + self.format_theader_blue_integer_conditional_center = workbook.\ + add_format(dict(theader_blue, num_format=int_format_conditional, + align='center')) + self.format_theader_blue_integer_conditional_right = workbook.\ + add_format(dict(theader_blue, num_format=int_format_conditional, + align='right')) + + # formats for worksheet table cells + self.format_tcell_left = workbook.add_format( + dict(border, align='left')) + self.format_tcell_center = workbook.add_format( + dict(border, align='center')) + self.format_tcell_right = workbook.add_format( + dict(border, align='right')) + self.format_tcell_amount_left = workbook.add_format( + dict(border, num_format=num_format, align='left')) + self.format_tcell_amount_center = workbook.add_format( + dict(border, num_format=num_format, align='center')) + self.format_tcell_amount_right = workbook.add_format( + dict(border, num_format=num_format, align='right')) + self.format_tcell_amount_conditional_left = workbook.add_format( + dict(border, num_format=num_format_conditional, align='left')) + self.format_tcell_amount_conditional_center = workbook.add_format( + dict(border, num_format=num_format_conditional, align='center')) + self.format_tcell_amount_conditional_right = workbook.add_format( + dict(border, num_format=num_format_conditional, align='right')) + self.format_tcell_percent_left = workbook.add_format( + dict(border, num_format=pct_format, align='left')) + self.format_tcell_percent_center = workbook.add_format( + dict(border, num_format=pct_format, align='center')) + self.format_tcell_percent_right = workbook.add_format( + dict(border, num_format=pct_format, align='right')) + self.format_tcell_percent_conditional_left = workbook.add_format( + dict(border, num_format=pct_format_conditional, align='left')) + self.format_tcell_percent_conditional_center = workbook.add_format( + dict(border, num_format=pct_format_conditional, align='center')) + self.format_tcell_percent_conditional_right = workbook.add_format( + dict(border, num_format=pct_format_conditional, align='right')) + self.format_tcell_integer_left = workbook.add_format( + dict(border, num_format=int_format, align='left')) + self.format_tcell_integer_center = workbook.add_format( + dict(border, num_format=int_format, align='center')) + self.format_tcell_integer_right = workbook.add_format( + dict(border, num_format=int_format, align='right')) + self.format_tcell_integer_conditional_left = workbook.add_format( + dict(border, num_format=int_format_conditional, align='left')) + self.format_tcell_integer_conditional_center = workbook.add_format( + dict(border, num_format=int_format_conditional, align='center')) + self.format_tcell_integer_conditional_right = workbook.add_format( + dict(border, num_format=int_format_conditional, align='right')) + self.format_tcell_date_left = workbook.add_format( + dict(border, num_format=date_format, align='left')) + self.format_tcell_date_center = workbook.add_format( + dict(border, num_format=date_format, align='center')) + self.format_tcell_date_right = workbook.add_format( + dict(border, num_format=date_format, align='right')) + + self.format_tcell_left_bold = workbook.add_format( + dict(border, align='left', bold=True)) + self.format_tcell_center_bold = workbook.add_format( + dict(border, align='center', bold=True)) + self.format_tcell_right_bold = workbook.add_format( + dict(border, align='right', bold=True)) + self.format_tcell_amount_left_bold = workbook.add_format( + dict(border, num_format=num_format, align='left', bold=True)) + self.format_tcell_amount_center_bold = workbook.add_format( + dict(border, num_format=num_format, align='center', bold=True)) + self.format_tcell_amount_right_bold = workbook.add_format( + dict(border, num_format=num_format, align='right', bold=True)) + self.format_tcell_amount_conditional_left_bold = workbook.\ + add_format(dict(border, num_format=num_format_conditional, + align='left', bold=True)) + self.format_tcell_amount_conditional_center_bold = workbook.\ + add_format(dict(border, num_format=num_format_conditional, + align='center', bold=True)) + self.format_tcell_amount_conditional_right_bold = workbook.\ + add_format(dict(border, num_format=num_format_conditional, + align='right', bold=True)) + self.format_tcell_percent_left_bold = workbook.add_format( + dict(border, num_format=pct_format, align='left', bold=True)) + self.format_tcell_percent_center_bold = workbook.add_format( + dict(border, num_format=pct_format, align='center', bold=True)) + self.format_tcell_percent_right_bold = workbook.add_format( + dict(border, num_format=pct_format, align='right', bold=True)) + self.format_tcell_percent_conditional_left_bold = workbook.\ + add_format(dict(border, num_format=pct_format_conditional, + align='left', bold=True)) + self.format_tcell_percent_conditional_center_bold = workbook.\ + add_format(dict(border, num_format=pct_format_conditional, + align='center', bold=True)) + self.format_tcell_percent_conditional_right_bold = workbook.\ + add_format(dict(border, num_format=pct_format_conditional, + align='right', bold=True)) + self.format_tcell_integer_left_bold = workbook.add_format( + dict(border, num_format=int_format, align='left', bold=True)) + self.format_tcell_integer_center_bold = workbook.add_format( + dict(border, num_format=int_format, align='center', bold=True)) + self.format_tcell_integer_right_bold = workbook.add_format( + dict(border, num_format=int_format, align='right', bold=True)) + self.format_tcell_integer_conditional_left_bold = workbook.\ + add_format(dict(border, num_format=int_format_conditional, + align='left', bold=True)) + self.format_tcell_integer_conditional_center_bold = workbook.\ + add_format(dict(border, num_format=int_format_conditional, + align='center', bold=True)) + self.format_tcell_integer_conditional_right_bold = workbook.\ + add_format(dict(border, num_format=int_format_conditional, + align='right', bold=True)) + self.format_tcell_date_left_bold = workbook.add_format( + dict(border, num_format=date_format, align='left', bold=True)) + self.format_tcell_date_center_bold = workbook.add_format( + dict(border, num_format=date_format, align='center', bold=True)) + self.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, self.format_ws_title) + else: + ws.write_string(row_pos, 0, title, self.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): + """ + 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' + else: + if not cell_value: + cell_type = 'blank' + else: + msg = _( + "%s, _write_line : programming error " + "detected while processing " + "col_specs_section %s, column %s" + ) % (__name__, col_specs_section, col) + if cell_value: + msg += _(", cellvalue %s") + raise UserError(msg) + colspan = cell_spec.get('colspan') or colspan + args_pos = [row_pos, pos] + args_data = [cell_value] + if cell_format: + args_data.append(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, 'write_%s' % cell_type) + args = args_pos + args_data + ws_method(*args) + pos += colspan + + return row_pos + 1 + + @staticmethod + def _render(code): + return compile(code, '', '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) diff --git a/report_xlsx_helper/report/test_partner_report_xlsx.py b/report_xlsx_helper/report/test_partner_report_xlsx.py new file mode 100644 index 00000000..53196771 --- /dev/null +++ b/report_xlsx_helper/report/test_partner_report_xlsx.py @@ -0,0 +1,94 @@ +# -*- coding: utf-8 -*- +# Copyright 2009-2018 Noviat. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import models + + +class TestPartnerXlsx(models.AbstractModel): + _name = 'report.report_xlsx_helper.test_partner_xlsx' + _inherit = 'report.report_xlsx.abstract' + + 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, + }, + 'is_customer': { + 'header': { + 'value': 'Customer', + }, + 'data': { + 'value': self._render("partner.customer"), + }, + 'width': 10, + }, + 'is_customer_formula': { + 'header': { + 'value': 'Customer Y/N ?', + }, + 'data': { + 'type': 'formula', + 'value': self._render("customer_formula"), + }, + 'width': 14, + }, + } + + 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(self.xls_headers['standard']) + ws.set_footer(self.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=self.format_theader_yellow_left) + ws.freeze_panes(row_pos, 0) + + wl = ws_params['wanted_list'] + + for partner in partners: + is_customer_pos = 'is_customer' in wl and \ + wl.index('is_customer') + is_customer_cell = self._rowcol_to_cell( + row_pos, is_customer_pos) + customer_formula = 'IF({},"Y", "N")'.format(is_customer_cell) + row_pos = self._write_line( + ws, row_pos, ws_params, col_specs_section='data', + render_space={ + 'partner': partner, + 'customer_formula': customer_formula, + }, + default_format=self.format_tcell_left) diff --git a/report_xlsx_helper/static/description/icon.png b/report_xlsx_helper/static/description/icon.png new file mode 100644 index 00000000..3a0328b5 Binary files /dev/null and b/report_xlsx_helper/static/description/icon.png differ diff --git a/report_xlsx_helper/tests/__init__.py b/report_xlsx_helper/tests/__init__.py new file mode 100644 index 00000000..e33d6b90 --- /dev/null +++ b/report_xlsx_helper/tests/__init__.py @@ -0,0 +1 @@ +from . import test_report_xlsx_helper diff --git a/report_xlsx_helper/tests/test_report_xlsx_helper.py b/report_xlsx_helper/tests/test_report_xlsx_helper.py new file mode 100644 index 00000000..059c1243 --- /dev/null +++ b/report_xlsx_helper/tests/test_report_xlsx_helper.py @@ -0,0 +1,23 @@ +# Copyright 2009-2018 Noviat. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo.tests.common import TransactionCase + + +class TestReportXlsxHelper(TransactionCase): + + def setUp(self): + super(TestReportXlsxHelper, self).setUp() + p1 = self.env.ref('base.res_partner_1') + p2 = self.env.ref('base.res_partner_2') + self.partners = p1 + p2 + ctx = { + 'report_name': 'report_xlsx_helper.test_partner_xlsx', + 'active_model': 'res.partner', + 'active_ids': self.partners.ids, + } + self.report = self.env['ir.actions.report'].with_context(ctx) + + def test_report_xlsx_helper(self): + report_xls = self.report.render_xlsx(None, None) + self.assertEqual(report_xls[1], 'xlsx') diff --git a/report_xlsx_helper_demo/README.rst b/report_xlsx_helper_demo/README.rst new file mode 100644 index 00000000..5730d9db --- /dev/null +++ b/report_xlsx_helper_demo/README.rst @@ -0,0 +1,58 @@ +.. image:: https://img.shields.io/badge/license-AGPL--3-blue.png + :target: https://www.gnu.org/licenses/agpl + :alt: License: AGPL-3 + +================================== +Excel report engine helpers - demo +================================== + +This module demonstrates the capabilities or the report_xlsx_helper module via +a basic example. + +Usage +===== + +Open a partner record and click on the 'Export XLS' button. + +Installation +============ + +There is no specific installation procedure for this module. + +Configuration and Usage +======================= + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/143/11.0 + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues +`_. In case of trouble, please +check there if your issue has already been reported. If you spotted it first, +help us smashing it by providing a detailed and welcomed feedback. + +Credits +======= + +Contributors +------------ + +* Luc De Meyer + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +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. + +To contribute to this module, please visit http://odoo-community.org. diff --git a/report_xlsx_helper_demo/__init__.py b/report_xlsx_helper_demo/__init__.py new file mode 100644 index 00000000..bf588bc8 --- /dev/null +++ b/report_xlsx_helper_demo/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import report diff --git a/report_xlsx_helper_demo/__manifest__.py b/report_xlsx_helper_demo/__manifest__.py new file mode 100644 index 00000000..c7c05651 --- /dev/null +++ b/report_xlsx_helper_demo/__manifest__.py @@ -0,0 +1,18 @@ +# Copyright 2009-2018 Noviat. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + 'name': 'Report xlsx helpers - demo', + 'author': 'Noviat,' + 'Odoo Community Association (OCA)', + 'category': 'Reporting', + 'version': '11.0.1.0.0', + 'license': 'AGPL-3', + 'depends': [ + 'report_xlsx_helper', + ], + 'data': [ + 'views/res_partner.xml', + ], + 'installable': True, +} diff --git a/report_xlsx_helper_demo/models/__init__.py b/report_xlsx_helper_demo/models/__init__.py new file mode 100644 index 00000000..91fed54d --- /dev/null +++ b/report_xlsx_helper_demo/models/__init__.py @@ -0,0 +1 @@ +from . import res_partner diff --git a/report_xlsx_helper_demo/models/res_partner.py b/report_xlsx_helper_demo/models/res_partner.py new file mode 100644 index 00000000..14fff263 --- /dev/null +++ b/report_xlsx_helper_demo/models/res_partner.py @@ -0,0 +1,25 @@ +# Copyright 2009-2018 Noviat +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, models + + +class ResPartner(models.Model): + _inherit = 'res.partner' + + @api.multi + def export_xls(self): + module = __name__.split('addons.')[1].split('.')[0] + report_name = '{}.partner_export_xlsx'.format(module) + report = { + 'type': 'ir.actions.report', + 'report_type': 'xlsx', + 'report_name': report_name, + # model name will be used if no report_file passed via context + 'context': dict(self.env.context, report_file='partner'), + # report_xlsx doesn't pass the context if the data dict is empty + # cf. report_xlsx\static\src\js\report\qwebactionmanager.js + # TODO: create PR on report_xlsx to fix this + 'data': {'dynamic_report': True}, + } + return report diff --git a/report_xlsx_helper_demo/report/__init__.py b/report_xlsx_helper_demo/report/__init__.py new file mode 100644 index 00000000..af555af1 --- /dev/null +++ b/report_xlsx_helper_demo/report/__init__.py @@ -0,0 +1 @@ +from . import partner_export_xlsx diff --git a/report_xlsx_helper_demo/report/partner_export_xlsx.py b/report_xlsx_helper_demo/report/partner_export_xlsx.py new file mode 100644 index 00000000..3ea0c813 --- /dev/null +++ b/report_xlsx_helper_demo/report/partner_export_xlsx.py @@ -0,0 +1,99 @@ +# -*- coding: utf-8 -*- +# Copyright 2009-2018 Noviat. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import models + + +class PartnerExportXlsx(models.AbstractModel): + _name = 'report.report_xlsx_helper_demo.partner_export_xlsx' + _inherit = 'report.report_xlsx.abstract' + + 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, + }, + 'is_customer': { + 'header': { + 'value': 'Customer', + }, + 'data': { + 'value': self._render("partner.customer"), + }, + 'width': 10, + }, + 'is_customer_formula': { + 'header': { + 'value': 'Customer Y/N ?', + }, + 'data': { + 'type': 'formula', + 'value': self._render("customer_formula"), + }, + 'width': 14, + }, + } + + wanted_list = [ + 'name', 'number_of_contacts', 'is_customer', + 'is_customer_formula'] + ws_params = { + 'ws_name': 'Partners', + 'generate_ws_method': '_partner_report', + 'title': 'Partners', + 'wanted_list': wanted_list, + '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(self.xls_headers['standard']) + ws.set_footer(self.xls_footers['standard']) + + self._set_column_width(ws, ws_params) + + row_pos = 0 + if len(partners) == 1: + ws_params['title'] = partners.name + 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=self.format_theader_yellow_left) + ws.freeze_panes(row_pos, 0) + + wl = ws_params['wanted_list'] + + for partner in partners: + is_customer_pos = 'is_customer' in wl and \ + wl.index('is_customer') + is_customer_cell = self._rowcol_to_cell( + row_pos, is_customer_pos) + customer_formula = 'IF({},"Y", "N")'.format(is_customer_cell) + row_pos = self._write_line( + ws, row_pos, ws_params, col_specs_section='data', + render_space={ + 'partner': partner, + 'customer_formula': customer_formula, + }, + default_format=self.format_tcell_left) diff --git a/report_xlsx_helper_demo/static/description/icon.png b/report_xlsx_helper_demo/static/description/icon.png new file mode 100644 index 00000000..3a0328b5 Binary files /dev/null and b/report_xlsx_helper_demo/static/description/icon.png differ diff --git a/report_xlsx_helper_demo/views/res_partner.xml b/report_xlsx_helper_demo/views/res_partner.xml new file mode 100644 index 00000000..63f46199 --- /dev/null +++ b/report_xlsx_helper_demo/views/res_partner.xml @@ -0,0 +1,19 @@ + + + + + res.partner.test_xlsx + res.partner + + +
+ +
+
+
+ +