Files
odoo_source/addons/account/tests/test_payment.py
T
qdp-odoo d8137d35b7 [FIX] [tldr] account: multi-currencies fixes.
[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
2016-03-23 09:57:59 +01:00

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},
])