Files
enterprise-addons/account_iso20022/tests/test_iso_reconciliation.py
T

445 lines
19 KiB
Python

from odoo import Command
from odoo.tests import tagged
from odoo.addons.account_iso20022.tests.test_sepa_credit_transfer import TestSEPACreditTransferCommon
@tagged('post_install', '-at_install')
class TestIsoReconciliation(TestSEPACreditTransferCommon):
@classmethod
def setUpClass(cls):
super().setUpClass()
branch = cls._create_company(
name='Branch Company',
parent_id=cls.company_data['company'].id,
)
cls.branch_data = cls.collect_company_accounting_data(branch)
unrelated = cls._create_company(name='Unrelated Company')
cls.unrelated_data = cls.collect_company_accounting_data(unrelated)
def test_matching_on_end_to_end_uuid(self):
"""
Assert that a payment and a bank statement line are matched if they have the same end_to_end_uuid.
"""
bill = self.env['account.move'].create({
'partner_id': self.partner_a.id,
'move_type': 'in_invoice',
'invoice_date': '2025-01-01',
'invoice_line_ids': [Command.create({
'quantity': 1,
'price_unit': 100,
})],
})
bill.action_post()
bill_line = bill.line_ids.filtered(lambda l: l.account_id.account_type in {'asset_receivable', 'liability_payable'})
# The payment_method_line_id should have a payment_account_id value to generate the correct journal entries.
sepa_ct_method_line = self.bank_journal.outbound_payment_method_line_ids.filtered(lambda l: l.code == 'sepa_ct')
sepa_ct_method_line.payment_account_id = self.outbound_payment_method_line.payment_account_id
payment = self.env['account.payment.register'].with_context(
active_ids=bill_line.move_id.ids,
active_model='account.move',
).create({
'amount': 100,
'journal_id': self.bank_journal.id,
'payment_date': '2010-01-01',
'payment_method_line_id': sepa_ct_method_line.id,
})._create_payments()
bank_statement_line = self.env['account.bank.statement.line'].create([
{
'journal_id': self.bank_journal.id,
'date': '2020-01-01',
'payment_ref': 'PAYMENT',
'amount': -100,
'sequence': 1,
'end_to_end_uuid': payment.end_to_end_uuid,
},
])
bank_statement_line._try_auto_reconcile_statement_lines()
# Assert that the statement line has been reconciled with the payment and the invoice.
self.assertEqual(
payment.move_id.line_ids._all_reconciled_lines(),
payment.move_id.line_ids | bank_statement_line.line_ids[1] | bill_line
)
# Assert that the payment has been automatically validated.
self.assertEqual(payment.state, 'paid')
def test_matching_end_to_end_uuid_no_payment_entry(self):
# Check if accounting is installed as this test doesn't make sense without accounting installed
if self.env.ref('base.module_accountant').state != 'installed':
self.skipTest("`accountant` module not installed")
bill = self.env['account.move'].create({
'partner_id': self.partner_a.id,
'move_type': 'in_invoice',
'invoice_date': '2025-01-01',
'invoice_line_ids': [Command.create({
'quantity': 1,
'price_unit': 100,
})],
})
bill.action_post()
bill_line = bill.line_ids.filtered(lambda line: line.account_id.account_type in {'asset_receivable', 'liability_payable'})
payment = self.env['account.payment.register'].with_context(
active_ids=bill_line.move_id.ids,
active_model='account.move',
).create({
'amount': 100,
'journal_id': self.bank_journal.id,
'payment_date': '2010-01-01',
'payment_method_line_id': self.bank_journal.outbound_payment_method_line_ids.filtered(lambda l: l.code == 'sepa_ct').id,
})._create_payments()
bank_statement_line = self.env['account.bank.statement.line'].create([
{
'journal_id': self.bank_journal.id,
'date': '2020-01-01',
'payment_ref': 'PAYMENT',
'amount': -100,
'sequence': 1,
'end_to_end_uuid': payment.end_to_end_uuid,
},
])
bank_statement_line._try_auto_reconcile_statement_lines()
self.assertEqual(
payment.invoice_ids.line_ids.filtered(lambda line: line.account_type == 'liability_payable'),
bank_statement_line.line_ids.reconciled_lines_ids,
)
# Assert that the payment has been automatically validated.
self.assertEqual(payment.state, 'paid')
def test_matching_end_to_end_uuid_single_payment(self):
# Check if accounting is installed as this test doesn't make sense without accounting installed
if self.env.ref('base.module_accountant').state != 'installed':
self.skipTest("`accountant` module not installed")
payment = self.env['account.payment'].create({
'amount': 100,
'journal_id': self.bank_journal.id,
'date': '2010-01-01',
'partner_id': self.partner_a.id,
'payment_method_line_id': self.bank_journal.outbound_payment_method_line_ids.filtered(lambda l: l.code == 'sepa_ct').id,
'payment_type': 'outbound',
})
payment.action_post()
bank_statement_line = self.env['account.bank.statement.line'].create([
{
'journal_id': self.bank_journal.id,
'date': '2020-01-01',
'payment_ref': 'PAYMENT',
'amount': -100,
'sequence': 1,
'end_to_end_uuid': payment.end_to_end_uuid,
},
])
bank_statement_line._try_auto_reconcile_statement_lines()
# Assert that the payment has been automatically validated.
self.assertEqual(payment.state, 'paid')
self.assertRecordValues(
bank_statement_line.line_ids,
[
{
'balance': -100.0,
'account_id': bank_statement_line.journal_id.default_account_id.id,
'name': bank_statement_line.payment_ref,
},
{
'balance': 100.0,
'account_id': self.partner_a.property_account_payable_id.id,
'name': payment.name,
}
]
)
def test_no_end_to_end_uuid_matching_across_unrelated_companies(self):
"""
Assert that a bank statement line is NOT matched against a payment from an
unrelated company via end_to_end_uuid, even when both share the same UUID
(e.g. two companies on the same database doing an intercompany bank transfer).
"""
bill = self.env['account.move'].create({
'partner_id': self.partner_a.id,
'move_type': 'in_invoice',
'invoice_date': '2025-01-01',
'invoice_line_ids': [Command.create({
'quantity': 1,
'price_unit': 100,
})],
'company_id': self.company_data['company'].id,
})
bill.action_post()
sepa_ct_method_line = self.bank_journal.outbound_payment_method_line_ids.filtered(lambda l: l.code == 'sepa_ct')
sepa_ct_method_line.payment_account_id = self.outbound_payment_method_line.payment_account_id
payment = self.env['account.payment.register'].with_context(
active_ids=bill.ids,
active_model='account.move',
).create({
'amount': 100,
'journal_id': self.bank_journal.id,
'payment_date': '2010-01-01',
'payment_method_line_id': sepa_ct_method_line.id,
'company_id': self.company_data['company'].id,
})._create_payments()
bank_statement_line = self.env['account.bank.statement.line'].create({
'journal_id': self.unrelated_data['default_journal_bank'].id,
'date': '2020-01-01',
'payment_ref': 'PAYMENT',
'amount': -100,
'end_to_end_uuid': payment.end_to_end_uuid,
'company_id': self.unrelated_data['company'].id,
})
# sudo() replicates the SUPERUSER context used by the auto-reconciliation cron,
# which has access to all companies and is what triggered the original bug.
bank_statement_line.sudo()._try_auto_reconcile_statement_lines()
self.assertFalse(bank_statement_line.is_reconciled)
def test_matching_end_to_end_uuid_draft_payment(self):
payment = self.env['account.payment'].create({
'amount': 100,
'journal_id': self.bank_journal.id,
'date': '2010-01-01',
'partner_id': self.partner_a.id,
'payment_method_line_id': self.bank_journal.outbound_payment_method_line_ids.filtered(lambda l: l.code == 'sepa_ct').id,
'payment_type': 'outbound',
})
bank_statement_line = self.env['account.bank.statement.line'].create([
{
'journal_id': self.bank_journal.id,
'date': '2020-01-01',
'payment_ref': 'PAYMENT',
'amount': -100,
'sequence': 1,
'end_to_end_uuid': payment.end_to_end_uuid,
},
])
bank_statement_line._try_auto_reconcile_statement_lines()
# Keep draft payment as draft (even if the reconciliation is done)
self.assertEqual(payment.state, 'draft')
self.assertRecordValues(
bank_statement_line.line_ids,
[
{
'balance': -100.0,
'account_id': bank_statement_line.journal_id.default_account_id.id,
'name': bank_statement_line.payment_ref,
},
{
'balance': 100.0,
'account_id': self.partner_a.property_account_payable_id.id,
'name': payment.name,
}
]
)
def test_matching_end_to_end_uuid_only_access_to_branch_company_for_statement_line(self):
bill = self.env['account.move'].create({
'partner_id': self.partner_a.id,
'move_type': 'in_invoice',
'invoice_date': '2025-01-01',
'invoice_line_ids': [Command.create({
'quantity': 1,
'price_unit': 100,
})],
'company_id': self.company_data['company'].id,
})
bill.action_post()
# The payment_method_line_id should have a payment_account_id value to generate the correct journal entries.
sepa_ct_method_line = self.bank_journal.outbound_payment_method_line_ids.filtered(lambda l: l.code == 'sepa_ct')
sepa_ct_method_line.payment_account_id = self.outbound_payment_method_line.payment_account_id
payment = self.env['account.payment.register'].with_context(
active_ids=bill.ids,
active_model='account.move',
).create({
'amount': 100,
'journal_id': self.bank_journal.id,
'payment_date': '2010-01-01',
'payment_method_line_id': sepa_ct_method_line.id,
'company_id': self.company_data['company'].id,
})._create_payments()
bank_statement_line = self.env['account.bank.statement.line'].create([
{
'journal_id': self.bank_journal.id,
'date': '2020-01-01',
'payment_ref': 'PAYMENT',
'amount': -100,
'sequence': 1,
'end_to_end_uuid': payment.end_to_end_uuid,
'company_id': self.branch_data['company'].id,
},
])
# Make sure that user has only access to the branch company
self.env.user.company_id = self.branch_data['company'].id
self.env.user.company_ids = self.branch_data['company'].ids
bank_statement_line.with_company(self.branch_data['company'])._try_auto_reconcile_statement_lines()
# Reset allowed companies for the test
self.env.user.company_ids = (self.branch_data['company'] | self.company_data['company']).ids
self.assertEqual(
bank_statement_line.line_ids.reconciled_lines_ids,
payment.move_id.line_ids.filtered(lambda line: line.account_id == self.outbound_payment_method_line.payment_account_id),
)
# Assert that the payment has been automatically validated.
self.assertEqual(payment.state, 'paid')
def test_matching_end_to_end_uuid_access_to_main_and_branch_company_for_statement_line(self):
bill = self.env['account.move'].create({
'partner_id': self.partner_a.id,
'move_type': 'in_invoice',
'invoice_date': '2025-01-01',
'invoice_line_ids': [Command.create({
'quantity': 1,
'price_unit': 100,
})],
'company_id': self.company_data['company'].id,
})
bill.action_post()
# The payment_method_line_id should have a payment_account_id value to generate the correct journal entries.
sepa_ct_method_line = self.bank_journal.outbound_payment_method_line_ids.filtered(lambda l: l.code == 'sepa_ct')
sepa_ct_method_line.payment_account_id = self.outbound_payment_method_line.payment_account_id
payment = self.env['account.payment.register'].with_context(
active_ids=bill.ids,
active_model='account.move',
).create({
'amount': 100,
'journal_id': self.bank_journal.id,
'payment_date': '2010-01-01',
'payment_method_line_id': sepa_ct_method_line.id,
'company_id': self.company_data['company'].id,
})._create_payments()
bank_statement_line = self.env['account.bank.statement.line'].create([
{
'journal_id': self.bank_journal.id,
'date': '2020-01-01',
'payment_ref': 'PAYMENT',
'amount': -100,
'sequence': 1,
'end_to_end_uuid': payment.end_to_end_uuid,
'company_id': self.branch_data['company'].id,
},
])
bank_statement_line.with_company(self.branch_data['company'])._try_auto_reconcile_statement_lines()
self.assertEqual(
bank_statement_line.line_ids.reconciled_lines_ids,
payment.move_id.line_ids.filtered(lambda line: line.account_id == self.outbound_payment_method_line.payment_account_id),
)
# Assert that the payment has been automatically validated.
self.assertEqual(payment.state, 'paid')
def test_matching_end_to_end_uuid_single_payment_only_access_to_branch_company_for_statement_line(self):
# Check if accounting is installed as this test doesn't make sense without accounting installed
if self.env.ref('base.module_accountant').state != 'installed':
self.skipTest("`accountant` module not installed")
payment = self.env['account.payment'].create({
'amount': 100,
'journal_id': self.bank_journal.id,
'date': '2010-01-01',
'partner_id': self.partner_a.id,
'payment_method_line_id': self.bank_journal.outbound_payment_method_line_ids.filtered(lambda l: l.code == 'sepa_ct').id,
'payment_type': 'outbound',
'company_id': self.company_data['company'].id,
})
payment.action_post()
bank_statement_line = self.env['account.bank.statement.line'].create([
{
'journal_id': self.bank_journal.id,
'date': '2020-01-01',
'payment_ref': 'PAYMENT',
'amount': -100,
'sequence': 1,
'end_to_end_uuid': payment.end_to_end_uuid,
'company_id': self.branch_data['company'].id,
},
])
# Make sure that user has only access to the branch company
self.env.user.company_id = self.branch_data['company'].id
self.env.user.company_ids = self.branch_data['company'].ids
bank_statement_line._try_auto_reconcile_statement_lines()
# Reset allowed companies for the test
self.env.user.company_ids = (self.branch_data['company'] | self.company_data['company']).ids
# Assert that the payment has been automatically validated.
self.assertEqual(payment.state, 'paid')
self.assertRecordValues(
bank_statement_line.line_ids,
[
{
'balance': -100.0,
'account_id': bank_statement_line.journal_id.default_account_id.id,
'name': bank_statement_line.payment_ref,
},
{
'balance': 100.0,
'account_id': self.partner_a.property_account_payable_id.id,
'name': payment.name,
}
]
)
def test_matching_end_to_end_uuid_single_payment_access_to_main_and_branch_company_for_statement_line(self):
# Check if accounting is installed as this test doesn't make sense without accounting installed
if self.env.ref('base.module_accountant').state != 'installed':
self.skipTest("`accountant` module not installed")
payment = self.env['account.payment'].create({
'amount': 100,
'journal_id': self.bank_journal.id,
'date': '2010-01-01',
'partner_id': self.partner_a.id,
'payment_method_line_id': self.bank_journal.outbound_payment_method_line_ids.filtered(lambda l: l.code == 'sepa_ct').id,
'payment_type': 'outbound',
'company_id': self.company_data['company'].id,
})
payment.action_post()
bank_statement_line = self.env['account.bank.statement.line'].create([
{
'journal_id': self.bank_journal.id,
'date': '2020-01-01',
'payment_ref': 'PAYMENT',
'amount': -100,
'sequence': 1,
'end_to_end_uuid': payment.end_to_end_uuid,
'company_id': self.branch_data['company'].id,
},
])
bank_statement_line._try_auto_reconcile_statement_lines()
# Assert that the payment has been automatically validated.
self.assertEqual(payment.state, 'paid')
self.assertRecordValues(
bank_statement_line.line_ids,
[
{
'balance': -100.0,
'account_id': bank_statement_line.journal_id.default_account_id.id,
'name': bank_statement_line.payment_ref,
},
{
'balance': 100.0,
'account_id': self.partner_a.property_account_payable_id.id,
'name': payment.name,
}
]
)