Browse Source

[ADD] Aged Partner Balance webkit report. Report inherit Open Invoice Report and uses previously computed ledger lines to determin aged lines

pull/7/merge
unknown 10 years ago
committed by Yannick Vaucher
parent
commit
33e7da97fe
  1. 49
      account_financial_report_webkit/__openerp__.py
  2. 1
      account_financial_report_webkit/report/__init__.py
  3. 403
      account_financial_report_webkit/report/aged_partner_balance.py
  4. 14
      account_financial_report_webkit/report/common_reports.py
  5. 1
      account_financial_report_webkit/report/open_invoices.py
  6. 45
      account_financial_report_webkit/report/report.xml
  7. 155
      account_financial_report_webkit/report/templates/aged_trial_webkit.mako
  8. 4
      account_financial_report_webkit/report/templates/open_invoices_inclusion.mako.html
  9. 1
      account_financial_report_webkit/report/webkit_parser_header_fix.py
  10. 4
      account_financial_report_webkit/report_menus.xml
  11. 60
      account_financial_report_webkit/tests/aged_trial_balance.yml
  12. 1
      account_financial_report_webkit/wizard/__init__.py
  13. 39
      account_financial_report_webkit/wizard/aged_partner_balance_wizard.py
  14. 72
      account_financial_report_webkit/wizard/aged_partner_balance_wizard.xml

49
account_financial_report_webkit/__openerp__.py

