'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
-------------------
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__)
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):
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)
--- /dev/null
+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)
-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)