Add OCA Aged Partner Balance XLSX
parent
9472a9c51a
commit
6f00485dc4
|
@ -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
|
||||
===========
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
)
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -102,4 +102,13 @@
|
|||
<field name="auto" eval="False"/>
|
||||
</record>
|
||||
|
||||
<record id="action_report_aged_partner_balance_xlsx" model="ir.actions.report.xml">
|
||||
<field name="name">Aged Partner Balance XLSX</field>
|
||||
<field name="model">report_aged_partner_balance_qweb</field>
|
||||
<field name="type">ir.actions.report.xml</field>
|
||||
<field name="report_name">account_financial_report_qweb.report_aged_partner_balance_xlsx</field>
|
||||
<field name="report_type">xlsx</field>
|
||||
<field name="auto" eval="False"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
<footer>
|
||||
<button name="button_export_pdf" string="Export PDF" type="object" default_focus="1" class="oe_highlight"/>
|
||||
or
|
||||
<button name="button_export_xlsx" string="Export XLSX" type="object"/>
|
||||
or
|
||||
<button string="Cancel" class="oe_link" special="cancel" />
|
||||
</footer>
|
||||
</form>
|
||||
|
|
Loading…
Reference in New Issue