Fekete Mihai
9 years ago
9 changed files with 327 additions and 0 deletions
-
65account_bank_statement_import_mt940_ro_brd/README.rst
-
5account_bank_statement_import_mt940_ro_brd/__init__.py
-
20account_bank_statement_import_mt940_ro_brd/__openerp__.py
-
28account_bank_statement_import_mt940_ro_brd/account_bank_statement_import.py
-
26account_bank_statement_import_mt940_ro_brd/demo/demo_data.xml
-
143account_bank_statement_import_mt940_ro_brd/mt940.py
-
17account_bank_statement_import_mt940_ro_brd/test_files/test-brd.940
-
5account_bank_statement_import_mt940_ro_brd/tests/__init__.py
-
18account_bank_statement_import_mt940_ro_brd/tests/test_import_bank_statement.py
@ -0,0 +1,65 @@ |
|||||
|
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg |
||||
|
:alt: License: AGPL-3 |
||||
|
|
||||
|
Import MT940 ROMANIAN BRD Bank Statements |
||||
|
========================================= |
||||
|
|
||||
|
This module allows you to import the MT940 files from the Romanian BRD bank |
||||
|
in Odoo as bank statements. |
||||
|
|
||||
|
Installation |
||||
|
============ |
||||
|
|
||||
|
To install this module, you need to: |
||||
|
|
||||
|
* clone the branch 8.0 of the repository https://github.com/OCA/bank-statement-import |
||||
|
* add the path to this repository in your configuration (addons-path) |
||||
|
* update the module list |
||||
|
* search for "MT940 BRD Format Bank Statements Import" in your addons |
||||
|
* install the module |
||||
|
|
||||
|
Known issues / Roadmap |
||||
|
====================== |
||||
|
|
||||
|
* None |
||||
|
|
||||
|
Usage |
||||
|
===== |
||||
|
|
||||
|
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas |
||||
|
:alt: Try me on Runbot |
||||
|
:target: https://runbot.odoo-community.org/runbot/174/8.0 |
||||
|
|
||||
|
Bug Tracker |
||||
|
=========== |
||||
|
|
||||
|
Bugs are tracked on `GitHub Issues <https://github.com/OCA/bank-statement-import/issues>`_. |
||||
|
In case of trouble, please check there if your issue has already been reported. |
||||
|
If you spotted it first, help us smashing it by providing a detailed and welcomed feedback |
||||
|
`here <https://github.com/OCA/bank-statement-import/issues/new?body=module:%20account_bank_statement_import_mt940_ro_brd%0Aversion:%208.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_. |
||||
|
|
||||
|
|
||||
|
Credits |
||||
|
======= |
||||
|
|
||||
|
Contributors |
||||
|
------------ |
||||
|
|
||||
|
* Fekete Mihai <feketemihai@gmail.com> |
||||
|
|
||||
|
Maintainer |
||||
|
---------- |
||||
|
|
||||
|
.. image:: https://odoo-community.org/logo.png |
||||
|
:alt: Odoo Community Association |
||||
|
:target: https://odoo-community.org |
||||
|
|
||||
|
This module is maintained by the OCA. |
||||
|
|
||||
|
OCA, or the Odoo Community Association, is a nonprofit organization whose |
||||
|
mission is to support the collaborative development of Odoo features and |
||||
|
promote its widespread use. |
||||
|
|
||||
|
To contribute to this module, please visit http://odoo-community.org. |
||||
|
This module should make it easy to migrate bank statement import |
||||
|
modules written for earlies versions of Odoo/OpenERP. |
@ -0,0 +1,5 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
# © 2016 Forest and Biomass Services Romania |
||||
|
# See README.rst file on addons root folder for license details |
||||
|
|
||||
|
from . import account_bank_statement_import |
@ -0,0 +1,20 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
# © 2016 Forest and Biomass Services Romania |
||||
|
# See README.rst file on addons root folder for license details |
||||
|
{ |
||||
|
'name': 'MT940 BRD Format Bank Statements Import', |
||||
|
'version': '8.0.0.1.0', |
||||
|
'license': 'AGPL-3', |
||||
|
'author': 'Forest and Biomass Services Romania, ' |
||||
|
'Odoo Community Association (OCA)', |
||||
|
'website': 'https://www.forbiom.eu', |
||||
|
'website': 'www.forbiom.eu', |
||||
|
'category': 'Banking addons', |
||||
|
'depends': [ |
||||
|
'account_bank_statement_import_mt940_base' |
||||
|
], |
||||
|
'demo': [ |
||||
|
'demo/demo_data.xml', |
||||
|
], |
||||
|
'installable': True, |
||||
|
} |
@ -0,0 +1,28 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
# © 2016 Forest and Biomass Services Romania |
||||
|
# See README.rst file on addons root folder for license details |
||||
|
|
||||
|
import logging |
||||
|
from openerp import models, api |
||||
|
from .mt940 import MT940Parser as Parser |
||||
|
|
||||
|
_logger = logging.getLogger(__name__) |
||||
|
|
||||
|
|
||||
|
class AccountBankStatementImport(models.TransientModel): |
||||
|
"""Add parsing of mt940 files to bank statement import.""" |
||||
|
_inherit = 'account.bank.statement.import' |
||||
|
|
||||
|
|
||||
|
def _parse_file(self, cr, uid, data_file, context=None): |
||||
|
"""Parse a MT940 IBAN BRD file.""" |
||||
|
parser = Parser() |
||||
|
try: |
||||
|
_logger.debug("Try parsing with MT940 IBAN BRD.") |
||||
|
return parser.parse(data_file) |
||||
|
except ValueError: |
||||
|
# Returning super will call next candidate: |
||||
|
_logger.debug("Statement file was not a MT940 IBAN BRD file.", |
||||
|
exc_info=True) |
||||
|
return super(AccountBankStatementImport, self)._parse_file( |
||||
|
cr, uid, data_file, context=context) |
@ -0,0 +1,26 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<openerp> |
||||
|
<data> |
||||
|
|
||||
|
<record id="mt940_brd_bank_journal" model="account.journal"> |
||||
|
<field name="name">Bank Journal - (test mt940 BRD)</field> |
||||
|
<field name="code">TBNKMT940BRD</field> |
||||
|
<field name="type">bank</field> |
||||
|
<field name="sequence_id" ref="account.sequence_bank_journal"/> |
||||
|
<field name="default_debit_account_id" ref="account.bnk"/> |
||||
|
<field name="default_credit_account_id" ref="account.bnk"/> |
||||
|
<field name="user_id" ref="base.user_root"/> |
||||
|
</record> |
||||
|
|
||||
|
<record id="mt940_brd_company_bank" model="res.partner.bank"> |
||||
|
<field name="owner_name">Your Company</field> |
||||
|
<field name="acc_number">RO56BRDE360SV52474653600</field> |
||||
|
<field name="partner_id" ref="base.partner_root"></field> |
||||
|
<field name="company_id" ref="base.main_company"></field> |
||||
|
<field name="journal_id" ref="mt940_brd_bank_journal"></field> |
||||
|
<field name="state">bank</field> |
||||
|
<field name="bank" ref="base.res_bank_1"/> |
||||
|
</record> |
||||
|
</data> |
||||
|
|
||||
|
</openerp> |
@ -0,0 +1,143 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
# © 2016 Forest and Biomass Services Romania |
||||
|
# See README.rst file on addons root folder for license details |
||||
|
|
||||
|
import re |
||||
|
from datetime import datetime |
||||
|
from openerp.addons.account_bank_statement_import_mt940_base.mt940 import ( |
||||
|
MT940, str2amount) |
||||
|
|
||||
|
def get_counterpart(transaction, subfield): |
||||
|
"""Get counterpart from transaction. |
||||
|
|
||||
|
Counterpart is often stored in subfield of tag 86. The subfield |
||||
|
can be 31, 32, 33""" |
||||
|
if not subfield: |
||||
|
return # subfield is empty |
||||
|
if len(subfield) >= 1 and subfield[0]: |
||||
|
transaction.remote_account = subfield[0] |
||||
|
if len(subfield) >= 2 and subfield[1]: |
||||
|
transaction.remote_owner = subfield[1] |
||||
|
if len(subfield) >= 3 and subfield[2]: |
||||
|
transaction.remote_owner_tin = subfield[2] |
||||
|
|
||||
|
def get_subfields(data, codewords): |
||||
|
"""Return dictionary with value array for each codeword in data. |
||||
|
|
||||
|
For instance: |
||||
|
data = |
||||
|
000+20TRANSACTIONTYPE+30BANKTRANSACTIONNUMBER+31PARTNERBANKACCOUNT |
||||
|
+32PARTNER+33CUI/CNPTIN |
||||
|
+23TRANSACTIONMESSAGE1+24TRANSACTIONMESSAGE2 |
||||
|
+25TRANSACTIONMESSAGE3+26TRANSACTIONMESSAGE4 |
||||
|
+27TRANSACTIONMESSAGE5 |
||||
|
+61PARTNERADDRESS1+62PARTNERADDRESS2 |
||||
|
codewords = ['20', '23', '24', '25', '26', '27', |
||||
|
'30', '31', '32', '33', '61', '62'] |
||||
|
!!! NOT ALL CODEWORDS ARE PRESENT !!! |
||||
|
Then return subfields = { |
||||
|
'20': [TRANSACTIONTYPE], |
||||
|
'30': [BANKTRANSACTIONNUMBER], |
||||
|
'31': [PARTNERBANKACCOUNT], |
||||
|
'32': [PARTNER], |
||||
|
'33': [TIN], |
||||
|
'23': [TRANSACTIONMESSAGE1], |
||||
|
'24': [TRANSACTIONMESSAGE2], |
||||
|
'25': [TRANSACTIONMESSAGE3], |
||||
|
'26': [TRANSACTIONMESSAGE4], |
||||
|
'27': [TRANSACTIONMESSAGE5], |
||||
|
'61': [PARTNERADDRESS1], |
||||
|
'62': [PARTNERADDRESS2], |
||||
|
} |
||||
|
""" |
||||
|
subfields = {} |
||||
|
current_codeword = None |
||||
|
for word in data.split('+'): |
||||
|
if not word and not current_codeword: |
||||
|
continue |
||||
|
if word[:2] in codewords: |
||||
|
current_codeword = word[:2] |
||||
|
subfields[current_codeword] = [word[2:]] |
||||
|
continue |
||||
|
if current_codeword in subfields: |
||||
|
subfields[current_codeword].append(word[2:]) |
||||
|
return subfields |
||||
|
|
||||
|
def handle_common_subfields(transaction, subfields): |
||||
|
"""Deal with common functionality for tag 86 subfields.""" |
||||
|
# Get counterpart from 31, 32 or 33 subfields: |
||||
|
counterpart_fields = [] |
||||
|
for counterpart_field in ['31', '32', '33']: |
||||
|
if counterpart_field in subfields: |
||||
|
new_value = subfields[counterpart_field][0].replace('CUI/CNP', '') |
||||
|
counterpart_fields.append(new_value) |
||||
|
else: |
||||
|
counterpart_fields.append('') |
||||
|
if counterpart_fields: |
||||
|
get_counterpart(transaction, counterpart_fields) |
||||
|
# REMI: Remitter information (text entered by other party on trans.): |
||||
|
transaction.message = '' |
||||
|
for counterpart_field in ['23', '24', '25', '26', '27']: |
||||
|
if counterpart_field in subfields: |
||||
|
transaction.message += ( |
||||
|
'/'.join(x for x in subfields[counterpart_field] if x)) |
||||
|
# Get transaction reference subfield (might vary): |
||||
|
if transaction.eref in subfields: |
||||
|
transaction.eref = ''.join( |
||||
|
subfields[transaction.eref]) |
||||
|
|
||||
|
class MT940Parser(MT940): |
||||
|
"""Parser for ing MT940 bank statement import files.""" |
||||
|
|
||||
|
tag_61_regex = re.compile( |
||||
|
r'^(?P<date>\d{6})(?P<line_date>\d{0,4})' |
||||
|
r'(?P<sign>[CD])(?P<amount>\d+,\d{2})N(?P<type>.{3})' |
||||
|
r'(?P<reference>\w{1,50})' |
||||
|
) |
||||
|
|
||||
|
def __init__(self): |
||||
|
"""Initialize parser - override at least header_regex.""" |
||||
|
super(MT940Parser, self).__init__() |
||||
|
self.mt940_type = 'BRD' |
||||
|
self.header_lines = 1 # Number of lines to skip |
||||
|
# Do not user $ for end of string below: line contains much |
||||
|
# more data than just the first line. |
||||
|
self.header_regex = '^:20:' # Start of relevant data |
||||
|
|
||||
|
def handle_tag_25(self, data): |
||||
|
"""Local bank account information.""" |
||||
|
data = data.replace('.', '').strip() |
||||
|
self.current_statement.local_account = data |
||||
|
|
||||
|
def handle_tag_28(self, data): |
||||
|
"""Number of BRD bank statement.""" |
||||
|
stmt = self.current_statement |
||||
|
stmt.statement_id = data.replace('.', '').strip() |
||||
|
|
||||
|
def handle_tag_61(self, data): |
||||
|
"""get transaction values""" |
||||
|
super(MT940Parser, self).handle_tag_61(data) |
||||
|
re_61 = self.tag_61_regex.match(data) |
||||
|
if not re_61: |
||||
|
raise ValueError("Cannot parse %s" % data) |
||||
|
parsed_data = re_61.groupdict() |
||||
|
self.current_transaction.transferred_amount = ( |
||||
|
str2amount(parsed_data['sign'], parsed_data['amount'])) |
||||
|
self.current_transaction.eref = parsed_data['reference'] |
||||
|
|
||||
|
def handle_tag_86(self, data): |
||||
|
"""Parse 86 tag containing reference data.""" |
||||
|
if not self.current_transaction: |
||||
|
return |
||||
|
codewords = ['20', '23', '24', '25', '26', '27', |
||||
|
'30', '31', '32', '33', '61', '62'] |
||||
|
subfields = get_subfields(data, codewords) |
||||
|
transaction = self.current_transaction |
||||
|
# If we have no subfields, set message to whole of data passed: |
||||
|
if not subfields: |
||||
|
transaction.message = data |
||||
|
else: |
||||
|
handle_common_subfields(transaction, subfields) |
||||
|
# Prevent handling tag 86 later for non transaction details: |
||||
|
self.current_transaction = None |
||||
|
|
@ -0,0 +1,17 @@ |
|||||
|
:20:6450374100 |
||||
|
:25:RO56BRDE360SV52474653600 |
||||
|
:28:00138/1 |
||||
|
:60F:C160517EUR3885,24 |
||||
|
:61:1605170517D210,60NTRFOPH478 |
||||
|
PLATA FACT 4603309 |
||||
|
:86:000+20Plata +30302410000+31RO89RZBR0000060003480121 |
||||
|
+32RUSSMEDIA PRESS SRL+33/ |
||||
|
+23PLATA FACT 4603309 |
||||
|
:61:1605170517D2,76NCOMOPH478 |
||||
|
25-Comision MULTIX |
||||
|
:86:000+20Comision |
||||
|
+2325-Comision MULTIX |
||||
|
:62F:C160517EUR3671,88 |
||||
|
:64:C160517EUR3671,88 |
||||
|
:86:Credit neutilizat la 17/05/2016: 0,00 \ |
||||
|
Sume blocate la 17/05/2016: 0,00 |
@ -0,0 +1,5 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
# © 2016 Forest and Biomass Services Romania |
||||
|
# See README.rst file on addons root folder for license details |
||||
|
|
||||
|
from . import test_import_bank_statement |
@ -0,0 +1,18 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
# © 2016 Forest and Biomass Services Romania |
||||
|
# See README.rst file on addons root folder for license details |
||||
|
|
||||
|
from openerp.addons.account_bank_statement_import.tests import ( |
||||
|
TestStatementFile) |
||||
|
|
||||
|
|
||||
|
class TestImport(TestStatementFile): |
||||
|
"""Run test to import MT940 ING import.""" |
||||
|
|
||||
|
def test_statement_import(self): |
||||
|
"""Test correct creation of single statement.""" |
||||
|
self._test_statement_import( |
||||
|
'account_bank_statement_import_mt940_ro_brd', 'test-brd.940', |
||||
|
'00138/1', |
||||
|
start_balance=3885.24, end_balance=3671.88 |
||||
|
) |
Write
Preview
Loading…
Cancel
Save
Reference in new issue