account/tests/test_account_bank_statement.py

1450 lines
65 KiB
Python
Raw Normal View History

# -*- coding: utf-8 -*-
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
from odoo.tests import tagged
from odoo.tests.common import Form
from odoo.exceptions import ValidationError, UserError
from odoo import fields, Command
import base64
@tagged('post_install', '-at_install')
class TestAccountBankStatementLine(AccountTestInvoicingCommon):
@classmethod
def setUpClass(cls, chart_template_ref=None):
super().setUpClass(chart_template_ref=chart_template_ref)
# We need a third currency as you could have a company's currency != journal's currency !=
cls.currency_data_2 = cls.setup_multi_currency_data(default_values={
'name': 'Dark Chocolate Coin',
'symbol': '🍫',
'currency_unit_label': 'Dark Choco',
'currency_subunit_label': 'Dark Cacao Powder',
}, rate2016=6.0, rate2017=4.0)
cls.currency_data_3 = cls.setup_multi_currency_data(default_values={
'name': 'Black Chocolate Coin',
'symbol': '🍫',
'currency_unit_label': 'Black Choco',
'currency_subunit_label': 'Black Cacao Powder',
}, rate2016=12.0, rate2017=8.0)
cls.bank_journal_1 = cls.company_data['default_journal_bank']
cls.bank_journal_2 = cls.bank_journal_1.copy()
cls.bank_journal_3 = cls.bank_journal_2.copy()
cls.currency_1 = cls.company_data['currency']
cls.currency_2 = cls.currency_data['currency']
cls.currency_3 = cls.currency_data_2['currency']
cls.currency_4 = cls.currency_data_3['currency']
cls.statement = cls.env['account.bank.statement'].create({
'name': 'test_statement',
'line_ids': [
(0, 0, {
'date': '2019-01-01',
'payment_ref': 'line_1',
'partner_id': cls.partner_a.id,
'foreign_currency_id': cls.currency_2.id,
'journal_id': cls.bank_journal_1.id,
'amount': 1250.0,
'amount_currency': 2500.0,
}),
],
})
cls.statement_line = cls.statement.line_ids
cls.expected_st_line = {
'date': fields.Date.from_string('2019-01-01'),
'journal_id': cls.statement.journal_id.id,
'payment_ref': 'line_1',
'partner_id': cls.partner_a.id,
'currency_id': cls.currency_1.id,
'foreign_currency_id': cls.currency_2.id,
'amount': 1250.0,
'amount_currency': 2500.0,
'is_reconciled': False,
}
cls.expected_bank_line = {
'name': cls.statement_line.payment_ref,
'partner_id': cls.statement_line.partner_id.id,
'currency_id': cls.currency_1.id,
'account_id': cls.statement.journal_id.default_account_id.id,
'debit': 1250.0,
'credit': 0.0,
'amount_currency': 1250.0,
}
cls.expected_counterpart_line = {
'name': cls.statement_line.payment_ref,
'partner_id': cls.statement_line.partner_id.id,
'currency_id': cls.currency_2.id,
'account_id': cls.statement.journal_id.suspense_account_id.id,
'debit': 0.0,
'credit': 1250.0,
'amount_currency': -2500.0,
}
def assertBankStatementLine(self, statement_line, expected_statement_line_vals, expected_move_line_vals):
self.assertRecordValues(statement_line, [expected_statement_line_vals])
self.assertRecordValues(statement_line.line_ids.sorted('balance'), expected_move_line_vals)
def create_bank_transaction(self, amount, date, amount_currency=None, currency=None, statement=None,
partner=None, journal=None, sequence=0):
values = {
'payment_ref': str(amount),
'amount': amount,
'date': date,
'partner_id': partner and partner.id,
'sequence': sequence,
}
if amount_currency:
values['amount_currency'] = amount_currency
values['foreign_currency_id'] = currency.id
if statement and journal and statement.journal_id != journal:
raise (ValidationError("The statement and the journal are contradictory"))
if statement:
values['journal_id'] = statement.journal_id.id
values['statement_id'] = statement.id
if journal:
values['journal_id'] = journal.id
if not values.get('journal_id'):
values['journal_id'] = (self.company_data_2['default_journal_bank']
if self.env.company == self.company_data_2['company']
else self.company_data['default_journal_bank']
).id
return self.env['account.bank.statement.line'].create(values)
# -------------------------------------------------------------------------
# TESTS about the statement line model.
# -------------------------------------------------------------------------
def _test_statement_line_edition(
self,
journal,
amount, amount_currency,
journal_currency, foreign_currency,
expected_liquidity_values, expected_counterpart_values):
''' Test the edition of a statement line from itself or from its linked journal entry.
:param journal: The account.journal record that will be set on the statement line.
:param amount: The amount in journal's currency.
:param amount_currency: The amount in the foreign currency.
:param journal_currency: The journal's currency as a res.currency record.
:param foreign_currency: The foreign currency as a res.currency record.
:param expected_liquidity_values: The expected account.move.line values for the liquidity line.
:param expected_counterpart_values: The expected account.move.line values for the counterpart line.
'''
if journal_currency:
journal.currency_id = journal_currency.id
statement_line = self.env['account.bank.statement.line'].create({
'date': '2019-01-01',
'journal_id': journal.id,
'payment_ref': 'line_1',
'partner_id': self.partner_a.id,
'foreign_currency_id': foreign_currency and foreign_currency.id,
'amount': amount,
'amount_currency': amount_currency,
})
# ==== Test the statement line amounts are correct ====
# If there is a bug in the compute/inverse methods, the amount/amount_currency could be
# incorrect directly after the creation of the statement line.
self.assertRecordValues(statement_line, [{
'amount': amount,
'amount_currency': amount_currency,
}])
self.assertRecordValues(statement_line.move_id, [{
'partner_id': self.partner_a.id,
'currency_id': (statement_line.foreign_currency_id or statement_line.currency_id).id,
}])
# ==== Test the edition of statement line amounts ====
# The statement line must remain consistent with its account.move.
# To test the compute/inverse methods are correctly managing all currency setup,
# we check the edition of amounts in both directions statement line <-> journal entry.
# Check initial state of the statement line.
liquidity_lines, suspense_lines, other_lines = statement_line._seek_for_lines()
self.assertRecordValues(liquidity_lines, [expected_liquidity_values])
self.assertRecordValues(suspense_lines, [expected_counterpart_values])
# Check the account.move is still correct after editing the account.bank.statement.line.
statement_line.write({
'amount': statement_line.amount * 2,
'amount_currency': statement_line.amount_currency * 2,
})
self.assertRecordValues(statement_line, [{
'amount': amount * 2,
'amount_currency': amount_currency * 2,
}])
self.assertRecordValues(liquidity_lines, [{
**expected_liquidity_values,
'debit': expected_liquidity_values.get('debit', 0.0) * 2,
'credit': expected_liquidity_values.get('credit', 0.0) * 2,
'amount_currency': expected_liquidity_values.get('amount_currency', 0.0) * 2,
}])
self.assertRecordValues(suspense_lines, [{
'debit': expected_counterpart_values.get('debit', 0.0) * 2,
'credit': expected_counterpart_values.get('credit', 0.0) * 2,
'amount_currency': expected_counterpart_values.get('amount_currency', 0.0) * 2,
}])
# Check the account.bank.statement.line is still correct after editing the account.move.
statement_line.move_id.write({'line_ids': [
(1, liquidity_lines.id, {
'debit': expected_liquidity_values.get('debit', 0.0),
'credit': expected_liquidity_values.get('credit', 0.0),
'amount_currency': expected_liquidity_values.get('amount_currency', 0.0),
}),
(1, suspense_lines.id, {
'debit': expected_counterpart_values.get('debit', 0.0),
'credit': expected_counterpart_values.get('credit', 0.0),
'amount_currency': expected_counterpart_values.get('amount_currency', 0.0),
}),
]})
self.assertRecordValues(statement_line, [{
'amount': amount,
'amount_currency': amount_currency,
}])
def _test_edition_customer_and_supplier_flows(
self,
amount, amount_currency,
journal_currency, foreign_currency,
expected_liquidity_values, expected_counterpart_values):
''' Test '_test_statement_line_edition' using the customer (positive amounts)
& the supplier flow (negative amounts).
:param amount: The amount in journal's currency.
:param amount_currency: The amount in the foreign currency.
:param journal_currency: The journal's currency as a res.currency record.
:param foreign_currency: The foreign currency as a res.currency record.
:param expected_liquidity_values: The expected account.move.line values for the liquidity line.
:param expected_counterpart_values: The expected account.move.line values for the counterpart line.
'''
# Check the full process with positive amount (customer process).
self._test_statement_line_edition(
self.bank_journal_2,
amount, amount_currency,
journal_currency, foreign_currency,
expected_liquidity_values,
expected_counterpart_values,
)
# Check the full process with negative amount (supplier process).
self._test_statement_line_edition(
self.bank_journal_3,
-amount, -amount_currency,
journal_currency, foreign_currency,
{
**expected_liquidity_values,
'debit': expected_liquidity_values.get('credit', 0.0),
'credit': expected_liquidity_values.get('debit', 0.0),
'amount_currency': -expected_liquidity_values.get('amount_currency', 0.0),
},
{
**expected_counterpart_values,
'debit': expected_counterpart_values.get('credit', 0.0),
'credit': expected_counterpart_values.get('debit', 0.0),
'amount_currency': -expected_counterpart_values.get('amount_currency', 0.0),
},
)
def test_edition_journal_curr_2_statement_curr_3(self):
self._test_edition_customer_and_supplier_flows(
# pylint: disable=bad-whitespace
80.0, 120.0,
self.currency_2, self.currency_3,
{'debit': 40.0, 'credit': 0.0, 'amount_currency': 80.0, 'currency_id': self.currency_2.id},
{'debit': 0.0, 'credit': 40.0, 'amount_currency': -120.0, 'currency_id': self.currency_3.id},
)
def test_edition_journal_curr_2_statement_curr_1(self):
self._test_edition_customer_and_supplier_flows(
# pylint: disable=bad-whitespace
120.0, 80.0,
self.currency_2, self.currency_1,
{'debit': 80.0, 'credit': 0.0, 'amount_currency': 120.0, 'currency_id': self.currency_2.id},
{'debit': 0.0, 'credit': 80.0, 'amount_currency': -80.0, 'currency_id': self.currency_1.id},
)
def test_edition_journal_curr_1_statement_curr_2(self):
self._test_edition_customer_and_supplier_flows(
# pylint: disable=bad-whitespace
80.0, 120.0,
self.currency_1, self.currency_2,
{'debit': 80.0, 'credit': 0.0, 'amount_currency': 80.0, 'currency_id': self.currency_1.id},
{'debit': 0.0, 'credit': 80.0, 'amount_currency': -120.0, 'currency_id': self.currency_2.id},
)
def test_edition_journal_curr_2_statement_false(self):
self._test_edition_customer_and_supplier_flows(
# pylint: disable=bad-whitespace
80.0, 0.0,
self.currency_2, False,
{'debit': 40.0, 'credit': 0.0, 'amount_currency': 80.0, 'currency_id': self.currency_2.id},
{'debit': 0.0, 'credit': 40.0, 'amount_currency': -80.0, 'currency_id': self.currency_2.id},
)
def test_edition_journal_curr_1_statement_false(self):
self._test_edition_customer_and_supplier_flows(
# pylint: disable=bad-whitespace
80.0, 0.0,
self.currency_1, False,
{'debit': 80.0, 'credit': 0.0, 'amount_currency': 80.0, 'currency_id': self.currency_1.id},
{'debit': 0.0, 'credit': 80.0, 'amount_currency': -80.0, 'currency_id': self.currency_1.id},
)
def test_zero_amount_journal_curr_1_statement_curr_2(self):
self.bank_journal_2.currency_id = self.currency_1
statement_line = self.env['account.bank.statement.line'].create({
'journal_id': self.bank_journal_2.id,
'date': '2019-01-01',
'payment_ref': 'line_1',
'partner_id': self.partner_a.id,
'foreign_currency_id': self.currency_2.id,
'amount': 0.0,
'amount_currency': 10.0,
})
self.assertRecordValues(statement_line.move_id.line_ids, [
# pylint: disable=bad-whitespace
{'debit': 0.0, 'credit': 0.0, 'amount_currency': 0.0, 'currency_id': self.currency_1.id},
{'debit': 0.0, 'credit': 0.0, 'amount_currency': -10.0, 'currency_id': self.currency_2.id},
])
def test_zero_amount_journal_curr_2_statement_curr_1(self):
self.bank_journal_2.currency_id = self.currency_2
statement_line = self.env['account.bank.statement.line'].create({
'journal_id': self.bank_journal_2.id,
'date': '2019-01-01',
'payment_ref': 'line_1',
'partner_id': self.partner_a.id,
'foreign_currency_id': self.currency_1.id,
'amount': 0.0,
'amount_currency': 10.0,
})
self.assertRecordValues(statement_line.move_id.line_ids, [
# pylint: disable=bad-whitespace
{'debit': 10.0, 'credit': 0.0, 'amount_currency': 0.0, 'currency_id': self.currency_2.id},
{'debit': 0.0, 'credit': 10.0, 'amount_currency': -10.0, 'currency_id': self.currency_1.id},
])
def test_zero_amount_journal_curr_2_statement_curr_3(self):
self.bank_journal_2.currency_id = self.currency_2
statement_line = self.env['account.bank.statement.line'].create({
'journal_id': self.bank_journal_2.id,
'date': '2019-01-01',
'payment_ref': 'line_1',
'partner_id': self.partner_a.id,
'foreign_currency_id': self.currency_3.id,
'amount': 0.0,
'amount_currency': 10.0,
})
self.assertRecordValues(statement_line.move_id.line_ids, [
# pylint: disable=bad-whitespace
{'debit': 0.0, 'credit': 0.0, 'amount_currency': 0.0, 'currency_id': self.currency_2.id},
{'debit': 0.0, 'credit': 0.0, 'amount_currency': -10.0, 'currency_id': self.currency_3.id},
])
def test_constraints(self):
def assertStatementLineConstraint(statement_line_vals):
with self.assertRaises(Exception), self.cr.savepoint():
self.env['account.bank.statement.line'].create(statement_line_vals)
statement_line_vals = {
'journal_id': self.bank_journal_2.id,
'date': '2019-01-01',
'payment_ref': 'line_1',
'partner_id': self.partner_a.id,
'foreign_currency_id': False,
'amount': 10.0,
'amount_currency': 0.0,
}
# ==== Test constraints at creation ====
# Can't have a stand alone amount in foreign currency without foreign currency set.
assertStatementLineConstraint({
**statement_line_vals,
'amount_currency': 10.0,
})
# Can't have a foreign currency set without amount in foreign currency.
assertStatementLineConstraint({
**statement_line_vals,
'foreign_currency_id': self.currency_2.id,
})
# ==== Test constraints at edition ====
st_line = self.env['account.bank.statement.line'].create(statement_line_vals)
# You can't messed up the journal entry by adding another liquidity line.
addition_lines_to_create = [
{
'debit': 1.0,
'credit': 0,
'account_id': self.bank_journal_2.default_account_id.id,
'move_id': st_line.move_id.id,
},
{
'debit': 0,
'credit': 1.0,
'account_id': self.company_data['default_account_revenue'].id,
'move_id': st_line.move_id.id,
},
]
with self.assertRaises(UserError), self.cr.savepoint():
st_line.move_id.write({
'line_ids': [(0, 0, vals) for vals in addition_lines_to_create]
})
with self.assertRaises(UserError), self.cr.savepoint():
st_line.line_ids.create(addition_lines_to_create)
def test_statement_line_move_onchange_1(self):
''' Test the consistency between the account.bank.statement.line and the generated account.move.lines
using the form view emulator.
'''
# Check the initial state of the statement line.
self.assertBankStatementLine(self.statement_line, self.expected_st_line,
[self.expected_counterpart_line, self.expected_bank_line])
# Inverse the amount + change them.
self.statement_line.write({
'amount': -2000.0,
'amount_currency': -4000.0,
'foreign_currency_id': self.currency_3.id,
})
self.assertBankStatementLine(self.statement_line, {
**self.expected_st_line,
'amount': -2000.0,
'amount_currency': -4000.0,
'foreign_currency_id': self.currency_3.id,
}, [
{
**self.expected_bank_line,
'debit': 0.0,
'credit': 2000.0,
'amount_currency': -2000.0,
'currency_id': self.currency_1.id,
},
{
**self.expected_counterpart_line,
'debit': 2000.0,
'credit': 0.0,
'amount_currency': 4000.0,
'currency_id': self.currency_3.id,
},
])
# Check changing the label and the partner.
self.statement_line.write({
'payment_ref': 'line_1 (bis)',
'partner_id': self.partner_b.id,
})
self.assertBankStatementLine(self.statement_line, {
**self.expected_st_line,
'payment_ref': self.statement_line.payment_ref,
'partner_id': self.statement_line.partner_id.id,
'amount': -2000.0,
'amount_currency': -4000.0,
'foreign_currency_id': self.currency_3.id,
}, [
{
**self.expected_bank_line,
'name': self.statement_line.payment_ref,
'partner_id': self.statement_line.partner_id.id,
'debit': 0.0,
'credit': 2000.0,
'amount_currency': -2000.0,
'currency_id': self.currency_1.id,
},
{
**self.expected_counterpart_line,
'name': self.statement_line.payment_ref,
'partner_id': self.statement_line.partner_id.id,
'debit': 2000.0,
'credit': 0.0,
'amount_currency': 4000.0,
'currency_id': self.currency_3.id,
},
])
def test_prepare_counterpart_amounts_using_st_line_rate(self):
def assertAppliedRate(
journal_currency, foreign_currency, aml_currency,
amount, amount_currency, aml_amount_currency, aml_balance,
expected_amount_currency, expected_balance,
):
journal = self.bank_journal_1.copy()
journal.currency_id = journal_currency
statement_line = self.env['account.bank.statement.line'].create({
'journal_id': journal.id,
'date': '2019-01-01',
'payment_ref': 'test_prepare_counterpart_amounts_using_st_line_rate',
'foreign_currency_id': foreign_currency.id if foreign_currency != journal_currency else None,
'amount': amount,
'amount_currency': amount_currency if foreign_currency != journal_currency else 0.0,
})
res = statement_line._prepare_counterpart_amounts_using_st_line_rate(aml_currency, -aml_balance,
-aml_amount_currency)
self.assertAlmostEqual(res['amount_currency'], expected_amount_currency)
self.assertAlmostEqual(res['balance'], expected_balance)
for params in (
(self.currency_2, self.currency_3, self.currency_3, 80.0, 120.0, 120.0, 20.0, -120.0, -40.0),
(self.currency_2, self.currency_1, self.currency_2, 120.0, 80.0, 120.0, 40.0, -80.0, -80.0),
(self.currency_2, self.currency_3, self.currency_2, 80.0, 120.0, 80.0, 26.67, -120.0, -40.0),
(self.currency_2, self.currency_3, self.currency_4, 80.0, 120.0, 480.0, 40.0, -120.0, -40.0),
(self.currency_1, self.currency_2, self.currency_2, 80.0, 120.0, 120.0, 40.0, -120.0, -80.0),
(self.currency_1, self.currency_2, self.currency_3, 80.0, 120.0, 480.0, 80.0, -120.0, -80.0),
(self.currency_2, self.currency_2, self.currency_2, 80.0, 80.0, 80.0, 26.67, -80.0, -40.0),
(self.currency_2, self.currency_2, self.currency_3, 80.0, 80.0, 240.0, 40.0, -80.0, -40.0),
(self.currency_1, self.currency_1, self.currency_3, 80.0, 80.0, 480.0, 80.0, -80.0, -80.0),
(self.currency_2, self.currency_1, self.currency_1, 120.0, 80.0, 80.0, 80.0, -80.0, -80.0),
(self.currency_2, self.currency_3, self.currency_1, 80.0, 120.0, 40.0, 40.0, -120.0, -40.0),
(self.currency_1, self.currency_2, self.currency_1, 80.0, 120.0, 80.0, 80.0, -120.0, -80.0),
(self.currency_2, self.currency_2, self.currency_1, 80.0, 80.0, 40.0, 40.0, -80.0, -40.0),
(self.currency_1, self.currency_1, self.currency_1, 80.0, 80.0, 80.0, 80.0, -80.0, -80.0),
):
with self.subTest(params=params):
assertAppliedRate(*params)
def test_for_presence_single_suspense_line(self):
statement_line = self.env['account.bank.statement.line'].create({
'journal_id': self.bank_journal_3.id,
'date': '2019-01-01',
'payment_ref': 'line_1',
'amount': 0.0,
})
with self.assertRaises(UserError):
statement_line.line_ids = [Command.create({
'account_id': statement_line.journal_id.suspense_account_id.id,
'balance': 0.0,
})]
def test_zero_amount_statement_line(self):
''' Ensure the statement line is directly marked as reconciled when having an amount of zero. '''
self.company_data['company'].account_journal_suspense_account_id.reconcile = False
statement = self.env['account.bank.statement'].with_context(skip_check_amounts_currencies=True).create({
'name': 'test_statement',
'line_ids': [
(0, 0, {
'date': '2019-01-01',
'payment_ref': "Happy new year",
'amount': 0.0,
'journal_id': self.bank_journal_2.id,
}),
],
})
statement_line = statement.line_ids
self.assertRecordValues(statement_line, [{'is_reconciled': True, 'amount_residual': 0.0}])
def test_statement_valid_complete_1(self):
self.env.user.company_id = self.company_data_2['company']
# create a valid and complete statement as the first lines (no statement before)
line1 = self.create_bank_transaction(1, '2020-01-10')
line2 = self.create_bank_transaction(2, '2020-01-11')
statement1 = self.env['account.bank.statement'].with_context(st_line_id=line1.id, active_ids=[line1.id, line2.id]).create({})
self.assertRecordValues(statement1, [{
'is_complete': True,
'is_valid': True,
'balance_start': 0,
'balance_end': 3,
'balance_end_real': 3,
}])
# remove the first line, so not complete but it is still valid because there is no statement before
line1.statement_id = False
self.assertRecordValues(statement1, [{
'is_complete': False,
'is_valid': True,
'balance_start': 0,
'balance_end': 2,
'balance_end_real': 3,
}])
# create a new line in the statement to make it complete again. Starting value does not match the last line
# but it is still valid because it is the first statement
line3 = self.create_bank_transaction(1, '2020-01-12', statement=statement1)
statement1.invalidate_recordset(['is_valid'])
self.assertRecordValues(statement1, [{
'is_complete': True,
'is_valid': True,
}])
# add a statement to the first line, statement1 is still complete but not valid because balance start
# does not match the previous statement
statement2 = self.env['account.bank.statement'].create({
'line_ids': [Command.set(line1.ids)],
'balance_end_real': 1,
})
(statement1 + statement2).invalidate_recordset(['is_valid'])
self.assertRecordValues(statement1 + statement2, [{
'is_complete': True,
'is_valid': False,
}, {
'is_complete': True,
'is_valid': True, # first statement
}])
# Fix the statement balance start, the balance_end_real is computed, making it complete
statement1.balance_start = 1
statement1.invalidate_recordset(['is_valid'])
self.assertRecordValues(statement1, [{
'is_complete': True,
'is_valid': True,
}])
# change the prev statement so the end balance does not match the start balance of statement 1
statement2.balance_end_real = 10
statement2.flush_recordset(['balance_end_real'])
statement1.invalidate_recordset(['is_valid'])
self.assertRecordValues(statement1, [{
'is_complete': True,
'is_valid': False,
}])
# make the statement valid again, but keep it incomplete
statement1.write({'balance_start': 10, 'balance_end_real': 3})
statement1.invalidate_recordset(['is_valid'])
self.assertRecordValues(statement1, [{
'is_complete': False,
'is_valid': True,
}])
# and complete again by adding a new transaction to it
line4 = self.create_bank_transaction(-10, '2020-01-13', statement=statement1)
(statement1 + statement2).invalidate_recordset(['is_valid'])
self.assertRecordValues(statement1 + statement2, [{
'is_complete': True,
'is_valid': True,
'date': fields.Date.from_string('2020-01-13'),
}, {
'is_complete': False,
'is_valid': True,
'date': fields.Date.from_string('2020-01-10'),
}])
# check point
self.assertRecordValues(line1 + line2 + line3 + line4, [
{'date': fields.Date.from_string('2020-01-10'), 'statement_id': statement2.id},
{'date': fields.Date.from_string('2020-01-11'), 'statement_id': statement1.id},
{'date': fields.Date.from_string('2020-01-12'), 'statement_id': statement1.id},
{'date': fields.Date.from_string('2020-01-13'), 'statement_id': statement1.id},
])
# changing statement 2 balance makes statement 1 invalid,
# but making statement 1 the first statement should make it valid again
statement2.balance_end_real = 100
statement2.flush_recordset(['balance_end_real'])
statement1.invalidate_recordset(['is_valid'])
self.assertRecordValues(statement1, [{
'is_valid': False,
}])
line1.statement_id = False
line1.flush_model()
statement2.flush_model()
statement1.invalidate_model(['is_valid'])
self.assertRecordValues(statement1, [{
'is_valid': True,
}])
# having a gap in the statement shouldn't make it invalid
line3.statement_id = False
statement1.flush_recordset(['is_valid'])
self.assertRecordValues(statement1, [{
'is_valid': True,
}])
# Change the statement on one of the lines of statement 1
statement3 = self.env['account.bank.statement'].create({
'line_ids': [Command.set(line4.ids)],
'balance_start': -5,
})
(statement1 + statement3).flush_recordset(['is_valid'])
self.assertRecordValues(statement1 + statement3, [{
'is_valid': True,
}, {
'is_valid': False, # balance does not match with statement1
}])
# changing statement1 end_balance should change the validity of statement3
statement1.balance_end_real = -5
statement1.flush_recordset(['balance_end_real'])
(statement1 + statement3).invalidate_recordset(['is_valid'])
self.assertRecordValues(statement1 + statement3, [{
'is_valid': True,
}, {
'is_valid': True, # balance start matches previous end, despite the gap
}])
# check point
self.assertRecordValues(line1 + line2 + line3 + line4, [
{'date': fields.Date.from_string('2020-01-10'), 'statement_id': False},
{'date': fields.Date.from_string('2020-01-11'), 'statement_id': statement1.id},
{'date': fields.Date.from_string('2020-01-12'), 'statement_id': False},
{'date': fields.Date.from_string('2020-01-13'), 'statement_id': statement3.id},
])
self.assertRecordValues(statement1 + statement2 + statement3, [
{'is_valid': True, 'balance_start': 10, 'balance_end_real': -5,
'date': fields.Date.from_string('2020-01-11')},
{'is_valid': True, 'balance_start': False, 'balance_end_real': 100, 'date': False},
{'is_valid': True, 'balance_start': -5, 'balance_end_real': -15,
'date': fields.Date.from_string('2020-01-13')},
])
# adding a statement to the first line should make statement1 invalid
line1.statement_id = statement2
statement2.flush_model()
(statement1 + statement2).invalidate_recordset(['is_valid'])
self.assertRecordValues(statement1 + statement2, [{'is_valid': False}, {'is_valid': True}])
# moving statement2 the line between statement1 and statement3 should make statement1 valid again
# and statement3 invalid
statement2.line_ids = line3
statement2.flush_model()
(statement1 + statement2 + statement3).invalidate_recordset(['is_valid'])
self.assertRecordValues(statement1 + statement2 + statement3, [
{'is_valid': True}, {'is_valid': False}, {'is_valid': False},
])
def test_statement_line_ordering(self):
self.env.user.company_id = self.company_data_2['company']
# the line numbers are chosen based on the order of the lines in the list view
line7 = self.create_bank_transaction(7, '2020-01-10', sequence=1)
line8 = self.create_bank_transaction(8, '2020-01-10', sequence=2)
line2 = self.create_bank_transaction(2, '2020-01-13')
line6 = self.create_bank_transaction(6, '2020-01-11')
line5 = self.create_bank_transaction(5, '2020-01-12', sequence=3)
line4 = self.create_bank_transaction(4, '2020-01-12', sequence=2)
line1 = self.create_bank_transaction(1, '2020-01-13')
line3 = self.create_bank_transaction(3, '2020-01-12', sequence=1)
self.assertRecordValues(
self.env['account.bank.statement.line'].search([('company_id', '=', self.env.company.id)]),
[
{'amount': 1, 'running_balance': 36, 'statement_id': False},
{'amount': 2, 'running_balance': 35, 'statement_id': False},
{'amount': 3, 'running_balance': 33, 'statement_id': False},
{'amount': 4, 'running_balance': 30, 'statement_id': False},
{'amount': 5, 'running_balance': 26, 'statement_id': False},
{'amount': 6, 'running_balance': 21, 'statement_id': False},
{'amount': 7, 'running_balance': 15, 'statement_id': False},
{'amount': 8, 'running_balance': 8, 'statement_id': False},
],
)
# Same but with a subset of lines to ensure the balance is not only computed based on selected records.
self.env['account.bank.statement.line'].invalidate_model(fnames=['running_balance'])
self.assertRecordValues(
self.env['account.bank.statement.line'].search([
('company_id', '=', self.env.company.id),
('amount', '>=', 3),
('amount', '<=', 6),
]),
[
{'amount': 3, 'running_balance': 33},
{'amount': 4, 'running_balance': 30},
{'amount': 5, 'running_balance': 26},
{'amount': 6, 'running_balance': 21},
],
)
# Put line2 -> line4 inside a statement with a wrong balance_end_real.
(line2 + line3 + line4).statement_id = statement1 = \
self.env['account.bank.statement'].create({'balance_start': 20, 'balance_end_real': 29})
self.assertRecordValues(statement1, [{
'is_complete': True,
}])
statement1.invalidate_recordset(['is_valid'])
statement1.balance_start = 26
self.assertRecordValues(statement1, [{
'is_complete': True,
'balance_end_real': 35, # autocorrect
}])
# line3, line4 and line5 have the same date. Move line5 at the first place using the sequence.
line5.sequence = -1
statement1.invalidate_recordset(['is_valid'])
self.env['account.bank.statement.line'].invalidate_model(fnames=['running_balance'])
self.assertRecordValues(statement1, [{
'is_complete': True,
}])
statement1.balance_start = 21
statement1.invalidate_recordset(['is_valid'])
self.assertRecordValues(statement1, [{
'is_complete': True,
'balance_end_real': 30,
}])
self.env['account.bank.statement.line'].invalidate_model(fnames=['running_balance'])
self.assertRecordValues(
self.env['account.bank.statement.line'].search([('company_id', '=', self.env.company.id)]),
[
# pylint: disable=C0326
{'amount': 1, 'running_balance': 36, 'statement_id': False},
{'amount': 2, 'running_balance': 35, 'statement_id': statement1.id},
{'amount': 5, 'running_balance': 33, 'statement_id': False},
{'amount': 3, 'running_balance': 28, 'statement_id': statement1.id},
{'amount': 4, 'running_balance': 25, 'statement_id': statement1.id},
{'amount': 6, 'running_balance': 21, 'statement_id': False},
{'amount': 7, 'running_balance': 15, 'statement_id': False},
{'amount': 8, 'running_balance': 8, 'statement_id': False},
],
)
line8.amount = 18
self.env['account.bank.statement.line'].invalidate_model(fnames=['running_balance'])
self.assertRecordValues(
self.env['account.bank.statement.line'].search([('company_id', '=', self.env.company.id)]),
[
# pylint: disable=C0326
{'amount': 1, 'running_balance': 36, 'statement_id': False},
{'amount': 2, 'running_balance': 35, 'statement_id': statement1.id},
{'amount': 5, 'running_balance': 33, 'statement_id': False},
{'amount': 3, 'running_balance': 28, 'statement_id': statement1.id},
{'amount': 4, 'running_balance': 25, 'statement_id': statement1.id},
{'amount': 6, 'running_balance': 31, 'statement_id': False},
{'amount': 7, 'running_balance': 25, 'statement_id': False},
{'amount': 18, 'running_balance': 18, 'statement_id': False},
],
)
line5.amount = 15
self.env['account.bank.statement.line'].invalidate_model(fnames=['running_balance'])
self.assertRecordValues(
self.env['account.bank.statement.line'].search([('company_id', '=', self.env.company.id)]),
[
# pylint: disable=C0326
{'amount': 1, 'running_balance': 46, 'statement_id': False},
{'amount': 2, 'running_balance': 45, 'statement_id': statement1.id},
{'amount': 15, 'running_balance': 43, 'statement_id': False},
{'amount': 3, 'running_balance': 28, 'statement_id': statement1.id},
{'amount': 4, 'running_balance': 25, 'statement_id': statement1.id},
{'amount': 6, 'running_balance': 31, 'statement_id': False},
{'amount': 7, 'running_balance': 25, 'statement_id': False},
{'amount': 18, 'running_balance': 18, 'statement_id': False},
],
)
line7.unlink()
self.env['account.bank.statement.line'].invalidate_model(fnames=['running_balance'])
self.assertRecordValues(
self.env['account.bank.statement.line'].search([('company_id', '=', self.env.company.id)]),
[
# pylint: disable=C0326
{'amount': 1, 'running_balance': 46, 'statement_id': False},
{'amount': 2, 'running_balance': 45, 'statement_id': statement1.id},
{'amount': 15, 'running_balance': 43, 'statement_id': False},
{'amount': 3, 'running_balance': 28, 'statement_id': statement1.id},
{'amount': 4, 'running_balance': 25, 'statement_id': statement1.id},
{'amount': 6, 'running_balance': 24, 'statement_id': False},
{'amount': 18, 'running_balance': 18, 'statement_id': False},
],
)
line1.move_id.button_cancel()
line6.move_id.button_cancel()
line6.move_id.button_draft()
self.env['account.bank.statement.line'].invalidate_model(fnames=['running_balance'])
self.assertRecordValues(
self.env['account.bank.statement.line'].search([('company_id', '=', self.env.company.id)]),
[
# pylint: disable=C0326
{'amount': 1, 'running_balance': 45, 'statement_id': False},
{'amount': 2, 'running_balance': 45, 'statement_id': statement1.id},
{'amount': 15, 'running_balance': 43, 'statement_id': False},
{'amount': 3, 'running_balance': 28, 'statement_id': statement1.id},
{'amount': 4, 'running_balance': 25, 'statement_id': statement1.id},
{'amount': 6, 'running_balance': 18, 'statement_id': False},
{'amount': 18, 'running_balance': 18, 'statement_id': False},
],
)
# remove the anchor point
statement1.line_ids = False
self.env['account.bank.statement.line'].invalidate_model(fnames=['running_balance'])
self.assertRecordValues(
self.env['account.bank.statement.line'].search([('company_id', '=', self.env.company.id)]),
[
# pylint: disable=C0326
{'amount': 1, 'running_balance': 42, 'statement_id': False},
{'amount': 2, 'running_balance': 42, 'statement_id': False},
{'amount': 15, 'running_balance': 40, 'statement_id': False},
{'amount': 3, 'running_balance': 25, 'statement_id': False},
{'amount': 4, 'running_balance': 22, 'statement_id': False},
{'amount': 6, 'running_balance': 18, 'statement_id': False},
{'amount': 18, 'running_balance': 18, 'statement_id': False},
],
)
def test_statement_split(self):
self.env.user.company_id = self.company_data_2['company']
# the line numbers are chosen based on the order of the lines in the list view
line7 = self.create_bank_transaction(7, '2020-01-10', sequence=1)
line8 = self.create_bank_transaction(8, '2020-01-10', sequence=2)
line2 = self.create_bank_transaction(2, '2020-01-13')
line6 = self.create_bank_transaction(6, '2020-01-11')
_line5 = self.create_bank_transaction(5, '2020-01-12', sequence=3)
line4 = self.create_bank_transaction(4, '2020-01-12', sequence=2)
line1 = self.create_bank_transaction(1, '2020-01-13')
line3 = self.create_bank_transaction(3, '2020-01-12', sequence=1)
# Split the last 2 lines by splitting on the line before last.
statement1 = self.env['account.bank.statement'].with_context({'split_line_id': line7.id}).create({})
self.assertRecordValues(statement1, [{
'balance_start': 0.0,
'balance_end': 15.0,
'balance_end_real': 15.0,
'is_complete': True,
'is_valid': True,
}])
self.assertRecordValues(line7 + line8 + line6, [
{'amount': 7, 'statement_id': statement1.id},
{'amount': 8, 'statement_id': statement1.id},
{'amount': 6, 'statement_id': False},
])
# Split on a line adjutant to another statement
statement2 = self.env['account.bank.statement'].with_context({'split_line_id': line6.id}).create({})
self.assertRecordValues(statement2, [{
'balance_start': 15.0,
'balance_end': 21.0,
'balance_end_real': 21.0,
'is_complete': True,
'is_valid': True,
}])
self.assertRecordValues(
self.env['account.bank.statement.line'].search([('company_id', '=', self.env.company.id)]),
[
# pylint: disable=C0326
{'amount': 1, 'running_balance': 36, 'statement_id': False},
{'amount': 2, 'running_balance': 35, 'statement_id': False},
{'amount': 3, 'running_balance': 33, 'statement_id': False},
{'amount': 4, 'running_balance': 30, 'statement_id': False},
{'amount': 5, 'running_balance': 26, 'statement_id': False},
{'amount': 6, 'running_balance': 21, 'statement_id': statement2.id},
{'amount': 7, 'running_balance': 15, 'statement_id': statement1.id},
{'amount': 8, 'running_balance': 8, 'statement_id': statement1.id},
],
)
# Split on a line with a gap to another statement
statement1.unlink()
statement3 = self.env['account.bank.statement'].with_context({'split_line_id': line3.id}).create({})
self.assertRecordValues(statement3, [{
'balance_start': 21.0,
'balance_end': 33.0,
'balance_end_real': 33.0,
'is_complete': True,
'is_valid': True,
}])
self.assertRecordValues(
self.env['account.bank.statement.line'].search([('company_id', '=', self.env.company.id)]),
[
# pylint: disable=C0326
{'amount': 1, 'running_balance': 36, 'statement_id': False},
{'amount': 2, 'running_balance': 35, 'statement_id': False},
{'amount': 3, 'running_balance': 33, 'statement_id': statement3.id},
{'amount': 4, 'running_balance': 30, 'statement_id': statement3.id},
{'amount': 5, 'running_balance': 26, 'statement_id': statement3.id},
{'amount': 6, 'running_balance': 21, 'statement_id': statement2.id},
{'amount': 7, 'running_balance': 15, 'statement_id': False},
{'amount': 8, 'running_balance': 8, 'statement_id': False},
],
)
# Split on a line with a single line statement
statement4 = self.env['account.bank.statement'].with_context({'split_line_id': line6.id}).create({})
self.assertRecordValues(statement3 + statement4, [
{
'balance_start': 21.0,
'balance_end': 33.0,
'balance_end_real': 33.0,
'is_complete': True,
'is_valid': True,
},
{
'balance_start': 0.0,
'balance_end': 21.0,
'balance_end_real': 21.0,
'is_complete': True,
'is_valid': True,
},
])
self.assertRecordValues(
self.env['account.bank.statement.line'].search([('company_id', '=', self.env.company.id)]),
[
# pylint: disable=C0326
{'amount': 1, 'running_balance': 36, 'statement_id': False},
{'amount': 2, 'running_balance': 35, 'statement_id': False},
{'amount': 3, 'running_balance': 33, 'statement_id': statement3.id},
{'amount': 4, 'running_balance': 30, 'statement_id': statement3.id},
{'amount': 5, 'running_balance': 26, 'statement_id': statement3.id},
{'amount': 6, 'running_balance': 21, 'statement_id': statement4.id},
{'amount': 7, 'running_balance': 15, 'statement_id': statement4.id},
{'amount': 8, 'running_balance': 8, 'statement_id': statement4.id},
],
)
# check double split on a single line
statement5 = self.env['account.bank.statement'].with_context({'split_line_id': line2.id}).create({})
self.assertRecordValues(statement5, [{
'balance_start': 33.0,
'balance_end': 35.0,
'balance_end_real': 35.0,
'is_complete': True,
'is_valid': True,
}])
self.assertRecordValues(
self.env['account.bank.statement.line'].search([('company_id', '=', self.env.company.id)]),
[
# pylint: disable=C0326
{'amount': 1, 'running_balance': 36, 'statement_id': False},
{'amount': 2, 'running_balance': 35, 'statement_id': statement5.id},
{'amount': 3, 'running_balance': 33, 'statement_id': statement3.id},
{'amount': 4, 'running_balance': 30, 'statement_id': statement3.id},
{'amount': 5, 'running_balance': 26, 'statement_id': statement3.id},
{'amount': 6, 'running_balance': 21, 'statement_id': statement4.id},
{'amount': 7, 'running_balance': 15, 'statement_id': statement4.id},
{'amount': 8, 'running_balance': 8, 'statement_id': statement4.id},
],
)
statement6 = self.env['account.bank.statement'].with_context({'split_line_id': line2.id}).create({'reference': '6'})
self.assertRecordValues(statement6, [{
'balance_start': 33.0,
'balance_end': 35.0,
'balance_end_real': 35.0,
'is_complete': True,
'is_valid': True,
}])
self.assertRecordValues(
self.env['account.bank.statement.line'].search([('company_id', '=', self.env.company.id)]),
[
# pylint: disable=C0326
{'amount': 1, 'running_balance': 36, 'statement_id': False},
{'amount': 2, 'running_balance': 35, 'statement_id': statement6.id},
{'amount': 3, 'running_balance': 33, 'statement_id': statement3.id},
{'amount': 4, 'running_balance': 30, 'statement_id': statement3.id},
{'amount': 5, 'running_balance': 26, 'statement_id': statement3.id},
{'amount': 6, 'running_balance': 21, 'statement_id': statement4.id},
{'amount': 7, 'running_balance': 15, 'statement_id': statement4.id},
{'amount': 8, 'running_balance': 8, 'statement_id': statement4.id},
],
)
# Split in the middle of a statement
statement7 = self.env['account.bank.statement'].with_context({'split_line_id': line4.id}).create({})
self.assertRecordValues(statement3 + statement7, [
{
'balance_start': 21.0,
'balance_end': 24.0,
'balance_end_real': 33.0,
'is_complete': False,
'is_valid': False,
},
{
'balance_start': 21.0,
'balance_end': 30.0,
'balance_end_real': 30.0,
'is_complete': True,
'is_valid': True,
},
])
# Fix statement3
statement3._compute_balance_start()
self.assertRecordValues(statement3, [{
'balance_start': 30.0,
'balance_end': 33.0,
'balance_end_real': 33.0,
'is_complete': True,
'is_valid': True,
}])
self.assertRecordValues(
self.env['account.bank.statement.line'].search([('company_id', '=', self.env.company.id)]),
[
# pylint: disable=C0326
{'amount': 1, 'running_balance': 36, 'statement_id': False},
{'amount': 2, 'running_balance': 35, 'statement_id': statement6.id},
{'amount': 3, 'running_balance': 33, 'statement_id': statement3.id},
{'amount': 4, 'running_balance': 30, 'statement_id': statement7.id},
{'amount': 5, 'running_balance': 26, 'statement_id': statement7.id},
{'amount': 6, 'running_balance': 21, 'statement_id': statement4.id},
{'amount': 7, 'running_balance': 15, 'statement_id': statement4.id},
{'amount': 8, 'running_balance': 8, 'statement_id': statement4.id},
],
)
# split at start of another statement
statement8 = self.env['account.bank.statement'].with_context({'split_line_id': line6.id}).create({})
self.assertRecordValues(statement7 + statement8, [
{
'balance_end_real': 30.0,
'balance_end': 30.0,
'balance_start': 21.0,
'is_complete': True,
'is_valid': True,
},
{
'balance_end_real': 21.0,
'balance_end': 21.0,
'balance_start': 0.0,
'is_complete': True,
'is_valid': True,
},
])
self.assertRecordValues(
self.env['account.bank.statement.line'].search([('company_id', '=', self.env.company.id)]),
[
# pylint: disable=C0326
{'amount': 1, 'statement_id': False},
{'amount': 2, 'statement_id': statement6.id},
{'amount': 3, 'statement_id': statement3.id},
{'amount': 4, 'statement_id': statement7.id},
{'amount': 5, 'statement_id': statement7.id},
{'amount': 6, 'statement_id': statement8.id},
{'amount': 7, 'statement_id': statement8.id},
{'amount': 8, 'statement_id': statement8.id},
],
)
# split at end of another statement
statement9 = self.env['account.bank.statement'].with_context({'split_line_id': line8.id}).create({})
self.assertRecordValues(statement8 + statement9, [
{
'balance_end_real': 21.0,
'balance_end': 13.0,
'balance_start': 0.0,
'is_complete': False,
'is_valid': False,
},
{
'balance_end_real': 8.0,
'balance_end': 8.0,
'balance_start': 0.0,
'is_complete': True,
'is_valid': True,
},
])
# Fix statement8
statement8._compute_balance_start() # TODO: add_to_compute not working, why?
self.assertRecordValues(statement8, [{
'balance_end_real': 21.0,
'balance_end': 21.0,
'balance_start': 8.0,
'is_complete': True,
'is_valid': True,
}])
self.assertRecordValues(
self.env['account.bank.statement.line'].search([('company_id', '=', self.env.company.id)]),
[
# pylint: disable=C0326
{'amount': 1, 'statement_id': False},
{'amount': 2, 'statement_id': statement6.id},
{'amount': 3, 'statement_id': statement3.id},
{'amount': 4, 'statement_id': statement7.id},
{'amount': 5, 'statement_id': statement7.id},
{'amount': 6, 'statement_id': statement8.id},
{'amount': 7, 'statement_id': statement8.id},
{'amount': 8, 'statement_id': statement9.id},
],
)
# split at most recent line
statement10 = self.env['account.bank.statement'].with_context({'split_line_id': line1.id}).create({})
self.assertRecordValues(statement10, [{
'balance_start': 35.0,
'balance_end': 36.0,
'balance_end_real': 36.0,
'is_complete': True,
'is_valid': True,
}])
self.assertRecordValues(
self.env['account.bank.statement.line'].search([('company_id', '=', self.env.company.id)]),
[
# pylint: disable=C0326
{'amount': 1, 'statement_id': statement10.id},
{'amount': 2, 'statement_id': statement6.id},
{'amount': 3, 'statement_id': statement3.id},
{'amount': 4, 'statement_id': statement7.id},
{'amount': 5, 'statement_id': statement7.id},
{'amount': 6, 'statement_id': statement8.id},
{'amount': 7, 'statement_id': statement8.id},
{'amount': 8, 'statement_id': statement9.id},
],
)
all_statements = self.env['account.bank.statement'].search([
('line_ids', '!=', False),
('company_id', '=', self.env.company.id),
])
all_statements.invalidate_recordset(['is_valid'])
self.assertRecordValues(all_statements, [{'is_valid': True, 'is_complete': True}] * len(all_statements))
def test_statement_with_canceled_lines(self):
line1 = self.create_bank_transaction(1, '2020-01-10', journal=self.bank_journal_2)
line2 = self.create_bank_transaction(2, '2020-01-11', journal=self.bank_journal_2)
statement1 = self.env['account.bank.statement'].create({
'line_ids': [Command.set((line1 + line2).ids)],
})
self.assertRecordValues(statement1, [{
'is_complete': True,
'is_valid': True,
'date': fields.Date.from_string(line2.date),
'balance_start': 0,
'balance_end_real': 3,
}])
# test canceling a line
line2.move_id.button_cancel()
self.assertRecordValues(statement1, [{
'is_complete': False,
'balance_end': 1,
'date': fields.Date.from_string(line1.date),
}])
# add a line with same amount as the canceled line makes statement1 complete again
line3 = self.create_bank_transaction(2, '2020-01-12', journal=self.bank_journal_2, statement=statement1)
self.assertRecordValues(statement1, [{
'is_complete': True,
'balance_end': 3,
'date': fields.Date.from_string(line3.date),
}])
# test adding a draft line to a statement, nothing should be changed in statement
line4 = self.create_bank_transaction(4, '2020-01-13', journal=self.bank_journal_2)
line4.move_id.button_cancel()
line4.move_id.button_draft()
statement1.line_ids |= line4
self.assertRecordValues(statement1, [{
'is_complete': True,
'balance_end': 3,
'date': fields.Date.from_string(line3.date),
}])
# test split with canceled/draft lines
statement2 = self.env['account.bank.statement'].with_context({'split_line_id': line2.id}).create({})
self.assertRecordValues(statement1 + statement2, [{
'is_complete': False,
'balance_end': 2,
'balance_start': 0,
'line_ids': [line4.id, line3.id],
}, {
'is_complete': True,
'balance_start': 0,
'balance_end': 1,
'line_ids': [line1.id, line2.id],
}])
# test cancel/draft all statement lines
# line 4 is draft, and we cancel line 3 so the statement should be empty
line3.move_id.button_cancel()
self.assertRecordValues(statement1, [{
'is_complete': False,
'balance_start': 0,
'balance_end': 0,
}])
# create a statement line with already canceled lines
statement3 = self.env['account.bank.statement'].create({
'line_ids': [Command.set((line3 + line4).ids)],
})
self.assertRecordValues(statement1 + statement3, [{
'is_complete': False,
'balance_start': 0,
'balance_end': 0,
}, {
'is_complete': False, # no posted transactions
'balance_start': 1, # from statement2's balance_end_real
'balance_end': 1, # no posted transactions
}])
def test_create_statement_line_with_inconsistent_currencies(self):
statement_line = self.env['account.bank.statement.line'].create({
'date': '2019-01-01',
'journal_id': self.bank_journal_1.id,
'payment_ref': "Happy new year",
'amount': 200.0,
'amount_currency': 200.0,
'foreign_currency_id': self.env.company.currency_id.id,
})
self.assertRecordValues(statement_line, [{
'currency_id': self.env.company.currency_id.id,
'foreign_currency_id': False,
'amount': 200.0,
'amount_currency': 0.0,
}])
def test_statement_balance_warnings(self):
''' Ensure that new statements have the correct opening/closing balances or warnings '''
lines = [
self.create_bank_transaction(amount, date, journal=self.bank_journal_2)
for amount, date in [
(10.0, '2019-01-01'),
(15.0, '2019-01-02'),
(20.0, '2019-01-03'),
(30.0, '2019-01-03'),
(40.0, '2019-01-04'),
(50.0, '2019-01-05'),
]
]
# new statement from single line
contexts = [{
'active_ids': [line.id],
'st_line_id': line.id,
} for line in lines[:3]]
self.assertRecordValues(self.env['account.bank.statement'].with_context(contexts[1]).new({}), [{
'balance_start': 10.0,
'balance_end_real': 25.0,
'is_valid': True,
'is_complete': True,
}])
st1 = self.env['account.bank.statement'].with_context(contexts[0]).create({'name': 'Statement 1'})
self.assertRecordValues(st1, [{
'balance_start': 0.0,
'balance_end_real': 10.0,
'is_valid': True,
'is_complete': True,
}])
self.assertEqual(lines[0].statement_id, st1)
self.assertRecordValues(self.env['account.bank.statement'].with_context(contexts[2]).new(), [{
'balance_start': 25.0,
'balance_end_real': 45.0,
'is_valid': False,
'is_complete': True,
}])
self.assertRecordValues(self.env['account.bank.statement'].with_context(contexts[1]).new(), [{
'balance_start': 10.0,
'balance_end_real': 25.0,
'is_valid': True,
'is_complete': True,
}])
# multi line edit, one line with statement
context = {
'active_ids': [line.id for line in lines[:3]],
'st_line_id': lines[2].id,
}
self.assertRecordValues(self.env['account.bank.statement'].with_context(context).new({}), [{
'balance_start': 0.0,
'balance_end_real': 45.0,
'is_valid': True,
'is_complete': True,
'line_ids': [lines[0].id, lines[1].id, lines[2].id],
}])
# multi line edit, skip lines
context = {
'active_ids': [line.id for line in lines[2:4]],
'st_line_id': lines[3].id,
}
self.assertRecordValues(self.env['account.bank.statement'].with_context(context).new({}), [{
'balance_start': 25.0,
'balance_end_real': 75.0,
'is_valid': False,
'is_complete': True,
'line_ids': [lines[2].id, lines[3].id],
}])
# multi line edit
expected_st_vals = [{
'balance_start': 10.0,
'balance_end_real': 75.0,
'is_valid': True,
'is_complete': True,
'line_ids': [lines[1].id, lines[2].id, lines[3].id],
}]
context = {
'active_ids': [line.id for line in lines[1:4]],
'st_line_id': lines[3].id,
}
self.assertRecordValues(self.env['account.bank.statement'].with_context(context).new({}), expected_st_vals)
# split button
self.assertRecordValues(self.env['account.bank.statement'].with_context({'split_line_id': lines[3].id}).new({}),
expected_st_vals)
# raise error if lines skipped during multi-edit
context = {
'active_ids': [lines[1].id, lines[3].id],
'st_line_id': lines[3].id,
}
with self.assertRaises(UserError):
self.env['account.bank.statement'].with_context(context).create({})
# create the second statement using split button
st2 = self.env['account.bank.statement'].with_context({'split_line_id': lines[-1].id}).create({'name': 'Statement 2'})
self.assertRecordValues(st2, [{
'balance_start': 10.0,
'balance_end_real': 165.0,
'is_valid': True,
'is_complete': True,
}])
def test_statement_attachments(self):
''' Ensure that attachments are properly linked to bank statements '''
attachment_vals = {
'datas': base64.b64encode(b'My attachment'),
'name': 'doc.txt',
}
attachment = self.env['ir.attachment'].create(attachment_vals)
statement = self.env['account.bank.statement'].create({
'name': 'test_statement',
'attachment_ids': [Command.set(attachment.ids)],
})
attachment = self.env['ir.attachment'].create(attachment_vals)
statement.write({'attachment_ids': [Command.link(attachment.id)]})
self.assertRecordValues(statement.attachment_ids, [
{'res_id': statement.id, 'res_model': 'account.bank.statement'},
{'res_id': statement.id, 'res_model': 'account.bank.statement'},
])
def test_statement_reverse_keeps_partner(self):
partner = self.env['res.partner'].create({
'name': 'Test Partner',
})
statement_line = self.env['account.bank.statement.line'].create({
'date': '2019-01-01',
'payment_ref': 'line_1',
'partner_id': partner.id,
'journal_id': self.bank_journal_1.id,
'amount': 1250.0,
})
move = statement_line.move_id
move_reversal = self.env['account.move.reversal'].with_context(active_model="account.move", active_ids=move.ids).create({
'date': fields.Date.from_string('2021-02-01'),
'journal_id': self.bank_journal_1.id,
})
reversal = move_reversal.reverse_moves()
reversed_move = self.env['account.move'].browse(reversal['res_id'])
self.assertEqual(reversed_move.partner_id, partner)