diff --git a/account_financial_report_qweb/README.rst b/account_financial_report_qweb/README.rst
index ed3c89e6..307f02d9 100644
--- a/account_financial_report_qweb/README.rst
+++ b/account_financial_report_qweb/README.rst
@@ -21,10 +21,6 @@ Accunting / Reporting / OCA Reports.
Known issues / Roadmap
======================
-Some reports are being worked on and will be available at some point:
-
-- 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 abb2e865..111bf4f4 100644
--- a/account_financial_report_qweb/report/__init__.py
+++ b/account_financial_report_qweb/report/__init__.py
@@ -6,6 +6,7 @@
from . import abstract_report_xlsx
from . import aged_partner_balance
+from . import aged_partner_balance_xlsx
from . import general_ledger
from . import general_ledger_xlsx
from . import open_items
diff --git a/account_financial_report_qweb/report/abstract_report_xlsx.py b/account_financial_report_qweb/report/abstract_report_xlsx.py
index b5481579..5debe764 100644
--- a/account_financial_report_qweb/report/abstract_report_xlsx.py
+++ b/account_financial_report_qweb/report/abstract_report_xlsx.py
@@ -24,12 +24,14 @@ class AbstractReportXslx(ReportXlsx):
# Formats
self.format_right = None
+ self.format_right_bold_italic = 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
+ self.format_percent_bold_italic = None
def generate_xlsx_report(self, workbook, data, objects):
report = objects
@@ -59,14 +61,19 @@ class AbstractReportXslx(ReportXlsx):
Available formats are :
* format_bold
* format_right
+ * format_right_bold_italic
* format_header_left
* format_header_center
* format_header_right
* format_header_amount
* format_amount
+ * format_percent_bold_italic
"""
self.format_bold = workbook.add_format({'bold': True})
self.format_right = workbook.add_format({'align': 'right'})
+ self.format_right_bold_italic = workbook.add_format(
+ {'align': 'right', 'bold': True, 'italic': True}
+ )
self.format_header_left = workbook.add_format(
{'bold': True,
'border': True,
@@ -88,6 +95,10 @@ class AbstractReportXslx(ReportXlsx):
self.format_header_amount.set_num_format('#,##0.00')
self.format_amount = workbook.add_format()
self.format_amount.set_num_format('#,##0.00')
+ self.format_percent_bold_italic = workbook.add_format(
+ {'bold': True, 'italic': True}
+ )
+ self.format_percent_bold_italic.set_num_format('#,##0.00%')
def _set_column_width(self):
"""Set width for all defined columns.
diff --git a/account_financial_report_qweb/report/aged_partner_balance.py b/account_financial_report_qweb/report/aged_partner_balance.py
index a34a12df..3fe45ce4 100644
--- a/account_financial_report_qweb/report/aged_partner_balance.py
+++ b/account_financial_report_qweb/report/aged_partner_balance.py
@@ -185,11 +185,15 @@ class AgedPartnerBalanceReportCompute(models.TransientModel):
_inherit = 'report_aged_partner_balance_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_aged_partner_balance_qweb'
+ if xlsx_report:
+ report_name = 'account_financial_report_qweb.' \
+ 'report_aged_partner_balance_xlsx'
+ else:
+ report_name = 'account_financial_report_qweb.' \
+ 'report_aged_partner_balance_qweb'
return self.env['report'].get_action(records=self,
report_name=report_name)
diff --git a/account_financial_report_qweb/report/aged_partner_balance_xlsx.py b/account_financial_report_qweb/report/aged_partner_balance_xlsx.py
new file mode 100644
index 00000000..7f31ea4b
--- /dev/null
+++ b/account_financial_report_qweb/report/aged_partner_balance_xlsx.py
@@ -0,0 +1,269 @@
+# -*- 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 AgedPartnerBalanceXslx(abstract_report_xlsx.AbstractReportXslx):
+
+ def __init__(self, name, table, rml=False, parser=False, header=True,
+ store=False):
+ super(AgedPartnerBalanceXslx, self).__init__(
+ name, table, rml, parser, header, store)
+
+ def _get_report_name(self):
+ return _('Aged Partner Balance')
+
+ def _get_report_columns(self, report):
+ if not report.show_move_line_details:
+ return {
+ 0: {'header': _('Partner'), 'field': 'partner', 'width': 70},
+ 1: {'header': _('Residual'),
+ 'field': 'amount_residual',
+ 'field_footer_total': 'cumul_amount_residual',
+ 'type': 'amount',
+ 'width': 14},
+ 2: {'header': _('Current'),
+ 'field': 'current',
+ 'field_footer_total': 'cumul_current',
+ 'field_footer_percent': 'percent_current',
+ 'type': 'amount',
+ 'width': 14},
+ 3: {'header': _(u'Age ≤ 30 d.'),
+ 'field': 'age_30_days',
+ 'field_footer_total': 'cumul_age_30_days',
+ 'field_footer_percent': 'percent_age_30_days',
+ 'type': 'amount',
+ 'width': 14},
+ 4: {'header': _(u'Age ≤ 60 d.'),
+ 'field': 'age_60_days',
+ 'field_footer_total': 'cumul_age_60_days',
+ 'field_footer_percent': 'percent_age_60_days',
+ 'type': 'amount',
+ 'width': 14},
+ 5: {'header': _(u'Age ≤ 90 d.'),
+ 'field': 'age_90_days',
+ 'field_footer_total': 'cumul_age_90_days',
+ 'field_footer_percent': 'percent_age_90_days',
+ 'type': 'amount',
+ 'width': 14},
+ 6: {'header': _(u'Age ≤ 120 d.'),
+ 'field': 'age_120_days',
+ 'field_footer_total': 'cumul_age_120_days',
+ 'field_footer_percent': 'percent_age_120_days',
+ 'type': 'amount',
+ 'width': 14},
+ 7: {'header': _('Older'),
+ 'field': 'older',
+ 'field_footer_total': 'cumul_older',
+ 'field_footer_percent': 'percent_older',
+ 'type': 'amount',
+ 'width': 14},
+ }
+ else:
+ 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': _('Residual'),
+ 'field': 'amount_residual',
+ 'field_footer_total': 'cumul_amount_residual',
+ 'field_final_balance': 'amount_residual',
+ 'type': 'amount',
+ 'width': 14},
+ 8: {'header': _('Current'),
+ 'field': 'current',
+ 'field_footer_total': 'cumul_current',
+ 'field_footer_percent': 'percent_current',
+ 'field_final_balance': 'current',
+ 'type': 'amount',
+ 'width': 14},
+ 9: {'header': _(u'Age ≤ 30 d.'),
+ 'field': 'age_30_days',
+ 'field_footer_total': 'cumul_age_30_days',
+ 'field_footer_percent': 'percent_age_30_days',
+ 'field_final_balance': 'age_30_days',
+ 'type': 'amount',
+ 'width': 14},
+ 10: {'header': _(u'Age ≤ 60 d.'),
+ 'field': 'age_60_days',
+ 'field_footer_total': 'cumul_age_60_days',
+ 'field_footer_percent': 'percent_age_60_days',
+ 'field_final_balance': 'age_60_days',
+ 'type': 'amount',
+ 'width': 14},
+ 11: {'header': _(u'Age ≤ 90 d.'),
+ 'field': 'age_90_days',
+ 'field_footer_total': 'cumul_age_90_days',
+ 'field_footer_percent': 'percent_age_90_days',
+ 'field_final_balance': 'age_90_days',
+ 'type': 'amount',
+ 'width': 14},
+ 12: {'header': _(u'Age ≤ 120 d.'),
+ 'field': 'age_120_days',
+ 'field_footer_total': 'cumul_age_120_days',
+ 'field_footer_percent': 'percent_age_120_days',
+ 'field_final_balance': 'age_120_days',
+ 'type': 'amount',
+ 'width': 14},
+ 13: {'header': _('Older'),
+ 'field': 'older',
+ 'field_footer_total': 'cumul_older',
+ 'field_footer_percent': 'percent_older',
+ 'field_final_balance': 'older',
+ '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')],
+ ]
+
+ def _get_col_count_filter_name(self):
+ return 2
+
+ def _get_col_count_filter_value(self):
+ return 3
+
+ def _get_col_pos_footer_label(self, report):
+ return 0 if not report.show_move_line_details else 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):
+ if not report.show_move_line_details:
+ # For each account
+ for account in report.account_ids:
+ # Write account title
+ self.write_array_title(account.code + ' - ' + account.name)
+
+ # Display array header for partners lines
+ self.write_array_header()
+
+ # Display partner lines
+ for partner in account.partner_ids:
+ self.write_line(partner.line_ids)
+
+ # Display account lines
+ self.write_account_footer(report,
+ account,
+ _('Total'),
+ 'field_footer_total',
+ self.format_header_right,
+ self.format_header_amount,
+ False)
+ self.write_account_footer(report,
+ account,
+ _('Percents'),
+ 'field_footer_percent',
+ self.format_right_bold_italic,
+ self.format_percent_bold_italic,
+ True)
+
+ # 2 lines break
+ self.row_pos += 2
+ else:
+ # 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.line_ids)
+
+ # Line break
+ self.row_pos += 1
+
+ # Display account lines
+ self.write_account_footer(report,
+ account,
+ _('Total'),
+ 'field_footer_total',
+ self.format_header_right,
+ self.format_header_amount,
+ False)
+ self.write_account_footer(report,
+ account,
+ _('Percents'),
+ 'field_footer_percent',
+ self.format_right_bold_italic,
+ self.format_percent_bold_italic,
+ True)
+
+ # 2 lines break
+ self.row_pos += 2
+
+ def write_ending_balance(self, my_object):
+ """
+ Specific function to write ending partner balance
+ for Aged Partner Balance
+ """
+ name = None
+ label = _('Partner cumul aged balance')
+ super(AgedPartnerBalanceXslx, self).write_ending_balance(
+ my_object, name, label
+ )
+
+ def write_account_footer(self, report, account, label, field_name,
+ string_format, amount_format, amount_is_percent):
+ """
+ Specific function to write account footer for Aged Partner Balance
+ """
+ col_pos_footer_label = self._get_col_pos_footer_label(report)
+ for col_pos, column in self.columns.iteritems():
+ if col_pos == col_pos_footer_label or column.get(field_name):
+ if col_pos == col_pos_footer_label:
+ value = label
+ else:
+ value = getattr(account, column[field_name])
+ cell_type = column.get('type', 'string')
+ if cell_type == 'string' or col_pos == col_pos_footer_label:
+ self.sheet.write_string(self.row_pos, col_pos, value or '',
+ string_format)
+ elif cell_type == 'amount':
+ number = float(value)
+ if amount_is_percent:
+ number /= 100
+ self.sheet.write_number(self.row_pos, col_pos,
+ number,
+ amount_format)
+ else:
+ self.sheet.write_string(self.row_pos, col_pos, '',
+ string_format)
+
+ self.row_pos += 1
+
+
+AgedPartnerBalanceXslx(
+ 'report.account_financial_report_qweb.report_aged_partner_balance_xlsx',
+ 'report_aged_partner_balance_qweb',
+ parser=report_sxw.rml_parse
+)
diff --git a/account_financial_report_qweb/report/trial_balance_xlsx.py b/account_financial_report_qweb/report/trial_balance_xlsx.py
index fb21129f..a56b3b4f 100644
--- a/account_financial_report_qweb/report/trial_balance_xlsx.py
+++ b/account_financial_report_qweb/report/trial_balance_xlsx.py
@@ -102,7 +102,7 @@ class TrialBalanceXslx(abstract_report_xlsx.AbstractReportXslx):
# Display partner lines
self.write_line(partner)
- # Display account lines
+ # Display account footer line
self.write_account_footer(account,
account.code + ' - ' + account.name)
diff --git a/account_financial_report_qweb/reports.xml b/account_financial_report_qweb/reports.xml
index ffa2e646..33330c0c 100644
--- a/account_financial_report_qweb/reports.xml
+++ b/account_financial_report_qweb/reports.xml
@@ -102,4 +102,13 @@
+
+ Aged Partner Balance XLSX
+ report_aged_partner_balance_qweb
+ ir.actions.report.xml
+ account_financial_report_qweb.report_aged_partner_balance_xlsx
+ xlsx
+
+
+
diff --git a/account_financial_report_qweb/tests/test_aged_partner_balance.py b/account_financial_report_qweb/tests/test_aged_partner_balance.py
index fe2456c2..9920af73 100644
--- a/account_financial_report_qweb/tests/test_aged_partner_balance.py
+++ b/account_financial_report_qweb/tests/test_aged_partner_balance.py
@@ -45,3 +45,30 @@ class TestAgedPartnerBalance(TransactionCase):
report_html = self.env['report'].get_html(self.report, report_name)
self.assertRegexpMatches(report_html, 'Aged Partner Balance')
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_aged_partner_balance_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_aged_partner_balance_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/aged_partner_balance_wizard.py b/account_financial_report_qweb/wizard/aged_partner_balance_wizard.py
index e591881c..8bc9c121 100644
--- a/account_financial_report_qweb/wizard/aged_partner_balance_wizard.py
+++ b/account_financial_report_qweb/wizard/aged_partner_balance_wizard.py
@@ -57,7 +57,12 @@ class AgedPartnerBalance(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_aged_partner_balance_qweb']
report = model.create({
@@ -68,4 +73,4 @@ class AgedPartnerBalance(models.TransientModel):
'filter_partner_ids': [(6, 0, self.partner_ids.ids)],
'show_move_line_details': self.show_move_line_details,
})
- return report.print_report()
+ return report.print_report(xlsx_report)
diff --git a/account_financial_report_qweb/wizard/aged_partner_balance_wizard_view.xml b/account_financial_report_qweb/wizard/aged_partner_balance_wizard_view.xml
index ca49f9f4..be180406 100644
--- a/account_financial_report_qweb/wizard/aged_partner_balance_wizard_view.xml
+++ b/account_financial_report_qweb/wizard/aged_partner_balance_wizard_view.xml
@@ -31,6 +31,8 @@