[IMP] account: Cleanup remaining stuff of Aged Partner Balance

This report is only available in enterprise edition. This report is now entirely
moved in account_reports.

This report is no longer available in community since:
https://github.com/odoo/odoo/commit/e51b4f659fc5e4253d3156754ada21166c8d1bf8

closes odoo/odoo#38502

Signed-off-by: Quentin De Paoli (qdp) <qdp@openerp.com>
This commit is contained in:
Laurent Smet
2019-10-17 09:14:33 +00:00
parent 9387952214
commit ebac65bebf
5 changed files with 0 additions and 488 deletions
-1
View File
@@ -3,5 +3,4 @@
from . import account_invoice_report
from . import account_journal
from . import account_aged_partner_balance
from . import account_hash_integrity_templates
@@ -1,262 +0,0 @@
# -*- coding: utf-8 -*-
import time
from odoo import api, fields, models, _
from odoo.exceptions import UserError
from odoo.tools import float_is_zero
from datetime import datetime
from dateutil.relativedelta import relativedelta
class ReportAgedPartnerBalance(models.AbstractModel):
_name = 'report.account.report_agedpartnerbalance'
_description = 'Aged Partner Balance Report'
def _get_partner_move_lines(self, account_type, date_from, target_move, period_length):
# This method can receive the context key 'include_nullified_amount' {Boolean}
# Do an invoice and a payment and unreconcile. The amount will be nullified
# By default, the partner wouldn't appear in this report.
# The context key allow it to appear
# In case of a period_length of 30 days as of 2019-02-08, we want the following periods:
# Name Stop Start
# 1 - 30 : 2019-02-07 - 2019-01-09
# 31 - 60 : 2019-01-08 - 2018-12-10
# 61 - 90 : 2018-12-09 - 2018-11-10
# 91 - 120 : 2018-11-09 - 2018-10-11
# +120 : 2018-10-10
ctx = self._context
periods = {}
date_from = fields.Date.from_string(date_from)
start = date_from
for i in range(5)[::-1]:
stop = start - relativedelta(days=period_length)
period_name = str((5-(i+1)) * period_length + 1) + '-' + str((5-i) * period_length)
period_stop = (start - relativedelta(days=1)).strftime('%Y-%m-%d')
if i == 0:
period_name = '+' + str(4 * period_length)
periods[str(i)] = {
'name': period_name,
'stop': period_stop,
'start': (i!=0 and stop.strftime('%Y-%m-%d') or False),
}
start = stop
res = []
total = []
partner_clause = ''
cr = self.env.cr
user_company = self.env.company
user_currency = user_company.currency_id
company_ids = self._context.get('company_ids') or [user_company.id]
move_state = ['draft', 'posted']
if target_move == 'posted':
move_state = ['posted']
arg_list = (tuple(move_state), tuple(account_type))
#build the reconciliation clause to see what partner needs to be printed
reconciliation_clause = '(l.reconciled IS FALSE)'
cr.execute('SELECT debit_move_id, credit_move_id FROM account_partial_reconcile where max_date > %s', (date_from,))
reconciled_after_date = []
for row in cr.fetchall():
reconciled_after_date += [row[0], row[1]]
if reconciled_after_date:
reconciliation_clause = '(l.reconciled IS FALSE OR l.id IN %s)'
arg_list += (tuple(reconciled_after_date),)
if ctx.get('partner_ids'):
partner_clause = 'AND (l.partner_id IN %s)'
arg_list += (tuple(ctx['partner_ids'].ids),)
if ctx.get('partner_categories'):
partner_clause += 'AND (l.partner_id IN %s)'
partner_ids = self.env['res.partner'].search([('category_id', 'in', ctx['partner_categories'].ids)]).ids
arg_list += (tuple(partner_ids or [0]),)
arg_list += (date_from, tuple(company_ids))
query = '''
SELECT DISTINCT l.partner_id, res_partner.name AS name, UPPER(res_partner.name) AS UPNAME, CASE WHEN prop.value_text IS NULL THEN 'normal' ELSE prop.value_text END AS trust
FROM account_move_line AS l
LEFT JOIN res_partner ON l.partner_id = res_partner.id
LEFT JOIN ir_property prop ON (prop.res_id = 'res.partner,'||res_partner.id AND prop.name='trust' AND prop.company_id=%s),
account_account, account_move am
WHERE (l.account_id = account_account.id)
AND (l.move_id = am.id)
AND (am.state IN %s)
AND (account_account.internal_type IN %s)
AND ''' + reconciliation_clause + partner_clause + '''
AND (l.date <= %s)
AND l.company_id IN %s
ORDER BY UPPER(res_partner.name)'''
arg_list = (self.env.company.id,) + arg_list
cr.execute(query, arg_list)
partners = cr.dictfetchall()
# put a total of 0
for i in range(7):
total.append(0)
# Build a string like (1,2,3) for easy use in SQL query
partner_ids = [partner['partner_id'] for partner in partners if partner['partner_id']]
lines = dict((partner['partner_id'] or False, []) for partner in partners)
if not partner_ids:
return [], [], {}
# Use one query per period and store results in history (a list variable)
# Each history will contain: history[1] = {'<partner_id>': <partner_debit-credit>}
history = []
for i in range(5):
args_list = (tuple(move_state), tuple(account_type), tuple(partner_ids),)
dates_query = '(COALESCE(l.date_maturity,l.date)'
if periods[str(i)]['start'] and periods[str(i)]['stop']:
dates_query += ' BETWEEN %s AND %s)'
args_list += (periods[str(i)]['start'], periods[str(i)]['stop'])
elif periods[str(i)]['start']:
dates_query += ' >= %s)'
args_list += (periods[str(i)]['start'],)
else:
dates_query += ' <= %s)'
args_list += (periods[str(i)]['stop'],)
args_list += (date_from, tuple(company_ids))
query = '''SELECT l.id
FROM account_move_line AS l, account_account, account_move am
WHERE (l.account_id = account_account.id) AND (l.move_id = am.id)
AND (am.state IN %s)
AND (account_account.internal_type IN %s)
AND ((l.partner_id IN %s) OR (l.partner_id IS NULL))
AND ''' + dates_query + '''
AND (l.date <= %s)
AND l.company_id IN %s
ORDER BY COALESCE(l.date_maturity, l.date)'''
cr.execute(query, args_list)
partners_amount = {}
aml_ids = cr.fetchall()
aml_ids = aml_ids and [x[0] for x in aml_ids] or []
for line in self.env['account.move.line'].browse(aml_ids).with_context(prefetch_fields=False):
partner_id = line.partner_id.id or False
if partner_id not in partners_amount:
partners_amount[partner_id] = 0.0
line_amount = line.company_id.currency_id._convert(line.balance, user_currency, user_company, date_from)
if user_currency.is_zero(line_amount):
continue
for partial_line in line.matched_debit_ids:
if partial_line.max_date <= date_from:
line_amount += partial_line.company_id.currency_id._convert(partial_line.amount, user_currency, user_company, date_from)
for partial_line in line.matched_credit_ids:
if partial_line.max_date <= date_from:
line_amount -= partial_line.company_id.currency_id._convert(partial_line.amount, user_currency, user_company, date_from)
if not self.env.company.currency_id.is_zero(line_amount):
partners_amount[partner_id] += line_amount
lines.setdefault(partner_id, [])
lines[partner_id].append({
'line': line,
'amount': line_amount,
'period': i + 1,
})
history.append(partners_amount)
# This dictionary will store the not due amount of all partners
undue_amounts = {}
query = '''SELECT l.id
FROM account_move_line AS l, account_account, account_move am
WHERE (l.account_id = account_account.id) AND (l.move_id = am.id)
AND (am.state IN %s)
AND (account_account.internal_type IN %s)
AND (COALESCE(l.date_maturity,l.date) >= %s)\
AND ((l.partner_id IN %s) OR (l.partner_id IS NULL))
AND (l.date <= %s)
AND l.company_id IN %s
ORDER BY COALESCE(l.date_maturity, l.date)'''
cr.execute(query, (tuple(move_state), tuple(account_type), date_from, tuple(partner_ids), date_from, tuple(company_ids)))
aml_ids = cr.fetchall()
aml_ids = aml_ids and [x[0] for x in aml_ids] or []
for line in self.env['account.move.line'].browse(aml_ids):
partner_id = line.partner_id.id or False
if partner_id not in undue_amounts:
undue_amounts[partner_id] = 0.0
line_amount = line.company_id.currency_id._convert(line.balance, user_currency, user_company, date_from)
if user_currency.is_zero(line_amount):
continue
for partial_line in line.matched_debit_ids:
if partial_line.max_date <= date_from:
line_amount += partial_line.company_id.currency_id._convert(partial_line.amount, user_currency, user_company, date_from)
for partial_line in line.matched_credit_ids:
if partial_line.max_date <= date_from:
line_amount -= partial_line.company_id.currency_id._convert(partial_line.amount, user_currency, user_company, date_from)
if not self.env.company.currency_id.is_zero(line_amount):
undue_amounts[partner_id] += line_amount
lines.setdefault(partner_id, [])
lines[partner_id].append({
'line': line,
'amount': line_amount,
'period': 6,
})
for partner in partners:
if partner['partner_id'] is None:
partner['partner_id'] = False
at_least_one_amount = False
values = {}
undue_amt = 0.0
if partner['partner_id'] in undue_amounts: # Making sure this partner actually was found by the query
undue_amt = undue_amounts[partner['partner_id']]
total[6] = total[6] + undue_amt
values['direction'] = undue_amt
if not float_is_zero(values['direction'], precision_rounding=self.env.company.currency_id.rounding):
at_least_one_amount = True
for i in range(5):
during = False
if partner['partner_id'] in history[i]:
during = [history[i][partner['partner_id']]]
# Adding counter
total[(i)] = total[(i)] + (during and during[0] or 0)
values[str(i)] = during and during[0] or 0.0
if not float_is_zero(values[str(i)], precision_rounding=self.env.company.currency_id.rounding):
at_least_one_amount = True
values['total'] = sum([values['direction']] + [values[str(i)] for i in range(5)])
# Add for total
total[(i + 1)] += values['total']
values['partner_id'] = partner['partner_id']
if partner['partner_id']:
values['name'] = len(partner['name']) >= 45 and partner['name'][0:40] + '...' or partner['name']
values['trust'] = partner['trust']
else:
values['name'] = _('Unknown Partner')
values['trust'] = False
if at_least_one_amount or (self._context.get('include_nullified_amount') and lines[partner['partner_id']]):
res.append(values)
return res, total, lines
@api.model
def _get_report_values(self, docids, data=None):
if not data.get('form') or not self.env.context.get('active_model') or not self.env.context.get('active_id'):
raise UserError(_("Form content is missing, this report cannot be printed."))
total = []
model = self.env.context.get('active_model')
docs = self.env[model].browse(self.env.context.get('active_id'))
target_move = data['form'].get('target_move', 'all')
date_from = fields.Date.from_string(data['form'].get('date_from')) or fields.Date.today()
if data['form']['result_selection'] == 'customer':
account_type = ['receivable']
elif data['form']['result_selection'] == 'supplier':
account_type = ['payable']
else:
account_type = ['payable', 'receivable']
movelines, total, dummy = self._get_partner_move_lines(account_type, date_from, target_move, data['form']['period_length'])
return {
'doc_ids': self.ids,
'doc_model': model,
'data': data['form'],
'docs': docs,
'time': time,
'get_partner_lines': movelines,
'get_direction': total,
'company_id': self.env['res.company'].browse(
data['form']['company_id'][0]),
}
-120
View File
@@ -915,48 +915,6 @@ class TestReconciliationExec(TestReconciliation):
self.assertEqual(reversed_customer_line.full_reconcile_id.id, customer_line.full_reconcile_id.id)
def test_aged_report(self):
AgedReport = self.env['report.account.report_agedpartnerbalance'].with_context(include_nullified_amount=True)
account_type = ['receivable']
report_date_to = time.strftime('%Y') + '-07-17'
partner = self.env['res.partner'].create({'name': 'AgedPartner'})
currency = self.env.company.currency_id
invoice = self.create_invoice_partner(currency_id=currency.id, partner_id=partner.id)
journal = self.env['account.journal'].create({'name': 'Bank', 'type': 'bank', 'code': 'THE'})
statement = self.make_payment(invoice, journal, 50)
# The report searches on the create_date to dispatch reconciled lines to report periods
# Also, in this case, there can be only 1 partial_reconcile
statement_partial_id = statement.move_line_ids.mapped(lambda l: l.matched_credit_ids + l.matched_debit_ids)
self.env.cr.execute('UPDATE account_partial_reconcile SET create_date = %(date)s WHERE id = %(partial_id)s',
{'date': report_date_to + ' 00:00:00',
'partial_id': statement_partial_id.id})
# Case 1: The invoice and payment are reconciled: Nothing should appear
report_lines, total, amls = AgedReport._get_partner_move_lines(account_type, report_date_to, 'posted', 30)
partner_lines = [line for line in report_lines if line['partner_id'] == partner.id]
self.assertEqual(partner_lines, [], 'The aged receivable shouldn\'t have lines at this point')
self.assertFalse(amls.get(partner.id, False), 'The aged receivable should not have amls either')
# Case 2: The invoice and payment are not reconciled: we should have one line on the report
# and 2 amls
invoice.line_ids.with_context(invoice_id=invoice.id).remove_move_reconcile()
report_lines, total, amls = AgedReport._get_partner_move_lines(account_type, report_date_to, 'posted', 30)
partner_lines = [line for line in report_lines if line['partner_id'] == partner.id]
self.assertEqual(partner_lines, [{'trust': 'normal', '1': 0.0, '0': 0.0, 'direction': 0.0, 'partner_id': partner.id, '3': 0.0, 'total': 0.0, 'name': 'AgedPartner', '4': 0.0, '2': 0.0}],
'We should have a line in the report for the partner')
self.assertEqual(len(amls[partner.id]), 2, 'We should have 2 account move lines for the partner')
positive_line = [line for line in amls[partner.id] if line['line'].balance > 0]
negative_line = [line for line in amls[partner.id] if line['line'].balance < 0]
self.assertEqual(positive_line[0]['amount'], 50.0, 'The amount of the amls should be 50')
self.assertEqual(negative_line[0]['amount'], -50.0, 'The amount of the amls should be -50')
def test_revert_payment_and_reconcile_exchange(self):
# A reversal of a reconciled payment which created a currency exchange entry, should create reversal moves
@@ -1025,84 +983,6 @@ class TestReconciliationExec(TestReconciliation):
_move_revert_test_pair(payment_move, reverted_payment_move)
_move_revert_test_pair(exchange_move, reverted_exchange_move)
def test_aged_report_future_payment(self):
AgedReport = self.env['report.account.report_agedpartnerbalance'].with_context(include_nullified_amount=True)
account_type = ['receivable']
partner = self.env['res.partner'].create({'name': 'AgedPartner'})
currency = self.env.company.currency_id
invoice = self.create_invoice_partner(currency_id=currency.id, partner_id=partner.id)
journal = self.env['account.journal'].create({'name': 'Bank', 'type': 'bank', 'code': 'THE'})
statement = self.make_payment(invoice, journal, 50)
# Force the payment recording to take place on the invoice date
# Although the payment due_date is in the future relative to the invoice
# Also, in this case, there can be only 1 partial_reconcile
statement_partial_id = statement.move_line_ids.mapped(lambda l: l.matched_credit_ids + l.matched_debit_ids)
self.env.cr.execute('UPDATE account_partial_reconcile SET create_date = %(date)s WHERE id = %(partial_id)s',
{'date': invoice.invoice_date,
'partial_id': statement_partial_id.id})
statement.flush()
# Case 1: report date is invoice date
# There should be an entry for the partner
report_date_to = invoice.invoice_date
report_lines, total, amls = AgedReport._get_partner_move_lines(account_type, report_date_to, 'posted', 30)
partner_lines = [line for line in report_lines if line['partner_id'] == partner.id]
self.assertEqual(partner_lines, [{
'name': 'AgedPartner',
'trust': 'normal',
'partner_id': partner.id,
'0': 0.0,
'1': 0.0,
'2': 0.0,
'3': 0.0,
'4': 0.0,
'total': 50.0,
'direction': 50.0,
}], 'We should have a line in the report for the partner')
self.assertEqual(len(amls[partner.id]), 1, 'We should have 1 account move lines for the partner')
positive_line = [line for line in amls[partner.id] if line['line'].balance > 0]
self.assertEqual(positive_line[0]['amount'], 50.0, 'The amount of the amls should be 50')
# Case 2: report date between invoice date and payment date
# There should be an entry for the partner
# And the amount has shifted to '1-30 due'
report_date_to = time.strftime('%Y') + '-07-08'
report_lines, total, amls = AgedReport._get_partner_move_lines(account_type, report_date_to, 'posted', 30)
partner_lines = [line for line in report_lines if line['partner_id'] == partner.id]
self.assertEqual(partner_lines, [{
'name': 'AgedPartner',
'trust': 'normal',
'partner_id': partner.id,
'0': 0.0,
'1': 0.0,
'2': 0.0,
'3': 0.0,
'4': 50.0,
'total': 50.0,
'direction': 0.0,
}], 'We should have a line in the report for the partner')
self.assertEqual(len(amls[partner.id]), 1, 'We should have 1 account move lines for the partner')
positive_line = [line for line in amls[partner.id] if line['line'].balance > 0]
self.assertEqual(positive_line[0]['amount'], 50.0, 'The amount of the amls should be 50')
# Case 2: report date on payment date
# There should not be an entry for the partner
report_date_to = time.strftime('%Y') + '-07-15'
report_lines, total, amls = AgedReport._get_partner_move_lines(account_type, report_date_to, 'posted', 30)
partner_lines = [line for line in report_lines if line['partner_id'] == partner.id]
self.assertEqual(partner_lines, [], 'The aged receivable shouldn\'t have lines at this point')
self.assertFalse(amls.get(partner.id, False), 'The aged receivable should not have amls either')
def test_partial_reconcile_currencies_02(self):
####
# Day 1: Invoice Cust/001 to customer (expressed in USD)
-10
View File
@@ -37,16 +37,6 @@
print_report_name="(object._get_report_base_filename())"
/>
<report
id="action_report_aged_partner_balance"
model="res.partner"
string="Aged Partner Balance"
menu="False"
report_type="qweb-pdf"
name="account.report_agedpartnerbalance"
file="account.report_agedpartnerbalance"
/>
<report
id="action_report_journal"
model="account.common.journal.report"
@@ -1,95 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="report_agedpartnerbalance">
<t t-call="web.html_container">
<t t-call="web.internal_layout">
<div class="page">
<h2>Aged Partner Balance</h2>
<div class="row mt32">
<div class="col-3">
<strong>Start Date:</strong>
<p t-esc="data['date_from']"/>
</div>
<div class="col-3">
<strong>Period Length (days)</strong>
<p t-esc="data['period_length']"/>
</div>
</div>
<div class="row mb32">
<div class="col-3">
<strong>Partner's:</strong>
<p>
<span t-if="data['result_selection'] == 'customer'">Receivable Accounts</span>
<span t-if="data['result_selection'] == 'supplier'">Payable Accounts</span>
<span t-if="data['result_selection'] == 'customer_supplier'">Receivable and Payable Accounts</span>
</p>
</div>
<div class="col-3">
<strong>Target Moves:</strong>
<p>
<span t-if="data['target_move'] == 'all'">All Entries</span>
<span t-if="data['target_move'] == 'posted'">All Posted Entries</span>
</p>
</div>
</div>
<table class="table table-sm">
<thead>
<tr>
<th><span>Partners</span></th>
<th class="text-right">
<span>Not due</span>
</th>
<th class="text-right"><span t-esc="data['4']['name']"/></th>
<th class="text-right"><span t-esc="data['3']['name']"/></th>
<th class="text-right"><span t-esc="data['2']['name']"/></th>
<th class="text-right"><span t-esc="data['1']['name']"/></th>
<th class="text-right"><span t-esc="data['0']['name']"/></th>
<th class="text-right"><span>Total</span></th>
</tr>
<tr t-if="get_partner_lines">
<th>Account Total</th>
<th class="text-right"><span t-esc="get_direction[6]" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/></th>
<th class="text-right"><span t-esc="get_direction[4]" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/></th>
<th class="text-right"><span t-esc="get_direction[3]" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/></th>
<th class="text-right"><span t-esc="get_direction[2]" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/></th>
<th class="text-right"><span t-esc="get_direction[1]" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/></th>
<th class="text-right"><span t-esc="get_direction[0]" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/></th>
<th class="text-right"><span t-esc="get_direction[5]" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/></th>
</tr>
</thead>
<tbody>
<tr t-foreach="get_partner_lines" t-as="partner">
<td>
<span t-esc="partner['name']"/>
</td>
<td class="text-right">
<span t-esc="partner['direction']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
</td>
<td class="text-right">
<span t-esc="partner['4']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
</td>
<td class="text-right">
<span t-esc="partner['3']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
</td>
<td class="text-right">
<span t-esc="partner['2']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
</td>
<td class="text-right">
<span t-esc="partner['1']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
</td>
<td class="text-right">
<span t-esc="partner['0']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
</td>
<td class="text-right">
<span t-esc="partner['total']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
</td>
</tr>
</tbody>
</table>
</div>
</t>
</t>
</template>
</odoo>