3264 lines
166 KiB
Python
3264 lines
166 KiB
Python
# -*- coding: utf-8 -*-
|
||
from odoo.addons.fusion_accounting.tests.test_bank_rec_widget_common import TestBankRecWidgetCommon
|
||
from odoo.tests import tagged
|
||
from odoo.tools import html2plaintext
|
||
from odoo import fields, Command
|
||
|
||
from freezegun import freeze_time
|
||
from unittest.mock import patch
|
||
import re
|
||
|
||
|
||
@tagged('post_install', '-at_install')
|
||
class TestBankRecWidget(TestBankRecWidgetCommon):
|
||
|
||
@classmethod
|
||
def setUpClass(cls):
|
||
super().setUpClass()
|
||
cls.company_data_2 = cls.setup_other_company()
|
||
|
||
cls.early_payment_term = cls.env['account.payment.term'].create({
|
||
'name': "early_payment_term",
|
||
'company_id': cls.company_data['company'].id,
|
||
'discount_percentage': 10,
|
||
'discount_days': 10,
|
||
'early_discount': True,
|
||
'line_ids': [
|
||
Command.create({
|
||
'value': 'percent',
|
||
'value_amount': 100,
|
||
'nb_days': 20,
|
||
}),
|
||
],
|
||
})
|
||
|
||
cls.account_revenue1 = cls.company_data['default_account_revenue']
|
||
cls.account_revenue2 = cls.copy_account(cls.account_revenue1)
|
||
|
||
cls.reco_model_bill = cls.env['account.reconcile.model'].create({
|
||
'name': "test create bill",
|
||
'rule_type': 'writeoff_button',
|
||
'counterpart_type': 'purchase',
|
||
'line_ids': [
|
||
Command.create({'amount_string': '50'}),
|
||
Command.create({'amount_string': '50'}),
|
||
],
|
||
})
|
||
|
||
def assert_form_extra_text_value(self, wizard, regex):
|
||
line = wizard.line_ids.filtered(lambda x: x.index == wizard.form_index)
|
||
value = line.suggestion_html
|
||
if regex:
|
||
cleaned_value = html2plaintext(value).replace('\n', '')
|
||
if not re.match(regex, cleaned_value):
|
||
self.fail(f"The following 'form_extra_text':\n\n'{cleaned_value}'\n\n...doesn't match the provided regex:\n\n'{regex}'")
|
||
else:
|
||
self.assertFalse(value)
|
||
|
||
def test_retrieve_partner_from_account_number(self):
|
||
st_line = self._create_st_line(1000.0, partner_id=None, account_number="014 474 8555")
|
||
bank_account = self.env['res.partner.bank'].create({
|
||
'acc_number': '0144748555',
|
||
'partner_id': self.partner_a.id,
|
||
})
|
||
self.assertEqual(st_line._retrieve_partner(), bank_account.partner_id)
|
||
|
||
# Can't retrieve the partner since the bank account is used by multiple partners.
|
||
self.env['res.partner.bank'].create({
|
||
'acc_number': '0144748555',
|
||
'partner_id': self.partner_b.id,
|
||
})
|
||
self.assertEqual(st_line._retrieve_partner(), self.env['res.partner'])
|
||
|
||
# Archive partner_a and see if partner_b is then chosen
|
||
self.partner_a.active = False
|
||
self.assertEqual(st_line._retrieve_partner(), self.partner_b)
|
||
|
||
def test_retrieve_partner_from_account_number_in_other_company(self):
|
||
st_line = self._create_st_line(1000.0, partner_id=None, account_number="014 474 8555")
|
||
self.env['res.partner.bank'].create({
|
||
'acc_number': '0144748555',
|
||
'partner_id': self.partner_a.id,
|
||
})
|
||
|
||
# Bank account is owned by another company.
|
||
new_company = self.env['res.company'].create({'name': "test_retrieve_partner_from_account_number_in_other_company"})
|
||
self.partner_a.company_id = new_company
|
||
self.assertEqual(st_line._retrieve_partner(), self.env['res.partner'])
|
||
|
||
def test_retrieve_partner_from_partner_name(self):
|
||
""" Ensure the partner having a name fitting exactly the 'partner_name' is retrieved first.
|
||
This test create two partners that will be ordered in the lexicographic order when performing
|
||
a search. So:
|
||
row1: "Turlututu tsoin tsoin"
|
||
row2: "turlututu"
|
||
|
||
Since "turlututu" matches exactly (case insensitive) the partner_name of the statement line,
|
||
it should be suggested first.
|
||
|
||
However if we have two partners called turlututu, we should not suggest any or we risk selecting
|
||
the wrong one.
|
||
"""
|
||
_partner_a, partner_b = self.env['res.partner'].create([
|
||
{'name': "Turlututu tsoin tsoin"},
|
||
{'name': "turlututu"},
|
||
])
|
||
|
||
st_line = self._create_st_line(1000.0, partner_id=None, partner_name="Turlututu")
|
||
self.assertEqual(st_line._retrieve_partner(), partner_b)
|
||
|
||
self.env['res.partner'].create({'name': "turlututu"})
|
||
self.assertFalse(st_line._retrieve_partner())
|
||
|
||
def test_retrieve_partner_suggested_account_from_rank(self):
|
||
""" Ensure a retrieved partner is proposing his receivable/payable according his customer/supplier rank. """
|
||
partner = self.env['res.partner'].create({'name': "turlututu"})
|
||
rec_account_id = partner.property_account_receivable_id.id
|
||
pay_account_id = partner.property_account_payable_id.id
|
||
|
||
st_line = self._create_st_line(1000.0, partner_id=None, partner_name="turlututu")
|
||
liq_account_id = st_line.journal_id.default_account_id.id
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'account_id': liq_account_id, 'balance': 1000.0},
|
||
{'flag': 'auto_balance', 'account_id': rec_account_id, 'balance': -1000.0},
|
||
])
|
||
|
||
partner._increase_rank('supplier_rank', 1)
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'account_id': liq_account_id, 'balance': 1000.0},
|
||
{'flag': 'auto_balance', 'account_id': pay_account_id, 'balance': -1000.0},
|
||
])
|
||
|
||
def test_res_partner_bank_find_create_when_archived(self):
|
||
""" Test we don't get the "The combination Account Number/Partner must be unique." error with archived
|
||
bank account.
|
||
"""
|
||
partner = self.env['res.partner'].create({
|
||
'name': "Zitycard",
|
||
'bank_ids': [Command.create({
|
||
'acc_number': "123456789",
|
||
'active': False,
|
||
})],
|
||
})
|
||
|
||
st_line = self._create_st_line(
|
||
100.0,
|
||
partner_name="Zeumat Zitycard",
|
||
account_number="123456789",
|
||
)
|
||
inv_line = self._create_invoice_line(
|
||
'out_invoice',
|
||
partner_id=partner.id,
|
||
invoice_line_ids=[{'price_unit': 100.0, 'tax_ids': []}],
|
||
)
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
wizard._action_add_new_amls(inv_line)
|
||
wizard._action_validate()
|
||
|
||
# Should not trigger the error.
|
||
self.env['res.partner.bank'].flush_model()
|
||
|
||
def test_res_partner_bank_find_create_multi_company(self):
|
||
""" Test we don't get the "The combination Account Number/Partner must be unique." error when the bank account
|
||
already exists on another company.
|
||
"""
|
||
partner = self.env['res.partner'].create({
|
||
'name': "Zitycard",
|
||
'bank_ids': [Command.create({'acc_number': "123456789"})],
|
||
})
|
||
partner.bank_ids.company_id = self.company_data_2['company']
|
||
self.env.user.company_ids = self.env.company
|
||
|
||
st_line = self._create_st_line(
|
||
100.0,
|
||
partner_name="Zeumat Zitycard",
|
||
account_number="123456789",
|
||
)
|
||
inv_line = self._create_invoice_line(
|
||
'out_invoice',
|
||
partner_id=partner.id,
|
||
invoice_line_ids=[{'price_unit': 100.0, 'tax_ids': []}],
|
||
)
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
wizard._action_add_new_amls(inv_line)
|
||
wizard._action_validate()
|
||
|
||
# Should not trigger the error.
|
||
self.env['res.partner.bank'].flush_model()
|
||
|
||
def test_validation_base_case(self):
|
||
st_line = self._create_st_line(
|
||
1000.0,
|
||
date='2017-01-01',
|
||
)
|
||
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
line = wizard.line_ids.filtered(lambda x: x.flag == 'auto_balance')
|
||
wizard._js_action_mount_line_in_edit(line.index)
|
||
line.account_id = self.account_revenue1
|
||
wizard._line_value_changed_account_id(line)
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1000.0, 'currency_id': self.company_data['currency'].id, 'balance': 1000.0},
|
||
{'flag': 'manual', 'amount_currency': -1000.0, 'currency_id': self.company_data['currency'].id, 'balance': -1000.0},
|
||
])
|
||
self.assertRecordValues(wizard, [{'state': 'valid'}])
|
||
|
||
# The amount is the same, no message under the 'amount' field.
|
||
self.assert_form_extra_text_value(wizard, False)
|
||
|
||
wizard._action_validate()
|
||
self.assertRecordValues(st_line.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'account_id': st_line.journal_id.default_account_id.id, 'amount_currency': 1000.0, 'currency_id': self.company_data['currency'].id, 'balance': 1000.0, 'reconciled': False},
|
||
{'account_id': self.account_revenue1.id, 'amount_currency': -1000.0, 'currency_id': self.company_data['currency'].id, 'balance': -1000.0, 'reconciled': False},
|
||
])
|
||
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'account_id': st_line.journal_id.default_account_id.id, 'amount_currency': 1000.0, 'currency_id': self.company_data['currency'].id, 'balance': 1000.0},
|
||
{'flag': 'aml', 'account_id': self.account_revenue1.id, 'amount_currency': -1000.0, 'currency_id': self.company_data['currency'].id, 'balance': -1000.0},
|
||
])
|
||
|
||
def test_validation_exchange_difference(self):
|
||
# 240.0 curr2 == 120.0 comp_curr
|
||
st_line = self._create_st_line(
|
||
120.0,
|
||
date='2017-01-01',
|
||
foreign_currency_id=self.other_currency.id,
|
||
amount_currency=240.0,
|
||
)
|
||
# 240.0 curr2 == 80.0 comp_curr
|
||
inv_line = self._create_invoice_line(
|
||
'out_invoice',
|
||
currency_id=self.other_currency.id,
|
||
invoice_date='2016-01-01',
|
||
invoice_line_ids=[{'price_unit': 240.0}],
|
||
)
|
||
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
wizard._action_add_new_amls(inv_line)
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 120.0, 'currency_id': self.company_data['currency'].id, 'balance': 120.0},
|
||
{'flag': 'new_aml', 'amount_currency': -240.0, 'currency_id': self.other_currency.id, 'balance': -80.0},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'currency_id': self.other_currency.id, 'balance': -40.0},
|
||
])
|
||
self.assertRecordValues(wizard, [{'state': 'valid'}])
|
||
|
||
wizard._action_validate()
|
||
|
||
# Check the statement line.
|
||
self.assertRecordValues(st_line.line_ids.sorted(), [
|
||
# pylint: disable=C0326
|
||
{'account_id': st_line.journal_id.default_account_id.id, 'amount_currency': 120.0, 'currency_id': self.company_data['currency'].id, 'balance': 120.0, 'reconciled': False},
|
||
{'account_id': inv_line.account_id.id, 'amount_currency': -240.0, 'currency_id': self.other_currency.id, 'balance': -120.0, 'reconciled': True},
|
||
])
|
||
|
||
# Check the partials.
|
||
partials = st_line.line_ids.matched_debit_ids
|
||
exchange_move = partials.exchange_move_id
|
||
_liquidity_line, _suspense_line, other_line = st_line._seek_for_lines()
|
||
self.assertRecordValues(partials.sorted(), [
|
||
# pylint: disable=C0326
|
||
{
|
||
'amount': 40.0,
|
||
'debit_amount_currency': 0.0,
|
||
'credit_amount_currency': 0.0,
|
||
'debit_move_id': exchange_move.line_ids.sorted()[0].id,
|
||
'credit_move_id': other_line.id,
|
||
'exchange_move_id': False,
|
||
},
|
||
{
|
||
'amount': 80.0,
|
||
'debit_amount_currency': 240.0,
|
||
'credit_amount_currency': 240.0,
|
||
'debit_move_id': inv_line.id,
|
||
'credit_move_id': other_line.id,
|
||
'exchange_move_id': exchange_move.id,
|
||
},
|
||
])
|
||
|
||
# Check the exchange diff journal entry.
|
||
self.assertRecordValues(exchange_move.line_ids.sorted(), [
|
||
# pylint: disable=C0326
|
||
{'account_id': inv_line.account_id.id, 'amount_currency': 0.0, 'currency_id': self.other_currency.id, 'balance': 40.0, 'reconciled': True},
|
||
{'account_id': self.env.company.income_currency_exchange_account_id.id, 'amount_currency': 0.0, 'currency_id': self.other_currency.id, 'balance': -40.0, 'reconciled': False},
|
||
])
|
||
|
||
def test_validation_new_aml_same_foreign_currency(self):
|
||
income_exchange_account = self.env.company.income_currency_exchange_account_id
|
||
|
||
# 6000.0 curr2 == 1200.0 comp_curr (bank rate 5:1 instead of the odoo rate 4:1)
|
||
st_line = self._create_st_line(
|
||
1200.0,
|
||
date='2017-01-01',
|
||
foreign_currency_id=self.other_currency_2.id,
|
||
amount_currency=6000.0,
|
||
)
|
||
# 6000.0 curr2 == 1000.0 comp_curr (rate 6:1)
|
||
inv_line = self._create_invoice_line(
|
||
'out_invoice',
|
||
currency_id=self.other_currency_2.id,
|
||
invoice_date='2016-01-01',
|
||
invoice_line_ids=[{'price_unit': 6000.0}],
|
||
)
|
||
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
wizard._action_add_new_amls(inv_line)
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1200.0, 'currency_id': self.company_data['currency'].id, 'balance': 1200.0},
|
||
{'flag': 'new_aml', 'amount_currency': -6000.0, 'currency_id': self.other_currency_2.id, 'balance': -1000.0},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'currency_id': self.other_currency_2.id, 'balance': -200.0},
|
||
])
|
||
self.assertRecordValues(wizard, [{'state': 'valid'}])
|
||
|
||
# The amount is the same, no message under the 'amount' field.
|
||
self.assert_form_extra_text_value(wizard, False)
|
||
|
||
wizard._action_validate()
|
||
self.assertRecordValues(st_line.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'account_id': st_line.journal_id.default_account_id.id, 'amount_currency': 1200.0, 'currency_id': self.company_data['currency'].id, 'balance': 1200.0, 'reconciled': False},
|
||
{'account_id': inv_line.account_id.id, 'amount_currency': -6000.0, 'currency_id': self.other_currency_2.id, 'balance': -1200.0, 'reconciled': True},
|
||
])
|
||
self.assertRecordValues(st_line, [{'is_reconciled': True}])
|
||
self.assertRecordValues(inv_line.move_id, [{'payment_state': 'paid'}])
|
||
self.assertRecordValues(inv_line.matched_credit_ids.exchange_move_id.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'account_id': inv_line.account_id.id, 'amount_currency': 0.0, 'currency_id': self.other_currency_2.id, 'balance': 200.0, 'reconciled': True, 'date': fields.Date.from_string('2017-01-31')},
|
||
{'account_id': income_exchange_account.id, 'amount_currency': 0.0, 'currency_id': self.other_currency_2.id, 'balance': -200.0, 'reconciled': False, 'date': fields.Date.from_string('2017-01-31')},
|
||
])
|
||
|
||
# Reset the wizard.
|
||
wizard._js_action_reset()
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1200.0, 'currency_id': self.company_data['currency'].id, 'balance': 1200.0},
|
||
{'flag': 'auto_balance', 'amount_currency': -6000.0, 'currency_id': self.other_currency_2.id, 'balance': -1200.0},
|
||
])
|
||
|
||
# Create the same invoice with a higher amount to check the partial flow.
|
||
# 9000.0 curr2 == 1500.0 comp_curr (rate 6:1)
|
||
inv_line = self._create_invoice_line(
|
||
'out_invoice',
|
||
currency_id=self.other_currency_2.id,
|
||
invoice_date='2016-01-01',
|
||
invoice_line_ids=[{'price_unit': 9000.0}],
|
||
)
|
||
wizard._action_add_new_amls(inv_line)
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1200.0, 'currency_id': self.company_data['currency'].id, 'balance': 1200.0, 'name': "turlututu"},
|
||
{'flag': 'new_aml', 'amount_currency': -6000.0, 'currency_id': self.other_currency_2.id, 'balance': -1000.0, 'name': "INV/2016/00002"},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'currency_id': self.other_currency_2.id, 'balance': -200.0, 'name': "Exchange Difference: INV/2016/00002"},
|
||
])
|
||
|
||
# Check the message under the 'amount' field.
|
||
line = wizard.line_ids.filtered(lambda x: x.flag == 'new_aml')
|
||
wizard._js_action_mount_line_in_edit(line.index)
|
||
self.assert_form_extra_text_value(
|
||
wizard,
|
||
r".+open amount of 9,000.000.+ reduced by 6,000.000.+ set the invoice as fully paid .",
|
||
)
|
||
self.assertRecordValues(line, [{
|
||
'suggestion_amount_currency': -9000.0,
|
||
'suggestion_balance': -1500.0,
|
||
}])
|
||
|
||
# Switch to a full reconciliation.
|
||
wizard._js_action_apply_line_suggestion(line.index)
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1200.0, 'currency_id': self.company_data['currency'].id, 'balance': 1200.0, 'name': "turlututu"},
|
||
{'flag': 'new_aml', 'amount_currency': -9000.0, 'currency_id': self.other_currency_2.id, 'balance': -1500.0, 'name': "INV/2016/00002"},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'currency_id': self.other_currency_2.id, 'balance': -300.0, 'name': "Exchange Difference: INV/2016/00002"},
|
||
{'flag': 'auto_balance', 'amount_currency': 3000.0, 'currency_id': self.other_currency_2.id, 'balance': 600.0, 'name': "Open balance of 6,000.000 $"},
|
||
])
|
||
|
||
# Check the message under the 'amount' field.
|
||
line = wizard.line_ids.filtered(lambda x: x.flag == 'new_aml')
|
||
wizard._js_action_mount_line_in_edit(line.index)
|
||
self.assert_form_extra_text_value(
|
||
wizard,
|
||
r".+open amount of 9,000.000.+ paid .+ record a partial payment .",
|
||
)
|
||
self.assertRecordValues(line, [{
|
||
'suggestion_amount_currency': -6000.0,
|
||
'suggestion_balance': -1000.0,
|
||
}])
|
||
|
||
# Switch back to a partial reconciliation.
|
||
wizard._js_action_apply_line_suggestion(line.index)
|
||
self.assertRecordValues(wizard, [{'state': 'valid'}])
|
||
|
||
# Reconcile
|
||
wizard._action_validate()
|
||
self.assertRecordValues(st_line.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'account_id': st_line.journal_id.default_account_id.id, 'amount_currency': 1200.0, 'currency_id': self.company_data['currency'].id, 'balance': 1200.0, 'reconciled': False},
|
||
{'account_id': inv_line.account_id.id, 'amount_currency': -6000.0, 'currency_id': self.other_currency_2.id, 'balance': -1200.0, 'reconciled': True},
|
||
])
|
||
self.assertRecordValues(st_line, [{'is_reconciled': True}])
|
||
self.assertRecordValues(inv_line.move_id, [{
|
||
'payment_state': 'partial',
|
||
'amount_residual': 3000.0,
|
||
}])
|
||
self.assertRecordValues(inv_line.matched_credit_ids.exchange_move_id.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'account_id': inv_line.account_id.id, 'amount_currency': 0.0, 'currency_id': self.other_currency_2.id, 'balance': 200.0, 'reconciled': True, 'date': fields.Date.from_string('2017-01-31')},
|
||
{'account_id': income_exchange_account.id, 'amount_currency': 0.0, 'currency_id': self.other_currency_2.id, 'balance': -200.0, 'reconciled': False, 'date': fields.Date.from_string('2017-01-31')},
|
||
])
|
||
|
||
def test_validation_expense_exchange_difference(self):
|
||
expense_exchange_account = self.env.company.expense_currency_exchange_account_id
|
||
|
||
# 1200.0 comp_curr = 3600.0 foreign_curr in 2016 (rate 1:3)
|
||
st_line = self._create_st_line(
|
||
1200.0,
|
||
date='2016-01-01',
|
||
)
|
||
# 1800.0 comp_curr = 3600.0 foreign_curr in 2017 (rate 1:2)
|
||
inv_line = self._create_invoice_line(
|
||
'out_invoice',
|
||
currency_id=self.other_currency.id,
|
||
invoice_date='2017-01-01',
|
||
invoice_line_ids=[{'price_unit': 3600.0}],
|
||
)
|
||
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
wizard._action_add_new_amls(inv_line)
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1200.0, 'currency_id': self.company_data['currency'].id, 'balance': 1200.0},
|
||
{'flag': 'new_aml', 'amount_currency': -3600.0, 'currency_id': self.other_currency.id, 'balance': -1800.0},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'currency_id': self.other_currency.id, 'balance': 600.0},
|
||
])
|
||
self.assertRecordValues(wizard, [{'state': 'valid'}])
|
||
|
||
wizard._action_validate()
|
||
self.assertRecordValues(st_line.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'account_id': st_line.journal_id.default_account_id.id, 'amount_currency': 1200.0, 'currency_id': self.company_data['currency'].id, 'balance': 1200.0, 'reconciled': False},
|
||
{'account_id': inv_line.account_id.id, 'amount_currency': -3600.0, 'currency_id': self.other_currency.id, 'balance': -1200.0, 'reconciled': True},
|
||
])
|
||
self.assertRecordValues(st_line, [{'is_reconciled': True}])
|
||
self.assertRecordValues(inv_line.move_id, [{'payment_state': 'paid'}])
|
||
self.assertRecordValues(inv_line.matched_credit_ids.exchange_move_id.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'account_id': inv_line.account_id.id, 'amount_currency': 0.0, 'currency_id': self.other_currency.id, 'balance': -600.0, 'reconciled': True, 'date': fields.Date.from_string('2017-01-31')},
|
||
{'account_id': expense_exchange_account.id, 'amount_currency': 0.0, 'currency_id': self.other_currency.id, 'balance': 600.0, 'reconciled': False, 'date': fields.Date.from_string('2017-01-31')},
|
||
])
|
||
# Checks that the wizard still display the 3 initial lines
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1200.0, 'currency_id': self.company_data['currency'].id, 'balance': 1200.0},
|
||
{'flag': 'aml', 'amount_currency': -3600.0, 'currency_id': self.other_currency.id, 'balance': -1800.0},
|
||
{'flag': 'aml', 'amount_currency': 0.0, 'currency_id': self.other_currency.id, 'balance': 600.0},
|
||
])
|
||
|
||
def test_validation_income_exchange_difference(self):
|
||
income_exchange_account = self.env.company.income_currency_exchange_account_id
|
||
|
||
# 1800.0 comp_curr = 3600.0 foreign_curr in 2017 (rate 1:2)
|
||
st_line = self._create_st_line(
|
||
1800.0,
|
||
date='2017-01-01',
|
||
)
|
||
# 1200.0 comp_curr = 3600.0 foreign_curr in 2016 (rate 1:3)
|
||
inv_line = self._create_invoice_line(
|
||
'out_invoice',
|
||
currency_id=self.other_currency.id,
|
||
invoice_date='2016-01-01',
|
||
invoice_line_ids=[{'price_unit': 3600.0}],
|
||
)
|
||
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
wizard._action_add_new_amls(inv_line)
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1800.0, 'currency_id': self.company_data['currency'].id, 'balance': 1800.0},
|
||
{'flag': 'new_aml', 'amount_currency': -3600.0, 'currency_id': self.other_currency.id, 'balance': -1200.0},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'currency_id': self.other_currency.id, 'balance': -600.0},
|
||
])
|
||
self.assertRecordValues(wizard, [{'state': 'valid'}])
|
||
|
||
wizard._action_validate()
|
||
self.assertRecordValues(st_line.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'account_id': st_line.journal_id.default_account_id.id, 'amount_currency': 1800.0, 'currency_id': self.company_data['currency'].id, 'balance': 1800.0, 'reconciled': False},
|
||
{'account_id': inv_line.account_id.id, 'amount_currency': -3600.0, 'currency_id': self.other_currency.id, 'balance': -1800.0, 'reconciled': True},
|
||
])
|
||
self.assertRecordValues(st_line, [{'is_reconciled': True}])
|
||
self.assertRecordValues(inv_line.move_id, [{'payment_state': 'paid'}])
|
||
self.assertRecordValues(inv_line.matched_credit_ids.exchange_move_id.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'account_id': inv_line.account_id.id, 'amount_currency': 0.0, 'currency_id': self.other_currency.id, 'balance': 600.0, 'reconciled': True, 'date': fields.Date.from_string('2017-01-31')},
|
||
{'account_id': income_exchange_account.id, 'amount_currency': 0.0, 'currency_id': self.other_currency.id, 'balance': -600.0, 'reconciled': False, 'date': fields.Date.from_string('2017-01-31')},
|
||
])
|
||
# Checks that the wizard still display the 3 initial lines
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1800.0, 'currency_id': self.company_data['currency'].id, 'balance': 1800.0},
|
||
{'flag': 'aml', 'amount_currency': -3600.0, 'currency_id': self.other_currency.id, 'balance': -1200.0},
|
||
{'flag': 'aml', 'amount_currency': 0.0, 'currency_id': self.other_currency.id, 'balance': -600.0},
|
||
])
|
||
|
||
def test_validation_income_exchange_difference_with_rounding(self):
|
||
# 1000.0 comp_curr = 3000.0 foreign_curr in 2016 (rate 1:3)
|
||
# However divided in 3 invoices + rounding we have 333.33333 ≃ 333.33 comp_curr = 1000.0 foreign_curr
|
||
# this implies that the full amount has been used in foreign_curr but there is 0.01 in comp_curr
|
||
st_line = self._create_st_line(
|
||
1000.0,
|
||
date='2016-01-01',
|
||
)
|
||
|
||
# 1500 comp_curr = 3000.0 foreign_curr in 2017 (rate 1:2)
|
||
inv_line_1 = self._create_invoice_line(
|
||
'out_invoice',
|
||
currency_id=self.other_currency.id,
|
||
invoice_date='2017-01-01',
|
||
invoice_line_ids=[{'price_unit': 1000.0}],
|
||
)
|
||
|
||
inv_line_2 = self._create_invoice_line(
|
||
'out_invoice',
|
||
currency_id=self.other_currency.id,
|
||
invoice_date='2017-01-01',
|
||
invoice_line_ids=[{'price_unit': 1000.0}],
|
||
)
|
||
|
||
inv_line_3 = self._create_invoice_line(
|
||
'out_invoice',
|
||
currency_id=self.other_currency.id,
|
||
invoice_date='2017-01-01',
|
||
invoice_line_ids=[{'price_unit': 1000.0}],
|
||
)
|
||
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
wizard._action_add_new_amls(inv_line_1 + inv_line_2 + inv_line_3)
|
||
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1000.0, 'currency_id': self.company_data['currency'].id, 'balance': 1000.0},
|
||
{'flag': 'new_aml', 'amount_currency': -1000.0, 'currency_id': self.other_currency.id, 'balance': -500.0},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'currency_id': self.other_currency.id, 'balance': 166.67},
|
||
{'flag': 'new_aml', 'amount_currency': -1000.0, 'currency_id': self.other_currency.id, 'balance': -500.0},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'currency_id': self.other_currency.id, 'balance': 166.67},
|
||
{'flag': 'new_aml', 'amount_currency': -1000.0, 'currency_id': self.other_currency.id, 'balance': -500.0},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'currency_id': self.other_currency.id, 'balance': 166.67},
|
||
{'flag': 'auto_balance', 'amount_currency': -0.01, 'currency_id': self.company_data['currency'].id, 'balance': -0.01},
|
||
])
|
||
|
||
# Remove 0.01 cent in the balance of first exchange line
|
||
first_exchange_line = wizard.line_ids.filtered(lambda x: x.flag == 'exchange_diff')[:1]
|
||
wizard._js_action_mount_line_in_edit(first_exchange_line.index)
|
||
first_exchange_line.balance = 166.66
|
||
wizard._line_value_changed_balance(first_exchange_line)
|
||
|
||
# Every line balance so no 'auto_balance' is generated
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1000.0, 'currency_id': self.company_data['currency'].id, 'balance': 1000.0},
|
||
{'flag': 'new_aml', 'amount_currency': -1000.0, 'currency_id': self.other_currency.id, 'balance': -500.0},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'currency_id': self.other_currency.id, 'balance': 166.66},
|
||
{'flag': 'new_aml', 'amount_currency': -1000.0, 'currency_id': self.other_currency.id, 'balance': -500.0},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'currency_id': self.other_currency.id, 'balance': 166.67},
|
||
{'flag': 'new_aml', 'amount_currency': -1000.0, 'currency_id': self.other_currency.id, 'balance': -500.0},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'currency_id': self.other_currency.id, 'balance': 166.67},
|
||
])
|
||
|
||
self.assertRecordValues(wizard, [{'state': 'valid'}])
|
||
|
||
wizard._action_validate()
|
||
|
||
# Check that the first line with exchange has -0.01 compared to others
|
||
self.assertRecordValues(st_line.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'account_id': st_line.journal_id.default_account_id.id, 'amount_currency': 1000.0, 'currency_id': self.company_data['currency'].id, 'balance': 1000.0, 'reconciled': False},
|
||
{'account_id': inv_line_1.account_id.id, 'amount_currency': -1000.0, 'currency_id': self.other_currency.id, 'balance': -333.34, 'reconciled': True},
|
||
{'account_id': inv_line_2.account_id.id, 'amount_currency': -1000.0, 'currency_id': self.other_currency.id, 'balance': -333.33, 'reconciled': True},
|
||
{'account_id': inv_line_3.account_id.id, 'amount_currency': -1000.0, 'currency_id': self.other_currency.id, 'balance': -333.33, 'reconciled': True},
|
||
])
|
||
|
||
self.assertRecordValues(st_line, [{'is_reconciled': True}])
|
||
self.assertRecordValues(inv_line_1.move_id, [{'payment_state': 'paid'}])
|
||
self.assertRecordValues(inv_line_2.move_id, [{'payment_state': 'paid'}])
|
||
self.assertRecordValues(inv_line_3.move_id, [{'payment_state': 'paid'}])
|
||
|
||
def test_validation_exchange_diff_multiple(self):
|
||
income_exchange_account = self.env.company.income_currency_exchange_account_id
|
||
foreign_currency = self.setup_other_currency('AED', rates=[('2016-01-01', 6.0), ('2017-01-01', 5.0)])
|
||
|
||
# 6000.0 curr2 == 1200.0 comp_curr (bank rate 5:1 instead of the odoo rate 6:1)
|
||
st_line = self._create_st_line(
|
||
1200.0,
|
||
date='2016-01-01',
|
||
foreign_currency_id=foreign_currency.id,
|
||
amount_currency=6000.0,
|
||
)
|
||
# 1000.0 foreign_curr == 166.67 comp_curr (rate 6:1)
|
||
inv_line_1 = self._create_invoice_line(
|
||
'out_invoice',
|
||
currency_id=foreign_currency.id,
|
||
invoice_date='2016-01-01',
|
||
invoice_line_ids=[{'price_unit': 1000.0}],
|
||
)
|
||
# 2000.00 foreign_curr == 400.0 comp_curr (rate 5:1)
|
||
inv_line_2 = self._create_invoice_line(
|
||
'out_invoice',
|
||
currency_id=foreign_currency.id,
|
||
invoice_date='2017-01-01',
|
||
invoice_line_ids=[{'price_unit': 2000.0}],
|
||
)
|
||
# 3000.0 foreign_curr == 500.0 comp_curr (rate 6:1)
|
||
inv_line_3 = self._create_invoice_line(
|
||
'out_invoice',
|
||
currency_id=foreign_currency.id,
|
||
invoice_date='2016-01-01',
|
||
invoice_line_ids=[{'price_unit': 3000.0}],
|
||
)
|
||
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
wizard._action_add_new_amls(inv_line_1 + inv_line_2 + inv_line_3)
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1200.0, 'currency_id': self.company_data['currency'].id, 'balance': 1200.0},
|
||
{'flag': 'new_aml', 'amount_currency': -1000.0, 'currency_id': foreign_currency.id, 'balance': -166.67},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'currency_id': foreign_currency.id, 'balance': -33.33},
|
||
{'flag': 'new_aml', 'amount_currency': -2000.0, 'currency_id': foreign_currency.id, 'balance': -400.0},
|
||
{'flag': 'new_aml', 'amount_currency': -3000.0, 'currency_id': foreign_currency.id, 'balance': -500.0},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'currency_id': foreign_currency.id, 'balance': -100.0},
|
||
])
|
||
self.assertRecordValues(wizard, [{'state': 'valid'}])
|
||
|
||
# The amount is the same, no message under the 'amount' field.
|
||
self.assert_form_extra_text_value(wizard, False)
|
||
|
||
wizard._action_validate()
|
||
self.assertRecordValues(st_line.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'account_id': st_line.journal_id.default_account_id.id, 'amount_currency': 1200.0, 'currency_id': self.company_data['currency'].id, 'balance': 1200.0, 'reconciled': False},
|
||
{'account_id': inv_line_1.account_id.id, 'amount_currency': -1000.0, 'currency_id': foreign_currency.id, 'balance': -200.0, 'reconciled': True},
|
||
{'account_id': inv_line_2.account_id.id, 'amount_currency': -2000.0, 'currency_id': foreign_currency.id, 'balance': -400.0, 'reconciled': True},
|
||
{'account_id': inv_line_3.account_id.id, 'amount_currency': -3000.0, 'currency_id': foreign_currency.id, 'balance': -600.0, 'reconciled': True},
|
||
])
|
||
self.assertRecordValues(st_line, [{'is_reconciled': True}])
|
||
self.assertRecordValues(inv_line_1.move_id, [{'payment_state': 'paid'}])
|
||
self.assertRecordValues(inv_line_2.move_id, [{'payment_state': 'paid'}])
|
||
self.assertRecordValues(inv_line_3.move_id, [{'payment_state': 'paid'}])
|
||
self.assertRecordValues((inv_line_1 + inv_line_2 + inv_line_3).matched_credit_ids.exchange_move_id.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'account_id': inv_line_1.account_id.id, 'amount_currency': 0.0, 'currency_id': foreign_currency.id, 'balance': 33.33, 'reconciled': True},
|
||
{'account_id': income_exchange_account.id, 'amount_currency': 0.0, 'currency_id': foreign_currency.id, 'balance': -33.33, 'reconciled': False},
|
||
{'account_id': inv_line_3.account_id.id, 'amount_currency': 0.0, 'currency_id': foreign_currency.id, 'balance': 100.0, 'reconciled': True},
|
||
{'account_id': income_exchange_account.id, 'amount_currency': 0.0, 'currency_id': foreign_currency.id, 'balance': -100.0, 'reconciled': False},
|
||
])
|
||
|
||
def test_validation_foreign_curr_st_line_comp_curr_payment_partial_exchange_difference(self):
|
||
comp_curr = self.env.company.currency_id
|
||
foreign_curr = self.other_currency
|
||
|
||
st_line = self._create_st_line(
|
||
650.0,
|
||
date='2017-01-01',
|
||
foreign_currency_id=foreign_curr.id,
|
||
amount_currency=800,
|
||
)
|
||
|
||
payment = self.env['account.payment'].create({
|
||
'partner_id': self.partner_a.id,
|
||
'payment_type': 'inbound',
|
||
'partner_type': 'customer',
|
||
'date': '2017-01-01',
|
||
'amount': 725.0,
|
||
})
|
||
payment.action_post()
|
||
pay_line, _counterpart_lines, _writeoff_lines = payment._seek_for_lines()
|
||
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
wizard._action_add_new_amls(pay_line)
|
||
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 650.0, 'currency_id': comp_curr.id, 'balance': 650.0},
|
||
{'flag': 'new_aml', 'amount_currency': -650.0, 'currency_id': comp_curr.id, 'balance': -650.0},
|
||
])
|
||
|
||
# Switch to a full reconciliation.
|
||
line = wizard.line_ids.filtered(lambda x: x.flag == 'new_aml')
|
||
wizard._js_action_mount_line_in_edit(line.index)
|
||
wizard._js_action_apply_line_suggestion(line.index)
|
||
|
||
# 725 * 800 / 650 = 892.308
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 650.0, 'currency_id': comp_curr.id, 'balance': 650.0},
|
||
{'flag': 'new_aml', 'amount_currency': -725.0, 'currency_id': comp_curr.id, 'balance': -725.0},
|
||
{'flag': 'auto_balance', 'amount_currency': 92.308, 'currency_id': foreign_curr.id, 'balance': 75.0},
|
||
])
|
||
|
||
# Switch to a partial reconciliation.
|
||
wizard._js_action_apply_line_suggestion(line.index)
|
||
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 650.0, 'currency_id': comp_curr.id, 'balance': 650.0},
|
||
{'flag': 'new_aml', 'amount_currency': -650.0, 'currency_id': comp_curr.id, 'balance': -650.0},
|
||
])
|
||
|
||
wizard._action_validate()
|
||
self.assertRecordValues(pay_line, [{'amount_residual': 75.0}])
|
||
|
||
def test_validation_remove_exchange_difference(self):
|
||
""" Test the case when the foreign currency is missing on the statement line.
|
||
In that case, the user can remove the exchange difference in order to fully reconcile both items without additional
|
||
write-off/exchange difference.
|
||
"""
|
||
# 1200.0 comp_curr = 2400.0 foreign_curr in 2017 (rate 1:2)
|
||
st_line = self._create_st_line(
|
||
1200.0,
|
||
date='2017-01-01',
|
||
)
|
||
# 1200.0 comp_curr = 3600.0 foreign_curr in 2016 (rate 1:3)
|
||
inv_line = self._create_invoice_line(
|
||
'out_invoice',
|
||
currency_id=self.other_currency.id,
|
||
invoice_date='2016-01-01',
|
||
invoice_line_ids=[{'price_unit': 3600.0}],
|
||
)
|
||
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
wizard._action_add_new_amls(inv_line)
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1200.0, 'currency_id': self.company_data['currency'].id, 'balance': 1200.0},
|
||
{'flag': 'new_aml', 'amount_currency': -2400.0, 'currency_id': self.other_currency.id, 'balance': -800.0},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'currency_id': self.other_currency.id, 'balance': -400.0},
|
||
])
|
||
self.assertRecordValues(wizard, [{'state': 'valid'}])
|
||
|
||
# Remove the partial.
|
||
line_index = wizard.line_ids.filtered(lambda x: x.flag == 'new_aml').index
|
||
wizard._js_action_mount_line_in_edit(line_index)
|
||
wizard._js_action_apply_line_suggestion(line_index)
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1200.0, 'currency_id': self.company_data['currency'].id, 'balance': 1200.0},
|
||
{'flag': 'new_aml', 'amount_currency': -3600.0, 'currency_id': self.other_currency.id, 'balance': -1200.0},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'currency_id': self.other_currency.id, 'balance': -600.0},
|
||
{'flag': 'auto_balance', 'amount_currency': 600.0, 'currency_id': self.company_data['currency'].id, 'balance': 600.0},
|
||
])
|
||
|
||
exchange_diff_index = wizard.line_ids.filtered(lambda x: x.flag == 'exchange_diff').index
|
||
wizard._js_action_remove_line(exchange_diff_index)
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1200.0, 'currency_id': self.company_data['currency'].id, 'balance': 1200.0},
|
||
{'flag': 'new_aml', 'amount_currency': -3600.0, 'currency_id': self.other_currency.id, 'balance': -1200.0},
|
||
])
|
||
|
||
wizard._action_validate()
|
||
self.assertRecordValues(st_line.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'account_id': st_line.journal_id.default_account_id.id, 'amount_currency': 1200.0, 'currency_id': self.company_data['currency'].id, 'balance': 1200.0, 'reconciled': False},
|
||
{'account_id': inv_line.account_id.id, 'amount_currency': -3600.0, 'currency_id': self.other_currency.id, 'balance': -1200.0, 'reconciled': True},
|
||
])
|
||
self.assertRecordValues(st_line, [{'is_reconciled': True}])
|
||
self.assertRecordValues(inv_line.move_id, [{'payment_state': 'paid'}])
|
||
|
||
def test_validation_new_aml_one_foreign_currency_on_st_line(self):
|
||
income_exchange_account = self.env.company.income_currency_exchange_account_id
|
||
|
||
# 4800.0 curr2 == 1200.0 comp_curr (rate 4:1)
|
||
st_line = self._create_st_line(
|
||
1200.0,
|
||
date='2017-01-01',
|
||
)
|
||
# 4800.0 curr2 in 2016 (rate 6:1)
|
||
inv_line = self._create_invoice_line(
|
||
'out_invoice',
|
||
invoice_date='2016-01-01',
|
||
currency_id=self.other_currency_2.id,
|
||
invoice_line_ids=[{'price_unit': 4800.0}],
|
||
)
|
||
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
wizard._action_add_new_amls(inv_line)
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1200.0, 'currency_id': self.company_data['currency'].id, 'balance': 1200.0},
|
||
{'flag': 'new_aml', 'amount_currency': -4800.0, 'currency_id': self.other_currency_2.id, 'balance': -800.0},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'currency_id': self.other_currency_2.id, 'balance': -400.0},
|
||
])
|
||
self.assertRecordValues(wizard, [{'state': 'valid'}])
|
||
|
||
# The amount is the same, no message under the 'amount' field.
|
||
self.assert_form_extra_text_value(wizard, False)
|
||
|
||
wizard._action_validate()
|
||
self.assertRecordValues(st_line.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'account_id': st_line.journal_id.default_account_id.id, 'amount_currency': 1200.0, 'currency_id': self.company_data['currency'].id, 'balance': 1200.0, 'reconciled': False},
|
||
{'account_id': inv_line.account_id.id, 'amount_currency': -4800.0, 'currency_id': self.other_currency_2.id, 'balance': -1200.0, 'reconciled': True},
|
||
])
|
||
self.assertRecordValues(st_line, [{'is_reconciled': True}])
|
||
self.assertRecordValues(inv_line.move_id, [{'payment_state': 'paid'}])
|
||
self.assertRecordValues(inv_line.matched_credit_ids.exchange_move_id.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'account_id': inv_line.account_id.id, 'amount_currency': 0.0, 'currency_id': self.other_currency_2.id, 'balance': 400.0, 'reconciled': True, 'date': fields.Date.from_string('2017-01-31')},
|
||
{'account_id': income_exchange_account.id, 'amount_currency': 0.0, 'currency_id': self.other_currency_2.id, 'balance': -400.0, 'reconciled': False, 'date': fields.Date.from_string('2017-01-31')},
|
||
])
|
||
|
||
# Checks that the wizard still display the 3 initial lines
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1200.0, 'currency_id': self.company_data['currency'].id, 'balance': 1200.0},
|
||
{'flag': 'aml', 'amount_currency': -4800.0, 'currency_id': self.other_currency_2.id, 'balance': -800.0},
|
||
{'flag': 'aml', 'amount_currency': 0.0, 'currency_id': self.other_currency_2.id, 'balance': -400.0}, # represents the exchange diff
|
||
])
|
||
|
||
# Reset the wizard.
|
||
wizard._js_action_reset()
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1200.0, 'currency_id': self.company_data['currency'].id, 'balance': 1200.0},
|
||
{'flag': 'auto_balance', 'amount_currency': -1200.0, 'currency_id': self.company_data['currency'].id, 'balance': -1200.0},
|
||
])
|
||
|
||
# Create the same invoice with a higher amount to check the partial flow.
|
||
# 4800.0 curr2 in 2016 (rate 6:1)
|
||
inv_line = self._create_invoice_line(
|
||
'out_invoice',
|
||
invoice_date='2016-01-01',
|
||
currency_id=self.other_currency_2.id,
|
||
invoice_line_ids=[{'price_unit': 9600.0}],
|
||
)
|
||
wizard._action_add_new_amls(inv_line)
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1200.0, 'currency_id': self.company_data['currency'].id, 'balance': 1200.0},
|
||
{'flag': 'new_aml', 'amount_currency': -4800.0, 'currency_id': self.other_currency_2.id, 'balance': -800.0},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'currency_id': self.other_currency_2.id, 'balance': -400.0},
|
||
])
|
||
|
||
# Check the message under the 'amount' field.
|
||
line = wizard.line_ids.filtered(lambda x: x.flag == 'new_aml')
|
||
wizard._js_action_mount_line_in_edit(line.index)
|
||
self.assert_form_extra_text_value(
|
||
wizard,
|
||
r".+open amount of 9,600.000.+ reduced by 4,800.000.+ set the invoice as fully paid .",
|
||
)
|
||
self.assertRecordValues(line, [{
|
||
'suggestion_amount_currency': -9600.0,
|
||
'suggestion_balance': -1600.0,
|
||
}])
|
||
|
||
# Switch to a full reconciliation.
|
||
wizard._js_action_apply_line_suggestion(line.index)
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1200.0, 'currency_id': self.company_data['currency'].id, 'balance': 1200.0},
|
||
{'flag': 'new_aml', 'amount_currency': -9600.0, 'currency_id': self.other_currency_2.id, 'balance': -1600.0},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'currency_id': self.other_currency_2.id, 'balance': -800.0},
|
||
{'flag': 'auto_balance', 'amount_currency': 1200.0, 'currency_id': self.company_data['currency'].id, 'balance': 1200.0},
|
||
])
|
||
|
||
# Check the message under the 'amount' field.
|
||
line = wizard.line_ids.filtered(lambda x: x.flag == 'new_aml')
|
||
wizard._js_action_mount_line_in_edit(line.index)
|
||
self.assert_form_extra_text_value(
|
||
wizard,
|
||
r".+open amount of 9,600.000.+ paid .+ record a partial payment .",
|
||
)
|
||
self.assertRecordValues(line, [{
|
||
'suggestion_amount_currency': -4800.0,
|
||
'suggestion_balance': -800.0,
|
||
}])
|
||
|
||
# Switch back to a partial reconciliation.
|
||
wizard._js_action_apply_line_suggestion(line.index)
|
||
self.assertRecordValues(wizard, [{'state': 'valid'}])
|
||
|
||
# Reconcile
|
||
wizard._action_validate()
|
||
self.assertRecordValues(st_line.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'account_id': st_line.journal_id.default_account_id.id, 'amount_currency': 1200.0, 'currency_id': self.company_data['currency'].id, 'balance': 1200.0, 'reconciled': False},
|
||
{'account_id': inv_line.account_id.id, 'amount_currency': -4800.0, 'currency_id': self.other_currency_2.id, 'balance': -1200.0, 'reconciled': True},
|
||
])
|
||
self.assertRecordValues(st_line, [{'is_reconciled': True}])
|
||
self.assertRecordValues(inv_line.move_id, [{
|
||
'payment_state': 'partial',
|
||
'amount_residual': 4800.0,
|
||
}])
|
||
self.assertRecordValues(inv_line, [{
|
||
'amount_residual_currency': 4800.0,
|
||
'amount_residual': 800.0,
|
||
'reconciled': False,
|
||
}])
|
||
self.assertRecordValues(inv_line.matched_credit_ids.exchange_move_id.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'account_id': inv_line.account_id.id, 'amount_currency': 0.0, 'currency_id': self.other_currency_2.id, 'balance': 400.0, 'reconciled': True, 'date': fields.Date.from_string('2017-01-31')},
|
||
{'account_id': income_exchange_account.id, 'amount_currency': 0.0, 'currency_id': self.other_currency_2.id, 'balance': -400.0, 'reconciled': False, 'date': fields.Date.from_string('2017-01-31')},
|
||
])
|
||
|
||
def test_validation_new_aml_one_foreign_currency_on_inv_line(self):
|
||
income_exchange_account = self.env.company.income_currency_exchange_account_id
|
||
|
||
# 1200.0 comp_curr is equals to 4800.0 curr2 in 2017 (rate 4:1)
|
||
st_line = self._create_st_line(
|
||
1200.0,
|
||
date='2017-01-01',
|
||
)
|
||
# 4800.0 curr2 == 800.0 comp_curr (rate 6:1)
|
||
inv_line = self._create_invoice_line(
|
||
'out_invoice',
|
||
currency_id=self.other_currency_2.id,
|
||
invoice_date='2016-01-01',
|
||
invoice_line_ids=[{'price_unit': 4800.0}],
|
||
)
|
||
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
wizard._action_add_new_amls(inv_line)
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1200.0, 'currency_id': self.company_data['currency'].id, 'balance': 1200.0},
|
||
{'flag': 'new_aml', 'amount_currency': -4800.0, 'currency_id': self.other_currency_2.id, 'balance': -800.0},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'currency_id': self.other_currency_2.id, 'balance': -400.0},
|
||
])
|
||
self.assertRecordValues(wizard, [{'state': 'valid'}])
|
||
|
||
# The amount is the same, no message under the 'amount' field.
|
||
self.assert_form_extra_text_value(wizard, False)
|
||
|
||
# Remove the line to see if the exchange difference is well removed.
|
||
wizard._action_remove_new_amls(inv_line)
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1200.0, 'currency_id': self.company_data['currency'].id, 'balance': 1200.0},
|
||
{'flag': 'auto_balance', 'amount_currency': -1200.0, 'currency_id': self.company_data['currency'].id, 'balance': -1200.0},
|
||
])
|
||
self.assertRecordValues(wizard, [{'state': 'invalid'}])
|
||
|
||
# Mount the line again and validate.
|
||
wizard._action_add_new_amls(inv_line)
|
||
wizard._action_validate()
|
||
self.assertRecordValues(st_line.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'account_id': st_line.journal_id.default_account_id.id, 'amount_currency': 1200.0, 'currency_id': self.company_data['currency'].id, 'balance': 1200.0, 'reconciled': False},
|
||
{'account_id': inv_line.account_id.id, 'amount_currency': -4800.0, 'currency_id': self.other_currency_2.id, 'balance': -1200.0, 'reconciled': True},
|
||
])
|
||
self.assertRecordValues(st_line, [{'is_reconciled': True}])
|
||
self.assertRecordValues(inv_line.move_id, [{'payment_state': 'paid'}])
|
||
self.assertRecordValues(inv_line.matched_credit_ids.exchange_move_id.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'account_id': inv_line.account_id.id, 'amount_currency': 0.0, 'currency_id': self.other_currency_2.id, 'balance': 400.0, 'reconciled': True, 'date': fields.Date.from_string('2017-01-31')},
|
||
{'account_id': income_exchange_account.id, 'amount_currency': 0.0, 'currency_id': self.other_currency_2.id, 'balance': -400.0, 'reconciled': False, 'date': fields.Date.from_string('2017-01-31')},
|
||
])
|
||
|
||
# Reset the wizard.
|
||
wizard._js_action_reset()
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1200.0, 'currency_id': self.company_data['currency'].id, 'balance': 1200.0},
|
||
{'flag': 'auto_balance', 'amount_currency': -1200.0, 'currency_id': self.company_data['currency'].id, 'balance': -1200.0},
|
||
])
|
||
|
||
# Create the same invoice with a higher amount to check the partial flow.
|
||
# 7200.0 curr2 == 1200.0 comp_curr (rate 6:1)
|
||
inv_line = self._create_invoice_line(
|
||
'out_invoice',
|
||
currency_id=self.other_currency_2.id,
|
||
invoice_date='2016-01-01',
|
||
invoice_line_ids=[{'price_unit': 7200.0}],
|
||
)
|
||
wizard._action_add_new_amls(inv_line)
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1200.0, 'currency_id': self.company_data['currency'].id, 'balance': 1200.0},
|
||
{'flag': 'new_aml', 'amount_currency': -4800.0, 'currency_id': self.other_currency_2.id, 'balance': -800.0},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'currency_id': self.other_currency_2.id, 'balance': -400.0},
|
||
])
|
||
|
||
# Check the message under the 'amount' field.
|
||
line = wizard.line_ids.filtered(lambda x: x.flag == 'new_aml')
|
||
wizard._js_action_mount_line_in_edit(line.index)
|
||
self.assert_form_extra_text_value(
|
||
wizard,
|
||
r".+open amount of 7,200.000.+ reduced by 4,800.000.+ set the invoice as fully paid .",
|
||
)
|
||
self.assertRecordValues(line, [{
|
||
'suggestion_amount_currency': -7200.0,
|
||
'suggestion_balance': -1200.0,
|
||
}])
|
||
|
||
# Switch to a full reconciliation.
|
||
wizard._js_action_apply_line_suggestion(line.index)
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1200.0, 'currency_id': self.company_data['currency'].id, 'balance': 1200.0},
|
||
{'flag': 'new_aml', 'amount_currency': -7200.0, 'currency_id': self.other_currency_2.id, 'balance': -1200.0},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'currency_id': self.other_currency_2.id, 'balance': -600.0},
|
||
{'flag': 'auto_balance', 'amount_currency': 600.0, 'currency_id': self.company_data['currency'].id, 'balance': 600.0},
|
||
])
|
||
|
||
# Check the message under the 'amount' field.
|
||
line = wizard.line_ids.filtered(lambda x: x.flag == 'new_aml')
|
||
wizard._js_action_mount_line_in_edit(line.index)
|
||
self.assert_form_extra_text_value(
|
||
wizard,
|
||
r".+open amount of 7,200.000.+ paid .+ record a partial payment .",
|
||
)
|
||
self.assertRecordValues(line, [{
|
||
'suggestion_amount_currency': -4800.0,
|
||
'suggestion_balance': -800.0,
|
||
}])
|
||
|
||
# Switch back to a partial reconciliation.
|
||
wizard._js_action_apply_line_suggestion(line.index)
|
||
self.assertRecordValues(wizard, [{'state': 'valid'}])
|
||
|
||
# Reconcile
|
||
wizard._action_validate()
|
||
self.assertRecordValues(st_line.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'account_id': st_line.journal_id.default_account_id.id, 'amount_currency': 1200.0, 'currency_id': self.company_data['currency'].id, 'balance': 1200.0, 'reconciled': False},
|
||
{'account_id': inv_line.account_id.id, 'amount_currency': -4800.0, 'currency_id': self.other_currency_2.id, 'balance': -1200.0, 'reconciled': True},
|
||
])
|
||
self.assertRecordValues(st_line, [{'is_reconciled': True}])
|
||
self.assertRecordValues(inv_line.move_id, [{
|
||
'payment_state': 'partial',
|
||
'amount_residual': 2400.0,
|
||
}])
|
||
self.assertRecordValues(inv_line.matched_credit_ids.exchange_move_id.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'account_id': inv_line.account_id.id, 'amount_currency': 0.0, 'currency_id': self.other_currency_2.id, 'balance': 400.0, 'reconciled': True, 'date': fields.Date.from_string('2017-01-31')},
|
||
{'account_id': income_exchange_account.id, 'amount_currency': 0.0, 'currency_id': self.other_currency_2.id, 'balance': -400.0, 'reconciled': False, 'date': fields.Date.from_string('2017-01-31')},
|
||
])
|
||
|
||
def test_validation_new_aml_multi_currencies(self):
|
||
# 6300.0 curr2 == 1800.0 comp_curr (bank rate 3.5:1 instead of the odoo rate 4:1)
|
||
st_line = self._create_st_line(
|
||
1800.0,
|
||
date='2017-01-01',
|
||
foreign_currency_id=self.other_currency_2.id,
|
||
amount_currency=6300.0,
|
||
)
|
||
# 21600.0 curr3 == 1800.0 comp_curr (rate 12:1)
|
||
inv_line = self._create_invoice_line(
|
||
'out_invoice',
|
||
currency_id=self.other_currency_3.id,
|
||
invoice_date='2016-01-01',
|
||
invoice_line_ids=[{'price_unit': 21600.0}],
|
||
)
|
||
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
wizard._action_add_new_amls(inv_line)
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1800.0, 'currency_id': self.company_data['currency'].id, 'balance': 1800.0},
|
||
{'flag': 'new_aml', 'amount_currency': -21600.0, 'currency_id': self.other_currency_3.id, 'balance': -1800.0},
|
||
])
|
||
self.assertRecordValues(wizard, [{'state': 'valid'}])
|
||
|
||
# The amount is the same, no message under the 'amount' field.
|
||
self.assert_form_extra_text_value(wizard, False)
|
||
|
||
wizard._action_validate()
|
||
self.assertRecordValues(st_line.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'account_id': st_line.journal_id.default_account_id.id, 'amount_currency': 1800.0, 'currency_id': self.company_data['currency'].id, 'balance': 1800.0, 'reconciled': False},
|
||
{'account_id': inv_line.account_id.id, 'amount_currency': -21600.0, 'currency_id': self.other_currency_3.id, 'balance': -1800.0, 'reconciled': True},
|
||
])
|
||
self.assertRecordValues(st_line, [{'is_reconciled': True}])
|
||
self.assertRecordValues(inv_line.move_id, [{'payment_state': 'paid'}])
|
||
|
||
# Reset the wizard.
|
||
wizard._js_action_reset()
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1800.0, 'currency_id': self.company_data['currency'].id, 'balance': 1800.0},
|
||
{'flag': 'auto_balance', 'amount_currency': -6300.0, 'currency_id': self.other_currency_2.id, 'balance': -1800.0},
|
||
])
|
||
|
||
# Create the same invoice with a higher amount to check the partial flow.
|
||
# 32400.0 curr3 == 2700.0 comp_curr (rate 12:1)
|
||
inv_line = self._create_invoice_line(
|
||
'out_invoice',
|
||
currency_id=self.other_currency_3.id,
|
||
invoice_date='2016-01-01',
|
||
invoice_line_ids=[{'price_unit': 32400.0}],
|
||
)
|
||
wizard._action_add_new_amls(inv_line)
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1800.0, 'currency_id': self.company_data['currency'].id, 'balance': 1800.0},
|
||
{'flag': 'new_aml', 'amount_currency': -21600.0, 'currency_id': self.other_currency_3.id, 'balance': -1800.0},
|
||
])
|
||
|
||
# Check the message under the 'amount' field.
|
||
line = wizard.line_ids.filtered(lambda x: x.flag == 'new_aml')
|
||
wizard._js_action_mount_line_in_edit(line.index)
|
||
self.assert_form_extra_text_value(
|
||
wizard,
|
||
r".+open amount of 32,400.000.+ reduced by 21,600.000.+ set the invoice as fully paid .",
|
||
)
|
||
self.assertRecordValues(line, [{
|
||
'suggestion_amount_currency': -32400.0,
|
||
'suggestion_balance': -2700.0,
|
||
}])
|
||
|
||
# Switch to a full reconciliation.
|
||
wizard._js_action_apply_line_suggestion(line.index)
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1800.0, 'currency_id': self.company_data['currency'].id, 'balance': 1800.0},
|
||
{'flag': 'new_aml', 'amount_currency': -32400.0, 'currency_id': self.other_currency_3.id, 'balance': -2700.0},
|
||
{'flag': 'auto_balance', 'amount_currency': 3150.0, 'currency_id': self.other_currency_2.id, 'balance': 900.0},
|
||
])
|
||
|
||
# Check the message under the 'amount' field.
|
||
line = wizard.line_ids.filtered(lambda x: x.flag == 'new_aml')
|
||
wizard._js_action_mount_line_in_edit(line.index)
|
||
self.assert_form_extra_text_value(
|
||
wizard,
|
||
r".+open amount of 32,400.000.+ paid .+ record a partial payment .",
|
||
)
|
||
self.assertRecordValues(line, [{
|
||
'suggestion_amount_currency': -21600.0,
|
||
'suggestion_balance': -1800.0,
|
||
}])
|
||
|
||
# Switch back to a partial reconciliation.
|
||
wizard._js_action_apply_line_suggestion(line.index)
|
||
self.assertRecordValues(wizard, [{'state': 'valid'}])
|
||
|
||
# Reconcile
|
||
wizard._action_validate()
|
||
self.assertRecordValues(st_line.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'account_id': st_line.journal_id.default_account_id.id, 'amount_currency': 1800.0, 'currency_id': self.company_data['currency'].id, 'balance': 1800.0, 'reconciled': False},
|
||
{'account_id': inv_line.account_id.id, 'amount_currency': -21600.0, 'currency_id': self.other_currency_3.id, 'balance': -1800.0, 'reconciled': True},
|
||
])
|
||
self.assertRecordValues(st_line, [{'is_reconciled': True}])
|
||
self.assertRecordValues(inv_line.move_id, [{
|
||
'payment_state': 'partial',
|
||
'amount_residual': 10800.0,
|
||
}])
|
||
|
||
def test_validation_new_aml_multi_currencies_exchange_diff_custom_rates(self):
|
||
self.company_data['default_journal_bank'].currency_id = self.other_currency
|
||
|
||
self.env['res.currency.rate'].create([
|
||
{
|
||
'name': '2017-02-01',
|
||
'rate': 1.0683,
|
||
'currency_id': self.other_currency.id,
|
||
'company_id': self.env.company.id,
|
||
},
|
||
{
|
||
'name': '2017-03-01',
|
||
'rate': 1.0812,
|
||
'currency_id': self.other_currency.id,
|
||
'company_id': self.env.company.id,
|
||
},
|
||
])
|
||
|
||
# 960.14 curr1 = 888.03 comp_curr
|
||
st_line = self._create_st_line(
|
||
-960.14,
|
||
date='2017-03-01',
|
||
)
|
||
# 112.7 curr1 == 105.49 comp_curr
|
||
inv_line1 = self._create_invoice_line(
|
||
'in_invoice',
|
||
currency_id=self.other_currency.id,
|
||
invoice_date='2017-02-01',
|
||
invoice_line_ids=[{'price_unit': 112.7}],
|
||
)
|
||
# 847.44 curr1 == 793.26 comp_curr
|
||
inv_line2 = self._create_invoice_line(
|
||
'in_invoice',
|
||
currency_id=self.other_currency.id,
|
||
invoice_date='2017-02-01',
|
||
invoice_line_ids=[{'price_unit': 847.44}],
|
||
)
|
||
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
wizard._action_add_new_amls(inv_line1)
|
||
wizard._action_add_new_amls(inv_line2)
|
||
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': -960.14, 'balance': -888.03},
|
||
{'flag': 'new_aml', 'amount_currency': 112.7, 'balance': 105.49},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'balance': -1.25},
|
||
{'flag': 'new_aml', 'amount_currency': 847.44, 'balance': 793.26},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'balance': -9.47},
|
||
])
|
||
wizard._action_remove_new_amls(inv_line1 + inv_line2)
|
||
wizard._action_add_new_amls(inv_line2)
|
||
wizard._action_add_new_amls(inv_line1)
|
||
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': -960.14, 'balance': -888.03},
|
||
{'flag': 'new_aml', 'amount_currency': 847.44, 'balance': 793.26},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'balance': -9.47},
|
||
{'flag': 'new_aml', 'amount_currency': 112.7, 'balance': 105.49},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'balance': -1.25},
|
||
])
|
||
|
||
def test_validation_with_partner(self):
|
||
partner = self.partner_a.copy()
|
||
|
||
st_line = self._create_st_line(1000.0, partner_id=self.partner_a.id)
|
||
|
||
# The wizard can be validated directly thanks to the receivable account set on the partner.
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
self.assertRecordValues(wizard, [{'state': 'valid'}])
|
||
|
||
# Validate and check the statement line.
|
||
wizard._action_validate()
|
||
self.assertRecordValues(st_line, [{'partner_id': self.partner_a.id}])
|
||
liquidity_line, _suspense_line, other_line = st_line._seek_for_lines()
|
||
account = self.partner_a.property_account_receivable_id
|
||
self.assertRecordValues(liquidity_line + other_line, [
|
||
# pylint: disable=C0326
|
||
{'account_id': liquidity_line.account_id.id, 'balance': 1000.0},
|
||
{'account_id': account.id, 'balance': -1000.0},
|
||
])
|
||
self.assertRecordValues(wizard, [{'state': 'reconciled'}])
|
||
|
||
# Match an invoice with a different partner.
|
||
wizard._js_action_reset()
|
||
inv_line = self._create_invoice_line(
|
||
'out_invoice',
|
||
partner_id=partner.id,
|
||
invoice_line_ids=[{'price_unit': 1000.0}],
|
||
)
|
||
wizard._action_add_new_amls(inv_line)
|
||
wizard._action_validate()
|
||
liquidity_line, suspense_line, other_line = st_line._seek_for_lines()
|
||
self.assertRecordValues(st_line, [{'partner_id': partner.id}])
|
||
self.assertRecordValues(st_line.move_id, [{'partner_id': partner.id}])
|
||
self.assertRecordValues(liquidity_line + other_line, [
|
||
# pylint: disable=C0326
|
||
{'account_id': liquidity_line.account_id.id, 'partner_id': partner.id, 'balance': 1000.0},
|
||
{'account_id': inv_line.account_id.id, 'partner_id': partner.id, 'balance': -1000.0},
|
||
])
|
||
self.assertRecordValues(wizard, [{'state': 'reconciled'}])
|
||
|
||
# Reset the wizard and match invoices with different partners.
|
||
wizard._js_action_reset()
|
||
partner1 = self.partner_a.copy()
|
||
inv_line1 = self._create_invoice_line(
|
||
'out_invoice',
|
||
partner_id=partner1.id,
|
||
invoice_line_ids=[{'price_unit': 300.0}],
|
||
)
|
||
partner2 = self.partner_a.copy()
|
||
inv_line2 = self._create_invoice_line(
|
||
'out_invoice',
|
||
partner_id=partner2.id,
|
||
invoice_line_ids=[{'price_unit': 300.0}],
|
||
)
|
||
wizard._action_add_new_amls(inv_line1 + inv_line2)
|
||
wizard._action_validate()
|
||
liquidity_line, _suspense_line, other_line = st_line._seek_for_lines()
|
||
self.assertRecordValues(st_line, [{'partner_id': False}])
|
||
self.assertRecordValues(st_line.move_id, [{'partner_id': False}])
|
||
self.assertRecordValues(liquidity_line + other_line, [
|
||
# pylint: disable=C0326
|
||
{'account_id': liquidity_line.account_id.id, 'partner_id': False, 'balance': 1000.0},
|
||
{'account_id': inv_line1.account_id.id, 'partner_id': partner1.id, 'balance': -300.0},
|
||
{'account_id': inv_line2.account_id.id, 'partner_id': partner2.id, 'balance': -300.0},
|
||
{'account_id': account.id, 'partner_id': False, 'balance': -400.0},
|
||
])
|
||
self.assertRecordValues(wizard, [{'state': 'reconciled'}])
|
||
|
||
# Clear the accounts set on the partner and reset the widget.
|
||
# The wizard should be invalid since we are not able to set an open balance.
|
||
partner.property_account_receivable_id = None
|
||
wizard._js_action_reset()
|
||
liquidity_line, suspense_line, other_line = st_line._seek_for_lines()
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'account_id': liquidity_line.account_id.id},
|
||
{'flag': 'auto_balance', 'account_id': suspense_line.account_id.id},
|
||
])
|
||
self.assertRecordValues(wizard, [{'state': 'invalid'}])
|
||
|
||
def test_partner_receivable_payable_account(self):
|
||
self.partner_a.write({'customer_rank': 1, 'supplier_rank': 0}) # always receivable
|
||
self.partner_b.write({'customer_rank': 0, 'supplier_rank': 1}) # always payable
|
||
partner_c = self.partner_b.copy({'customer_rank': 3, 'supplier_rank': 2}) # no preference
|
||
|
||
positive_st_line = self._create_st_line(1000)
|
||
journal_account = positive_st_line.journal_id.default_account_id
|
||
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=positive_st_line.id).new({})
|
||
suspense_line = wizard.line_ids.filtered(lambda l: l.flag != "liquidity")
|
||
wizard._js_action_mount_line_in_edit(suspense_line.index)
|
||
|
||
suspense_line.partner_id = self.partner_a
|
||
wizard._line_value_changed_partner_id(suspense_line)
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'partner_id': False, 'account_id': journal_account.id},
|
||
{'partner_id': self.partner_a.id, 'account_id': self.partner_a.property_account_receivable_id.id},
|
||
])
|
||
|
||
suspense_line.partner_id = self.partner_b
|
||
wizard._line_value_changed_partner_id(suspense_line)
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'partner_id': False, 'account_id': journal_account.id},
|
||
{'partner_id': self.partner_b.id, 'account_id': self.partner_b.property_account_payable_id.id},
|
||
])
|
||
|
||
suspense_line.partner_id = partner_c
|
||
wizard._line_value_changed_partner_id(suspense_line)
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'partner_id': False, 'account_id': journal_account.id},
|
||
{'partner_id': partner_c.id, 'account_id': partner_c.property_account_receivable_id.id},
|
||
])
|
||
|
||
negative_st_line = self._create_st_line(-1000)
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=negative_st_line.id).new({})
|
||
suspense_line = wizard.line_ids.filtered(lambda l: l.flag != "liquidity")
|
||
wizard._js_action_mount_line_in_edit(suspense_line.index)
|
||
|
||
suspense_line.partner_id = self.partner_a
|
||
wizard._line_value_changed_partner_id(suspense_line)
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'partner_id': False, 'account_id': journal_account.id},
|
||
{'partner_id': self.partner_a.id, 'account_id': self.partner_a.property_account_receivable_id.id},
|
||
])
|
||
|
||
suspense_line.partner_id = self.partner_b
|
||
wizard._line_value_changed_partner_id(suspense_line)
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'partner_id': False, 'account_id': journal_account.id},
|
||
{'partner_id': self.partner_b.id, 'account_id': self.partner_b.property_account_payable_id.id},
|
||
])
|
||
|
||
suspense_line.partner_id = partner_c
|
||
wizard._line_value_changed_partner_id(suspense_line)
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'partner_id': False, 'account_id': journal_account.id},
|
||
{'partner_id': partner_c.id, 'account_id': partner_c.property_account_payable_id.id},
|
||
])
|
||
|
||
def test_validation_using_custom_account(self):
|
||
st_line = self._create_st_line(1000.0)
|
||
|
||
# By default, the wizard can't be validated directly due to the suspense account.
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
self.assertRecordValues(wizard, [{'state': 'invalid'}])
|
||
|
||
# Mount the auto-balance line in edit mode.
|
||
line = wizard.line_ids.filtered(lambda x: x.flag == 'auto_balance')
|
||
wizard._js_action_mount_line_in_edit(line.index)
|
||
liquidity_line, suspense_line, _other_lines = st_line._seek_for_lines()
|
||
self.assertRecordValues(line, [{
|
||
'account_id': suspense_line.account_id.id,
|
||
'balance': -1000.0,
|
||
}])
|
||
|
||
# Switch to a custom account.
|
||
account = self.env['account.account'].create({
|
||
'name': "test_validation_using_custom_account",
|
||
'code': "424242",
|
||
'account_type': "asset_current",
|
||
})
|
||
line.account_id = account
|
||
wizard._line_value_changed_account_id(line)
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'account_id': liquidity_line.account_id.id, 'balance': 1000.0},
|
||
{'flag': 'manual', 'account_id': account.id, 'balance': -1000.0},
|
||
])
|
||
|
||
# The wizard can be validated.
|
||
self.assertRecordValues(wizard, [{'state': 'valid'}])
|
||
|
||
# Validate and check the statement line.
|
||
wizard._action_validate()
|
||
liquidity_line, _suspense_line, other_line = st_line._seek_for_lines()
|
||
self.assertRecordValues(liquidity_line + other_line, [
|
||
# pylint: disable=C0326
|
||
{'account_id': liquidity_line.account_id.id, 'balance': 1000.0},
|
||
{'account_id': account.id, 'balance': -1000.0},
|
||
])
|
||
self.assertRecordValues(wizard, [{'state': 'reconciled'}])
|
||
|
||
def test_validation_with_taxes(self):
|
||
st_line = self._create_st_line(1000.0)
|
||
|
||
tax_tags = self.env['account.account.tag'].create({
|
||
'name': f'tax_tag_{i}',
|
||
'applicability': 'taxes',
|
||
'country_id': self.env.company.account_fiscal_country_id.id,
|
||
} for i in range(4))
|
||
|
||
tax_21 = self.env['account.tax'].create({
|
||
'name': "tax_21",
|
||
'amount': 21,
|
||
'invoice_repartition_line_ids': [
|
||
Command.create({
|
||
'factor_percent': 100,
|
||
'repartition_type': 'base',
|
||
'tag_ids': [Command.set(tax_tags[0].ids)],
|
||
}),
|
||
Command.create({
|
||
'factor_percent': 100,
|
||
'repartition_type': 'tax',
|
||
'tag_ids': [Command.set(tax_tags[1].ids)],
|
||
}),
|
||
],
|
||
'refund_repartition_line_ids': [
|
||
Command.create({
|
||
'factor_percent': 100,
|
||
'repartition_type': 'base',
|
||
'tag_ids': [Command.set(tax_tags[2].ids)],
|
||
}),
|
||
Command.create({
|
||
'factor_percent': 100,
|
||
'repartition_type': 'tax',
|
||
'tag_ids': [Command.set(tax_tags[3].ids)],
|
||
}),
|
||
],
|
||
})
|
||
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
line = wizard.line_ids.filtered(lambda x: x.flag == 'auto_balance')
|
||
wizard._js_action_mount_line_in_edit(line.index)
|
||
line.tax_ids = [Command.link(tax_21.id)]
|
||
wizard._line_value_changed_tax_ids(line)
|
||
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'balance': 1000.0, 'tax_tag_ids': []},
|
||
{'flag': 'manual', 'balance': -826.45, 'tax_tag_ids': tax_tags[0].ids},
|
||
{'flag': 'tax_line', 'balance': -173.55, 'tax_tag_ids': tax_tags[1].ids},
|
||
])
|
||
|
||
# Remove the tax directly.
|
||
line.tax_ids = [Command.clear()]
|
||
wizard._line_value_changed_tax_ids(line)
|
||
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'balance': 1000.0, 'tax_tag_ids': []},
|
||
{'flag': 'manual', 'balance': -1000.0, 'tax_tag_ids': []},
|
||
])
|
||
|
||
# Edit the base line. The tax tags should be the refund ones.
|
||
line = wizard.line_ids.filtered(lambda x: x.flag == 'manual')
|
||
wizard._js_action_mount_line_in_edit(line.index)
|
||
line.tax_ids = [Command.link(tax_21.id)]
|
||
wizard._line_value_changed_tax_ids(line)
|
||
line.balance = 500.0
|
||
wizard._line_value_changed_balance(line)
|
||
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'balance': 1000.0, 'tax_tag_ids': []},
|
||
{'flag': 'manual', 'balance': 500.0, 'tax_tag_ids': tax_tags[2].ids},
|
||
{'flag': 'tax_line', 'balance': 105.0, 'tax_tag_ids': tax_tags[3].ids},
|
||
{'flag': 'auto_balance', 'balance': -1605.0, 'tax_tag_ids': []},
|
||
])
|
||
|
||
# Edit the base line.
|
||
line = wizard.line_ids.filtered(lambda x: x.flag == 'manual')
|
||
wizard._js_action_mount_line_in_edit(line.index)
|
||
line.balance = -500.0
|
||
wizard._line_value_changed_balance(line)
|
||
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'balance': 1000.0, 'tax_tag_ids': []},
|
||
{'flag': 'manual', 'balance': -500.0, 'tax_tag_ids': tax_tags[0].ids},
|
||
{'flag': 'tax_line', 'balance': -105.0, 'tax_tag_ids': tax_tags[1].ids},
|
||
{'flag': 'auto_balance', 'balance': -395.0, 'tax_tag_ids': []},
|
||
])
|
||
|
||
# Edit the tax line.
|
||
line = wizard.line_ids.filtered(lambda x: x.flag == 'tax_line')
|
||
wizard._js_action_mount_line_in_edit(line.index)
|
||
line.balance = -100.0
|
||
wizard._line_value_changed_balance(line)
|
||
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'balance': 1000.0, 'tax_tag_ids': []},
|
||
{'flag': 'manual', 'balance': -500.0, 'tax_tag_ids': tax_tags[0].ids},
|
||
{'flag': 'tax_line', 'balance': -100.0, 'tax_tag_ids': tax_tags[1].ids},
|
||
{'flag': 'auto_balance', 'balance': -400.0, 'tax_tag_ids': []},
|
||
])
|
||
|
||
# Add a new tax.
|
||
tax_10 = self.env['account.tax'].create({
|
||
'name': "tax_10",
|
||
'amount': 10,
|
||
})
|
||
|
||
line = wizard.line_ids.filtered(lambda x: x.flag == 'manual')
|
||
wizard._js_action_mount_line_in_edit(line.index)
|
||
line.tax_ids = [Command.link(tax_10.id)]
|
||
wizard._line_value_changed_tax_ids(line)
|
||
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'balance': 1000.0},
|
||
{'flag': 'manual', 'balance': -500.0},
|
||
{'flag': 'tax_line', 'balance': -105.0},
|
||
{'flag': 'tax_line', 'balance': -50.0},
|
||
{'flag': 'auto_balance', 'balance': -345.0},
|
||
])
|
||
|
||
# Remove the taxes.
|
||
line = wizard.line_ids.filtered(lambda x: x.flag == 'manual')
|
||
wizard._js_action_mount_line_in_edit(line.index)
|
||
line.tax_ids = [Command.clear()]
|
||
wizard._line_value_changed_tax_ids(line)
|
||
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'balance': 1000.0},
|
||
{'flag': 'manual', 'balance': -500.0},
|
||
{'flag': 'auto_balance', 'balance': -500.0},
|
||
])
|
||
|
||
# Reset the amount.
|
||
line = wizard.line_ids.filtered(lambda x: x.flag == 'manual')
|
||
wizard._js_action_mount_line_in_edit(line.index)
|
||
line.balance = -1000.0
|
||
wizard._line_value_changed_balance(line)
|
||
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'balance': 1000.0},
|
||
{'flag': 'manual', 'balance': -1000.0},
|
||
])
|
||
|
||
# Add taxes. We should be back into the "price included taxes" mode.
|
||
line = wizard.line_ids.filtered(lambda x: x.flag == 'manual')
|
||
wizard._js_action_mount_line_in_edit(line.index)
|
||
line.tax_ids = [Command.link(tax_21.id)]
|
||
wizard._line_value_changed_tax_ids(line)
|
||
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'balance': 1000.0},
|
||
{'flag': 'manual', 'balance': -826.45},
|
||
{'flag': 'tax_line', 'balance': -173.55},
|
||
])
|
||
|
||
line = wizard.line_ids.filtered(lambda x: x.flag == 'manual')
|
||
wizard._js_action_mount_line_in_edit(line.index)
|
||
line.tax_ids = [Command.link(tax_10.id)]
|
||
wizard._line_value_changed_tax_ids(line)
|
||
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'balance': 1000.0},
|
||
{'flag': 'manual', 'balance': -763.35},
|
||
{'flag': 'tax_line', 'balance': -160.31},
|
||
{'flag': 'tax_line', 'balance': -76.34},
|
||
])
|
||
|
||
# Changing the account should recompute the taxes but preserve the "price included taxes" mode.
|
||
line = wizard.line_ids.filtered(lambda x: x.flag == 'manual')
|
||
wizard._js_action_mount_line_in_edit(line.index)
|
||
line.account_id = self.account_revenue1
|
||
wizard._line_value_changed_account_id(line)
|
||
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'balance': 1000.0},
|
||
{'flag': 'manual', 'balance': -763.35},
|
||
{'flag': 'tax_line', 'balance': -160.31},
|
||
{'flag': 'tax_line', 'balance': -76.34},
|
||
])
|
||
|
||
# The wizard can be validated.
|
||
self.assertRecordValues(wizard, [{'state': 'valid'}])
|
||
|
||
# Validate and check the statement line.
|
||
wizard._action_validate()
|
||
self.assertRecordValues(st_line.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'balance': 1000.0},
|
||
{'balance': -763.35},
|
||
{'balance': -160.31},
|
||
{'balance': -76.34},
|
||
])
|
||
self.assertRecordValues(wizard, [{'state': 'reconciled'}])
|
||
|
||
def test_validation_caba_tax_account(self):
|
||
""" Cash basis taxes usually put their tax lines on a transition account, and the cash basis entries then move those amounts
|
||
to the regular tax accounts. When using a cash basis tax in the bank reconciliation widget, their won't be any cash basis
|
||
entry and the lines will directly be exigible, so we want to use the final tax account directly.
|
||
"""
|
||
tax_account = self.company_data['default_account_tax_sale']
|
||
|
||
caba_tax = self.env['account.tax'].create({
|
||
'name': "CABA",
|
||
'amount_type': 'percent',
|
||
'amount': 20.0,
|
||
'tax_exigibility': 'on_payment',
|
||
'cash_basis_transition_account_id': self.safe_copy(tax_account).id,
|
||
'invoice_repartition_line_ids': [
|
||
(0, 0, {
|
||
'repartition_type': 'base',
|
||
}),
|
||
(0, 0, {
|
||
'repartition_type': 'tax',
|
||
'account_id': tax_account.id,
|
||
}),
|
||
],
|
||
'refund_repartition_line_ids': [
|
||
(0, 0, {
|
||
'repartition_type': 'base',
|
||
}),
|
||
(0, 0, {
|
||
'repartition_type': 'tax',
|
||
'account_id': tax_account.id,
|
||
}),
|
||
],
|
||
})
|
||
|
||
st_line = self._create_st_line(120.0)
|
||
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
line = wizard.line_ids.filtered(lambda x: x.flag == 'auto_balance')
|
||
wizard._js_action_mount_line_in_edit(line.index)
|
||
line.account_id = self.account_revenue1
|
||
line.tax_ids = [Command.link(caba_tax.id)]
|
||
wizard._line_value_changed_tax_ids(line)
|
||
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'balance': 120.0, 'account_id': st_line.journal_id.default_account_id.id},
|
||
{'flag': 'manual', 'balance': -100.0, 'account_id': self.account_revenue1.id},
|
||
{'flag': 'tax_line', 'balance': -20.0, 'account_id': tax_account.id},
|
||
])
|
||
|
||
self.assertRecordValues(wizard, [{'state': 'valid'}])
|
||
|
||
wizard._action_validate()
|
||
self.assertRecordValues(st_line.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'balance': 120.0, 'tax_ids': [], 'tax_line_id': False, 'account_id': st_line.journal_id.default_account_id.id},
|
||
{'balance': -100.0, 'tax_ids': caba_tax.ids, 'tax_line_id': False, 'account_id': self.account_revenue1.id},
|
||
{'balance': -20.0, 'tax_ids': [], 'tax_line_id': caba_tax.id, 'account_id': tax_account.id},
|
||
])
|
||
self.assertRecordValues(wizard, [{'state': 'reconciled'}])
|
||
|
||
def test_validation_changed_default_account(self):
|
||
st_line = self._create_st_line(100.0, partner_id=self.partner_a.id)
|
||
original_journal_account_id = st_line.journal_id.default_account_id
|
||
# Change the default account of the journal (exceptional case)
|
||
st_line.journal_id.default_account_id = self.company_data['default_journal_cash'].default_account_id
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
self.assertRecordValues(wizard, [{'state': 'valid'}])
|
||
# Validate and check the statement line.
|
||
wizard._action_validate()
|
||
liquidity_line, _suspense_line, _other_line = st_line._seek_for_lines()
|
||
self.assertRecordValues(liquidity_line, [
|
||
{'account_id': original_journal_account_id.id, 'balance': 100.0},
|
||
])
|
||
self.assertRecordValues(wizard, [{'state': 'reconciled'}])
|
||
|
||
def test_apply_taxes_with_reco_model(self):
|
||
st_line = self._create_st_line(1000.0)
|
||
|
||
tax_21 = self.env['account.tax'].create({
|
||
'name': "tax_21",
|
||
'amount': 21,
|
||
})
|
||
|
||
reco_model = self.env['account.reconcile.model'].create({
|
||
'name': "test_apply_taxes_with_reco_model",
|
||
'rule_type': 'writeoff_button',
|
||
'line_ids': [Command.create({
|
||
'account_id': self.account_revenue1.id,
|
||
'tax_ids': [Command.set(tax_21.ids)],
|
||
})],
|
||
})
|
||
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
wizard._action_select_reconcile_model(reco_model)
|
||
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'balance': 1000.0},
|
||
{'flag': 'manual', 'balance': -826.45},
|
||
{'flag': 'tax_line', 'balance': -173.55},
|
||
])
|
||
|
||
def test_percentage_st_line_with_reco_model(self):
|
||
journal_curr = self.other_currency
|
||
foreign_curr = self.other_currency_2
|
||
self.company_data['default_journal_bank'].currency_id = journal_curr
|
||
|
||
# Setup triple currency.
|
||
st_line = self._create_st_line(
|
||
1000.0,
|
||
date='2018-01-01',
|
||
foreign_currency_id=foreign_curr.id,
|
||
amount_currency=4000.0,
|
||
)
|
||
|
||
reco_model = self.env['account.reconcile.model'].create({
|
||
'name': "test_percentage_st_line_with_reco_model",
|
||
'rule_type': 'writeoff_button',
|
||
'line_ids': [
|
||
Command.create({
|
||
'amount_type': 'percentage_st_line',
|
||
'amount_string': str(percentage),
|
||
'label': str(i),
|
||
'account_id': self.account_revenue1.id,
|
||
})
|
||
for i, percentage in enumerate((74.0, 24.0, 12.0, -10.0))
|
||
],
|
||
})
|
||
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
wizard._action_select_reconcile_model(reco_model)
|
||
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
{'flag': 'liquidity', 'currency_id': journal_curr.id, 'amount_currency': 1000.0, 'balance': 500.0},
|
||
{'flag': 'manual', 'currency_id': journal_curr.id, 'amount_currency': -740.0, 'balance': -370.0},
|
||
{'flag': 'manual', 'currency_id': journal_curr.id, 'amount_currency': -240.0, 'balance': -120.0},
|
||
{'flag': 'manual', 'currency_id': journal_curr.id, 'amount_currency': -120.0, 'balance': -60.0},
|
||
{'flag': 'manual', 'currency_id': journal_curr.id, 'amount_currency': 100.0, 'balance': 50.0},
|
||
])
|
||
|
||
def test_manual_edits_not_replaced(self):
|
||
""" 2 partial payments should keep the edited balance """
|
||
st_line = self._create_st_line(
|
||
1200.0,
|
||
date='2017-02-01',
|
||
)
|
||
inv_line_1 = self._create_invoice_line(
|
||
'out_invoice',
|
||
invoice_date='2016-01-01',
|
||
invoice_line_ids=[{'price_unit': 3000.0}],
|
||
)
|
||
inv_line_2 = self._create_invoice_line(
|
||
'out_invoice',
|
||
invoice_date='2017-01-01',
|
||
invoice_line_ids=[{'price_unit': 4000.0}],
|
||
)
|
||
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
wizard._action_add_new_amls(inv_line_1)
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'balance': 1200.0},
|
||
{'flag': 'new_aml', 'balance':-1200.0},
|
||
])
|
||
|
||
line = wizard.line_ids.filtered(lambda x: x.flag == 'new_aml')
|
||
wizard._js_action_mount_line_in_edit(line.index)
|
||
line.balance = -600.0
|
||
wizard._line_value_changed_balance(line)
|
||
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'balance': 1200.0},
|
||
{'flag': 'new_aml', 'balance': -600.0},
|
||
{'flag': 'auto_balance', 'balance': -600.0},
|
||
])
|
||
|
||
wizard._action_add_new_amls(inv_line_2)
|
||
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'balance': 1200.0},
|
||
{'flag': 'new_aml', 'balance': -600.0},
|
||
{'flag': 'new_aml', 'balance': -600.0},
|
||
])
|
||
|
||
def test_manual_edits_not_replaced_multicurrency(self):
|
||
""" 2 partial payments should keep the edited amount_currency """
|
||
st_line = self._create_st_line(
|
||
1200.0,
|
||
date='2018-01-01',
|
||
foreign_currency_id=self.other_currency_2.id,
|
||
amount_currency=6000.0, # rate 5:1
|
||
)
|
||
|
||
inv_line_1 = self._create_invoice_line(
|
||
'out_invoice',
|
||
invoice_date='2016-01-01',
|
||
currency_id=self.other_currency_2.id,
|
||
invoice_line_ids=[{'price_unit': 6000.0}], # 1000 company curr (rate 6:1)
|
||
)
|
||
inv_line_2 = self._create_invoice_line(
|
||
'out_invoice',
|
||
invoice_date='2017-01-01',
|
||
currency_id=self.other_currency_2.id,
|
||
invoice_line_ids=[{'price_unit': 4000.0}], # 1000 company curr (rate 4:1)
|
||
)
|
||
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
wizard._action_add_new_amls(inv_line_1)
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1200.0, 'balance': 1200.0},
|
||
{'flag': 'new_aml', 'amount_currency':-6000.0, 'balance':-1000.0},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'balance': -200.0},
|
||
])
|
||
|
||
line = wizard.line_ids.filtered(lambda x: x.flag == 'new_aml')
|
||
wizard._js_action_mount_line_in_edit(line.index)
|
||
line.amount_currency = -3000.0
|
||
wizard._line_value_changed_amount_currency(line)
|
||
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1200.0, 'balance': 1200.0},
|
||
{'flag': 'new_aml', 'amount_currency':-3000.0, 'balance': -500.0},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'balance': -100.0},
|
||
{'flag': 'auto_balance', 'amount_currency':-3000.0, 'balance': -600.0},
|
||
])
|
||
|
||
wizard._action_add_new_amls(inv_line_2)
|
||
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1200.0, 'balance': 1200.0},
|
||
{'flag': 'new_aml', 'amount_currency':-3000.0, 'balance': -500.0},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'balance': -100.0},
|
||
{'flag': 'new_aml', 'amount_currency':-3000.0, 'balance': -750.0},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'balance': 150.0},
|
||
])
|
||
|
||
def test_creating_manual_line_multi_currencies(self):
|
||
# 6300.0 curr2 == 1800.0 comp_curr (bank rate 3.5:1 instead of the odoo rate 4:1)
|
||
st_line = self._create_st_line(
|
||
1800.0,
|
||
date='2017-01-01',
|
||
foreign_currency_id=self.other_currency_2.id,
|
||
amount_currency=6300.0,
|
||
)
|
||
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1800.0, 'currency_id': self.company_data['currency'].id, 'balance': 1800.0},
|
||
{'flag': 'auto_balance', 'amount_currency': -6300.0, 'currency_id': self.other_currency_2.id, 'balance': -1800.0},
|
||
])
|
||
|
||
# Custom balance.
|
||
line = wizard.line_ids.filtered(lambda x: x.flag == 'auto_balance')
|
||
wizard._js_action_mount_line_in_edit(line.index)
|
||
line.balance = -1500.0
|
||
wizard._line_value_changed_balance(line)
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1800.0, 'currency_id': self.company_data['currency'].id, 'balance': 1800.0},
|
||
{'flag': 'manual', 'amount_currency': -6300.0, 'currency_id': self.other_currency_2.id, 'balance': -1500.0},
|
||
{'flag': 'auto_balance', 'amount_currency': 0.0, 'currency_id': self.other_currency_2.id, 'balance': -300.0},
|
||
])
|
||
|
||
# Custom amount_currency.
|
||
line = wizard.line_ids.filtered(lambda x: x.flag == 'manual')
|
||
wizard._js_action_mount_line_in_edit(line.index)
|
||
line.amount_currency = -4200.0
|
||
wizard._line_value_changed_amount_currency(line)
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1800.0, 'currency_id': self.company_data['currency'].id, 'balance': 1800.0},
|
||
{'flag': 'manual', 'amount_currency': -4200.0, 'currency_id': self.other_currency_2.id, 'balance': -1200.0},
|
||
{'flag': 'auto_balance', 'amount_currency': -2100.0, 'currency_id': self.other_currency_2.id, 'balance': -600.0},
|
||
])
|
||
|
||
# Custom currency_id.
|
||
line = wizard.line_ids.filtered(lambda x: x.flag == 'manual')
|
||
wizard._js_action_mount_line_in_edit(line.index)
|
||
line.currency_id = self.other_currency
|
||
wizard._line_value_changed_currency_id(line)
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1800.0, 'currency_id': self.company_data['currency'].id, 'balance': 1800.0},
|
||
{'flag': 'manual', 'amount_currency': -4200.0, 'currency_id': self.other_currency.id, 'balance': -2100.0},
|
||
{'flag': 'auto_balance', 'amount_currency': 1050.0, 'currency_id': self.other_currency_2.id, 'balance': 300.0},
|
||
])
|
||
|
||
# Custom balance.
|
||
line = wizard.line_ids.filtered(lambda x: x.flag == 'manual')
|
||
wizard._js_action_mount_line_in_edit(line.index)
|
||
line.balance = -1800.0
|
||
wizard._line_value_changed_balance(line)
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1800.0, 'currency_id': self.company_data['currency'].id, 'balance': 1800.0},
|
||
{'flag': 'manual', 'amount_currency': -4200.0, 'currency_id': self.other_currency.id, 'balance': -1800.0},
|
||
])
|
||
|
||
def test_auto_reconcile_cron(self):
|
||
self.env['account.reconcile.model'].search([('company_id', '=', self.company_data['company'].id)]).unlink()
|
||
cron = self.env.ref('fusion_accounting.auto_reconcile_bank_statement_line')
|
||
self.env['ir.cron.trigger'].search([('cron_id', '=', cron.id)]).unlink()
|
||
|
||
st_line = self._create_st_line(1234.0, partner_id=self.partner_a.id, date='2017-01-01')
|
||
self.assertEqual(len(self.env['ir.cron.trigger'].search([('cron_id', '=', cron.id)])), 1)
|
||
|
||
self._create_invoice_line(
|
||
'out_invoice',
|
||
invoice_date='2017-01-01',
|
||
invoice_line_ids=[{'price_unit': 1234.0}],
|
||
)
|
||
|
||
rule = self.env['account.reconcile.model'].create({
|
||
'name': "test_auto_reconcile_cron",
|
||
'rule_type': 'writeoff_suggestion',
|
||
'auto_reconcile': False,
|
||
'line_ids': [Command.create({'account_id': self.account_revenue1.id})],
|
||
})
|
||
|
||
# The CRON is not doing anything since the model is not auto reconcile.
|
||
with freeze_time('2017-01-01'):
|
||
self.env['account.bank.statement.line']._cron_try_auto_reconcile_statement_lines()
|
||
self.assertRecordValues(st_line, [{'is_reconciled': False, 'cron_last_check': False}])
|
||
self.assertEqual(len(self.env['ir.cron.trigger'].search([('cron_id', '=', cron.id)])), 1)
|
||
|
||
rule.auto_reconcile = True
|
||
|
||
# The CRON don't consider old statement lines.
|
||
with freeze_time('2017-06-01'):
|
||
self.env['account.bank.statement.line']._cron_try_auto_reconcile_statement_lines()
|
||
self.assertRecordValues(st_line, [{'is_reconciled': False, 'cron_last_check': False}])
|
||
self.assertEqual(len(self.env['ir.cron.trigger'].search([('cron_id', '=', cron.id)])), 1)
|
||
|
||
# The CRON will auto-reconcile the line.
|
||
with freeze_time('2017-01-02'):
|
||
self.env['account.bank.statement.line']._cron_try_auto_reconcile_statement_lines()
|
||
self.assertRecordValues(st_line, [{'is_reconciled': True, 'cron_last_check': fields.Datetime.from_string('2017-01-02 00:00:00')}])
|
||
self.assertEqual(len(self.env['ir.cron.trigger'].search([('cron_id', '=', cron.id)])), 1)
|
||
|
||
st_line1 = self._create_st_line(1234.0, partner_id=self.partner_a.id, date='2018-01-01')
|
||
self.assertEqual(len(self.env['ir.cron.trigger'].search([('cron_id', '=', cron.id)])), 2)
|
||
self._create_invoice_line(
|
||
'out_invoice',
|
||
invoice_date='2018-01-01',
|
||
invoice_line_ids=[{'price_unit': 1234.0}],
|
||
)
|
||
st_line2 = self._create_st_line(1234.0, partner_id=self.partner_a.id, date='2018-01-01')
|
||
self.assertEqual(len(self.env['ir.cron.trigger'].search([('cron_id', '=', cron.id)])), 3)
|
||
self._create_invoice_line(
|
||
'out_invoice',
|
||
invoice_date='2018-01-01',
|
||
invoice_line_ids=[{'price_unit': 1234.0}],
|
||
)
|
||
|
||
# Simulate the cron already tried to process 'st_line1' before.
|
||
with freeze_time('2017-12-31'):
|
||
st_line1.cron_last_check = fields.Datetime.now()
|
||
|
||
# The statement line with no 'cron_last_check' must be processed before others.
|
||
with freeze_time('2018-01-02'):
|
||
self.env['account.bank.statement.line']._cron_try_auto_reconcile_statement_lines(batch_size=1)
|
||
|
||
self.assertRecordValues(st_line1 + st_line2, [
|
||
{'is_reconciled': False, 'cron_last_check': fields.Datetime.from_string('2017-12-31 00:00:00')},
|
||
{'is_reconciled': True, 'cron_last_check': fields.Datetime.from_string('2018-01-02 00:00:00')},
|
||
])
|
||
self.assertEqual(len(self.env['ir.cron.trigger'].search([('cron_id', '=', cron.id)])), 4)
|
||
|
||
with freeze_time('2018-01-03'):
|
||
self.env['account.bank.statement.line']._cron_try_auto_reconcile_statement_lines(batch_size=1)
|
||
|
||
self.assertRecordValues(st_line1, [{'is_reconciled': True, 'cron_last_check': fields.Datetime.from_string('2018-01-03 00:00:00')}])
|
||
self.assertEqual(len(self.env['ir.cron.trigger'].search([('cron_id', '=', cron.id)])), 4)
|
||
|
||
st_line3 = self._create_st_line(1234.0, date='2018-01-01')
|
||
self.assertEqual(len(self.env['ir.cron.trigger'].search([('cron_id', '=', cron.id)])), 5)
|
||
self._create_invoice_line(
|
||
'out_invoice',
|
||
invoice_date='2018-01-01',
|
||
invoice_line_ids=[{'price_unit': 1234.0}],
|
||
)
|
||
st_line4 = self._create_st_line(1234.0, date='2018-01-01')
|
||
self.assertEqual(len(self.env['ir.cron.trigger'].search([('cron_id', '=', cron.id)])), 6)
|
||
self._create_invoice_line(
|
||
'out_invoice',
|
||
invoice_date='2018-01-01',
|
||
invoice_line_ids=[{'price_unit': 1234.0}],
|
||
)
|
||
|
||
# Make sure the CRON is no longer applicable.
|
||
rule.match_partner = True
|
||
rule.match_partner_ids = [Command.set(self.partner_a.ids)]
|
||
with freeze_time('2018-01-01'):
|
||
self.env['account.bank.statement.line']._cron_try_auto_reconcile_statement_lines(batch_size=1)
|
||
|
||
self.assertRecordValues(st_line3 + st_line4, [
|
||
{'is_reconciled': False, 'cron_last_check': fields.Datetime.from_string('2018-01-01 00:00:00')},
|
||
{'is_reconciled': False, 'cron_last_check': False},
|
||
])
|
||
self.assertEqual(len(self.env['ir.cron.trigger'].search([('cron_id', '=', cron.id)])), 7)
|
||
|
||
# Make sure the statement lines are reconciled by the cron in the right order.
|
||
self.assertRecordValues(st_line3 + st_line4, [
|
||
{'is_reconciled': False, 'cron_last_check': fields.Datetime.from_string('2018-01-01 00:00:00')},
|
||
{'is_reconciled': False, 'cron_last_check': False},
|
||
])
|
||
|
||
# st_line4 is processed because cron_last_check is null.
|
||
with freeze_time('2018-01-02'):
|
||
self.env['account.bank.statement.line']._cron_try_auto_reconcile_statement_lines(batch_size=1)
|
||
|
||
self.assertRecordValues(st_line3 + st_line4, [
|
||
{'is_reconciled': False, 'cron_last_check': fields.Datetime.from_string('2018-01-01 00:00:00')},
|
||
{'is_reconciled': False, 'cron_last_check': fields.Datetime.from_string('2018-01-02 00:00:00')},
|
||
])
|
||
self.assertEqual(len(self.env['ir.cron.trigger'].search([('cron_id', '=', cron.id)])), 7)
|
||
|
||
# st_line3 is processed because it has the oldest cron_last_check.
|
||
with freeze_time('2018-01-03'):
|
||
self.env['account.bank.statement.line']._cron_try_auto_reconcile_statement_lines(batch_size=1)
|
||
|
||
self.assertRecordValues(st_line3 + st_line4, [
|
||
{'is_reconciled': False, 'cron_last_check': fields.Datetime.from_string('2018-01-03 00:00:00')},
|
||
{'is_reconciled': False, 'cron_last_check': fields.Datetime.from_string('2018-01-02 00:00:00')},
|
||
])
|
||
self.assertEqual(len(self.env['ir.cron.trigger'].search([('cron_id', '=', cron.id)])), 7)
|
||
|
||
def test_duplicate_amls_constraint(self):
|
||
st_line = self._create_st_line(1000.0)
|
||
inv_line = self._create_invoice_line(
|
||
'out_invoice',
|
||
invoice_line_ids=[{'price_unit': 1000.0}],
|
||
)
|
||
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
wizard._action_add_new_amls(inv_line)
|
||
self.assertTrue(len(wizard.line_ids), 2)
|
||
|
||
wizard._action_add_new_amls(inv_line)
|
||
self.assertTrue(len(wizard.line_ids), 2)
|
||
|
||
@freeze_time('2017-01-01')
|
||
def test_reconcile_model_with_payment_tolerance(self):
|
||
self.env['account.reconcile.model'].search([('company_id', '=', self.company_data['company'].id)]).unlink()
|
||
|
||
invoice_line = self._create_invoice_line(
|
||
'out_invoice',
|
||
invoice_date='2017-01-01',
|
||
invoice_line_ids=[{'price_unit': 1000.0}],
|
||
)
|
||
st_line = self._create_st_line(998.0, partner_id=self.partner_a.id, date='2017-01-01', payment_ref=invoice_line.move_id.name)
|
||
|
||
rule = self.env['account.reconcile.model'].create({
|
||
'name': "test_reconcile_model_with_payment_tolerance",
|
||
'rule_type': 'invoice_matching',
|
||
'allow_payment_tolerance': True,
|
||
'payment_tolerance_type': 'percentage',
|
||
'payment_tolerance_param': 2.0,
|
||
'line_ids': [Command.create({'account_id': self.account_revenue1.id})],
|
||
})
|
||
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
wizard._action_trigger_matching_rules()
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'balance': 998.0, 'reconcile_model_id': False},
|
||
{'flag': 'new_aml', 'balance': -1000.0, 'reconcile_model_id': rule.id},
|
||
{'flag': 'manual', 'balance': 2.0, 'reconcile_model_id': rule.id},
|
||
])
|
||
|
||
@freeze_time('2017-01-01')
|
||
def test_auto_reconcile_model_with_archived_partner(self):
|
||
self.env['account.reconcile.model'].search([('company_id', '=', self.company_data['company'].id)]).unlink()
|
||
self.env['res.partner.bank'].search([('company_id', '=', self.company_data['company'].id)]).unlink()
|
||
|
||
# Needed as partner_a does not have a company and we need a company in order to find matching partner from account_number
|
||
self.partner_a.company_id = self.company_data['company'].id
|
||
self.env['res.partner.bank'].create({
|
||
'partner_id': self.partner_a.id,
|
||
'acc_number': '12345',
|
||
})
|
||
invoice_line = self._create_invoice_line(
|
||
'out_invoice',
|
||
invoice_date='2017-01-01',
|
||
invoice_line_ids=[{'price_unit': 1000.0}],
|
||
)
|
||
st_line = self._create_st_line(1000.0, account_number='12345', date='2017-01-01', payment_ref=invoice_line.move_id.name)
|
||
|
||
self.env['account.reconcile.model'].create({
|
||
'name': "test_reconcile_model_with_archived_partner",
|
||
'rule_type': 'invoice_matching',
|
||
'auto_reconcile': True,
|
||
'match_partner': True,
|
||
})
|
||
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
wizard._action_trigger_matching_rules()
|
||
self.assertEqual(wizard.partner_id, self.partner_a)
|
||
self.assertTrue(wizard.matching_rules_allow_auto_reconcile)
|
||
|
||
# archive partner and trigger again, partner should still be set (match based on account_number), because it's the only match
|
||
self.partner_a.active = False
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
wizard._action_trigger_matching_rules()
|
||
self.assertEqual(wizard.partner_id, self.partner_a) # Partner should still be set
|
||
self.assertTrue(wizard.matching_rules_allow_auto_reconcile) # no special process because the partner is unactive
|
||
|
||
def test_early_payment_included_multi_currency(self):
|
||
self.env['account.reconcile.model'].search([('company_id', '=', self.company_data['company'].id)]).unlink()
|
||
self.early_payment_term.early_pay_discount_computation = 'included'
|
||
income_exchange_account = self.env.company.income_currency_exchange_account_id
|
||
expense_exchange_account = self.env.company.expense_currency_exchange_account_id
|
||
|
||
inv_line1_with_epd = self._create_invoice_line(
|
||
'out_invoice',
|
||
currency_id=self.other_currency_2.id,
|
||
partner_id=self.partner_a.id,
|
||
invoice_payment_term_id=self.early_payment_term.id,
|
||
invoice_date='2016-12-01',
|
||
invoice_line_ids=[
|
||
{
|
||
'price_unit': 4800.0,
|
||
'account_id': self.account_revenue1.id,
|
||
'tax_ids': [Command.set(self.company_data['default_tax_sale'].ids)],
|
||
},
|
||
{
|
||
'price_unit': 9600.0,
|
||
'account_id': self.account_revenue2.id,
|
||
'tax_ids': [Command.set(self.company_data['default_tax_sale'].ids)],
|
||
},
|
||
],
|
||
)
|
||
inv_line1_with_epd_rec_lines = inv_line1_with_epd.move_id.line_ids\
|
||
.filtered(lambda x: x.account_type == 'asset_receivable')\
|
||
.sorted(lambda x: x.discount_date or x.date_maturity)
|
||
self.assertRecordValues(
|
||
inv_line1_with_epd_rec_lines,
|
||
[
|
||
{
|
||
'amount_currency': 16560.0,
|
||
'balance': 2760.0,
|
||
'discount_amount_currency': 14904.0,
|
||
'discount_balance': 2484.0,
|
||
'discount_date': fields.Date.from_string('2016-12-11'),
|
||
'date_maturity': fields.Date.from_string('2016-12-21'),
|
||
},
|
||
],
|
||
)
|
||
|
||
inv_line2_with_epd = self._create_invoice_line(
|
||
'out_invoice',
|
||
currency_id=self.other_currency_2.id,
|
||
partner_id=self.partner_a.id,
|
||
invoice_payment_term_id=self.early_payment_term.id,
|
||
invoice_date='2017-01-20',
|
||
invoice_line_ids=[
|
||
{
|
||
'price_unit': 480.0,
|
||
'account_id': self.account_revenue1.id,
|
||
'tax_ids': [Command.set(self.company_data['default_tax_sale'].ids)],
|
||
},
|
||
{
|
||
'price_unit': 960.0,
|
||
'account_id': self.account_revenue2.id,
|
||
'tax_ids': [Command.set(self.company_data['default_tax_sale'].ids)],
|
||
},
|
||
],
|
||
)
|
||
inv_line2_with_epd_rec_lines = inv_line2_with_epd.move_id.line_ids\
|
||
.filtered(lambda x: x.account_type == 'asset_receivable')\
|
||
.sorted(lambda x: x.discount_date or x.date_maturity)
|
||
self.assertRecordValues(
|
||
inv_line2_with_epd_rec_lines,
|
||
[
|
||
{
|
||
'amount_currency': 1656.0,
|
||
'balance': 414.0,
|
||
'discount_amount_currency': 1490.4,
|
||
'discount_balance': 372.6,
|
||
'discount_date': fields.Date.from_string('2017-01-30'),
|
||
'date_maturity': fields.Date.from_string('2017-02-09'),
|
||
},
|
||
],
|
||
)
|
||
|
||
# inv1: 16560.0 (no epd)
|
||
# inv2: 1490.4 (epd)
|
||
st_line = self._create_st_line(
|
||
4512.0, # instead of 4512.6 (rate 1:4)
|
||
date='2017-01-04',
|
||
foreign_currency_id=self.other_currency_2.id,
|
||
amount_currency=18050.4,
|
||
)
|
||
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
|
||
# Add all lines from the first invoice plus the first one from the second one.
|
||
wizard._action_add_new_amls(inv_line1_with_epd_rec_lines + inv_line2_with_epd_rec_lines)
|
||
liquidity_acc = st_line.journal_id.default_account_id
|
||
receivable_acc = self.company_data['default_account_receivable']
|
||
early_pay_acc = self.env.company.account_journal_early_pay_discount_loss_account_id
|
||
tax_acc = self.company_data['default_tax_sale'].invoice_repartition_line_ids.account_id
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 4512.0, 'balance': 4512.0, 'account_id': liquidity_acc.id},
|
||
{'flag': 'new_aml', 'amount_currency': -16560.0, 'balance': -2760.0, 'account_id': receivable_acc.id},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'balance': -1379.45, 'account_id': income_exchange_account.id},
|
||
{'flag': 'new_aml', 'amount_currency': -1656.0, 'balance': -414.0, 'account_id': receivable_acc.id},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'balance': 0.06, 'account_id': expense_exchange_account.id},
|
||
{'flag': 'early_payment', 'amount_currency': 144.0, 'balance': 36.0, 'account_id': early_pay_acc.id},
|
||
{'flag': 'early_payment', 'amount_currency': 21.6, 'balance': 5.4, 'account_id': tax_acc.id},
|
||
{'flag': 'early_payment', 'amount_currency': 0.0, 'balance': -0.01, 'account_id': income_exchange_account.id},
|
||
])
|
||
|
||
def test_early_payment_excluded_multi_currency(self):
|
||
self.env['account.reconcile.model'].search([('company_id', '=', self.company_data['company'].id)]).unlink()
|
||
self.early_payment_term.early_pay_discount_computation = 'excluded'
|
||
income_exchange_account = self.env.company.income_currency_exchange_account_id
|
||
expense_exchange_account = self.env.company.expense_currency_exchange_account_id
|
||
|
||
inv_line1_with_epd = self._create_invoice_line(
|
||
'out_invoice',
|
||
currency_id=self.other_currency_2.id,
|
||
partner_id=self.partner_a.id,
|
||
invoice_payment_term_id=self.early_payment_term.id,
|
||
invoice_date='2016-12-01',
|
||
invoice_line_ids=[
|
||
{
|
||
'price_unit': 4800.0,
|
||
'account_id': self.account_revenue1.id,
|
||
'tax_ids': [Command.set(self.company_data['default_tax_sale'].ids)],
|
||
},
|
||
{
|
||
'price_unit': 9600.0,
|
||
'account_id': self.account_revenue2.id,
|
||
'tax_ids': [Command.set(self.company_data['default_tax_sale'].ids)],
|
||
},
|
||
],
|
||
)
|
||
inv_line1_with_epd_rec_lines = inv_line1_with_epd.move_id.line_ids\
|
||
.filtered(lambda x: x.account_type == 'asset_receivable')\
|
||
.sorted(lambda x: x.discount_date or x.date_maturity)
|
||
self.assertRecordValues(
|
||
inv_line1_with_epd_rec_lines,
|
||
[
|
||
{
|
||
'amount_currency': 16560.0,
|
||
'balance': 2760.0,
|
||
'discount_amount_currency': 15120.0,
|
||
'discount_balance': 2520.0,
|
||
'discount_date': fields.Date.from_string('2016-12-11'),
|
||
'date_maturity': fields.Date.from_string('2016-12-21'),
|
||
},
|
||
],
|
||
)
|
||
|
||
inv_line2_with_epd = self._create_invoice_line(
|
||
'out_invoice',
|
||
currency_id=self.other_currency_2.id,
|
||
partner_id=self.partner_a.id,
|
||
invoice_payment_term_id=self.early_payment_term.id,
|
||
invoice_date='2017-01-20',
|
||
invoice_line_ids=[
|
||
{
|
||
'price_unit': 480.0,
|
||
'account_id': self.account_revenue1.id,
|
||
'tax_ids': [Command.set(self.company_data['default_tax_sale'].ids)],
|
||
},
|
||
{
|
||
'price_unit': 960.0,
|
||
'account_id': self.account_revenue2.id,
|
||
'tax_ids': [Command.set(self.company_data['default_tax_sale'].ids)],
|
||
},
|
||
],
|
||
)
|
||
inv_line2_with_epd_rec_lines = inv_line2_with_epd.move_id.line_ids\
|
||
.filtered(lambda x: x.account_type == 'asset_receivable')\
|
||
.sorted(lambda x: x.discount_date or x.date_maturity)
|
||
self.assertRecordValues(
|
||
inv_line2_with_epd_rec_lines,
|
||
[
|
||
{
|
||
'amount_currency': 1656.0,
|
||
'balance': 414.0,
|
||
'discount_amount_currency': 1512.0,
|
||
'discount_balance': 378.0,
|
||
'discount_date': fields.Date.from_string('2017-01-30'),
|
||
'date_maturity': fields.Date.from_string('2017-02-09'),
|
||
},
|
||
],
|
||
)
|
||
|
||
# inv1: 16560.0 (no epd)
|
||
# inv2: 1512.0 (epd)
|
||
st_line = self._create_st_line(
|
||
4515.0, # instead of 4518.0 (rate 1:4)
|
||
date='2017-01-04',
|
||
foreign_currency_id=self.other_currency_2.id,
|
||
amount_currency=18072.0,
|
||
)
|
||
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
|
||
# Add all lines from the first invoice plus the first one from the second one.
|
||
wizard._action_add_new_amls(inv_line1_with_epd_rec_lines + inv_line2_with_epd_rec_lines[:2])
|
||
liquidity_acc = st_line.journal_id.default_account_id
|
||
receivable_acc = self.company_data['default_account_receivable']
|
||
early_pay_acc = self.env.company.account_journal_early_pay_discount_loss_account_id
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 4515.0, 'balance': 4515.0, 'account_id': liquidity_acc.id},
|
||
{'flag': 'new_aml', 'amount_currency': -16560.0, 'balance': -2760.0, 'account_id': receivable_acc.id},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'balance': -1377.25, 'account_id': income_exchange_account.id},
|
||
{'flag': 'new_aml', 'amount_currency': -1656.0, 'balance': -414.0, 'account_id': receivable_acc.id},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'balance': 0.27, 'account_id': expense_exchange_account.id},
|
||
{'flag': 'early_payment', 'amount_currency': 144.0, 'balance': 36.0, 'account_id': early_pay_acc.id},
|
||
{'flag': 'early_payment', 'amount_currency': 0.0, 'balance': -0.02, 'account_id': income_exchange_account.id},
|
||
])
|
||
|
||
def test_early_payment_mixed_multi_currency(self):
|
||
self.env['account.reconcile.model'].search([('company_id', '=', self.company_data['company'].id)]).unlink()
|
||
self.early_payment_term.early_pay_discount_computation = 'mixed'
|
||
income_exchange_account = self.env.company.income_currency_exchange_account_id
|
||
expense_exchange_account = self.env.company.expense_currency_exchange_account_id
|
||
|
||
inv_line1_with_epd = self._create_invoice_line(
|
||
'out_invoice',
|
||
currency_id=self.other_currency_2.id,
|
||
partner_id=self.partner_a.id,
|
||
invoice_payment_term_id=self.early_payment_term.id,
|
||
invoice_date='2016-12-01',
|
||
invoice_line_ids=[
|
||
{
|
||
'price_unit': 4800.0,
|
||
'account_id': self.account_revenue1.id,
|
||
'tax_ids': [Command.set(self.company_data['default_tax_sale'].ids)],
|
||
},
|
||
{
|
||
'price_unit': 9600.0,
|
||
'account_id': self.account_revenue2.id,
|
||
'tax_ids': [Command.set(self.company_data['default_tax_sale'].ids)],
|
||
},
|
||
],
|
||
)
|
||
inv_line1_with_epd_rec_lines = inv_line1_with_epd.move_id.line_ids\
|
||
.filtered(lambda x: x.account_type == 'asset_receivable')\
|
||
.sorted(lambda x: x.discount_date or x.date_maturity)
|
||
self.assertRecordValues(
|
||
inv_line1_with_epd_rec_lines,
|
||
[
|
||
{
|
||
'amount_currency': 16344.0,
|
||
'balance': 2724.0,
|
||
'discount_amount_currency': 14904.0,
|
||
'discount_balance': 2484.0,
|
||
'discount_date': fields.Date.from_string('2016-12-11'),
|
||
'date_maturity': fields.Date.from_string('2016-12-21'),
|
||
},
|
||
],
|
||
)
|
||
|
||
inv_line2_with_epd = self._create_invoice_line(
|
||
'out_invoice',
|
||
currency_id=self.other_currency_2.id,
|
||
partner_id=self.partner_a.id,
|
||
invoice_payment_term_id=self.early_payment_term.id,
|
||
invoice_date='2017-01-20',
|
||
invoice_line_ids=[
|
||
{
|
||
'price_unit': 480.0,
|
||
'account_id': self.account_revenue1.id,
|
||
'tax_ids': [Command.set(self.company_data['default_tax_sale'].ids)],
|
||
},
|
||
{
|
||
'price_unit': 960.0,
|
||
'account_id': self.account_revenue2.id,
|
||
'tax_ids': [Command.set(self.company_data['default_tax_sale'].ids)],
|
||
},
|
||
],
|
||
)
|
||
inv_line2_with_epd_rec_lines = inv_line2_with_epd.move_id.line_ids\
|
||
.filtered(lambda x: x.account_type == 'asset_receivable')\
|
||
.sorted(lambda x: x.discount_date or x.date_maturity)
|
||
self.assertRecordValues(
|
||
inv_line2_with_epd_rec_lines,
|
||
[
|
||
{
|
||
'amount_currency': 1634.4,
|
||
'balance': 408.6,
|
||
'discount_amount_currency': 1490.4,
|
||
'discount_balance': 372.6,
|
||
'discount_date': fields.Date.from_string('2017-01-30'),
|
||
'date_maturity': fields.Date.from_string('2017-02-09'),
|
||
},
|
||
],
|
||
)
|
||
|
||
# inv1: 16344.0 (no epd)
|
||
# inv2: 1490.4 (epd)
|
||
st_line = self._create_st_line(
|
||
4458.0, # instead of 4458.6 (rate 1:4)
|
||
date='2017-01-04',
|
||
foreign_currency_id=self.other_currency_2.id,
|
||
amount_currency=17834.4,
|
||
)
|
||
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
|
||
# Add all lines from the first invoice plus the first one from the second one.
|
||
wizard._action_add_new_amls(inv_line1_with_epd_rec_lines + inv_line2_with_epd_rec_lines[:2])
|
||
liquidity_acc = st_line.journal_id.default_account_id
|
||
receivable_acc = self.company_data['default_account_receivable']
|
||
early_pay_acc = self.env.company.account_journal_early_pay_discount_loss_account_id
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 4458.0, 'balance': 4458.0, 'account_id': liquidity_acc.id},
|
||
{'flag': 'new_aml', 'amount_currency': -16344.0, 'balance': -2724.0, 'account_id': receivable_acc.id},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'balance': -1361.45, 'account_id': income_exchange_account.id},
|
||
{'flag': 'new_aml', 'amount_currency': -1634.4, 'balance': -408.6, 'account_id': receivable_acc.id},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'balance': 0.05, 'account_id': expense_exchange_account.id},
|
||
{'flag': 'early_payment', 'amount_currency': 144.0, 'balance': 36.0, 'account_id': early_pay_acc.id},
|
||
])
|
||
|
||
def test_early_payment_included_intracomm_bill(self):
|
||
tax_tags = self.env['account.account.tag'].create({
|
||
'name': f'tax_tag_{i}',
|
||
'applicability': 'taxes',
|
||
'country_id': self.env.company.account_fiscal_country_id.id,
|
||
} for i in range(6))
|
||
|
||
intracomm_tax = self.env['account.tax'].create({
|
||
'name': 'tax20',
|
||
'amount_type': 'percent',
|
||
'amount': 20,
|
||
'type_tax_use': 'purchase',
|
||
'invoice_repartition_line_ids': [
|
||
# pylint: disable=bad-whitespace
|
||
Command.create({'repartition_type': 'base', 'factor_percent': 100.0, 'tag_ids': [Command.set(tax_tags[0].ids)]}),
|
||
Command.create({'repartition_type': 'tax', 'factor_percent': 100.0, 'tag_ids': [Command.set(tax_tags[1].ids)]}),
|
||
Command.create({'repartition_type': 'tax', 'factor_percent': -100.0, 'tag_ids': [Command.set(tax_tags[2].ids)]}),
|
||
],
|
||
'refund_repartition_line_ids': [
|
||
# pylint: disable=bad-whitespace
|
||
Command.create({'repartition_type': 'base', 'factor_percent': 100.0, 'tag_ids': [Command.set(tax_tags[3].ids)]}),
|
||
Command.create({'repartition_type': 'tax', 'factor_percent': 100.0, 'tag_ids': [Command.set(tax_tags[4].ids)]}),
|
||
Command.create({'repartition_type': 'tax', 'factor_percent': -100.0, 'tag_ids': [Command.set(tax_tags[5].ids)]}),
|
||
],
|
||
})
|
||
|
||
early_payment_term = self.env['account.payment.term'].create({
|
||
'name': "early_payment_term",
|
||
'company_id': self.company_data['company'].id,
|
||
'early_pay_discount_computation': 'included',
|
||
'early_discount': True,
|
||
'discount_percentage': 2,
|
||
'discount_days': 7,
|
||
'line_ids': [
|
||
Command.create({
|
||
'value': 'percent',
|
||
'value_amount': 100.0,
|
||
'nb_days': 30,
|
||
}),
|
||
],
|
||
})
|
||
|
||
bill = self.env['account.move'].create({
|
||
'move_type': 'in_invoice',
|
||
'partner_id': self.partner_a.id,
|
||
'invoice_payment_term_id': early_payment_term.id,
|
||
'invoice_date': '2019-01-01',
|
||
'date': '2019-01-01',
|
||
'invoice_line_ids': [
|
||
Command.create({
|
||
'name': 'line',
|
||
'price_unit': 1000.0,
|
||
'tax_ids': [Command.set(intracomm_tax.ids)],
|
||
}),
|
||
],
|
||
})
|
||
bill.action_post()
|
||
|
||
st_line = self._create_st_line(
|
||
-980.0,
|
||
date='2017-01-01',
|
||
)
|
||
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
wizard._action_add_new_amls(bill.line_ids.filtered(lambda x: x.account_type == 'liability_payable'))
|
||
wizard._action_validate()
|
||
|
||
self.assertRecordValues(st_line.line_ids.sorted('balance'), [
|
||
# pylint: disable=bad-whitespace
|
||
{'amount_currency': -980.0, 'tax_ids': [], 'tax_tag_ids': [], 'tax_tag_invert': False},
|
||
{'amount_currency': -20.0, 'tax_ids': intracomm_tax.ids, 'tax_tag_ids': tax_tags[3].ids, 'tax_tag_invert': True},
|
||
{'amount_currency': -4.0, 'tax_ids': [], 'tax_tag_ids': tax_tags[4].ids, 'tax_tag_invert': True},
|
||
{'amount_currency': 4.0, 'tax_ids': [], 'tax_tag_ids': tax_tags[5].ids, 'tax_tag_invert': True},
|
||
{'amount_currency': 1000.0, 'tax_ids': [], 'tax_tag_ids': [], 'tax_tag_invert': False},
|
||
])
|
||
|
||
def test_multi_currencies_with_custom_rate(self):
|
||
self.company_data['default_journal_bank'].currency_id = self.other_currency
|
||
st_line = self._create_st_line(1200.0) # rate 1:2
|
||
self.assertRecordValues(st_line.move_id.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'amount_currency': 1200.0, 'balance': 600.0},
|
||
{'amount_currency': -1200.0, 'balance': -600.0},
|
||
])
|
||
|
||
# invoice with other_currency and rate 1:2
|
||
invoice_line1 = self._create_invoice_line(
|
||
'out_invoice',
|
||
currency_id=self.other_currency.id,
|
||
invoice_date='2017-01-01',
|
||
invoice_line_ids=[{'price_unit': 300.0}], # = 150 USD
|
||
)
|
||
|
||
# Remove all rates.
|
||
self.other_currency.rate_ids.unlink()
|
||
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1200.0, 'balance': 600.0},
|
||
{'flag': 'auto_balance', 'amount_currency': -1200.0, 'balance': -600.0},
|
||
])
|
||
|
||
# invoice with other_currency_2 and rate 1:6
|
||
invoice_line2 = self._create_invoice_line(
|
||
'out_invoice',
|
||
currency_id=self.other_currency_2.id,
|
||
invoice_date='2016-01-01',
|
||
invoice_line_ids=[{'price_unit': 600.0}], # = 100 USD
|
||
)
|
||
# invoice with other_currency_2 and rate 1:4
|
||
invoice_line3 = self._create_invoice_line(
|
||
'out_invoice',
|
||
currency_id=self.other_currency_2.id,
|
||
invoice_date='2017-01-01',
|
||
invoice_line_ids=[{'price_unit': 400.0}], # = 100 USD
|
||
)
|
||
|
||
# Remove all rates.
|
||
self.other_currency_2.rate_ids.unlink()
|
||
|
||
# Ensure no conversion rate has been made.
|
||
wizard._action_add_new_amls(invoice_line1 + invoice_line2 + invoice_line3)
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1200.0, 'balance': 600.0},
|
||
{'flag': 'new_aml', 'amount_currency': -300.0, 'balance': -150.0},
|
||
{'flag': 'new_aml', 'amount_currency': -600.0, 'balance': -100.0},
|
||
{'flag': 'new_aml', 'amount_currency': -400.0, 'balance': -100.0},
|
||
{'flag': 'auto_balance', 'amount_currency': -500.0, 'balance': -250.0},
|
||
])
|
||
|
||
def test_partial_reconciliation_suggestion_with_mixed_invoice_and_refund(self):
|
||
""" Test the partial reconciliation suggestion is well recomputed when adding another
|
||
line. For example, when adding 2 invoices having an higher amount then a refund. In that
|
||
case, the partial on the second invoice should be removed since the difference is filled
|
||
by the newly added refund.
|
||
"""
|
||
st_line = self._create_st_line(
|
||
1800.0,
|
||
date='2017-01-01',
|
||
foreign_currency_id=self.other_currency.id,
|
||
amount_currency=3600.0,
|
||
)
|
||
|
||
inv1 = self._create_invoice_line(
|
||
'out_invoice',
|
||
currency_id=self.other_currency.id,
|
||
invoice_date='2016-01-01',
|
||
invoice_line_ids=[{'price_unit': 2400.0}],
|
||
)
|
||
inv2 = self._create_invoice_line(
|
||
'out_invoice',
|
||
currency_id=self.other_currency.id,
|
||
invoice_date='2016-01-01',
|
||
invoice_line_ids=[{'price_unit': 2400.0}],
|
||
)
|
||
refund = self._create_invoice_line(
|
||
'out_refund',
|
||
currency_id=self.other_currency.id,
|
||
invoice_date='2016-01-01',
|
||
invoice_line_ids=[{'price_unit': 1200.0}],
|
||
)
|
||
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
wizard._action_add_new_amls(inv1 + inv2)
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1800.0, 'balance': 1800.0},
|
||
{'flag': 'new_aml', 'amount_currency': -2400.0, 'balance': -800.0},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'balance': -400.0},
|
||
{'flag': 'new_aml', 'amount_currency': -1200.0, 'balance': -400.0},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'balance': -200.0},
|
||
])
|
||
wizard._action_add_new_amls(refund)
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1800.0, 'balance': 1800.0},
|
||
{'flag': 'new_aml', 'amount_currency': -2400.0, 'balance': -800.0},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'balance': -400.0},
|
||
{'flag': 'new_aml', 'amount_currency': -2400.0, 'balance': -800.0},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'balance': -400.0},
|
||
{'flag': 'new_aml', 'amount_currency': 1200.0, 'balance': 400.0},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'balance': 200.0},
|
||
])
|
||
|
||
def test_auto_reconcile_cron_with_time_limit(self):
|
||
self.env['account.reconcile.model'].search([('company_id', '=', self.company_data['company'].id)]).unlink()
|
||
cron = self.env.ref('fusion_accounting.auto_reconcile_bank_statement_line')
|
||
self.env['ir.cron.trigger'].search([('cron_id', '=', cron.id)]).unlink()
|
||
|
||
st_line1 = self._create_st_line(1234.0, partner_id=self.partner_a.id, date='2017-01-01')
|
||
self.assertEqual(len(self.env['ir.cron.trigger'].search([('cron_id', '=', cron.id)])), 1)
|
||
st_line2 = self._create_st_line(5678.0, partner_id=self.partner_a.id, date='2017-01-02')
|
||
self.assertEqual(len(self.env['ir.cron.trigger'].search([('cron_id', '=', cron.id)])), 2)
|
||
|
||
self._create_invoice_line(
|
||
'out_invoice',
|
||
invoice_date='2017-01-01',
|
||
invoice_line_ids=[{'price_unit': 1234.0}],
|
||
)
|
||
self._create_invoice_line(
|
||
'out_invoice',
|
||
invoice_date='2017-01-01',
|
||
invoice_line_ids=[{'price_unit': 5678.0}],
|
||
)
|
||
self.env['account.reconcile.model'].create({
|
||
'name': "test_auto_reconcile_cron_with_time_limit",
|
||
'rule_type': 'writeoff_suggestion',
|
||
'auto_reconcile': True,
|
||
'line_ids': [Command.create({'account_id': self.account_revenue1.id})],
|
||
})
|
||
|
||
with freeze_time('2017-01-01 00:00:00') as frozen_time:
|
||
def datetime_now_override():
|
||
frozen_time.tick()
|
||
return frozen_time()
|
||
with patch('odoo.fields.Datetime.now', side_effect=datetime_now_override):
|
||
# we simulate that the time limit is reached after first loop
|
||
self.env['account.bank.statement.line']._cron_try_auto_reconcile_statement_lines(limit_time=1)
|
||
# after first loop, only one statement should be reconciled
|
||
self.assertRecordValues(st_line1, [{'is_reconciled': True, 'cron_last_check': fields.Datetime.from_string('2017-01-01 00:00:01')}])
|
||
# the other one should be in queue for regular cron tigger
|
||
self.assertRecordValues(st_line2, [{'is_reconciled': False, 'cron_last_check': False}])
|
||
self.assertEqual(len(self.env['ir.cron.trigger'].search([('cron_id', '=', cron.id)])), 3)
|
||
|
||
def test_auto_reconcile_cron_with_provided_statements_lines(self):
|
||
self.env['account.reconcile.model'].search([('company_id', '=', self.company_data['company'].id)]).unlink()
|
||
|
||
st_line1 = self._create_st_line(1234.0, partner_id=self.partner_a.id, date='2017-01-01')
|
||
st_line2 = self._create_st_line(5678.0, partner_id=self.partner_a.id, date='2017-01-02')
|
||
self._create_invoice_line(
|
||
'out_invoice',
|
||
invoice_date='2017-01-01',
|
||
invoice_line_ids=[{'price_unit': 1234.0}],
|
||
)
|
||
self._create_invoice_line(
|
||
'out_invoice',
|
||
invoice_date='2017-01-01',
|
||
invoice_line_ids=[{'price_unit': 5678.0}],
|
||
)
|
||
self.env['account.reconcile.model'].create({
|
||
'name': "test_auto_reconcile_cron_with_time_limit",
|
||
'rule_type': 'writeoff_suggestion',
|
||
'auto_reconcile': True,
|
||
'line_ids': [Command.create({'account_id': self.account_revenue1.id})],
|
||
})
|
||
with freeze_time('2017-01-01 00:00:00'):
|
||
# we call auto reconcile on st_lines1 **only**
|
||
st_line1._cron_try_auto_reconcile_statement_lines()
|
||
self.assertRecordValues(st_line1, [{'is_reconciled': True, 'cron_last_check': fields.Datetime.from_string('2017-01-01 00:00:00')}])
|
||
self.assertRecordValues(st_line2, [{'is_reconciled': False, 'cron_last_check': False}])
|
||
|
||
@freeze_time('2019-01-01')
|
||
def test_button_apply_reco_model(self):
|
||
inv_line = self._create_invoice_line(
|
||
'in_invoice',
|
||
invoice_date='2019-01-01',
|
||
invoice_line_ids=[{'price_unit': 980.0}],
|
||
)
|
||
st_line = self._create_st_line(-1000.0, partner_id=self.partner_a.id, date=inv_line.date, payment_ref=inv_line.move_name)
|
||
|
||
reco_model = self.env['account.reconcile.model'].create({
|
||
'name': "test_apply_taxes_with_reco_model",
|
||
'rule_type': 'writeoff_button',
|
||
'line_ids': [Command.create({
|
||
'account_id': self.account_revenue1.copy().id,
|
||
'label': 'Bank Fees'
|
||
})],
|
||
})
|
||
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
wizard._action_trigger_matching_rules()
|
||
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'account_id': st_line.journal_id.default_account_id.id, 'balance': -1000.0},
|
||
{'flag': 'new_aml', 'account_id': inv_line.account_id.id, 'balance': 980.0},
|
||
{'flag': 'auto_balance', 'account_id': self.company_data['default_account_payable'].id, 'balance': 20.0},
|
||
])
|
||
|
||
wizard._action_select_reconcile_model(reco_model)
|
||
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'account_id': st_line.journal_id.default_account_id.id, 'balance': -1000.0},
|
||
{'flag': 'new_aml', 'account_id': inv_line.account_id.id, 'balance': 980.0},
|
||
{'flag': 'manual', 'account_id': reco_model.line_ids[0].account_id.id, 'balance': 20.0},
|
||
])
|
||
|
||
def test_exchange_diff_on_partial_aml_multi_currency(self):
|
||
self.company_data['default_journal_bank'].currency_id = self.other_currency
|
||
st_line = self._create_st_line(-36000.0) # rate 1:2
|
||
inv_line = self._create_invoice_line(
|
||
'in_invoice',
|
||
invoice_date='2016-01-01', # rate 1:3
|
||
currency_id=self.other_currency.id,
|
||
invoice_line_ids=[{'price_unit': 38000.0}],
|
||
)
|
||
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
wizard._action_add_new_amls(inv_line)
|
||
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': -36000.0, 'currency_id': self.other_currency.id, 'balance': -18000.0},
|
||
{'flag': 'new_aml', 'amount_currency': 36000.0, 'currency_id': self.other_currency.id, 'balance': 12000.0},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'currency_id': self.other_currency.id, 'balance': 6000.0},
|
||
])
|
||
|
||
def test_exchange_diff_on_partial_aml_multi_currency_close_amount(self):
|
||
self.other_currency.rate_ids.rate = 0.9839
|
||
self.company_data['default_journal_bank'].currency_id = self.other_currency
|
||
|
||
st_line = self._create_st_line(-37436.50)
|
||
self.assertRecordValues(st_line.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'amount_currency': -37436.50, 'balance': -38049.09},
|
||
{'amount_currency': 37436.50, 'balance': 38049.09},
|
||
])
|
||
|
||
inv_line = self._create_invoice_line(
|
||
'in_invoice',
|
||
invoice_date=st_line.date,
|
||
currency_id=self.other_currency.id,
|
||
invoice_line_ids=[{'price_unit': 37436.52}],
|
||
)
|
||
self.assertRecordValues(inv_line, [{
|
||
'amount_currency': -37436.52,
|
||
'balance': -38049.11,
|
||
}])
|
||
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
wizard._action_add_new_amls(inv_line)
|
||
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': -37436.50, 'currency_id': self.other_currency.id, 'balance': -38049.09},
|
||
{'flag': 'new_aml', 'amount_currency': 37436.50, 'currency_id': self.other_currency.id, 'balance': 38049.09},
|
||
])
|
||
|
||
def test_matching_zero_amount_misc_entry(self):
|
||
""" Check for division by zero with foreign currencies and some 0 making a broken rate. """
|
||
self.company_data['default_journal_bank'].currency_id = self.other_currency
|
||
st_line = self._create_st_line(0.0, amount_currency=10.0, foreign_currency_id=self.company_data['currency'].id)
|
||
|
||
entry = self.env['account.move'].create({
|
||
'date': '2019-01-01',
|
||
'line_ids': [
|
||
Command.create({
|
||
'account_id': self.company_data['default_account_receivable'].id,
|
||
'currency_id': self.other_currency.id,
|
||
'debit': 1.0,
|
||
'credit': 0.0,
|
||
}),
|
||
Command.create({
|
||
'account_id': self.company_data['default_account_revenue'].id,
|
||
'currency_id': self.other_currency.id,
|
||
'debit': 0.0,
|
||
'credit': 1.0,
|
||
}),
|
||
]
|
||
})
|
||
entry.action_post()
|
||
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
aml = entry.line_ids.filtered('debit')
|
||
wizard._action_add_new_amls(aml)
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'balance': 10.0},
|
||
{'flag': 'new_aml', 'balance': -1.0},
|
||
{'flag': 'exchange_diff', 'balance': 1.0},
|
||
{'flag': 'auto_balance', 'balance': -10.0},
|
||
])
|
||
|
||
def test_amls_order_with_matching_amount(self):
|
||
""" AML's with a matching amount_residual should be displayed first when the order is not specified. """
|
||
|
||
foreign_st_line = self._create_st_line(
|
||
500.0,
|
||
date='2016-01-01',
|
||
foreign_currency_id=self.other_currency.id,
|
||
amount_currency=1500.0,
|
||
)
|
||
st_line = self._create_st_line(
|
||
66.66,
|
||
date='2016-01-01',
|
||
)
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=foreign_st_line.id).new({})
|
||
|
||
aml1_id = self._create_invoice_line(
|
||
'out_invoice',
|
||
invoice_date='2017-01-30',
|
||
invoice_line_ids=[{'price_unit': 1000.0}],
|
||
).id
|
||
aml2_id = self._create_invoice_line(
|
||
'out_invoice',
|
||
invoice_date='2017-01-29',
|
||
currency_id=self.other_currency.id,
|
||
invoice_line_ids=[{'price_unit': 1500.0}], # = 100 USD
|
||
).id
|
||
aml3_id = self._create_invoice_line(
|
||
'out_invoice',
|
||
invoice_date='2017-01-28',
|
||
invoice_line_ids=[{'price_unit': 500.0}],
|
||
).id
|
||
aml4_id = self._create_invoice_line(
|
||
'out_invoice',
|
||
invoice_date='2017-01-27',
|
||
invoice_line_ids=[{'price_unit': 55.55}], # = 55.550000000000004
|
||
).id
|
||
aml5_id = self._create_invoice_line(
|
||
'out_invoice',
|
||
invoice_date='2017-01-26',
|
||
invoice_line_ids=[{'price_unit': 66.66}],
|
||
).id
|
||
|
||
# Check the lines without the context key.
|
||
wizard._js_action_mount_st_line(foreign_st_line.id)
|
||
domain = wizard.return_todo_command['amls']['domain']
|
||
amls_list = self.env['account.move.line'].search_fetch(domain=domain, field_names=['id'])
|
||
self.assertEqual(
|
||
[x['id'] for x in amls_list],
|
||
[aml1_id, aml2_id, aml3_id, aml4_id, aml5_id],
|
||
)
|
||
|
||
# Check the lines with the context key.
|
||
suspense_line = wizard.line_ids.filtered(lambda l: l.flag == 'auto_balance')
|
||
amls_list = self.env['account.move.line']\
|
||
.with_context(preferred_aml_value=suspense_line.amount_currency * -1, preferred_aml_currency_id=suspense_line.currency_id.id)\
|
||
.search_fetch(domain=domain, field_names=['id'])
|
||
self.assertEqual(
|
||
[x['id'] for x in amls_list],
|
||
[aml2_id, aml1_id, aml3_id, aml4_id, aml5_id],
|
||
)
|
||
|
||
# Check the order with limits and offsets
|
||
amls_list = self.env['account.move.line']\
|
||
.with_context(preferred_aml_value=suspense_line.amount_currency * -1, preferred_aml_currency_id=suspense_line.currency_id.id)\
|
||
.search_fetch(domain=domain, field_names=['id'], limit=2)
|
||
self.assertEqual(
|
||
[x['id'] for x in amls_list],
|
||
[aml2_id, aml1_id],
|
||
)
|
||
amls_list = self.env['account.move.line']\
|
||
.with_context(preferred_aml_value=suspense_line.amount_currency * -1, preferred_aml_currency_id=suspense_line.currency_id.id)\
|
||
.search_fetch(domain=domain, field_names=['id'], offset=2, limit=3)
|
||
self.assertEqual(
|
||
[x['id'] for x in amls_list],
|
||
[aml3_id, aml4_id, aml5_id],
|
||
)
|
||
|
||
# Check rounding and new suspense line
|
||
wizard._js_action_mount_st_line(st_line.id)
|
||
suspense_line = wizard.line_ids.filtered(lambda l: l.flag == 'auto_balance')
|
||
amls_list = self.env['account.move.line']\
|
||
.with_context(preferred_aml_value=suspense_line.amount_currency * -1, preferred_aml_currency_id=suspense_line.currency_id.id)\
|
||
.search_fetch(domain=domain, field_names=['id'])
|
||
self.assertEqual(
|
||
[x['id'] for x in amls_list],
|
||
[aml5_id, aml1_id, aml2_id, aml3_id, aml4_id],
|
||
)
|
||
wizard._js_action_mount_line_in_edit(suspense_line.index)
|
||
suspense_line.balance = -11.11
|
||
wizard._line_value_changed_balance(suspense_line)
|
||
suspense_line = wizard.line_ids.filtered(lambda l: l.flag == 'auto_balance')
|
||
self.assertEqual(suspense_line.balance, -55.55)
|
||
self.env.cr.execute(f"""
|
||
UPDATE account_move_line SET amount_residual_currency = 55.550000001 WHERE id = {aml4_id};
|
||
""")
|
||
amls_list = self.env['account.move.line']\
|
||
.with_context(preferred_aml_value=55.550003, preferred_aml_currency_id=suspense_line.currency_id.id)\
|
||
.search_fetch(domain=domain, field_names=['id'])
|
||
self.assertEqual(
|
||
[x['id'] for x in amls_list],
|
||
[aml4_id, aml1_id, aml2_id, aml3_id, aml5_id],
|
||
)
|
||
|
||
# Check that context keys are not propagated
|
||
action = amls_list[0].action_open_business_doc()
|
||
self.assertFalse(action['context'].get('preferred_aml_value'))
|
||
|
||
@freeze_time('2023-12-25')
|
||
def test_analtyic_distribution_model_exchange_diff_line(self):
|
||
"""Test that the analytic distribution model is present on the exchange diff line."""
|
||
expense_exchange_account = self.env.company.expense_currency_exchange_account_id
|
||
analytic_plan = self.env['account.analytic.plan'].create({
|
||
'name': 'Plan 1',
|
||
'default_applicability': 'unavailable',
|
||
})
|
||
analytic_account_1 = self.env['account.analytic.account'].create({'name': 'Account 1', 'plan_id': analytic_plan.id})
|
||
analytic_account_2 = self.env['account.analytic.account'].create({'name': 'Account 1', 'plan_id': analytic_plan.id})
|
||
distribution_model = self.env['account.analytic.distribution.model'].create({
|
||
'account_prefix': expense_exchange_account.code,
|
||
'partner_id': self.partner_a.id,
|
||
'analytic_distribution': {analytic_account_1.id: 100},
|
||
})
|
||
|
||
# 1200.0 comp_curr = 3600.0 foreign_curr in 2016 (rate 1:3)
|
||
st_line = self._create_st_line(
|
||
1200.0,
|
||
date='2016-01-01',
|
||
)
|
||
# 1800.0 comp_curr = 3600.0 foreign_curr in 2017 (rate 1:2)
|
||
inv_line = self._create_invoice_line(
|
||
'out_invoice',
|
||
currency_id=self.other_currency.id,
|
||
invoice_date='2017-01-01',
|
||
invoice_line_ids=[{'price_unit': 3600.0}],
|
||
)
|
||
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
wizard._action_add_new_amls(inv_line)
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'amount_currency': 1200.0, 'currency_id': self.company_data['currency'].id, 'balance': 1200.0, 'analytic_distribution': False},
|
||
{'flag': 'new_aml', 'amount_currency': -3600.0, 'currency_id': self.other_currency.id, 'balance': -1800.0, 'analytic_distribution': False},
|
||
{'flag': 'exchange_diff', 'amount_currency': 0.0, 'currency_id': self.other_currency.id, 'balance': 600.0, 'analytic_distribution': distribution_model.analytic_distribution},
|
||
])
|
||
|
||
# Test that the analytic distribution is kept on the creation of the exchange diff move
|
||
new_distribution = {**distribution_model.analytic_distribution, str(analytic_account_2.id): 100}
|
||
|
||
line = wizard.line_ids.filtered(lambda x: x.flag == 'exchange_diff')
|
||
line.analytic_distribution = new_distribution
|
||
wizard._action_validate()
|
||
|
||
self.assertRecordValues(inv_line.matched_credit_ids.exchange_move_id.line_ids, [
|
||
{'analytic_distribution': False},
|
||
{'analytic_distribution': new_distribution},
|
||
])
|
||
|
||
def test_access_child_bank_with_user_set_on_child(self):
|
||
"""
|
||
Demo user with a Child Company as default company/allowed companies
|
||
should be able to access the Bank set on this same Child Company
|
||
"""
|
||
child_company = self.env['res.company'].create({
|
||
'name': 'Childest Company',
|
||
'parent_id': self.env.company.id,
|
||
})
|
||
child_bank_journal = self.env['account.journal'].create({
|
||
'name': 'Child Bank',
|
||
'type': 'bank',
|
||
'company_id': child_company.id,
|
||
})
|
||
self.user.write({
|
||
'company_ids': [Command.set(child_company.ids)],
|
||
'company_id': child_company.id,
|
||
'groups_id': [
|
||
Command.set(self.env.ref('account.group_account_user').ids),
|
||
]
|
||
})
|
||
res = self.env['bank.rec.widget'].with_user(self.user).collect_global_info_data(child_bank_journal.id)
|
||
self.assertTrue(res, "Journal should be accessible")
|
||
|
||
def test_collect_global_info_data_other_company_bank_journal_with_user_on_main_company(self):
|
||
""" The aim of this test is checking that a user who having
|
||
access to 2 companies will have values even when he's
|
||
calling collect_global_info_data function if
|
||
it's current company it's not the one on the journal
|
||
but is still available.
|
||
To do that, we add 2 companies to the user, and try to
|
||
call collect_global_info_data on the journal of the second
|
||
company, even if the main company it's the first one.
|
||
"""
|
||
self.user.write({
|
||
'company_ids': [Command.set((self.company_data['company'] + self.company_data_2['company']).ids)],
|
||
'company_id': self.company_data['company'].id,
|
||
})
|
||
|
||
result = self.env['bank.rec.widget'].with_user(self.user).collect_global_info_data(self.company_data_2['default_journal_bank'].id)
|
||
self.assertTrue(result['balance_amount'], "Balance amount shouldn't be False value")
|
||
|
||
def test_collect_global_info_data_non_existing_bank_journal(self):
|
||
""" The aim of this test is checking that we receive an empty
|
||
string when we call collect_global_info_data function
|
||
with a non-existing journal. This use case could happen
|
||
when we try to open the bank rec widget on a journal that
|
||
is not actually existing. As this function is callable by
|
||
rpc, this usecase could happen.
|
||
"""
|
||
result = self.env['bank.rec.widget'].with_user(self.user).collect_global_info_data(99999999)
|
||
self.assertEqual(result['balance_amount'], "", "If no value, the function should return an empty string")
|
||
|
||
def test_res_partner_bank_find_create_multi_account(self):
|
||
""" Make sure that we can save multiple bank accounts for a partner. """
|
||
partner = self.env['res.partner'].create({'name': "Zitycard"})
|
||
|
||
for acc_number in ("123456789", "123456780"):
|
||
st_line = self._create_st_line(100.0, account_number=acc_number)
|
||
inv_line = self._create_invoice_line(
|
||
'out_invoice',
|
||
partner_id=partner.id,
|
||
invoice_line_ids=[{'price_unit': 100.0, 'tax_ids': []}],
|
||
)
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
wizard._action_add_new_amls(inv_line)
|
||
wizard._action_validate()
|
||
|
||
bank_accounts = self.env['res.partner.bank'].sudo().with_context(active_test=False).search([
|
||
('partner_id', '=', partner.id),
|
||
])
|
||
self.assertEqual(len(bank_accounts), 2, "Second bank account was not registered!")
|
||
|
||
####################################################
|
||
# RECO MODEL MOVE CREATION
|
||
####################################################
|
||
|
||
def create_test_reco_invoice(self, st_line, reco_model):
|
||
""" Helper method to create a move given a statement line and reconciliation model """
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
wizard._action_select_reconcile_model(reco_model)
|
||
move = self.env['account.move'].browse(wizard.return_todo_command['res_id'])
|
||
return move
|
||
|
||
def assert_reco_invoice_values(self, move, st_line, expected_move_type, expected_amount_total=None):
|
||
""" Helper method to assert that values in a move match the information in the given statement line """
|
||
if expected_amount_total is None:
|
||
expected_amount_total = abs(st_line.amount)
|
||
self.assertRecordValues(move, [{
|
||
'amount_total': expected_amount_total,
|
||
'move_type': expected_move_type,
|
||
'partner_id': st_line.partner_id.id,
|
||
'invoice_date': st_line.date,
|
||
}])
|
||
|
||
def test_invoice_creation_from_reco_model(self):
|
||
""" Test the created invoice from a sale/purchase reconciliation model. """
|
||
reco_model_invoice = self.env['account.reconcile.model'].create({
|
||
'name': "test reconcile create invoice",
|
||
'rule_type': 'writeoff_button',
|
||
'counterpart_type': 'sale',
|
||
'line_ids': [
|
||
Command.create({'amount_string': '50'}),
|
||
Command.create({'amount_string': '50'}),
|
||
],
|
||
})
|
||
|
||
for st_line_amount, move_type, reco_model in (
|
||
(1000.0, 'out_invoice', reco_model_invoice),
|
||
(-1000.0, 'out_refund', reco_model_invoice),
|
||
(1000.0, 'in_refund', self.reco_model_bill),
|
||
(-1000.0, 'in_invoice', self.reco_model_bill),
|
||
):
|
||
st_line = self._create_st_line(st_line_amount, partner_id=self.partner_a.id)
|
||
with self.subTest():
|
||
move = self.create_test_reco_invoice(st_line, reco_model)
|
||
self.assert_reco_invoice_values(move, st_line, move_type)
|
||
|
||
def test_invoice_reco_model_round_odd(self):
|
||
""" Test if correct move is created when rounding is needed for multiple reco model lines"""
|
||
# Odd amount and a reconciliation model with two lines (50% each line) requires rounding to ensure values match.
|
||
st_line = self._create_st_line(amount=-111.11, partner_id=self.partner_a.id)
|
||
move = self.create_test_reco_invoice(st_line, self.reco_model_bill)
|
||
self.assert_reco_invoice_values(move, st_line, 'in_invoice')
|
||
|
||
def test_invoice_reco_model_round_single_line(self):
|
||
""" Test if correct move is created with single-line reco model when rounding is needed """
|
||
# A st_line of $150 with 15% tax (default) requires rounding, as without it the invoice would total $149.99
|
||
reco_model_single_line = self.env['account.reconcile.model'].create({
|
||
'name': "single line reco model",
|
||
'rule_type': 'writeoff_button',
|
||
'counterpart_type': 'purchase',
|
||
'line_ids': [Command.create({})],
|
||
})
|
||
st_line = self._create_st_line(amount=150, partner_id=self.partner_a.id)
|
||
move = self.create_test_reco_invoice(st_line, reco_model_single_line)
|
||
self.assert_reco_invoice_values(move, st_line, 'in_refund')
|
||
|
||
def test_invoice_reco_model_round_large_percentage(self):
|
||
""" Test if total move amount is correctly rounded when reco model lines go above 100% of st_line amount """
|
||
# Reco model with two 100% st_line amount lines
|
||
reco_model_two_lines = self.env['account.reconcile.model'].create({
|
||
'name': "two lines reco model",
|
||
'rule_type': 'writeoff_button',
|
||
'counterpart_type': 'purchase',
|
||
'line_ids': [Command.create({}), Command.create({})],
|
||
})
|
||
# Reco model with one 200% st_line amount line
|
||
reco_model_single_line = self.env['account.reconcile.model'].create({
|
||
'name': "single line reco model",
|
||
'rule_type': 'writeoff_button',
|
||
'counterpart_type': 'purchase',
|
||
'line_ids': [Command.create({'amount_string': '200'})],
|
||
})
|
||
st_line = self._create_st_line(-150, partner_id=self.partner_a.id)
|
||
for reco_model in (reco_model_two_lines, reco_model_single_line):
|
||
with self.subTest():
|
||
move = self.create_test_reco_invoice(st_line, reco_model)
|
||
# Total move amount should equal 2 * st_line amount = 300 in both cases
|
||
self.assert_reco_invoice_values(move, st_line, 'in_invoice', 300.0)
|
||
|
||
def test_invoice_reco_model_round_combined(self):
|
||
""" Test if total move amount is correctly rounded when reco model lines are a combination of
|
||
percentage_st_line and fixed amount types """
|
||
# Reco model with 100% amount + fixed amount, total move amount should equal st_line amount + fixed amount
|
||
reco_model_percentage_fixed = self.env['account.reconcile.model'].create({
|
||
'name': "combined percentage + fixed reco model",
|
||
'rule_type': 'writeoff_button',
|
||
'counterpart_type': 'sale',
|
||
'line_ids': [
|
||
Command.create({}),
|
||
Command.create({
|
||
'amount_type': 'fixed',
|
||
'amount_string': '50',
|
||
}),
|
||
],
|
||
})
|
||
st_line = self._create_st_line(amount=100, partner_id=self.partner_a.id)
|
||
move = self.create_test_reco_invoice(st_line, reco_model_percentage_fixed)
|
||
self.assert_reco_invoice_values(move, st_line, 'out_invoice', 150.0)
|
||
|
||
def test_invoice_reco_model_multiple_taxes(self):
|
||
"""
|
||
Test if correct move is created through reco model writeoff button when the
|
||
reconciliation model has more than one tax per line.
|
||
Note: this only works if taxes are all price_include or all not price_include
|
||
"""
|
||
tax_21 = self.env['account.tax'].create({
|
||
'name': "tax_21",
|
||
'amount': 21,
|
||
})
|
||
reco_model_bill_mult_taxes = self.env['account.reconcile.model'].create({
|
||
'name': "test reconcile bill multiple taxes",
|
||
'rule_type': 'writeoff_button',
|
||
'counterpart_type': 'purchase',
|
||
'line_ids': [
|
||
Command.create({
|
||
'tax_ids': [Command.set((tax_21 + self.company_data['default_tax_purchase']).ids)],
|
||
}),
|
||
],
|
||
})
|
||
st_line = self._create_st_line(amount=-100, partner_id=self.partner_a.id)
|
||
move = self.create_test_reco_invoice(st_line, reco_model_bill_mult_taxes)
|
||
self.assert_reco_invoice_values(move, st_line, 'in_invoice')
|
||
|
||
def test_invoice_creation_reco_model_percentage(self):
|
||
""" Test if correct move is created when rounding is needed """
|
||
# Odd amount and a reconciliation model with two lines (50% each line) requires rounding to ensure values match.
|
||
st_line = self._create_st_line(amount=150, partner_id=self.partner_a.id)
|
||
reco_model_bill_balance = self.env['account.reconcile.model'].create({
|
||
'name': "test balance",
|
||
'rule_type': 'writeoff_button',
|
||
'counterpart_type': 'purchase',
|
||
'line_ids': [
|
||
Command.create({
|
||
'amount_type': 'percentage',
|
||
'amount_string': '50',
|
||
}),
|
||
Command.create({
|
||
'amount_type': 'percentage',
|
||
'amount_string': '50',
|
||
}),
|
||
],
|
||
})
|
||
move = self.create_test_reco_invoice(st_line, reco_model_bill_balance)
|
||
self.assert_reco_invoice_values(move, st_line, 'in_refund', 112.5)
|
||
|
||
def test_invoice_creation_reco_model_fixed(self):
|
||
""" Test if correct move is created when rounding is needed """
|
||
# Odd amount and a reconciliation model with two lines (50% each line) requires rounding to ensure values match.
|
||
st_line = self._create_st_line(amount=-150, partner_id=self.partner_a.id)
|
||
reco_model_invoice_fixed = self.env['account.reconcile.model'].create({
|
||
'name': "test fixed",
|
||
'rule_type': 'writeoff_button',
|
||
'counterpart_type': 'sale',
|
||
'line_ids': [
|
||
Command.create({
|
||
'amount_type': 'fixed',
|
||
'amount_string': '100',
|
||
}),
|
||
],
|
||
})
|
||
move = self.create_test_reco_invoice(st_line, reco_model_invoice_fixed)
|
||
self.assert_reco_invoice_values(move, st_line, 'out_refund', 100.0)
|
||
|
||
def test_invoice_creation_reco_model_regex(self):
|
||
""" Test if correct move is created when rounding is needed """
|
||
# Odd amount and a reconciliation model with two lines (50% each line) requires rounding to ensure values match.
|
||
st_line = self._create_st_line(amount=150, partner_id=self.partner_a.id, payment_ref="150 invoice", statement_name='150 invoice')
|
||
reco_model_invoice_regex = self.env['account.reconcile.model'].create({
|
||
'name': "test label/regex",
|
||
'rule_type': 'writeoff_button',
|
||
'counterpart_type': 'sale',
|
||
'line_ids': [
|
||
Command.create({
|
||
'amount_type': 'regex',
|
||
'amount_string': r'([\d,.]+)',
|
||
}),
|
||
],
|
||
})
|
||
move = self.create_test_reco_invoice(st_line, reco_model_invoice_regex)
|
||
self.assert_reco_invoice_values(move, st_line, 'out_invoice', 150.0)
|
||
|
||
def test_unreconciled_with_other_lines(self):
|
||
"""Test that other lines are shown in the widget if they exist."""
|
||
st_line = self._create_st_line(
|
||
1000.0,
|
||
date='2017-01-01',
|
||
)
|
||
|
||
# Edit the associated move to partially reconcile some of the suspense amount; i.e. we add another line
|
||
liquidity_line, suspense_line, other_line = st_line._seek_for_lines()
|
||
other_account = st_line.journal_id.company_id.default_cash_difference_income_account_id
|
||
self.assertFalse(other_line)
|
||
move = st_line.move_id
|
||
move.button_draft()
|
||
move.write({'line_ids': [
|
||
Command.create({'account_id': other_account.id, 'credit': 100.0}),
|
||
Command.update(suspense_line.id, {'credit': 900.0}),
|
||
]})
|
||
move.action_post()
|
||
liquidity_line, suspense_line, other_line = st_line._seek_for_lines()
|
||
self.assertTrue(other_line)
|
||
|
||
# Check that the wizard displays the new line
|
||
wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=st_line.id).new({})
|
||
self.assertRecordValues(wizard.line_ids, [
|
||
# pylint: disable=C0326
|
||
{'flag': 'liquidity', 'account_id': liquidity_line.account_id.id, 'amount_currency': 1000.0},
|
||
{'flag': 'aml', 'account_id': other_line.account_id.id, 'amount_currency': -100.0},
|
||
{'flag': 'auto_balance', 'account_id': suspense_line.account_id.id, 'amount_currency': -900.0},
|
||
])
|