]> git.ipfire.org Git - thirdparty/python-fints.git/commitdiff
Simply _find_highest_command() API. Implement get_holdings() (untested).
authorHenryk Plötz <henryk@ploetzli.ch>
Tue, 21 Aug 2018 12:32:59 +0000 (14:32 +0200)
committerRaphael Michel <mail@raphaelmichel.de>
Mon, 3 Dec 2018 18:34:29 +0000 (19:34 +0100)
fints/client.py
fints/segments/depot.py

index e7c35a5ab960fd6a3d7740f46edba3ebdea35e0c..8f9773486a0cefeedb2597b0427ac4ffab0ba17f 100644 (file)
@@ -24,7 +24,7 @@ from .security import (
 from .segments import HIBPA3, HIRMS2, HIUPA4
 from .segments.accounts import HISPA1, HKSPA, HKSPA1
 from .segments.auth import HKTAB, HKTAN
-from .segments.depot import HKWPD
+from .segments.depot import HKWPD5, HKWPD6
 from .segments.dialog import HISYN4, HKSYN3
 from .segments.saldo import HKSAL5, HKSAL6, HKSAL7
 from .segments.statement import HKKAZ5, HKKAZ6, HKKAZ7
@@ -60,7 +60,7 @@ class FinTS3Client:
         self.product_version = '0.2'
         self._standing_dialog = None
 
-    def _get_dialog(self, lazy_init=False):
+    def _new_dialog(self, lazy_init=False):
         raise NotImplemented()
 
     def _new_message(self, dialog: FinTSDialogOLD, segments, tan=None):
@@ -83,6 +83,18 @@ class FinTS3Client:
 
         self._standing_dialog = None
 
+    def _get_dialog(self, lazy_init=False):
+        if lazy_init and self._standing_dialog:
+            raise Error("Cannot _get_dialog(lazy_init=True) with _standing_dialog")
+
+        if self._standing_dialog:
+            return self._standing_dialog
+
+        if not lazy_init:
+            self._ensure_system_id()
+
+        return self._new_dialog(lazy_init=lazy_init)
+
     def process_institute_response(self, message):
         bpa = message.find_segment_first(HIBPA3)
         if bpa:
@@ -126,6 +138,13 @@ class FinTS3Client:
         return [a for a in [acc.as_sepa_account() for acc in self.accounts] if a]
 
     def _fetch_with_touchdowns(self, dialog, segment_factory, *args, **kwargs):
+        """Execute a sequence of fetch commands on dialog.
+        segment_factory must be a callable with one argument touchdown. Will be None for the
+        first call and contains the institute's touchdown point on subsequent calls.
+        segment_factory must return a command segment.
+        Extra arguments will be passed to FinTSMessage.response_segments.
+        Return value is a concatenated list of the return values of FinTSMessage.response_segments().
+        """
         responses = []
         touchdown_counter = 1
         touchdown = None
@@ -150,7 +169,10 @@ class FinTS3Client:
 
         return responses
 
-    def _find_highest_command(self, parameter_segment_name, version_map):
+    def _find_highest_supported_command(self, *segment_classes):
+        """Search the BPD for the highest supported version of a segment."""
+        parameter_segment_name = "{}I{}S".format(segment_classes[0].TYPE[0], segment_classes[0].TYPE[2:])
+        version_map = dict((clazz.VERSION, clazz) for clazz in segment_classes)
         max_version = self.bpd.find_segment_highest_version(parameter_segment_name, version_map.keys())
         if not max_version:
             raise ValueError('No supported {} version found'.format(parameter_segment_name))
@@ -169,12 +191,7 @@ class FinTS3Client:
         """
 
         with self._get_dialog() as dialog:
-            hkkaz = self._find_highest_command('HIKAZS', {
-                    5: HKKAZ5,
-                    6: HKKAZ6,
-                    7: HKKAZ7,
-                }
-            )
+            hkkaz = self._find_highest_supported_command(HKKAZ5, HKKAZ6, HKKAZ7)
 
             logger.info('Start fetching from {} to {}'.format(start_date, end_date))
             responses = self._fetch_with_touchdowns(
@@ -208,12 +225,7 @@ class FinTS3Client:
         """
 
         with self._get_dialog() as dialog:
-            hksal = self._find_highest_command('HISALS', {
-                    5: HKSAL5,
-                    6: HKSAL6,
-                    7: HKSAL7,
-                }
-            )
+            hksal = self._find_highest_supported_command(HKSAL5, HKSAL6, HKSAL7)
 
             seg = hksal(
                 account=hksal._fields['account'].type.from_sepa_account(account),
@@ -233,59 +245,26 @@ class FinTS3Client:
         :return: List of Holding objects
         """
         # init dialog
-        dialog = self._get_dialog()
-        dialog.sync()
-        dialog.init()
-
-        # execute job
-        def _get_msg():
-            return self._create_get_holdings_message(dialog, account)
-
-        with self.pin.protect():
-            logger.debug('Sending HKWPD: {}'.format(_get_msg()))
+        with self._get_dialog() as dialog:
+            hkwpd = self._find_highest_supported_command(HKWPD5, HKWPD6)
 
-        resp = dialog.send(_get_msg())
-        logger.debug('Got HIWPD response: {}'.format(resp))
+            seg = hkwpd(
+                account=hkwpd._fields['account'].type.from_sepa_account(account),
+            )
 
-        # end dialog
-        dialog.end()
+            response = dialog.send(seg)
 
+            for resp in response.response_segments(seg, 'HIWPD'):
+                ## FIXME BROKEN
+                mt535_lines = str.splitlines(resp)
+                # The first line contains a FinTS HIWPD header - drop it.
+                del mt535_lines[0]
+                mt535 = MT535_Miniparser()
+                return mt535.parse(mt535_lines)
 
-        ## FIXME BROKEN
-        # find segment and split up to balance part
-        seg = resp._find_segment('HIWPD')
-        if seg:
-            mt535_lines = str.splitlines(seg)
-            # The first line contains a FinTS HIWPD header - drop it.
-            del mt535_lines[0]
-            mt535 = MT535_Miniparser()
-            return mt535.parse(mt535_lines)
-        else:
             logger.debug('No HIWPD response segment found - maybe account has no holdings?')
             return []
 
-    def _create_get_holdings_message(self, dialog: FinTSDialogOLD, account: SEPAAccount):
-        hversion = dialog.hksalversion
-
-        if hversion in (1, 2, 3, 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 HKSAL version {}'.format(hversion))
-
-        return self._new_message(dialog, [
-            HKWPD(
-                3,
-                hversion,
-                acc,
-            )
-        ])
-
     def _create_send_tan_message(self, dialog: FinTSDialogOLD, challenge: TANChallenge, tan):
         return self._new_message(dialog, [
             HKTAN(3, '2', challenge.reference, '', challenge.version)
@@ -510,16 +489,7 @@ class FinTS3PinTanClient(FinTS3Client):
         self.connection = FinTSHTTPSConnection(server)
         super().__init__(bank_identifier=bank_identifier, user_id=user_id, customer_id=customer_id)
 
-    def _get_dialog(self, lazy_init=False):
-        if lazy_init and self._standing_dialog:
-            raise Error("Cannot _get_dialog(lazy_init=True) with _standing_dialog")
-
-        if self._standing_dialog:
-            return self._standing_dialog
-
-        if not lazy_init:
-            self._ensure_system_id()
-
+    def _new_dialog(self, lazy_init=False):
         if not self.selected_security_function or self.selected_security_function == '999':
             enc = PinTanDummyEncryptionMechanism(1)
             auth = PinTanOneStepAuthenticationMechanism(self.pin)
index 2b4c31f7ba27bbcbdcbf0d989c528b5ec04b81b5..0e4b703d4d33465d48cc58994d4b05a799a3cc18 100644 (file)
@@ -1,4 +1,7 @@
-from . import FinTS3SegmentOLD
+from fints.fields import DataElementField, DataElementGroupField
+from fints.formals import Account2, Account3
+
+from . import FinTS3Segment, FinTS3SegmentOLD
 
 
 class HKWPD(FinTS3SegmentOLD):
@@ -21,3 +24,38 @@ class HKWPD(FinTS3SegmentOLD):
             # 2             # Kursqualität
         ]
         super().__init__(segno, data)
+
+class HKWPD5(FinTS3Segment):
+    """Depotaufstellung anfordern, version 5
+
+    Source: HBCI Homebanking-Computer-Interface, Schnittstellenspezifikation"""
+    account = DataElementGroupField(type=Account2, _d="Depot")
+    currency = DataElementField(type='cur', required=False, _d="Währung der Depotaufstellung")
+    quality = DataElementField(type='num', length=1, required=False, _d="Kursqualität")
+    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 HIWPD5(FinTS3Segment):
+    """Depotaufstellung rückmelden, version 5
+
+    Source: HBCI Homebanking-Computer-Interface, Schnittstellenspezifikation"""
+    holdings = DataElementField(type='bin', _d="Depotaufstellung")
+
+
+class HKWPD6(FinTS3Segment):
+    """Depotaufstellung anfordern, version 6
+
+    Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Messages -- Multibankfähige Geschäftsvorfälle """
+    account = DataElementGroupField(type=Account3, _d="Depot")
+    currency = DataElementField(type='cur', required=False, _d="Währung der Depotaufstellung")
+    quality = DataElementField(type='code', length=1, required=False, _d="Kursqualität")
+    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 HIWPD6(FinTS3Segment):
+    """Depotaufstellung rückmelden, version 6
+
+    Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Messages -- Multibankfähige Geschäftsvorfälle """
+    holdings = DataElementField(type='bin', _d="Depotaufstellung")