From: Raphael Michel Date: Mon, 2 Jan 2017 19:34:44 +0000 (+0100) Subject: Add get_statement X-Git-Tag: v0.1.0~5 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2cc7c84c296e567ca8d49cfdc3f2cfbe882e88be;p=thirdparty%2Fpython-fints.git Add get_statement --- diff --git a/README.md b/README.md index 1c5a0a9..7971656 100644 --- a/README.md +++ b/README.md @@ -25,8 +25,17 @@ Usage 'https://mybank.com/…' # endpoint, e.g.: https://hbci-pintan.gad.de/cgi-bin/hbciservlet ) - print(f.get_sepa_accounts()) - + accounts = f.get_sepa_accounts() + print(accounts) + # [SEPAAccount(iban='DE12345678901234567890', bic='ABCDEFGH1DEF', accountnumber='123456790', subaccount='', + # blz='123456789')] + + statement = f.get_statement(accounts[0], date(2016, 12, 1), date.today()) + print([t.data for t in statement]) + # The statement is a list of transaction objects as parsed by the mt940 parser, see + # https://mt940.readthedocs.io/en/latest/mt940.html#mt940.models.Transaction + # for documentation. Most information is contained in a dict accessible via their + # ``data`` property Credits and License ------------------- diff --git a/fints3/client.py b/fints3/client.py index b52ed72..81b84cf 100644 --- a/fints3/client.py +++ b/fints3/client.py @@ -1,10 +1,14 @@ import logging -from .segments.accounts import HKSPA +import re + from .connection import FinTSHTTPSConnection from .dialog import FinTSDialog from .message import FinTSMessage from .models import SEPAAccount +from .segments.accounts import HKSPA +from .segments.statement import HKKAZ +from .utils import mt940_to_array logger = logging.getLogger(__name__) @@ -45,6 +49,71 @@ class FinTS3Client: return self.accounts + def get_statement(self, account, start_date, end_date): + logger.info('Start fetching from {} to {}'.format(start_date, end_date)) + + dialog = self._new_dialog() + dialog.sync() + dialog.init() + + msg = self._create_statement_message(dialog, account, start_date, end_date, None) + resp = dialog.send(msg) + touchdowns = resp.get_touchdowns(msg) + responses = [resp] + touchdown_counter = 1 + + while HKKAZ.type in touchdowns: + logger.info('Fetching more results ({})...'.format(touchdown_counter)) + msg = self._create_statement_message(dialog, account, start_date, end_date, touchdowns[HKKAZ.type]) + + resp = dialog.send(msg) + responses.append(resp) + touchdowns = resp.get_touchdowns(msg) + + touchdown_counter += 1 + + logger.info('Fetching done.') + + re_data = re.compile(r'[^@]*@([0-9]+)@(.+)', flags=re.MULTILINE | re.DOTALL) + statement = [] + for resp in responses: + seg = resp._find_segment('HIKAZ') + if seg: + m = re_data.match(seg) + if m: + statement += mt940_to_array(m.group(2)) + + logger.debug('Statement: {}'.format(statement)) + + dialog.end() + return statement + + def _create_statement_message(self, dialog: FinTSDialog, account: SEPAAccount, start_date, end_date, touchdown): + hversion = dialog.hkkazversion + + if hversion in (4, 5, 6): + acc = ':'.join([ + account.accountnumber, account.subaccount, str(280), account.blz + ]) + elif hversion == 7: + acc = ':'.join([ + account.iban, account.bic, account.accountnumber, account.subaccount, str(280), account.blz + ]) + else: + raise ValueError('Unsupported HKKAZ version {}'.format(hversion)) + + return self._new_message(dialog, [ + HKKAZ( + 3, + hversion, + acc, + start_date, + end_date, + touchdown + ) + ]) + + class FinTS3PinTanClient(FinTS3Client): def __init__(self, blz, username, pin, server): diff --git a/fints3/message.py b/fints3/message.py index b5fe3cf..9ec14a3 100644 --- a/fints3/message.py +++ b/fints3/message.py @@ -144,6 +144,26 @@ class FinTSResponse: return [m.group(0)] return False + def _find_segment_for_reference(self, name, ref): + segs = self._find_segments(name) + for seg in segs: + segsplit = seg.split('+')[0].split(':') + if segsplit[3] == str(ref.segmentno): + return seg + + def get_touchdowns(self, msg: FinTSMessage): + touchdown = {} + for msgseg in msg.encrypted_segments: + seg = self._find_segment_for_reference('HIRMS', msgseg) + if seg: + parts = seg.split('+')[1:] + for p in parts: + psplit = p.split(':') + if psplit[0] == "3040": + td = psplit[3] + touchdown[msgseg.type] = td + return touchdown + def _get_segment_max_version(self, name): v = 3 segs = self._find_segments(name) diff --git a/fints3/segments/statement.py b/fints3/segments/statement.py new file mode 100644 index 0000000..9d58aba --- /dev/null +++ b/fints3/segments/statement.py @@ -0,0 +1,23 @@ +import time +from . import FinTS3Segment + + +class HKKAZ(FinTS3Segment): + """ + HKKAZ (Kontoumsätze) + Section C.2.1.1.1.2 + """ + type = 'HKKAZ' + + def __init__(self, segno, version, account, date_start, date_end, touchdown): + self.version = version + + data = [ + account, + 'N', + date_start.strftime('%Y%m%d'), + date_end.strftime('%Y%m%d'), + '', + touchdown if touchdown is not None else '' + ] + super().__init__(segno, data) diff --git a/fints3/utils.py b/fints3/utils.py index 28eb3a9..bcc10c3 100644 --- a/fints3/utils.py +++ b/fints3/utils.py @@ -1,13 +1,7 @@ -def segments_to_ascii(segments, counter=1): - ascii = '' +import mt940 - for segment in segments: - # do only set counter if is 0; see B.8 - if segment.get_counter() == 0: - counter += 1 - segment.set_counter(counter) - s = segment.to_ascii() - ascii += s + "'" - - return counter, ascii +def mt940_to_array(data): + data = data.replace("@@", "\r\n") + transactions = mt940.models.Transactions() + return transactions.parse(data) diff --git a/requirements.txt b/requirements.txt index 3288e92..3b33fbc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ requests +mt-940