[FIX] account: fixed behavior in order to create the exchange rate difference entry at the full reconciliation only. This is needed in order to support the use case where an invoice in foreign currency is paid on a bank account in company currency, with the user encoding manually the difference due to the currency fluctuation (writeoff in the bank statement reconciliation interface): before this patch, Odoo was doing automatically the same, what results in a duplicated currency difference booking). ticket ID: 670876. A test has also been added in account/tests/test_reconcilication.py (test_statement_euro_invoice_usd_transaction_euro_full) to ensure that use case is now fixed. [FIX] account_full_reconcile: a reconciliation is full as soon as either total_debit = total_credit OR if total_amount_currency is 0 [FIX] account: when registering a payment with a writeoff, always create the writeoff line exactly as entered by the user. Before, it was modified by the exchange rate entry, for some reason... [FIX] account: on invoices, show the payment widget and outstanding payments in the invoice's currency always [FIX] account: when assigning a payment in company currency on an invoice in foreign currency, save the amount_currency in order to create the correct exchange rate. [FIX] account: show the currency difference entry on the invoice payments widget [FIX] account: fixed the reconciliation methods in order to work with exchange rate entries that have debit = credit = 0 [FIX] account: fixed reconciled field computation. A line is reconciled only if its amount_residual AND amount_residual_currency are 0 [IMP] display the move name of payments on the invoice payments widget
182 lines
10 KiB
Python
182 lines
10 KiB
Python
from openerp.addons.account.tests.account_test_classes import AccountingTestCase
|
|
import time
|
|
|
|
class TestPayment(AccountingTestCase):
|
|
|
|
def setUp(self):
|
|
super(TestPayment, self).setUp()
|
|
self.register_payments_model = self.env['account.register.payments']
|
|
self.payment_model = self.env['account.payment']
|
|
self.invoice_model = self.env['account.invoice']
|
|
self.invoice_line_model = self.env['account.invoice.line']
|
|
self.acc_bank_stmt_model = self.env['account.bank.statement']
|
|
self.acc_bank_stmt_line_model = self.env['account.bank.statement.line']
|
|
|
|
self.partner_agrolait = self.env.ref("base.res_partner_2")
|
|
self.partner_axelor = self.env.ref("base.res_partner_2")
|
|
self.currency_chf_id = self.env.ref("base.CHF").id
|
|
self.currency_usd_id = self.env.ref("base.USD").id
|
|
self.currency_eur_id = self.env.ref("base.EUR").id
|
|
self.env.ref('base.main_company').write({'currency_id': self.currency_eur_id})
|
|
self.product = self.env.ref("product.product_product_4")
|
|
self.payment_method_manual_in = self.env.ref("account.account_payment_method_manual_in")
|
|
self.payment_method_manual_out = self.env.ref("account.account_payment_method_manual_out")
|
|
|
|
self.account_receivable = self.env['account.account'].search([('user_type_id', '=', self.env.ref('account.data_account_type_receivable').id)], limit=1)
|
|
self.account_payable = self.env['account.account'].search([('user_type_id', '=', self.env.ref('account.data_account_type_payable').id)], limit=1)
|
|
self.account_revenue = self.env['account.account'].search([('user_type_id', '=', self.env.ref('account.data_account_type_revenue').id)], limit=1)
|
|
|
|
self.bank_journal_euro = self.env['account.journal'].create({'name': 'Bank', 'type': 'bank', 'code': 'BNK67'})
|
|
self.account_eur = self.bank_journal_euro.default_debit_account_id
|
|
|
|
self.bank_journal_usd = self.env['account.journal'].create({'name': 'Bank US', 'type': 'bank', 'code': 'BNK68', 'currency_id': self.currency_usd_id})
|
|
self.account_usd = self.bank_journal_usd.default_debit_account_id
|
|
|
|
self.transfer_account = self.env['res.users'].browse(self.env.uid).company_id.transfer_account_id
|
|
self.diff_income_account = self.env['res.users'].browse(self.env.uid).company_id.income_currency_exchange_account_id
|
|
self.diff_expense_account = self.env['res.users'].browse(self.env.uid).company_id.expense_currency_exchange_account_id
|
|
|
|
def create_invoice(self, amount=100, type='out_invoice', currency_id=None):
|
|
""" Returns an open invoice """
|
|
invoice = self.invoice_model.create({
|
|
'partner_id': self.partner_agrolait.id,
|
|
'reference_type': 'none',
|
|
'currency_id': currency_id,
|
|
'name': type == 'out_invoice' and 'invoice to client' or 'invoice to supplier',
|
|
'account_id': self.account_receivable.id,
|
|
'type': type,
|
|
'date_invoice': time.strftime('%Y') + '-06-26',
|
|
})
|
|
self.invoice_line_model.create({
|
|
'product_id': self.product.id,
|
|
'quantity': 1,
|
|
'price_unit': amount,
|
|
'invoice_id': invoice.id,
|
|
'name': 'something',
|
|
'account_id': self.account_revenue.id,
|
|
})
|
|
invoice.signal_workflow('invoice_open')
|
|
return invoice
|
|
|
|
def reconcile(self, liquidity_aml, amount=0.0, amount_currency=0.0, currency_id=None):
|
|
""" Reconcile a journal entry corresponding to a payment with its bank statement line """
|
|
bank_stmt = self.acc_bank_stmt_model.create({
|
|
'journal_id': liquidity_aml.journal_id.id,
|
|
'date': time.strftime('%Y') + '-07-15',
|
|
})
|
|
bank_stmt_line = self.acc_bank_stmt_line_model.create({
|
|
'name': 'payment',
|
|
'statement_id': bank_stmt.id,
|
|
'partner_id': self.partner_agrolait.id,
|
|
'amount': amount,
|
|
'amount_currency': amount_currency,
|
|
'currency_id': currency_id,
|
|
'date': time.strftime('%Y') + '-07-15'
|
|
})
|
|
|
|
amount_in_widget = currency_id and amount_currency or amount
|
|
bank_stmt_line.process_reconciliation(payment_aml_rec=liquidity_aml)
|
|
return bank_stmt
|
|
|
|
def check_journal_items(self, aml_recs, aml_dicts):
|
|
def compare_rec_dict(aml_rec, aml_dict):
|
|
return aml_rec.account_id.id == aml_dict['account_id'] \
|
|
and round(aml_rec.debit, 2) == aml_dict['debit'] \
|
|
and round(aml_rec.credit, 2) == aml_dict['credit'] \
|
|
and round(aml_rec.amount_currency, 2) == aml_dict['amount_currency'] \
|
|
and aml_rec.currency_id.id == aml_dict['currency_id']
|
|
|
|
for aml_dict in aml_dicts:
|
|
# There is no unique key to identify journal items (an account_payment may create several lines
|
|
# in the same account), so to check the expected entries are created, we check there is a line
|
|
# matching for each dict of expected values
|
|
aml_rec = aml_recs.filtered(lambda r: compare_rec_dict(r, aml_dict))
|
|
self.assertEqual(len(aml_rec), 1, "Expected a move line with values : %s" % str(aml_dict))
|
|
if aml_dict.get('currency_diff'):
|
|
if aml_rec.credit:
|
|
currency_diff_move = aml_rec.matched_debit_ids.full_reconcile_id.exchange_move_id
|
|
else:
|
|
currency_diff_move = aml_rec.matched_credit_ids.full_reconcile_id.exchange_move_id
|
|
for currency_diff_line in currency_diff_move.line_ids:
|
|
if aml_dict.get('currency_diff') > 0:
|
|
if currency_diff_line.account_id.id == aml_rec.account_id.id:
|
|
self.assertAlmostEquals(currency_diff_line.debit, aml_dict.get('currency_diff'))
|
|
else:
|
|
self.assertAlmostEquals(currency_diff_line.credit, aml_dict.get('currency_diff'))
|
|
self.assertIn(currency_diff_line.account_id.id, [self.diff_expense_account.id, self.diff_income_account.id])
|
|
else:
|
|
if currency_diff_line.account_id.id == aml_rec.account_id.id:
|
|
self.assertAlmostEquals(currency_diff_line.credit, abs(aml_dict.get('currency_diff')))
|
|
else:
|
|
self.assertAlmostEquals(currency_diff_line.debit, abs(aml_dict.get('currency_diff')))
|
|
self.assertIn(currency_diff_line.account_id.id, [self.diff_expense_account.id, self.diff_income_account.id])
|
|
|
|
def test_full_payment_process(self):
|
|
""" Create a payment for two invoices, post it and reconcile it with a bank statement """
|
|
inv_1 = self.create_invoice(amount=100, currency_id=self.currency_eur_id)
|
|
inv_2 = self.create_invoice(amount=200, currency_id=self.currency_eur_id)
|
|
|
|
ctx = { 'active_model': 'account.invoice', 'active_ids': [inv_1.id, inv_2.id] }
|
|
register_payments = self.register_payments_model.with_context(ctx).create({
|
|
'payment_date': time.strftime('%Y') + '-07-15',
|
|
'journal_id': self.bank_journal_euro.id,
|
|
'payment_method_id': self.payment_method_manual_in.id,
|
|
})
|
|
register_payments.create_payment()
|
|
payment = self.payment_model.search([], order="id desc", limit=1)
|
|
|
|
self.assertAlmostEquals(payment.amount, 300)
|
|
self.assertEqual(payment.state, 'posted')
|
|
self.assertEqual(inv_1.state, 'paid')
|
|
self.assertEqual(inv_2.state, 'paid')
|
|
|
|
self.check_journal_items(payment.move_line_ids, [
|
|
{'account_id': self.account_eur.id, 'debit': 300.0, 'credit': 0.0, 'amount_currency': 0, 'currency_id': False},
|
|
{'account_id': inv_1.account_id.id, 'debit': 0.0, 'credit': 300.0, 'amount_currency': 00, 'currency_id': False},
|
|
])
|
|
|
|
liquidity_aml = payment.move_line_ids.filtered(lambda r: r.account_id == self.account_eur)
|
|
bank_statement = self.reconcile(liquidity_aml, 200, 0, False)
|
|
|
|
self.assertEqual(liquidity_aml.statement_id, bank_statement)
|
|
self.assertEqual(liquidity_aml.move_id.statement_line_id, bank_statement.line_ids[0])
|
|
|
|
self.assertEqual(payment.state, 'reconciled')
|
|
|
|
def test_internal_transfer_journal_usd_journal_eur(self):
|
|
""" Create a transfer from a EUR journal to a USD journal """
|
|
payment = self.payment_model.create({
|
|
'payment_date': time.strftime('%Y') + '-07-15',
|
|
'payment_type': 'transfer',
|
|
'amount': 50,
|
|
'currency_id': self.currency_usd_id,
|
|
'journal_id': self.bank_journal_usd.id,
|
|
'destination_journal_id': self.bank_journal_euro.id,
|
|
'payment_method_id': self.payment_method_manual_out.id,
|
|
})
|
|
payment.post()
|
|
self.check_journal_items(payment.move_line_ids, [
|
|
{'account_id': self.transfer_account.id, 'debit': 32.70, 'credit': 0.0, 'amount_currency': 50, 'currency_id': self.currency_usd_id},
|
|
{'account_id': self.transfer_account.id, 'debit': 0.0, 'credit': 32.70, 'amount_currency': -50, 'currency_id': self.currency_usd_id},
|
|
{'account_id': self.account_eur.id, 'debit': 32.70, 'credit': 0.0, 'amount_currency': 0, 'currency_id': False},
|
|
{'account_id': self.account_usd.id, 'debit': 0.0, 'credit': 32.70, 'amount_currency': -50, 'currency_id': self.currency_usd_id},
|
|
])
|
|
|
|
def test_payment_chf_journal_usd(self):
|
|
payment = self.payment_model.create({
|
|
'payment_date': time.strftime('%Y') + '-07-15',
|
|
'payment_type': 'outbound',
|
|
'amount': 50,
|
|
'currency_id': self.currency_chf_id,
|
|
'journal_id': self.bank_journal_usd.id,
|
|
'partner_type': 'supplier',
|
|
'partner_id': self.partner_axelor.id,
|
|
'payment_method_id': self.payment_method_manual_out.id,
|
|
})
|
|
payment.post()
|
|
|
|
self.check_journal_items(payment.move_line_ids, [
|
|
{'account_id': self.account_usd.id, 'debit': 0.0, 'credit': 38.21, 'amount_currency': -58.42, 'currency_id': self.currency_usd_id},
|
|
{'account_id': self.partner_axelor.property_account_payable_id.id, 'debit': 38.21, 'credit': 0.0, 'amount_currency': 50, 'currency_id': self.currency_chf_id},
|
|
])
|