@ -30,7 +30,7 @@ This module adds or replaces the following standard OpenERP financial reports:
- Partner ledger
- Partner balance
- Open invoices report
- Aged Partner Balance
Main improvements per report:
-----------------------------
@ -100,6 +100,47 @@ The Partner balance: list of account with balances
* Subtotal by account and partner
* Alphabetical sorting (same as in partner balance)
Aged Partner Balance: Summary of aged open amount per partner
This report is an accounting tool helping in various tasks.
You can credit control or partner balance provisions computation for instance.
The aged balance report allows you to print balances per partner
like the trial balance but add an extra information :
* It will split balances into due amounts
(due date not reached à the end date of the report) and overdue amounts
Overdue data are also split by period.
* For each partner following columns will be displayed:
* Total balance (all figures must match with same date partner balance report).
This column equals the sum of all following columns)
* Due
* Overdue <= 30 days
* Overdue <= 60 days
* Overdue <= 90 days
* Overdue <= 120 days
* Older
Hypothesis / Contraints of aged partner balance
* Overdues columns will be by default be based on 30 days range fix number of days.
This can be changed by changes the RANGES constraint
* All data will be displayed in company currency
* When partial payments, the payment must appear in the same colums than the invoice
(Except if multiple payment terms)
* Data granularity: partner (will not display figures at invoices level)
* The report aggregate data per account with sub-totals
* Initial balance must be calculated the same way that
the partner balance / Ignoring the opening entry
in special period (idem open invoice report)
* Only accounts with internal type payable or receivable are considered
(idem open invoice report)
* If maturity date is null then use move line date
Limitations:
------------
@ -126,7 +167,7 @@ an issue in wkhtmltopdf
the header and footer are created as text with arguments passed to
wkhtmltopdf. The texts are defined inside the report classes.
""",
'version': '1.0.2',
'version': '1.1.0',
'author': 'Camptocamp',
'license': 'AGPL-3',
'category': 'Finance',
@ -147,6 +188,7 @@ wkhtmltopdf. The texts are defined inside the report classes.
'wizard/trial_balance_wizard_view.xml',
'wizard/partner_balance_wizard_view.xml',
'wizard/open_invoices_wizard_view.xml',
'wizard/aged_partner_balance_wizard.xml',
'wizard/print_journal_view.xml',
'report_menus.xml',
],
@ -155,7 +197,8 @@ wkhtmltopdf. The texts are defined inside the report classes.
'tests/partner_ledger.yml',
'tests/trial_balance.yml',
'tests/partner_balance.yml',
'tests/open_invoices.yml',],
'tests/open_invoices.yml',
'tests/aged_trial_balance.yml'],
#'tests/account_move_line.yml'
'active': False,
'installable': True,

1
account_financial_report_webkit/report/__init__.py

@ -9,3 +9,4 @@ from . import trial_balance
from . import partner_balance
from . import open_invoices
from . import print_journal
from . import aged_partner_balance

403
account_financial_report_webkit/report/aged_partner_balance.py

@ -0,0 +1,403 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Nicolas Bessi
# Copyright 2014 Camptocamp SA
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from __future__ import division
from datetime import datetime
from openerp import pooler
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT
from openerp.tools.translate import _
from .open_invoices import PartnersOpenInvoicesWebkit
from .webkit_parser_header_fix import HeaderFooterTextWebKitParser
def make_ranges(top, offset):
"""Return sorted days ranges
:param top: maximum overdue day
:param offset: offset for ranges
:returns: list of sorted ranges tuples in days
eg. [(-100000, 0), (0, offset), (offset, n*offset), ... (top, 100000)]
"""
ranges = [(n, min(n + offset, top)) for n in xrange(0, top, offset)]
ranges.insert(0, (-100000000000, 0))
ranges.append((top, 100000000000))
return ranges
#list of overdue ranges
RANGES = make_ranges(120, 30)
def make_ranges_titles():
"""Generates title to be used by mako"""
titles = [_('Due')]
titles += [_(u'Overdue ≤ %s d.') % x[1] for x in RANGES[1:-1]]
titles.append(_('Older'))
return titles
#list of overdue ranges title
RANGES_TITLES = make_ranges_titles()
#list of payable journal types
REC_PAY_TYPE = ('purchase', 'sale')
#list of refund payable type
REFUND_TYPE = ('purchase_refund', 'sale_refund')
INV_TYPE = REC_PAY_TYPE + REFUND_TYPE
class AccountAgedTrialBalanceWebkit(PartnersOpenInvoicesWebkit):
"""Compute Aged Partner Balance based on result of Open Invoices"""
def __init__(self, cursor, uid, name, context=None):
"""Constructor, refer to :class:`openerp.report.report_sxw.rml_parse`"""
super(AccountAgedTrialBalanceWebkit, self).__init__(cursor, uid, name,
context=context)
self.pool = pooler.get_pool(self.cr.dbname)
self.cursor = self.cr
company = self.pool.get('res.users').browse(self.cr, uid, uid,
context=context).company_id
header_report_name = ' - '.join((_('Aged Partner Balance'),
company.currency_id.name))
footer_date_time = self.formatLang(str(datetime.today()),
date_time=True)
self.localcontext.update({
'cr': cursor,
'uid': uid,
'company': company,
'ranges': self._get_ranges(),
'ranges_titles': self._get_ranges_titles(),
'report_name': _('Aged Partner Balance'),
'additional_args': [
('--header-font-name', 'Helvetica'),
('--footer-font-name', 'Helvetica'),
('--header-font-size', '10'),
('--footer-font-size', '6'),
('--header-left', header_report_name),
('--header-spacing', '2'),
('--footer-left', footer_date_time),
('--footer-right', ' '.join((_('Page'), '[page]', _('of'), '[topage]'))),
('--footer-line',),
],
})
def _get_ranges(self):
""":returns: :cons:`RANGES`"""
return RANGES
def _get_ranges_titles(self):
""":returns: :cons: `RANGES_TITLES`"""
return RANGES_TITLES
def set_context(self, objects, data, ids, report_type=None):
"""Populate aged_lines, aged_balance, aged_percents attributes
on each account browse record that will be used by mako template
The browse record are store in :attr:`objects`
The computation are based on the ledger_lines attribute set on account
contained by :attr:`objects`
:attr:`objects` values were previously set by parent class
:class: `.open_invoices.PartnersOpenInvoicesWebkit`
:returns: parent :class:`.open_invoices.PartnersOpenInvoicesWebkit`
call to set_context
"""
res = super(AccountAgedTrialBalanceWebkit, self).set_context(
objects,
data,
ids,
report_type=report_type
)
for acc in self.objects:
acc.aged_lines = {}
acc.agged_totals = {}
acc.agged_percents = {}
for part_id, partner_lines in acc.ledger_lines.items():
aged_lines = self.compute_aged_lines(part_id,
partner_lines,
data)
if aged_lines:
acc.aged_lines[part_id] = aged_lines
acc.aged_totals = totals = self.compute_totals(acc.aged_lines.values())
acc.aged_percents = self.compute_percents(totals)
#Free some memory
del(acc.ledger_lines)
return res
def compute_aged_lines(self, partner_id, ledger_lines, data):
"""Add property aged_lines to accounts browse records
contained in :attr:`objects` for a given partner
:param: partner_id: current partner
:param ledger_lines: generated by parent
:class:`.open_invoices.PartnersOpenInvoicesWebkit`
:returns: dict of computed aged lines
eg {'balance': 1000.0,
'aged_lines': {(90, 120): 0.0, ...}
"""
lines_to_age = self.filter_lines(partner_id, ledger_lines)
res = {}
end_date = self._get_end_date(data)
aged_lines = dict.fromkeys(RANGES, 0.0)
reconcile_lookup = self.get_reconcile_count_lookup(lines_to_age)
res['aged_lines'] = aged_lines
for line in lines_to_age:
compute_method = self.get_compute_method(reconcile_lookup,
partner_id,
line)
delay = compute_method(line, end_date, ledger_lines)
classification = self.classify_line(partner_id, delay)
aged_lines[classification] += line['debit'] - line['credit']
self.compute_balance(res, aged_lines)
return res
def _get_end_date(self, data):
"""Retrieve end date to be used to compute delay.
:param data: data dict send to report contains form dict
:returns: end date to be used to compute overdue delay
"""
end_date = None
date_to = data['form']['date_to']
period_to_id = data['form']['period_to']
fiscal_to_id = data['form']['fiscalyear_id']
if date_to:
end_date = date_to
elif period_to_id:
period_to = self.pool['account.period'].browse(self.cr,
self.uid,
period_to_id)
end_date = period_to.date_stop
elif fiscal_to_id:
fiscal_to = self.pool['account.fiscalyear'].browse(self.cr,
self.uid,
fiscal_to_id)
end_date = fiscal_to.date_stop
else:
raise ValueError('End date and end period not available')
return end_date
def _compute_delay_from_key(self, key, line, end_date):
"""Compute overdue delay delta in days for line using attribute in key
delta = end_date - date of key
:param line: current ledger line
:param key: date key to be used to compute delta
:param end_date: end_date computed for wizard data
:returns: delta in days
"""
from_date = datetime.strptime(line[key], DEFAULT_SERVER_DATE_FORMAT)
end_date = datetime.strptime(end_date, DEFAULT_SERVER_DATE_FORMAT)
delta = end_date - from_date
return delta.days
def compute_delay_from_maturity(self, line, end_date, ledger_lines):
"""Compute overdue delay delta in days for line using attribute in key
delta = end_date - maturity date
:param line: current ledger line
:param end_date: end_date computed for wizard data
:param ledger_lines: generated by parent
:class:`.open_invoices.PartnersOpenInvoicesWebkit`
:returns: delta in days
"""
return self._compute_delay_from_key('date_maturity',
line,
end_date)
def compute_delay_from_date(self, line, end_date, ledger_lines):
"""Compute overdue delay delta in days for line using attribute in key
delta = end_date - date
:param line: current ledger line
:param end_date: end_date computed for wizard data
:param ledger_lines: generated by parent
:class:`.open_invoices.PartnersOpenInvoicesWebkit`
:returns: delta in days
"""
return self._compute_delay_from_key('ldate',
line,
end_date)
def compute_delay_from_partial_rec(self, line, end_date, ledger_lines):
"""Compute overdue delay delta in days for the case where move line
is related to a partial reconcile with more than one reconcile line
:param line: current ledger line
:param end_date: end_date computed for wizard data
:param ledger_lines: generated by parent
:class:`.open_invoices.PartnersOpenInvoicesWebkit`
:returns: delta in days
"""
sale_lines = [x for x in ledger_lines if x['jtype'] in REC_PAY_TYPE and
line['rec_id'] == x['rec_id']]
refund_lines = [x for x in ledger_lines if x['jtype'] in REFUND_TYPE and
line['rec_id'] == x['rec_id']]
if len(sale_lines) == 1:
reference_line = sale_lines[0]
elif len(refund_lines) == 1:
reference_line = refund_lines[0]
else:
reference_line = line
key = 'date_maturity' if reference_line.get('date_maturity') else 'ldate'
return self._compute_delay_from_key(key,
reference_line,
end_date)
def get_compute_method(self, reconcile_lookup, partner_id, line):
"""Get the function that should compute the delay for a given line
:param reconcile_lookup: dict of reconcile group by id and count
{rec_id: count of line related to reconcile}
:param partner_id: current partner_id
:param line: current ledger line generated by parent
:class:`.open_invoices.PartnersOpenInvoicesWebkit`
:returns: function bounded to :class:`.AccountAgedTrialBalanceWebkit`
"""
if reconcile_lookup.get(line['rec_id'], 0.0) > 1:
return self.compute_delay_from_partial_rec
elif line['jtype'] in INV_TYPE and line.get('date_maturity'):
return self.compute_delay_from_maturity
else:
return self.compute_delay_from_date
def line_is_valid(self, partner_id, line):
"""Predicate hook that allows to filter line to be treated
:param partner_id: current partner_id
:param line: current ledger line generated by parent
:class:`.open_invoices.PartnersOpenInvoicesWebkit`
:returns: boolean True if line is allowed
"""
return True
def filter_lines(self, partner_id, lines):
"""Filter ledger lines that have to be treated
:param partner_id: current partner_id
:param lines: ledger_lines related to current partner
and generated by parent
:class:`.open_invoices.PartnersOpenInvoicesWebkit`
:returns: list of allowed lines
"""
return [x for x in lines if self.line_is_valid(partner_id, x)]
def classify_line(self, partner_id, overdue_days):
"""Return the overdue range for a given delay
We loop from smaller range to higher
This should be the most effective solution as generaly
customer tend to have one or two month of delay
:param overdue_days: delay in days
:param partner_id: current partner_id
:returns: the correct range in :const:`RANGES`
"""
for drange in RANGES:
if overdue_days <= drange[1]:
return drange
return drange
def compute_balance(self, res, aged_lines):
"""Compute the total balance of aged line
for given account"""
res['balance'] = sum(aged_lines.values())
def compute_totals(self, aged_lines):
"""Compute the totals for an account
:param aged_lines: dict of aged line taken from the
property added to account record
:returns: dict of total {'balance':1000.00, (30, 60): 3000,...}
"""
totals = {}
totals['balance'] = sum(x.get('balance', 0.0) for
x in aged_lines)
aged_ranges = [x.get('aged_lines', {}) for x in aged_lines]
for drange in RANGES:
totals[drange] = sum(x.get(drange, 0.0) for x in aged_ranges)
return totals
def compute_percents(self, totals):
percents = {}
base = totals['balance'] or 1.0
for drange in RANGES:
percents[drange] = (totals[drange] / base) * 100.0
return percents
def get_reconcile_count_lookup(self, lines):
"""Compute an lookup dict
It contains has partial reconcile id as key and the count of lines
related to the reconcile id
:param: a list of ledger lines generated by parent
:class:`.open_invoices.PartnersOpenInvoicesWebkit`
:retuns: lookup dict {ec_id: count}
"""
# possible bang if l_ids is really long.
# We have the same weakness in common_report ...
# but it seems not really possible for a partner
# So I'll keep that option.
l_ids = tuple(x['id'] for x in lines)
sql = ("SELECT reconcile_partial_id, COUNT(*) FROM account_move_line"
" WHERE reconcile_partial_id IS NOT NULL"
" AND id in %s"
" GROUP BY reconcile_partial_id")
self.cr.execute(sql, (l_ids,))
res = self.cr.fetchall()
return dict((x[0], x[1]) for x in res)
HeaderFooterTextWebKitParser(
'report.account.account_aged_trial_balance_webkit',
'account.account',
'addons/account_financial_report_webkit/report/templates/aged_trial_webkit.mako',
parser=AccountAgedTrialBalanceWebkit,
)

14
account_financial_report_webkit/report/common_reports.py

@ -30,7 +30,7 @@ from openerp.addons.account.report.common_report_header import common_report_hea
_logger = logging.getLogger('financial.reports.webkit')
MAX_MONSTER_SLICE = 50000
class CommonReportHeaderWebkit(common_report_header):
"""Define common helper for financial report"""
@ -433,6 +433,14 @@ class CommonReportHeaderWebkit(common_report_header):
raise osv.except_osv(_('No valid filter'), _('Please set a valid time filter'))
def _get_move_line_datas(self, move_line_ids, order='per.special DESC, l.date ASC, per.date_start ASC, m.name ASC'):
# Possible bang if move_line_ids is too long
# We can not slice here as we have to do the sort.
# If slice has to be done it means that we have to reorder in python
# after all is finished. That quite crapy...
# We have a defective desing here (mea culpa) that should be fixed
#
# TODO improve that by making a better domain or if not possible
# by using python sort
if not move_line_ids:
return []
if not isinstance(move_line_ids, list):
@ -441,6 +449,7 @@ class CommonReportHeaderWebkit(common_report_header):
SELECT l.id AS id,
l.date AS ldate,
j.code AS jcode ,
j.type AS jtype,
l.currency_id,
l.account_id,
l.amount_currency,
@ -455,7 +464,8 @@ SELECT l.id AS id,
l.partner_id AS lpartner_id,
p.name AS partner_name,
m.name AS move_name,
COALESCE(partialrec.name, fullrec.name, '') AS rec_name,
COALESCE(partialrec.name, fullrec.name, '') AS rec_name,
COALESCE(partialrec.id, fullrec.id, NULL) AS rec_id,
m.id AS move_id,
c.name AS currency_code,
i.id AS invoice_id,

1
account_financial_report_webkit/report/open_invoices.py

@ -93,7 +93,6 @@ class PartnersOpenInvoicesWebkit(report_sxw.rml_parse, CommonPartnersReportHeade
"""Populate a ledger_lines attribute on each browse record that will be used
by mako template"""
new_ids = data['form']['chart_account_id']
# Account initial balance memoizer
init_balance_memoizer = {}
# Reading form

45
account_financial_report_webkit/report/report.xml

@ -14,23 +14,16 @@
<field name="name">General Ledger Webkit</field>
<field name="report_rml">account_financial_report_webkit/report/templates/account_report_general_ledger.mako</field>
<field name="report_file">account_financial_report_webkit/report/templates/account_report_general_ledger.mako</field>
</record>
</record>
<record id="property_account_report_general_ledger_webkit" model="ir.property">
<field name="name">account_report_general_ledger_webkit</field>
<field name="fields_id" ref="report_webkit.field_ir_act_report_xml_webkit_header"/>
<field eval="'ir.header_webkit,'+str(ref('account_financial_report_webkit.financial_landscape_header'))" model="ir.header_webkit" name="value"/>
<field eval="'ir.actions.report.xml,'+str(ref('account_financial_report_webkit.account_report_general_ledger_webkit'))" model="ir.actions.report.xml" name="res_id"/>
</record>
<!-- waiting the fix
<report auto="False"
id="account_report_partner_ledger_webkit"
model="account.account"
name="account.account_report_partner_ledger_webkit"
file="account_financial_report_webkit/report/templates/account_report_partner_ledger.mako"
string="General Ledger Webkit"
report_type="webkit"/> -->
<!-- we do not use report tag has we can not set header ref -->
<!-- we do not use report tag has we can not set header ref -->
<record id="account_report_partners_ledger_webkit" model="ir.actions.report.xml">
<field name="report_type">webkit</field>
<field name="report_name">account.account_report_partners_ledger_webkit</field>
@ -44,6 +37,7 @@
<field name="report_rml">account_financial_report_webkit/report/templates/account_report_partners_ledger.mako</field>
<field name="report_file">account_financial_report_webkit/report/templates/account_report_partners_ledger.mako</field>
</record>
<record id="property_account_report_partners_ledger_webkit" model="ir.property">
<field name="name">account_report_partners_ledger_webkit</field>
<field name="fields_id" ref="report_webkit.field_ir_act_report_xml_webkit_header"/>
@ -64,6 +58,7 @@
<field name="report_rml">account_financial_report_webkit/report/templates/account_report_trial_balance.mako</field>
<field name="report_file">account_financial_report_webkit/report/templates/account_report_trial_balance.mako</field>
</record>
<record id="property_account_report_trial_balance_webkit" model="ir.property">
<field name="name">account_report_trial_balance_webkit</field>
<field name="fields_id" ref="report_webkit.field_ir_act_report_xml_webkit_header"/>
@ -84,6 +79,7 @@
<field name="report_rml">account_financial_report_webkit/report/templates/account_report_partner_balance.mako</field>
<field name="report_file">account_financial_report_webkit/report/templates/account_report_partner_balance.mako</field>
</record>
<record id="property_account_report_partner_balance_webkit" model="ir.property">
<field name="name">account_report_partner_balance_webkit</field>
<field name="fields_id" ref="report_webkit.field_ir_act_report_xml_webkit_header"/>
@ -104,6 +100,7 @@
<field name="report_rml">account_financial_report_webkit/report/templates/account_report_open_invoices.mako</field>
<field name="report_file">account_financial_report_webkit/report/templates/account_report_open_invoices.mako</field>
</record>
<record id="property_account_report_open_invoices_webkit" model="ir.property">
<field name="name">account_report_open_invoices_webkit</field>
<field name="fields_id" ref="report_webkit.field_ir_act_report_xml_webkit_header"/>
@ -111,6 +108,31 @@
<field eval="'ir.actions.report.xml,'+str(ref('account_financial_report_webkit.account_report_open_invoices_webkit'))" model="ir.actions.report.xml" name="res_id"/>
</record>
<record id="account_report_aged_trial_blanance_webkit" model="ir.actions.report.xml">
<field name="report_type">webkit</field>
<field name="report_name">account.account_aged_trial_balance_webkit</field>
<field eval="[(6,0,[])]" name="groups_id"/>
<field eval="0" name="multi"/>
<field eval="0" name="auto"/>
<field eval="1" name="header"/>
<field name="model">account.account</field>
<field name="type">ir.actions.report.xml</field>
<field name="name">Aged Partner Balance</field>
<field name="report_rml">account_financial_report_webkit/report/templates/aged_trial_webkit.mako</field>
<field name="report_file">account_financial_report_webkit/report/templates/aged_trial_webkit.mako</field>
</record>
<record id="property_account_report_aged_trial_balance_webkit" model="ir.property">
<field name="name">account_aged_trial_balance_webkit</field>
<field name="fields_id" ref="report_webkit.field_ir_act_report_xml_webkit_header"/>
<field eval="'ir.header_webkit,'+str(ref('account_financial_report_webkit.financial_landscape_header'))"
model="ir.header_webkit"
name="value"/>
<field eval="'ir.actions.report.xml,'+str(ref('account_financial_report_webkit.account_report_aged_trial_blanance_webkit'))"
model="ir.actions.report.xml"
name="res_id"/>
</record>
<record id="account_report_print_journal_webkit" model="ir.actions.report.xml">
<field name="report_type">webkit</field>
<field name="report_name">account.account_report_print_journal_webkit</field>
@ -124,6 +146,7 @@
<field name="report_rml">account_financial_report_webkit/report/templates/account_report_print_journal.mako</field>
<field name="report_file">account_financial_report_webkit/report/templates/account_report_print_journal.mako</field>
</record>
<record id="property_account_report_print_journal_webkit" model="ir.property">
<field name="name">account_report_print_journal_webkit</field>
<field name="fields_id" ref="report_webkit.field_ir_act_report_xml_webkit_header"/>

155
account_financial_report_webkit/report/templates/aged_trial_webkit.mako

@ -0,0 +1,155 @@
## -*- coding: utf-8 -*-
<!DOCTYPE html SYSTEM
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<style type="text/css">
.overflow_ellipsis {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.open_invoice_previous_line {
font-style: italic;
}
.percent_line {
font-style: italic;
}
.amount {
text-align:right;
}
.classif_title {
text-align:right;
}
.classif{
width: ${700/len(ranges)}px;
}
.total{
font-weight:bold;
}
${css}
</style>
</head>
<%!
def amount(text):
# replace by a non-breaking hyphen (it will not word-wrap between hyphen and numbers)
return text.replace('-', '&#8209;')
%>
<body>
<%setLang(user.lang)%>
<div class="act_as_table data_table">
<div class="act_as_row labels">
<div class="act_as_cell">${_('Chart of Account')}</div>
<div class="act_as_cell">${_('Fiscal Year')}</div>
<div class="act_as_cell">
%if filter_form(data) == 'filter_date':
${_('Dates Filter')}
%else:
${_('Periods Filter')}
%endif
</div>
<div class="act_as_cell">${_('Clearance Date')}</div>
<div class="act_as_cell">${_('Accounts Filter')}</div>
<div class="act_as_cell">${_('Target Moves')}</div>
</div>
<div class="act_as_row">
<div class="act_as_cell">${ chart_account.name }</div>
<div class="act_as_cell">${ fiscalyear.name if fiscalyear else '-' }</div>
<div class="act_as_cell">
${_('From:')}
%if filter_form(data) == 'filter_date':
${formatLang(start_date, date=True) if start_date else u'' }
%else:
${start_period.name if start_period else u''}
%endif
${_('To:')}
%if filter_form(data) == 'filter_date':
${ formatLang(stop_date, date=True) if stop_date else u'' }
%else:
${stop_period.name if stop_period else u'' }
%endif
</div>
<div class="act_as_cell">${ formatLang(date_until, date=True) }</div>
<div class="act_as_cell">
%if partner_ids:
${_('Custom Filter')}
%else:
${ display_partner_account(data) }
%endif
</div>
<div class="act_as_cell">${ display_target_move(data) }</div>
</div>
</div>
%for acc in objects:
%if acc.aged_lines:
<div class="account_title bg" style="width: 1080px; margin-top: 20px; font-size: 12px;">${acc.code} - ${acc.name}</div>
<div class="act_as_table list_table" style="margin-top: 5px;">
<div class="act_as_thead">
<div class="act_as_row labels">
## partner
<div class="act_as_cell first_column" style="width: 60px;">${_('Partner')}</div>
## code
<div class="act_as_cell" style="width: 70px;">${_('code')}</div>
## balance
<div class="act_as_cell classif_title" style="width: 70px;">${_('balance')}</div>
## Classifications
%for title in ranges_titles:
<div class="act_as_cell classif classif_title">${title}</div>
%endfor
</div>
</div>
<div class="act_as_tbody">
%for partner_name, p_id, p_ref, p_name in acc.partners_order:
%if acc.aged_lines.get(p_id):
<div class="act_as_row lines">
<%line = acc.aged_lines[p_id]%>
<%percents = acc.aged_percents%>
<%totals = acc.aged_totals%>
<div class="act_as_cell first_column">${partner_name}</div>
<div class="act_as_cell">${p_ref or ''}</div>
<div class="act_as_cell amount">${formatLang(line.get('balance') or 0.0) | amount}</div>
%for classif in ranges:
<div class="act_as_cell classif amount">
${formatLang(line['aged_lines'][classif] or 0.0) | amount}
</div>
%endfor
</div>
%endif
%endfor
<div class="act_as_row labels">
<div class="act_as_cell total">${_('Total')}</div>
<div class="act_as_cell"></div>
<div class="act_as_cell amount classif total">${formatLang(totals['balance']) | amount}</div>
%for classif in ranges:
<div class="act_as_cell amount classif total">${formatLang(totals[classif]) | amount}</div>
%endfor
</div>
<div class="act_as_row">
<div class="act_as_cell"><b>${_('Percents')}</b></div>
<div class="act_as_cell"></div>
<div class="act_as_cell"></div>
%for classif in ranges:
<div class="act_as_cell amount percent_line classif">${formatLang(percents[classif]) | amount}%</div>
%endfor
</div>
</div>
<br/>
%endif
%endfor
</div>
</body>
</html>

4
account_financial_report_webkit/report/templates/open_invoices_inclusion.mako.html

@ -9,7 +9,7 @@
%>
<div class="account_title bg" style="width: 1080px; margin-top: 20px; font-size: 12px;">${account.code} - ${account.name}</div>
%for partner_name, p_id, p_ref, p_name in account.partners_order:
<%
total_debit = 0.0
@ -18,7 +18,7 @@
cumul_balance_curr = 0.0
part_cumul_balance = 0.0
part_cumul_balance_curr = 0.0
part_cumul_balance_curr = 0.0
%>
<div class="act_as_table list_table" style="margin-top: 5px;">
<div class="act_as_caption account_title">

1
account_financial_report_webkit/report/webkit_parser_header_fix.py

@ -160,7 +160,6 @@ class HeaderFooterTextWebKitParser(webkit_report.WebKitParser):
# override needed to keep the attachments' storing procedure
def create_single_pdf(self, cursor, uid, ids, data, report_xml, context=None):
"""generate the PDF"""
if context is None:
context={}
htmls = []

4
account_financial_report_webkit/report_menus.xml

@ -18,6 +18,10 @@
parent="account.next_id_22" action="action_account_partner_balance_menu_webkit"
groups="account.group_account_manager,account.group_account_user" id="account.menu_account_partner_balance_report"/>
<menuitem icon="STOCK_PRINT" name="Aged Partner Balance"
parent="account.next_id_22" action="action_account_aged_trial_balance_menu_webkit"
groups="account.group_account_manager,account.group_account_user" id="account.menu_aged_trial_balance"/>
<menuitem icon="STOCK_PRINT" name="Open Invoices"
parent="account.next_id_22" action="action_account_open_invoices_menu_webkit"
groups="account.group_account_manager,account.group_account_user" id="menu_account_open_invoices"/>

60
account_financial_report_webkit/tests/aged_trial_balance.yml

@ -0,0 +1,60 @@
-
In order to test the PDF Aged Partner Balance Report webkit wizard I will print report with default setting
-
!python {model: account.account}: |
from datetime import datetime
ctx={}
data_dict = {'chart_account_id':ref('account.chart0'), 'until_date': '%s-12-31' %(datetime.now().year)}
from tools import test_reports
test_reports.try_report_action(cr, uid, 'action_account_aged_trial_balance_menu_webkit',wiz_data=data_dict, context=ctx, our_module='account_financial_report_webkit')
-
In order to test the PDF Aged Partner Balance Report webkit wizard I will print report with filters and currency
-
!python {model: account.account}: |
from datetime import datetime
ctx={}
data_dict = {'chart_account_id':ref('account.chart0'), 'fiscalyear_id': ref('account.data_fiscalyear'),
'until_date': '%s-12-31' %(datetime.now().year), 'target_move': 'posted',
'amount_currency': True, 'result_selection': 'customer_supplier'}
from tools import test_reports
test_reports.try_report_action(cr, uid, 'action_account_aged_trial_balance_menu_webkit',wiz_data=data_dict, context=ctx, our_module='account_financial_report_webkit')
-
In order to test the PDF Aged Partner Balance Report webkit wizard I will print report with filters on partners
-
!python {model: account.account}: |
from datetime import datetime
ctx={}
data_dict = {'chart_account_id':ref('account.chart0'), 'fiscalyear_id': ref('account.data_fiscalyear'),
'until_date': '%s-12-31' %(datetime.now().year), 'target_move': 'posted',
'amount_currency': True, 'result_selection': 'customer_supplier',
'partner_ids': [ref('base.res_partner_2'), ref('base.res_partner_1')]}
from tools import test_reports
test_reports.try_report_action(cr, uid, 'action_account_aged_trial_balance_menu_webkit',wiz_data=data_dict, context=ctx, our_module='account_financial_report_webkit')
-
In order to test the PDF Aged Partner Balance Report webkit wizard I will print report with filters on periods
-
!python {model: account.account}: |
from datetime import datetime
ctx={}
data_dict = {'chart_account_id':ref('account.chart0'), 'fiscalyear_id': ref('account.data_fiscalyear'),
'until_date': '%s-12-31' %(datetime.now().year), 'target_move': 'posted',
'amount_currency': True, 'result_selection': 'customer_supplier',
'filter': 'filter_period', 'period_from': ref('account.period_1'), 'period_to': ref('account.period_12')}
from tools import test_reports
test_reports.try_report_action(cr, uid, 'action_account_aged_trial_balance_menu_webkit',wiz_data=data_dict, context=ctx, our_module='account_financial_report_webkit')
-
In order to test the PDF Aged Partner Balance Report webkit wizard I will print report with filters on dates
-
!python {model: account.account}: |
from datetime import datetime
ctx={}
data_dict = {'chart_account_id':ref('account.chart0'), 'fiscalyear_id': ref('account.data_fiscalyear'),
'until_date': '%s-12-31' %(datetime.now().year), 'target_move': 'posted',
'amount_currency': True, 'result_selection': 'customer_supplier',
'filter': 'filter_date', 'date_from': '%s-01-01' %(datetime.now().year), 'date_to': '%s-12-31' %(datetime.now().year)}
from tools import test_reports
test_reports.try_report_action(cr, uid, 'action_account_aged_trial_balance_menu_webkit',wiz_data=data_dict, context=ctx, our_module='account_financial_report_webkit')

1
account_financial_report_webkit/wizard/__init__.py

@ -27,3 +27,4 @@ from . import trial_balance_wizard
from . import partner_balance_wizard
from . import open_invoices_wizard
from . import print_journal
from . import aged_partner_balance_wizard

39
account_financial_report_webkit/wizard/aged_partner_balance_wizard.py

@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Nicolas Bessi
# Copyright 2014 Camptocamp SA
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import orm
class AccountAgedTrialBalance(orm.TransientModel):
"""Will launch age partner balance report.
This report is based on Open Invoice Report
and share a lot of knowledge with him
"""
_inherit = "open.invoices.webkit"
_name = "account.aged.trial.balance.webkit"
_description = "Aged partner balanced"
def _print_report(self, cr, uid, ids, data, context=None):
# we update form with display account value
data = self.pre_print_report(cr, uid, ids, data, context=context)
return {'type': 'ir.actions.report.xml',
'report_name': 'account.account_aged_trial_balance_webkit',
'datas': data}

72
account_financial_report_webkit/wizard/aged_partner_balance_wizard.xml

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="account_aged_trial_balance_webkit" model="ir.ui.view">
<field name="name">Aged Partner Balance Report</field>
<field name="model">account.aged.trial.balance.webkit</field>
<field name="inherit_id" ref="account.account_common_report_view"/>
<field name="arch" type="xml">
<data>
<xpath expr="/form/label[@string='']" position="replace">
<separator string="Aged Partner Balance" colspan="4"/>
<label nolabel="1"
colspan="4"
string="This report list partner open balances and indicate when payment is (or was) supposed to be completed"/>
</xpath>
<field name="chart_account_id" position='attributes'>
<attribute name="colspan">4</attribute>
</field>
<xpath expr="//field[@name='target_move']" position="after">
<newline/>
<field name="result_selection" colspan="4"/>
</xpath>
<xpath expr="/form/notebook[1]" position="after">
<separator string="Clearance Analysis Options" colspan="4"/>
<newline/>
<field name="until_date"/>
</xpath>
<page name="filters" position="after">
<page string="Partners Filters" name="partners">
<separator string="Print only" colspan="4"/>
<field name="partner_ids" colspan="4" nolabel="1"/>
</page>
</page>
<page name="filters" position="attributes">
<attribute name="string">Time Filters</attribute>
</page>
<page name="journal_ids" position="attributes">
<attribute name="invisible">True</attribute>
</page>
<field name="fiscalyear_id" position="attributes">
<attribute name="on_change">onchange_fiscalyear(fiscalyear_id, period_to, date_to, until_date)</attribute>
</field>
<field name="date_to" position="attributes">
<attribute name="on_change">onchange_date_to(fiscalyear_id, period_to, date_to, until_date)</attribute>
</field>
<field name="period_to" position="attributes">
<attribute name="on_change">onchange_period_to(fiscalyear_id, period_to, date_to, until_date)</attribute>
</field>
<field name="period_from" position="attributes">
<attribute name="domain">[('fiscalyear_id', '=', fiscalyear_id), ('special', '=', False)]</attribute>
</field>
<field name="period_to" position="attributes">
<attribute name="domain">[('fiscalyear_id', '=', fiscalyear_id), ('special', '=', False)]</attribute>
</field>
</data>
</field>
</record>
<record id="action_account_aged_trial_balance_menu_webkit"
model="ir.actions.act_window">
<field name="name">Aged partner balance</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">account.aged.trial.balance.webkit</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="view_id" ref="account_aged_trial_balance_webkit"/>
<field name="target">new</field>
</record>
</data>
</openerp>
Loading…
Cancel
Save