Browse Source

Merge pull request #211 from jcoux/ledger_reports

OCA financial reports
pull/218/head
Frédéric Clementi 8 years ago
committed by GitHub
parent
commit
274256d1e8
  1. 70
      account_financial_report_qweb/README.rst
  2. 8
      account_financial_report_qweb/__init__.py
  3. 44
      account_financial_report_qweb/__openerp__.py
  4. 1365
      account_financial_report_qweb/i18n/account_financial_report_qweb.pot
  5. 1365
      account_financial_report_qweb/i18n/fr.po
  6. 87
      account_financial_report_qweb/menuitems.xml
  7. 6
      account_financial_report_qweb/models/__init__.py
  8. 14
      account_financial_report_qweb/models/account.py
  9. 15
      account_financial_report_qweb/report/__init__.py
  10. 301
      account_financial_report_qweb/report/abstract_report_xlsx.py
  11. 607
      account_financial_report_qweb/report/aged_partner_balance.py
  12. 269
      account_financial_report_qweb/report/aged_partner_balance_xlsx.py
  13. 1151
      account_financial_report_qweb/report/general_ledger.py
  14. 149
      account_financial_report_qweb/report/general_ledger_xlsx.py
  15. 724
      account_financial_report_qweb/report/open_items.py
  16. 117
      account_financial_report_qweb/report/open_items_xlsx.py
  17. 327
      account_financial_report_qweb/report/templates/aged_partner_balance.xml
  18. 250
      account_financial_report_qweb/report/templates/general_ledger.xml
  19. 38
      account_financial_report_qweb/report/templates/layouts.xml
  20. 187
      account_financial_report_qweb/report/templates/open_items.xml
  21. 154
      account_financial_report_qweb/report/templates/trial_balance.xml
  22. 249
      account_financial_report_qweb/report/trial_balance.py
  23. 133
      account_financial_report_qweb/report/trial_balance_xlsx.py
  24. 114
      account_financial_report_qweb/reports.xml
  25. BIN
      account_financial_report_qweb/static/description/icon.png
  26. 95
      account_financial_report_qweb/static/src/css/report.css
  27. 9
      account_financial_report_qweb/tests/__init__.py
  28. 204
      account_financial_report_qweb/tests/abstract_test.py
  29. 42
      account_financial_report_qweb/tests/test_aged_partner_balance.py
  30. 52
      account_financial_report_qweb/tests/test_general_ledger.py
  31. 41
      account_financial_report_qweb/tests/test_open_items.py
  32. 54
      account_financial_report_qweb/tests/test_trial_balance.py
  33. 14
      account_financial_report_qweb/view/account_view.xml
  34. 9
      account_financial_report_qweb/wizard/__init__.py
  35. 81
      account_financial_report_qweb/wizard/aged_partner_balance_wizard.py
  36. 50
      account_financial_report_qweb/wizard/aged_partner_balance_wizard_view.xml
  37. 136
      account_financial_report_qweb/wizard/general_ledger_wizard.py
  38. 70
      account_financial_report_qweb/wizard/general_ledger_wizard_view.xml
  39. 87
      account_financial_report_qweb/wizard/open_items_wizard.py
  40. 50
      account_financial_report_qweb/wizard/open_items_wizard_view.xml
  41. 129
      account_financial_report_qweb/wizard/trial_balance_wizard.py
  42. 67
      account_financial_report_qweb/wizard/trial_balance_wizard_view.xml
  43. 1
      oca_dependencies.txt
  44. 1
      setup/account_financial_report_qweb/odoo_addons/__init__.py
  45. 1
      setup/account_financial_report_qweb/odoo_addons/account_financial_report_qweb
  46. 6
      setup/account_financial_report_qweb/setup.py

70
account_financial_report_qweb/README.rst

