mt940-0.8.0/mt940/MT940-optional.txt0000644000000000000000000000031613615410400013332 0ustar00ABNANL2A 940 ABNANL2A :20:ABN AMRO BANK NV :25:123456789 :28:13501/1 :61:1205120514C500,01N654NONREF 987654321 :61:1412051205RC15,67IDXXREF DATA :61:171214C15,67TIDXTEST//REFERENCE :86:/SUM/ - mt940-0.8.0/mt940/MT940.txt0000644000000000000000000000134713615410400011514 0ustar00ABNANL2A 940 ABNANL2A :20:ABN AMRO BANK NV :25:123456789 :28:13501/1 :60F:C120511EUR5138,61 :61:1205120514C500,01N654NONREF 987654321 :86:/TRTP/SEPA OVERBOEKING/IBAN/FR12345678901234/BIC/GEFRADAM /NAME/QASD JGRED/REMI/Dit zijn de omschrijvingsregels/EREF/NOTPRO VIDED :61:1412051205RC15,67IDXXREF DATA :61:171214C15,67TIDXTEST//REFERENCE :62F:C120514EUR5638,62 :86:/SUM/ - :20:ABN AMRO BANK NV :25:123456789 :28:13501/1 :60F:C120511EUR5138,61 :61:1205120514C500,01N654NONREF 987654321 :86:/TRTP/SEPA OVERBOEKING/IBAN/FR12345678901234/BIC/GEFRADAM /NAME/QASD JGRED/REMI/Dit zijn de omschrijvingsregels/EREF/NOTPRO VIDED :61:1412051205RC15,67IDXXREF DATA :61:171214C15,67TIDXTEST//REFERENCE :62F:C120514EUR5638,62 mt940-0.8.0/mt940/__init__.py0000644000000000000000000002305713615410400012311 0ustar00# This file is part of mt940. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. """a parser for MT940 files """ __version__ = '0.8.0' __all__ = ['MT940', 'rabo_description', 'abn_amro_description', 'ing_description', 'regiobank_description'] import datetime import io import re from collections import defaultdict, namedtuple from decimal import Decimal SECTIONS = { 'begin': [':940:'], 'statement': [':20:'], 'account': [':25:'], 'information': [':28:', ':28C:'], 'start_balance': [':60F:'], 'transaction': [':61:'], 'description': [':86:'], 'end_balance': [':62F:'], } def _parse_date(date): return datetime.datetime.strptime(date, '%y%m%d').date() def _parse_amount(amount, sign='C'): amount = Decimal(amount.replace(',', '.')) if sign in ('D', 'RC'): return -amount return amount TRANSACTION_RE = re.compile(r""" (?P\d{6}) (?P\d{4})? (?PD|C|RC|RD) (?P\w)?? # ING skips this mandatory field (?P(-|\d)(\d|,){0,14}) (?P\w{4}) (?P.{0,34})""", re.VERBOSE) class MT940(object): def __init__(self, name, encoding=None): self.statements = [] if isinstance(name, (bytes, str)): with io.open(name, encoding=encoding, mode='r') as f: self._parse(f) else: self._parse(name) def _parse(self, f): values = defaultdict(str) transactions = [] for line in self._readline(f): for name, sections in SECTIONS.items(): if name == 'begin': continue for section in sections: if line.startswith(section): if name in values and name == 'statement': self._set_statement(values, transactions) if name.endswith('_balance'): values[name] = self._get_balance( line[len(section):]) elif name == 'transaction': transactions.append( self._get_transaction(line[len(section):])) elif name == 'description': description = line[len(section):] if 'end_balance' in values: values['description'] += description else: transactions[-1] = (transactions[-1][:-1] + (description,)) else: values[name] += line[len(section):] if values: self._set_statement(values, transactions) @staticmethod def _readline(f): buf = [] for line in f: line = line.strip('\n') if buf: if (line.startswith(':') or line.startswith('-')): yield '\n'.join(buf) del buf[:] buf.append(line) if buf: yield '\n'.join(buf) @staticmethod def _get_balance(balance): date = _parse_date(balance[1:7]) amount = _parse_amount(balance[10:], balance[0]) return Balance(date=date, amount=amount, currency=balance[7:10]) @staticmethod def _get_transaction(transaction): lines = transaction.splitlines() if len(lines) == 1: transaction, = lines additional_data = None else: transaction, additional_data = lines transaction = TRANSACTION_RE.match(transaction) date = _parse_date(transaction.group('date')) if transaction.group('booking'): booking = _parse_date( transaction.group('date')[:2] + transaction.group('booking')) else: booking = None amount = _parse_amount(transaction.group('amount'), transaction.group('sign')) fund_code = transaction.group('fund_code') id_ = transaction.group('id') reference = transaction.group('reference') reference, _, institution_reference = reference.partition('//') return (date, booking, amount, fund_code, id_, reference, institution_reference, additional_data, '') def _set_statement(self, values, transactions): # Set optional values values.setdefault('start_balance') values.setdefault('end_balance') values.setdefault('description') self.statements.append( Statement( transactions=[Transaction(*t) for t in transactions], **values)) values.clear() del transactions[:] Statement = namedtuple('Statement', ['statement', 'account', 'information', 'start_balance', 'transactions', 'end_balance', 'description']) Balance = namedtuple('Balance', ['date', 'amount', 'currency']) Transaction = namedtuple( 'Transaction', ['date', 'booking', 'amount', 'fund_code', 'id', 'reference', 'institution_reference', 'additional_data', 'description']) def _find_swift_tags(tags, description): values = {} for tag, name in tags: if description.startswith(tag): description = description[len(tag):] cursor = len(description) for next_tag, _ in tags: if next_tag in values or next_tag == tag: continue index = description.find(next_tag) if index == -1: continue cursor = min(cursor, index) next_tag_index = cursor values[name] = description[:next_tag_index] description = description[next_tag_index:] if not description: break return values RABO_TAGS = [ ('/MARF/', 'marf'), ('/EREF/', 'eref'), ('/PREF/', 'pref'), ('/TRCD/', 'trcd'), ('/BENM/', 'benm'), ('/ORDP/', 'ordp'), ('/NAME/', 'name'), ('/ID/', 'id'), ('/ADDR/', 'addr'), ('/REMI/', 'remi'), ('/CDTRREFTP//CD/SCOR/ISSR/CUR/CDTRREF/', 'cdtrref'), ('/CSID/', 'csid'), ('/ISDT/', 'isdt'), ('/RTRN/', 'rtrn'), ] def rabo_description(description): "Return dictionary with Rabo informations" description = ''.join(description.splitlines()) return _find_swift_tags(RABO_TAGS, description) ABN_AMRO_ACCOUNT = re.compile(r""" ^([0-9]{1,3}\.[0-9]{1,2}\.[0-9]{1,2}\.[0-9]{1,3})""", re.VERBOSE) ABN_AMRO_GIRO = re.compile(r""" ^GIRO\ +([0-9]+)""", re.VERBOSE) ABN_AMRO_TAGS = [ ('/TRTP/', 'trtp'), ('/IBAN/', 'iban'), ('/BIC/', 'bic'), ('/CSID', 'csid'), ('/NAME/', 'name'), ('/REMI/', 'remi'), ('/EREF/', 'eref'), ('/ORDP//ID/', 'ordp'), ('/BENM//ID/', 'benm'), ] def abn_amro_description(description): "Return dictionary with ABN AMRO informations" description = ''.join(description.splitlines()) values = {} m = ABN_AMRO_ACCOUNT.match(description) if m: values['account'] = m.group(1).replace('.', '') m = ABN_AMRO_GIRO.match(description) if m: values['account'] = m.group(1) values.update(_find_swift_tags(ABN_AMRO_TAGS, description)) return values ING_TAGS = re.compile(r'/(RTRN|EREF|PREF|MARF|CSID|CNTP|REMI|PURP|ULT[CD])/') ING_TAGS_DEFINITION = { 'RTRN': ('rtrn', []), 'EREF': ('eref', []), 'PREF': ('pref', []), 'MARF': ('marf', []), 'CSID': ('csid', []), 'CNTP': ('cntp', ['account_number', 'bic', 'name', 'city']), 'REMI': ('remi', ['code', 'issuer', 'remittance_info']), 'PURP': ('purp', []), 'ULTC': ('ultc', ['name', 'id']), 'ULTD': ('ultd', ['name', 'id']), } def ing_description(description): "Return dictionary with ING informations" description = ''.join(description.splitlines()) values = {} ing_tags = iter(ING_TAGS.split(description)[1:]) for tag, tag_value in zip(ing_tags, ing_tags): tag_value = tag_value[:-1] name, subfields = ING_TAGS_DEFINITION[tag] if not subfields: values[name] = tag_value continue values[name] = {} if 'name' in subfields or 'remittance_info' in subfields: special_tag = 'name' if 'name' in subfields else 'remittance_info' tag_idx = subfields.index(special_tag) subtags = tag_value.split('/', tag_idx) for sf_name, sf_value in zip(subfields[:tag_idx], subtags[:-1]): values[name][sf_name] = sf_value subtags = subtags[-1].rsplit('/', len(subfields) - tag_idx - 1) for sf_name, sf_value in zip(subfields[tag_idx:], subtags): values[name][sf_name] = sf_value else: subtags = tag_value.split('/') for sf_name, sf_value in zip(subfields, subtags): values[name][sf_name] = sf_value return values def regiobank_description(description): "Return dictionary with RegioBank informations" lines = description.splitlines() values = {} try: first, second, third = lines[0], lines[1], ''.join(lines[2:]) except (ValueError, IndexError): return {} try: values['account_number'], values['name'] = first.split(' ', 1) except ValueError: return {} values['address'] = second # XXX Not clear how to split it if third.startswith('aan %s' % values['name']): _, values['iban'], values['remittance_info'], values['description'] = \ third.split(',') else: values['reference'] = third return values mt940-0.8.0/mt940/test.py0000644000000000000000000002222213615410400011522 0ustar00# This file is part of mt940. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. """Test MT940 """ import datetime import io import os import unittest from decimal import Decimal from mt940 import ( MT940, abn_amro_description, ing_description, rabo_description, regiobank_description) here = os.path.dirname(__file__) class TestMT940(unittest.TestCase): def setUp(self): self.mt940 = MT940(os.path.join(here, 'MT940.txt')) def test_number_statements(self): "Test number of statements" self.assertEqual(len(self.mt940.statements), 2) def test_statement_account(self): "Test statement account" self.assertEqual(self.mt940.statements[0].account, '123456789') def test_statement_information(self): "Test statement information" self.assertEqual(self.mt940.statements[0].information, '13501/1') def test_statement_start_balance(self): "Test statement start balance" start_balance = self.mt940.statements[0].start_balance self.assertEqual(start_balance.date, datetime.date(2012, 5, 11)) self.assertEqual(start_balance.amount, Decimal('5138.61')) self.assertEqual(start_balance.currency, 'EUR') def test_statement_end_balance(self): "Test statement end balance" end_balance = self.mt940.statements[0].end_balance self.assertEqual(end_balance.date, datetime.date(2012, 5, 14)) self.assertEqual(end_balance.amount, Decimal('5638.62')) self.assertEqual(end_balance.currency, 'EUR') def test_statement_description(self): self.assertEqual(self.mt940.statements[0].description, '/SUM/') def test_transaction(self): "Test transaction" transaction = self.mt940.statements[0].transactions[0] self.assertEqual(transaction.date, datetime.date(2012, 5, 12)) self.assertEqual(transaction.booking, datetime.date(2012, 5, 14)) self.assertEqual(transaction.amount, Decimal('500.01')) self.assertEqual(transaction.fund_code, None) self.assertEqual(transaction.id, 'N654') self.assertEqual(transaction.reference, 'NONREF') self.assertEqual(transaction.additional_data, '987654321') self.assertEqual(transaction.description, '''/TRTP/SEPA OVERBOEKING/IBAN/FR12345678901234/BIC/GEFRADAM /NAME/QASD JGRED/REMI/Dit zijn de omschrijvingsregels/EREF/NOTPRO VIDED''') transaction = self.mt940.statements[0].transactions[1] self.assertEqual(transaction.date, datetime.date(2014, 12, 5)) self.assertEqual(transaction.booking, datetime.date(2014, 12, 5)) self.assertEqual(transaction.amount, Decimal('-15.67')) self.assertEqual(transaction.fund_code, None) self.assertEqual(transaction.id, 'IDXX') self.assertEqual(transaction.reference, 'REF'), self.assertEqual(transaction.institution_reference, '') self.assertEqual(transaction.additional_data, 'DATA') self.assertEqual(transaction.description, '') transaction = self.mt940.statements[0].transactions[2] self.assertEqual(transaction.date, datetime.date(2017, 12, 14)) self.assertEqual(transaction.booking, None) self.assertEqual(transaction.amount, Decimal('15.67')) self.assertEqual(transaction.fund_code, None) self.assertEqual(transaction.id, 'TIDX') self.assertEqual(transaction.reference, 'TEST'), self.assertEqual(transaction.institution_reference, 'REFERENCE') self.assertEqual(transaction.additional_data, None) self.assertEqual(transaction.description, '') class TestMT940Stream(TestMT940): def setUp(self): with io.open(os.path.join(here, 'MT940.txt')) as fp: self.mt940 = MT940(fp, encoding='ascii') class TestMT940Optional(unittest.TestCase): def setUp(self): self.mt940 = MT940(os.path.join(here, 'MT940-optional.txt')) def test_statement_start_balance(self): "Test statement has not start balance" start_balance = self.mt940.statements[0].start_balance self.assertEqual(start_balance, None) def test_statement_end_balance(self): "Test statement has no end balance" end_balance = self.mt940.statements[0].end_balance self.assertEqual(end_balance, None) def test_statement_description(self): "Test statement has no description" description = self.mt940.statements[0].description self.assertEqual(description, None) class TestMT940Transaction(unittest.TestCase): def test_transaction(self): for transaction, result in [ ('240325C-50000,00NMSCREV20240325AAAAAAA010O9902538776', (datetime.date(2024, 3, 25), None, Decimal('-50000.00'), None, 'NMSC', 'REV20240325AAAAAAA010O9902538776', '', None, '')), ('240325CS-50000,00NMSCREV20240325AAAAAAA010O9902538776', (datetime.date(2024, 3, 25), None, Decimal('-50000.00'), 'S', 'NMSC', 'REV20240325AAAAAAA010O9902538776', '', None, '')), ]: with self.subTest(transaction=transaction): self.assertEqual(MT940._get_transaction(transaction), result) class TestRaboDescription(unittest.TestCase): def test_one_tag(self): self.assertEqual(rabo_description('/EREF/foo'), {'eref': 'foo'}) def test_empty_tags(self): self.assertEqual(rabo_description('/BENM//NAME/Doe'), {'benm': '', 'name': 'Doe'}) def test_long_tags(self): self.assertEqual(rabo_description( '/ORDP//NAME/Doe/REMI//CDTRREFTP//CD/SCOR/ISSR/CUR/CDTRREF/' '12345' )['cdtrref'], '12345') def test_non_rabo(self): self.assertEqual(rabo_description('foo'), {}) self.assertEqual(rabo_description('/FOO/BAR/NAME/'), {}) def test_mixed_tags(self): self.assertEqual( rabo_description( '/EREF/0007301960/ORDP//NAME/Acist Europe B.V./ADDR/' 'Heerlen 6422 PH Heerlen NL/REMI//INV/16000291 29.7.2016'), {'eref': '0007301960', 'ordp': '', 'name': 'Acist Europe B.V.', 'addr': 'Heerlen 6422 PH Heerlen NL', 'remi': '/INV/16000291 29.7.2016'}) class TestABNAMRODescription(unittest.TestCase): def test_account(self): self.assertEqual(abn_amro_description('12.34.56.789 John Doe'), {'account': '123456789'}) def test_giro(self): self.assertEqual(abn_amro_description('GIRO 4090309'), {'account': '4090309'}) def test_tag(self): self.assertEqual(abn_amro_description( '''/TRTP/SEPA OVERBOEKING/IBAN/FR001234567890/BIC/GEF RADAM/NAME/ENERGIE BEDRIJF/EREF/NOTPROVIDED'''), { 'trtp': 'SEPA OVERBOEKING', 'iban': 'FR001234567890', 'bic': 'GEFRADAM', 'name': 'ENERGIE BEDRIJF', 'eref': 'NOTPROVIDED', }) def test_non_abn_amro(self): self.assertEqual(abn_amro_description('foo'), {}) self.assertEqual(rabo_description('/FOO/BAR/NAME/'), {}) class TestINGDescription(unittest.TestCase): def test_tag(self): description = """/EREF/170330P40411570.4342.2964442//CNTP/ NL94RABO0123456789/RABONL2U/ENERGIE BEDRIJF///REMI/USTD// 170330/REM INFO/""" self.assertEqual(ing_description(description), { 'eref': '170330P40411570.4342.2964442', 'cntp': { 'account_number': 'NL94RABO0123456789', 'bic': 'RABONL2U', 'name': 'ENERGIE BEDRIJF', 'city': '', }, 'remi': { 'code': 'USTD', 'issuer': '', 'remittance_info': '170330/REM INFO', }, }) def test_non_ing(self): self.assertEqual(ing_description('foo'), {}) self.assertEqual(ing_description('/FOO/BAR/NAME/'), {}) class TestRegioBankDescription(unittest.TestCase): def test_reference(self): description = """0102792984 jyhhenewr f j k rgt-test-004""" self.assertEqual(regiobank_description(description), { 'account_number': '0102792984', 'name': 'jyhhenewr f j k', 'address': '', 'reference': 'rgt-test-004', }) def test_sepa(self): description = """0707464188 dsfg w van aan dsfg w van,nl04asnb070746418 8,sct2013021540684000000000004, t est 1""" self.assertEqual(regiobank_description(description), { 'account_number': '0707464188', 'name': 'dsfg w van', 'address': '', 'iban': 'nl04asnb070746418 8', 'remittance_info': 'sct2013021540684000000000004', 'description': 't est 1', }) def test_non_regiobank(self): self.assertEqual(regiobank_description('foo'), {}) description = """foo bar test""" self.assertEqual(regiobank_description(description), {}) mt940-0.8.0/mt940/test_readme.py0000644000000000000000000000104313615410400013035 0ustar00# This file is part of mt940. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. import doctest import os here = os.path.dirname(__file__) readme = os.path.normpath(os.path.join(here, '..', 'README.rst')) def load_tests(loader, tests, pattern): if os.path.isfile(readme): tests.addTest(doctest.DocFileSuite( readme, module_relative=False, encoding='utf-8', optionflags=doctest.REPORT_ONLY_FIRST_FAILURE)) return tests mt940-0.8.0/.gitignore0000755000000000000000000000000013615410400017303 2Sync/dotfiles/git/dot-gitignoreustar00mt940-0.8.0/.hgignore0000644000000000000000000000007713615410400011123 0ustar00syntax: glob *.py[cdo] *.egg-info dist/ build/ .tox/ .coverage mt940-0.8.0/COPYRIGHT0000644000000000000000000000313313615410400010607 0ustar00Copyright (c) 2013-2026 Cédric Krier Copyright (c) 2014-2017 Nicolas Évrard Copyright (c) 2013-2026 B2CK SRL All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. mt940-0.8.0/README.rst0000644000000000000000000000270113615410400011003 0ustar00mt940 ===== mt940 is a parser for MT940 files. Nutshell -------- Import:: >>> import os >>> from mt940 import MT940 Instanciate:: >>> mt940 = MT940('mt940/MT940.txt') The statements:: >>> len(mt940.statements) 2 >>> statement = mt940.statements[0] >>> statement.account '123456789' >>> statement.information '13501/1' >>> start_balance = statement.start_balance >>> start_balance.date datetime.date(2012, 5, 11) >>> start_balance.amount Decimal('5138.61') >>> start_balance.currency 'EUR' >>> end_balance = statement.end_balance >>> end_balance.date datetime.date(2012, 5, 14) >>> end_balance.amount Decimal('5638.62') >>> end_balance.currency 'EUR' The transactions:: >>> len(statement.transactions) 3 >>> transaction, _, _ = statement.transactions >>> transaction.date datetime.date(2012, 5, 12) >>> transaction.booking datetime.date(2012, 5, 14) >>> transaction.amount Decimal('500.01') >>> transaction.id 'N654' >>> transaction.reference 'NONREF' >>> transaction.additional_data '987654321' >>> transaction.description # doctest: +NORMALIZE_WHITESPACE '/TRTP/SEPA OVERBOEKING/IBAN/FR12345678901234/BIC/GEFRADAM\n/NAME/QASD JGRED/REMI/Dit zijn de omschrijvingsregels/EREF/NOTPRO\nVIDED' To report issues please visit the `mt940 bugtracker`_. .. _mt940 bugtracker: https://bugs.tryton.org/mt940 mt940-0.8.0/pyproject.toml0000644000000000000000000000202513615410400012227 0ustar00[build-system] requires = ['hatchling >= 1', 'hatch-tryton'] build-backend = 'hatchling.build' [project] name = 'mt940' dynamic = ['version', 'authors'] requires-python = '>=3.9' maintainers = [ {name = "Tryton", email = "foundation@tryton.org"}, ] description = "A module to parse MT940 files" readme = 'README.rst' license = 'BSD-3-Clause' license-files = ['COPYRIGHT'] keywords = ["MT940", "parser"] classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Topic :: Office/Business", "Topic :: Software Development :: Libraries", "Topic :: Utilities", ] [project.urls] homepage = "https://www.tryton.org/" changelog = "https://code.tryton.org/mt940/-/blob/branch/default/CHANGELOG" forum = "https://discuss.tryton.org/tags/mt940" issues = "https://bugs.tryton.org/mt940" repository = "https://code.tryton.org/mt940" [tool.hatch.version] path = 'mt940/__init__.py' [tool.hatch.build] packages = ['mt940'] [tool.hatch.metadata.hooks.tryton] copyright = 'COPYRIGHT' mt940-0.8.0/PKG-INFO0000644000000000000000000000451713615410400010420 0ustar00Metadata-Version: 2.4 Name: mt940 Version: 0.8.0 Summary: A module to parse MT940 files Project-URL: homepage, https://www.tryton.org/ Project-URL: changelog, https://code.tryton.org/mt940/-/blob/branch/default/CHANGELOG Project-URL: forum, https://discuss.tryton.org/tags/mt940 Project-URL: issues, https://bugs.tryton.org/mt940 Project-URL: repository, https://code.tryton.org/mt940 Author: B2CK SRL Author-email: Cédric Krier , Nicolas Évrard Maintainer-email: Tryton License-Expression: BSD-3-Clause License-File: COPYRIGHT Keywords: MT940,parser Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: Topic :: Office/Business Classifier: Topic :: Software Development :: Libraries Classifier: Topic :: Utilities Requires-Python: >=3.9 Description-Content-Type: text/x-rst mt940 ===== mt940 is a parser for MT940 files. Nutshell -------- Import:: >>> import os >>> from mt940 import MT940 Instanciate:: >>> mt940 = MT940('mt940/MT940.txt') The statements:: >>> len(mt940.statements) 2 >>> statement = mt940.statements[0] >>> statement.account '123456789' >>> statement.information '13501/1' >>> start_balance = statement.start_balance >>> start_balance.date datetime.date(2012, 5, 11) >>> start_balance.amount Decimal('5138.61') >>> start_balance.currency 'EUR' >>> end_balance = statement.end_balance >>> end_balance.date datetime.date(2012, 5, 14) >>> end_balance.amount Decimal('5638.62') >>> end_balance.currency 'EUR' The transactions:: >>> len(statement.transactions) 3 >>> transaction, _, _ = statement.transactions >>> transaction.date datetime.date(2012, 5, 12) >>> transaction.booking datetime.date(2012, 5, 14) >>> transaction.amount Decimal('500.01') >>> transaction.id 'N654' >>> transaction.reference 'NONREF' >>> transaction.additional_data '987654321' >>> transaction.description # doctest: +NORMALIZE_WHITESPACE '/TRTP/SEPA OVERBOEKING/IBAN/FR12345678901234/BIC/GEFRADAM\n/NAME/QASD JGRED/REMI/Dit zijn de omschrijvingsregels/EREF/NOTPRO\nVIDED' To report issues please visit the `mt940 bugtracker`_. .. _mt940 bugtracker: https://bugs.tryton.org/mt940