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

Proposed by Stefan Rijnhart (Opener)
Status: Merged
Merged at revision: 201
Proposed branch: lp:~therp-nl/banking-addons/ba70-add_tests
Merge into: lp:banking-addons
Diff against target: 392 lines (+375/-0)
3 files modified
account_banking_tests/__openerp__.py (+42/-0)
account_banking_tests/tests/__init__.py (+5/-0)
account_banking_tests/tests/test_payment_roundtrip.py (+328/-0)
To merge this branch: bzr merge lp:~therp-nl/banking-addons/ba70-add_tests
Reviewer Review Type Date Requested Status
Holger Brunn (Therp) code review Approve
Review via email: mp+187780@code.launchpad.net

Description of the change

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.

To post a comment you must log in.
190. By Stefan Rijnhart (Opener)

[FIX] Additional substitution of out-refactored assertion check
[FIX] Remove debug statement

Revision history for this message
Holger Brunn (Therp) (hbrunn) wrote :

Yes, adding tests is a very good idea

review: Approve (code review)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'account_banking_tests'
2=== added file 'account_banking_tests/__init__.py'
3=== added file 'account_banking_tests/__openerp__.py'
4--- account_banking_tests/__openerp__.py 1970-01-01 00:00:00 +0000
5+++ account_banking_tests/__openerp__.py 2013-09-26 17:55:39 +0000
6@@ -0,0 +1,42 @@
7+# -*- coding: utf-8 -*-
8+##############################################################################
9+#
10+# Copyright (C) 2013 Therp BV (<http://therp.nl>)
11+#
12+# This program is free software: you can redistribute it and/or modify
13+# it under the terms of the GNU Affero General Public License as
14+# published by the Free Software Foundation, either version 3 of the
15+# License, or (at your option) any later version.
16+#
17+# This program is distributed in the hope that it will be useful,
18+# but WITHOUT ANY WARRANTY; without even the implied warranty of
19+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20+# GNU Affero General Public License for more details.
21+#
22+# You should have received a copy of the GNU Affero General Public License
23+# along with this program. If not, see <http://www.gnu.org/licenses/>.
24+#
25+##############################################################################
26+
27+{
28+ 'name': 'Banking Addons - Tests',
29+ 'version': '0.1',
30+ 'license': 'AGPL-3',
31+ 'author': 'Therp BV',
32+ 'website': 'https://launchpad.net/banking-addons',
33+ 'category': 'Banking addons',
34+ 'depends': [
35+ 'account_accountant',
36+ 'account_banking',
37+ 'account_banking_sepa_credit_transfer',
38+ ],
39+ 'description': '''
40+This addon adds unit tests for the Banking addons. Installing this
41+module will not give you any benefit other than having the tests'
42+dependencies installed, so that you can run the tests. If you only
43+run the tests manually, you don't even have to install this module,
44+only its dependencies.
45+ ''',
46+ 'auto_install': False,
47+ 'installable': True,
48+}
49
50=== added directory 'account_banking_tests/tests'
51=== added file 'account_banking_tests/tests/__init__.py'
52--- account_banking_tests/tests/__init__.py 1970-01-01 00:00:00 +0000
53+++ account_banking_tests/tests/__init__.py 2013-09-26 17:55:39 +0000
54@@ -0,0 +1,5 @@
55+import test_payment_roundtrip
56+
57+fast_suite = [
58+ test_payment_roundtrip,
59+ ]
60
61=== added file 'account_banking_tests/tests/test_payment_roundtrip.py'
62--- account_banking_tests/tests/test_payment_roundtrip.py 1970-01-01 00:00:00 +0000
63+++ account_banking_tests/tests/test_payment_roundtrip.py 2013-09-26 17:55:39 +0000
64@@ -0,0 +1,328 @@
65+# -*- coding: utf-8 -*-
66+##############################################################################
67+#
68+# Copyright (C) 2013 Therp BV (<http://therp.nl>)
69+#
70+# This program is free software: you can redistribute it and/or modify
71+# it under the terms of the GNU Affero General Public License as
72+# published by the Free Software Foundation, either version 3 of the
73+# License, or (at your option) any later version.
74+#
75+# This program is distributed in the hope that it will be useful,
76+# but WITHOUT ANY WARRANTY; without even the implied warranty of
77+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
78+# GNU Affero General Public License for more details.
79+#
80+# You should have received a copy of the GNU Affero General Public License
81+# along with this program. If not, see <http://www.gnu.org/licenses/>.
82+#
83+##############################################################################
84+from datetime import datetime
85+from openerp.tests.common import SingleTransactionCase
86+from openerp import netsvc
87+
88+
89+class TestPaymentRoundtrip(SingleTransactionCase):
90+
91+ def assert_payment_order_state(self, expected):
92+ """
93+ Check that the state of our payment order is
94+ equal to the 'expected' parameter
95+ """
96+ state = self.registry('payment.order').read(
97+ self.cr, self.uid, self.payment_order_id, ['state'])['state']
98+ assert state == expected, \
99+ 'Payment order does not go into state \'%s\'.' % expected
100+
101+ def assert_invoices_state(self, expected):
102+ """
103+ Check that the state of our invoices is
104+ equal to the 'expected' parameter
105+ """
106+ for invoice in self.registry('account.invoice').read(
107+ self.cr, self.uid, self.invoice_ids, ['state']):
108+ assert invoice['state'] == expected, \
109+ 'Invoice does not go into state \'%s\'' % expected
110+
111+ def setup_company(self, reg, cr, uid):
112+ """
113+ Set up a company with a bank account and configure the
114+ current user to work with that company
115+ """
116+ data_model = reg('ir.model.data')
117+ self.country_id = data_model.get_object_reference(
118+ cr, uid, 'base', 'nl')[1]
119+ self.currency_id = data_model.get_object_reference(
120+ cr, uid, 'base', 'EUR')[1]
121+ self.bank_id = reg('res.bank').create(
122+ cr, uid, {
123+ 'name': 'ING Bank',
124+ 'bic': 'INGBNL2A',
125+ 'country': self.country_id,
126+ })
127+ self.company_id = reg('res.company').create(
128+ cr, uid, {
129+ 'name': '_banking_addons_test_company',
130+ 'currency_id': self.currency_id,
131+ 'country_id': self.country_id,
132+ })
133+ self.partner_id = reg('res.company').read(
134+ cr, uid, self.company_id, ['partner_id'])['partner_id'][0]
135+ self.partner_bank_id = reg('res.partner.bank').create(
136+ cr, uid, {
137+ 'state': 'iban',
138+ 'acc_number': 'NL08INGB0000000555',
139+ 'bank': self.bank_id,
140+ 'bank_bic': 'INGBNL2A',
141+ 'partner_id': self.partner_id,
142+ 'company_id': self.company_id,
143+ })
144+ reg('res.users').write(
145+ cr, uid, [uid], {
146+ 'company_ids': [(4, self.company_id)]})
147+ reg('res.users').write(
148+ cr, uid, [uid], {
149+ 'company_id': self.company_id})
150+
151+ def setup_chart(self, reg, cr, uid):
152+ """
153+ Set up the configurable chart of accounts and create periods
154+ """
155+ data_model = reg('ir.model.data')
156+ chart_setup_model = reg('wizard.multi.charts.accounts')
157+ chart_template_id = data_model.get_object_reference(
158+ cr, uid, 'account', 'configurable_chart_template')[1]
159+ chart_values = {
160+ 'company_id': self.company_id,
161+ 'currency_id': self.currency_id,
162+ 'chart_template_id': chart_template_id}
163+ chart_values.update(
164+ chart_setup_model.onchange_chart_template_id(
165+ cr, uid, [], 1)['value'])
166+ chart_setup_id = chart_setup_model.create(
167+ cr, uid, chart_values)
168+ chart_setup_model.execute(
169+ cr, uid, [chart_setup_id])
170+ year = datetime.now().strftime('%Y')
171+ fiscalyear_id = reg('account.fiscalyear').create(
172+ cr, uid, {
173+ 'name': year,
174+ 'code': year,
175+ 'company_id': self.company_id,
176+ 'date_start': '%s-01-01' % year,
177+ 'date_stop': '%s-12-31' % year,
178+ })
179+ reg('account.fiscalyear').create_period(
180+ cr, uid, [fiscalyear_id])
181+
182+ def setup_payables(self, reg, cr, uid):
183+ """
184+ Set up suppliers and invoice them. Check that the invoices
185+ can be validated properly.
186+ """
187+ partner_model = reg('res.partner')
188+ supplier1 = partner_model.create(
189+ cr, uid, {
190+ 'name': 'Supplier 1',
191+ 'supplier': True,
192+ 'country_id': self.country_id,
193+ 'bank_ids': [(0, False, {
194+ 'state': 'iban',
195+ 'acc_number': 'NL42INGB0000454000',
196+ 'bank': self.bank_id,
197+ 'bank_bic': 'INGBNL2A',
198+ })],
199+ })
200+ supplier2 = partner_model.create(
201+ cr, uid, {
202+ 'name': 'Supplier 2',
203+ 'supplier': True,
204+ 'country_id': self.country_id,
205+ 'bank_ids': [(0, False, {
206+ 'state': 'iban',
207+ 'acc_number': 'NL86INGB0002445588',
208+ 'bank': self.bank_id,
209+ 'bank_bic': 'INGBNL2A',
210+ })],
211+ })
212+ self.payable_id = reg('account.account').search(
213+ cr, uid, [
214+ ('company_id', '=', self.company_id),
215+ ('code', '=', '120000')])[0]
216+ expense_id = reg('account.account').search(
217+ cr, uid, [
218+ ('company_id', '=', self.company_id),
219+ ('code', '=', '123000')])[0]
220+ invoice_model = reg('account.invoice')
221+ values = {
222+ 'type': 'in_invoice',
223+ 'partner_id': supplier1,
224+ 'account_id': self.payable_id,
225+ 'invoice_line': [(0, False, {
226+ 'name': 'Purchase 1',
227+ 'price_unit': 100.0,
228+ 'quantity': 1,
229+ 'account_id': expense_id,})],
230+ }
231+ self.invoice_ids = [
232+ invoice_model.create(
233+ cr, uid, values, context={
234+ 'type': 'in_invoice',
235+ })]
236+ values.update({
237+ 'partner_id': supplier2,
238+ 'name': 'Purchase 2'})
239+ self.invoice_ids.append(
240+ invoice_model.create(
241+ cr, uid, values, context={
242+ 'type': 'in_invoice'}))
243+ wf_service = netsvc.LocalService('workflow')
244+ for invoice_id in self.invoice_ids:
245+ wf_service.trg_validate(
246+ uid, 'account.invoice', invoice_id, 'invoice_open', cr)
247+ self.assert_invoices_state('open')
248+
249+ def setup_payment_config(self, reg, cr, uid):
250+ """
251+ Configure an additional account and journal for payments
252+ in transit and configure a payment mode with them.
253+ """
254+ account_parent_id = reg('account.account').search(
255+ cr, uid, [
256+ ('company_id', '=', self.company_id),
257+ ('parent_id', '=', False)])[0]
258+ user_type = reg('ir.model.data').get_object_reference(
259+ cr, uid, 'account', 'data_account_type_liability')[1]
260+ transfer_account_id = reg('account.account').create(
261+ cr, uid, {
262+ 'company_id': self.company_id,
263+ 'parent_id': account_parent_id,
264+ 'code': 'TRANS',
265+ 'name': 'Transfer account',
266+ 'type': 'other',
267+ 'user_type': user_type,
268+ 'reconcile': True,
269+ })
270+ transfer_journal_id = reg('account.journal').search(
271+ cr, uid, [
272+ ('company_id', '=', self.company_id),
273+ ('code', '=', 'MISC')])[0]
274+ self.bank_journal_id = reg('account.journal').search(
275+ cr, uid, [
276+ ('company_id', '=', self.company_id),
277+ ('type', '=', 'bank')])[0]
278+ payment_mode_type_id = reg('ir.model.data').get_object_reference(
279+ cr, uid, 'account_banking_sepa_credit_transfer',
280+ 'export_sepa_sct_001_001_03')[1]
281+ self.payment_mode_id = reg('payment.mode').create(
282+ cr, uid, {
283+ 'name': 'SEPA Mode',
284+ 'bank_id': self.partner_bank_id,
285+ 'journal': self.bank_journal_id,
286+ 'company_id': self.company_id,
287+ 'transfer_account_id': transfer_account_id,
288+ 'transfer_journal_id': transfer_journal_id,
289+ 'type': payment_mode_type_id,
290+ })
291+
292+ def setup_payment(self, reg, cr, uid):
293+ """
294+ Create a payment order with the invoices' payable move lines.
295+ Check that the payment order can be confirmed.
296+ """
297+ self.payment_order_id = reg('payment.order').create(
298+ cr, uid, {
299+ 'reference': 'PAY001',
300+ 'mode': self.payment_mode_id,
301+ })
302+ context = {'active_id': self.payment_order_id}
303+ entries = reg('account.move.line').search(
304+ cr, uid, [
305+ ('company_id', '=', self.company_id),
306+ ('account_id', '=', self.payable_id),
307+ ])
308+ self.payment_order_create_id = reg('payment.order.create').create(
309+ cr, uid, {
310+ 'entries': [(6, 0, entries)],
311+ }, context=context)
312+ reg('payment.order.create').create_payment(
313+ cr, uid, [self.payment_order_create_id], context=context)
314+ wf_service = netsvc.LocalService('workflow')
315+ wf_service.trg_validate(
316+ uid, 'payment.order', self.payment_order_id, 'open', cr)
317+ self.assert_payment_order_state('open')
318+
319+ def export_payment(self, reg, cr, uid):
320+ """
321+ Call the SEPA export wizard on the payment order
322+ and check that the payment order and related invoices'
323+ states are moved forward afterwards
324+ """
325+ export_model = reg('banking.export.sepa.wizard')
326+ export_id = export_model.create(
327+ cr, uid, {
328+ 'msg_identification': 'EXP001'},
329+ context={'active_ids': [self.payment_order_id]})
330+ export_model.create_sepa(
331+ cr, uid, [export_id])
332+ export_model.save_sepa(
333+ cr, uid, [export_id])
334+ self.assert_payment_order_state('sent')
335+ self.assert_invoices_state('paid')
336+
337+ def setup_bank_statement(self, reg, cr, uid):
338+ """
339+ Create a bank statement with a single line. Call the reconciliation
340+ wizard to match the line with the open payment order. Confirm the
341+ bank statement. Check if the payment order is done.
342+ """
343+ statement_model = reg('account.bank.statement')
344+ line_model = reg('account.bank.statement.line')
345+ wizard_model = reg('banking.transaction.wizard')
346+ statement_id = statement_model.create(
347+ cr, uid, {
348+ 'name': 'Statement',
349+ 'journal_id': self.bank_journal_id,
350+ 'balance_end_real': -200.0,
351+ 'period_id': reg('account.period').find(cr, uid)[0]
352+ })
353+ line_id = line_model.create(
354+ cr, uid, {
355+ 'name': 'Statement line',
356+ 'statement_id': statement_id,
357+ 'amount': -200.0,
358+ 'account_id': self.payable_id,
359+ })
360+ wizard_id = wizard_model.create(
361+ cr, uid, {'statement_line_id': line_id})
362+ wizard_model.write(
363+ cr, uid, [wizard_id], {
364+ 'manual_payment_order_id': self.payment_order_id})
365+ statement_model.button_confirm_bank(cr, uid, [statement_id])
366+ self.assert_payment_order_state('done')
367+
368+ def check_reconciliations(self, reg, cr, uid):
369+ """
370+ Check if the payment order has any lines and that
371+ the transit move lines of those payment lines are
372+ reconciled by now.
373+ """
374+ payment_order = reg('payment.order').browse(
375+ cr, uid, self.payment_order_id)
376+ assert payment_order.line_ids, 'Payment order has no payment lines'
377+ for line in payment_order.line_ids:
378+ assert line.transit_move_line_id, \
379+ 'Payment order has no transfer move line'
380+ assert line.transit_move_line_id.reconcile_id, \
381+ 'Transfer move line on payment line is not reconciled'
382+
383+ def test_payment_roundtrip(self):
384+ reg, cr, uid, = self.registry, self.cr, self.uid
385+ self.setup_company(reg, cr, uid)
386+ self.setup_chart(reg, cr, uid)
387+ self.setup_payables(reg, cr, uid)
388+ self.setup_payment_config(reg, cr, uid)
389+ self.setup_payment(reg, cr, uid)
390+ self.export_payment(reg, cr, uid)
391+ self.setup_bank_statement(reg, cr, uid)
392+ self.check_reconciliations(reg, cr, uid)

Subscribers

People subscribed via source and target branches

to status/vote changes: