diff --git a/account_bank_statement_import_mt940_ro_brd/README.rst b/account_bank_statement_import_mt940_ro_brd/README.rst new file mode 100644 index 0000000..9a7db32 --- /dev/null +++ b/account_bank_statement_import_mt940_ro_brd/README.rst @@ -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 `_. +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 `_. + + +Credits +======= + +Contributors +------------ + +* Fekete Mihai + +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. diff --git a/account_bank_statement_import_mt940_ro_brd/__init__.py b/account_bank_statement_import_mt940_ro_brd/__init__.py new file mode 100644 index 0000000..b55f515 --- /dev/null +++ b/account_bank_statement_import_mt940_ro_brd/__init__.py @@ -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 diff --git a/account_bank_statement_import_mt940_ro_brd/__openerp__.py b/account_bank_statement_import_mt940_ro_brd/__openerp__.py new file mode 100644 index 0000000..32b3480 --- /dev/null +++ b/account_bank_statement_import_mt940_ro_brd/__openerp__.py @@ -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, +} diff --git a/account_bank_statement_import_mt940_ro_brd/account_bank_statement_import.py b/account_bank_statement_import_mt940_ro_brd/account_bank_statement_import.py new file mode 100644 index 0000000..291e8a3 --- /dev/null +++ b/account_bank_statement_import_mt940_ro_brd/account_bank_statement_import.py @@ -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) diff --git a/account_bank_statement_import_mt940_ro_brd/demo/demo_data.xml b/account_bank_statement_import_mt940_ro_brd/demo/demo_data.xml new file mode 100644 index 0000000..c18046b --- /dev/null +++ b/account_bank_statement_import_mt940_ro_brd/demo/demo_data.xml @@ -0,0 +1,26 @@ + + + + + + Bank Journal - (test mt940 BRD) + TBNKMT940BRD + bank + + + + + + + + Your Company + RO56BRDE360SV52474653600 + + + + bank + + + + + diff --git a/account_bank_statement_import_mt940_ro_brd/mt940.py b/account_bank_statement_import_mt940_ro_brd/mt940.py new file mode 100644 index 0000000..5ab1ea7 --- /dev/null +++ b/account_bank_statement_import_mt940_ro_brd/mt940.py @@ -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\d{6})(?P\d{0,4})' + r'(?P[CD])(?P\d+,\d{2})N(?P.{3})' + r'(?P\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 + diff --git a/account_bank_statement_import_mt940_ro_brd/test_files/test-brd.940 b/account_bank_statement_import_mt940_ro_brd/test_files/test-brd.940 new file mode 100644 index 0000000..bd693c0 --- /dev/null +++ b/account_bank_statement_import_mt940_ro_brd/test_files/test-brd.940 @@ -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 diff --git a/account_bank_statement_import_mt940_ro_brd/tests/__init__.py b/account_bank_statement_import_mt940_ro_brd/tests/__init__.py new file mode 100644 index 0000000..1c268e5 --- /dev/null +++ b/account_bank_statement_import_mt940_ro_brd/tests/__init__.py @@ -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 diff --git a/account_bank_statement_import_mt940_ro_brd/tests/test_import_bank_statement.py b/account_bank_statement_import_mt940_ro_brd/tests/test_import_bank_statement.py new file mode 100644 index 0000000..81c1722 --- /dev/null +++ b/account_bank_statement_import_mt940_ro_brd/tests/test_import_bank_statement.py @@ -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 + )