Merge lp:~therp-nl/banking-addons/6.1-account_banking_nl_ing_mt940 into lp:banking-addons/6.1
- 6.1-account_banking_nl_ing_mt940
- Merge into 6.1
Status: | Merged |
---|---|
Merged at revision: | 195 |
Proposed branch: | lp:~therp-nl/banking-addons/6.1-account_banking_nl_ing_mt940 |
Merge into: | lp:banking-addons/6.1 |
Diff against target: |
502 lines (+460/-0) 6 files modified
account_banking_mt940/__init__.py (+21/-0) account_banking_mt940/__openerp__.py (+53/-0) account_banking_mt940/mt940.py (+217/-0) account_banking_nl_ing_mt940/__init__.py (+21/-0) account_banking_nl_ing_mt940/__openerp__.py (+48/-0) account_banking_nl_ing_mt940/account_banking_nl_ing_mt940.py (+100/-0) |
To merge this branch: | bzr merge lp:~therp-nl/banking-addons/6.1-account_banking_nl_ing_mt940 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Guewen Baconnier @ Camptocamp | Approve | ||
Stefan Rijnhart (Opener) | Approve | ||
Review via email: mp+208430@code.launchpad.net |
Commit message
Description of the change
This adds a generic mt940 parser (account_
The hope would be that account_
- 197. By Holger Brunn (Therp)
-
[FIX] typos
- 198. By Holger Brunn (Therp)
-
[IMP] code formatting, forgotten comment
Stefan Rijnhart (Opener) (stefan-opener) wrote : | # |
- 199. By Holger Brunn (Therp)
-
[FIX] typos, manifest formatting, comments
[RFR] allow using custom mem_bank_{statement, transaction} s in a
convenient way
Holger Brunn (Therp) (hbrunn) wrote : | # |
thanks for the points, I fixed a typo and added a minor refactoring
Stefan Rijnhart (Opener) (stefan-opener) wrote : | # |
Thanks. I have not been able to find a sample statement file for this particular bank, so I won't be able to test. But the code looks excellent so it will have to do. Maybe there are other reviewers who have access to ING structured MT940 statement files?
- 200. By Holger Brunn (Therp)
-
[FIX] pass cr to refactored method
- 201. By Holger Brunn (Therp)
-
[ADD] allow transaction without remote_account
[ADD] get some information for unstructured mt940
Guewen Baconnier @ Camptocamp (gbaconnier-c2c) wrote : | # |
> This addon provides a generic parser for MT940 files. Given that MT940 is a
> non-open non-standard of pure evil in the way that every bank cooks up its own
> interpretation of it, this addon alone won't help you much. It is rather
> intended to be used by other addons to implement the dialect specific to a
> certain bank.
hmmm I trust you and will try to stay away of this evil dialect.
Holger Brunn (Therp) (hbrunn) wrote : | # |
well, fortunately you have account_
Guewen Baconnier @ Camptocamp (gbaconnier-c2c) wrote : | # |
I will remember of that, thanks ;-)
Stefan Rijnhart (Opener) (stefan-opener) wrote : | # |
Merged into 6.1, and cherrypicked unmodified in 7.0.
Preview Diff
1 | === added directory 'account_banking_mt940' | |||
2 | === added file 'account_banking_mt940/__init__.py' | |||
3 | --- account_banking_mt940/__init__.py 1970-01-01 00:00:00 +0000 | |||
4 | +++ account_banking_mt940/__init__.py 2014-02-28 13:34:37 +0000 | |||
5 | @@ -0,0 +1,21 @@ | |||
6 | 1 | # -*- coding: utf-8 -*- | ||
7 | 2 | ############################################################################## | ||
8 | 3 | # | ||
9 | 4 | # OpenERP, Open Source Management Solution | ||
10 | 5 | # This module copyright (C) 2014 Therp BV (<http://therp.nl>). | ||
11 | 6 | # | ||
12 | 7 | # This program is free software: you can redistribute it and/or modify | ||
13 | 8 | # it under the terms of the GNU Affero General Public License as | ||
14 | 9 | # published by the Free Software Foundation, either version 3 of the | ||
15 | 10 | # License, or (at your option) any later version. | ||
16 | 11 | # | ||
17 | 12 | # This program is distributed in the hope that it will be useful, | ||
18 | 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19 | 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
20 | 15 | # GNU Affero General Public License for more details. | ||
21 | 16 | # | ||
22 | 17 | # You should have received a copy of the GNU Affero General Public License | ||
23 | 18 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
24 | 19 | # | ||
25 | 20 | ############################################################################## | ||
26 | 21 | from . import mt940 | ||
27 | 0 | 22 | ||
28 | === added file 'account_banking_mt940/__openerp__.py' | |||
29 | --- account_banking_mt940/__openerp__.py 1970-01-01 00:00:00 +0000 | |||
30 | +++ account_banking_mt940/__openerp__.py 2014-02-28 13:34:37 +0000 | |||
31 | @@ -0,0 +1,53 @@ | |||
32 | 1 | # -*- coding: utf-8 -*- | ||
33 | 2 | ############################################################################## | ||
34 | 3 | # | ||
35 | 4 | # OpenERP, Open Source Management Solution | ||
36 | 5 | # This module copyright (C) 2014 Therp BV (<http://therp.nl>). | ||
37 | 6 | # | ||
38 | 7 | # This program is free software: you can redistribute it and/or modify | ||
39 | 8 | # it under the terms of the GNU Affero General Public License as | ||
40 | 9 | # published by the Free Software Foundation, either version 3 of the | ||
41 | 10 | # License, or (at your option) any later version. | ||
42 | 11 | # | ||
43 | 12 | # This program is distributed in the hope that it will be useful, | ||
44 | 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
45 | 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
46 | 15 | # GNU Affero General Public License for more details. | ||
47 | 16 | # | ||
48 | 17 | # You should have received a copy of the GNU Affero General Public License | ||
49 | 18 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
50 | 19 | # | ||
51 | 20 | ############################################################################## | ||
52 | 21 | { | ||
53 | 22 | "name" : "MT940", | ||
54 | 23 | "version" : "1.0", | ||
55 | 24 | "author" : "Therp BV", | ||
56 | 25 | "complexity": "expert", | ||
57 | 26 | "description": """ | ||
58 | 27 | This addon provides a generic parser for MT940 files. Given that MT940 is a | ||
59 | 28 | non-open non-standard of pure evil in the way that every bank cooks up its own | ||
60 | 29 | interpretation of it, this addon alone won't help you much. It is rather | ||
61 | 30 | intended to be used by other addons to implement the dialect specific to a | ||
62 | 31 | certain bank. | ||
63 | 32 | |||
64 | 33 | See account_banking_nl_ing_mt940 for an example on how to use it. | ||
65 | 34 | """, | ||
66 | 35 | "category" : "Dependency", | ||
67 | 36 | "depends" : [ | ||
68 | 37 | 'account_banking', | ||
69 | 38 | ], | ||
70 | 39 | "data" : [ | ||
71 | 40 | ], | ||
72 | 41 | "js": [ | ||
73 | 42 | ], | ||
74 | 43 | "css": [ | ||
75 | 44 | ], | ||
76 | 45 | "qweb": [ | ||
77 | 46 | ], | ||
78 | 47 | "auto_install": False, | ||
79 | 48 | "installable": True, | ||
80 | 49 | "application": False, | ||
81 | 50 | "external_dependencies" : { | ||
82 | 51 | 'python' : [], | ||
83 | 52 | }, | ||
84 | 53 | } | ||
85 | 0 | 54 | ||
86 | === added file 'account_banking_mt940/mt940.py' | |||
87 | --- account_banking_mt940/mt940.py 1970-01-01 00:00:00 +0000 | |||
88 | +++ account_banking_mt940/mt940.py 2014-02-28 13:34:37 +0000 | |||
89 | @@ -0,0 +1,217 @@ | |||
90 | 1 | #!/usr/bin/env python2 | ||
91 | 2 | # -*- coding: utf-8 -*- | ||
92 | 3 | ############################################################################## | ||
93 | 4 | # | ||
94 | 5 | # OpenERP, Open Source Management Solution | ||
95 | 6 | # This module copyright (C) 2014 Therp BV (<http://therp.nl>). | ||
96 | 7 | # | ||
97 | 8 | # This program is free software: you can redistribute it and/or modify | ||
98 | 9 | # it under the terms of the GNU Affero General Public License as | ||
99 | 10 | # published by the Free Software Foundation, either version 3 of the | ||
100 | 11 | # License, or (at your option) any later version. | ||
101 | 12 | # | ||
102 | 13 | # This program is distributed in the hope that it will be useful, | ||
103 | 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
104 | 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
105 | 16 | # GNU Affero General Public License for more details. | ||
106 | 17 | # | ||
107 | 18 | # You should have received a copy of the GNU Affero General Public License | ||
108 | 19 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
109 | 20 | # | ||
110 | 21 | ############################################################################## | ||
111 | 22 | |||
112 | 23 | """ | ||
113 | 24 | Parser for MT940 format files | ||
114 | 25 | """ | ||
115 | 26 | import re | ||
116 | 27 | import datetime | ||
117 | 28 | import logging | ||
118 | 29 | try: | ||
119 | 30 | from openerp.addons.account_banking.parsers.models import\ | ||
120 | 31 | mem_bank_statement, mem_bank_transaction | ||
121 | 32 | from openerp.tools.misc import DEFAULT_SERVER_DATE_FORMAT | ||
122 | 33 | except ImportError: | ||
123 | 34 | #this allows us to run this file standalone, see __main__ at the end | ||
124 | 35 | class mem_bank_statement: | ||
125 | 36 | def __init__(self): | ||
126 | 37 | self.transactions = [] | ||
127 | 38 | class mem_bank_transaction: | ||
128 | 39 | pass | ||
129 | 40 | DEFAULT_SERVER_DATE_FORMAT = "%Y-%m-%d" | ||
130 | 41 | |||
131 | 42 | class MT940(object): | ||
132 | 43 | '''Inherit this class in your account_banking.parsers.models.parser, | ||
133 | 44 | define functions to handle the tags you need to handle and adjust static | ||
134 | 45 | variables as needed. | ||
135 | 46 | |||
136 | 47 | Note that order matters: You need to do your_parser(MT940, parser), not the | ||
137 | 48 | other way around! | ||
138 | 49 | |||
139 | 50 | At least, you should override handle_tag_61 and handle_tag_86. Don't forget | ||
140 | 51 | to call super. | ||
141 | 52 | handle_tag_* functions receive the remainder of the the line (that is, | ||
142 | 53 | without ':XX:') and are supposed to write into self.current_transaction''' | ||
143 | 54 | |||
144 | 55 | header_lines = 3 | ||
145 | 56 | '''One file can contain multiple statements, each with its own poorly | ||
146 | 57 | documented header. For now, the best thing to do seems to skip that''' | ||
147 | 58 | |||
148 | 59 | footer_regex = '^-}$' | ||
149 | 60 | footer_regex = '^-XXX$' | ||
150 | 61 | 'The line that denotes end of message, we need to create a new statement' | ||
151 | 62 | |||
152 | 63 | tag_regex = '^:[0-9]{2}[A-Z]*:' | ||
153 | 64 | 'The beginning of a record, should be anchored to beginning of the line' | ||
154 | 65 | |||
155 | 66 | def __init__(self, *args, **kwargs): | ||
156 | 67 | super(MT940, self).__init__(*args, **kwargs) | ||
157 | 68 | 'state variables' | ||
158 | 69 | self.current_statement = None | ||
159 | 70 | 'type account_banking.parsers.models.mem_bank_statement' | ||
160 | 71 | self.current_transaction = None | ||
161 | 72 | 'type account_banking.parsers.models.mem_bank_transaction' | ||
162 | 73 | self.statements = [] | ||
163 | 74 | 'parsed statements up to now' | ||
164 | 75 | |||
165 | 76 | def parse(self, cr, data): | ||
166 | 77 | 'implements account_banking.parsers.models.parser.parse()' | ||
167 | 78 | iterator = data.split('\r\n').__iter__() | ||
168 | 79 | line = None | ||
169 | 80 | record_line = '' | ||
170 | 81 | try: | ||
171 | 82 | while True: | ||
172 | 83 | if not self.current_statement: | ||
173 | 84 | self.handle_header(cr, line, iterator) | ||
174 | 85 | line = iterator.next() | ||
175 | 86 | if not self.is_tag(cr, line) and not self.is_footer(cr, line): | ||
176 | 87 | record_line = self.append_continuation_line( | ||
177 | 88 | cr, record_line, line) | ||
178 | 89 | continue | ||
179 | 90 | if record_line: | ||
180 | 91 | self.handle_record(cr, record_line) | ||
181 | 92 | if self.is_footer(cr, line): | ||
182 | 93 | self.handle_footer(cr, line, iterator) | ||
183 | 94 | record_line = '' | ||
184 | 95 | continue | ||
185 | 96 | record_line = line | ||
186 | 97 | except StopIteration: | ||
187 | 98 | pass | ||
188 | 99 | return self.statements | ||
189 | 100 | |||
190 | 101 | def append_continuation_line(self, cr, line, continuation_line): | ||
191 | 102 | '''append a continuation line for a multiline record. | ||
192 | 103 | Override and do data cleanups as necessary.''' | ||
193 | 104 | return line + continuation_line | ||
194 | 105 | |||
195 | 106 | def create_statement(self, cr): | ||
196 | 107 | '''create a mem_bank_statement - override if you need a custom | ||
197 | 108 | implementation''' | ||
198 | 109 | return mem_bank_statement() | ||
199 | 110 | |||
200 | 111 | def create_transaction(self, cr): | ||
201 | 112 | '''create a mem_bank_transaction - override if you need a custom | ||
202 | 113 | implementation''' | ||
203 | 114 | return mem_bank_transaction() | ||
204 | 115 | |||
205 | 116 | def is_footer(self, cr, line): | ||
206 | 117 | '''determine if a line is the footer of a statement''' | ||
207 | 118 | return line and bool(re.match(self.footer_regex, line)) | ||
208 | 119 | |||
209 | 120 | def is_tag(self, cr, line): | ||
210 | 121 | '''determine if a line has a tag''' | ||
211 | 122 | return line and bool(re.match(self.tag_regex, line)) | ||
212 | 123 | |||
213 | 124 | def handle_header(self, cr, line, iterator): | ||
214 | 125 | '''skip header lines, create current statement''' | ||
215 | 126 | for i in range(self.header_lines): | ||
216 | 127 | iterator.next() | ||
217 | 128 | self.current_statement = self.create_statement(cr) | ||
218 | 129 | |||
219 | 130 | def handle_footer(self, cr, line, iterator): | ||
220 | 131 | '''add current statement to list, reset state''' | ||
221 | 132 | self.statements.append(self.current_statement) | ||
222 | 133 | self.current_statement = None | ||
223 | 134 | |||
224 | 135 | def handle_record(self, cr, line): | ||
225 | 136 | '''find a function to handle the record represented by line''' | ||
226 | 137 | tag_match = re.match(self.tag_regex, line) | ||
227 | 138 | tag = tag_match.group(0).strip(':') | ||
228 | 139 | if not hasattr(self, 'handle_tag_%s' % tag): | ||
229 | 140 | logging.error('Unknown tag %s', tag) | ||
230 | 141 | logging.error(line) | ||
231 | 142 | return | ||
232 | 143 | handler = getattr(self, 'handle_tag_%s' % tag) | ||
233 | 144 | handler(cr, line[tag_match.end():]) | ||
234 | 145 | |||
235 | 146 | def handle_tag_20(self, cr, data): | ||
236 | 147 | '''ignore reference number''' | ||
237 | 148 | pass | ||
238 | 149 | |||
239 | 150 | def handle_tag_25(self, cr, data): | ||
240 | 151 | '''get account owner information''' | ||
241 | 152 | self.current_statement.local_account = data | ||
242 | 153 | |||
243 | 154 | def handle_tag_28C(self, cr, data): | ||
244 | 155 | '''get sequence number _within_this_batch_ - this alone | ||
245 | 156 | doesn't provide a unique id!''' | ||
246 | 157 | self.current_statement.id = data | ||
247 | 158 | |||
248 | 159 | def handle_tag_60F(self, cr, data): | ||
249 | 160 | '''get start balance and currency''' | ||
250 | 161 | self.current_statement.local_currency = data[7:10] | ||
251 | 162 | self.current_statement.date = str2date(data[1:7]) | ||
252 | 163 | self.current_statement.start_balance = \ | ||
253 | 164 | (1 if data[0] == 'C' else -1) * str2float(data[10:]) | ||
254 | 165 | self.current_statement.id = '%s/%s' % ( | ||
255 | 166 | self.current_statement.date.strftime('%Y'), | ||
256 | 167 | self.current_statement.id) | ||
257 | 168 | |||
258 | 169 | def handle_tag_62F(self, cr, data): | ||
259 | 170 | '''get ending balance''' | ||
260 | 171 | self.current_statement.end_balance = \ | ||
261 | 172 | (1 if data[0] == 'C' else -1) * str2float(data[10:]) | ||
262 | 173 | |||
263 | 174 | def handle_tag_64(self, cr, data): | ||
264 | 175 | '''get current balance in currency''' | ||
265 | 176 | pass | ||
266 | 177 | |||
267 | 178 | def handle_tag_65(self, cr, data): | ||
268 | 179 | '''get future balance in currency''' | ||
269 | 180 | pass | ||
270 | 181 | |||
271 | 182 | def handle_tag_61(self, cr, data): | ||
272 | 183 | '''get transaction values''' | ||
273 | 184 | transaction = self.create_transaction(cr) | ||
274 | 185 | self.current_statement.transactions.append(transaction) | ||
275 | 186 | self.current_transaction = transaction | ||
276 | 187 | transaction.execution_date = str2date(data[:6]) | ||
277 | 188 | transaction.effective_date = str2date(data[:6]) | ||
278 | 189 | '...and the rest already is highly bank dependent' | ||
279 | 190 | |||
280 | 191 | def handle_tag_86(self, cr, data): | ||
281 | 192 | '''details for previous transaction, here most differences between | ||
282 | 193 | banks occur''' | ||
283 | 194 | pass | ||
284 | 195 | |||
285 | 196 | 'utility functions' | ||
286 | 197 | def str2date(string, fmt='%y%m%d'): | ||
287 | 198 | return datetime.datetime.strptime(string, fmt) | ||
288 | 199 | |||
289 | 200 | def str2float(string): | ||
290 | 201 | return float(string.replace(',', '.')) | ||
291 | 202 | |||
292 | 203 | 'testing' | ||
293 | 204 | def main(filename): | ||
294 | 205 | parser = MT940() | ||
295 | 206 | parser.parse(None, open(filename, 'r').read()) | ||
296 | 207 | for statement in parser.statements: | ||
297 | 208 | print '''statement found for %(local_account)s at %(date)s | ||
298 | 209 | with %(local_currency)s%(start_balance)s to %(end_balance)s | ||
299 | 210 | ''' % statement.__dict__ | ||
300 | 211 | for transaction in statement.transactions: | ||
301 | 212 | print ''' | ||
302 | 213 | transaction on %(execution_date)s''' % transaction.__dict__ | ||
303 | 214 | |||
304 | 215 | if __name__ == '__main__': | ||
305 | 216 | import sys | ||
306 | 217 | main(*sys.argv[1:]) | ||
307 | 0 | 218 | ||
308 | === added directory 'account_banking_mt940/static' | |||
309 | === added directory 'account_banking_mt940/static/src' | |||
310 | === added directory 'account_banking_mt940/static/src/img' | |||
311 | === added file 'account_banking_mt940/static/src/img/icon.png' | |||
312 | 1 | Binary files account_banking_mt940/static/src/img/icon.png 1970-01-01 00:00:00 +0000 and account_banking_mt940/static/src/img/icon.png 2014-02-28 13:34:37 +0000 differ | 219 | Binary files account_banking_mt940/static/src/img/icon.png 1970-01-01 00:00:00 +0000 and account_banking_mt940/static/src/img/icon.png 2014-02-28 13:34:37 +0000 differ |
313 | === added directory 'account_banking_nl_ing_mt940' | |||
314 | === added file 'account_banking_nl_ing_mt940/__init__.py' | |||
315 | --- account_banking_nl_ing_mt940/__init__.py 1970-01-01 00:00:00 +0000 | |||
316 | +++ account_banking_nl_ing_mt940/__init__.py 2014-02-28 13:34:37 +0000 | |||
317 | @@ -0,0 +1,21 @@ | |||
318 | 1 | # -*- coding: utf-8 -*- | ||
319 | 2 | ############################################################################## | ||
320 | 3 | # | ||
321 | 4 | # OpenERP, Open Source Management Solution | ||
322 | 5 | # This module copyright (C) 2014 Therp BV (<http://therp.nl>). | ||
323 | 6 | # | ||
324 | 7 | # This program is free software: you can redistribute it and/or modify | ||
325 | 8 | # it under the terms of the GNU Affero General Public License as | ||
326 | 9 | # published by the Free Software Foundation, either version 3 of the | ||
327 | 10 | # License, or (at your option) any later version. | ||
328 | 11 | # | ||
329 | 12 | # This program is distributed in the hope that it will be useful, | ||
330 | 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
331 | 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
332 | 15 | # GNU Affero General Public License for more details. | ||
333 | 16 | # | ||
334 | 17 | # You should have received a copy of the GNU Affero General Public License | ||
335 | 18 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
336 | 19 | # | ||
337 | 20 | ############################################################################## | ||
338 | 21 | from . import account_banking_nl_ing_mt940 | ||
339 | 0 | 22 | ||
340 | === added file 'account_banking_nl_ing_mt940/__openerp__.py' | |||
341 | --- account_banking_nl_ing_mt940/__openerp__.py 1970-01-01 00:00:00 +0000 | |||
342 | +++ account_banking_nl_ing_mt940/__openerp__.py 2014-02-28 13:34:37 +0000 | |||
343 | @@ -0,0 +1,48 @@ | |||
344 | 1 | # -*- coding: utf-8 -*- | ||
345 | 2 | ############################################################################## | ||
346 | 3 | # | ||
347 | 4 | # OpenERP, Open Source Management Solution | ||
348 | 5 | # This module copyright (C) 2014 Therp BV (<http://therp.nl>). | ||
349 | 6 | # | ||
350 | 7 | # This program is free software: you can redistribute it and/or modify | ||
351 | 8 | # it under the terms of the GNU Affero General Public License as | ||
352 | 9 | # published by the Free Software Foundation, either version 3 of the | ||
353 | 10 | # License, or (at your option) any later version. | ||
354 | 11 | # | ||
355 | 12 | # This program is distributed in the hope that it will be useful, | ||
356 | 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
357 | 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
358 | 15 | # GNU Affero General Public License for more details. | ||
359 | 16 | # | ||
360 | 17 | # You should have received a copy of the GNU Affero General Public License | ||
361 | 18 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
362 | 19 | # | ||
363 | 20 | ############################################################################## | ||
364 | 21 | { | ||
365 | 22 | "name" : "MT940 import for Dutch ING", | ||
366 | 23 | "version" : "1.0", | ||
367 | 24 | "author" : "Therp BV", | ||
368 | 25 | "complexity": "normal", | ||
369 | 26 | "description": """ | ||
370 | 27 | This addon imports the structured MT940 format as offered by the Dutch ING | ||
371 | 28 | bank. | ||
372 | 29 | """, | ||
373 | 30 | "category" : "Account Banking", | ||
374 | 31 | "depends" : [ | ||
375 | 32 | 'account_banking_mt940', | ||
376 | 33 | ], | ||
377 | 34 | "data" : [ | ||
378 | 35 | ], | ||
379 | 36 | "js": [ | ||
380 | 37 | ], | ||
381 | 38 | "css": [ | ||
382 | 39 | ], | ||
383 | 40 | "qweb": [ | ||
384 | 41 | ], | ||
385 | 42 | "auto_install": False, | ||
386 | 43 | "installable": True, | ||
387 | 44 | "application": False, | ||
388 | 45 | "external_dependencies" : { | ||
389 | 46 | 'python' : [], | ||
390 | 47 | }, | ||
391 | 48 | } | ||
392 | 0 | 49 | ||
393 | === added file 'account_banking_nl_ing_mt940/account_banking_nl_ing_mt940.py' | |||
394 | --- account_banking_nl_ing_mt940/account_banking_nl_ing_mt940.py 1970-01-01 00:00:00 +0000 | |||
395 | +++ account_banking_nl_ing_mt940/account_banking_nl_ing_mt940.py 2014-02-28 13:34:37 +0000 | |||
396 | @@ -0,0 +1,100 @@ | |||
397 | 1 | # -*- coding: utf-8 -*- | ||
398 | 2 | ############################################################################## | ||
399 | 3 | # | ||
400 | 4 | # OpenERP, Open Source Management Solution | ||
401 | 5 | # This module copyright (C) 2014 Therp BV (<http://therp.nl>). | ||
402 | 6 | # | ||
403 | 7 | # This program is free software: you can redistribute it and/or modify | ||
404 | 8 | # it under the terms of the GNU Affero General Public License as | ||
405 | 9 | # published by the Free Software Foundation, either version 3 of the | ||
406 | 10 | # License, or (at your option) any later version. | ||
407 | 11 | # | ||
408 | 12 | # This program is distributed in the hope that it will be useful, | ||
409 | 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
410 | 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
411 | 15 | # GNU Affero General Public License for more details. | ||
412 | 16 | # | ||
413 | 17 | # You should have received a copy of the GNU Affero General Public License | ||
414 | 18 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
415 | 19 | # | ||
416 | 20 | ############################################################################## | ||
417 | 21 | import re | ||
418 | 22 | from openerp.tools.translate import _ | ||
419 | 23 | from openerp.addons.account_banking.parsers.models import parser,\ | ||
420 | 24 | mem_bank_transaction | ||
421 | 25 | from openerp.addons.account_banking_mt940.mt940 import MT940, str2float | ||
422 | 26 | |||
423 | 27 | |||
424 | 28 | class transaction(mem_bank_transaction): | ||
425 | 29 | def is_valid(self): | ||
426 | 30 | '''allow transactions without remote account''' | ||
427 | 31 | return bool(self.execution_date) and bool(self.transferred_amount) | ||
428 | 32 | |||
429 | 33 | class IngMT940Parser(MT940, parser): | ||
430 | 34 | name = _('ING MT940 (structured)') | ||
431 | 35 | country_code = 'NL' | ||
432 | 36 | code = 'INT_MT940_STRUC' | ||
433 | 37 | |||
434 | 38 | tag_61_regex = re.compile( | ||
435 | 39 | '^(?P<date>\d{6})(?P<sign>[CD])(?P<amount>\d+,\d{2})N(?P<type>\d{3})' | ||
436 | 40 | '(?P<reference>\w{1,16})') | ||
437 | 41 | |||
438 | 42 | def create_transaction(self, cr): | ||
439 | 43 | return transaction() | ||
440 | 44 | |||
441 | 45 | def handle_tag_60F(self, cr, data): | ||
442 | 46 | super(IngMT940Parser, self).handle_tag_60F(cr, data) | ||
443 | 47 | self.current_statement.id = '%s-%s' % ( | ||
444 | 48 | self.get_unique_account_identifier( | ||
445 | 49 | cr, self.current_statement.local_account), | ||
446 | 50 | self.current_statement.id) | ||
447 | 51 | |||
448 | 52 | def handle_tag_61(self, cr, data): | ||
449 | 53 | super(IngMT940Parser, self).handle_tag_61(cr, data) | ||
450 | 54 | parsed_data = self.tag_61_regex.match(data).groupdict() | ||
451 | 55 | self.current_transaction.transferred_amount = \ | ||
452 | 56 | (-1 if parsed_data['sign'] == 'D' else 1) * str2float( | ||
453 | 57 | parsed_data['amount']) | ||
454 | 58 | self.current_transaction.reference = parsed_data['reference'] | ||
455 | 59 | |||
456 | 60 | def handle_tag_86(self, cr, data): | ||
457 | 61 | if not self.current_transaction: | ||
458 | 62 | return | ||
459 | 63 | super(IngMT940Parser, self).handle_tag_86(cr, data) | ||
460 | 64 | codewords = ['RTRN', 'BENM', 'ORDP', 'CSID', 'BUSP', 'MARF', 'EREF', | ||
461 | 65 | 'PREF', 'REMI', 'ID', 'PURP', 'ULTB', 'ULTD'] | ||
462 | 66 | subfields = {} | ||
463 | 67 | current_codeword = None | ||
464 | 68 | for word in data.split('/'): | ||
465 | 69 | if not word and not current_codeword: | ||
466 | 70 | continue | ||
467 | 71 | if word in codewords: | ||
468 | 72 | current_codeword = word | ||
469 | 73 | subfields[current_codeword] = [] | ||
470 | 74 | continue | ||
471 | 75 | subfields[current_codeword].append(word) | ||
472 | 76 | |||
473 | 77 | if 'BENM' in subfields: | ||
474 | 78 | self.current_transaction.remote_account = subfields['BENM'][0] | ||
475 | 79 | self.current_transaction.remote_bank_bic = subfields['BENM'][1] | ||
476 | 80 | self.current_transaction.remote_owner = subfields['BENM'][2] | ||
477 | 81 | self.current_transaction.remote_owner_city = subfields['BENM'][3] | ||
478 | 82 | |||
479 | 83 | if 'ORDP' in subfields: | ||
480 | 84 | self.current_transaction.remote_account = subfields['ORDP'][0] | ||
481 | 85 | self.current_transaction.remote_bank_bic = subfields['ORDP'][1] | ||
482 | 86 | self.current_transaction.remote_owner = subfields['ORDP'][2] | ||
483 | 87 | self.current_transaction.remote_owner_city = subfields['ORDP'][3] | ||
484 | 88 | |||
485 | 89 | if 'REMI' in subfields: | ||
486 | 90 | self.current_transaction.message = '/'.join( | ||
487 | 91 | filter(lambda x: bool(x), subfields['REMI'])) | ||
488 | 92 | |||
489 | 93 | if self.current_transaction.reference in subfields: | ||
490 | 94 | self.current_transaction.reference = ''.join( | ||
491 | 95 | subfields[self.current_transaction.reference]) | ||
492 | 96 | |||
493 | 97 | if not subfields: | ||
494 | 98 | self.current_transaction.message = data | ||
495 | 99 | |||
496 | 100 | self.current_transaction = None | ||
497 | 0 | 101 | ||
498 | === added directory 'account_banking_nl_ing_mt940/static' | |||
499 | === added directory 'account_banking_nl_ing_mt940/static/src' | |||
500 | === added directory 'account_banking_nl_ing_mt940/static/src/img' | |||
501 | === added file 'account_banking_nl_ing_mt940/static/src/img/icon.png' | |||
502 | 1 | Binary files account_banking_nl_ing_mt940/static/src/img/icon.png 1970-01-01 00:00:00 +0000 and account_banking_nl_ing_mt940/static/src/img/icon.png 2014-02-28 13:34:37 +0000 differ | 102 | Binary files account_banking_nl_ing_mt940/static/src/img/icon.png 1970-01-01 00:00:00 +0000 and account_banking_nl_ing_mt940/static/src/img/icon.png 2014-02-28 13:34:37 +0000 differ |
Awesome, finally a generic MT940 parser!
Just two comments from code review, I'll do some testing later on:
- l.122 You might want to add a comment that the exception handling here allows you to execute the file to test the parser outside the context of the OpenERP server (I think).
- l.165 Quite a fashion statement ;-) I know this runs, but you might want to either add two single quotes at the start or remove two at the end so that it looks like a regular docstring.