diff --git a/account_banking_tests/__init__.py b/account_banking_tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/account_banking_tests/__openerp__.py b/account_banking_tests/__openerp__.py new file mode 100644 index 000000000..df329766d --- /dev/null +++ b/account_banking_tests/__openerp__.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (C) 2013 Therp BV () +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +{ + 'name': 'Banking Addons - Tests', + 'version': '0.1', + 'license': 'AGPL-3', + 'author': 'Therp BV', + 'website': 'https://launchpad.net/banking-addons', + 'category': 'Banking addons', + 'depends': [ + 'account_accountant', + 'account_banking', + 'account_banking_sepa_credit_transfer', + ], + 'description': ''' + This addon adds unit tests for the Banking addons + ''', + 'auto_install': False, + 'installable': True, +} diff --git a/account_banking_tests/tests/__init__.py b/account_banking_tests/tests/__init__.py new file mode 100644 index 000000000..991b780d8 --- /dev/null +++ b/account_banking_tests/tests/__init__.py @@ -0,0 +1,5 @@ +import test_payment_roundtrip + +fast_suite = [ + test_payment_roundtrip, + ] diff --git a/account_banking_tests/tests/test_payment_roundtrip.py b/account_banking_tests/tests/test_payment_roundtrip.py new file mode 100644 index 000000000..05c703a92 --- /dev/null +++ b/account_banking_tests/tests/test_payment_roundtrip.py @@ -0,0 +1,331 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (C) 2013 Therp BV () +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from datetime import datetime +from openerp.tests.common import SingleTransactionCase +from openerp import netsvc + + +class TestPaymentRoundtrip(SingleTransactionCase): + + def assert_payment_order_state(self, expected): + """ + Check that the state of our payment order is + equal to the 'expected' parameter + """ + state = self.registry('payment.order').read( + self.cr, self.uid, self.payment_order_id, ['state'])['state'] + assert state == expected, \ + 'Payment order does not go into state \'%s\'.' % expected + + def assert_invoices_state(self, expected): + """ + Check that the state of our invoices is + equal to the 'expected' parameter + """ + for invoice in self.registry('account.invoice').read( + self.cr, self.uid, self.invoice_ids, ['state']): + assert invoice['state'] == expected, \ + 'Invoice does not go into state \'%s\'' % expected + + def setup_company(self, reg, cr, uid): + """ + Set up a company with a bank account and configure the + current user to work with that company + """ + data_model = reg('ir.model.data') + self.country_id = data_model.get_object_reference( + cr, uid, 'base', 'nl')[1] + self.currency_id = data_model.get_object_reference( + cr, uid, 'base', 'EUR')[1] + self.bank_id = reg('res.bank').create( + cr, uid, { + 'name': 'ING Bank', + 'bic': 'INGBNL2A', + 'country': self.country_id, + }) + self.company_id = reg('res.company').create( + cr, uid, { + 'name': '_banking_addons_test_company', + 'currency_id': self.currency_id, + 'country_id': self.country_id, + }) + self.partner_id = reg('res.company').read( + cr, uid, self.company_id, ['partner_id'])['partner_id'][0] + self.partner_bank_id = reg('res.partner.bank').create( + cr, uid, { + 'state': 'iban', + 'acc_number': 'NL08INGB0000000555', + 'bank': self.bank_id, + 'bank_bic': 'INGBNL2A', + 'partner_id': self.partner_id, + 'company_id': self.company_id, + }) + reg('res.users').write( + cr, uid, [uid], { + 'company_ids': [(4, self.company_id)]}) + reg('res.users').write( + cr, uid, [uid], { + 'company_id': self.company_id}) + + def setup_chart(self, reg, cr, uid): + """ + Set up the configurable chart of accounts and create periods + """ + data_model = reg('ir.model.data') + chart_setup_model = reg('wizard.multi.charts.accounts') + chart_template_id = data_model.get_object_reference( + cr, uid, 'account', 'configurable_chart_template')[1] + chart_values = { + 'company_id': self.company_id, + 'currency_id': self.currency_id, + 'chart_template_id': chart_template_id} + chart_values.update( + chart_setup_model.onchange_chart_template_id( + cr, uid, [], 1)['value']) + chart_setup_id = chart_setup_model.create( + cr, uid, chart_values) + chart_setup_model.read(cr, uid, chart_setup_id) + chart_setup_model.execute( + cr, uid, [chart_setup_id]) + year = datetime.now().strftime('%Y') + fiscalyear_id = reg('account.fiscalyear').create( + cr, uid, { + 'name': year, + 'code': year, + 'company_id': self.company_id, + 'date_start': '%s-01-01' % year, + 'date_stop': '%s-12-31' % year, + }) + reg('account.fiscalyear').create_period( + cr, uid, [fiscalyear_id]) + + def setup_payables(self, reg, cr, uid): + """ + Set up suppliers and invoice them. Check that the invoices + can be validated properly. + """ + partner_model = reg('res.partner') + supplier1 = partner_model.create( + cr, uid, { + 'name': 'Supplier 1', + 'supplier': True, + 'country_id': self.country_id, + 'bank_ids': [(0, False, { + 'state': 'iban', + 'acc_number': 'NL42INGB0000454000', + 'bank': self.bank_id, + 'bank_bic': 'INGBNL2A', + })], + }) + supplier2 = partner_model.create( + cr, uid, { + 'name': 'Supplier 2', + 'supplier': True, + 'country_id': self.country_id, + 'bank_ids': [(0, False, { + 'state': 'iban', + 'acc_number': 'NL86INGB0002445588', + 'bank': self.bank_id, + 'bank_bic': 'INGBNL2A', + })], + }) + self.payable_id = reg('account.account').search( + cr, uid, [ + ('company_id', '=', self.company_id), + ('code', '=', '120000')])[0] + expense_id = reg('account.account').search( + cr, uid, [ + ('company_id', '=', self.company_id), + ('code', '=', '123000')])[0] + invoice_model = reg('account.invoice') + values = { + 'type': 'in_invoice', + 'partner_id': supplier1, + 'account_id': self.payable_id, + 'invoice_line': [(0, False, { + 'name': 'Purchase 1', + 'price_unit': 100.0, + 'quantity': 1, + 'account_id': expense_id,})], + } + self.invoice_ids = [ + invoice_model.create( + cr, uid, values, context={ + 'type': 'in_invoice', + })] + values.update({ + 'partner_id': supplier2, + 'name': 'Purchase 2'}) + self.invoice_ids.append( + invoice_model.create( + cr, uid, values, context={ + 'type': 'in_invoice'})) + wf_service = netsvc.LocalService('workflow') + for invoice_id in self.invoice_ids: + wf_service.trg_validate( + uid, 'account.invoice', invoice_id, 'invoice_open', cr) + self.assert_invoices_state('open') + + def setup_payment_config(self, reg, cr, uid): + """ + Configure an additional account and journal for payments + in transit and configure a payment mode with them. + """ + account_parent_id = reg('account.account').search( + cr, uid, [ + ('company_id', '=', self.company_id), + ('parent_id', '=', False)])[0] + user_type = reg('ir.model.data').get_object_reference( + cr, uid, 'account', 'data_account_type_liability')[1] + transfer_account_id = reg('account.account').create( + cr, uid, { + 'company_id': self.company_id, + 'parent_id': account_parent_id, + 'code': 'TRANS', + 'name': 'Transfer account', + 'type': 'other', + 'user_type': user_type, + 'reconcile': True, + }) + transfer_journal_id = reg('account.journal').search( + cr, uid, [ + ('company_id', '=', self.company_id), + ('code', '=', 'MISC')])[0] + self.bank_journal_id = reg('account.journal').search( + cr, uid, [ + ('company_id', '=', self.company_id), + ('type', '=', 'bank')])[0] + payment_mode_type_id = reg('ir.model.data').get_object_reference( + cr, uid, 'account_banking_sepa_credit_transfer', + 'export_sepa_sct_001_001_03')[1] + self.payment_mode_id = reg('payment.mode').create( + cr, uid, { + 'name': 'SEPA Mode', + 'bank_id': self.partner_bank_id, + 'journal': self.bank_journal_id, + 'company_id': self.company_id, + 'transfer_account_id': transfer_account_id, + 'transfer_journal_id': transfer_journal_id, + 'type': payment_mode_type_id, + }) + + def setup_payment(self, reg, cr, uid): + """ + Create a payment order with the invoices' payable move lines. + Check that the payment order can be confirmed. + """ + self.payment_order_id = reg('payment.order').create( + cr, uid, { + 'reference': 'PAY001', + 'mode': self.payment_mode_id, + }) + context = {'active_id': self.payment_order_id} + entries = reg('account.move.line').search( + cr, uid, [ + ('company_id', '=', self.company_id), + ('account_id', '=', self.payable_id), + ]) + self.payment_order_create_id = reg('payment.order.create').create( + cr, uid, { + 'entries': [(6, 0, entries)], + }, context=context) + reg('payment.order.create').create_payment( + cr, uid, [self.payment_order_create_id], context=context) + wf_service = netsvc.LocalService('workflow') + wf_service.trg_validate( + uid, 'payment.order', self.payment_order_id, 'open', cr) + self.assert_payment_order_state('open') + + def export_payment(self, reg, cr, uid): + """ + Call the SEPA export wizard on the payment order + and check that the payment order and related invoices' + states are moved forward afterwards + """ + export_model = reg('banking.export.sepa.wizard') + export_id = export_model.create( + cr, uid, { + 'msg_identification': 'EXP001'}, + context={'active_ids': [self.payment_order_id]}) + export_model.create_sepa( + cr, uid, [export_id]) + export_model.save_sepa( + cr, uid, [export_id]) + self.assert_payment_order_state('sent') + self.assert_invoices_state('paid') + + def setup_bank_statement(self, reg, cr, uid): + """ + Create a bank statement with a single line. Call the reconciliation + wizard to match the line with the open payment order. Confirm the + bank statement. Check if the payment order is done. + """ + statement_model = reg('account.bank.statement') + line_model = reg('account.bank.statement.line') + wizard_model = reg('banking.transaction.wizard') + statement_id = statement_model.create( + cr, uid, { + 'name': 'Statement', + 'journal_id': self.bank_journal_id, + 'balance_end_real': -200.0, + 'period_id': reg('account.period').find(cr, uid)[0] + }) + line_id = line_model.create( + cr, uid, { + 'name': 'Statement line', + 'statement_id': statement_id, + 'amount': -200.0, + 'account_id': self.payable_id, + }) + wizard_id = wizard_model.create( + cr, uid, {'statement_line_id': line_id}) + wizard_model.write( + cr, uid, [wizard_id], { + 'manual_payment_order_id': self.payment_order_id}) + statement_model.button_confirm_bank(cr, uid, [statement_id]) + state = reg('payment.order').read( + cr, uid, self.payment_order_id, ['state'])['state'] + assert state == 'done', 'Payment order does not go into state \'done\'.' + + def check_reconciliations(self, reg, cr, uid): + """ + Check if the payment order has any lines and that + the transit move lines of those payment lines are + reconciled by now. + """ + payment_order = reg('payment.order').browse( + cr, uid, self.payment_order_id) + assert payment_order.line_ids, 'Payment order has no payment lines' + for line in payment_order.line_ids: + assert line.transit_move_line_id, \ + 'Payment order has no transfer move line' + assert line.transit_move_line_id.reconcile_id, \ + 'Transfer move line on payment line is not reconciled' + + def test_payment_roundtrip(self): + reg, cr, uid, = self.registry, self.cr, self.uid + self.setup_company(reg, cr, uid) + self.setup_chart(reg, cr, uid) + self.setup_payables(reg, cr, uid) + self.setup_payment_config(reg, cr, uid) + self.setup_payment(reg, cr, uid) + self.export_payment(reg, cr, uid) + self.setup_bank_statement(reg, cr, uid) + self.check_reconciliations(reg, cr, uid)