1450 lines
65 KiB
Python
1450 lines
65 KiB
Python
# -*- 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)
|