From: Henryk Plötz Date: Mon, 20 Aug 2018 22:59:04 +0000 (+0200) Subject: (Re-)implement get_statement() in new API X-Git-Tag: v2.0.0~1^2~95 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2d703f0da950b385612cc5ed2d2976759f784ff6;p=thirdparty%2Fpython-fints.git (Re-)implement get_statement() in new API --- diff --git a/fints/client.py b/fints/client.py index e43c6f5..f38c503 100644 --- a/fints/client.py +++ b/fints/client.py @@ -26,8 +26,8 @@ from .segments.accounts import HISPA1, HKSPA, HKSPA1 from .segments.auth import HKTAB, HKTAN from .segments.depot import HKWPD from .segments.dialog import HISYN4, HKSYN3 -from .segments.saldo import HISAL5, HISAL6, HISAL7, HKSAL5, HKSAL6, HKSAL7 -from .segments.statement import HKKAZ +from .segments.saldo import HKSAL5, HKSAL6, HKSAL7 +from .segments.statement import HKKAZ5, HKKAZ6, HKKAZ7 from .segments.transfer import HKCCM, HKCCS from .types import SegmentSequence from .utils import MT535_Miniparser, Password, mt940_to_array @@ -110,7 +110,7 @@ class FinTS3Client: return [a for a in [acc.as_sepa_account() for acc in self.accounts] if a] - def get_statement(self, account: SEPAAccount, start_date: datetime.datetime, end_date: datetime.date): + def get_statement(self, account: SEPAAccount, start_date: datetime.date, end_date: datetime.date): """ Fetches the statement of a bank account in a certain timeframe. @@ -119,77 +119,58 @@ class FinTS3Client: :param end_date: Last day to fetch :return: A list of mt940.models.Transaction objects """ - logger.info('Start fetching from {} to {}'.format(start_date, end_date)) - dialog = self._new_dialog() - dialog.sync() - dialog.init() - - def _get_msg(): - return self._create_statement_message(dialog, account, start_date, end_date, None) - - with self.pin.protect(): - logger.debug('Send message: {}'.format(_get_msg())) + with self._new_dialog() as dialog: + max_hikazs = self.bpd.find_segment_highest_version('HIKAZS', (5, 6, 7)) + if not max_hikazs: + raise ValueError('No supported HIKAZS version found') + + hkkaz = { + 5: HKKAZ5, + 6: HKKAZ6, + 7: HKKAZ7, + }.get(max_hikazs.header.version) + + responses = [] + touchdown_counter = 1 + touchdown = None + + logger.info('Start fetching from {} to {}'.format(start_date, end_date)) + while touchdown or touchdown_counter == 1: + seg = hkkaz( + account=hkkaz._fields['account'].type.from_sepa_account(account), + all_accounts=False, + date_start=start_date, + date_end=end_date, + touchdown_point=touchdown, + ) - msg = _get_msg() - resp = dialog.send(msg) - touchdowns = resp.get_touchdowns(msg) - responses = [resp] - touchdown_counter = 1 + rm = dialog.send(seg) - while HKKAZ.type in touchdowns: - logger.info('Fetching more results ({})...'.format(touchdown_counter)) + for resp in rm.response_segments(seg, 'HIKAZ'): + responses.append(resp) - with self.pin.protect(): - logger.debug('Send message: {}'.format( - self._create_statement_message(dialog, account, start_date, end_date, touchdowns[HKKAZ.type]) - )) + touchdown = None + for response in rm.responses(seg, '3040'): + touchdown = response.parameters[0] + break - 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) + if touchdown: + logger.info('Fetching more results ({})...'.format(touchdown_counter)) - touchdown_counter += 1 + touchdown_counter += 1 + logger.info('Fetching done.') - logger.info('Fetching done.') statement = [] - for resp in responses: - seg = resp._find_segment('HIKAZ') + for seg in responses: ## FIXME What is the encoding of MT940 messages? - statement += mt940_to_array(seg[1].decode('iso-8859-1')) + statement += mt940_to_array(seg.statement_booked.decode('iso-8859-1')) logger.debug('Statement: {}'.format(statement)) - dialog.end() return statement - def _create_statement_message(self, dialog: FinTSDialogOLD, account: SEPAAccount, start_date, end_date, touchdown): - hversion = dialog.hkkazversion - - if hversion in (4, 5, 6): - acc = ':'.join([ - account.accountnumber, account.subaccount or '', str(280), account.blz - ]) - elif hversion == 7: - acc = ':'.join([ - account.iban, account.bic, account.accountnumber, account.subaccount or '', 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 - ) - ]) - def get_balance(self, account: SEPAAccount): """ Fetches an accounts current balance. @@ -207,7 +188,7 @@ class FinTS3Client: 5: HKSAL5, 6: HKSAL6, 7: HKSAL7, - }.get(max_hisals.header.version, None) + }.get(max_hisals.header.version) seg = hksal( account=hksal._fields['account'].type.from_sepa_account(account), diff --git a/fints/message.py b/fints/message.py index ddf8783..a396b54 100644 --- a/fints/message.py +++ b/fints/message.py @@ -32,6 +32,12 @@ class FinTSMessage(SegmentSequence): if segment.header.reference == ref.header.number: yield segment + def responses(self, ref, code=None): + for segment in self.response_segments(ref, HIRMS2): + for response in segment.responses: + if code is None or response.code == code: + yield response + class FinTSCustomerMessage(FinTSMessage): DIRECTION = MessageDirection.FROM_CUSTOMER # Identification, authentication diff --git a/fints/segments/statement.py b/fints/segments/statement.py index c1d93d1..b80ede0 100644 --- a/fints/segments/statement.py +++ b/fints/segments/statement.py @@ -1,6 +1,11 @@ from fints.utils import fints_escape -from . import FinTS3SegmentOLD +from fints.fields import DataElementField, DataElementGroupField +from fints.formals import ( + KTI1, Account3, Account2 +) + +from . import FinTS3Segment, FinTS3SegmentOLD class HKKAZ(FinTS3SegmentOLD): @@ -24,3 +29,60 @@ class HKKAZ(FinTS3SegmentOLD): fints_escape(touchdown) if touchdown is not None else '' ] super().__init__(segno, data) + +class HKKAZ5(FinTS3Segment): + """Kontoumsätze anfordern/Zeitraum, version 5 + + Source: HBCI Homebanking-Computer-Interface, Schnittstellenspezifikation""" + account = DataElementGroupField(type=Account2, _d="Kontoverbindung Auftraggeber") + all_accounts = DataElementField(type='jn', _d="Alle Konten") + date_start = DataElementField(type='dat', required=False, _d="Von Datum") + date_end = DataElementField(type='dat', required=False, _d="Bis Datum") + max_number_responses = DataElementField(type='num', max_length=4, required=False, _d="Maximale Anzahl Einträge") + touchdown_point = DataElementField(type='an', max_length=35, required=False, _d="Aufsetzpunkt") + + +class HIKAZ5(FinTS3Segment): + """Kontoumsätze rückmelden/Zeitraum, version 5 + + Source: HBCI Homebanking-Computer-Interface, Schnittstellenspezifikation""" + statement_booked = DataElementField(type='bin', _d="Gebuchte Umsätze") + statement_pending = DataElementField(type='bin', required=False, _d="Nicht gebuchte Umsätze") + +class HKKAZ6(FinTS3Segment): + """Kontoumsätze anfordern/Zeitraum, version 6 + + Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Messages -- Multibankfähige Geschäftsvorfälle """ + account = DataElementGroupField(type=Account3, _d="Kontoverbindung Auftraggeber") + all_accounts = DataElementField(type='jn', _d="Alle Konten") + date_start = DataElementField(type='dat', required=False, _d="Von Datum") + date_end = DataElementField(type='dat', required=False, _d="Bis Datum") + max_number_responses = DataElementField(type='num', max_length=4, required=False, _d="Maximale Anzahl Einträge") + touchdown_point = DataElementField(type='an', max_length=35, required=False, _d="Aufsetzpunkt") + + +class HIKAZ6(FinTS3Segment): + """Kontoumsätze rückmelden/Zeitraum, version 6 + + Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Messages -- Multibankfähige Geschäftsvorfälle """ + statement_booked = DataElementField(type='bin', _d="Gebuchte Umsätze") + statement_pending = DataElementField(type='bin', required=False, _d="Nicht gebuchte Umsätze") + +class HKKAZ7(FinTS3Segment): + """Kontoumsätze anfordern/Zeitraum, version 7 + + Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Messages -- Multibankfähige Geschäftsvorfälle """ + account = DataElementGroupField(type=KTI1, _d="Kontoverbindung international") + all_accounts = DataElementField(type='jn', _d="Alle Konten") + date_start = DataElementField(type='dat', required=False, _d="Von Datum") + date_end = DataElementField(type='dat', required=False, _d="Bis Datum") + max_number_responses = DataElementField(type='num', max_length=4, required=False, _d="Maximale Anzahl Einträge") + touchdown_point = DataElementField(type='an', max_length=35, required=False, _d="Aufsetzpunkt") + + +class HIKAZ7(FinTS3Segment): + """Kontoumsätze rückmelden/Zeitraum, version 7 + + Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Messages -- Multibankfähige Geschäftsvorfälle """ + statement_booked = DataElementField(type='bin', _d="Gebuchte Umsätze") + statement_pending = DataElementField(type='bin', required=False, _d="Nicht gebuchte Umsätze")