Skip to content

Commit

Permalink
Merge pull request #10 from feketemihai/brd_statement_import
Browse files Browse the repository at this point in the history
Add module for import BRD MT940bank statements.
  • Loading branch information
feketemihai committed Oct 20, 2017
2 parents 8e872dd + 653314c commit 4dd8815
Show file tree
Hide file tree
Showing 11 changed files with 359 additions and 8 deletions.
72 changes: 72 additions & 0 deletions l10n_ro_account_bank_statement_import_mt940_brd/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
: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/l10n-romania
* 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

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/177/8.0

Known issues / Roadmap
======================

* None

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/l10n-romania/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/l10n-romania/issues/new?body=module:%20l10n_ro_account_bank_statement_import_mt940_brd%0Aversion:%208.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.


Credits
=======

Images
------

* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_.

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.
5 changes: 5 additions & 0 deletions l10n_ro_account_bank_statement_import_mt940_brd/__init__.py
Original file line number Diff line number Diff line change
@@ -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 models
19 changes: 19 additions & 0 deletions l10n_ro_account_bank_statement_import_mt940_brd/__openerp__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# -*- 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.1.0.0',
'license': 'AGPL-3',
'author': 'Forest and Biomass Services Romania, '
'Odoo Community Association (OCA)',
'website': 'https://www.forbiom.eu',
'category': 'Banking addons',
'depends': [
'account_bank_statement_import_mt940_base'
],
'demo': [
'demo/demo_data.xml',
],
'installable': True,
}
26 changes: 26 additions & 0 deletions l10n_ro_account_bank_statement_import_mt940_brd/demo/demo_data.xml
Original file line number Diff line number Diff line change
@@ -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">BRD940</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>
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# -*- 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
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)
145 changes: 145 additions & 0 deletions l10n_ro_account_bank_statement_import_mt940_brd/models/mt940.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
# -*- coding: utf-8 -*-
# © 2016 Forest and Biomass Services Romania
# See README.rst file on addons root folder for license details

import re
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
# © 2016 Forest and Biomass Services Romania
# See README.rst file on addons root folder for license details

from openerp.tests.common import TransactionCase
from openerp.modules.module import get_module_resource


class TestBRDImport(TransactionCase):
"""Run test to import MT940 BRD import."""

def setUp(self):
super(TestBRDImport, self).setUp()
self.statement_import_model = self.env['account.bank.statement.import']
self.bank_statement_model = self.env['account.bank.statement']

def test_statement_import(self):
"""Test correct creation of single statement."""
brd_file_path = get_module_resource(
'l10n_ro_account_bank_statement_import_mt940_brd',
'test_files', 'test_brd_940.txt')
brd_file = open(brd_file_path, 'rb').read().encode('base64')
bank_statement = self.statement_import_model.create(
dict(data_file=brd_file))
bank_statement.import_file()
bank_st_record = self.bank_statement_model.search(
[('name', '=', '00138/1')])[0]
self.assertEquals(bank_st_record.balance_start, 3885.24)
self.assertEquals(bank_st_record.balance_end_real, 3671.88)

line = bank_st_record.line_ids[0]
self.assertEquals(line.name, 'PLATA FACT 4603309')
self.assertEquals(line.amount, -210.60)
13 changes: 5 additions & 8 deletions oca_dependencies.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
# Add account-financial-tools repo dependency to have access to currency_rate_update module.
# Add account-payment repo dependency to have access to account_vat_on_payment and account_voucher_cash_basis modules.
# Add hr repo dependency to have access to some hr modules.
# Add partner-contact repo dependency to have access to partner_create_by_vat module.
account-financial-tools https://github.com/OCA/account-financial-tools 8.0
account-payment https://github.com/OCA/account-payment 8.0
hr https://github.com/OCA/hr 8.0
partner-contact https://github.com/OCA/partner-contact 8.0
account-financial-tools
account-payment
bank-statement-import
hr
partner-contact

0 comments on commit 4dd8815

Please sign in to comment.