Files
enterprise-addons/account_reports/tests/test_partner_ledger_report.py
T

1012 lines
60 KiB
Python

# pylint: disable=C0326
from .common import TestAccountReportsCommon
from odoo import Command, fields
from odoo.tests import tagged
import json
@tagged('post_install', '-at_install')
class TestPartnerLedgerReport(TestAccountReportsCommon):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.partner_category_a = cls.env['res.partner.category'].create({'name': 'partner_categ_a'})
cls.partner_category_b = cls.env['res.partner.category'].create({'name': 'partner_categ_b'})
cls.partner_a.write({'category_id': [Command.set([cls.partner_category_a.id, cls.partner_category_b.id])]})
cls.partner_b.write({'category_id': [Command.set([cls.partner_category_a.id])]})
cls.partner_c = cls._create_partner(name='partner_c', category_id=[Command.set([cls.partner_category_b.id])])
# Entries in 2016 for company_1 to test the initial balance.
cls.move_2016_1 = cls.env['account.move'].create({
'move_type': 'entry',
'date': fields.Date.from_string('2016-01-01'),
'journal_id': cls.company_data['default_journal_misc'].id,
'line_ids': [
(0, 0, {'debit': 100.0, 'credit': 0.0, 'name': '2016_1_1', 'account_id': cls.company_data['default_account_payable'].id, 'partner_id': cls.partner_a.id}),
(0, 0, {'debit': 200.0, 'credit': 0.0, 'name': '2016_1_1', 'account_id': cls.company_data['default_account_payable'].id, 'partner_id': cls.partner_b.id}),
(0, 0, {'debit': 0.0, 'credit': 300.0, 'name': '2016_1_2', 'account_id': cls.company_data['default_account_receivable'].id, 'partner_id': cls.partner_c.id}),
],
})
cls.move_2016_1.action_post()
# Entries in 2016 for company_2 to test the initial balance in multi-companies/multi-currencies.
cls.move_2016_2 = cls.env['account.move'].create({
'move_type': 'entry',
'date': fields.Date.from_string('2016-06-01'),
'journal_id': cls.company_data_2['default_journal_misc'].id,
'line_ids': [
(0, 0, {'debit': 100.0, 'credit': 0.0, 'name': '2016_2_1', 'account_id': cls.company_data_2['default_account_payable'].id, 'partner_id': cls.partner_a.id}),
(0, 0, {'debit': 0.0, 'credit': 100.0, 'name': '2016_2_2', 'account_id': cls.company_data_2['default_account_receivable'].id, 'partner_id': cls.partner_c.id}),
],
})
cls.move_2016_2.action_post()
# Entry in 2017 for company_1 to test the report at current date.
cls.move_2017_1 = cls.env['account.move'].create({
'move_type': 'entry',
'date': fields.Date.from_string('2017-01-01'),
'journal_id': cls.company_data['default_journal_misc'].id,
'line_ids': [
(0, 0, {'debit': 1000.0, 'credit': 0.0, 'name': '2017_1_1', 'account_id': cls.company_data['default_account_payable'].id, 'partner_id': cls.partner_b.id}),
(0, 0, {'debit': 2000.0, 'credit': 0.0, 'name': '2017_1_2', 'account_id': cls.company_data['default_account_payable'].id, 'partner_id': cls.partner_a.id}),
(0, 0, {'debit': 3000.0, 'credit': 0.0, 'name': '2017_1_3', 'account_id': cls.company_data['default_account_payable'].id, 'partner_id': cls.partner_a.id}),
(0, 0, {'debit': 4000.0, 'credit': 0.0, 'name': '2017_1_4', 'account_id': cls.company_data['default_account_receivable'].id, 'partner_id': cls.partner_a.id}),
(0, 0, {'debit': 5000.0, 'credit': 0.0, 'name': '2017_1_5', 'account_id': cls.company_data['default_account_receivable'].id, 'partner_id': cls.partner_a.id}),
(0, 0, {'debit': 6000.0, 'credit': 0.0, 'name': '2017_1_6', 'account_id': cls.company_data['default_account_receivable'].id, 'partner_id': cls.partner_a.id}),
(0, 0, {'debit': 0.0, 'credit': 6000.0, 'name': '2017_1_7', 'account_id': cls.company_data['default_account_receivable'].id, 'partner_id': cls.partner_c.id}),
(0, 0, {'debit': 0.0, 'credit': 7000.0, 'name': '2017_1_8', 'account_id': cls.company_data['default_account_receivable'].id, 'partner_id': cls.partner_c.id}),
(0, 0, {'debit': 0.0, 'credit': 8000.0, 'name': '2017_1_9', 'account_id': cls.company_data['default_account_receivable'].id, 'partner_id': cls.partner_c.id}),
],
})
cls.move_2017_1.action_post()
# Entry in 2017 for company_2 to test the current period in multi-companies/multi-currencies.
cls.move_2017_2 = cls.env['account.move'].create({
'move_type': 'entry',
'date': fields.Date.from_string('2017-06-01'),
'journal_id': cls.company_data_2['default_journal_misc'].id,
'line_ids': [
(0, 0, {'debit': 400.0, 'credit': 0.0, 'name': '2017_2_1', 'account_id': cls.company_data_2['default_account_receivable'].id}),
(0, 0, {'debit': 0.0, 'credit': 400.0, 'name': '2017_2_2', 'account_id': cls.company_data_2['default_account_receivable'].id}),
],
})
cls.move_2017_2.action_post()
cls.report = cls.env.ref('account_reports.partner_ledger_report')
def test_partner_ledger_send_recipients_multi_currency(self):
''' Computing the recipients when sending the report by email runs the sums
query, which joins the currency table. In a multi-currency setup that table
is a temporary table; make sure it is created first so the query does not
crash with a missing "account_currency_table" relation. '''
report = self.report.with_context(
allowed_company_ids=(self.company_data['company'] + self.company_data_2['company']).ids
)
options = self._generate_options(report, fields.Date.from_string('2017-01-01'), fields.Date.from_string('2017-12-31'))
# Two companies with different currencies must use the (temporary) currency table.
self.assertNotEqual(options['currency_table']['type'], 'monocurrency')
# Without initializing the currency table this raises psycopg2.errors.UndefinedTable.
recipients = report._get_report_send_recipients(options)
self.assertEqual(
sorted(recipients.mapped('name')),
['partner_a', 'partner_b', 'partner_c'],
)
def test_partner_ledger_unfold(self):
''' Test unfolding a line when rendering the whole report. '''
options = self._generate_options(self.report, fields.Date.from_string('2017-01-01'), fields.Date.from_string('2017-12-31'))
self.assertLinesValues(
self.report._get_lines(options),
# Name Debit Credit Balance
[ 0, 6, 7, 9],
[
('partner_a', 20000.0, 0.0, 20150.0),
('partner_b', 1000.0, 0.0, 1200.0),
('partner_c', 0.0, 21000.0, -21350.0),
('Unknown Partner', 200.0, 200.0, 0.0),
('Total', 21200.0, 21200.0, 0.0),
],
options,
)
options['unfolded_lines'] = [self.report._get_generic_line_id('res.partner', self.partner_a.id)]
self.assertLinesValues(
self.report._get_lines(options),
# Name Debit Credit Balance
[ 0, 6, 7, 9],
[
('partner_a', 20000.0, 0.0, 20150.0),
('Initial Balance', '', '', 150.0),
('MISC/2017/01/0001 2017_1_2', 2000.0, 0.0, 2150.0),
('MISC/2017/01/0001 2017_1_3', 3000.0, 0.0, 5150.0),
('MISC/2017/01/0001 2017_1_4', 4000.0, 0.0, 9150.0),
('MISC/2017/01/0001 2017_1_5', 5000.0, 0.0, 14150.0),
('MISC/2017/01/0001 2017_1_6', 6000.0, 0.0, 20150.0),
('Total partner_a', 20000.0, 0.0, 20150.0),
('partner_b', 1000.0, 0.0, 1200.0),
('partner_c', 0.0, 21000.0, -21350.0),
('Unknown Partner', 200.0, 200.0, 0.0),
('Total', 21200.0, 21200.0, 0.0),
],
options,
)
def test_partner_ledger_batch_data_generator(self):
""" Test the custom_unfold_all_batch_data_generator """
options = self._generate_options(
self.report,
fields.Date.from_string('2017-01-01'),
fields.Date.from_string('2017-12-31'),
default_options={'unfold_all': True}
)
self.assertLinesValues(
self.report._get_lines(options),
# Name Debit Credit Balance
[ 0, 6, 7, 9],
[
('partner_a', 20000.0, 0.0, 20150.0),
('Initial Balance', '', '', 150.0),
('MISC/2017/01/0001 2017_1_2', 2000.0, 0.0, 2150.0),
('MISC/2017/01/0001 2017_1_3', 3000.0, 0.0, 5150.0),
('MISC/2017/01/0001 2017_1_4', 4000.0, 0.0, 9150.0),
('MISC/2017/01/0001 2017_1_5', 5000.0, 0.0, 14150.0),
('MISC/2017/01/0001 2017_1_6', 6000.0, 0.0, 20150.0),
('Total partner_a', 20000.0, 0.0, 20150.0),
('partner_b', 1000.0, 0.0, 1200.0),
('Initial Balance', '', '', 200.0),
('MISC/2017/01/0001 2017_1_1', 1000.0, 0.0, 1200.0),
('Total partner_b', 1000.0, 0.0, 1200.0),
('partner_c', 0.0, 21000.0, -21350.0),
('Initial Balance', '', '', -350.0),
('MISC/2017/01/0001 2017_1_7', 0.0, 6000.0, -6350.0),
('MISC/2017/01/0001 2017_1_8', 0.0, 7000.0, -13350.0),
('MISC/2017/01/0001 2017_1_9', 0.0, 8000.0, -21350.0),
('Total partner_c', 0.0, 21000.0, -21350.0),
('Unknown Partner', 200.0, 200.0, 0.0),
('MISC/2017/06/0001 2017_2_1', 200.0, 0.0, 200.0),
('MISC/2017/06/0001 2017_2_2', 0.0, 200.0, 0.0),
('Total Unknown Partner', 200.0, 200.0, 0.0),
('Total', 21200.0, 21200.0, 0.0),
],
options,
)
def test_partner_ledger_load_more(self):
''' Test unfolding a line to use the load more. '''
self.report.load_more_limit = 2
options = self._generate_options(self.report, fields.Date.from_string('2017-01-01'), fields.Date.from_string('2017-12-31'))
options['unfolded_lines'] = [self.report._get_generic_line_id('res.partner', self.partner_a.id)]
report_lines = self.report._get_lines(options)
self.assertLinesValues(
report_lines,
# Name Debit Credit Balance
[ 0, 6, 7, 9],
[
('partner_a', 20000.0, 0.0, 20150.0),
('Initial Balance', '', '', 150.0),
('MISC/2017/01/0001 2017_1_2', 2000.0, 0.0, 2150.0),
('MISC/2017/01/0001 2017_1_3', 3000.0, 0.0, 5150.0),
('Load more...', '', '', ''),
('Total partner_a', 20000.0, 0.0, 20150.0),
('partner_b', 1000.0, 0.0, 1200.0),
('partner_c', 0.0, 21000.0, -21350.0),
('Unknown Partner', 200.0, 200.0, 0.0),
('Total', 21200.0, 21200.0, 0.0),
],
options,
)
load_more_1 = self.report.get_expanded_lines(
options,
report_lines[0]['id'],
report_lines[4]['groupby'],
'_report_expand_unfoldable_line_partner_ledger',
report_lines[4]['progress'],
report_lines[4]['offset'],
None,
)
self.assertLinesValues(
load_more_1,
# Name Debit Credit Balance
[ 0, 6, 7, 9],
[
('MISC/2017/01/0001 2017_1_4', 4000.0, 0.0, 9150.0),
('MISC/2017/01/0001 2017_1_5', 5000.0, 0.0, 14150.0),
('Load more...', '', '', ''),
],
options,
)
load_more_2 = self.report.get_expanded_lines(
options,
report_lines[0]['id'],
load_more_1[2]['groupby'],
'_report_expand_unfoldable_line_partner_ledger',
load_more_1[2]['progress'],
load_more_1[2]['offset'],
None,
)
self.assertLinesValues(
load_more_2,
# Name Debit Credit Balance
[ 0, 6, 7, 9],
[
('MISC/2017/01/0001 2017_1_6', 6000.0, 0.0, 20150.0),
],
options,
)
def test_partner_ledger_filter_account_types(self):
''' Test building the report with a filter on account types.
When filtering on receivable accounts (i.e. trade_receivable and/or non_trade_receivable), partner_b should disappear from the report.
'''
options = self._generate_options(self.report, fields.Date.from_string('2017-01-01'), fields.Date.from_string('2017-12-31'))
options['unfolded_lines'] = [self.report._get_generic_line_id('res.partner', self.partner_a.id)]
options = self._update_multi_selector_filter(options, 'account_type', ['non_trade_receivable', 'trade_receivable'])
self.assertLinesValues(
self.report._get_lines(options),
# Name Debit Credit Balance
[ 0, 6, 7, 9],
[
('partner_a', 15000.0, 0.0, 15000.0),
('MISC/2017/01/0001 2017_1_4', 4000.0, 0.0, 4000.0),
('MISC/2017/01/0001 2017_1_5', 5000.0, 0.0, 9000.0),
('MISC/2017/01/0001 2017_1_6', 6000.0, 0.0, 15000.0),
('Total partner_a', 15000.0, 0.0, 15000.0),
('partner_c', 0.0, 21000.0, -21350.0),
('Unknown Partner', 200.0, 200.0, 0.0),
('Total', 15200.0, 21200.0, -6350.0),
],
options,
)
def test_partner_ledger_filter_partners(self):
''' Test the filter on top allowing to filter on res.partner.'''
options = self._generate_options(self.report, fields.Date.from_string('2017-01-01'), fields.Date.from_string('2017-12-31'))
options['partner_ids'] = (self.partner_a + self.partner_c).ids
self.assertLinesValues(
self.report._get_lines(options),
# Name Debit Credit Balance
[ 0, 6, 7, 9],
[
('partner_a', 20000.0, 0.00, 20150.0),
('partner_c', 0.00, 21000.0, -21350.0),
('Total', 20000.0, 21000.0, -1200.00),
],
options,
)
def test_partner_ledger_filter_partner_categories(self):
''' Test the filter on top allowing to filter on res.partner.category.'''
options = self._generate_options(self.report, fields.Date.from_string('2017-01-01'), fields.Date.from_string('2017-12-31'))
options['partner_categories'] = self.partner_category_a.ids
self.assertLinesValues(
self.report._get_lines(options),
# Name Debit Credit Balance
[ 0, 6, 7, 9],
[
('partner_a', 20000.0, 0.0, 20150.0),
('partner_b', 1000.0, 0.0, 1200.0),
('Total', 21000.0, 0.0, 21350.0),
],
options,
)
def test_partner_ledger_unknown_partner(self):
''' Test the partner ledger for whenever a line appearing in it has no partner assigned.
Check that reconciling this line with an invoice/bill of a partner does affect his balance.
'''
options = self._generate_options(self.report, fields.Date.from_string('2017-01-01'), fields.Date.from_string('2017-12-31'))
misc_move = self.env['account.move'].create({
'date': '2017-03-31',
'line_ids': [
(0, 0, {'debit': 1000.0, 'credit': 0.0, 'account_id': self.company_data['default_account_revenue'].id}),
(0, 0, {'debit': 0.0, 'credit': 1000.0, 'account_id': self.company_data['default_account_receivable'].id}),
],
})
misc_move.action_post()
self.assertLinesValues(
self.report._get_lines(options),
# Name Debit Credit Balance
[ 0, 6, 7, 9],
[
('partner_a', 20000.0, 0.00, 20150.0),
('partner_b', 1000.00, 0.00, 1200.00),
('partner_c', 0.00, 21000.0, -21350.0),
('Unknown Partner', 200.00, 1200.00, -1000.00),
('Total', 21200.0, 22200.0, -1000.00),
],
options,
)
debit_line = self.move_2017_1.line_ids.filtered(lambda line: line.debit == 4000.0)
credit_line = misc_move.line_ids.filtered(lambda line: line.credit == 1000.0)
(debit_line + credit_line).reconcile()
self.assertLinesValues(
self.report._get_lines(options),
# Name Debit Credit Balance
[ 0, 6, 7, 9],
[
('partner_a', 20000.0, 1000.00, 19150.0),
('partner_b', 1000.00, 0.00, 1200.00),
('partner_c', 0.00, 21000.0, -21350.0),
('Unknown Partner', 1200.00, 1200.00, 0.00),
('Total', 22200.0, 23200.0, -1000.00),
],
options,
)
# Unfold 'partner_a'
options['unfolded_lines'] = [self.report._get_generic_line_id('res.partner', self.partner_a.id)]
self.assertLinesValues(
self.report._get_lines(options),
# Name Debit Credit Balance
[ 0, 6, 7, 9],
[
('partner_a', 20000.0, 1000.00, 19150.0),
('Initial Balance', '', '', 150.0),
('MISC/2017/01/0001 2017_1_2', 2000.00, 0.00, 2150.0),
('MISC/2017/01/0001 2017_1_3', 3000.00, 0.00, 5150.0),
('MISC/2017/01/0001 2017_1_4', 4000.00, 0.00, 9150.0),
('MISC/2017/01/0001 2017_1_5', 5000.00, 0.00, 14150.0),
('MISC/2017/01/0001 2017_1_6', 6000.00, 0.00, 20150.0),
('MISC/2017/03/0001', 0.00, 1000.00, 19150.0),
('Total partner_a', 20000.0, 1000.00, 19150.0),
('partner_b', 1000.00, 0.00, 1200.00),
('partner_c', 0.00, 21000.0, -21350.0),
('Unknown Partner', 1200.00, 1200.00, 0.00),
('Total', 22200.0, 23200.0, -1000.00),
],
options,
)
# Unfold 'Unknown Partner'
options['unfolded_lines'] = [self.report._get_generic_line_id('res.partner', None, markup='no_partner')]
self.assertLinesValues(
self.report._get_lines(options),
# Name Debit Credit Balance
[ 0, 6, 7, 9],
[
('partner_a', 20000.0, 1000.00, 19150.0),
('partner_b', 1000.00, 0.00, 1200.00),
('partner_c', 0.00, 21000.0, -21350.0),
('Unknown Partner', 1200.00, 1200.00, 0.00),
('MISC/2017/03/0001', 0.00, 1000.00, -1000.00),
('MISC/2017/06/0001 2017_2_1', 200.00, 0.00, -800.00),
('MISC/2017/06/0001 2017_2_2', 0.00, 200.00, -1000.00),
('MISC/2017/03/0001', 1000.00, 0.00, 0.00),
('Total Unknown Partner', 1200.00, 1200.00, 0.00),
('Total', 22200.0, 23200.0, -1000.00),
],
options,
)
# Change the dates to exclude the reconciliation max date: situation is back to the beginning
options = self._generate_options(self.report, fields.Date.from_string('2017-01-01'), fields.Date.from_string('2017-03-30'))
self.assertLinesValues(
self.report._get_lines(options),
# Name Debit Credit Balance
[ 0, 6, 7, 9],
[
('partner_a', 20000.0, 0.00, 20150.0),
('partner_b', 1000.00, 0.00, 1200.00),
('partner_c', 0.00, 21000.0, -21350.0),
('Total', 21000.0, 21000.0, 0.00),
],
options,
)
# Change the dates to have a date_from > to the reconciliation max date and check the initial balances are correct
options = self._generate_options(self.report, fields.Date.from_string('2017-04-01'), fields.Date.from_string('2017-04-01'))
self.assertLinesValues(
self.report._get_lines(options),
# Name Debit Credit Balance
[ 0, 6, 7, 9],
[
('partner_a', 0.0, 1000.0, 19150.0),
('partner_b', 0.0, 0.0, 1200.00),
('partner_c', 0.0, 0.0, -21350.0),
('Unknown Partner', 1000.0, 0.0, 0.0),
('Total', 1000.0, 1000.0, -1000.0),
],
options,
)
# Unfold 'partner_a' to check the initial balance line is correct
options['unfolded_lines'] = [self.report._get_generic_line_id('res.partner', self.partner_a.id)]
self.assertLinesValues(
self.report._get_lines(options),
# Name Debit Credit Balance
[ 0, 6, 7, 9],
[
('partner_a', 0.0, 1000.0, 19150.0),
('Initial Balance', '', 1000.0, 19150.0),
('Total partner_a', 0.0, 1000.0, 19150.0),
('partner_b', 0.0, 0.0, 1200.0),
('partner_c', 0.0, 0.0, -21350.0),
('Unknown Partner', 1000.0, 0.0, 0.0),
('Total', 1000.0, 1000.0, -1000.0),
],
options,
)
def test_partner_ledger_prefix_groups(self):
partner_names = [
'A',
'A partner',
'A nice partner',
'A new partner',
'An original partner',
'Another partner',
'Anonymous partner',
'Annoyed partner',
'Brave partner',
]
test_date = '2010-12-13'
test_partners = self.env['res.partner']
for name in partner_names:
partner = self.env['res.partner'].create({'name': name})
test_partners += partner
invoice = self.init_invoice('out_invoice', partner=partner, invoice_date=test_date, amounts=[42.0], taxes=[], post=True)
# Without prefix groups
options = self._generate_options(self.report, test_date, test_date)
self.assertLinesValues(
self.report._get_lines(options),
# Name Journal Debit Credit Balance
[ 0, 1, 6, 7, 9],
[
('A', '', 42.0, 0.0, 42.0),
('A new partner', '', 42.0, 0.0, 42.0),
('A nice partner', '', 42.0, 0.0, 42.0),
('A partner', '', 42.0, 0.0, 42.0),
('An original partner', '', 42.0, 0.0, 42.0),
('Annoyed partner', '', 42.0, 0.0, 42.0),
('Anonymous partner', '', 42.0, 0.0, 42.0),
('Another partner', '', 42.0, 0.0, 42.0),
('Brave partner', '', 42.0, 0.0, 42.0),
('Total', '', 378.0, 0.0, 378.0),
],
options,
)
# With prefix groups
self.report.prefix_groups_threshold = 3
options = self._generate_options(self.report, test_date, test_date, default_options={'unfold_all': True})
self.assertLinesValues(
self.report._get_lines(options),
# Name Journal Debit Credit Balance
[ 0, 1, 6, 7, 9],
[
('A (8 lines)', '', 336.0, 0.0, 336.0),
('A', '', 42.0, 0.0, 42.0),
('INV/2010/00001', 'INV', 42.0, 0.0, 42.0),
('Total A', '', 42.0, 0.0, 42.0),
('A[ ] (3 lines)', '', 126.0, 0.0, 126.0),
('A N (2 lines)', '', 84.0, 0.0, 84.0),
('A new partner', '', 42.0, 0.0, 42.0),
('INV/2010/00004', 'INV', 42.0, 0.0, 42.0),
('Total A new partner', '', 42.0, 0.0, 42.0),
('A nice partner', '', 42.0, 0.0, 42.0),
('INV/2010/00003', 'INV', 42.0, 0.0, 42.0),
('Total A nice partner', '', 42.0, 0.0, 42.0),
('Total A N (2 lines)', '', 84.0, 0.0, 84.0),
('A P (1 line)', '', 42.0, 0.0, 42.0),
('A partner', '', 42.0, 0.0, 42.0),
('INV/2010/00002', 'INV', 42.0, 0.0, 42.0),
('Total A partner', '', 42.0, 0.0, 42.0),
('Total A P (1 line)', '', 42.0, 0.0, 42.0),
('Total A[ ] (3 lines)', '', 126.0, 0.0, 126.0),
('AN (4 lines)', '', 168.0, 0.0, 168.0),
('AN[ ] (1 line)', '', 42.0, 0.0, 42.0),
('An original partner', '', 42.0, 0.0, 42.0),
('INV/2010/00005', 'INV', 42.0, 0.0, 42.0),
('Total An original partner', '', 42.0, 0.0, 42.0),
('Total AN[ ] (1 line)', '', 42.0, 0.0, 42.0),
('ANN (1 line)', '', 42.0, 0.0, 42.0),
('Annoyed partner', '', 42.0, 0.0, 42.0),
('INV/2010/00008', 'INV', 42.0, 0.0, 42.0),
('Total Annoyed partner', '', 42.0, 0.0, 42.0),
('Total ANN (1 line)', '', 42.0, 0.0, 42.0),
('ANO (2 lines)', '', 84.0, 0.0, 84.0),
('Anonymous partner', '', 42.0, 0.0, 42.0),
('INV/2010/00007', 'INV', 42.0, 0.0, 42.0),
('Total Anonymous partner', '', 42.0, 0.0, 42.0),
('Another partner', '', 42.0, 0.0, 42.0),
('INV/2010/00006', 'INV', 42.0, 0.0, 42.0),
('Total Another partner', '', 42.0, 0.0, 42.0),
('Total ANO (2 lines)', '', 84.0, 0.0, 84.0),
('Total AN (4 lines)', '', 168.0, 0.0, 168.0),
('Total A (8 lines)', '', 336.0, 0.0, 336.0),
('B (1 line)', '', 42.0, 0.0, 42.0),
('Brave partner', '', 42.0, 0.0, 42.0),
('INV/2010/00009', 'INV', 42.0, 0.0, 42.0),
('Total Brave partner', '', 42.0, 0.0, 42.0),
('Total B (1 line)', '', 42.0, 0.0, 42.0),
('Total', '', 378.0, 0.0, 378.0),
],
options,
)
def test_filter_unreconciled_entries_only(self):
new_partner = self.env['res.partner'].create({'name': 'Obiwan Kenobi'})
move_1 = self.init_invoice('out_invoice', partner=new_partner, invoice_date='2019-01-01', amounts=[1000.0], taxes=[], post=True)
move_2 = self.init_invoice('out_invoice', partner=new_partner, invoice_date='2019-01-01', amounts=[5000.0], taxes=[], post=True)
self.env['account.payment.register'].create({
'payment_date': '2019-01-01',
'line_ids': move_1.line_ids.filtered(lambda l: l.display_type == 'payment_term'),
'amount': 700.0,
})._create_payments()
self.env['account.payment.register'].create({
'payment_date': '2019-01-01',
'line_ids': move_2.line_ids.filtered(lambda l: l.display_type == 'payment_term'),
})._create_payments()
self.assertEqual(move_1.payment_state, 'partial')
self.assertEqual(move_2.payment_state, move_2._get_invoice_in_payment_state())
options = self._generate_options(self.report, '2019-01-01', '2019-12-31', default_options={'partner_ids': new_partner.ids})
self.assertLinesValues(
self.report._get_lines(options),
# Name Debit Credit Balance
[ 0, 6, 7, 9],
[
('Obiwan Kenobi', 6000.0, 5700.0, 300.0),
('Total', 6000.0, 5700.0, 300.0),
],
options
)
options['unreconciled'] = True
self.assertLinesValues(
self.report._get_lines(options),
# Name Debit Credit Balance
[ 0, 6, 7, 9],
[
('Obiwan Kenobi', 1000.0, 700.0, 300.0),
('Total', 1000.0, 700.0, 300.0),
],
options
)
def test_print_pdf_exclude_partner_with_name_similar_to_another_partner_email(self):
"""
This test verifies that when printing a PDF report, the report accurately reflects the data displayed on the view
by excluding partners whose email addresses are similar to other partners' names if the search bar is used to filter out partners.
"""
partner = self.env['res.partner'].create({'name': 'Great Customer', 'email': 'partner_a@test.com'})
self.init_invoice('out_invoice', partner=partner, invoice_date='2019-02-14', amounts=[1000.0], taxes=[], post=True)
options = self._generate_options(self.report, '2019-02-01', '2019-02-28', default_options={
'filter_search_bar': 'partner_a',
'export_mode': 'print',
})
lines = self.report._get_lines(options)
self.assertFalse(partner.name in [line['name'] for line in lines])
def test_partner_ledger_search_with_unknown_partner(self):
''' Test that the lines with no partners are included in the report when searching for "Unknown Partner" '''
options = self._generate_options(self.report, fields.Date.from_string('2017-01-01'), fields.Date.from_string('2017-12-31'), default_options={
'filter_search_bar': 'un',
'export_mode': 'print',
})
self.assertLinesValues(
self.report._get_lines(options),
# Name Debit Credit Balance
[ 0, 6, 7, 9],
[
('Unknown Partner', 200.0, 200.0, 0.0),
('Total', 200.0, 200.0, 0.0),
],
options,
)
partner = self.env['res.partner'].create({'name': 'Unexpected Customer'})
self.init_invoice('out_invoice', partner=partner, invoice_date='2017-02-14', amounts=[1000.0], taxes=[], post=True)
self.assertLinesValues(
self.report._get_lines(options),
# Name Debit Credit Balance
[ 0, 6, 7, 9],
[
('Unexpected Customer', 1000.0, 0.0, 1000.0),
('Unknown Partner', 200.0, 200.0, 0.0),
('Total', 1200.0, 200.0, 1000.0),
],
options,
)
def test_no_amount_currency_col_in_single_currency(self):
# In multi-currency, we get the amount_currency column
self.assertGreater(len(self.env['res.currency'].search([])), 1)
options = self._generate_options(self.report, fields.Date.from_string('2023-01-01'), fields.Date.from_string('2023-12-31'))
self.assertTrue(any(col['expression_label'] == 'amount_currency' for col in options['columns']))
# Not in single-currency
self.env['res.currency'].search([('name', '!=', 'USD')]).with_context(force_deactivate=True).active = False
self.assertEqual(len(self.env['res.currency'].search([])), 1)
options = self._generate_options(self.report, fields.Date.from_string('2023-01-01'), fields.Date.from_string('2023-12-31'))
self.assertFalse(any(col['expression_label'] == 'amount_currency' for col in options['columns']))
def test_partner_ledger_fully_reconcile_previous_year(self):
"""
Verify the report's behavior when a partner is fully reconciled and has a zero balance.
If the partner's balance is zero:
If the partner has any unreconciled items, display the partner's information in the report.
If the partner has no unreconciled items but has an initial balance > 0, show the partner.
If the partner has no unreconciled items and has an initial balance = 0, hide the partner.
"""
new_partner = self.env['res.partner'].create({'name': 'Anakin Skywalker'})
move = self.env['account.move'].create({
'move_type': 'out_invoice',
'invoice_date': '2019-01-01',
'partner_id': new_partner.id,
'invoice_line_ids': [Command.create({
'quantity': 1,
'price_unit': 500.0,
'tax_ids': [],
})]
})
move.action_post()
payment_1 = self.env['account.move'].create({
'move_type': 'entry',
'date': '2019-01-01',
'journal_id': self.company_data['default_journal_misc'].id,
'partner_id': new_partner.id,
'line_ids': [
Command.create({'debit': 0.0, 'credit': 500.0, 'account_id': self.company_data['default_account_receivable'].id}),
Command.create({'debit': 500.0, 'credit': 0.0, 'account_id': self.company_data['default_journal_bank'].default_account_id.id}),
],
})
payment_1.action_post()
options = self._generate_options(self.report, '2020-01-01', '2020-12-31', default_options={'partner_ids': new_partner.ids})
# Balance is 0 but there are unreconciled entries, so we show the line
self.assertLinesValues(
self.report._get_lines(options),
# Name Debit Credit Balance
[ 0, 6, 7, 9],
[
('Total', 0.0, 0.0, 0.0),
],
options,
)
(payment_1 + move).line_ids.filtered(
lambda line: line.account_id == self.company_data['default_account_receivable']
).reconcile()
# Balance is still 0 and there is no more unreconciled entries, the partner is hide from the report
self.assertLinesValues(
self.report._get_lines(options),
# Name Debit Credit Balance
[ 0, 6, 7, 9],
[
('Total', 0.0, 0.0, 0.0),
],
options,
)
def test_partner_ledger_fully_reconcile_current_year(self):
new_partner = self.env['res.partner'].create({'name': 'Darth Vader'})
move = self.env['account.move'].create({
'move_type': 'out_invoice',
'invoice_date': '2019-01-01',
'partner_id': new_partner.id,
'invoice_line_ids': [Command.create({
'quantity': 1,
'price_unit': 500.0,
'tax_ids': [],
})]
})
move.action_post()
payment_1 = self.env['account.move'].create({
'move_type': 'entry',
'date': '2019-01-01',
'journal_id': self.company_data['default_journal_misc'].id,
'partner_id': new_partner.id,
'line_ids': [
Command.create({'debit': 0.0, 'credit': 500.0, 'account_id': self.company_data['default_account_receivable'].id}),
Command.create({'debit': 500.0, 'credit': 0.0, 'account_id': self.company_data['default_journal_bank'].default_account_id.id}),
],
})
payment_1.action_post()
options = self._generate_options(self.report, '2019-01-01', '2019-12-31', default_options={'partner_ids': new_partner.ids})
# Balance is 0 but there are unreconciled entries, so we show the line
self.assertLinesValues(
self.report._get_lines(options),
# Name Debit Credit Balance
[ 0, 6, 7, 9],
[
('Darth Vader', 500.0, 500.0, 0.0),
('Total', 500.0, 500.0, 0.0),
],
options,
)
(payment_1 + move).line_ids.filtered(
lambda line: line.account_id == self.company_data['default_account_receivable']
).reconcile()
# Balance is still 0 and there is no more unreconciled entries, but the moves is in the current report period,
# so we still show the partner in the report
self.assertLinesValues(
self.report._get_lines(options),
# Name Debit Credit Balance
[ 0, 6, 7, 9],
[
('Darth Vader', 500.0, 500.0, 0.0),
('Total', 500.0, 500.0, 0.0),
],
options,
)
def test_partner_ledger_toggle_followup(self):
"""Make sure that toggling the followup also (and only) toggles other lines of the same invoice."""
installments_payment_term = self.env['account.payment.term'].create({
'name': "3 installments",
'line_ids': [
Command.create({'value_amount': 40, 'value': 'percent', 'nb_days': 0}),
Command.create({'value_amount': 30, 'value': 'percent', 'nb_days': 30}),
Command.create({'value_amount': 30, 'value': 'percent', 'nb_days': 60}),
],
})
invoices = self.env['account.move'].create([
{
'move_type': 'out_invoice',
'invoice_date': fields.Date.from_string('2024-08-01'),
'partner_id': self.partner_a.id,
'invoice_line_ids': [Command.create({'quantity': 1, 'price_unit': 1000})],
'invoice_payment_term_id': installments_payment_term.id,
},
{
'move_type': 'out_invoice',
'invoice_date': fields.Date.from_string('2024-08-10'),
'partner_id': self.partner_a.id,
'invoice_line_ids': [Command.create({'quantity': 1, 'price_unit': 500})],
'invoice_payment_term_id': installments_payment_term.id,
},
])
invoices.action_post()
invoice_name = invoices[0].name
options = self._generate_options(self.report, '2024-01-01', '2024-12-31', default_options={'unfold_all': True})
lines = self.report._get_lines(options)
line_ids = [line['id'] for line in lines]
invoice_1_line_ids = [line['id'] for line in lines if invoice_name in line['name']]
self.assertEqual(
self.env['account.partner.ledger.report.handler'].action_toggle_no_followup(invoice_1_line_ids[0], line_ids)['updated_line_ids'],
invoice_1_line_ids,
)
def test_reconciled_misc_without_partner_shows_with_partner_filter(self):
"""A misc move without a partner reconciled with a partner invoice must still appear
when filtering the Partner Ledger by that partner."""
bernard = self.env['res.partner'].create({'name': 'Bernard Gagnant'})
invoice = self.init_invoice('out_invoice', partner=bernard, invoice_date='2019-03-01', amounts=[1000.0], taxes=[], post=True)
misc_move = self.env['account.move'].create({
'date': '2019-03-02',
'journal_id': self.company_data['default_journal_misc'].id,
'line_ids': [
(0, 0, {'debit': 1000.0, 'credit': 0.0, 'account_id': self.company_data['default_account_revenue'].id}),
(0, 0, {'debit': 0.0, 'credit': 1000.0, 'account_id': self.company_data['default_account_receivable'].id}),
],
})
misc_move.action_post()
invoice_receivable = invoice.line_ids.filtered(lambda l: l.account_id == self.company_data['default_account_receivable'])
misc_receivable = misc_move.line_ids.filtered(lambda l: l.account_id == self.company_data['default_account_receivable'] and not l.partner_id)
(invoice_receivable + misc_receivable).reconcile()
# Ensure both the invoice and the misc move appear when the report is fully unfolded.
options = self._generate_options(self.report, '2019-03-01', '2019-03-31', default_options={'unfold_all': True})
lines = self.report._get_lines(options)
self.assertLinesValues(
lines,
# Name Debit Credit Balance
[ 0, 6, 7, 9],
[
(bernard.name, 1000.0, 1000.0, 0.0),
(invoice.name, 1000.0, 0.0, 1000.0),
(misc_move.name, 0.0, 1000.0, 0.0),
('Total ' + bernard.name, 1000.0, 1000.0, 0.0),
('partner_a', 0.0, 0.0, 20150.0),
('Initial Balance', '', '', 20150.0),
('Total partner_a', 0.0, 0.0, 20150.0),
('partner_b', 0.0, 0.0, 1200.0),
('Initial Balance', '', '', 1200.0),
('Total partner_b', 0.0, 0.0, 1200.0),
('partner_c', 0.0, 0.0, -21350.0),
('Initial Balance', '', '', -21350.0),
('Total partner_c', 0.0, 0.0, -21350.0),
('Unknown Partner', 1000.0, 1000.0, 0.0),
('Initial Balance', '', '', 0.0),
('MISC/2019/03/0001', 0.0, 1000.0, -1000.0),
('MISC/2019/03/0001', 1000.0, 0.0, 0.0),
('Total Unknown Partner', 1000.0, 1000.0, 0.0),
('Total', 2000.0, 2000.0, 0.0),
],
options,
)
# When filtering the report by the partner, the misc move (despite having no partner)
# must still appear because it is reconciled with the partner's invoice.
options_partner = self._generate_options(self.report, '2019-03-01', '2019-03-31', default_options={'unfold_all': True, 'partner_ids': bernard.ids})
lines_filtered = self.report._get_lines(options_partner)
self.assertLinesValues(
lines_filtered,
# Name Debit Credit Balance
[ 0, 6, 7, 9],
[
(bernard.name, 1000.0, 1000.0, 0.0),
(invoice.name, 1000.0, 0.0, 1000.0),
(misc_move.name, 0.0, 1000.0, 0.0),
('Total ' + bernard.name, 1000.0, 1000.0, 0.0),
('Total', 1000.0, 1000.0, 0.0),
],
options_partner,
)
def test_partner_ledger_without_filter_partner(self):
""""Test opening the partner ledger report when filter_partner is disabled."""
self.report.filter_partner = False
options = self._generate_options(
self.report, '2019-03-01', '2019-03-31',
default_options={'unfold_all': True}
)
self.assertNotIn('partner_ids', options)
report_lines = self.report._get_lines(options)
self.assertLinesValues(
report_lines,
# Name Debit Credit Balance
[ 0, 6, 7, 9],
[
('partner_a', 0.0, 0.0, 20150.0),
('Initial Balance', '', '', 20150.0),
('Total partner_a', 0.0, 0.0, 20150.0),
('partner_b', 0.0, 0.0, 1200.0),
('Initial Balance', '', '', 1200.0),
('Total partner_b', 0.0, 0.0, 1200.0),
('partner_c', 0.0, 0.0, -21350.0),
('Initial Balance', '', '', -21350.0),
('Total partner_c', 0.0, 0.0, -21350.0),
('Total', 0.0, 0.0, 0.0),
],
options,
)
def test_partner_ledger_amount_currency(self):
"""
Ensure the partner ledger (and its variants) correctly display the amount currency for the partner line
in the case where all journal items of this partner share the same foreign currency per column group.
Also ensure that initial balance moves aren't taken into consideration in the calculation of the amount currency.
"""
bernard = self.env['res.partner'].create({'name': 'Bernard Gagnant'})
eur_currency = self.env.ref('base.EUR')
eur_currency.active = True
self.env['res.currency.rate'].create({
'name': '2026-12-01',
'rate': 1.0 / 2.0,
'currency_id': eur_currency.id,
'company_id': self.env.company.id,
})
usd_invoice = self.init_invoice('out_invoice', partner=bernard, invoice_date='2025-12-31', amounts=[1000.0], taxes=[])
eur_invoice = self.init_invoice('out_invoice', partner=bernard, invoice_date='2026-01-01', amounts=[1000.0], taxes=[], currency=eur_currency)
(usd_invoice + eur_invoice).action_post()
# USD invoice from previous period; shouldn't affect amount_currency (displayed since it's the only foreign currency in this period).
options = self._generate_options(self.report, '2026-01-01', '2026-01-31', default_options={'partner_ids': bernard.ids})
self.assertLinesValues(
self.report._get_lines(options),
# Name Debit Credit Amount Currency Balance
[ 0, 6, 7, 8, 9],
[
('Bernard Gagnant', 2000.0, 0.0, 1000.0, 3000.0),
('Total', 2000.0, 0.0, '', 3000.0),
],
options,
currency_map={8: {'currency': eur_currency}},
)
# Test that if multiple currencies are used during the evaluated period, the amount currency shouldn't be displayed
gbp_currency = self.env.ref('base.GBP')
gbp_currency.active = True
self.env['res.currency.rate'].create({
'name': '2026-12-01',
'rate': 1.0 / 2.0,
'currency_id': gbp_currency.id,
'company_id': self.env.company.id,
})
self.init_invoice('out_invoice', partner=bernard, invoice_date='2026-01-01', amounts=[1000.0], taxes=[], currency=gbp_currency, post=True)
self.assertLinesValues(
self.report._get_lines(options),
# Name Debit Credit Amount Currency Balance
[ 0, 6, 7, 8, 9],
[
('Bernard Gagnant', 4000.0, 0.0, '', 5000.0),
('Total', 4000.0, 0.0, '', 5000.0),
],
options,
)
# Test that if multiple currencies are used, but a horizontal groupby is applied so that only a single foreign
# currency is used per column group, then the report should display the amount currency correctly
horizontal_group = self.env['account.report.horizontal.group'].create({
'name': 'Horizontal Group Currencies',
'report_ids': [self.report.id],
'rule_ids': [
Command.create({
'field_name': 'currency_id',
'domain': f"[('id', 'in', {(eur_currency + gbp_currency + self.env.company.currency_id).ids})]",
}),
],
})
self.init_invoice('out_invoice', partner=bernard, invoice_date='2026-01-01', amounts=[1000.0], taxes=[], post=True)
options = self._generate_options(
self.report, '2026-01-01', '2026-01-31',
default_options={
'partner_ids': bernard.ids,
'selected_horizontal_group_id': horizontal_group.id,
}
)
self.assertLinesValues(
self.report._get_lines(options),
# [ EUR ] [ GBP ] [ USD ]
# Name Amount Currency Balance Amount Currency Balance Amount Currency Balance
[ 0, 8, 9, 17, 18, 26, 27],
[
('Bernard Gagnant', 1000.0, 2000.0, 1000.0, 2000.0, '', 2000.0),
('Total', '', 2000.0, '', 2000.0, '', 2000.0),
],
options,
currency_map={
8: {'currency': eur_currency},
17: {'currency': gbp_currency},
},
)