Skip to content

Commit 0b7619f

Browse files
henrykraphaelm
authored andcommitted
Improve BPD and UPD: Is now SegmentSequence, allows full querying.
Improve get_balance(): Ignore HISALS versions we don't know, don't blindly ask for highest version
1 parent c7feda9 commit 0b7619f

File tree

3 files changed

+46
-48
lines changed

3 files changed

+46
-48
lines changed

fints/client.py

Lines changed: 23 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,14 @@
2929
from .segments.saldo import HISAL5, HISAL6, HISAL7, HKSAL5, HKSAL6, HKSAL7
3030
from .segments.statement import HKKAZ
3131
from .segments.transfer import HKCCM, HKCCS
32+
from .types import SegmentSequence
3233
from .utils import MT535_Miniparser, Password, mt940_to_array
3334

3435
logger = logging.getLogger(__name__)
3536

3637
SYSTEM_ID_UNASSIGNED = '0'
3738

3839
class FinTS3Client:
39-
version = 300
40-
4140
def __init__(self, bank_identifier, user_id, customer_id=None):
4241
self.accounts = []
4342
if isinstance(bank_identifier, BankIdentifier):
@@ -51,10 +50,10 @@ def __init__(self, bank_identifier, user_id, customer_id=None):
5150
self.customer_id = customer_id or user_id
5251
self.bpd_version = 0
5352
self.bpa = None
54-
self.bpd = []
53+
self.bpd = SegmentSequence()
5554
self.upd_version = 0
5655
self.upa = None
57-
self.upd = []
56+
self.upd = SegmentSequence()
5857
self.allowed_security_functions = []
5958
self.selected_security_function = None
6059
self.product_name = 'pyfints'
@@ -74,7 +73,7 @@ def process_institute_response(self, message):
7473
if bpa:
7574
self.bpa = bpa
7675
self.bpd_version = bpa.bpd_version
77-
self.bpd = list(
76+
self.bpd = SegmentSequence(
7877
message.find_segments(
7978
callback = lambda m: len(m.header.type) == 6 and m.header.type[1] == 'I' and m.header.type[5] == 'S'
8079
)
@@ -84,7 +83,7 @@ def process_institute_response(self, message):
8483
if upa:
8584
self.upa = upa
8685
self.upd_version = upa.upd_version
87-
self.upd = list(
86+
self.upd = SegmentSequence(
8887
message.find_segments('HIUPD')
8988
)
9089

@@ -95,11 +94,6 @@ def process_institute_response(self, message):
9594
if self.selected_security_function is None:
9695
self.selected_security_function = self.allowed_security_functions[0]
9796

98-
def find_bpd(self, type):
99-
for seg in self.bpd:
100-
if seg.header.type == type:
101-
yield seg
102-
10397
def get_sepa_accounts(self):
10498
"""
10599
Returns a list of SEPA accounts
@@ -204,32 +198,26 @@ def get_balance(self, account: SEPAAccount):
204198
:return: A mt940.models.Balance object
205199
"""
206200

207-
max_hksal_version = max(
208-
(seg.header.version for seg in self.find_bpd('HISALS')),
209-
default=6
210-
)
211-
212-
clazz = {
213-
5: HKSAL5,
214-
6: HKSAL6,
215-
7: HKSAL7,
216-
}.get(max_hksal_version, None)
217-
218-
if clazz is None:
219-
raise ValueError('Unsupported HKSAL version {}'.format(max_hksal_version))
220-
221-
seg = clazz(
222-
account=clazz._fields['account'].type.from_sepa_account(account),
223-
all_accounts=False,
224-
)
225-
226201
with self._new_dialog() as dialog:
202+
max_hisals = self.bpd.find_segment_highest_version('HISALS', (5, 6, 7))
203+
if not max_hisals:
204+
raise ValueError('No supported HISALS version found')
205+
206+
hksal = {
207+
5: HKSAL5,
208+
6: HKSAL6,
209+
7: HKSAL7,
210+
}.get(max_hisals.header.version, None)
211+
212+
seg = hksal(
213+
account=hksal._fields['account'].type.from_sepa_account(account),
214+
all_accounts=False,
215+
)
216+
227217
response = dialog.send(seg)
228-
229-
# find segment
230-
seg = response.find_segment_first((HISAL5, HISAL6, HISAL7))
231-
if seg:
232-
return seg.balance_booked.as_mt940_Balance()
218+
219+
for resp in response.response_segments(seg, 'HISAL'):
220+
return resp.balance_booked.as_mt940_Balance()
233221

234222
def get_holdings(self, account: SEPAAccount):
235223
"""

fints/message.py

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,10 @@ def __iadd__(self, segment: FinTS3Segment):
2727
self.segments.append(segment)
2828
return self
2929

30-
def sign_prepare(self, auth_mech):
31-
pass
32-
33-
def sign_commit(self, auth_mech):
34-
pass
35-
36-
def encrypt(self, enc_mech):
37-
pass
38-
39-
def decrypt(self, enc_mech):
40-
pass
41-
30+
def response_segments(self, ref, *args, **kwargs):
31+
for segment in self.find_segments(*args, **kwargs):
32+
if segment.header.reference == ref.header.number:
33+
yield segment
4234

4335
class FinTSCustomerMessage(FinTSMessage):
4436
DIRECTION = MessageDirection.FROM_CUSTOMER

fints/types.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ def __init__(self, segments = None):
9797
parser = FinTS3Parser()
9898
data = parser.explode_segments(segments)
9999
segments = [parser.parse_segment(segment) for segment in data]
100-
self.segments = segments or []
100+
self.segments = list(segments) if segments else []
101101

102102
def render_bytes(self) -> bytes:
103103
from .parser import FinTS3Serializer
@@ -175,6 +175,24 @@ def find_segment_first(self, *args, **kwargs):
175175

176176
return None
177177

178+
def find_segment_highest_version(self, query=None, version=None, callback=None, recurse=True, default=None, *args, **kwargs):
179+
"""Finds the highest matching segment.
180+
181+
Same parameters as find_segments(), but returns the match with the highest version, or default if no match is found."""
182+
# FIXME Test
183+
184+
retval = None
185+
186+
for s in self.find_segments(query=query, version=version, callback=callback, recurse=recurse, *args, **kwargs):
187+
if not retval or s.header.version > retval.header.version:
188+
retval = s
189+
190+
if retval is None:
191+
return default
192+
193+
return retval
194+
195+
178196
class ContainerMeta(type):
179197
@classmethod
180198
def __prepare__(metacls, name, bases):

0 commit comments

Comments
 (0)