]> git.ipfire.org Git - thirdparty/python-fints.git/commitdiff
Improve BPD and UPD: Is now SegmentSequence, allows full querying.
authorHenryk Plötz <henryk@ploetzli.ch>
Mon, 20 Aug 2018 20:26:24 +0000 (22:26 +0200)
committerRaphael Michel <mail@raphaelmichel.de>
Mon, 3 Dec 2018 18:34:29 +0000 (19:34 +0100)
Improve get_balance(): Ignore HISALS versions we don't know, don't blindly ask for highest version

fints/client.py
fints/message.py
fints/types.py

index 4554b8dcd3371e273dd0f369c6deb8ac5e1683c7..e43c6f5a4259ec35fc575d2af66795750d39be7d 100644 (file)
@@ -29,6 +29,7 @@ from .segments.dialog import HISYN4, HKSYN3
 from .segments.saldo import HISAL5, HISAL6, HISAL7, HKSAL5, HKSAL6, HKSAL7
 from .segments.statement import HKKAZ
 from .segments.transfer import HKCCM, HKCCS
+from .types import SegmentSequence
 from .utils import MT535_Miniparser, Password, mt940_to_array
 
 logger = logging.getLogger(__name__)
@@ -36,8 +37,6 @@ logger = logging.getLogger(__name__)
 SYSTEM_ID_UNASSIGNED = '0'
 
 class FinTS3Client:
-    version = 300
-
     def __init__(self, bank_identifier, user_id, customer_id=None):
         self.accounts = []
         if isinstance(bank_identifier, BankIdentifier):
@@ -51,10 +50,10 @@ class FinTS3Client:
         self.customer_id = customer_id or user_id
         self.bpd_version = 0
         self.bpa = None
-        self.bpd = []
+        self.bpd = SegmentSequence()
         self.upd_version = 0
         self.upa = None
-        self.upd = []
+        self.upd = SegmentSequence()
         self.allowed_security_functions = []
         self.selected_security_function = None
         self.product_name = 'pyfints'
@@ -74,7 +73,7 @@ class FinTS3Client:
         if bpa:
             self.bpa = bpa
             self.bpd_version = bpa.bpd_version
-            self.bpd = list(
+            self.bpd = SegmentSequence(
                 message.find_segments(
                     callback = lambda m: len(m.header.type) == 6 and m.header.type[1] == 'I' and m.header.type[5] == 'S'
                 )
@@ -84,7 +83,7 @@ class FinTS3Client:
         if upa:
             self.upa = upa
             self.upd_version = upa.upd_version
-            self.upd = list(
+            self.upd = SegmentSequence(
                 message.find_segments('HIUPD')
             )
 
@@ -95,11 +94,6 @@ class FinTS3Client:
                     if self.selected_security_function is None:
                         self.selected_security_function = self.allowed_security_functions[0]
 
-    def find_bpd(self, type):
-        for seg in self.bpd:
-            if seg.header.type == type:
-                yield seg
-
     def get_sepa_accounts(self):
         """
         Returns a list of SEPA accounts
@@ -204,32 +198,26 @@ class FinTS3Client:
         :return: A mt940.models.Balance object
         """
 
-        max_hksal_version = max(
-            (seg.header.version for seg in self.find_bpd('HISALS')),
-            default=6
-        )
-
-        clazz = {
-            5: HKSAL5,
-            6: HKSAL6,
-            7: HKSAL7,
-        }.get(max_hksal_version, None)
-
-        if clazz is None:
-            raise ValueError('Unsupported HKSAL version {}'.format(max_hksal_version))
-
-        seg = clazz(
-            account=clazz._fields['account'].type.from_sepa_account(account),
-            all_accounts=False,
-        )
-
         with self._new_dialog() as dialog:
+            max_hisals = self.bpd.find_segment_highest_version('HISALS', (5, 6, 7))
+            if not max_hisals:
+                raise ValueError('No supported HISALS version found')
+
+            hksal = {
+                5: HKSAL5,
+                6: HKSAL6,
+                7: HKSAL7,
+            }.get(max_hisals.header.version, None)
+
+            seg = hksal(
+                account=hksal._fields['account'].type.from_sepa_account(account),
+                all_accounts=False,
+            )
+
             response = dialog.send(seg)
-        
-        # find segment
-        seg = response.find_segment_first((HISAL5, HISAL6, HISAL7))
-        if seg:
-            return seg.balance_booked.as_mt940_Balance()
+
+            for resp in response.response_segments(seg, 'HISAL'):
+                return resp.balance_booked.as_mt940_Balance()
 
     def get_holdings(self, account: SEPAAccount):
         """
index 67ebfeec828490de6aebcdc35c42a745439c8307..ddf87830cb049d955233a9bcbb9ee9f487ac3b8a 100644 (file)
@@ -27,18 +27,10 @@ class FinTSMessage(SegmentSequence):
         self.segments.append(segment)
         return self
 
-    def sign_prepare(self, auth_mech):
-        pass
-
-    def sign_commit(self, auth_mech):
-        pass
-
-    def encrypt(self, enc_mech):
-        pass
-
-    def decrypt(self, enc_mech):
-        pass
-
+    def response_segments(self, ref, *args, **kwargs):
+        for segment in self.find_segments(*args, **kwargs):
+            if segment.header.reference == ref.header.number:
+                yield segment
 
 class FinTSCustomerMessage(FinTSMessage):
     DIRECTION = MessageDirection.FROM_CUSTOMER
index e5876fa537059db0a411202e9a3ebd557f7b930b..689276fb3adaf7bb56a364f1d4c662aea272c414 100644 (file)
@@ -97,7 +97,7 @@ class SegmentSequence:
             parser = FinTS3Parser()
             data = parser.explode_segments(segments)
             segments = [parser.parse_segment(segment) for segment in data]
-        self.segments = segments or []
+        self.segments = list(segments) if segments else []
 
     def render_bytes(self) -> bytes:
         from .parser import FinTS3Serializer
@@ -175,6 +175,24 @@ class SegmentSequence:
 
         return None
 
+    def find_segment_highest_version(self, query=None, version=None, callback=None, recurse=True, default=None, *args, **kwargs):
+        """Finds the highest matching segment.
+
+        Same parameters as find_segments(), but returns the match with the highest version, or default if no match is found."""
+        # FIXME Test
+
+        retval = None
+
+        for s in self.find_segments(query=query, version=version, callback=callback, recurse=recurse, *args, **kwargs):
+            if not retval or s.header.version > retval.header.version:
+                retval = s
+
+        if retval is None:
+            return default
+
+        return retval
+
+
 class ContainerMeta(type):
     @classmethod
     def __prepare__(metacls, name, bases):