[ADD] Aged Partner Balance webkit report. Report inherit Open Invoice Report and uses previously computed ledger lines to determin aged lines
commit
33e7da97fe
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
)
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"/>
|
||||
|
|
|
@ -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('-', '‑')
|
||||
%>
|
||||
<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>
|
|
@ -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">
|
||||
|
|
|
@ -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 = []
|
||||
|
|
|
@ -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"/>
|
||||
|
|
|
@ -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')
|
|
@ -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
|
||||
|
|
|
@ -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}
|
|
@ -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…
Reference in New Issue