Merge lp:~openerp-dev/openobject-addons/trunk-import-bank-statements into lp:openobject-addons

Proposed by DJ Patel (OpenERP)
Status: Needs review
Proposed branch: lp:~openerp-dev/openobject-addons/trunk-import-bank-statements
Merge into: lp:openobject-addons
Diff against target: 817 lines (+520/-103)
19 files modified
account/__init__.py (+1/-0)
account/__openerp__.py (+1/-1)
account/account_bank_statement_import.py (+118/-0)
account/account_bank_statement_import_view.xml (+47/-0)
account/tests/__init__.py (+4/-0)
account/tests/test_import_bank_statement.py (+73/-0)
account/wizard/account_financial_report.py (+1/-1)
account_qif/__init__.py (+24/-0)
account_qif/__openerp__.py (+49/-0)
account_qif/account_qif.py (+96/-0)
account_qif/tests/__init__.py (+4/-0)
account_qif/tests/test_import_bank_statement.py (+30/-0)
l10n_be_coda/__openerp__.py (+0/-1)
l10n_be_coda/l10n_be_coda_view.xml (+1/-2)
l10n_be_coda/l10n_be_coda_wizard.xml (+0/-35)
l10n_be_coda/test_coda_file/Ontvangen_CODA.2013-01-11-18.59.15.txt (+11/-11)
l10n_be_coda/tests/__init__.py (+4/-0)
l10n_be_coda/tests/test_import_bank_statement.py (+39/-0)
l10n_be_coda/wizard/account_coda_import.py (+17/-52)
To merge this branch: bzr merge lp:~openerp-dev/openobject-addons/trunk-import-bank-statements
Reviewer Review Type Date Requested Status
OpenERP Core Team Pending
Review via email: mp+212993@code.launchpad.net

Description of the change

Hello,

I have created a generic wizard for ofx, qif and coda file format.
It is used to import these types of bank statements into openerp.

Thanks,
Divyesh

To post a comment you must log in.
9257. By DJ Patel (OpenERP)

[IMP] account : Improved the code to return an action.

9258. By DJ Patel (OpenERP)

[FIX] account, account_qif, l10n_be_coda : Fixed the test cases.

9259. By DJ Patel (OpenERP)

[Merge] Merged with trunk.

9260. By DJ Patel (OpenERP)

[Merge] Merged with trunk.

9261. By DJ Patel (OpenERP)

[Merge] Merged with trunk.

9262. By DJ Patel (OpenERP)

[Merge] Merged with trunk.

Unmerged revisions

9262. By DJ Patel (OpenERP)

[Merge] Merged with trunk.

9261. By DJ Patel (OpenERP)

[Merge] Merged with trunk.

9260. By DJ Patel (OpenERP)

[Merge] Merged with trunk.

9259. By DJ Patel (OpenERP)

[Merge] Merged with trunk.

9258. By DJ Patel (OpenERP)

[FIX] account, account_qif, l10n_be_coda : Fixed the test cases.

9257. By DJ Patel (OpenERP)

[IMP] account : Improved the code to return an action.

9256. By DJ Patel (OpenERP)

