From 6f69b3066648b5fa5819b58781a81b963bdf434e Mon Sep 17 00:00:00 2001 From: jcoux Date: Thu, 28 Jul 2016 14:50:49 +0200 Subject: [PATCH] Add OCA Open Items Report XLSX --- account_financial_report_qweb/README.rst | 1 - .../report/__init__.py | 1 + .../report/abstract_report_xlsx.py | 68 ++++++++++ .../report/general_ledger_xlsx.py | 60 ++------- .../report/open_items.py | 9 +- .../report/open_items_xlsx.py | 117 ++++++++++++++++++ account_financial_report_qweb/reports.xml | 9 ++ .../tests/test_open_items.py | 27 ++++ .../wizard/open_items_wizard.py | 9 +- .../wizard/open_items_wizard_view.xml | 2 + 10 files changed, 251 insertions(+), 52 deletions(-) create mode 100644 account_financial_report_qweb/report/open_items_xlsx.py diff --git a/account_financial_report_qweb/README.rst b/account_financial_report_qweb/README.rst index 33a0a5d5..ed3c89e6 100644 --- a/account_financial_report_qweb/README.rst +++ b/account_financial_report_qweb/README.rst @@ -23,7 +23,6 @@ Known issues / Roadmap Some reports are being worked on and will be available at some point: -- Open Items (XLSX) - Aged Partner Balance (XLSX) Bug Tracker diff --git a/account_financial_report_qweb/report/__init__.py b/account_financial_report_qweb/report/__init__.py index 1c5ad7f0..abb2e865 100644 --- a/account_financial_report_qweb/report/__init__.py +++ b/account_financial_report_qweb/report/__init__.py @@ -9,5 +9,6 @@ from . import aged_partner_balance from . import general_ledger from . import general_ledger_xlsx from . import open_items +from . import open_items_xlsx from . import trial_balance from . import trial_balance_xlsx diff --git a/account_financial_report_qweb/report/abstract_report_xlsx.py b/account_financial_report_qweb/report/abstract_report_xlsx.py index 2e5455a1..b5481579 100644 --- a/account_financial_report_qweb/report/abstract_report_xlsx.py +++ b/account_financial_report_qweb/report/abstract_report_xlsx.py @@ -163,6 +163,56 @@ class AbstractReportXslx(ReportXlsx): ) self.row_pos += 1 + def write_initial_balance(self, my_object, label): + """Write a specific initial balance line on current line + using defined columns field_initial_balance name. + + Columns are defined with `_get_report_columns` method. + """ + col_pos_label = self._get_col_pos_initial_balance_label() + self.sheet.write(self.row_pos, col_pos_label, label, self.format_right) + for col_pos, column in self.columns.iteritems(): + if column.get('field_initial_balance'): + value = getattr(my_object, column['field_initial_balance']) + cell_type = column.get('type', 'string') + if cell_type == 'string': + self.sheet.write_string(self.row_pos, col_pos, value or '') + elif cell_type == 'amount': + self.sheet.write_number( + self.row_pos, col_pos, float(value), self.format_amount + ) + self.row_pos += 1 + + def write_ending_balance(self, my_object, name, label): + """Write a specific ending balance line on current line + using defined columns field_final_balance name. + + Columns are defined with `_get_report_columns` method. + """ + for i in range(0, len(self.columns)): + self.sheet.write(self.row_pos, i, '', self.format_header_right) + row_count_name = self._get_col_count_final_balance_name() + col_pos_label = self._get_col_pos_final_balance_label() + self.sheet.merge_range( + self.row_pos, 0, self.row_pos, row_count_name - 1, name, + self.format_header_left + ) + self.sheet.write(self.row_pos, col_pos_label, label, + self.format_header_right) + for col_pos, column in self.columns.iteritems(): + if column.get('field_final_balance'): + value = getattr(my_object, column['field_final_balance']) + cell_type = column.get('type', 'string') + if cell_type == 'string': + self.sheet.write_string(self.row_pos, col_pos, value or '', + self.format_header_right) + elif cell_type == 'amount': + self.sheet.write_number( + self.row_pos, col_pos, float(value), + self.format_header_amount + ) + self.row_pos += 1 + def _generate_report_content(self, workbook, report): pass @@ -220,3 +270,21 @@ class AbstractReportXslx(ReportXlsx): :return: the columns number used for filter values. """ raise NotImplementedError() + + def _get_col_pos_initial_balance_label(self): + """ + :return: the columns position used for initial balance label. + """ + raise NotImplementedError() + + def _get_col_count_final_balance_name(self): + """ + :return: the columns number used for final balance name. + """ + raise NotImplementedError() + + def _get_col_pos_final_balance_label(self): + """ + :return: the columns position used for final balance label. + """ + raise NotImplementedError() diff --git a/account_financial_report_qweb/report/general_ledger_xlsx.py b/account_financial_report_qweb/report/general_ledger_xlsx.py index 529e833b..f76d744a 100644 --- a/account_financial_report_qweb/report/general_ledger_xlsx.py +++ b/account_financial_report_qweb/report/general_ledger_xlsx.py @@ -16,11 +16,6 @@ class GeneralLedgerXslx(abstract_report_xlsx.AbstractReportXslx): super(GeneralLedgerXslx, self).__init__( name, table, rml, parser, header, store) - # Custom values needed to generate report - self.col_pos_initial_balance_label = 5 - self.col_count_final_balance_name = 5 - self.col_pos_final_balance_label = 5 - def _get_report_name(self): return _('General Ledger') @@ -80,6 +75,15 @@ class GeneralLedgerXslx(abstract_report_xlsx.AbstractReportXslx): def _get_col_count_filter_value(self): return 2 + def _get_col_pos_initial_balance_label(self): + return 5 + + def _get_col_count_final_balance_name(self): + return 5 + + def _get_col_pos_final_balance_label(self): + return 5 + def _generate_report_content(self, workbook, report): # For each account for account in report.account_ids: @@ -91,7 +95,7 @@ class GeneralLedgerXslx(abstract_report_xlsx.AbstractReportXslx): self.write_array_header() # Display initial balance line for account - self.write_initial_balance(account) + self.write_initial_balance(account, _('Initial balance')) # Display account move lines for line in account.move_line_ids: @@ -107,7 +111,7 @@ class GeneralLedgerXslx(abstract_report_xlsx.AbstractReportXslx): self.write_array_header() # Display initial balance line for partner - self.write_initial_balance(partner) + self.write_initial_balance(partner, _('Initial balance')) # Display account move lines for line in partner.move_line_ids: @@ -125,23 +129,6 @@ class GeneralLedgerXslx(abstract_report_xlsx.AbstractReportXslx): # 2 lines break self.row_pos += 2 - def write_initial_balance(self, my_object): - """Specific function to write initial balance for General Ledger""" - col_pos_label = self.col_pos_initial_balance_label - self.sheet.write(self.row_pos, col_pos_label, _('Initial balance'), - self.format_right) - for col_pos, column in self.columns.iteritems(): - if column.get('field_initial_balance'): - value = getattr(my_object, column['field_initial_balance']) - cell_type = column.get('type', 'string') - if cell_type == 'string': - self.sheet.write_string(self.row_pos, col_pos, value or '') - elif cell_type == 'amount': - self.sheet.write_number( - self.row_pos, col_pos, float(value), self.format_amount - ) - self.row_pos += 1 - def write_ending_balance(self, my_object, type_object): """Specific function to write ending balance for General Ledger""" if type_object == 'partner': @@ -150,30 +137,9 @@ class GeneralLedgerXslx(abstract_report_xlsx.AbstractReportXslx): elif type_object == 'account': name = my_object.code + ' - ' + my_object.name label = _('Ending balance') - for i in range(0, len(self.columns)): - self.sheet.write(self.row_pos, i, '', self.format_header_right) - row_count_name = self.col_count_final_balance_name - row_pos = self.row_pos - col_pos_label = self.col_pos_final_balance_label - self.sheet.merge_range( - row_pos, 0, row_pos, row_count_name - 1, name, - self.format_header_left + super(GeneralLedgerXslx, self).write_ending_balance( + my_object, name, label ) - self.sheet.write(row_pos, col_pos_label, label, - self.format_header_right) - for col_pos, column in self.columns.iteritems(): - if column.get('field_final_balance'): - value = getattr(my_object, column['field_final_balance']) - cell_type = column.get('type', 'string') - if cell_type == 'string': - self.sheet.write_string(self.row_pos, col_pos, value or '', - self.format_header_right) - elif cell_type == 'amount': - self.sheet.write_number( - self.row_pos, col_pos, float(value), - self.format_header_amount - ) - self.row_pos += 1 GeneralLedgerXslx( diff --git a/account_financial_report_qweb/report/open_items.py b/account_financial_report_qweb/report/open_items.py index da0314b2..9488deb0 100644 --- a/account_financial_report_qweb/report/open_items.py +++ b/account_financial_report_qweb/report/open_items.py @@ -141,10 +141,15 @@ class OpenItemsReportCompute(models.TransientModel): _inherit = 'report_open_items_qweb' @api.multi - def print_report(self): + def print_report(self, xlsx_report=False): self.ensure_one() self.compute_data_for_report() - report_name = 'account_financial_report_qweb.report_open_items_qweb' + if xlsx_report: + report_name = 'account_financial_report_qweb.' \ + 'report_open_items_xlsx' + else: + report_name = 'account_financial_report_qweb.' \ + 'report_open_items_qweb' return self.env['report'].get_action(records=self, report_name=report_name) diff --git a/account_financial_report_qweb/report/open_items_xlsx.py b/account_financial_report_qweb/report/open_items_xlsx.py new file mode 100644 index 00000000..344617de --- /dev/null +++ b/account_financial_report_qweb/report/open_items_xlsx.py @@ -0,0 +1,117 @@ +# -*- coding: utf-8 -*- +# Author: Julien Coux +# Copyright 2016 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import abstract_report_xlsx +from openerp.report import report_sxw +from openerp import _ + + +class OpenItemsXslx(abstract_report_xlsx.AbstractReportXslx): + + def __init__(self, name, table, rml=False, parser=False, header=True, + store=False): + super(OpenItemsXslx, self).__init__( + name, table, rml, parser, header, store) + + def _get_report_name(self): + return _('Open Items') + + def _get_report_columns(self, report): + return { + 0: {'header': _('Date'), 'field': 'date', 'width': 11}, + 1: {'header': _('Entry'), 'field': 'entry', 'width': 18}, + 2: {'header': _('Journal'), 'field': 'journal', 'width': 8}, + 3: {'header': _('Account'), 'field': 'account', 'width': 9}, + 4: {'header': _('Partner'), 'field': 'partner', 'width': 25}, + 5: {'header': _('Ref - Label'), 'field': 'label', 'width': 40}, + 6: {'header': _('Due date'), 'field': 'date_due', 'width': 11}, + 7: {'header': _('Original'), + 'field': 'amount_total_due', + 'type': 'amount', + 'width': 14}, + 8: {'header': _('Residual'), + 'field': 'amount_residual', + 'field_final_balance': 'final_amount_residual', + 'type': 'amount', + 'width': 14}, + 9: {'header': _('Cur.'), 'field': 'currency_name', 'width': 7}, + 10: {'header': _('Cur. Original'), + 'field': 'amount_total_due_currency', + 'type': 'amount', + 'width': 14}, + 11: {'header': _('Cur. Residual'), + 'field': 'amount_residual_currency', + 'type': 'amount', + 'width': 14}, + } + + def _get_report_filters(self, report): + return [ + [_('Date at filter'), report.date_at], + [_('Target moves filter'), + _('All posted entries') if report.only_posted_moves + else _('All entries')], + [_('Account balance at 0 filter'), + _('Hide') if report.hide_account_balance_at_0 else _('Show')], + ] + + def _get_col_count_filter_name(self): + return 2 + + def _get_col_count_filter_value(self): + return 2 + + def _get_col_count_final_balance_name(self): + return 5 + + def _get_col_pos_final_balance_label(self): + return 5 + + def _generate_report_content(self, workbook, report): + # For each account + for account in report.account_ids: + # Write account title + self.write_array_title(account.code + ' - ' + account.name) + + # For each partner + for partner in account.partner_ids: + # Write partner title + self.write_array_title(partner.name) + + # Display array header for move lines + self.write_array_header() + + # Display account move lines + for line in partner.move_line_ids: + self.write_line(line) + + # Display ending balance line for partner + self.write_ending_balance(partner, 'partner') + + # Line break + self.row_pos += 1 + + # Display ending balance line for account + self.write_ending_balance(account, 'account') + + # 2 lines break + self.row_pos += 2 + + def write_ending_balance(self, my_object, type_object): + """Specific function to write ending balance for Open Items""" + if type_object == 'partner': + name = my_object.name + label = _('Partner ending balance') + elif type_object == 'account': + name = my_object.code + ' - ' + my_object.name + label = _('Ending balance') + super(OpenItemsXslx, self).write_ending_balance(my_object, name, label) + + +OpenItemsXslx( + 'report.account_financial_report_qweb.report_open_items_xlsx', + 'report_open_items_qweb', + parser=report_sxw.rml_parse +) diff --git a/account_financial_report_qweb/reports.xml b/account_financial_report_qweb/reports.xml index 36ec6255..ffa2e646 100644 --- a/account_financial_report_qweb/reports.xml +++ b/account_financial_report_qweb/reports.xml @@ -93,4 +93,13 @@ + + Open Items XLSX + report_open_items_qweb + ir.actions.report.xml + account_financial_report_qweb.report_open_items_xlsx + xlsx + + + diff --git a/account_financial_report_qweb/tests/test_open_items.py b/account_financial_report_qweb/tests/test_open_items.py index bd37e5ab..0594fc51 100644 --- a/account_financial_report_qweb/tests/test_open_items.py +++ b/account_financial_report_qweb/tests/test_open_items.py @@ -45,3 +45,30 @@ class TestOpenItems(TransactionCase): report_html = self.env['report'].get_html(self.report, report_name) self.assertRegexpMatches(report_html, 'Open Items') self.assertRegexpMatches(report_html, self.report.account_ids[0].name) + + def test_03_generation_report_xlsx(self): + """Check if report XLSX is correctly generated""" + + report_name = 'account_financial_report_qweb.' \ + 'report_open_items_xlsx' + # Check if returned report action is correct + report_action = self.report.print_report(xlsx_report=True) + self.assertDictContainsSubset( + { + 'type': 'ir.actions.report.xml', + 'report_name': report_name, + 'report_type': 'xlsx', + }, + report_action + ) + + # Check if report template is correct + action_name = 'account_financial_report_qweb.' \ + 'action_report_open_items_xlsx' + report_xlsx = self.env.ref(action_name).render_report( + self.report.ids, + report_name, + {'report_type': u'xlsx'} + ) + self.assertGreaterEqual(len(report_xlsx[0]), 1) + self.assertEqual(report_xlsx[1], 'xlsx') diff --git a/account_financial_report_qweb/wizard/open_items_wizard.py b/account_financial_report_qweb/wizard/open_items_wizard.py index 32705214..4a089ac5 100644 --- a/account_financial_report_qweb/wizard/open_items_wizard.py +++ b/account_financial_report_qweb/wizard/open_items_wizard.py @@ -63,7 +63,12 @@ class OpenItemsReportWizard(models.TransientModel): self.ensure_one() return self._export() - def _export(self): + @api.multi + def button_export_xlsx(self): + self.ensure_one() + return self._export(xlsx_report=True) + + def _export(self, xlsx_report=False): """Default export is PDF.""" model = self.env['report_open_items_qweb'] report = model.create({ @@ -74,4 +79,4 @@ class OpenItemsReportWizard(models.TransientModel): 'filter_account_ids': [(6, 0, self.account_ids.ids)], 'filter_partner_ids': [(6, 0, self.partner_ids.ids)], }) - return report.print_report() + return report.print_report(xlsx_report) diff --git a/account_financial_report_qweb/wizard/open_items_wizard_view.xml b/account_financial_report_qweb/wizard/open_items_wizard_view.xml index 08b6db27..7aced656 100644 --- a/account_financial_report_qweb/wizard/open_items_wizard_view.xml +++ b/account_financial_report_qweb/wizard/open_items_wizard_view.xml @@ -31,6 +31,8 @@