@ -0,0 +1,70 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
=============================
account_financial_report_qweb
=============================
This module adds a set of financial reports. They are accessible under
Accunting / Reporting / OCA Reports.
- General ledger
- Trial Balance
- Open Items
- Aged Partner Balance
.. 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/9.0
Bug Tracker
===========
Bugs are tracked on `GitHub Issues
<https://github.com/OCA/account-financial-reporting/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
=======
Images
------
* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_.
Contributors
------------
* Jordi Ballestrer <jordi.ballestrer@eficient.com>
* Yannick Vaucher <yannick.vaucher@camptocamp.com>
* Simone Orsi <simone.orsi@abstract.com>
* Leonardo Pistone <leonardo.pistone@camptocamp.com>
* Damien Crier <damien.crier@camptocamp.com>
* Andrea Stirpe <a.stirpe@onestein.nl>
* Thomas Rehn <thomas.rehn@initos.com>
* Andrea Gallina <4everamd@gmail.com>
* Robert Rottermann <robert@redcor.ch>
* Ciro Urselli <c.urselli@apuliasoftware.it>
* Francesco Apruzzese <opencode@e-ware.org>
* Lorenzo Battistini <lorenzo.battistini@agilebg.com>
* Julien Coux <julien.coux@camptocamp.com>
Much of the work in this module was done at a sprint in Sorrento, Italy in
April 2016.
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 https://odoo-community.org.

8
account_financial_report_qweb/__init__.py

@ -0,0 +1,8 @@
# -*- coding: utf-8 -*-
# Author: Damien Crier
# Copyright 2016 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import models
from . import wizard
from . import report

44
account_financial_report_qweb/__openerp__.py

@ -0,0 +1,44 @@
# -*- 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).
{
'name': 'QWeb Financial Reports',
'version': '9.0.1.0.0',
'category': 'Reporting',
'summary': 'OCA Financial Reports',
'author': 'Camptocamp SA,'
'initOS GmbH,'
'redCOR AG,'
'Odoo Community Association (OCA)',
"website": "https://odoo-community.org/",
'depends': [
'account',
'account_full_reconcile',
'date_range',
'account_fiscal_year',
'report_xlsx',
'report',
],
'data': [
'wizard/aged_partner_balance_wizard_view.xml',
'wizard/general_ledger_wizard_view.xml',
'wizard/open_items_wizard_view.xml',
'wizard/trial_balance_wizard_view.xml',
'menuitems.xml',
'reports.xml',
'report/templates/aged_partner_balance.xml',
'report/templates/general_ledger.xml',
'report/templates/layouts.xml',
'report/templates/open_items.xml',
'report/templates/trial_balance.xml',
'view/account_view.xml'
],
'test': [
],
'installable': True,
'application': True,
'auto_install': False,
'license': 'AGPL-3',
}

1365
account_financial_report_qweb/i18n/account_financial_report_qweb.pot
File diff suppressed because it is too large
View File

1365
account_financial_report_qweb/i18n/fr.po
File diff suppressed because it is too large
View File

87
account_financial_report_qweb/menuitems.xml

@ -0,0 +1,87 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<menuitem
parent="account.menu_finance_reports"
id="menu_oca_reports"
name="OCA accounting reports"
groups="account.group_account_manager,account.group_account_user"
/>
<menuitem
parent="menu_oca_reports"
action="action_general_ledger_wizard"
id="menu_general_ledger_wizard"
sequence="10"
/>
<menuitem
parent="menu_oca_reports"
action="action_trial_balance_wizard"
id="menu_trial_balance_wizard"
sequence="20"
/>
<menuitem
parent="menu_oca_reports"
action="action_open_items_wizard"
id="menu_open_items_wizard"
sequence="30"
/>
<menuitem
parent="menu_oca_reports"
action="action_aged_partner_balance_wizard"
id="menu_aged_partner_balance_wizard"
sequence="40"
/>
<!-- Hide odoo PDF reports menu -->
<menuitem
id="account.menu_finance_legal_statement"
name="PDF Reports"
parent="account.menu_finance_reports"
groups="base.group_erp_manager"
/>
<menuitem
id="account.menu_general_ledger"
name="General Ledger"
parent="account.menu_finance_legal_statement"
action="account.action_account_general_ledger_menu"
groups="base.group_erp_manager"
/>
<menuitem
id="account.menu_general_Balance_report"
name="Trial Balance"
parent="account.menu_finance_legal_statement"
action="account.action_account_balance_menu"
groups="base.group_erp_manager"
/>
<menuitem
id="account.menu_account_report_bs"
name="Balance Sheet"
action="account.action_account_report_bs"
parent="account.menu_finance_legal_statement"
groups="base.group_erp_manager"
/>
<menuitem
id="account.menu_account_report_pl"
name="Profit and Loss"
action="account.action_account_report_pl"
parent="account.menu_finance_legal_statement"
groups="base.group_erp_manager"
/>
<menuitem
id="account.menu_aged_trial_balance"
name="Aged Partner Balance"
action="account.action_account_aged_balance_view"
parent="account.menu_finance_legal_statement"
groups="base.group_erp_manager"
/>
</odoo>

6
account_financial_report_qweb/models/__init__.py

@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
# Author: Damien Crier
# Copyright 2016 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import account

14
account_financial_report_qweb/models/account.py

@ -0,0 +1,14 @@
# -*- coding: utf-8 -*-
# © 2011 Guewen Baconnier (Camptocamp)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).-
from openerp import models, fields
class AccountAccount(models.Model):
_inherit = 'account.account'
centralized = fields.Boolean(
'Centralized',
help="If flagged, no details will be displayed in "
"the General Ledger report (the webkit one only), "
"only centralized amounts per period.")

15
account_financial_report_qweb/report/__init__.py

@ -0,0 +1,15 @@
# -*- coding: utf-8 -*-
# © 2015 Yannick Vaucher (Camptocamp)
# © 2016 Damien Crier (Camptocamp)
# © 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 aged_partner_balance_xlsx
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

301
account_financial_report_qweb/report/abstract_report_xlsx.py

@ -0,0 +1,301 @@
# -*- 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 = None
# 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
self.row_pos = 0
self._define_formats(workbook)
report_name = self._get_report_name()
filters = self._get_report_filters(report)
self.columns = self._get_report_columns(report)
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_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,
'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')
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.
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 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
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, report):
"""
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()
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()

607
account_financial_report_qweb/report/aged_partner_balance.py

@ -0,0 +1,607 @@
# -*- coding: utf-8 -*-
# © 2016 Julien Coux (Camptocamp)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, fields, api
class AgedPartnerBalanceReport(models.TransientModel):
""" Here, we just define class fields.
For methods, go more bottom at this file.
The class hierarchy is :
* AgedPartnerBalanceReport
** AgedPartnerBalanceReportAccount
*** AgedPartnerBalanceReportPartner
**** AgedPartnerBalanceReportLine
**** AgedPartnerBalanceReportMoveLine
If "show_move_line_details" is selected
"""
_name = 'report_aged_partner_balance_qweb'
# Filters fields, used for data computation
date_at = fields.Date()
only_posted_moves = fields.Boolean()
company_id = fields.Many2one(comodel_name='res.company')
filter_account_ids = fields.Many2many(comodel_name='account.account')
filter_partner_ids = fields.Many2many(comodel_name='res.partner')
show_move_line_details = fields.Boolean()
# Open Items Report Data fields, used as base for compute the data reports
open_items_id = fields.Many2one(comodel_name='report_open_items_qweb')
# Data fields, used to browse report data
account_ids = fields.One2many(
comodel_name='report_aged_partner_balance_qweb_account',
inverse_name='report_id'
)
class AgedPartnerBalanceReportAccount(models.TransientModel):
_name = 'report_aged_partner_balance_qweb_account'
_order = 'code ASC'
report_id = fields.Many2one(
comodel_name='report_aged_partner_balance_qweb',
ondelete='cascade',
index=True
)
# Data fields, used to keep link with real object
account_id = fields.Many2one(
'account.account',
index=True
)
# Data fields, used for report display
code = fields.Char()
name = fields.Char()
cumul_amount_residual = fields.Float(digits=(16, 2))
cumul_current = fields.Float(digits=(16, 2))
cumul_age_30_days = fields.Float(digits=(16, 2))
cumul_age_60_days = fields.Float(digits=(16, 2))
cumul_age_90_days = fields.Float(digits=(16, 2))
cumul_age_120_days = fields.Float(digits=(16, 2))
cumul_older = fields.Float(digits=(16, 2))
percent_current = fields.Float(digits=(16, 2))
percent_age_30_days = fields.Float(digits=(16, 2))
percent_age_60_days = fields.Float(digits=(16, 2))
percent_age_90_days = fields.Float(digits=(16, 2))
percent_age_120_days = fields.Float(digits=(16, 2))
percent_older = fields.Float(digits=(16, 2))
# Data fields, used to browse report data
partner_ids = fields.One2many(
comodel_name='report_aged_partner_balance_qweb_partner',
inverse_name='report_account_id'
)
class AgedPartnerBalanceReportPartner(models.TransientModel):
_name = 'report_aged_partner_balance_qweb_partner'
report_account_id = fields.Many2one(
comodel_name='report_aged_partner_balance_qweb_account',
ondelete='cascade',
index=True
)
# Data fields, used to keep link with real object
partner_id = fields.Many2one(
'res.partner',
index=True
)
# Data fields, used for report display
name = fields.Char()
# Data fields, used to browse report data
move_line_ids = fields.One2many(
comodel_name='report_aged_partner_balance_qweb_move_line',
inverse_name='report_partner_id'
)
line_ids = fields.One2many(
comodel_name='report_aged_partner_balance_qweb_line',
inverse_name='report_partner_id'
)
@api.model
def _generate_order_by(self, order_spec, query):
"""Custom order to display "No partner allocated" at last position."""
return """
ORDER BY
CASE
WHEN
"report_aged_partner_balance_qweb_partner"."partner_id" IS NOT NULL
THEN 0
ELSE 1
END,
"report_aged_partner_balance_qweb_partner"."name"
"""
class AgedPartnerBalanceReportLine(models.TransientModel):
_name = 'report_aged_partner_balance_qweb_line'
report_partner_id = fields.Many2one(
comodel_name='report_aged_partner_balance_qweb_partner',
ondelete='cascade',
index=True
)
# Data fields, used for report display
partner = fields.Char()
amount_residual = fields.Float(digits=(16, 2))
current = fields.Float(digits=(16, 2))
age_30_days = fields.Float(digits=(16, 2))
age_60_days = fields.Float(digits=(16, 2))
age_90_days = fields.Float(digits=(16, 2))
age_120_days = fields.Float(digits=(16, 2))
older = fields.Float(digits=(16, 2))
class AgedPartnerBalanceReportMoveLine(models.TransientModel):
_name = 'report_aged_partner_balance_qweb_move_line'
report_partner_id = fields.Many2one(
comodel_name='report_aged_partner_balance_qweb_partner',
ondelete='cascade',
index=True
)
# Data fields, used to keep link with real object
move_line_id = fields.Many2one('account.move.line')
# Data fields, used for report display
date = fields.Date()
date_due = fields.Date()
entry = fields.Char()
journal = fields.Char()
account = fields.Char()
partner = fields.Char()
label = fields.Char()
amount_residual = fields.Float(digits=(16, 2))
current = fields.Float(digits=(16, 2))
age_30_days = fields.Float(digits=(16, 2))
age_60_days = fields.Float(digits=(16, 2))
age_90_days = fields.Float(digits=(16, 2))
age_120_days = fields.Float(digits=(16, 2))
older = fields.Float(digits=(16, 2))
class AgedPartnerBalanceReportCompute(models.TransientModel):
""" Here, we just define methods.
For class fields, go more top at this file.
"""
_inherit = 'report_aged_partner_balance_qweb'
@api.multi
def print_report(self, xlsx_report=False):
self.ensure_one()
self.compute_data_for_report()
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)
def _prepare_report_open_items(self):
self.ensure_one()
return {
'date_at': self.date_at,
'only_posted_moves': self.only_posted_moves,
'company_id': self.company_id.id,
'filter_account_ids': [(6, 0, self.filter_account_ids.ids)],
'filter_partner_ids': [(6, 0, self.filter_partner_ids.ids)],
}
@api.multi
def compute_data_for_report(self):
self.ensure_one()
# Compute Open Items Report Data.
# The data of Aged Partner Balance Report
# are based on Open Items Report data.
model = self.env['report_open_items_qweb']
self.open_items_id = model.create(self._prepare_report_open_items())
self.open_items_id.compute_data_for_report()
# Compute report data
self._inject_account_values()
self._inject_partner_values()
self._inject_line_values()
self._inject_line_values(only_empty_partner_line=True)
if self.show_move_line_details:
self._inject_move_line_values()
self._inject_move_line_values(only_empty_partner_line=True)
self._compute_accounts_cumul()
# Refresh cache because all data are computed with SQL requests
self.refresh()
def _inject_account_values(self):
"""Inject report values for report_aged_partner_balance_qweb_account"""
query_inject_account = """
INSERT INTO
report_aged_partner_balance_qweb_account
(
report_id,
create_uid,
create_date,
account_id,
code,
name
)
SELECT
%s AS report_id,
%s AS create_uid,
NOW() AS create_date,
rao.account_id,
rao.code,
rao.name
FROM
report_open_items_qweb_account rao
WHERE
rao.report_id = %s
"""
query_inject_account_params = (
self.id,
self.env.uid,
self.open_items_id.id,
)
self.env.cr.execute(query_inject_account, query_inject_account_params)
def _inject_partner_values(self):
"""Inject report values for report_aged_partner_balance_qweb_partner"""
query_inject_partner = """
INSERT INTO
report_aged_partner_balance_qweb_partner
(
report_account_id,
create_uid,
create_date,
partner_id,
name
)
SELECT
ra.id AS report_account_id,
%s AS create_uid,
NOW() AS create_date,
rpo.partner_id,
rpo.name
FROM
report_open_items_qweb_partner rpo
INNER JOIN
report_open_items_qweb_account rao ON rpo.report_account_id = rao.id
INNER JOIN
report_aged_partner_balance_qweb_account ra ON rao.code = ra.code
WHERE
rao.report_id = %s
AND ra.report_id = %s
"""
query_inject_partner_params = (
self.env.uid,
self.open_items_id.id,
self.id,
)
self.env.cr.execute(query_inject_partner, query_inject_partner_params)
def _inject_line_values(self, only_empty_partner_line=False):
""" Inject report values for report_aged_partner_balance_qweb_line.
The "only_empty_partner_line" value is used
to compute data without partner.
"""
query_inject_line = """
WITH
date_range AS
(
SELECT
%s AS date_current,
DATE %s - INTEGER '30' AS date_less_30_days,
DATE %s - INTEGER '60' AS date_less_60_days,
DATE %s - INTEGER '90' AS date_less_90_days,
DATE %s - INTEGER '120' AS date_less_120_days,
DATE %s - INTEGER '150' AS date_older
)
INSERT INTO
report_aged_partner_balance_qweb_line
(
report_partner_id,
partner,
amount_residual,
current,
age_30_days,
age_60_days,
age_90_days,
age_120_days,
older
)
SELECT
rp.id AS report_partner_id,
rp.name,
SUM(rlo.amount_residual) AS amount_residual,
SUM(
CASE
WHEN rlo.date_due > date_range.date_less_30_days
THEN rlo.amount_residual
END
) AS current,
SUM(
CASE
WHEN
rlo.date_due > date_range.date_less_60_days
AND rlo.date_due <= date_range.date_less_30_days
THEN rlo.amount_residual
END
) AS age_30_days,
SUM(
CASE
WHEN
rlo.date_due > date_range.date_less_90_days
AND rlo.date_due <= date_range.date_less_60_days
THEN rlo.amount_residual
END
) AS age_60_days,
SUM(
CASE
WHEN
rlo.date_due > date_range.date_less_120_days
AND rlo.date_due <= date_range.date_less_90_days
THEN rlo.amount_residual
END
) AS age_90_days,
SUM(
CASE
WHEN
rlo.date_due > date_range.date_older
AND rlo.date_due <= date_range.date_less_120_days
THEN rlo.amount_residual
END
) AS age_120_days,
SUM(
CASE
WHEN rlo.date_due <= date_range.date_older
THEN rlo.amount_residual
END
) AS older
FROM
date_range,
report_open_items_qweb_move_line rlo
INNER JOIN
report_open_items_qweb_partner rpo ON rlo.report_partner_id = rpo.id
INNER JOIN
report_open_items_qweb_account rao ON rpo.report_account_id = rao.id
INNER JOIN
report_aged_partner_balance_qweb_account ra ON rao.code = ra.code
INNER JOIN
report_aged_partner_balance_qweb_partner rp
ON
ra.id = rp.report_account_id
"""
if not only_empty_partner_line:
query_inject_line += """
AND rpo.partner_id = rp.partner_id
"""
elif only_empty_partner_line:
query_inject_line += """
AND rpo.partner_id IS NULL
AND rp.partner_id IS NULL
"""
query_inject_line += """
WHERE
rao.report_id = %s
AND ra.report_id = %s
GROUP BY
rp.id
"""
query_inject_line_params = (self.date_at,) * 6
query_inject_line_params += (
self.open_items_id.id,
self.id,
)
self.env.cr.execute(query_inject_line, query_inject_line_params)
def _inject_move_line_values(self, only_empty_partner_line=False):
""" Inject report values for report_aged_partner_balance_qweb_move_line
The "only_empty_partner_line" value is used
to compute data without partner.
"""
query_inject_move_line = """
WITH
date_range AS
(
SELECT
%s AS date_current,
DATE %s - INTEGER '30' AS date_less_30_days,
DATE %s - INTEGER '60' AS date_less_60_days,
DATE %s - INTEGER '90' AS date_less_90_days,
DATE %s - INTEGER '120' AS date_less_120_days,
DATE %s - INTEGER '150' AS date_older
)
INSERT INTO
report_aged_partner_balance_qweb_move_line
(
report_partner_id,
date,
date_due,
entry,
journal,
account,
partner,
label,
amount_residual,
current,
age_30_days,
age_60_days,
age_90_days,
age_120_days,
older
)
SELECT
rp.id AS report_partner_id,
rlo.date,
rlo.date_due,
rlo.entry,
rlo.journal,
rlo.account,
rlo.partner,
rlo.label,
rlo.amount_residual AS amount_residual,
CASE
WHEN rlo.date_due > date_range.date_less_30_days
THEN rlo.amount_residual
END AS current,
CASE
WHEN
rlo.date_due > date_range.date_less_60_days
AND rlo.date_due <= date_range.date_less_30_days
THEN rlo.amount_residual
END AS age_30_days,
CASE
WHEN
rlo.date_due > date_range.date_less_90_days
AND rlo.date_due <= date_range.date_less_60_days
THEN rlo.amount_residual
END AS age_60_days,
CASE
WHEN
rlo.date_due > date_range.date_less_120_days
AND rlo.date_due <= date_range.date_less_90_days
THEN rlo.amount_residual
END AS age_90_days,
CASE
WHEN
rlo.date_due > date_range.date_older
AND rlo.date_due <= date_range.date_less_120_days
THEN rlo.amount_residual
END AS age_120_days,
CASE
WHEN rlo.date_due <= date_range.date_older
THEN rlo.amount_residual
END AS older
FROM
date_range,
report_open_items_qweb_move_line rlo
INNER JOIN
report_open_items_qweb_partner rpo ON rlo.report_partner_id = rpo.id
INNER JOIN
report_open_items_qweb_account rao ON rpo.report_account_id = rao.id
INNER JOIN
report_aged_partner_balance_qweb_account ra ON rao.code = ra.code
INNER JOIN
report_aged_partner_balance_qweb_partner rp
ON
ra.id = rp.report_account_id
"""
if not only_empty_partner_line:
query_inject_move_line += """
AND rpo.partner_id = rp.partner_id
"""
elif only_empty_partner_line:
query_inject_move_line += """
AND rpo.partner_id IS NULL
AND rp.partner_id IS NULL
"""
query_inject_move_line += """
WHERE
rao.report_id = %s
AND ra.report_id = %s
"""
query_inject_move_line_params = (self.date_at,) * 6
query_inject_move_line_params += (
self.open_items_id.id,
self.id,
)
self.env.cr.execute(query_inject_move_line,
query_inject_move_line_params)
def _compute_accounts_cumul(self):
""" Compute cumulative amount for
report_aged_partner_balance_qweb_account.
"""
query_compute_accounts_cumul = """
WITH
cumuls AS
(
SELECT
ra.id AS report_account_id,
SUM(rl.amount_residual) AS cumul_amount_residual,
SUM(rl.current) AS cumul_current,
SUM(rl.age_30_days) AS cumul_age_30_days,
SUM(rl.age_60_days) AS cumul_age_60_days,
SUM(rl.age_90_days) AS cumul_age_90_days,
SUM(rl.age_120_days) AS cumul_age_120_days,
SUM(rl.older) AS cumul_older
FROM
report_aged_partner_balance_qweb_line rl
INNER JOIN
report_aged_partner_balance_qweb_partner rp
ON rl.report_partner_id = rp.id
INNER JOIN
report_aged_partner_balance_qweb_account ra
ON rp.report_account_id = ra.id
WHERE
ra.report_id = %s
GROUP BY
ra.id
)
UPDATE
report_aged_partner_balance_qweb_account
SET
cumul_amount_residual = c.cumul_amount_residual,
cumul_current = c.cumul_current,
cumul_age_30_days = c.cumul_age_30_days,
cumul_age_60_days = c.cumul_age_60_days,
cumul_age_90_days = c.cumul_age_90_days,
cumul_age_120_days = c.cumul_age_120_days,
cumul_older = c.cumul_older,
percent_current =
CASE
WHEN c.cumul_amount_residual != 0
THEN 100 * c.cumul_current / c.cumul_amount_residual
END,
percent_age_30_days =
CASE
WHEN c.cumul_amount_residual != 0
THEN 100 * c.cumul_age_30_days / c.cumul_amount_residual
END,
percent_age_60_days =
CASE
WHEN c.cumul_amount_residual != 0
THEN 100 * c.cumul_age_60_days / c.cumul_amount_residual
END,
percent_age_90_days =
CASE
WHEN c.cumul_amount_residual != 0
THEN 100 * c.cumul_age_90_days / c.cumul_amount_residual
END,
percent_age_120_days =
CASE
WHEN c.cumul_amount_residual != 0
THEN 100 * c.cumul_age_120_days / c.cumul_amount_residual
END,
percent_older =
CASE
WHEN c.cumul_amount_residual != 0
THEN 100 * c.cumul_older / c.cumul_amount_residual
END
FROM
cumuls c
WHERE
id = c.report_account_id
"""
params_compute_accounts_cumul = (self.id,)
self.env.cr.execute(query_compute_accounts_cumul,
params_compute_accounts_cumul)

269
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
)

1151
account_financial_report_qweb/report/general_ledger.py
File diff suppressed because it is too large
View File

149
account_financial_report_qweb/report/general_ledger_xlsx.py

@ -0,0 +1,149 @@
# -*- 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 . import abstract_report_xlsx
from openerp.report import report_sxw
from openerp import _
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)
def _get_report_name(self):
return _('General Ledger')
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': _('Cost center'),
'field': 'cost_center',
'width': 15},
7: {'header': _('Rec.'), 'field': 'matching_number', 'width': 5},
8: {'header': _('Debit'),
'field': 'debit',
'field_initial_balance': 'initial_debit',
'field_final_balance': 'final_debit',
'type': 'amount',
'width': 14},
9: {'header': _('Credit'),
'field': 'credit',
'field_initial_balance': 'initial_credit',
'field_final_balance': 'final_credit',
'type': 'amount',
'width': 14},
10: {'header': _('Cumul. Bal.'),
'field': 'cumul_balance',
'field_initial_balance': 'initial_balance',
'field_final_balance': 'final_balance',
'type': 'amount',
'width': 14},
11: {'header': _('Cur.'), 'field': 'currency_name', 'width': 7},
12: {'header': _('Amount cur.'),
'field': 'amount_currency',
'type': 'amount',
'width': 14},
}
def _get_report_filters(self, report):
return [
[_('Date range filter'),
_('From: %s To: %s') % (report.date_from, report.date_to)],
[_('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')],
[_('Centralize filter'),
_('Yes') if report.centralize else _('No')],
]
def _get_col_count_filter_name(self):
return 2
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:
# Write account title
self.write_array_title(account.code + ' - ' + account.name)
if not account.partner_ids:
# Display array header for move lines
self.write_array_header()
# Display initial balance line for account
self.write_initial_balance(account, _('Initial balance'))
# Display account move lines
for line in account.move_line_ids:
self.write_line(line)
else:
# 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 initial balance line for partner
self.write_initial_balance(partner, _('Initial balance'))
# 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 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')
super(GeneralLedgerXslx, self).write_ending_balance(
my_object, name, label
)
GeneralLedgerXslx(
'report.account_financial_report_qweb.report_general_ledger_xlsx',
'report_general_ledger_qweb',
parser=report_sxw.rml_parse
)

724
account_financial_report_qweb/report/open_items.py

@ -0,0 +1,724 @@
# -*- coding: utf-8 -*-
# © 2016 Julien Coux (Camptocamp)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, fields, api, _
class OpenItemsReport(models.TransientModel):
""" Here, we just define class fields.
For methods, go more bottom at this file.
The class hierarchy is :
* OpenItemsReport
** OpenItemsReportAccount
*** OpenItemsReportPartner
**** OpenItemsReportMoveLine
"""
_name = 'report_open_items_qweb'
# Filters fields, used for data computation
date_at = fields.Date()
only_posted_moves = fields.Boolean()
hide_account_balance_at_0 = fields.Boolean()
company_id = fields.Many2one(comodel_name='res.company')
filter_account_ids = fields.Many2many(comodel_name='account.account')
filter_partner_ids = fields.Many2many(comodel_name='res.partner')
# Flag fields, used for report display
has_second_currency = fields.Boolean()
# Data fields, used to browse report data
account_ids = fields.One2many(
comodel_name='report_open_items_qweb_account',
inverse_name='report_id'
)
class OpenItemsReportAccount(models.TransientModel):
_name = 'report_open_items_qweb_account'
_order = 'code ASC'
report_id = fields.Many2one(
comodel_name='report_open_items_qweb',
ondelete='cascade',
index=True
)
# Data fields, used to keep link with real object
account_id = fields.Many2one(
'account.account',
index=True
)
# Data fields, used for report display
code = fields.Char()
name = fields.Char()
final_amount_residual = fields.Float(digits=(16, 2))
# Data fields, used to browse report data
partner_ids = fields.One2many(
comodel_name='report_open_items_qweb_partner',
inverse_name='report_account_id'
)
class OpenItemsReportPartner(models.TransientModel):
_name = 'report_open_items_qweb_partner'
report_account_id = fields.Many2one(
comodel_name='report_open_items_qweb_account',
ondelete='cascade',
index=True
)
# Data fields, used to keep link with real object
partner_id = fields.Many2one(
'res.partner',
index=True
)
# Data fields, used for report display
name = fields.Char()
final_amount_residual = fields.Float(digits=(16, 2))
# Data fields, used to browse report data
move_line_ids = fields.One2many(
comodel_name='report_open_items_qweb_move_line',
inverse_name='report_partner_id'
)
@api.model
def _generate_order_by(self, order_spec, query):
"""Custom order to display "No partner allocated" at last position."""
return """
ORDER BY
CASE
WHEN "report_open_items_qweb_partner"."partner_id" IS NOT NULL
THEN 0
ELSE 1
END,
"report_open_items_qweb_partner"."name"
"""
class OpenItemsReportMoveLine(models.TransientModel):
_name = 'report_open_items_qweb_move_line'
report_partner_id = fields.Many2one(
comodel_name='report_open_items_qweb_partner',
ondelete='cascade',
index=True
)
# Data fields, used to keep link with real object
move_line_id = fields.Many2one('account.move.line')
# Data fields, used for report display
date = fields.Date()
date_due = fields.Date()
entry = fields.Char()
journal = fields.Char()
account = fields.Char()
partner = fields.Char()
label = fields.Char()
amount_total_due = fields.Float(digits=(16, 2))
amount_residual = fields.Float(digits=(16, 2))
currency_name = fields.Char()
amount_total_due_currency = fields.Float(digits=(16, 2))
amount_residual_currency = fields.Float(digits=(16, 2))
class OpenItemsReportCompute(models.TransientModel):
""" Here, we just define methods.
For class fields, go more top at this file.
"""
_inherit = 'report_open_items_qweb'
@api.multi
def print_report(self, xlsx_report=False):
self.ensure_one()
self.compute_data_for_report()
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)
@api.multi
def compute_data_for_report(self):
self.ensure_one()
# Compute report data
self._inject_account_values()
self._inject_partner_values()
self._inject_line_values()
self._inject_line_values(only_empty_partner_line=True)
self._clean_partners_and_accounts()
self._compute_partners_and_accounts_cumul()
if self.hide_account_balance_at_0:
self._clean_partners_and_accounts(
only_delete_account_balance_at_0=True
)
# Compute display flag
self._compute_has_second_currency()
# Refresh cache because all data are computed with SQL requests
self.refresh()
def _inject_account_values(self):
"""Inject report values for report_open_items_qweb_account."""
query_inject_account = """
WITH
accounts AS
(
SELECT
a.id,
a.code,
a.name,
a.user_type_id
FROM
account_account a
INNER JOIN
account_move_line ml ON a.id = ml.account_id AND ml.date <= %s
"""
if self.filter_partner_ids:
query_inject_account += """
INNER JOIN
res_partner p ON ml.partner_id = p.id
"""
if self.only_posted_moves:
query_inject_account += """
INNER JOIN
account_move m ON ml.move_id = m.id AND m.state = 'posted'
"""
query_inject_account += """
WHERE
a.company_id = %s
AND a.internal_type IN ('payable', 'receivable')
"""
if self.filter_account_ids:
query_inject_account += """
AND
a.id IN %s
"""
if self.filter_partner_ids:
query_inject_account += """
AND
p.id IN %s
"""
query_inject_account += """
GROUP BY
a.id
)
INSERT INTO
report_open_items_qweb_account
(
report_id,
create_uid,
create_date,
account_id,
code,
name
)
SELECT
%s AS report_id,
%s AS create_uid,
NOW() AS create_date,
a.id AS account_id,
a.code,
a.name
FROM
accounts a
"""
query_inject_account_params = (
self.date_at,
self.company_id.id,
)
if self.filter_account_ids:
query_inject_account_params += (
tuple(self.filter_account_ids.ids),
)
if self.filter_partner_ids:
query_inject_account_params += (
tuple(self.filter_partner_ids.ids),
)
query_inject_account_params += (
self.id,
self.env.uid,
)
self.env.cr.execute(query_inject_account, query_inject_account_params)
def _inject_partner_values(self):
""" Inject report values for report_open_items_qweb_partner. """
query_inject_partner = """
WITH
accounts_partners AS
(
SELECT
ra.id AS report_account_id,
a.id AS account_id,
at.include_initial_balance AS include_initial_balance,
p.id AS partner_id,
COALESCE(
CASE
WHEN
NULLIF(p.name, '') IS NOT NULL
AND NULLIF(p.ref, '') IS NOT NULL
THEN p.name || ' (' || p.ref || ')'
ELSE p.name
END,
'""" + _('No partner allocated') + """'
) AS partner_name
FROM
report_open_items_qweb_account ra
INNER JOIN
account_account a ON ra.account_id = a.id
INNER JOIN
account_account_type at ON a.user_type_id = at.id
INNER JOIN
account_move_line ml ON a.id = ml.account_id AND ml.date <= %s
"""
if self.only_posted_moves:
query_inject_partner += """
INNER JOIN
account_move m ON ml.move_id = m.id AND m.state = 'posted'
"""
query_inject_partner += """
LEFT JOIN
res_partner p ON ml.partner_id = p.id
WHERE
ra.report_id = %s
"""
if self.filter_partner_ids:
query_inject_partner += """
AND
p.id IN %s
"""
query_inject_partner += """
GROUP BY
ra.id,
a.id,
p.id,
at.include_initial_balance
)
INSERT INTO
report_open_items_qweb_partner
(
report_account_id,
create_uid,
create_date,
partner_id,
name
)
SELECT
ap.report_account_id,
%s AS create_uid,
NOW() AS create_date,
ap.partner_id,
ap.partner_name
FROM
accounts_partners ap
"""
query_inject_partner_params = (
self.date_at,
self.id,
)
if self.filter_partner_ids:
query_inject_partner_params += (
tuple(self.filter_partner_ids.ids),
)
query_inject_partner_params += (
self.env.uid,
)
self.env.cr.execute(query_inject_partner, query_inject_partner_params)
def _inject_line_values(self, only_empty_partner_line=False):
""" Inject report values for report_open_items_qweb_move_line.
The "only_empty_partner_line" value is used
to compute data without partner.
"""
query_inject_move_line = """
WITH
move_lines_amount AS
(
SELECT
ml.id,
ml.balance,
SUM(
CASE
WHEN ml_past.id IS NOT NULL
THEN pr.amount
ELSE NULL
END
) AS partial_amount,
ml.amount_currency,
SUM(
CASE
WHEN ml_past.id IS NOT NULL
THEN pr.amount_currency
ELSE NULL
END
) AS partial_amount_currency
FROM
report_open_items_qweb_partner rp
INNER JOIN
report_open_items_qweb_account ra
ON rp.report_account_id = ra.id
INNER JOIN
account_move_line ml
ON ra.account_id = ml.account_id
"""
if not only_empty_partner_line:
query_inject_move_line += """
AND rp.partner_id = ml.partner_id
"""
elif only_empty_partner_line:
query_inject_move_line += """
AND ml.partner_id IS NULL
"""
query_inject_move_line += """
LEFT JOIN
account_partial_reconcile pr
ON ml.balance < 0 AND pr.credit_move_id = ml.id
OR ml.balance > 0 AND pr.debit_move_id = ml.id
LEFT JOIN
account_move_line ml_future
ON (
ml.balance < 0 AND pr.debit_move_id = ml_future.id
OR ml.balance > 0 AND pr.credit_move_id = ml_future.id
)
AND ml_future.date >= %s
LEFT JOIN
account_move_line ml_past
ON (
ml.balance < 0 AND pr.debit_move_id = ml_past.id
OR ml.balance > 0 AND pr.credit_move_id = ml_past.id
)
AND ml_past.date < %s
WHERE
ra.report_id = %s
GROUP BY
ml.id,
ml.balance,
ml.amount_currency
HAVING
(
ml.full_reconcile_id IS NULL
OR MAX(ml_future.id) IS NOT NULL
)
),
move_lines AS
(
SELECT
id,
CASE
WHEN SUM(partial_amount) > 0
THEN
CASE
WHEN balance > 0
THEN balance - SUM(partial_amount)
ELSE balance + SUM(partial_amount)
END
ELSE balance
END AS amount_residual,
CASE
WHEN SUM(partial_amount_currency) > 0
THEN
CASE
WHEN amount_currency > 0
THEN amount_currency - SUM(partial_amount_currency)
ELSE amount_currency + SUM(partial_amount_currency)
END
ELSE amount_currency
END AS amount_residual_currency
FROM
move_lines_amount
GROUP BY
id,
balance,
amount_currency
)
INSERT INTO
report_open_items_qweb_move_line
(
report_partner_id,
create_uid,
create_date,
move_line_id,
date,
date_due,
entry,
journal,
account,
partner,
label,
amount_total_due,
amount_residual,
currency_name,
amount_total_due_currency,
amount_residual_currency
)
SELECT
rp.id AS report_partner_id,
%s AS create_uid,
NOW() AS create_date,
ml.id AS move_line_id,
ml.date,
ml.date_maturity,
m.name AS entry,
j.code AS journal,
a.code AS account,
"""
if not only_empty_partner_line:
query_inject_move_line += """
CASE
WHEN
NULLIF(p.name, '') IS NOT NULL
AND NULLIF(p.ref, '') IS NOT NULL
THEN p.name || ' (' || p.ref || ')'
ELSE p.name
END AS partner,
"""
elif only_empty_partner_line:
query_inject_move_line += """
'""" + _('No partner allocated') + """' AS partner,
"""
query_inject_move_line += """
CONCAT_WS(' - ', NULLIF(ml.ref, ''), NULLIF(ml.name, '')) AS label,
ml.balance,
ml2.amount_residual,
c.name AS currency_name,
ml.amount_currency,
ml2.amount_residual_currency
FROM
report_open_items_qweb_partner rp
INNER JOIN
report_open_items_qweb_account ra ON rp.report_account_id = ra.id
INNER JOIN
account_move_line ml ON ra.account_id = ml.account_id
INNER JOIN
move_lines ml2
ON ml.id = ml2.id
AND ml2.amount_residual IS NOT NULL
AND ml2.amount_residual != 0
INNER JOIN
account_move m ON ml.move_id = m.id
INNER JOIN
account_journal j ON ml.journal_id = j.id
INNER JOIN
account_account a ON ml.account_id = a.id
"""
if not only_empty_partner_line:
query_inject_move_line += """
INNER JOIN
res_partner p
ON ml.partner_id = p.id AND rp.partner_id = p.id
"""
query_inject_move_line += """
LEFT JOIN
account_full_reconcile fr ON ml.full_reconcile_id = fr.id
LEFT JOIN
res_currency c ON a.currency_id = c.id
WHERE
ra.report_id = %s
AND
ml.date <= %s
"""
if self.only_posted_moves:
query_inject_move_line += """
AND
m.state = 'posted'
"""
if only_empty_partner_line:
query_inject_move_line += """
AND
ml.partner_id IS NULL
AND
rp.partner_id IS NULL
"""
if not only_empty_partner_line:
query_inject_move_line += """
ORDER BY
a.code, p.name, ml.date, ml.id
"""
elif only_empty_partner_line:
query_inject_move_line += """
ORDER BY
a.code, ml.date, ml.id
"""
self.env.cr.execute(
query_inject_move_line,
(self.date_at,
self.date_at,
self.id,
self.env.uid,
self.id,
self.date_at,)
)
def _compute_partners_and_accounts_cumul(self):
""" Compute cumulative amount for
report_open_items_qweb_partner and report_open_items_qweb_account.
"""
query_compute_partners_cumul = """
UPDATE
report_open_items_qweb_partner
SET
final_amount_residual =
(
SELECT
SUM(rml.amount_residual) AS final_amount_residual
FROM
report_open_items_qweb_move_line rml
WHERE
rml.report_partner_id = report_open_items_qweb_partner.id
)
WHERE
id IN
(
SELECT
rp.id
FROM
report_open_items_qweb_account ra
INNER JOIN
report_open_items_qweb_partner rp
ON ra.id = rp.report_account_id
WHERE
ra.report_id = %s
)
"""
params_compute_partners_cumul = (self.id,)
self.env.cr.execute(query_compute_partners_cumul,
params_compute_partners_cumul)
query_compute_accounts_cumul = """
UPDATE
report_open_items_qweb_account
SET
final_amount_residual =
(
SELECT
SUM(rp.final_amount_residual) AS final_amount_residual
FROM
report_open_items_qweb_partner rp
WHERE
rp.report_account_id = report_open_items_qweb_account.id
)
WHERE
report_id = %s
"""
params_compute_accounts_cumul = (self.id,)
self.env.cr.execute(query_compute_accounts_cumul,
params_compute_accounts_cumul)
def _clean_partners_and_accounts(self,
only_delete_account_balance_at_0=False):
""" Delete empty data for
report_open_items_qweb_partner and report_open_items_qweb_account.
The "only_delete_account_balance_at_0" value is used
to delete also the data with cumulative amounts at 0.
"""
query_clean_partners = """
DELETE FROM
report_open_items_qweb_partner
WHERE
id IN
(
SELECT
DISTINCT rp.id
FROM
report_open_items_qweb_account ra
INNER JOIN
report_open_items_qweb_partner rp
ON ra.id = rp.report_account_id
LEFT JOIN
report_open_items_qweb_move_line rml
ON rp.id = rml.report_partner_id
WHERE
ra.report_id = %s
"""
if not only_delete_account_balance_at_0:
query_clean_partners += """
AND rml.id IS NULL
"""
elif only_delete_account_balance_at_0:
query_clean_partners += """
AND (
rp.final_amount_residual IS NULL
OR rp.final_amount_residual = 0
)
"""
query_clean_partners += """
)
"""
params_clean_partners = (self.id,)
self.env.cr.execute(query_clean_partners, params_clean_partners)
query_clean_accounts = """
DELETE FROM
report_open_items_qweb_account
WHERE
id IN
(
SELECT
DISTINCT ra.id
FROM
report_open_items_qweb_account ra
LEFT JOIN
report_open_items_qweb_partner rp
ON ra.id = rp.report_account_id
WHERE
ra.report_id = %s
"""
if not only_delete_account_balance_at_0:
query_clean_accounts += """
AND rp.id IS NULL
"""
elif only_delete_account_balance_at_0:
query_clean_accounts += """
AND (
ra.final_amount_residual IS NULL
OR ra.final_amount_residual = 0
)
"""
query_clean_accounts += """
)
"""
params_clean_accounts = (self.id,)
self.env.cr.execute(query_clean_accounts, params_clean_accounts)
def _compute_has_second_currency(self):
""" Compute "has_second_currency" flag which will used for display."""
query_update_has_second_currency = """
UPDATE
report_open_items_qweb
SET
has_second_currency =
(
SELECT
TRUE
FROM
report_open_items_qweb_move_line l
INNER JOIN
report_open_items_qweb_partner p
ON l.report_partner_id = p.id
INNER JOIN
report_open_items_qweb_account a
ON p.report_account_id = a.id
WHERE
a.report_id = %s
AND l.currency_name IS NOT NULL
LIMIT 1
)
WHERE id = %s
"""
params = (self.id,) * 2
self.env.cr.execute(query_update_has_second_currency, params)

117
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
)

327
account_financial_report_qweb/report/templates/aged_partner_balance.xml

@ -0,0 +1,327 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="account_financial_report_qweb.report_aged_partner_balance_qweb">
<t t-call="report.html_container">
<t t-foreach="docs" t-as="o">
<!-- Saved flag fields into variables, used to define columns display -->
<t t-set="show_move_line_details" t-value="o.show_move_line_details"/>
<t t-call="account_financial_report_qweb.internal_layout">
<!-- Defines global variables used by internal layout -->
<t t-set="title">Aged Partner Balance</t>
<t t-set="company_name" t-value="o.company_id.name"/>
<div class="page">
<!-- Display filters -->
<t t-call="account_financial_report_qweb.report_aged_partner_balance_qweb_filters"/>
<t t-foreach="o.account_ids" t-as="account">
<div class="page_break">
<!-- Display account header -->
<div class="act_as_table list_table" style="margin-top: 10px;"/>
<div class="act_as_caption account_title" style="width: 1141px !important;">
<span t-field="account.code"/> - <span t-field="account.name"/>
</div>
<!-- Display account lines -->
<t t-if="not show_move_line_details">
<div class="act_as_table data_table" style="width: 1140px !important;">
<!-- Display account header -->
<t t-call="account_financial_report_qweb.report_aged_partner_balance_qweb_lines_header"/>
<t t-foreach="account.partner_ids" t-as="partner">
<!-- Display one line per partner -->
<t t-call="account_financial_report_qweb.report_aged_partner_balance_qweb_lines"/>
</t>
</div>
<!-- Display account footer -->
<t t-call="account_financial_report_qweb.report_aged_partner_balance_qweb_account_ending_cumul"/>
</t>
<!-- Display account move lines -->
<t t-if="show_move_line_details">
<!-- Display account partners -->
<t t-foreach="account.partner_ids" t-as="partner">
<div class="page_break">
<!-- Display partner header -->
<div class="act_as_caption account_title">
<span t-field="partner.name"/>
</div>
<!-- Display partner move lines -->
<t t-call="account_financial_report_qweb.report_aged_partner_balance_qweb_move_lines"/>
<!-- Display partner footer -->
<t t-call="account_financial_report_qweb.report_aged_partner_balance_qweb_partner_ending_cumul">
<t t-set="partner_cumul_line" t-value="partner.line_ids"/>
</t>
</div>
</t>
<!-- Display account footer -->
<t t-call="account_financial_report_qweb.report_aged_partner_balance_qweb_account_ending_cumul"/>
</t>
</div>
</t>
</div>
</t>
</t>
</t>
</template>
<template id="account_financial_report_qweb.report_aged_partner_balance_qweb_filters">
<div class="act_as_table data_table" style="width: 1140px !important;">
<div class="act_as_row labels">
<div class="act_as_cell">Date at filter</div>
<div class="act_as_cell">Target moves filter</div>
</div>
<div class="act_as_row">
<div class="act_as_cell">
<span t-field="o.date_at"/>
</div>
<div class="act_as_cell">
<t t-if="o.only_posted_moves">All posted entries</t>
<t t-if="not o.only_posted_moves">All entries</t>
</div>
</div>
</div>
</template>
<template id="account_financial_report_qweb.report_aged_partner_balance_qweb_lines_header">
<!-- Display table headers for lines -->
<div class="act_as_thead">
<div class="act_as_row labels">
<!--## partner-->
<div class="act_as_cell" style="width: 370px;">Partner</div>
<!--## amount_residual-->
<div class="act_as_cell" style="width: 110px;">Residual</div>
<!--## current-->
<div class="act_as_cell" style="width: 110px;">Current</div>
<!--## age_30_days-->
<div class="act_as_cell" style="width: 110px;">Age ≤ 30 d.</div>
<!--## age_60_days-->
<div class="act_as_cell" style="width: 110px;">Age ≤ 60 d.</div>
<!--## age_90_days-->
<div class="act_as_cell" style="width: 110px;">Age ≤ 90 d.</div>
<!--## age_120_days-->
<div class="act_as_cell" style="width: 110px;">Age ≤ 120 d.</div>
<!--## older-->
<div class="act_as_cell" style="width: 110px;">Older</div>
</div>
</div>
</template>
<template id="account_financial_report_qweb.report_aged_partner_balance_qweb_lines">
<!-- Display each lines -->
<t t-foreach="partner.line_ids" t-as="line">
<!-- # lines -->
<div class="act_as_row lines">
<!--## partner-->
<div class="act_as_cell left"><span t-field="line.partner"/></div>
<!--## amount_residual-->
<div class="act_as_cell amount"><span t-field="line.amount_residual"/></div>
<!--## current-->
<div class="act_as_cell amount"><span t-field="line.current"/></div>
<!--## age_30_days-->
<div class="act_as_cell amount"><span t-field="line.age_30_days"/></div>
<!--## age_60_days-->
<div class="act_as_cell amount"><span t-field="line.age_60_days"/></div>
<!--## age_90_days-->
<div class="act_as_cell amount"><span t-field="line.age_90_days"/></div>
<!--## age_120_days-->
<div class="act_as_cell amount"><span t-field="line.age_120_days"/></div>
<!--## older-->
<div class="act_as_cell amount"><span t-field="line.older"/></div>
</div>
</t>
</template>
<template id="account_financial_report_qweb.report_aged_partner_balance_qweb_move_lines">
<div class="act_as_table data_table" style="width: 1140px !important;">
<!-- Display table headers for move lines -->
<div class="act_as_thead">
<div class="act_as_row labels">
<!--## date-->
<div class="act_as_cell first_column" style="width: 60px;">Date</div>
<!--## move-->
<div class="act_as_cell" style="width: 100px;">Entry</div>
<!--## journal-->
<div class="act_as_cell" style="width: 40px;">Journal</div>
<!--## account code-->
<div class="act_as_cell" style="width: 50px;">Account</div>
<!--## partner-->
<div class="act_as_cell" style="width: 120px;">Partner</div>
<!--## ref - label-->
<div class="act_as_cell" style="width: 220px;">Ref - Label</div>
<!--## date_due-->
<div class="act_as_cell" style="width: 60px;">Due date</div>
<!--## amount_residual-->
<div class="act_as_cell" style="width: 70px;">Residual</div>
<!--## current-->
<div class="act_as_cell" style="width: 70px;">Current</div>
<!--## age_30_days-->
<div class="act_as_cell" style="width: 70px;">Age ≤ 30 d.</div>
<!--## age_60_days-->
<div class="act_as_cell" style="width: 70px;">Age ≤ 60 d.</div>
<!--## age_90_days-->
<div class="act_as_cell" style="width: 70px;">Age ≤ 90 d.</div>
<!--## age_120_days-->
<div class="act_as_cell" style="width: 70px;">Age ≤ 120 d.</div>
<!--## older-->
<div class="act_as_cell" style="width: 70px;">Older</div>
</div>
</div>
<!-- Display each move lines -->
<t t-foreach="partner.move_line_ids" t-as="line">
<!-- # lines or centralized lines -->
<div class="act_as_row lines">
<!--## date-->
<div class="act_as_cell left"><span t-field="line.date"/></div>
<!--## move-->
<div class="act_as_cell left"><span t-field="line.entry"/></div>
<!--## journal-->
<div class="act_as_cell left"><span t-field="line.journal"/></div>
<!--## account code-->
<div class="act_as_cell left"><span t-field="line.account"/></div>
<!--## partner-->
<div class="act_as_cell left"><span t-field="line.partner"/></div>
<!--## ref - label-->
<div class="act_as_cell left"><span t-field="line.label"/></div>
<!--## date_due-->
<div class="act_as_cell left"><span t-field="line.date_due"/></div>
<!--## amount_residual-->
<div class="act_as_cell amount"><span t-field="line.amount_residual"/></div>
<!--## current-->
<div class="act_as_cell amount"><span t-field="line.current"/></div>
<!--## age_30_days-->
<div class="act_as_cell amount"><span t-field="line.age_30_days"/></div>
<!--## age_60_days-->
<div class="act_as_cell amount"><span t-field="line.age_60_days"/></div>
<!--## age_90_days-->
<div class="act_as_cell amount"><span t-field="line.age_90_days"/></div>
<!--## age_120_days-->
<div class="act_as_cell amount"><span t-field="line.age_120_days"/></div>
<!--## older-->
<div class="act_as_cell amount"><span t-field="line.older"/></div>
</div>
</t>
</div>
</template>
<template id="account_financial_report_qweb.report_aged_partner_balance_qweb_partner_ending_cumul">
<!-- Display ending balance line for partner -->
<div class="act_as_table list_table" style="width: 1141px !important;">
<div class="act_as_row labels" style="font-weight: bold;">
<!--## date-->
<div class="act_as_cell right" style="width: 590px;">Partner cumul aged balance</div>
<!--## date_due-->
<div class="act_as_cell" style="width: 60px;"/>
<!--## amount_residual-->
<div class="act_as_cell amount" style="width: 70px;"><span t-field="partner_cumul_line.amount_residual"/></div>
<!--## current-->
<div class="act_as_cell amount" style="width: 70px;"><span t-field="partner_cumul_line.current"/></div>
<!--## age_30_days-->
<div class="act_as_cell amount" style="width: 70px;"><span t-field="partner_cumul_line.age_30_days"/></div>
<!--## age_60_days-->
<div class="act_as_cell amount" style="width: 70px;"><span t-field="partner_cumul_line.age_60_days"/></div>
<!--## age_90_days-->
<div class="act_as_cell amount" style="width: 70px;"><span t-field="partner_cumul_line.age_90_days"/></div>
<!--## age_120_days-->
<div class="act_as_cell amount" style="width: 70px;"><span t-field="partner_cumul_line.age_120_days"/></div>
<!--## older-->
<div class="act_as_cell amount" style="width: 70px;"><span t-field="partner_cumul_line.older"/></div>
</div>
</div>
</template>
<template id="account_financial_report_qweb.report_aged_partner_balance_qweb_account_ending_cumul">
<!-- Display ending balance line for account -->
<div class="act_as_table list_table" style="width: 1141px !important;">
<div class="act_as_row labels" style="font-weight: bold;">
<t t-if="not show_move_line_details">
<!--## total-->
<div class="act_as_cell right" style="width: 370px;">Total</div>
<!--## amount_residual-->
<div class="act_as_cell amount" style="width: 110px;"><span t-field="account.cumul_amount_residual"/></div>
<!--## current-->
<div class="act_as_cell amount" style="width: 110px;"><span t-field="account.cumul_current"/></div>
<!--## age_30_days-->
<div class="act_as_cell amount" style="width: 110px;"><span t-field="account.cumul_age_30_days"/></div>
<!--## age_60_days-->
<div class="act_as_cell amount" style="width: 110px;"><span t-field="account.cumul_age_60_days"/></div>
<!--## age_90_days-->
<div class="act_as_cell amount" style="width: 110px;"><span t-field="account.cumul_age_90_days"/></div>
<!--## age_120_days-->
<div class="act_as_cell amount" style="width: 110px;"><span t-field="account.cumul_age_120_days"/></div>
<!--## older-->
<div class="act_as_cell amount" style="width: 110px;"><span t-field="account.cumul_older"/></div>
</t>
<t t-if="show_move_line_details">
<!--## total-->
<div class="act_as_cell right" style="width: 590px;">Total</div>
<!--## date_due-->
<div class="act_as_cell" style="width: 60px;"/>
<!--## amount_residual-->
<div class="act_as_cell amount" style="width: 70px;"><span t-field="account.cumul_amount_residual"/></div>
<!--## current-->
<div class="act_as_cell amount" style="width: 70px;"><span t-field="account.cumul_current"/></div>
<!--## age_30_days-->
<div class="act_as_cell amount" style="width: 70px;"><span t-field="account.cumul_age_30_days"/></div>
<!--## age_60_days-->
<div class="act_as_cell amount" style="width: 70px;"><span t-field="account.cumul_age_60_days"/></div>
<!--## age_90_days-->
<div class="act_as_cell amount" style="width: 70px;"><span t-field="account.cumul_age_90_days"/></div>
<!--## age_120_days-->
<div class="act_as_cell amount" style="width: 70px;"><span t-field="account.cumul_age_120_days"/></div>
<!--## older-->
<div class="act_as_cell amount" style="width: 70px;"><span t-field="account.cumul_older"/></div>
</t>
</div>
<div class="act_as_row" style="font-weight: bold; font-style: italic;">
<t t-if="not show_move_line_details">
<!--## total-->
<div class="act_as_cell right" style="width: 370px;">Percents</div>
<!--## amount_residual-->
<div class="act_as_cell amount" style="width: 110px;"></div>
<!--## current-->
<div class="act_as_cell amount" style="width: 110px;"><span t-field="account.percent_current"/>%</div>
<!--## age_30_days-->
<div class="act_as_cell amount" style="width: 110px;"><span t-field="account.percent_age_30_days"/>%</div>
<!--## age_60_days-->
<div class="act_as_cell amount" style="width: 110px;"><span t-field="account.percent_age_60_days"/>%</div>
<!--## age_90_days-->
<div class="act_as_cell amount" style="width: 110px;"><span t-field="account.percent_age_90_days"/>%</div>
<!--## age_120_days-->
<div class="act_as_cell amount" style="width: 110px;"><span t-field="account.percent_age_120_days"/>%</div>
<!--## older-->
<div class="act_as_cell amount" style="width: 110px;"><span t-field="account.percent_older"/>%</div>
</t>
<t t-if="show_move_line_details">
<!--## total-->
<div class="act_as_cell right" style="width: 590px;">Percents</div>
<!--## date_due-->
<div class="act_as_cell" style="width: 60px;"/>
<!--## amount_residual-->
<div class="act_as_cell amount" style="width: 70px;"></div>
<!--## current-->
<div class="act_as_cell amount" style="width: 70px;"><span t-field="account.percent_current"/>%</div>
<!--## age_30_days-->
<div class="act_as_cell amount" style="width: 70px;"><span t-field="account.percent_age_30_days"/>%</div>
<!--## age_60_days-->
<div class="act_as_cell amount" style="width: 70px;"><span t-field="account.percent_age_60_days"/>%</div>
<!--## age_90_days-->
<div class="act_as_cell amount" style="width: 70px;"><span t-field="account.percent_age_90_days"/>%</div>
<!--## age_120_days-->
<div class="act_as_cell amount" style="width: 70px;"><span t-field="account.percent_age_120_days"/>%</div>
<!--## older-->
<div class="act_as_cell amount" style="width: 70px;"><span t-field="account.percent_older"/>%</div>
</t>
</div>
</div>
</template>
</odoo>

250
account_financial_report_qweb/report/templates/general_ledger.xml

@ -0,0 +1,250 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="account_financial_report_qweb.report_general_ledger_qweb">
<t t-call="report.html_container">
<t t-foreach="docs" t-as="o">
<!-- Saved flag fields into variables, used to define columns display -->
<t t-set="show_cost_center" t-value="o.show_cost_center"/>
<t t-set="has_second_currency" t-value="o.has_second_currency"/>
<t t-call="account_financial_report_qweb.internal_layout">
<!-- Defines global variables used by internal layout -->
<t t-set="title">General Ledger</t>
<t t-set="company_name" t-value="o.company_id.name"/>
<div class="page">
<!-- Display filters -->
<t t-call="account_financial_report_qweb.report_general_ledger_qweb_filters"/>
<t t-foreach="o.account_ids" t-as="account">
<div class="page_break">
<!-- Display account header -->
<div class="act_as_table list_table" style="margin-top: 10px;"/>
<div class="act_as_caption account_title" style="width: 1141px !important;">
<span t-field="account.code"/> - <span t-field="account.name"/>
</div>
<t t-if="not account.partner_ids">
<!-- Display account move lines without partner regroup -->
<t t-call="account_financial_report_qweb.report_general_ledger_qweb_lines">
<t t-set="account_or_partner_object" t-value="account"/>
</t>
</t>
<t t-if="account.partner_ids">
<!-- Display account partners -->
<t t-foreach="account.partner_ids" t-as="partner">
<div class="page_break">
<!-- Display partner header -->
<div class="act_as_caption account_title">
<span t-field="partner.name"/>
</div>
<!-- Display partner move lines -->
<t t-call="account_financial_report_qweb.report_general_ledger_qweb_lines">
<t t-set="account_or_partner_object" t-value="partner"/>
</t>
<!-- Display partner footer -->
<t t-call="account_financial_report_qweb.report_general_ledger_qweb_ending_cumul">
<t t-set="account_or_partner_object" t-value="partner"/>
<t t-set="type" t-value='"partner_type"'/>
</t>
</div>
</t>
</t>
<!-- Display account footer -->
<t t-call="account_financial_report_qweb.report_general_ledger_qweb_ending_cumul">
<t t-set="account_or_partner_object" t-value="account"/>
<t t-set="type" t-value='"account_type"'/>
</t>
</div>
</t>
</div>
</t>
</t>
</t>
</template>
<template id="account_financial_report_qweb.report_general_ledger_qweb_filters">
<div class="act_as_table data_table" style="width: 1140px !important;">
<div class="act_as_row labels">
<div class="act_as_cell">Date range filter</div>
<div class="act_as_cell">Target moves filter</div>
<div class="act_as_cell">Account balance at 0 filter</div>
<div class="act_as_cell">Centralize filter</div>
</div>
<div class="act_as_row">
<div class="act_as_cell">
From: <span t-field="o.date_from"/> To: <span t-field="o.date_to"/>
</div>
<div class="act_as_cell">
<t t-if="o.only_posted_moves">All posted entries</t>
<t t-if="not o.only_posted_moves">All entries</t>
</div>
<div class="act_as_cell">
<t t-if="o.hide_account_balance_at_0">Hide</t>
<t t-if="not o.hide_account_balance_at_0">Show</t>
</div>
<div class="act_as_cell">
<t t-if="o.centralize">Yes</t>
<t t-if="not o.centralize">No</t>
</div>
</div>
</div>
</template>
<template id="account_financial_report_qweb.report_general_ledger_qweb_lines">
<div class="act_as_table data_table" style="width: 1140px !important;">
<!-- Display table headers for lines -->
<div class="act_as_thead">
<div class="act_as_row labels">
<!--## date-->
<div class="act_as_cell first_column" style="width: 60px;">Date</div>
<!--## move-->
<div class="act_as_cell" style="width: 100px;">Entry</div>
<!--## journal-->
<div class="act_as_cell" style="width: 40px;">Journal</div>
<!--## account code-->
<div class="act_as_cell" style="width: 50px;">Account</div>
<!--## partner-->
<div class="act_as_cell" style="width: 140px;">Partner</div>
<!--## ref - label-->
<div class="act_as_cell" style="width: 290px;">Ref - Label</div>
<t t-if="show_cost_center">
<!--## cost_center-->
<div class="act_as_cell" style="width: 100px;">Cost center</div>
</t>
<!--## matching_number-->
<div class="act_as_cell" style="width: 25px;">Rec.</div>
<!--## debit-->
<div class="act_as_cell amount" style="width: 75px;">Debit</div>
<!--## credit-->
<div class="act_as_cell amount" style="width: 75px;">Credit</div>
<!--## balance cumulated-->
<div class="act_as_cell amount" style="width: 75px;">Cumul. Bal.</div>
<t t-if="has_second_currency">
<!--## currency_name-->
<div class="act_as_cell" style="width: 35px;">Cur.</div>
<!--## amount_currency-->
<div class="act_as_cell amount" style="width: 75px;">Amount cur.</div>
</t>
</div>
</div>
<!-- Display first line with initial balance -->
<div class="act_as_row lines">
<!--## date-->
<div class="act_as_cell"></div>
<!--## move-->
<div class="act_as_cell"></div>
<!--## journal-->
<div class="act_as_cell"></div>
<!--## account code-->
<div class="act_as_cell"></div>
<!--## partner-->
<div class="act_as_cell"></div>
<!--## ref - label-->
<div class="act_as_cell amount">Initial balance</div>
<t t-if="show_cost_center">
<!--## cost_center-->
<div class="act_as_cell"></div>
</t>
<!--## matching_number-->
<div class="act_as_cell"></div>
<!--## debit-->
<div class="act_as_cell amount"><span t-field="account_or_partner_object.initial_debit"/></div>
<!--## credit-->
<div class="act_as_cell amount"><span t-field="account_or_partner_object.initial_credit"/></div>
<!--## balance cumulated-->
<div class="act_as_cell amount"><span t-field="account_or_partner_object.initial_balance"/></div>
<t t-if="has_second_currency">
<!--## currency_name-->
<div class="act_as_cell"></div>
<!--## amount_currency-->
<div class="act_as_cell"></div>
</t>
</div>
<!-- Display each lines -->
<t t-foreach="account_or_partner_object.move_line_ids" t-as="line">
<!-- # lines or centralized lines -->
<div class="act_as_row lines">
<!--## date-->
<div class="act_as_cell left"><span t-field="line.date"/></div>
<!--## move-->
<div class="act_as_cell left"><span t-field="line.entry"/></div>
<!--## journal-->
<div class="act_as_cell left"><span t-field="line.journal"/></div>
<!--## account code-->
<div class="act_as_cell left"><span t-field="line.account"/></div>
<!--## partner-->
<div class="act_as_cell left"><span t-field="line.partner"/></div>
<!--## ref - label-->
<div class="act_as_cell left"><span t-field="line.label"/></div>
<t t-if="show_cost_center">
<!--## cost_center-->
<div class="act_as_cell left"><span t-field="line.cost_center"/></div>
</t>
<!--## matching_number-->
<div class="act_as_cell"><span t-field="line.matching_number"/></div>
<!--## debit-->
<div class="act_as_cell amount"><span t-field="line.debit"/></div>
<!--## credit-->
<div class="act_as_cell amount"><span t-field="line.credit"/></div>
<!--## balance cumulated-->
<div class="act_as_cell amount"><span t-field="line.cumul_balance"/></div>
<t t-if="has_second_currency">
<!--## currency_name-->
<div class="act_as_cell"><span t-field="line.currency_name"/></div>
<t t-if="line.currency_name">
<!--## amount_currency-->
<div class="act_as_cell amount"><span t-field="line.amount_currency"/></div>
</t>
<t t-if="not line.currency_name">
<!--## amount_currency-->
<div class="act_as_cell"></div>
</t>
</t>
</div>
</t>
</div>
</template>
<template id="account_financial_report_qweb.report_general_ledger_qweb_ending_cumul">
<!-- Display ending balance line for account or partner -->
<div class="act_as_table list_table" style="width: 1141px !important;">
<div class="act_as_row labels" style="font-weight: bold;">
<!--## date-->
<t t-if='type == "account_type"'>
<div class="act_as_cell first_column" style="width: 380px;"><span t-field="account_or_partner_object.code"/> - <span t-field="account_or_partner_object.name"/></div>
<div class="act_as_cell right" style="width: 290px;">Ending balance</div>
</t>
<t t-if='type == "partner_type"'>
<div class="act_as_cell first_column" style="width: 380px;"></div>
<div class="act_as_cell right" style="width: 290px;">Partner ending balance</div>
</t>
<t t-if="show_cost_center">
<!--## cost_center-->
<div class="act_as_cell" style="width: 100px;"></div>
</t>
<!--## matching_number-->
<div class="act_as_cell" style="width: 25px;"></div>
<!--## debit-->
<div class="act_as_cell amount" style="width: 75px;"><span t-field="account_or_partner_object.final_debit"/></div>
<!--## credit-->
<div class="act_as_cell amount" style="width: 75px;"><span t-field="account_or_partner_object.final_credit"/></div>
<!--## balance cumulated-->
<div class="act_as_cell amount" style="width: 75px; padding-right: 1px;"><span t-field="account_or_partner_object.final_balance"/></div>
<t t-if="has_second_currency">
<!--## currency_name + amount_currency-->
<div class="act_as_cell" style="width: 110px;"></div>
</t>
</div>
</div>
</template>
</odoo>

38
account_financial_report_qweb/report/templates/layouts.xml

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="assets_specific" inherit_id="report.assets_common">
<xpath expr="." position="inside">
<link href="/account_financial_report_qweb/static/src/css/report.css" rel="stylesheet"/>
</xpath>
</template>
<template id="account_financial_report_qweb.internal_layout">
<div class="header">
<div class="row">
<div class="col-xs-6">
<span t-esc="title"/>
</div>
<div class="col-xs-6 text-right">
<span t-esc="company_name"/>
</div>
</div>
</div>
<t t-raw="0" />
<div class="footer">
<div class="row">
<div class="col-xs-6 custom_footer">
<span t-esc="context_timestamp(datetime.datetime.now()).strftime('%Y-%m-%d %H:%M')"/>
</div>
<div class="col-xs-6 text-right custom_footer">
<ul class="list-inline">
<li><span class="page"/></li>
<li>/</li>
<li><span class="topage"/></li>
</ul>
</div>
</div>
</div>
</template>
</odoo>

187
account_financial_report_qweb/report/templates/open_items.xml

@ -0,0 +1,187 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="account_financial_report_qweb.report_open_items_qweb">
<t t-call="report.html_container">
<t t-foreach="docs" t-as="o">
<!-- Saved flag fields into variables, used to define columns display -->
<t t-set="has_second_currency" t-value="o.has_second_currency"/>
<t t-call="account_financial_report_qweb.internal_layout">
<!-- Defines global variables used by internal layout -->
<t t-set="title">Open Items</t>
<t t-set="company_name" t-value="o.company_id.name"/>
<div class="page">
<!-- Display filters -->
<t t-call="account_financial_report_qweb.report_open_items_qweb_filters"/>
<t t-foreach="o.account_ids" t-as="account">
<div class="page_break">
<!-- Display account header -->
<div class="act_as_table list_table" style="margin-top: 10px;"/>
<div class="act_as_caption account_title" style="width: 1141px !important;">
<span t-field="account.code"/> - <span t-field="account.name"/>
</div>
<!-- Display account partners -->
<t t-foreach="account.partner_ids" t-as="partner">
<div class="page_break">
<!-- Display partner header -->
<div class="act_as_caption account_title">
<span t-field="partner.name"/>
</div>
<!-- Display partner move lines -->
<t t-call="account_financial_report_qweb.report_open_items_qweb_lines"/>
<!-- Display partner footer -->
<t t-call="account_financial_report_qweb.report_open_items_qweb_ending_cumul">
<t t-set="account_or_partner_object" t-value="partner"/>
<t t-set="type" t-value='"partner_type"'/>
</t>
</div>
</t>
<!-- Display account footer -->
<t t-call="account_financial_report_qweb.report_open_items_qweb_ending_cumul">
<t t-set="account_or_partner_object" t-value="account"/>
<t t-set="type" t-value='"account_type"'/>
</t>
</div>
</t>
</div>
</t>
</t>
</t>
</template>
<template id="account_financial_report_qweb.report_open_items_qweb_filters">
<div class="act_as_table data_table" style="width: 1140px !important;">
<div class="act_as_row labels">
<div class="act_as_cell">Date at filter</div>
<div class="act_as_cell">Target moves filter</div>
<div class="act_as_cell">Account balance at 0 filter</div>
</div>
<div class="act_as_row">
<div class="act_as_cell">
<span t-field="o.date_at"/>
</div>
<div class="act_as_cell">
<t t-if="o.only_posted_moves">All posted entries</t>
<t t-if="not o.only_posted_moves">All entries</t>
</div>
<div class="act_as_cell">
<t t-if="o.hide_account_balance_at_0">Hide</t>
<t t-if="not o.hide_account_balance_at_0">Show</t>
</div>
</div>
</div>
</template>
<template id="account_financial_report_qweb.report_open_items_qweb_lines">
<div class="act_as_table data_table" style="width: 1140px !important;">
<!-- Display table headers for lines -->
<div class="act_as_thead">
<div class="act_as_row labels">
<!--## date-->
<div class="act_as_cell first_column" style="width: 60px;">Date</div>
<!--## move-->
<div class="act_as_cell" style="width: 100px;">Entry</div>
<!--## journal-->
<div class="act_as_cell" style="width: 40px;">Journal</div>
<!--## account code-->
<div class="act_as_cell" style="width: 50px;">Account</div>
<!--## partner-->
<div class="act_as_cell" style="width: 140px;">Partner</div>
<!--## ref - label-->
<div class="act_as_cell" style="width: 290px;">Ref - Label</div>
<!--## date_due-->
<div class="act_as_cell" style="width: 60px;">Due date</div>
<!--## amount_total_due-->
<div class="act_as_cell" style="width: 75px;">Original</div>
<!--## amount_residual-->
<div class="act_as_cell" style="width: 75px;">Residual</div>
<t t-if="has_second_currency">
<!--## currency_name-->
<div class="act_as_cell" style="width: 35px;">Cur.</div>
<!--## amount_total_due_currency-->
<div class="act_as_cell amount" style="width: 75px;">Cur. Original</div>
<!--## amount_residual_currency-->
<div class="act_as_cell amount" style="width: 75px;">Cur. Residual</div>
</t>
</div>
</div>
<!-- Display each lines -->
<t t-foreach="partner.move_line_ids" t-as="line">
<!-- # lines or centralized lines -->
<div class="act_as_row lines">
<!--## date-->
<div class="act_as_cell left"><span t-field="line.date"/></div>
<!--## move-->
<div class="act_as_cell left"><span t-field="line.entry"/></div>
<!--## journal-->
<div class="act_as_cell left"><span t-field="line.journal"/></div>
<!--## account code-->
<div class="act_as_cell left"><span t-field="line.account"/></div>
<!--## partner-->
<div class="act_as_cell left"><span t-field="line.partner"/></div>
<!--## ref - label-->
<div class="act_as_cell left"><span t-field="line.label"/></div>
<!--## date_due-->
<div class="act_as_cell left"><span t-field="line.date_due"/></div>
<!--## amount_total_due-->
<div class="act_as_cell amount"><span t-field="line.amount_total_due"/></div>
<!--## amount_residual-->
<div class="act_as_cell amount"><span t-field="line.amount_residual"/></div>
<t t-if="has_second_currency">
<!--## currency_name-->
<div class="act_as_cell"><span t-field="line.currency_name"/></div>
<t t-if="line.currency_name">
<!--## amount_total_due_currency-->
<div class="act_as_cell amount"><span t-field="line.amount_total_due_currency"/></div>
<!--## amount_residual_currency-->
<div class="act_as_cell amount"><span t-field="line.amount_residual_currency"/></div>
</t>
<t t-if="not line.currency_name">
<!--## amount_total_due_currency-->
<div class="act_as_cell"></div>
<!--## amount_residual_currency-->
<div class="act_as_cell"></div>
</t>
</t>
</div>
</t>
</div>
</template>
<template id="account_financial_report_qweb.report_open_items_qweb_ending_cumul">
<!-- Display ending balance line for account or partner -->
<div class="act_as_table list_table" style="width: 1141px !important;">
<div class="act_as_row labels" style="font-weight: bold;">
<!--## date-->
<t t-if='type == "account_type"'>
<div class="act_as_cell first_column" style="width: 380px;"><span t-field="account_or_partner_object.code"/> - <span t-field="account_or_partner_object.name"/></div>
<div class="act_as_cell right" style="width: 290px;">Ending balance</div>
</t>
<t t-if='type == "partner_type"'>
<div class="act_as_cell first_column" style="width: 380px;"></div>
<div class="act_as_cell right" style="width: 290px;">Partner ending balance</div>
</t>
<!--## date_due-->
<div class="act_as_cell" style="width: 60px;"></div>
<!--## amount_total_due-->
<div class="act_as_cell amount" style="width: 75px;"></div>
<!--## amount_currency-->
<div class="act_as_cell amount" style="width: 75px;"><span t-field="account_or_partner_object.final_amount_residual"/></div>
<t t-if="has_second_currency">
<!--## currency_name + amount_total_due_currency + amount_residual_currency -->
<div class="act_as_cell" style="width: 185px;"></div>
</t>
</div>
</div>
</template>
</odoo>

154
account_financial_report_qweb/report/templates/trial_balance.xml

@ -0,0 +1,154 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="account_financial_report_qweb.report_trial_balance_qweb">
<t t-call="report.html_container">
<t t-foreach="docs" t-as="o">
<!-- Saved flag fields into variables, used to define columns display -->
<t t-set="show_partner_details" t-value="o.show_partner_details"/>
<t t-call="account_financial_report_qweb.internal_layout">
<!-- Defines global variables used by internal layout -->
<t t-set="title">Trial Balance</t>
<t t-set="company_name" t-value="o.company_id.name"/>
<div class="page">
<!-- Display filters -->
<t t-call="account_financial_report_qweb.report_trial_balance_qweb_filters"/>
<div class="act_as_table list_table" style="margin-top: 10px;"/>
<!-- Display account lines -->
<t t-if="not show_partner_details">
<div class="act_as_table data_table" style="width: 1140px !important;">
<!-- Display account header -->
<t t-call="account_financial_report_qweb.report_trial_balance_qweb_lines_header"/>
<!-- Display each lines -->
<t t-foreach="o.account_ids" t-as="line">
<!-- Display account lines -->
<t t-call="account_financial_report_qweb.report_trial_balance_qweb_line"/>
</t>
</div>
</t>
<!-- Display partner lines -->
<t t-if="show_partner_details">
<t t-foreach="o.account_ids" t-as="account">
<div class="page_break">
<!-- Display account header -->
<div class="act_as_table list_table" style="margin-top: 10px;"/>
<div class="act_as_caption account_title" style="width: 1141px !important;">
<span t-field="account.code"/> - <span t-field="account.name"/>
</div>
<div class="act_as_table data_table" style="width: 1140px !important;">
<!-- Display account/partner header -->
<t t-call="account_financial_report_qweb.report_trial_balance_qweb_lines_header"/>
<!-- Display each partners -->
<t t-foreach="account.partner_ids" t-as="line">
<!-- Display partner line -->
<t t-call="account_financial_report_qweb.report_trial_balance_qweb_line"/>
</t>
</div>
<!-- Display account footer -->
<t t-call="account_financial_report_qweb.report_trial_balance_qweb_account_footer"/>
</div>
</t>
</t>
</div>
</t>
</t>
</t>
</template>
<template id="account_financial_report_qweb.report_trial_balance_qweb_filters">
<div class="act_as_table data_table" style="width: 1140px !important;">
<div class="act_as_row labels">
<div class="act_as_cell">Date range filter</div>
<div class="act_as_cell">Target moves filter</div>
<div class="act_as_cell">Account balance at 0 filter</div>
</div>
<div class="act_as_row">
<div class="act_as_cell">
From: <span t-field="o.date_from"/> To: <span t-field="o.date_to"/>
</div>
<div class="act_as_cell">
<t t-if="o.only_posted_moves">All posted entries</t>
<t t-if="not o.only_posted_moves">All entries</t>
</div>
<div class="act_as_cell">
<t t-if="o.hide_account_balance_at_0">Hide</t>
<t t-if="not o.hide_account_balance_at_0">Show</t>
</div>
</div>
</div>
</template>
<template id="account_financial_report_qweb.report_trial_balance_qweb_lines_header">
<!-- Display table headers for lines -->
<div class="act_as_thead">
<div class="act_as_row labels">
<t t-if="not show_partner_details">
<!--## Code-->
<div class="act_as_cell" style="width: 100px;">Code</div>
<!--## Account-->
<div class="act_as_cell" style="width: 600px;">Account</div>
</t>
<t t-if="show_partner_details">
<!--## Partner-->
/<div class="act_as_cell" style="width: 700px;">Partner</div>
</t>
<!--## Initial balance-->
<div class="act_as_cell" style="width: 110px;">Initial balance</div>
<!--## Debit-->
<div class="act_as_cell" style="width: 110px;">Debit</div>
<!--## Credit-->
<div class="act_as_cell" style="width: 110px;">Credit</div>
<!--## Ending balance-->
<div class="act_as_cell" style="width: 110px;">Ending balance</div>
</div>
</div>
</template>
<template id="account_financial_report_qweb.report_trial_balance_qweb_line">
<!-- # line -->
<div class="act_as_row lines">
<t t-if="not show_partner_details">
<!--## Code-->
<div class="act_as_cell left"><span t-field="line.code"/></div>
</t>
<!--## Account/Partner-->
<div class="act_as_cell left"><span t-field="line.name"/></div>
<!--## Initial balance-->
<div class="act_as_cell amount"><span t-field="line.initial_balance"/></div>
<!--## Debit-->
<div class="act_as_cell amount"><span t-field="line.debit"/></div>
<!--## Credit-->
<div class="act_as_cell amount"><span t-field="line.credit"/></div>
<!--## Ending balance-->
<div class="act_as_cell amount"><span t-field="line.final_balance"/></div>
</div>
</template>
<template id="account_financial_report_qweb.report_trial_balance_qweb_account_footer">
<!-- Display account footer -->
<div class="act_as_table list_table" style="width: 1141px !important;">
<div class="act_as_row labels" style="font-weight: bold;">
<!--## Account-->
<div class="act_as_cell left" style="width: 700px;"><span t-field="account.code"/> - <span t-field="account.name"/></div>
<!--## Initial balance-->
<div class="act_as_cell amount" style="width: 110px;"><span t-field="account.initial_balance"/></div>
<!--## Debit-->
<div class="act_as_cell amount" style="width: 110px;"><span t-field="account.debit"/></div>
<!--## Credit-->
<div class="act_as_cell amount" style="width: 110px;"><span t-field="account.credit"/></div>
<!--## Ending balance-->
<div class="act_as_cell amount" style="width: 110px;"><span t-field="account.final_balance"/></div>
</div>
</div>
</template>
</odoo>

249
account_financial_report_qweb/report/trial_balance.py

@ -0,0 +1,249 @@
# -*- coding: utf-8 -*-
# © 2016 Julien Coux (Camptocamp)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, fields, api
class TrialBalanceReport(models.TransientModel):
""" Here, we just define class fields.
For methods, go more bottom at this file.
The class hierarchy is :
* TrialBalanceReport
** TrialBalanceReportAccount
*** TrialBalanceReportPartner
If "show_partner_details" is selected
"""
_name = 'report_trial_balance_qweb'
# Filters fields, used for data computation
date_from = fields.Date()
date_to = fields.Date()
fy_start_date = fields.Date()
only_posted_moves = fields.Boolean()
hide_account_balance_at_0 = fields.Boolean()
company_id = fields.Many2one(comodel_name='res.company')
filter_account_ids = fields.Many2many(comodel_name='account.account')
filter_partner_ids = fields.Many2many(comodel_name='res.partner')
show_partner_details = fields.Boolean()
# General Ledger Report Data fields,
# used as base for compute the data reports
general_ledger_id = fields.Many2one(
comodel_name='report_general_ledger_qweb'
)
# Data fields, used to browse report data
account_ids = fields.One2many(
comodel_name='report_trial_balance_qweb_account',
inverse_name='report_id'
)
class TrialBalanceReportAccount(models.TransientModel):
_name = 'report_trial_balance_qweb_account'
_order = 'code ASC'
report_id = fields.Many2one(
comodel_name='report_trial_balance_qweb',
ondelete='cascade',
index=True
)
# Data fields, used to keep link with real object
account_id = fields.Many2one(
'account.account',
index=True
)
# Data fields, used for report display
code = fields.Char()
name = fields.Char()
initial_balance = fields.Float(digits=(16, 2))
debit = fields.Float(digits=(16, 2))
credit = fields.Float(digits=(16, 2))
final_balance = fields.Float(digits=(16, 2))
# Data fields, used to browse report data
partner_ids = fields.One2many(
comodel_name='report_trial_balance_qweb_partner',
inverse_name='report_account_id'
)
class TrialBalanceReportPartner(models.TransientModel):
_name = 'report_trial_balance_qweb_partner'
report_account_id = fields.Many2one(
comodel_name='report_trial_balance_qweb_account',
ondelete='cascade',
index=True
)
# Data fields, used to keep link with real object
partner_id = fields.Many2one(
'res.partner',
index=True
)
# Data fields, used for report display
name = fields.Char()
initial_balance = fields.Float(digits=(16, 2))
debit = fields.Float(digits=(16, 2))
credit = fields.Float(digits=(16, 2))
final_balance = fields.Float(digits=(16, 2))
@api.model
def _generate_order_by(self, order_spec, query):
"""Custom order to display "No partner allocated" at last position."""
return """
ORDER BY
CASE
WHEN "report_trial_balance_qweb_partner"."partner_id" IS NOT NULL
THEN 0
ELSE 1
END,
"report_trial_balance_qweb_partner"."name"
"""
class TrialBalanceReportCompute(models.TransientModel):
""" Here, we just define methods.
For class fields, go more top at this file.
"""
_inherit = 'report_trial_balance_qweb'
@api.multi
def print_report(self, xlsx_report=False):
self.ensure_one()
self.compute_data_for_report()
if xlsx_report:
report_name = 'account_financial_report_qweb.' \
'report_trial_balance_xlsx'
else:
report_name = 'account_financial_report_qweb.' \
'report_trial_balance_qweb'
return self.env['report'].get_action(records=self,
report_name=report_name)
def _prepare_report_general_ledger(self):
self.ensure_one()
return {
'date_from': self.date_from,
'date_to': self.date_to,
'only_posted_moves': self.only_posted_moves,
'hide_account_balance_at_0': self.hide_account_balance_at_0,
'company_id': self.company_id.id,
'filter_account_ids': [(6, 0, self.filter_account_ids.ids)],
'filter_partner_ids': [(6, 0, self.filter_partner_ids.ids)],
'fy_start_date': self.fy_start_date,
}
@api.multi
def compute_data_for_report(self):
self.ensure_one()
# Compute General Ledger Report Data.
# The data of Trial Balance Report
# are based on General Ledger Report data.
model = self.env['report_general_ledger_qweb']
self.general_ledger_id = model.create(
self._prepare_report_general_ledger()
)
self.general_ledger_id.compute_data_for_report()
# Compute report data
self._inject_account_values()
if self.show_partner_details:
self._inject_partner_values()
# Refresh cache because all data are computed with SQL requests
self.refresh()
def _inject_account_values(self):
"""Inject report values for report_trial_balance_qweb_account"""
query_inject_account = """
INSERT INTO
report_trial_balance_qweb_account
(
report_id,
create_uid,
create_date,
account_id,
code,
name,
initial_balance,
debit,
credit,
final_balance
)
SELECT
%s AS report_id,
%s AS create_uid,
NOW() AS create_date,
rag.account_id,
rag.code,
rag.name,
rag.initial_balance AS initial_balance,
rag.final_debit - rag.initial_debit AS debit,
rag.final_credit - rag.initial_credit AS credit,
rag.final_balance AS final_balance
FROM
report_general_ledger_qweb_account rag
WHERE
rag.report_id = %s
"""
query_inject_account_params = (
self.id,
self.env.uid,
self.general_ledger_id.id,
)
self.env.cr.execute(query_inject_account, query_inject_account_params)
def _inject_partner_values(self):
"""Inject report values for report_trial_balance_qweb_partner"""
query_inject_partner = """
INSERT INTO
report_trial_balance_qweb_partner
(
report_account_id,
create_uid,
create_date,
partner_id,
name,
initial_balance,
debit,
credit,
final_balance
)
SELECT
ra.id AS report_account_id,
%s AS create_uid,
NOW() AS create_date,
rpg.partner_id,
rpg.name,
rpg.initial_balance AS initial_balance,
rpg.final_debit - rpg.initial_debit AS debit,
rpg.final_credit - rpg.initial_credit AS credit,
rpg.final_balance AS final_balance
FROM
report_general_ledger_qweb_partner rpg
INNER JOIN
report_general_ledger_qweb_account rag ON rpg.report_account_id = rag.id
INNER JOIN
report_trial_balance_qweb_account ra ON rag.code = ra.code
WHERE
rag.report_id = %s
AND ra.report_id = %s
"""
query_inject_partner_params = (
self.env.uid,
self.general_ledger_id.id,
self.id,
)
self.env.cr.execute(query_inject_partner, query_inject_partner_params)

133
account_financial_report_qweb/report/trial_balance_xlsx.py

@ -0,0 +1,133 @@
# -*- 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 TrialBalanceXslx(abstract_report_xlsx.AbstractReportXslx):
def __init__(self, name, table, rml=False, parser=False, header=True,
store=False):
super(TrialBalanceXslx, self).__init__(
name, table, rml, parser, header, store)
def _get_report_name(self):
return _('Trial Balance')
def _get_report_columns(self, report):
if not report.show_partner_details:
return {
0: {'header': _('Code'), 'field': 'code', 'width': 10},
1: {'header': _('Account'), 'field': 'name', 'width': 60},
2: {'header': _('Initial balance'),
'field': 'initial_balance',
'type': 'amount',
'width': 14},
3: {'header': _('Debit'),
'field': 'debit',
'type': 'amount',
'width': 14},
4: {'header': _('Credit'),
'field': 'credit',
'type': 'amount',
'width': 14},
5: {'header': _('Ending balance'),
'field': 'final_balance',
'type': 'amount',
'width': 14},
}
else:
return {
0: {'header': _('Partner'), 'field': 'name', 'width': 70},
1: {'header': _('Initial balance'),
'field': 'initial_balance',
'type': 'amount',
'width': 14},
2: {'header': _('Debit'),
'field': 'debit',
'type': 'amount',
'width': 14},
3: {'header': _('Credit'),
'field': 'credit',
'type': 'amount',
'width': 14},
4: {'header': _('Ending balance'),
'field': 'final_balance',
'type': 'amount',
'width': 14},
}
def _get_report_filters(self, report):
return [
[_('Date range filter'),
_('From: %s To: %s') % (report.date_from, report.date_to)],
[_('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 3
def _generate_report_content(self, workbook, report):
if not report.show_partner_details:
# Display array header for account lines
self.write_array_header()
# For each account
for account in report.account_ids:
if not report.show_partner_details:
# Display account lines
self.write_line(account)
else:
# Write account title
self.write_array_title(account.code + ' - ' + account.name)
# Display array header for partner lines
self.write_array_header()
# For each partner
for partner in account.partner_ids:
# Display partner lines
self.write_line(partner)
# Display account footer line
self.write_account_footer(account,
account.code + ' - ' + account.name)
# Line break
self.row_pos += 2
def write_account_footer(self, account, name_value):
"""Specific function to write account footer for Trial Balance"""
for col_pos, column in self.columns.iteritems():
if column['field'] == 'name':
value = name_value
else:
value = getattr(account, column['field'])
cell_type = column.get('type', 'string')
if cell_type == 'string':
self.sheet.write_string(self.row_pos, col_pos, value or '',
self.format_header_left)
elif cell_type == 'amount':
self.sheet.write_number(self.row_pos, col_pos, float(value),
self.format_header_amount)
self.row_pos += 1
TrialBalanceXslx(
'report.account_financial_report_qweb.report_trial_balance_xlsx',
'report_trial_balance_qweb',
parser=report_sxw.rml_parse
)

114
account_financial_report_qweb/reports.xml

@ -0,0 +1,114 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- PDF REPORTS -->
<report
id="action_report_general_ledger_qweb"
model="report_general_ledger_qweb"
string="General Ledger"
report_type="qweb-pdf"
name="account_financial_report_qweb.report_general_ledger_qweb"
file="account_financial_report_qweb.report_general_ledger_qweb"
/>
<report
id="action_report_trial_balance_qweb"
model="report_trial_balance_qweb"
string="Trial Balance"
report_type="qweb-pdf"
name="account_financial_report_qweb.report_trial_balance_qweb"
file="account_financial_report_qweb.report_trial_balance_qweb"
/>
<report
id="action_report_open_items_qweb"
model="report_open_items_qweb"
string="Open Items"
report_type="qweb-pdf"
name="account_financial_report_qweb.report_open_items_qweb"
file="account_financial_report_qweb.report_open_items_qweb"
/>
<report
id="action_report_aged_partner_balance_qweb"
model="report_aged_partner_balance_qweb"
string="Aged Partner Balance"
report_type="qweb-pdf"
name="account_financial_report_qweb.report_aged_partner_balance_qweb"
file="account_financial_report_qweb.report_aged_partner_balance_qweb"
/>
<!-- PDF REPORTS : paperformat -->
<record id="report_qweb_paperformat" model="report.paperformat">
<field name="name">Account financial report qweb paperformat</field>
<field name="default" eval="True"/>
<field name="format">custom</field>
<field name="page_height">297</field>
<field name="page_width">210</field>
<field name="orientation">Portrait</field>
<field name="margin_top">12</field>
<field name="margin_bottom">8</field>
<field name="margin_left">5</field>
<field name="margin_right">5</field>
<field name="header_line" eval="False"/>
<field name="header_spacing">10</field>
<field name="dpi">110</field>
</record>
<record id="action_report_general_ledger_qweb" model="ir.actions.report.xml">
<field name="paperformat_id" ref="report_qweb_paperformat"/>
</record>
<record id="action_report_trial_balance_qweb" model="ir.actions.report.xml">
<field name="paperformat_id" ref="report_qweb_paperformat"/>
</record>
<record id="action_report_open_items_qweb" model="ir.actions.report.xml">
<field name="paperformat_id" ref="report_qweb_paperformat"/>
</record>
<record id="action_report_aged_partner_balance_qweb" model="ir.actions.report.xml">
<field name="paperformat_id" ref="report_qweb_paperformat"/>
</record>
<!-- XLSX REPORTS -->
<record id="action_report_general_ledger_xlsx" model="ir.actions.report.xml">
<field name="name">General Ledger XLSX</field>
<field name="model">report_general_ledger_qweb</field>
<field name="type">ir.actions.report.xml</field>
<field name="report_name">account_financial_report_qweb.report_general_ledger_xlsx</field>
<field name="report_type">xlsx</field>
<field name="auto" eval="False"/>
</record>
<record id="action_report_trial_balance_xlsx" model="ir.actions.report.xml">
<field name="name">Trial Balance XLSX</field>
<field name="model">report_trial_balance_qweb</field>
<field name="type">ir.actions.report.xml</field>
<field name="report_name">account_financial_report_qweb.report_trial_balance_xlsx</field>
<field name="report_type">xlsx</field>
<field name="auto" eval="False"/>
</record>
<record id="action_report_open_items_xlsx" model="ir.actions.report.xml">
<field name="name">Open Items XLSX</field>
<field name="model">report_open_items_qweb</field>
<field name="type">ir.actions.report.xml</field>
<field name="report_name">account_financial_report_qweb.report_open_items_xlsx</field>
<field name="report_type">xlsx</field>
<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>

BIN
account_financial_report_qweb/static/description/icon.png

After

Width: 256  |  Height: 256  |  Size: 15 KiB

95
account_financial_report_qweb/static/src/css/report.css

@ -0,0 +1,95 @@
body, table, td, span, div {
font-family: Helvetica, Arial;
}
.act_as_table {
display: table !important;
}
.act_as_row {
display: table-row !important;
page-break-inside: avoid;
}
.act_as_cell {
display: table-cell !important;
page-break-inside: avoid;
}
.act_as_thead {
display: table-header-group !important;
}
.act_as_tbody {
display: table-row-group !important;
}
.list_table, .data_table, .totals_table{
width: 100% !important;
table-layout: fixed !important;
}
.act_as_row.labels {
background-color:#F0F0F0 !important;
}
.list_table, .data_table, .totals_table, .list_table .act_as_row {
border-left:0px;
border-right:0px;
text-align:left;
font-size:10px;
padding-right:3px;
padding-left:3px;
padding-top:2px;
padding-bottom:2px;
border-collapse:collapse;
}
.totals_table {
font-weight: bold;
text-align: center;
}
.list_table .act_as_row.labels, .list_table .act_as_row.initial_balance, .list_table .act_as_row.lines {
border-color:grey !important;
border-bottom:1px solid lightGrey !important;
}
.data_table .act_as_cell{
border: 1px solid lightGrey;
text-align: center;
}
.data_table .act_as_cell, .list_table .act_as_cell, .totals_table .act_as_cell {
word-wrap: break-word;
}
.data_table .act_as_row.labels, .totals_table .act_as_row.labels {
font-weight: bold;
}
.initial_balance .act_as_cell {
font-style:italic;
}
.account_title {
font-size:11px;
font-weight:bold;
}
.account_title.labels {
background-color:#F0F0F0 !important;
}
.act_as_cell.amount {
word-wrap:normal;
text-align:right;
}
.act_as_cell.left {
text-align:left;
}
.act_as_cell.right {
text-align:right;
}
.list_table .act_as_cell{
padding-left: 5px;
/* border-right:1px solid lightGrey; uncomment to active column lines */
}
.list_table .act_as_cell.first_column {
padding-left: 0px;
/* border-left:1px solid lightGrey; uncomment to active column lines */
}
.overflow_ellipsis {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.custom_footer {
font-size:7px !important;
}
.page_break {
page-break-inside: avoid;
}

9
account_financial_report_qweb/tests/__init__.py

@ -0,0 +1,9 @@
# -*- coding: utf-8 -*-
# © 2016 Julien Coux (Camptocamp)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).-
from . import abstract_test
from . import test_aged_partner_balance
from . import test_general_ledger
from . import test_open_items
from . import test_trial_balance

204
account_financial_report_qweb/tests/abstract_test.py

@ -0,0 +1,204 @@
# -*- 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.tests.common import TransactionCase
class AbstractTest(TransactionCase):
"""Common technical tests for all reports."""
def setUp(cls):
super(AbstractTest, cls).setUp()
cls.model = cls._getReportModel()
cls.qweb_report_name = cls._getQwebReportName()
cls.xlsx_report_name = cls._getXlsxReportName()
cls.xlsx_action_name = cls._getXlsxReportActionName()
cls.report_title = cls._getReportTitle()
cls.base_filters = cls._getBaseFilters()
cls.additional_filters = cls._getAdditionalFiltersToBeTested()
cls.report = cls.model.create(cls.base_filters)
def test_01_generation_report_qweb(self):
"""Check if report PDF/HTML is correctly generated"""
# Check if returned report action is correct
report_action = self.report.print_report()
self.assertDictContainsSubset(
{
'type': 'ir.actions.report.xml',
'report_name': self.qweb_report_name,
'report_type': 'qweb-pdf',
},
report_action
)
# Check if report template is correct
report_html = self.env['report'].get_html(
self.report, self.qweb_report_name
)
self.assertTrue(self.report_title.encode('utf8') in report_html)
self.assertTrue(
self.report.account_ids[0].name.encode('utf8') in report_html
)
def test_02_generation_report_xlsx(self):
"""Check if report XLSX is correctly generated"""
# 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': self.xlsx_report_name,
'report_type': 'xlsx',
},
report_action
)
# Check if report template is correct
report_xlsx = self.env.ref(self.xlsx_action_name).render_report(
self.report.ids,
self.xlsx_report_name,
{'report_type': 'xlsx'}
)
self.assertGreaterEqual(len(report_xlsx[0]), 1)
self.assertEqual(report_xlsx[1], 'xlsx')
def test_03_compute_data(self):
"""Check that the SQL queries work with all filters options"""
for filters in [{}] + self.additional_filters:
current_filter = self.base_filters.copy()
current_filter.update(filters)
report = self.model.create(current_filter)
report.compute_data_for_report()
self.assertGreaterEqual(len(report.account_ids), 1)
# Same filters with only one account
current_filter = self.base_filters.copy()
current_filter.update(filters)
current_filter.update({
'filter_account_ids':
[(6, 0, report.account_ids[0].account_id.ids)],
})
report2 = self.model.create(current_filter)
report2.compute_data_for_report()
self.assertEqual(len(report2.account_ids), 1)
self.assertEqual(report2.account_ids.name,
report.account_ids[0].name)
if self._partner_test_is_possible(filters):
# Same filters with only one partner
report_partner_ids = report.account_ids.mapped('partner_ids')
partner_ids = report_partner_ids.mapped('partner_id')
current_filter = self.base_filters.copy()
current_filter.update(filters)
current_filter.update({
'filter_partner_ids': [(6, 0, partner_ids[0].ids)],
})
report3 = self.model.create(current_filter)
report3.compute_data_for_report()
self.assertGreaterEqual(len(report3.account_ids), 1)
report_partner_ids3 = report3.account_ids.mapped('partner_ids')
partner_ids3 = report_partner_ids3.mapped('partner_id')
self.assertEqual(len(partner_ids3), 1)
self.assertEqual(
partner_ids3.name,
partner_ids[0].name
)
# Same filters with only one partner and one account
report_partner_ids = report3.account_ids.mapped('partner_ids')
report_account_id = report_partner_ids.filtered(
lambda p: p.partner_id
)[0].report_account_id
current_filter = self.base_filters.copy()
current_filter.update(filters)
current_filter.update({
'filter_account_ids':
[(6, 0, report_account_id.account_id.ids)],
'filter_partner_ids': [(6, 0, partner_ids[0].ids)],
})
report4 = self.model.create(current_filter)
report4.compute_data_for_report()
self.assertEqual(len(report4.account_ids), 1)
self.assertEqual(report4.account_ids.name,
report_account_id.account_id.name)
report_partner_ids4 = report4.account_ids.mapped('partner_ids')
partner_ids4 = report_partner_ids4.mapped('partner_id')
self.assertEqual(len(partner_ids4), 1)
self.assertEqual(
partner_ids4.name,
partner_ids[0].name
)
def _partner_test_is_possible(self, filters):
"""
:return:
a boolean to indicate if partner test is possible
with current filters
"""
return True
def _getReportModel(self):
"""
:return: the report model name
"""
raise NotImplementedError()
def _getQwebReportName(self):
"""
:return: the qweb report name
"""
raise NotImplementedError()
def _getXlsxReportName(self):
"""
:return: the xlsx report name
"""
raise NotImplementedError()
def _getXlsxReportActionName(self):
"""
:return: the xlsx report action name
"""
raise NotImplementedError()
def _getReportTitle(self):
"""
:return: the report title displayed into the report
"""
raise NotImplementedError()
def _getBaseFilters(self):
"""
:return: the minimum required filters to generate report
"""
raise NotImplementedError()
def _getAdditionalFiltersToBeTested(self):
"""
:return: the additional filters to generate report variants
"""
raise NotImplementedError()

42
account_financial_report_qweb/tests/test_aged_partner_balance.py

@ -0,0 +1,42 @@
# -*- coding: utf-8 -*-
# Author: Julien Coux
# Copyright 2016 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import time
from . import abstract_test
class TestAgedPartnerBalance(abstract_test.AbstractTest):
"""
Technical tests for Aged Partner Balance Report.
"""
def _getReportModel(self):
return self.env['report_aged_partner_balance_qweb']
def _getQwebReportName(self):
return 'account_financial_report_qweb.report_aged_partner_balance_qweb'
def _getXlsxReportName(self):
return 'account_financial_report_qweb.report_aged_partner_balance_xlsx'
def _getXlsxReportActionName(self):
return 'account_financial_report_qweb.' \
'action_report_aged_partner_balance_xlsx'
def _getReportTitle(self):
return 'Aged Partner Balance'
def _getBaseFilters(self):
return {
'date_at': time.strftime('%Y-12-31'),
'company_id': self.env.ref('base.main_company').id,
}
def _getAdditionalFiltersToBeTested(self):
return [
{'only_posted_moves': True},
{'show_move_line_details': True},
{'only_posted_moves': True, 'show_move_line_details': True},
]

52
account_financial_report_qweb/tests/test_general_ledger.py

@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
# Author: Julien Coux
# Copyright 2016 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import time
from . import abstract_test
class TestGeneralLedger(abstract_test.AbstractTest):
"""
Technical tests for General Ledger Report.
"""
def _getReportModel(self):
return self.env['report_general_ledger_qweb']
def _getQwebReportName(self):
return 'account_financial_report_qweb.report_general_ledger_qweb'
def _getXlsxReportName(self):
return 'account_financial_report_qweb.report_general_ledger_xlsx'
def _getXlsxReportActionName(self):
return 'account_financial_report_qweb.' \
'action_report_general_ledger_xlsx'
def _getReportTitle(self):
return 'General Ledger'
def _getBaseFilters(self):
return {
'date_from': time.strftime('%Y-01-01'),
'date_to': time.strftime('%Y-12-31'),
'company_id': self.env.ref('base.main_company').id,
'fy_start_date': time.strftime('%Y-01-01'),
}
def _getAdditionalFiltersToBeTested(self):
return [
{'only_posted_moves': True},
{'hide_account_balance_at_0': True},
{'centralize': True},
{'only_posted_moves': True, 'hide_account_balance_at_0': True},
{'only_posted_moves': True, 'centralize': True},
{'hide_account_balance_at_0': True, 'centralize': True},
{
'only_posted_moves': True,
'hide_account_balance_at_0': True,
'centralize': True
},
]

41
account_financial_report_qweb/tests/test_open_items.py

@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-
# Author: Julien Coux
# Copyright 2016 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import time
from . import abstract_test
class TestOpenItems(abstract_test.AbstractTest):
"""
Technical tests for Open Items Report.
"""
def _getReportModel(self):
return self.env['report_open_items_qweb']
def _getQwebReportName(self):
return 'account_financial_report_qweb.report_open_items_qweb'
def _getXlsxReportName(self):
return 'account_financial_report_qweb.report_open_items_xlsx'
def _getXlsxReportActionName(self):
return 'account_financial_report_qweb.action_report_open_items_xlsx'
def _getReportTitle(self):
return 'Open Items'
def _getBaseFilters(self):
return {
'date_at': time.strftime('%Y-12-31'),
'company_id': self.env.ref('base.main_company').id,
}
def _getAdditionalFiltersToBeTested(self):
return [
{'only_posted_moves': True},
{'hide_account_balance_at_0': True},
{'only_posted_moves': True, 'hide_account_balance_at_0': True},
]

54
account_financial_report_qweb/tests/test_trial_balance.py

@ -0,0 +1,54 @@
# -*- coding: utf-8 -*-
# Author: Julien Coux
# Copyright 2016 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import time
from . import abstract_test
class TestTrialBalance(abstract_test.AbstractTest):
"""
Technical tests for Trial Balance Report.
"""
def _getReportModel(self):
return self.env['report_trial_balance_qweb']
def _getQwebReportName(self):
return 'account_financial_report_qweb.report_trial_balance_qweb'
def _getXlsxReportName(self):
return 'account_financial_report_qweb.report_trial_balance_xlsx'
def _getXlsxReportActionName(self):
return 'account_financial_report_qweb.action_report_trial_balance_xlsx'
def _getReportTitle(self):
return 'Trial Balance'
def _getBaseFilters(self):
return {
'date_from': time.strftime('%Y-01-01'),
'date_to': time.strftime('%Y-12-31'),
'company_id': self.env.ref('base.main_company').id,
'fy_start_date': time.strftime('%Y-01-01'),
}
def _getAdditionalFiltersToBeTested(self):
return [
{'only_posted_moves': True},
{'hide_account_balance_at_0': True},
{'show_partner_details': True},
{'only_posted_moves': True, 'hide_account_balance_at_0': True},
{'only_posted_moves': True, 'show_partner_details': True},
{'hide_account_balance_at_0': True, 'show_partner_details': True},
{
'only_posted_moves': True,
'hide_account_balance_at_0': True,
'show_partner_details': True
},
]
def _partner_test_is_possible(self, filters):
return 'show_partner_details' in filters

14
account_financial_report_qweb/view/account_view.xml

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record model="ir.ui.view" id="view_account_specific_form">
<field name="name">account.account.form.inherit</field>
<field name="inherit_id" ref="account.view_account_form"/>
<field name="model">account.account</field>
<field name="type">form</field>
<field name="arch" type="xml">
<field name="deprecated" position="after">
<field name="centralized"/>
</field>
</field>
</record>
</odoo>

9
account_financial_report_qweb/wizard/__init__.py

@ -0,0 +1,9 @@
# -*- 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 . import aged_partner_balance_wizard
from . import general_ledger_wizard
from . import open_items_wizard
from . import trial_balance_wizard

81
account_financial_report_qweb/wizard/aged_partner_balance_wizard.py

@ -0,0 +1,81 @@
# -*- coding: utf-8 -*-
# Author: Damien Crier, Andrea Stirpe, Kevin Graveman, Dennis Sluijk
# Author: Julien Coux
# Copyright 2016 Camptocamp SA, Onestein B.V.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from datetime import datetime
from openerp import api, fields, models
class AgedPartnerBalance(models.TransientModel):
"""Aged partner balance report wizard."""
_name = 'aged.partner.balance.wizard'
_description = 'Aged Partner Balance Wizard'
company_id = fields.Many2one(
comodel_name='res.company',
default=lambda self: self.env.user.company_id,
string='Company'
)
date_at = fields.Date(required=True,
default=fields.Date.to_string(datetime.today()))
target_move = fields.Selection([('posted', 'All Posted Entries'),
('all', 'All Entries')],
string='Target Moves',
required=True,
default='all')
account_ids = fields.Many2many(
comodel_name='account.account',
string='Filter accounts',
)
receivable_accounts_only = fields.Boolean()
payable_accounts_only = fields.Boolean()
partner_ids = fields.Many2many(
comodel_name='res.partner',
string='Filter partners',
)
show_move_line_details = fields.Boolean()
@api.onchange('receivable_accounts_only', 'payable_accounts_only')
def onchange_type_accounts_only(self):
"""Handle receivable/payable accounts only change."""
if self.receivable_accounts_only or self.payable_accounts_only:
domain = []
if self.receivable_accounts_only and self.payable_accounts_only:
domain += [('internal_type', 'in', ('receivable', 'payable'))]
elif self.receivable_accounts_only:
domain += [('internal_type', '=', 'receivable')]
elif self.payable_accounts_only:
domain += [('internal_type', '=', 'payable')]
self.account_ids = self.env['account.account'].search(domain)
else:
self.account_ids = None
@api.multi
def button_export_pdf(self):
self.ensure_one()
return self._export()
@api.multi
def button_export_xlsx(self):
self.ensure_one()
return self._export(xlsx_report=True)
def _prepare_report_aged_partner_balance(self):
self.ensure_one()
return {
'date_at': self.date_at,
'only_posted_moves': self.target_move == 'posted',
'company_id': self.company_id.id,
'filter_account_ids': [(6, 0, self.account_ids.ids)],
'filter_partner_ids': [(6, 0, self.partner_ids.ids)],
'show_move_line_details': self.show_move_line_details,
}
def _export(self, xlsx_report=False):
"""Default export is PDF."""
model = self.env['report_aged_partner_balance_qweb']
report = model.create(self._prepare_report_aged_partner_balance())
return report.print_report(xlsx_report)

50
account_financial_report_qweb/wizard/aged_partner_balance_wizard_view.xml

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- AGED PARTNER BALANCE -->
<record id="aged_partner_balance_wizard" model="ir.ui.view">
<field name="name">Aged Partner Balance</field>
<field name="model">aged.partner.balance.wizard</field>
<field name="arch" type="xml">
<form>
<group name="main_info">
<field name="company_id" options="{'no_create': True}" groups="base.group_multi_company"/>
</group>
<group name="filters">
<group name="date_range">
<field name="date_at"/>
</group>
<group name="other_filters">
<field name="target_move" widget="radio"/>
<field name="show_move_line_details"/>
</group>
</group>
<label for="partner_ids"/>
<field name="partner_ids" nolabel="1" options="{'no_create': True}"/>
<group/>
<label for="account_ids"/>
<group col="4">
<field name="receivable_accounts_only"/>
<field name="payable_accounts_only"/>
</group>
<field name="account_ids" nolabel="1" options="{'no_create': True}"/>
<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>
</field>
</record>
<act_window id="action_aged_partner_balance_wizard"
name="Aged Partner Balance"
res_model="aged.partner.balance.wizard"
view_type="form"
view_mode="form"
view_id="aged_partner_balance_wizard"
target="new" />
</odoo>

136
account_financial_report_qweb/wizard/general_ledger_wizard.py

@ -0,0 +1,136 @@
# -*- 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 import models, fields, api
class GeneralLedgerReportWizard(models.TransientModel):
"""General ledger report wizard."""
_name = "general.ledger.report.wizard"
_description = "General Ledger Report Wizard"
company_id = fields.Many2one(
comodel_name='res.company',
default=lambda self: self.env.user.company_id,
string='Company'
)
date_range_id = fields.Many2one(
comodel_name='date.range',
required=True,
string='Date range'
)
date_from = fields.Date(required=True)
date_to = fields.Date(required=True)
fy_start_date = fields.Date(required=True)
target_move = fields.Selection([('posted', 'All Posted Entries'),
('all', 'All Entries')],
string='Target Moves',
required=True,
default='all')
account_ids = fields.Many2many(
comodel_name='account.account',
string='Filter accounts',
)
centralize = fields.Boolean(string='Activate centralization',
default=True)
hide_account_balance_at_0 = fields.Boolean(
string='Hide account ending balance at 0',
help='Use this filter to hide an account or a partner '
'with an ending balance at 0. '
'If partners are filtered, '
'debits and credits totals will not match the trial balance.'
)
receivable_accounts_only = fields.Boolean()
payable_accounts_only = fields.Boolean()
partner_ids = fields.Many2many(
comodel_name='res.partner',
string='Filter partners',
)
cost_center_ids = fields.Many2many(
comodel_name='account.analytic.account',
string='Filter cost centers',
)
not_only_one_unaffected_earnings_account = fields.Boolean(
readonly=True,
string='Not only one unaffected earnings account'
)
@api.onchange('company_id')
def onchange_company_id(self):
"""Handle company change."""
account_type = self.env.ref('account.data_unaffected_earnings')
count = self.env['account.account'].search_count(
[
('user_type_id', '=', account_type.id),
('company_id', '=', self.company_id.id)
])
self.not_only_one_unaffected_earnings_account = count != 1
@api.onchange('date_range_id')
def onchange_date_range_id(self):
"""Handle date range change."""
self.date_from = self.date_range_id.date_start
self.date_to = self.date_range_id.date_end
if self.date_from:
self.fy_start_date = self.env.user.company_id.find_daterange_fy(
fields.Date.from_string(self.date_range_id.date_start)
).date_start
@api.onchange('receivable_accounts_only', 'payable_accounts_only')
def onchange_type_accounts_only(self):
"""Handle receivable/payable accounts only change."""
if self.receivable_accounts_only or self.payable_accounts_only:
domain = []
if self.receivable_accounts_only and self.payable_accounts_only:
domain += [('internal_type', 'in', ('receivable', 'payable'))]
elif self.receivable_accounts_only:
domain += [('internal_type', '=', 'receivable')]
elif self.payable_accounts_only:
domain += [('internal_type', '=', 'payable')]
self.account_ids = self.env['account.account'].search(domain)
else:
self.account_ids = None
@api.onchange('partner_ids')
def onchange_partner_ids(self):
"""Handle partners change."""
if self.partner_ids:
self.receivable_accounts_only = self.payable_accounts_only = True
else:
self.receivable_accounts_only = self.payable_accounts_only = False
@api.multi
def button_export_pdf(self):
self.ensure_one()
return self._export()
@api.multi
def button_export_xlsx(self):
self.ensure_one()
return self._export(xlsx_report=True)
def _prepare_report_general_ledger(self):
self.ensure_one()
return {
'date_from': self.date_from,
'date_to': self.date_to,
'only_posted_moves': self.target_move == 'posted',
'hide_account_balance_at_0': self.hide_account_balance_at_0,
'company_id': self.company_id.id,
'filter_account_ids': [(6, 0, self.account_ids.ids)],
'filter_partner_ids': [(6, 0, self.partner_ids.ids)],
'filter_cost_center_ids': [(6, 0, self.cost_center_ids.ids)],
'centralize': self.centralize,
'fy_start_date': self.fy_start_date,
}
def _export(self, xlsx_report=False):
"""Default export is PDF."""
model = self.env['report_general_ledger_qweb']
report = model.create(self._prepare_report_general_ledger())
return report.print_report(xlsx_report)

70
account_financial_report_qweb/wizard/general_ledger_wizard_view.xml

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- GENERAL LEDGER -->
<record id="general_ledger_wizard" model="ir.ui.view">
<field name="name">General Ledger</field>
<field name="model">general.ledger.report.wizard</field>
<field name="arch" type="xml">
<form>
<group name="main_info">
<field name="company_id" options="{'no_create': True}" groups="base.group_multi_company"/>
</group>
<div attrs="{'invisible': [('not_only_one_unaffected_earnings_account', '=', True)]}">
<group name="filters">
<group name="date_range">
<field name="date_range_id" domain="[('company_id','=',company_id)]"/>
<field name="date_from"/>
<field name="date_to"/>
<field name="fy_start_date" invisible="1"/>
</group>
<group name="other_filters">
<field name="target_move" widget="radio"/>
<field name="centralize"/>
<field name="hide_account_balance_at_0"/>
</group>
</group>
<label for="cost_center_ids" groups="analytic.group_analytic_accounting"/>
<field name="cost_center_ids" nolabel="1" options="{'no_create': True}" groups="analytic.group_analytic_accounting"/>
<group/>
<label for="partner_ids"/>
<field name="partner_ids" nolabel="1" options="{'no_create': True}"/>
<group/>
<label for="account_ids"/>
<group col="4">
<field name="receivable_accounts_only"/>
<field name="payable_accounts_only"/>
</group>
<field name="account_ids" nolabel="1" options="{'no_create': True}"/>
</div>
<div attrs="{'invisible': [('not_only_one_unaffected_earnings_account', '=', False)]}">
<field name="not_only_one_unaffected_earnings_account" invisible="1"/>
<group/>
<h4>General Ledger can be computed only if selected company have only one unaffected earnings account.</h4>
<group/>
</div>
<footer>
<div attrs="{'invisible': [('not_only_one_unaffected_earnings_account', '=', True)]}">
<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" />
</div>
<div attrs="{'invisible': [('not_only_one_unaffected_earnings_account', '=', False)]}">
<button string="Cancel" class="oe_link" special="cancel" />
</div>
</footer>
</form>
</field>
</record>
<act_window id="action_general_ledger_wizard"
name="General Ledger"
res_model="general.ledger.report.wizard"
view_type="form"
view_mode="form"
view_id="general_ledger_wizard"
target="new" />
</odoo>

87
account_financial_report_qweb/wizard/open_items_wizard.py

@ -0,0 +1,87 @@
# -*- 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 datetime import datetime
from openerp import models, fields, api
class OpenItemsReportWizard(models.TransientModel):
"""Open items report wizard."""
_name = "open.items.report.wizard"
_description = "Open Items Report Wizard"
company_id = fields.Many2one(
comodel_name='res.company',
default=lambda self: self.env.user.company_id,
string='Company'
)
date_at = fields.Date(required=True,
default=fields.Date.to_string(datetime.today()))
target_move = fields.Selection([('posted', 'All Posted Entries'),
('all', 'All Entries')],
string='Target Moves',
required=True,
default='all')
account_ids = fields.Many2many(
comodel_name='account.account',
string='Filter accounts',
)
hide_account_balance_at_0 = fields.Boolean(
string='Hide account ending balance at 0',
help='Use this filter to hide an account or a partner '
'with an ending balance at 0. '
'If partners are filtered, '
'debits and credits totals will not match the trial balance.'
)
receivable_accounts_only = fields.Boolean()
payable_accounts_only = fields.Boolean()
partner_ids = fields.Many2many(
comodel_name='res.partner',
string='Filter partners',
)
@api.onchange('receivable_accounts_only', 'payable_accounts_only')
def onchange_type_accounts_only(self):
"""Handle receivable/payable accounts only change."""
if self.receivable_accounts_only or self.payable_accounts_only:
domain = []
if self.receivable_accounts_only and self.payable_accounts_only:
domain += [('internal_type', 'in', ('receivable', 'payable'))]
elif self.receivable_accounts_only:
domain += [('internal_type', '=', 'receivable')]
elif self.payable_accounts_only:
domain += [('internal_type', '=', 'payable')]
self.account_ids = self.env['account.account'].search(domain)
else:
self.account_ids = None
@api.multi
def button_export_pdf(self):
self.ensure_one()
return self._export()
@api.multi
def button_export_xlsx(self):
self.ensure_one()
return self._export(xlsx_report=True)
def _prepare_report_open_items(self):
self.ensure_one()
return {
'date_at': self.date_at,
'only_posted_moves': self.target_move == 'posted',
'hide_account_balance_at_0': self.hide_account_balance_at_0,
'company_id': self.company_id.id,
'filter_account_ids': [(6, 0, self.account_ids.ids)],
'filter_partner_ids': [(6, 0, self.partner_ids.ids)],
}
def _export(self, xlsx_report=False):
"""Default export is PDF."""
model = self.env['report_open_items_qweb']
report = model.create(self._prepare_report_open_items())
return report.print_report(xlsx_report)

50
account_financial_report_qweb/wizard/open_items_wizard_view.xml

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- OPEN ITEMS -->
<record id="open_items_wizard" model="ir.ui.view">
<field name="name">Open Items</field>
<field name="model">open.items.report.wizard</field>
<field name="arch" type="xml">
<form>
<group name="main_info">
<field name="company_id" options="{'no_create': True}" groups="base.group_multi_company"/>
</group>
<group name="filters">
<group name="date_range">
<field name="date_at"/>
</group>
<group name="other_filters">
<field name="target_move" widget="radio"/>
<field name="hide_account_balance_at_0"/>
</group>
</group>
<label for="partner_ids"/>
<field name="partner_ids" nolabel="1" options="{'no_create': True}"/>
<group/>
<label for="account_ids"/>
<group col="4">
<field name="receivable_accounts_only"/>
<field name="payable_accounts_only"/>
</group>
<field name="account_ids" nolabel="1" options="{'no_create': True}"/>
<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>
</field>
</record>
<act_window id="action_open_items_wizard"
name="Open Items"
res_model="open.items.report.wizard"
view_type="form"
view_mode="form"
view_id="open_items_wizard"
target="new" />
</odoo>

129
account_financial_report_qweb/wizard/trial_balance_wizard.py

@ -0,0 +1,129 @@
# -*- 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 import models, fields, api
class TrialBalanceReportWizard(models.TransientModel):
"""Trial balance report wizard."""
_name = "trial.balance.report.wizard"
_description = "Trial Balance Report Wizard"
company_id = fields.Many2one(
comodel_name='res.company',
default=lambda self: self.env.user.company_id,
string='Company'
)
date_range_id = fields.Many2one(
comodel_name='date.range',
required=True,
string='Date range'
)
date_from = fields.Date(required=True)
date_to = fields.Date(required=True)
fy_start_date = fields.Date(required=True)
target_move = fields.Selection([('posted', 'All Posted Entries'),
('all', 'All Entries')],
string='Target Moves',
required=True,
default='all')
account_ids = fields.Many2many(
comodel_name='account.account',
string='Filter accounts',
)
hide_account_balance_at_0 = fields.Boolean(
string='Hide account ending balance at 0',
help='Use this filter to hide an account or a partner '
'with an ending balance at 0. '
'If partners are filtered, '
'debits and credits totals will not match the trial balance.'
)
receivable_accounts_only = fields.Boolean()
payable_accounts_only = fields.Boolean()
show_partner_details = fields.Boolean()
partner_ids = fields.Many2many(
comodel_name='res.partner',
string='Filter partners',
)
not_only_one_unaffected_earnings_account = fields.Boolean(
readonly=True,
string='Not only one unaffected earnings account'
)
@api.onchange('company_id')
def onchange_company_id(self):
"""Handle company change."""
account_type = self.env.ref('account.data_unaffected_earnings')
count = self.env['account.account'].search_count(
[
('user_type_id', '=', account_type.id),
('company_id', '=', self.company_id.id)
])
self.not_only_one_unaffected_earnings_account = count != 1
@api.onchange('date_range_id')
def onchange_date_range_id(self):
"""Handle date range change."""
self.date_from = self.date_range_id.date_start
self.date_to = self.date_range_id.date_end
if self.date_from:
self.fy_start_date = self.env.user.company_id.find_daterange_fy(
fields.Date.from_string(self.date_range_id.date_start)
).date_start
@api.onchange('receivable_accounts_only', 'payable_accounts_only')
def onchange_type_accounts_only(self):
"""Handle receivable/payable accounts only change."""
if self.receivable_accounts_only or self.payable_accounts_only:
domain = []
if self.receivable_accounts_only and self.payable_accounts_only:
domain += [('internal_type', 'in', ('receivable', 'payable'))]
elif self.receivable_accounts_only:
domain += [('internal_type', '=', 'receivable')]
elif self.payable_accounts_only:
domain += [('internal_type', '=', 'payable')]
self.account_ids = self.env['account.account'].search(domain)
else:
self.account_ids = None
@api.onchange('show_partner_details')
def onchange_show_partner_details(self):
"""Handle partners change."""
if self.show_partner_details:
self.receivable_accounts_only = self.payable_accounts_only = True
else:
self.receivable_accounts_only = self.payable_accounts_only = False
@api.multi
def button_export_pdf(self):
self.ensure_one()
return self._export()
@api.multi
def button_export_xlsx(self):
self.ensure_one()
return self._export(xlsx_report=True)
def _prepare_report_trial_balance(self):
self.ensure_one()
return {
'date_from': self.date_from,
'date_to': self.date_to,
'only_posted_moves': self.target_move == 'posted',
'hide_account_balance_at_0': self.hide_account_balance_at_0,
'company_id': self.company_id.id,
'filter_account_ids': [(6, 0, self.account_ids.ids)],
'filter_partner_ids': [(6, 0, self.partner_ids.ids)],
'fy_start_date': self.fy_start_date,
'show_partner_details': self.show_partner_details,
}
def _export(self, xlsx_report=False):
"""Default export is PDF."""
model = self.env['report_trial_balance_qweb']
report = model.create(self._prepare_report_trial_balance())
return report.print_report(xlsx_report)

67
account_financial_report_qweb/wizard/trial_balance_wizard_view.xml

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- TRIAL BALANCE -->
<record id="trial_balance_wizard" model="ir.ui.view">
<field name="name">Trial Balance</field>
<field name="model">trial.balance.report.wizard</field>
<field name="arch" type="xml">
<form>
<group name="main_info">
<field name="company_id" options="{'no_create': True}" groups="base.group_multi_company"/>
</group>
<div attrs="{'invisible': [('not_only_one_unaffected_earnings_account', '=', True)]}">
<group name="filters">
<group name="date_range">
<field name="date_range_id" domain="[('company_id','=',company_id)]"/>
<field name="date_from"/>
<field name="date_to"/>
<field name="fy_start_date" invisible="1"/>
</group>
<group name="other_filters">
<field name="target_move" widget="radio"/>
<field name="hide_account_balance_at_0"/>
<field name="show_partner_details"/>
</group>
</group>
<label for="partner_ids" attrs="{'invisible':[('show_partner_details','!=',True)]}"/>
<field name="partner_ids" nolabel="1" options="{'no_create': True}" attrs="{'invisible':[('show_partner_details','!=',True)]}"/>
<group attrs="{'invisible':[('show_partner_details','!=',True)]}"/>
<label for="account_ids"/>
<group col="4">
<field name="receivable_accounts_only"/>
<field name="payable_accounts_only"/>
</group>
<field name="account_ids" nolabel="1" options="{'no_create': True}"/>
</div>
<div attrs="{'invisible': [('not_only_one_unaffected_earnings_account', '=', False)]}">
<field name="not_only_one_unaffected_earnings_account" invisible="1"/>
<group/>
<h4>Trial Balance can be computed only if selected company have only one unaffected earnings account.</h4>
<group/>
</div>
<footer>
<div attrs="{'invisible': [('not_only_one_unaffected_earnings_account', '=', True)]}">
<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" />
</div>
<div attrs="{'invisible': [('not_only_one_unaffected_earnings_account', '=', False)]}">
<button string="Cancel" class="oe_link" special="cancel" />
</div>
</footer>
</form>
</field>
</record>
<act_window id="action_trial_balance_wizard"
name="Trial Balance"
res_model="trial.balance.report.wizard"
view_type="form"
view_mode="form"
view_id="trial_balance_wizard"
target="new" />
</odoo>

1
oca_dependencies.txt

@ -1,5 +1,6 @@
# list the OCA project dependencies, one per line # list the OCA project dependencies, one per line
# add a github url if you need a forked version # add a github url if you need a forked version
account-financial-tools
reporting-engine reporting-engine
server-tools server-tools
web web

1
setup/account_financial_report_qweb/odoo_addons/__init__.py

@ -0,0 +1 @@
__import__('pkg_resources').declare_namespace(__name__)

1
setup/account_financial_report_qweb/odoo_addons/account_financial_report_qweb

@ -0,0 +1 @@
../../../account_financial_report_qweb

6
setup/account_financial_report_qweb/setup.py

@ -0,0 +1,6 @@
import setuptools
setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)
Loading…
Cancel
Save