[Merge] Merged the branch: lp:~openerp-dev/openobject-addons/trunk-import-bank-statement with improvements.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'account/__init__.py'
2--- account/__init__.py 2012-11-29 22:26:45 +0000
3+++ account/__init__.py 2014-04-02 04:28:41 +0000
4@@ -38,5 +38,6 @@
5 import res_currency
6 import edi
7 import res_config
8+import account_bank_statement_import
9
10 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
11
12=== modified file 'account/__openerp__.py'
13--- account/__openerp__.py 2014-02-13 17:39:10 +0000
14+++ account/__openerp__.py 2014-04-02 04:28:41 +0000
15@@ -124,7 +124,7 @@
16 'account_bank_view.xml',
17 'res_config_view.xml',
18 'account_pre_install.yml',
19-
20+ 'account_bank_statement_import_view.xml',
21 'views/report_vat.xml',
22 ],
23 'js': [
24
25=== added file 'account/account_bank_statement_import.py'
26--- account/account_bank_statement_import.py 1970-01-01 00:00:00 +0000
27+++ account/account_bank_statement_import.py 2014-04-02 04:28:41 +0000
28@@ -0,0 +1,118 @@
29+# -*- coding: utf-8 -*-
30+##############################################################################
31+#
32+# OpenERP, Open Source Management Solution
33+# Copyright (C) 2012-Today OpenERP SA (<http://www.openerp.com>).
34+#
35+# This program is free software: you can redistribute it and/or modify
36+# it under the terms of the GNU Affero General Public License as
37+# published by the Free Software Foundation, either version 3 of the
38+# License, or (at your option) any later version.
39+#
40+# This program is distributed in the hope that it will be useful,
41+# but WITHOUT ANY WARRANTY; without even the implied warranty of
42+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
43+# GNU Affero General Public License for more details.
44+#
45+# You should have received a copy of the GNU Affero General Public License
46+# along with this program. If not, see <http://www.gnu.org/licenses/>.
47+#
48+##############################################################################
49+
50+import logging
51+import base64
52+import os
53+import time
54+
55+from openerp.osv import fields, osv
56+from openerp.tools.translate import _
57+from openerp import tools
58+
59+_logger = logging.getLogger(__name__)
60+
61+try:
62+ from ofxparse import OfxParser as ofxparser
63+except ImportError:
64+ _logger.warning("OFX parser partially unavailable because the `ofxparse` Python library cannot be found. "
65+ "It can be easily download and install from this `https://pypi.python.org/pypi/ofxparse`.")
66+ ofxparser = None
67+
68+_IMPORT_FILE_TYPE = [('ofx', 'OFX')]
69+
70+class account_bank_statement_import(osv.TransientModel):
71+ _name = 'account.bank.statement.import'
72+ _description = 'Import Bank Statement'
73+ _columns = {
74+ 'data_file': fields.binary('Bank Statement File', required=True, help='Select bank statement file to import in OpenERP. .OFX, .QIF or CODA are accepted.'),
75+ 'file_type': fields.selection(_IMPORT_FILE_TYPE, 'File Type'),
76+ 'journal_id': fields.many2one('account.journal', 'Journal', required=True),
77+ 'account_id': fields.many2one('account.account', 'Account', domain="[('type', '!=', 'view')]", help=" Choose the account to be credited/debited for the entries of the statement.", required=True),
78+ }
79+
80+ def _get_default_journal(self, cr, uid, context=None):
81+ company_id = self.pool.get('res.company')._company_default_get(cr, uid, 'account.bank.statement', context=context)
82+ journal_ids = self.pool.get('account.journal').search(cr, uid, [('type', '=', 'bank'), ('company_id', '=', company_id)], context=context)
83+ return journal_ids and journal_ids[0] or False
84+
85+ _defaults = {
86+ 'file_type': 'ofx',
87+ 'journal_id': _get_default_journal,
88+ }
89+
90+ def import_bank_statement(self, cr, uid, bank_statement_vals=False, context=None):
91+ statement_id = self.pool.get('account.bank.statement').create(cr, uid, bank_statement_vals, context=context)
92+ return statement_id
93+
94+ def process_ofx(self, cr, uid, data_file, journal_id=False, account_id=False, context=None):
95+ try:
96+ tempfile = open( "temp.ofx", "w+" )
97+ tempfile.write(base64.decodestring(data_file))
98+ tempfile.read()
99+ pathname = os.path.dirname('temp.ofx')
100+ path = os.path.join(os.path.abspath(pathname), 'temp.ofx')
101+ ofx = ofxparser.parse(file(path))
102+ except:
103+ raise osv.except_osv(_('Import Error!'), _('Please check OFX file format is proper or not.'))
104+ line_ids = []
105+ total_amt = 0.00
106+ try:
107+ for transaction in ofx.account.statement.transactions:
108+ vals_line = {
109+ 'date' : transaction.date,
110+ 'name' : transaction.memo,
111+ 'ref': transaction.id,
112+ 'amount' : transaction.amount,
113+ 'account_id': account_id
114+ }
115+ total_amt += float(transaction.amount)
116+ line_ids.append((0, 0, vals_line))
117+ except Exception,e:
118+ raise osv.except_osv(_('Error!'), _("Following problem has been occurred while importing your file, Please verify the file is proper or not.\n\n %s" % e.message))
119+ st_start_date = ofx.account.statement.start_date or False
120+ st_end_date = ofx.account.statement.end_date or False
121+ period_obj = self.pool.get('account.period')
122+ if st_end_date:
123+ period_ids = period_obj.find(cr, uid, st_end_date, context=context)
124+ else:
125+ period_ids = period_obj.find(cr, uid, st_start_date, context=context)
126+ vals_bank_statement = {
127+ 'name': ofx.account.routing_number,
128+ 'balance_start': ofx.account.statement.balance,
129+ 'balance_end_real': float(ofx.account.statement.balance) + total_amt,
130+ 'period_id': period_ids and period_ids[0] or False,
131+ 'journal_id': journal_id
132+ }
133+ vals_bank_statement.update({'line_ids': line_ids})
134+ os.remove(path)
135+ return vals_bank_statement
136+
137+ def parse_file(self, cr, uid, ids, context=None):
138+ data = self.browse(cr, uid, ids[0], context=context)
139+ vals = getattr(self, "process_%s" % data.file_type)(cr, uid, data.data_file, data.journal_id.id, data.account_id.id, context=context)
140+ statement_id = self.import_bank_statement(cr, uid, vals, context=context)
141+ model, action_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account', 'action_bank_statement_tree')
142+ action = self.pool[model].read(cr, uid, action_id, context=context)
143+ action['domain'] = "[('id', 'in', ["+', '.join(map(str, [statement_id]))+"])]"
144+ return action
145+
146+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
147
148=== added file 'account/account_bank_statement_import_view.xml'
149--- account/account_bank_statement_import_view.xml 1970-01-01 00:00:00 +0000
150+++ account/account_bank_statement_import_view.xml 2014-04-02 04:28:41 +0000
151@@ -0,0 +1,47 @@
152+<?xml version="1.0" ?>
153+<openerp>
154+ <data>
155+
156+ <record id="account_bank_statement_import_view" model="ir.ui.view">
157+ <field name="name">Import Bank Statements</field>
158+ <field name="model">account.bank.statement.import</field>
159+ <field name="priority">1</field>
160+ <field name="arch" type="xml">
161+ <form string="Import Bank Statements" version="7.0">
162+ <group>
163+ <group>
164+ <field name="file_type" required="1"/>
165+ <field name="data_file" attrs="{'invisible':[('file_type','=',False)]}"/>
166+ <field name="journal_id" domain="[('type', '=', 'bank')]" attrs="{'invisible':[('file_type','=',False)]}" context="{'default_type':'bank'}"/>
167+ <field name="account_id" attrs="{'invisible':[('file_type','=',False)]}"/>
168+ </group>
169+ <group>
170+ <b colspan="2"> How to import your bank statement in OpenERP.</b>
171+ <label string= "1. Go to your bank account website." colspan="2"/>
172+ <label string= "2. Download your bank statements in the right format. (.OFX, .QIF or CODA are accepted)" colspan="2"/>
173+ <label string= "3. Upload right here the bank statements file into OpenERP. Click Import." colspan="2"/>
174+ </group>
175+ </group>
176+ <footer>
177+ <button name="parse_file" string="_Import" type="object" class="oe_highlight"/>
178+ or
179+ <button string="Cancel" class="oe_link" special="cancel"/>
180+ </footer>
181+ </form>
182+ </field>
183+ </record>
184+
185+ <record id="action_account_bank_statement_import" model="ir.actions.act_window">
186+ <field name="name">Import Bank Statements</field>
187+ <field name="type">ir.actions.act_window</field>
188+ <field name="res_model">account.bank.statement.import</field>
189+ <field name="view_type">form</field>
190+ <field name="view_mode">form</field>
191+ <field name="target">new</field>
192+ <field name="view_id" ref="account_bank_statement_import_view"/>
193+ </record>
194+
195+ <menuitem parent="account.menu_finance_bank_and_cash" id="menu_account_bank_statement_import" action="action_account_bank_statement_import" sequence="11"/>
196+
197+ </data>
198+</openerp>
199
200=== modified file 'account/tests/__init__.py'
201--- account/tests/__init__.py 2013-12-06 17:00:12 +0000
202+++ account/tests/__init__.py 2014-04-02 04:28:41 +0000
203@@ -1,7 +1,11 @@
204 from . import test_tax
205+from . import test_import_bank_statement
206 from . import test_search
207
208 fast_suite = [
209 test_tax,
210 test_search,
211 ]
212+checks = [
213+ test_import_bank_statement
214+]
215
216=== added file 'account/tests/test_import_bank_statement.py'
217--- account/tests/test_import_bank_statement.py 1970-01-01 00:00:00 +0000
218+++ account/tests/test_import_bank_statement.py 2014-04-02 04:28:41 +0000
219@@ -0,0 +1,73 @@
220+from openerp.tests.common import TransactionCase
221+
222+ofx_file = """PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iQVNDSUkiPz4NCjw/
223+ T0ZYIE9GWEhFQURFUj0iMjAwIiBWRVJTSU9OPSIyMTEiIFNFQ1VSSVRZPSJOT05FIiBPTE
224+ RGSUxFVUlEPSJOT05FIiBORVdGSUxFVUlEPSJOT05FIj8+DQo8T0ZYPg0KICA8U0lHTk9O
225+ TVNHU1JTVjE+DQogICAgPFNPTlJTPg0KICAgICAgPFNUQVRVUz4NCiAgICAgICAgPENPRE
226+ U+MDwvQ09ERT4NCiAgICAgICAgPFNFVkVSSVRZPklORk88L1NFVkVSSVRZPg0KICAgICAg
227+ PC9TVEFUVVM+DQogICAgICA8RFRTRVJWRVI+MjAwNTA4MzExNjUxNTMuMDAwWy04OlBTVF
228+ 08L0RUU0VSVkVSPg0KICAgICAgPExBTkdVQUdFPkVORzwvTEFOR1VBR0U+DQogICAgPC9T
229+ T05SUz4NCiAgPC9TSUdOT05NU0dTUlNWMT4NCiAgPEJBTktNU0dTUlNWMT4NCiAgICA8U1
230+ RNVFRSTlJTPg0KICAgICAgPFRSTlVJRD4wPC9UUk5VSUQ+DQogICAgICA8U1RBVFVTPg0K
231+ ICAgICAgICA8Q09ERT4wPC9DT0RFPg0KICAgICAgICA8U0VWRVJJVFk+SU5GTzwvU0VWRV
232+ JJVFk+DQogICAgICA8L1NUQVRVUz4NCiAgICAgIDxTVE1UUlM+DQogICAgICAgIDxDVVJE
233+ RUY+VVNEPC9DVVJERUY+DQogICAgICAgIDxCQU5LQUNDVEZST00+DQogICAgICAgICAgPE
234+ JBTktJRD4wMDAwMDAxMjM8L0JBTktJRD4NCiAgICAgICAgICA8QUNDVElEPjEyMzQ1Njwv
235+ QUNDVElEPg0KICAgICAgICAgIDxBQ0NUVFlQRT5DSEVDS0lORzwvQUNDVFRZUEU+DQogIC
236+ AgICAgIDwvQkFOS0FDQ1RGUk9NPg0KICAgICAgICA8QkFOS1RSQU5MSVNUPg0KICAgICAg
237+ ICAgIDxEVFNUQVJUPjIwMTQwODAxPC9EVFNUQVJUPg0KICAgICAgICAgIDxEVEVORD4yMD
238+ E0MDgzMTE2NTE1My4wMDBbLTg6UFNUXTwvRFRFTkQ+DQogICAgICAgICAgPFNUTVRUUk4+
239+ DQogICAgICAgICAgICA8VFJOVFlQRT5QT1M8L1RSTlRZUEU+DQogICAgICAgICAgICA8RF
240+ RQT1NURUQ+MjAwNTA4MjQwODAwMDA8L0RUUE9TVEVEPg0KICAgICAgICAgICAgPFRSTkFN
241+ VD4tODA8L1RSTkFNVD4NCiAgICAgICAgICAgIDxGSVRJRD4yMTkzNzg8L0ZJVElEPg0KIC
242+ AgICAgICAgICAgPE5BTUU+RnJvZ0tpY2sgU2N1YmEgR2VhcjwvTkFNRT4NCiAgICAgICAg
243+ ICA8L1NUTVRUUk4+DQogICAgICAgIDwvQkFOS1RSQU5MSVNUPg0KICAgICAgICA8TEVER0
244+ VSQkFMPg0KICAgICAgICAgIDxCQUxBTVQ+MjE1Ni41NjwvQkFMQU1UPg0KICAgICAgICAg
245+ IDxEVEFTT0Y+MjAwNTA4MzExNjUxNTM8L0RUQVNPRj4NCiAgICAgICAgPC9MRURHRVJCQU
246+ w+DQogICAgICA8L1NUTVRSUz4NCiAgICA8L1NUTVRUUk5SUz4NCiAgPC9CQU5LTVNHU1JT
247+ VjE+DQogIDxDUkVESVRDQVJETVNHU1JTVjE+DQogICAgPENDU1RNVFRSTlJTPg0KICAgIC
248+ AgPFRSTlVJRD4wPC9UUk5VSUQ+DQogICAgICA8U1RBVFVTPg0KICAgICAgICA8Q09ERT4w
249+ PC9DT0RFPg0KICAgICAgICA8U0VWRVJJVFk+SU5GTzwvU0VWRVJJVFk+DQogICAgICA8L1
250+ NUQVRVUz4NCiAgICAgIDxDQ1NUTVRSUz4NCiAgICAgICAgPENVUkRFRj5VU0Q8L0NVUkRF
251+ Rj4NCiAgICAgICAgPENDQUNDVEZST00+DQogICAgICAgICAgPEFDQ1RJRD4xMjM0MTIzND
252+ EyMzQ8L0FDQ1RJRD4NCiAgICAgICAgPC9DQ0FDQ1RGUk9NPg0KICAgICAgICA8QkFOS1RS
253+ QU5MSVNUPg0KICAgICAgICAgIDxEVFNUQVJUPjIwMTQwODAxPC9EVFNUQVJUPg0KICAgIC
254+ AgICAgIDxEVEVORD4yMDE0MDgzMTE2NTE1My4wMDBbLTg6UFNUXTwvRFRFTkQ+DQogICAg
255+ ICAgICAgPFNUTVRUUk4+DQogICAgICAgICAgICA8VFJOVFlQRT5JTlQ8L1RSTlRZUEU+DQ
256+ ogICAgICAgICAgICA8RFRQT1NURUQ+MjAwNTA4MTEwODAwMDA8L0RUUE9TVEVEPg0KICAg
257+ ICAgICAgICAgPFRSTkFNVD4tMjMuMDA8L1RSTkFNVD4NCiAgICAgICAgICAgIDxGSVRJRD
258+ 4yMTk4Njc8L0ZJVElEPg0KICAgICAgICAgICAgPE5BTUU+SW50ZXJlc3QgQ2hhcmdlPC9O
259+ QU1FPg0KICAgICAgICAgIDwvU1RNVFRSTj4NCiAgICAgICAgICA8U1RNVFRSTj4NCiAgIC
260+ AgICAgICAgIDxUUk5UWVBFPkNSRURJVDwvVFJOVFlQRT4NCiAgICAgICAgICAgIDxEVFBP
261+ U1RFRD4yMDA1MDgxMTA4MDAwMDwvRFRQT1NURUQ+DQogICAgICAgICAgICA8VFJOQU1UPj
262+ M1MC4wMDwvVFJOQU1UPg0KICAgICAgICAgICAgPEZJVElEPjIxOTg2ODwvRklUSUQ+DQog
263+ ICAgICAgICAgICA8TkFNRT5QYXltZW50IC0gVGhhbmsgWW91PC9OQU1FPg0KICAgICAgIC
264+ AgIDwvU1RNVFRSTj4NCiAgICAgICAgPC9CQU5LVFJBTkxJU1Q+DQogICAgICAgIDxMRURH
265+ RVJCQUw+DQogICAgICAgICAgPEJBTEFNVD4tNTYyLjAwPC9CQUxBTVQ+DQogICAgICAgIC
266+ AgPERUQVNPRj4yMDA1MDgzMTE2NTE1MzwvRFRBU09GPg0KICAgICAgICA8L0xFREdFUkJB
267+ TD4NCiAgICAgIDwvQ0NTVE1UUlM+DQogICAgPC9DQ1NUTVRUUk5SUz4NCiAgPC9DUkVESV
268+ RDQVJETVNHU1JTVjE+DQo8L09GWD4NCg=="""
269+
270+class TestOfxFile(TransactionCase):
271+ """Tests for import bank statement ofx file format (account.bank.statement.import)
272+ """
273+
274+ def setUp(self):
275+ super(TestOfxFile, self).setUp()
276+ self.statement_import_model = self.registry('account.bank.statement.import')
277+ self.bank_statement_model = self.registry('account.bank.statement')
278+
279+ def test_ofx_file_import(self):
280+ cr, uid = self.cr, self.uid
281+ bank_temp_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'account', 'conf_bnk')
282+ self.bank_temp_id = bank_temp_ref and bank_temp_ref[1] or False
283+ bank_statement_id = self.statement_import_model.create(cr, uid, dict(
284+ file_type = 'ofx',
285+ data_file = ofx_file,
286+ account_id = self.bank_temp_id
287+ ))
288+ self.statement_import_model.parse_file(cr, uid, [bank_statement_id])
289+ statement_id = self.bank_statement_model.search(cr, uid, [('name', '=', '000000123')])[0]
290+ bank_st_record = self.bank_statement_model.browse(cr, uid, statement_id)
291+ self.assertEquals(bank_st_record.balance_start, 2156.56)
292+ self.assertEquals(bank_st_record.balance_end_real, 2076.56)
293
294=== modified file 'account/wizard/account_financial_report.py'
295--- account/wizard/account_financial_report.py 2013-10-27 12:31:04 +0000
296+++ account/wizard/account_financial_report.py 2014-04-02 04:28:41 +0000
297@@ -2,7 +2,7 @@
298 ##############################################################################
299 #
300 # OpenERP, Open Source Management Solution
301-# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
302+# Copyright (C) 2012-Today OpenERP SA (<http://www.openerp.com>).
303 #
304 # This program is free software: you can redistribute it and/or modify
305 # it under the terms of the GNU Affero General Public License as
306
307=== added directory 'account_qif'
308=== added file 'account_qif/__init__.py'
309--- account_qif/__init__.py 1970-01-01 00:00:00 +0000
310+++ account_qif/__init__.py 2014-04-02 04:28:41 +0000
311@@ -0,0 +1,24 @@
312+# -*- coding: utf-8 -*-
313+##############################################################################
314+#
315+# OpenERP, Open Source Management Solution
316+# Copyright (C) 2012-Today OpenERP SA (<http://www.openerp.com>).
317+#
318+# This program is free software: you can redistribute it and/or modify
319+# it under the terms of the GNU Affero General Public License as
320+# published by the Free Software Foundation, either version 3 of the
321+# License, or (at your option) any later version.
322+#
323+# This program is distributed in the hope that it will be useful,
324+# but WITHOUT ANY WARRANTY; without even the implied warranty of
325+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
326+# GNU Affero General Public License for more details.
327+#
328+# You should have received a copy of the GNU Affero General Public License
329+# along with this program. If not, see <http://www.gnu.org/licenses/>.
330+#
331+##############################################################################
332+
333+import account_qif
334+
335+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
336
337=== added file 'account_qif/__openerp__.py'
338--- account_qif/__openerp__.py 1970-01-01 00:00:00 +0000
339+++ account_qif/__openerp__.py 2014-04-02 04:28:41 +0000
340@@ -0,0 +1,49 @@
341+# -*- coding: utf-8 -*-
342+##############################################################################
343+#
344+# OpenERP, Open Source Management Solution
345+# Copyright (C) 2012-Today OpenERP SA (<http://www.openerp.com>).
346+#
347+# This program is free software: you can redistribute it and/or modify
348+# it under the terms of the GNU Affero General Public License as
349+# published by the Free Software Foundation, either version 3 of the
350+# License, or (at your option) any later version.
351+#
352+# This program is distributed in the hope that it will be useful,
353+# but WITHOUT ANY WARRANTY; without even the implied warranty of
354+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
355+# GNU Affero General Public License for more details.
356+#
357+# You should have received a copy of the GNU Affero General Public License
358+# along with this program. If not, see <http://www.gnu.org/licenses/>.
359+#
360+##############################################################################
361+
362+{
363+ 'name': 'Import QIF Bank Statement',
364+ 'version': '1.1',
365+ 'author': 'OpenERP SA',
366+ 'category': 'Accounting & Finance',
367+ 'description': '''
368+Module to import QIF bank statements.
369+======================================
370+
371+The machine readable QIF Files are parsed and stored in human readable format in
372+Bank Statements. Also Bank Statements are generated containing a subset of
373+the QIF information (only those transaction lines that are required for the
374+creation of the Financial Accounting records). The Bank Statement is a
375+'read-only' object, hence remaining a reliable representation of the original
376+QIF file whereas the Bank Statement will get modified as required by accounting
377+business processes.
378+
379+QIF Bank Accounts configured as type 'QIF' will only generate QIF Bank Statements.
380+''',
381+ 'images' : [],
382+ 'depends': ['account'],
383+ 'demo': [],
384+ 'data': [],
385+ 'auto_install': False,
386+ 'installable': True,
387+}
388+
389+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
390
391=== added file 'account_qif/account_qif.py'
392--- account_qif/account_qif.py 1970-01-01 00:00:00 +0000
393+++ account_qif/account_qif.py 2014-04-02 04:28:41 +0000
394@@ -0,0 +1,96 @@
395+# -*- coding: utf-8 -*-
396+##############################################################################
397+#
398+# OpenERP, Open Source Management Solution
399+# Copyright (C) 2012-Today OpenERP SA (<http://www.openerp.com>).
400+#
401+# This program is free software: you can redistribute it and/or modify
402+# it under the terms of the GNU Affero General Public License as
403+# published by the Free Software Foundation, either version 3 of the
404+# License, or (at your option) any later version.
405+#
406+# This program is distributed in the hope that it will be useful,
407+# but WITHOUT ANY WARRANTY; without even the implied warranty of
408+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
409+# GNU Affero General Public License for more details.
410+#
411+# You should have received a copy of the GNU Affero General Public License
412+# along with this program. If not, see <http://www.gnu.org/licenses/>.
413+#
414+##############################################################################
415+
416+import dateutil.parser
417+import base64
418+import logging
419+from tempfile import TemporaryFile
420+
421+from openerp.tools.translate import _
422+from openerp.osv import fields, osv
423+from openerp import tools
424+
425+_logger = logging.getLogger(__name__)
426+
427+from openerp.addons.account import account_bank_statement_import as ibs
428+
429+ibs._IMPORT_FILE_TYPE.append(('qif', 'QIF'))
430+
431+class account_bank_statement_import(osv.TransientModel):
432+ _inherit = "account.bank.statement.import"
433+
434+ _columns = {
435+ 'file_type': fields.selection(ibs._IMPORT_FILE_TYPE, 'File Type'),
436+ }
437+
438+ def process_qif(self, cr, uid, data_file, journal_id=False, account_id=False, context=None):
439+ try:
440+ fileobj = TemporaryFile('wb+')
441+ fileobj.write(base64.b64decode(data_file))
442+ fileobj.seek(0)
443+ file_data = ""
444+ for line in fileobj.readlines():
445+ file_data += line
446+ fileobj.close()
447+ if '\r' in file_data:
448+ data_list = file_data.split('\r')
449+ else:
450+ data_list = file_data.split('\n')
451+ header = data_list[0].strip()
452+ header = header.split(":")[1]
453+ except:
454+ raise osv.except_osv(_('Import Error!'), _('Please check QIF file format is proper or not.'))
455+ line_ids = []
456+ vals_line = {}
457+ total = 0
458+ if header == "Bank":
459+ vals_bank_statement = {}
460+ for line in data_list:
461+ line = line.strip()
462+ if not line: continue
463+ if line[0] == 'D': # date of transaction
464+ vals_line['date'] = dateutil.parser.parse(line[1:], fuzzy=True).date()
465+ if vals_line.get('date'):
466+ period_ids = self.pool.get('account.period').find(cr, uid, vals_line['date'], context=context)
467+ vals_bank_statement.update({'period_id': period_ids and period_ids[0] or False})
468+ elif line[0] == 'T': # Total amount
469+ total += float(line[1:].replace(',', ''))
470+ vals_line['amount'] = float(line[1:].replace(',', ''))
471+ elif line[0] == 'N': # Check number
472+ vals_line['ref'] = line[1:]
473+ elif line[0] == 'P': # Payee
474+ vals_line['name'] = line[1:]
475+ elif line[0] == '^': # end of item
476+ vals_line['account_id'] = account_id
477+ line_ids.append((0, 0, vals_line))
478+ vals_line = {}
479+ elif line[0] == '\n':
480+ line_ids = []
481+ else:
482+ pass
483+ else:
484+ raise osv.except_osv(_('Error!'), _('Cannot support this Format !Type:%s.') % (header,))
485+ vals_bank_statement.update({'balance_end_real': total,
486+ 'line_ids': line_ids,
487+ 'journal_id': journal_id})
488+ return vals_bank_statement
489+
490+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
491
492=== added directory 'account_qif/tests'
493=== added file 'account_qif/tests/__init__.py'
494--- account_qif/tests/__init__.py 1970-01-01 00:00:00 +0000
495+++ account_qif/tests/__init__.py 2014-04-02 04:28:41 +0000
496@@ -0,0 +1,4 @@
497+from . import test_import_bank_statement
498+checks = [
499+ test_import_bank_statement
500+]
501
502=== added file 'account_qif/tests/test_import_bank_statement.py'
503--- account_qif/tests/test_import_bank_statement.py 1970-01-01 00:00:00 +0000
504+++ account_qif/tests/test_import_bank_statement.py 2014-04-02 04:28:41 +0000
505@@ -0,0 +1,30 @@
506+from openerp.tests.common import TransactionCase
507+
508+qif_file = """IVR5cGU6QmFuawpEOC8xMi8xNApULTEsMDAwLjAwClBGcmFua3MgUGx1bWJpbmcKXgpEOC8xNS8xNApULTc1L
509+ jQ2ClBXYWx0cyBEcnVncwpeCkQzLzMvMTQKVC0zNzkuMDAKUENJVFkgT0YgU1BSSU5HRklFTEQKXgpEMy80LzE0ClQtMjAuM
510+ jgKUFlPVVIgTE9DQUwgU1VQRVJNQVJLRVQKXgpEMy8zLzE0ClQtNDIxLjM1ClBTUFJJTkdGSUVMRCBXQVRFUiBVVElMSVRZCl4K"""
511+
512+class TestQifFile(TransactionCase):
513+ """Tests for import bank statement qif file format (account.bank.statement.import)
514+ """
515+
516+ def setUp(self):
517+ super(TestQifFile, self).setUp()
518+ self.statement_import_model = self.registry('account.bank.statement.import')
519+ self.bank_statement_model = self.registry('account.bank.statement')
520+ self.bank_statement_line_model = self.registry('account.bank.statement.line')
521+
522+ def test_qif_file_import(self):
523+ cr, uid = self.cr, self.uid
524+ bank_temp_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'account', 'conf_bnk')
525+ self.bank_temp_id = bank_temp_ref and bank_temp_ref[1] or False
526+ bank_statement_id = self.statement_import_model.create(cr, uid, dict(
527+ file_type = 'qif',
528+ data_file = qif_file,
529+ account_id = self.bank_temp_id
530+ ))
531+ self.statement_import_model.parse_file(cr, uid, [bank_statement_id])
532+ line_id = self.bank_statement_line_model.search(cr, uid, [('name', '=', 'YOUR LOCAL SUPERMARKET')])[0]
533+ statement_id = self.bank_statement_line_model.browse(cr, uid, line_id).statement_id.id
534+ bank_st_record = self.bank_statement_model.browse(cr, uid, statement_id)
535+ self.assertEquals(bank_st_record.balance_end_real, -1896.09)
536
537=== modified file 'l10n_be_coda/__openerp__.py'
538--- l10n_be_coda/__openerp__.py 2012-12-06 11:18:16 +0000
539+++ l10n_be_coda/__openerp__.py 2014-04-02 04:28:41 +0000
540@@ -93,7 +93,6 @@
541 'depends': ['account_voucher','base_iban', 'l10n_be_invoice_bba',],
542 'demo': [],
543 'data': [
544- 'l10n_be_coda_wizard.xml',
545 'l10n_be_coda_view.xml',
546 ],
547 'auto_install': False,
548
549=== modified file 'l10n_be_coda/l10n_be_coda_view.xml'
550--- l10n_be_coda/l10n_be_coda_view.xml 2013-10-27 12:31:04 +0000
551+++ l10n_be_coda/l10n_be_coda_view.xml 2014-04-02 04:28:41 +0000
552@@ -93,6 +93,5 @@
553 </record>
554
555 <menuitem name="Bank Statement Lines" parent="account.menu_finance_bank_and_cash" id="menu_account_bank_statement_line_coda" action="action_account_bank_statement_line_coda" sequence="8" groups="base.group_no_one"/>
556- <menuitem name="Import CODA File" parent="account.menu_finance_bank_and_cash" id="menu_account_coda_import" action="action_account_coda_import" sequence="10"/>
557 </data>
558-</openerp>
559\ No newline at end of file
560+</openerp>
561
562=== removed file 'l10n_be_coda/l10n_be_coda_wizard.xml'
563--- l10n_be_coda/l10n_be_coda_wizard.xml 2012-12-20 14:52:27 +0000
564+++ l10n_be_coda/l10n_be_coda_wizard.xml 1970-01-01 00:00:00 +0000
565@@ -1,35 +0,0 @@
566-<?xml version="1.0" ?>
567-<openerp>
568- <data>
569-
570- <record id="account_coda_import_view" model="ir.ui.view">
571- <field name="name">Import CODA File</field>
572- <field name="model">account.coda.import</field>
573- <field name="priority">1</field>
574- <field name="arch" type="xml">
575- <form string="Import CODA File" version="7.0">
576- <group col="2">
577- <field name="coda_data" filename="coda_fname"/>
578- <field name="temporary_account_id" />
579- </group>
580- <footer>
581- <button name="coda_parsing" string="_Import" type="object" class="oe_highlight"/>
582- or
583- <button string="Cancel" class="oe_link" special="cancel"/>
584- </footer>
585- </form>
586- </field>
587- </record>
588-
589- <record id="action_account_coda_import" model="ir.actions.act_window">
590- <field name="name">Import CODA File</field>
591- <field name="type">ir.actions.act_window</field>
592- <field name="res_model">account.coda.import</field>
593- <field name="view_type">form</field>
594- <field name="view_mode">form</field>
595- <field name="target">new</field>
596- <field name="view_id" ref="account_coda_import_view"/>
597- </record>
598-
599- </data>
600-</openerp>
601
602=== renamed file 'l10n_be_coda/test_coda_file/Ontvangen_CODA.2011-01-11-18.59.15.txt' => 'l10n_be_coda/test_coda_file/Ontvangen_CODA.2013-01-11-18.59.15.txt'
603--- l10n_be_coda/test_coda_file/Ontvangen_CODA.2011-01-11-18.59.15.txt 2012-01-31 13:36:57 +0000
604+++ l10n_be_coda/test_coda_file/Ontvangen_CODA.2013-01-11-18.59.15.txt 2014-04-02 04:28:41 +0000
605@@ -1,24 +1,24 @@
606-0000011011172505 00178299 DE MEYER LUC KREDBEBB 00820512013 00000 2
607+0000011011472505 00178299 DE MEYER LUC KREDBEBB 00820512014 00000 2
608 12135BE33737018595246 EUR0000000011812700270710NOVIAT NV KBC-Business Comfortrekening 003
609-2100010000OL44483FW SCTOFBIONLO1000000000435000110111001010000MEDEDELING 11011113501 0
610+2100010000OL44483FW SCTOFBIONLO1000000000435000110114001010000MEDEDELING 11011413501 0
611 2200010000 GKCCBEBB 1 0
612 2300010000BE41063012345610 PARTNER 1 0 1
613 3100010001OL44483FW SCTOFBIONLO001010001001PARTNER 1 0 0
614-2100020000OL4414AC8BOVSOVSOVERS00000000030444501101110015000002010237 11011113501 0
615+2100020000OL4414AC8BOVSOVSOVERS00000000030444501101140015000002010237 11011413501 0
616 2200020000 BBRUBEBB 1 0
617 2300020000BE61310126985517 PARTNER 2 0 1
618 3100020001OL4414AC8BOVSOVSOVERS001500001001PARTNER 2 1 0
619 3200020001MOLENSTRAAT 60 9340 LEDE 0 0
620-2100030000AFECA0CVA IKLINNINNIG1000000000479040110111313410000 KBC-INVESTERINGSKREDIET 737-6543210-21 11011113510 0
621-2100030001AFECA0CVA IKLINNINNIG1000000000419920110111813410660 11011113500 0
622-2100030002AFECA0CVA IKLINNINNIG1000000000059120110111813410020 11011113510 0
623-2100040000AFECA0CVA IKLINNINNIG1000000000479040110111313410000 KBC-INVESTERINGSKREDIET 737-6543210-21 11011113510 0
624-2100040001AFECA0CVA IKLINNINNIG1000000000419920110111813410660 11011113500 0
625-2100040002AFECA0CVA IKLINNINNIG1000000000059120110111813410020 11011113510 0
626-2100050000AOGM00160BSCTOBOGOVER0000000000063740110111001500000TERUGGAVE 37232481 8400083296 . 11011113501 0
627+2100030000AFECA0CVA IKLINNINNIG1000000000479040110114313410000 KBC-INVESTERINGSKREDIET 737-6543210-21 11011413510 0
628+2100030001AFECA0CVA IKLINNINNIG1000000000419920110114813410660 11011413500 0
629+2100030002AFECA0CVA IKLINNINNIG1000000000059120110114813410020 11011413510 0
630+2100040000AFECA0CVA IKLINNINNIG1000000000479040110114313410000 KBC-INVESTERINGSKREDIET 737-6543210-21 11011413510 0
631+2100040001AFECA0CVA IKLINNINNIG1000000000419920110114813410660 11011413500 0
632+2100040002AFECA0CVA IKLINNINNIG1000000000059120110114813410020 11011413510 0
633+2100050000AOGM00160BSCTOBOGOVER0000000000063740110114001500000TERUGGAVE 37232481 8400083296 . 11011413501 0
634 2200050000 362/363 KREDBEBB 1 0
635 2300050000BE43730004200601 KBC VERZEKERINGEN NV 0 1
636 3100050001AOGM00160BSCTOBOGOVER001500001001KBC VERZEKERINGEN NV 1 0
637 3200050001VAN OVERSTRAETENPLEIN 2 3000 LEUVEN 0 0
638-8135BE44734024486445 EUR0000000013527810110111 0
639+8135BE44734024486445 EUR0000000013527810110114 0
640 9 000022000000001393080000000003108190 2
641
642=== added directory 'l10n_be_coda/tests'
643=== added file 'l10n_be_coda/tests/__init__.py'
644--- l10n_be_coda/tests/__init__.py 1970-01-01 00:00:00 +0000
645+++ l10n_be_coda/tests/__init__.py 2014-04-02 04:28:41 +0000
646@@ -0,0 +1,4 @@
647+from . import test_import_bank_statement
648+checks = [
649+ test_import_bank_statement
650+]
651
652=== added file 'l10n_be_coda/tests/test_import_bank_statement.py'
653--- l10n_be_coda/tests/test_import_bank_statement.py 1970-01-01 00:00:00 +0000
654+++ l10n_be_coda/tests/test_import_bank_statement.py 2014-04-02 04:28:41 +0000
655@@ -0,0 +1,39 @@
656+from openerp.tests.common import TransactionCase
657+from openerp.modules.module import get_module_resource
658+
659+class TestCodaFile(TransactionCase):
660+ """Tests for import bank statement coda file format (account.bank.statement.import)
661+ """
662+
663+ def setUp(self):
664+ super(TestCodaFile, self).setUp()
665+ self.statement_import_model = self.registry('account.bank.statement.import')
666+ self.bank_statement_model = self.registry('account.bank.statement')
667+
668+ def test_coda_file_import(self):
669+ cr, uid = self.cr, self.uid
670+ bank_temp_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'account', 'conf_bnk')
671+ partner_id_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'base', 'main_partner')
672+ company_id_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'base', 'main_company')
673+ self.bank_temp_id = bank_temp_ref and bank_temp_ref[1] or False
674+ self.partner_id = partner_id_ref and partner_id_ref[1] or False
675+ self.company_id = company_id_ref and company_id_ref[1] or False
676+ coda_file_path = get_module_resource('l10n_be_coda', 'test_coda_file', 'Ontvangen_CODA.2013-01-11-18.59.15.txt')
677+ coda_file = open(coda_file_path, 'rb').read().encode('base64')
678+ bank_account_id = self.registry('res.partner.bank').create(cr, uid, dict(
679+ state = 'bank',
680+ acc_number = 'BE33737018595246',
681+ bank_name = 'Reserve',
682+ partner_id = self.partner_id,
683+ company_id = self.company_id
684+ ))
685+ bank_statement_id = self.statement_import_model.create(cr, uid, dict(
686+ file_type = 'coda',
687+ data_file = coda_file,
688+ account_id = self.bank_temp_id
689+ ))
690+ self.statement_import_model.parse_file(cr, uid, [bank_statement_id])
691+ statement_id = self.bank_statement_model.search(cr, uid, [('name', '=', '135')])[0]
692+ bank_st_record = self.bank_statement_model.browse(cr, uid, statement_id)
693+ self.assertEquals(bank_st_record.balance_start, 11812.70)
694+ self.assertEquals(bank_st_record.balance_end_real, 13527.81)
695
696=== modified file 'l10n_be_coda/wizard/account_coda_import.py'
697--- l10n_be_coda/wizard/account_coda_import.py 2013-10-27 12:31:04 +0000
698+++ l10n_be_coda/wizard/account_coda_import.py 2014-04-02 04:28:41 +0000
699@@ -30,44 +30,21 @@
700
701 _logger = logging.getLogger(__name__)
702
703-class account_coda_import(osv.osv_memory):
704- _name = 'account.coda.import'
705+from openerp.addons.account import account_bank_statement_import as coda_ibs
706+
707+coda_ibs._IMPORT_FILE_TYPE.append(('coda', 'CODA'))
708+
709+class account_bank_statement_import(osv.TransientModel):
710+ _inherit = "account.bank.statement.import"
711 _description = 'Import CODA File'
712 _columns = {
713- 'coda_data': fields.binary('CODA File', required=True),
714- 'coda_fname': fields.char('CODA Filename', size=128, required=True),
715+ 'file_type': fields.selection(coda_ibs._IMPORT_FILE_TYPE, 'File Type'),
716 'note': fields.text('Log'),
717- 'temporary_account_id': fields.many2one('account.account', 'Temporary Account', domain="[('type','!=','view')]", help="It acts as a temporary account for general amount", required=True),
718- }
719-
720- def _get_default_tmp_account(self, cr, uid, context):
721- tmp_accounts = self.pool.get('account.account').search(cr, uid, [('code', '=', '490000')])
722- if tmp_accounts and len(tmp_accounts) > 0:
723- tmp_account_id = tmp_accounts[0]
724- else:
725- tmp_account_id = False
726- return tmp_account_id
727-
728- _defaults = {
729- 'coda_fname': lambda *a: '',
730- 'temporary_account_id': _get_default_tmp_account,
731- }
732-
733- def coda_parsing(self, cr, uid, ids, context=None, batch=False, codafile=None, codafilename=None):
734+ }
735+
736+ def process_coda(self, cr, uid, codafile, journal_id=False, account_id=False, context=None):
737 if context is None:
738 context = {}
739- if batch:
740- codafile = str(codafile)
741- codafilename = codafilename
742- else:
743- data = self.browse(cr, uid, ids)[0]
744- try:
745- codafile = data.coda_data
746- codafilename = data.coda_fname
747- temporaryaccount = data.temporary_account_id.id
748- except:
749- raise osv.except_osv(_('Error'), _('Wizard in incorrect state. Please hit the Cancel button'))
750- return {}
751 recordlist = unicode(base64.decodestring(codafile), 'windows-1252', 'strict').split('\n')
752 statements = []
753 for line in recordlist:
754@@ -254,6 +231,7 @@
755 statement['balance_end_real'] = statement['balance_start'] + statement['balancePlus'] - statement['balanceMin']
756 for i, statement in enumerate(statements):
757 statement['coda_note'] = ''
758+ statement_line = []
759 balance_start_check_date = (len(statement['lines']) > 0 and statement['lines'][0]['entryDate']) or statement['date']
760 cr.execute('SELECT balance_end_real \
761 FROM account_bank_statement \
762@@ -278,7 +256,6 @@
763 'balance_start': statement['balance_start'],
764 'balance_end_real': statement['balance_end_real'],
765 }
766- statement['id'] = self.pool.get('account.bank.statement').create(cr, uid, data, context=context)
767 for line in statement['lines']:
768 if line['type'] == 'information':
769 statement['coda_note'] = "\n".join([statement['coda_note'], line['type'].title() + ' with Ref. ' + str(line['ref']), 'Date: ' + str(line['entryDate']), 'Communication: ' + line['communication'], ''])
770@@ -372,12 +349,12 @@
771 if partner.supplier:
772 line['transaction_type'] = 'supplier'
773 if not partner and not invoice:
774- line['account'] = temporaryaccount
775+ line['account'] = account_id
776 if 'communication' in line and line['communication'] != '':
777 note.append(_('Communication') + ': ' + line['communication'])
778 if 'voucher_id' not in line:
779 line['voucher_id'] = None
780- data = {
781+ line_data = {
782 'name': line['name'],
783 'note': "\n".join(note),
784 'date': line['entryDate'],
785@@ -385,28 +362,16 @@
786 'type': line['transaction_type'],
787 'partner_id': partner_id,
788 'account_id': line['account'],
789- 'statement_id': statement['id'],
790 'ref': line['ref'],
791 'sequence': line['sequence'],
792 'voucher_id': line['voucher_id'],
793 'coda_account_number': line['counterpartyNumber'],
794 }
795- self.pool.get('account.bank.statement.line').create(cr, uid, data, context=context)
796+ statement_line.append((0, 0, line_data))
797 if statement['coda_note'] != '':
798- self.pool.get('account.bank.statement').write(cr, uid, [statement['id']], {'coda_note': statement['coda_note']}, context=context)
799- model, action_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account', 'action_bank_statement_tree')
800- action = self.pool[model].browse(cr, uid, action_id, context=context)
801- return {
802- 'name': action.name,
803- 'view_type': action.view_type,
804- 'view_mode': action.view_mode,
805- 'res_model': action.res_model,
806- 'domain': action.domain,
807- 'context': action.context,
808- 'type': 'ir.actions.act_window',
809- 'search_view_id': action.search_view_id.id,
810- 'views': [(v.view_id.id, v.view_mode) for v in action.view_ids]
811- }
812+ data.update({'coda_note': statement['coda_note']})
813+ data.update({'journal_id': journal_id, 'line_ids' : statement_line})
814+ return data
815
816
817 def rmspaces(s):

Subscribers

People subscribed via source and target branches

to all changes: