← Back to team overview

banking-addons-team team mailing list archive

[Merge] lp:~therp-nl/banking-addons/ba70-add_tests into lp:banking-addons

 

Stefan Rijnhart (Therp) has proposed merging lp:~therp-nl/banking-addons/ba70-add_tests into lp:banking-addons.

Requested reviews:
  Banking Addons Core Editors (banking-addons-team)

For more details, see:
https://code.launchpad.net/~therp-nl/banking-addons/ba70-add_tests/+merge/187780

This is my attempt at adding unit test for a payment/reconciliation roundtrip. I don't think I could have put it in any of the existing modules, because soon the payment export, workflow integration and reconciliation functionalities will no longer be necessary dependencies. Therefore, I put the test in its own module that depends on all needed functionality.


-- 
https://code.launchpad.net/~therp-nl/banking-addons/ba70-add_tests/+merge/187780
Your team Banking Addons Core Editors is requested to review the proposed merge of lp:~therp-nl/banking-addons/ba70-add_tests into lp:banking-addons.
=== added directory 'account_banking_tests'
=== added file 'account_banking_tests/__init__.py'
=== added file 'account_banking_tests/__openerp__.py'
--- account_banking_tests/__openerp__.py	1970-01-01 00:00:00 +0000
+++ account_banking_tests/__openerp__.py	2013-09-26 13:31:18 +0000
@@ -0,0 +1,42 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    Copyright (C) 2013 Therp BV (<http://therp.nl>)
+#
+#    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 <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+{
+    '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. Installing this
+module will not give you any benefit other than having the tests'
+dependencies installed, so that you can run the tests. If you only
+run the tests manually, you don't even have to install this module,
+only its dependencies.
+    ''',
+    'auto_install': False,
+    'installable': True,
+}

=== added directory 'account_banking_tests/tests'
=== added file 'account_banking_tests/tests/__init__.py'
--- account_banking_tests/tests/__init__.py	1970-01-01 00:00:00 +0000
+++ account_banking_tests/tests/__init__.py	2013-09-26 13:31:18 +0000
@@ -0,0 +1,5 @@
+import test_payment_roundtrip
+
+fast_suite = [
+    test_payment_roundtrip,
+    ]

=== added file 'account_banking_tests/tests/test_payment_roundtrip.py'
--- account_banking_tests/tests/test_payment_roundtrip.py	1970-01-01 00:00:00 +0000
+++ account_banking_tests/tests/test_payment_roundtrip.py	2013-09-26 13:31:18 +0000
@@ -0,0 +1,331 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    Copyright (C) 2013 Therp BV (<http://therp.nl>)
+#            
+#    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 <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+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)


Follow ups