From 94387501cb41148803c993efe0410a29fce9239b Mon Sep 17 00:00:00 2001 From: jcoux Date: Mon, 25 Jul 2016 17:51:40 +0200 Subject: [PATCH] Review and refactoring of OCA General Ledger XLSX --- .../report/__init__.py | 1 + .../report/abstract_report_xlsx.py | 220 ++++++++++++++++++ .../report/general_ledger_xlsx.py | 167 ++++--------- 3 files changed, 267 insertions(+), 121 deletions(-) create mode 100644 account_financial_report_qweb/report/abstract_report_xlsx.py diff --git a/account_financial_report_qweb/report/__init__.py b/account_financial_report_qweb/report/__init__.py index 4aef330d..959e6218 100644 --- a/account_financial_report_qweb/report/__init__.py +++ b/account_financial_report_qweb/report/__init__.py @@ -4,6 +4,7 @@ # © 2016 Julien Coux (Camptocamp) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).- +from . import abstract_report_xlsx from . import aged_partner_balance from . import general_ledger from . import general_ledger_xlsx diff --git a/account_financial_report_qweb/report/abstract_report_xlsx.py b/account_financial_report_qweb/report/abstract_report_xlsx.py new file mode 100644 index 00000000..5d4adb65 --- /dev/null +++ b/account_financial_report_qweb/report/abstract_report_xlsx.py @@ -0,0 +1,220 @@ +# -*- coding: utf-8 -*- +# Author: Julien Coux +# Copyright 2016 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from openerp.addons.report_xlsx.report.report_xlsx import ReportXlsx + + +class AbstractReportXslx(ReportXlsx): + + def __init__(self, name, table, rml=False, parser=False, header=True, + store=False): + super(AbstractReportXslx, self).__init__( + name, table, rml, parser, header, store) + + # main sheet which will contains report + self.sheet = None + + # columns of the report + self.columns = None + + # row_pos must be incremented at each writing lines + self.row_pos = 0 + + # Formats + self.format_right = None + self.format_bold = None + self.format_header_left = None + self.format_header_center = None + self.format_header_right = None + self.format_header_amount = None + self.format_amount = None + + def generate_xlsx_report(self, workbook, data, objects): + report = objects + + self._define_formats(workbook) + + report_name = self._get_report_name() + filters = self._get_report_filters(report) + self.columns = self._get_report_columns() + + self.sheet = workbook.add_worksheet(report_name[:31]) + + self._set_column_width() + + self._write_report_title(report_name) + + self._write_filters(filters) + + self._generate_report_content(workbook, report) + + def _define_formats(self, workbook): + """ Add cell formats to current workbook. + Those formats can be used on all cell. + + Available formats are : + * format_bold + * format_right + * format_header_left + * format_header_center + * format_header_right + * format_header_amount + * format_amount + """ + self.format_bold = workbook.add_format({'bold': True}) + self.format_right = workbook.add_format({'align': 'right'}) + self.format_header_left = workbook.add_format( + {'bold': True, + 'border': True, + 'bg_color': '#FFFFCC'}) + self.format_header_center = workbook.add_format( + {'bold': True, + 'align': 'center', + 'border': True, + 'bg_color': '#FFFFCC'}) + self.format_header_right = workbook.add_format( + {'bold': True, + 'align': 'right', + 'border': True, + 'bg_color': '#FFFFCC'}) + self.format_header_amount = workbook.add_format( + {'bold': True, + 'border': True, + 'bg_color': '#FFFFCC'}) + self.format_header_amount.set_num_format('#,##0.00') + self.format_amount = workbook.add_format() + self.format_amount.set_num_format('#,##0.00') + + def _set_column_width(self): + """Set width for all defined columns. + Columns are defined with `_get_report_columns` method. + """ + for position, column in self.columns.iteritems(): + self.sheet.set_column(position, position, column['width']) + + def _write_report_title(self, title): + """Write report title on current line using all defined columns width. + Columns are defined with `_get_report_columns` method. + """ + self.sheet.merge_range( + self.row_pos, 0, self.row_pos, len(self.columns) - 1, + title, self.format_bold + ) + self.row_pos += 3 + + def _write_filters(self, filters): + """Write one line per filters on starting on current line. + Columns number for filter name is defined + with `_get_col_count_filter_name` method. + Columns number for filter value is define + with `_get_col_count_filter_value` method. + """ + col_name = 1 + col_count_filter_name = self._get_col_count_filter_name() + col_count_filter_value = self._get_col_count_filter_value() + col_value = col_name + col_count_filter_name + 1 + for title, value in filters: + self.sheet.merge_range( + self.row_pos, col_name, + self.row_pos, col_name + col_count_filter_name - 1, + title, self.format_header_left) + self.sheet.merge_range( + self.row_pos, col_value, + self.row_pos, col_value + col_count_filter_value - 1, + value) + self.row_pos += 1 + self.row_pos += 2 + + def write_array_title(self, title): + """Write array title on current line using all defined columns width. + Columns are defined with `_get_report_columns` method. + """ + self.sheet.merge_range( + self.row_pos, 0, self.row_pos, len(self.columns) - 1, + title, self.format_bold + ) + self.row_pos += 1 + + def write_array_header(self): + """Write array header on current line using all defined columns name. + Columns are defined with `_get_report_columns` method. + """ + for col_pos, column in self.columns.iteritems(): + self.sheet.write(self.row_pos, col_pos, column['header'], + self.format_header_center) + self.row_pos += 1 + + def write_line(self, line_object): + """Write a line on current line using all defined columns field name. + Columns are defined with `_get_report_columns` method. + """ + for col_pos, column in self.columns.iteritems(): + value = getattr(line_object, column['field']) + 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 _generate_report_content(self, workbook, report): + pass + + def _get_report_name(self): + """ + Allow to define the report name. + Report name will be used as sheet name and as report title. + + :return: the report name + """ + raise NotImplementedError() + + def _get_report_columns(self): + """ + Allow to define the report columns + which will be used to generate report. + + :return: the report columns as dict + + :Example: + + { + 0: {'header': 'Simple column', + 'field': 'field_name_on_my_object', + 'width': 11}, + 1: {'header': 'Amount column', + 'field': 'field_name_on_my_object', + 'type': 'amount', + 'width': 14}, + } + """ + raise NotImplementedError() + + def _get_report_filters(self, report): + """ + :return: the report filters as list + + :Example: + + [ + ['first_filter_name', 'first_filter_value'], + ['second_filter_name', 'second_filter_value'] + ] + """ + raise NotImplementedError() + + def _get_col_count_filter_name(self): + """ + :return: the columns number used for filter names. + """ + raise NotImplementedError() + + def _get_col_count_filter_value(self): + """ + :return: the columns number used for filter values. + """ + 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 22dcab6e..19cc2a83 100644 --- a/account_financial_report_qweb/report/general_ledger_xlsx.py +++ b/account_financial_report_qweb/report/general_ledger_xlsx.py @@ -1,31 +1,30 @@ # -*- coding: utf-8 -*- # Author: Damien Crier +# Author: Julien Coux # Copyright 2016 Camptocamp SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from openerp.addons.report_xlsx.report.report_xlsx import ReportXlsx +from . import abstract_report_xlsx from openerp.report import report_sxw -class GeneralLedgerXslx(ReportXlsx): +class GeneralLedgerXslx(abstract_report_xlsx.AbstractReportXslx): def __init__(self, name, table, rml=False, parser=False, header=True, store=False): super(GeneralLedgerXslx, self).__init__( name, table, rml, parser, header, store) - # Initialisé à None car on ne peut pas les setter pour le moment - self.row_pos = None - self.format_right = None - self.format_blod = None - self.format_header_left = None - self.format_header_center = None - self.format_header_right = None - self.format_header_amount = None - self.format_amount = None - self.sheet = None - - self.columns = { + # 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' + + def _get_report_columns(self): + return { 0: {'header': 'Date', 'field': 'date', 'width': 11}, 1: {'header': 'Entry', 'field': 'entry', 'width': 18}, 2: {'header': 'Journal', 'field': 'journal', 'width': 8}, @@ -59,22 +58,8 @@ class GeneralLedgerXslx(ReportXlsx): 'width': 14}, } - self.col_pos_initial_balance_label = 5 - self.col_count_final_balance_name = 5 - - self.col_pos_final_balance_label = 5 - - self.col_count_filter_name = 2 - self.col_count_filter_value = 2 - - self.column_count = len(self.columns) - - def generate_xlsx_report(self, workbook, data, objects): - report = objects - - report_name = 'General Ledger' - - filters = [ + def _get_report_filters(self, report): + return [ ['Date range filter', 'From: '+report.date_from+' To: '+report.date_to], ['Target moves filter', @@ -86,120 +71,59 @@ class GeneralLedgerXslx(ReportXlsx): 'Yes' if report.centralize else 'No'], ] - self.row_pos = 0 - - self.format_blod = workbook.add_format({'bold': True}) - self.format_right = workbook.add_format({'align': 'right'}) - self.format_header_left = workbook.add_format( - {'bold': True, - 'border': True, - 'bg_color': '#FFFFCC'}) - self.format_header_center = workbook.add_format( - {'bold': True, - 'align': 'center', - 'border': True, - 'bg_color': '#FFFFCC'}) - self.format_header_right = workbook.add_format( - {'bold': True, - 'align': 'right', - 'border': True, - 'bg_color': '#FFFFCC'}) - self.format_header_amount = workbook.add_format( - {'bold': True, - 'border': True, - 'bg_color': '#FFFFCC'}) - self.format_header_amount.set_num_format('#,##0.00') - self.format_amount = workbook.add_format() - self.format_amount.set_num_format('#,##0.00') - std_cell_format = workbook.add_format({'bold': False, - 'align': 'left', - 'border': False, - 'bg_color': '#FFFFFF'}) - - self.sheet = workbook.add_worksheet(report_name[:31]) - - self.set_column_width() - - self.write_report_title(report_name) - - self.write_filters(filters) + def _get_col_count_filter_name(self): + return 2 + + def _get_col_count_filter_value(self): + return 2 + 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) if account.move_line_ids: - self.write_header() + # Display array header for move lines + self.write_array_header() + + # Display initial balance line for account self.write_initial_balance(account) + + # Display account move lines for line in account.move_line_ids: self.write_line(line) elif account.is_partner_account: + # For each partner for partner in account.partner_ids: + # Write partner title self.write_array_title(partner.name) - self.write_header() + # Display array header for move lines + self.write_array_header() + + # Display initial balance line for partner self.write_initial_balance(partner) + + # 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') - self.row_pos += 2 - - def set_column_width(self): - for position, column in self.columns.iteritems(): - self.sheet.set_column(position, position, column['width']) - def write_report_title(self, title): - self.sheet.merge_range( - self.row_pos, 0, self.row_pos, self.column_count - 1, - title, self.format_blod - ) - self.row_pos += 3 - - def write_filters(self, filters): - col_name = 1 - col_value = col_name + self.col_count_filter_name + 1 - for title, value in filters: - self.sheet.merge_range( - self.row_pos, col_name, - self.row_pos, col_name + self.col_count_filter_name - 1, - title, self.format_header_left) - self.sheet.merge_range( - self.row_pos, col_value, - self.row_pos, col_value + self.col_count_filter_value - 1, - value) - self.row_pos += 1 - self.row_pos += 2 - - def write_array_title(self, title): - self.sheet.merge_range( - self.row_pos, 0, self.row_pos, self.column_count - 1, - title, self.format_blod - ) - self.row_pos += 1 - - def write_line(self, line_object): - for col_pos, column in self.columns.iteritems(): - value = getattr(line_object, column['field']) - 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_header(self): - for col_pos, column in self.columns.iteritems(): - self.sheet.write(self.row_pos, col_pos, column['header'], - self.format_header_center) - self.row_pos += 1 + # 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) @@ -216,13 +140,14 @@ class GeneralLedgerXslx(ReportXlsx): 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': name = my_object.name label = 'Partner ending balance' elif type_object == 'account': name = my_object.code + ' - ' + my_object.name label = 'Ending balance' - for i in range(0, self.column_count): + 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