2
2
import logging
3
3
from decimal import Decimal
4
4
5
- from fints .segments import HISPA1
6
5
from fints .segments .debit import HKDME , HKDSE
7
6
from mt940 .models import Balance
8
7
from sepaxml import SepaTransfer
9
8
10
9
from .connection import FinTSHTTPSConnection
11
- from .dialog import FinTSDialog
10
+ from .dialog import FinTSDialogOLD , FinTSDialog
12
11
from .formals import TwoStepParametersCommon
13
- from .message import FinTSMessage
12
+ from .message import FinTSMessageOLD
14
13
from .models import (
15
14
SEPAAccount , TANChallenge , TANChallenge3 ,
16
15
TANChallenge4 , TANChallenge5 , TANChallenge6 ,
17
16
)
18
- from .segments .accounts import HKSPA
17
+ from .segments import HIUPA4 , HIBPA3
18
+ from .segments .accounts import HKSPA , HKSPA1 , HISPA1
19
19
from .segments .auth import HKTAB , HKTAN
20
+ from .segments .dialog import HKSYN3 , HISYN4
20
21
from .segments .depot import HKWPD
21
- from .segments .saldo import HKSAL
22
+ from .segments .saldo import HKSAL6 , HKSAL7 , HISAL6 , HISAL7
22
23
from .segments .statement import HKKAZ
23
24
from .segments .transfer import HKCCM , HKCCS
24
25
from .utils import MT535_Miniparser , Password , mt940_to_array
26
+ from .formals import Account3 , KTI1 , BankIdentifier , SynchronisationMode
25
27
26
28
logger = logging .getLogger (__name__ )
27
29
30
+ SYSTEM_ID_UNASSIGNED = '0'
28
31
29
32
class FinTS3Client :
30
33
version = 300
31
34
32
- def __init__ (self ):
35
+ def __init__ (self , bank_identifier , user_id , customer_id = None ):
33
36
self .accounts = []
37
+ if isinstance (bank_identifier , BankIdentifier ):
38
+ self .bank_identifier = bank_identifier
39
+ elif isinstance (bank_identifier , str ):
40
+ self .bank_identifier = BankIdentifier ('280' , bank_identifier )
41
+ else :
42
+ raise TypeError ("bank_identifier must be BankIdentifier or str (BLZ)" )
43
+ self .system_id = SYSTEM_ID_UNASSIGNED
44
+ self .user_id = user_id
45
+ self .customer_id = customer_id or user_id
46
+ self .bpd_version = 0
47
+ self .bpa = None
48
+ self .bpd = []
49
+ self .upd_version = 0
50
+ self .product_name = 'pyfints'
51
+ self .product_version = '0.2'
52
+
53
+ def _new_dialog (self , lazy_init = False ):
54
+ raise NotImplemented ()
34
55
35
- def _new_dialog (self ):
56
+ def _new_message (self , dialog : FinTSDialogOLD , segments , tan = None ):
36
57
raise NotImplemented ()
37
58
38
- def _new_message (self , dialog : FinTSDialog , segments , tan = None ):
59
+ def _ensure_system_id (self ):
39
60
raise NotImplemented ()
40
61
62
+ def process_institute_response (self , message ):
63
+ bpa = message .find_segment_first (HIBPA3 )
64
+ if bpa :
65
+ self .bpa = bpa
66
+ self .bpd_version = bpa .bpd_version
67
+ self .bpd = list (
68
+ message .find_segments (
69
+ callback = lambda m : len (m .header .type ) == 6 and m .header .type [1 ] == 'I' and m .header .type [5 ] == 'S'
70
+ )
71
+ )
72
+
73
+ for seg in message .find_segments (HIUPA4 ):
74
+ self .upd_version = seg .upd_version
75
+
76
+ def find_bpd (self , type ):
77
+ for seg in self .bpd :
78
+ if seg .header .type == type :
79
+ yield seg
80
+
41
81
def get_sepa_accounts (self ):
42
82
"""
43
83
Returns a list of SEPA accounts
44
84
45
85
:return: List of SEPAAccount objects.
46
86
"""
47
- dialog = self ._new_dialog ()
48
- dialog .sync ()
49
- dialog .init ()
50
-
51
- def _get_msg ():
52
- return self ._new_message (dialog , [
53
- HKSPA (3 , None , None , None )
54
- ])
55
-
56
- with self .pin .protect ():
57
- logger .debug ('Sending HKSPA: {}' .format (_get_msg ()))
58
-
59
- resp = dialog .send (_get_msg ())
60
- logger .debug ('Got HKSPA response: {}' .format (resp ))
61
- dialog .end ()
62
87
88
+ with self ._new_dialog () as dialog :
89
+ response = dialog .send (HKSPA1 ())
90
+
63
91
self .accounts = []
64
- for seg in resp .find_segments (HISPA1 ):
92
+ for seg in response .find_segments (HISPA1 ):
65
93
self .accounts .extend (seg .accounts )
66
94
67
- return self .accounts
95
+ return [ a for a in [ acc . as_sepa_account () for acc in self .accounts ] if a ]
68
96
69
97
def get_statement (self , account : SEPAAccount , start_date : datetime .datetime , end_date : datetime .date ):
70
98
"""
@@ -121,7 +149,7 @@ def _get_msg():
121
149
dialog .end ()
122
150
return statement
123
151
124
- def _create_statement_message (self , dialog : FinTSDialog , account : SEPAAccount , start_date , end_date , touchdown ):
152
+ def _create_statement_message (self , dialog : FinTSDialogOLD , account : SEPAAccount , start_date , end_date , touchdown ):
125
153
hversion = dialog .hkkazversion
126
154
127
155
if hversion in (4 , 5 , 6 ):
@@ -153,35 +181,35 @@ def get_balance(self, account: SEPAAccount):
153
181
:param account: SEPA account to fetch the balance
154
182
:return: A mt940.models.Balance object
155
183
"""
156
- # init dialog
157
- dialog = self ._new_dialog ()
158
- dialog .sync ()
159
- dialog .init ()
160
-
161
- # execute job
162
- def _get_msg ():
163
- return self ._create_balance_message (dialog , account )
164
-
165
- with self .pin .protect ():
166
- logger .debug ('Sending HKSAL: {}' .format (_get_msg ()))
167
-
168
- resp = dialog .send (_get_msg ())
169
- logger .debug ('Got HKSAL response: {}' .format (resp ))
170
184
171
- # end dialog
172
- dialog .end ()
185
+ max_hksal_version = max (
186
+ (seg .header .version for seg in self .find_bpd ('HISALS' )),
187
+ default = 6
188
+ )
173
189
174
- # find segment and split up to balance part
175
- seg = resp ._find_segment ('HISAL' )
176
- arr = seg [4 ]
190
+ if max_hksal_version in (1 , 2 , 3 , 4 , 5 , 6 ):
191
+ seg = HKSAL6 (
192
+ Account3 .from_sepa_account (account ),
193
+ False
194
+ )
195
+ elif max_hksal_version == 7 :
196
+ seg = HKSAL7 (
197
+ KTI1 .from_sepa_account (account ),
198
+ False
199
+ )
200
+ else :
201
+ raise ValueError ('Unsupported HKSAL version {}' .format (max_hksal_version ))
177
202
178
- # get balance date
179
- date = datetime .datetime .strptime (arr [3 ], "%Y%m%d" ).date ()
180
203
181
- # return balance
182
- return Balance (arr [0 ], arr [1 ], date , currency = arr [2 ])
204
+ with self ._new_dialog () as dialog :
205
+ response = dialog .send (seg )
206
+
207
+ # find segment
208
+ seg = response .find_segment_first ((HISAL6 , HISAL7 ))
209
+ if seg :
210
+ return seg .balance_booked .as_mt940_Balance ()
183
211
184
- def _create_balance_message (self , dialog : FinTSDialog , account : SEPAAccount ):
212
+ def _create_balance_message (self , dialog : FinTSDialogOLD , account : SEPAAccount ):
185
213
hversion = dialog .hksalversion
186
214
187
215
if hversion in (1 , 2 , 3 , 4 , 5 , 6 ):
@@ -242,7 +270,7 @@ def _get_msg():
242
270
logger .debug ('No HIWPD response segment found - maybe account has no holdings?' )
243
271
return []
244
272
245
- def _create_get_holdings_message (self , dialog : FinTSDialog , account : SEPAAccount ):
273
+ def _create_get_holdings_message (self , dialog : FinTSDialogOLD , account : SEPAAccount ):
246
274
hversion = dialog .hksalversion
247
275
248
276
if hversion in (1 , 2 , 3 , 4 , 5 , 6 ):
@@ -264,7 +292,7 @@ def _create_get_holdings_message(self, dialog: FinTSDialog, account: SEPAAccount
264
292
)
265
293
])
266
294
267
- def _create_send_tan_message (self , dialog : FinTSDialog , challenge : TANChallenge , tan ):
295
+ def _create_send_tan_message (self , dialog : FinTSDialogOLD , challenge : TANChallenge , tan ):
268
296
return self ._new_message (dialog , [
269
297
HKTAN (3 , '2' , challenge .reference , '' , challenge .version )
270
298
], tan )
@@ -454,7 +482,7 @@ def get_tan_methods(self):
454
482
dialog .end ()
455
483
return dialog .tan_mechs
456
484
457
- def _create_get_tan_description_message (self , dialog : FinTSDialog ):
485
+ def _create_get_tan_description_message (self , dialog : FinTSDialogOLD ):
458
486
return self ._new_message (dialog , [
459
487
HKTAB (3 )
460
488
])
@@ -483,18 +511,35 @@ def get_tan_description(self):
483
511
484
512
class FinTS3PinTanClient (FinTS3Client ):
485
513
486
- def __init__ (self , blz , username , pin , server ):
487
- self .username = username
488
- self .blz = blz
514
+ def __init__ (self , bank_identifier , user_id , pin , server , customer_id = None ):
489
515
self .pin = Password (pin )
490
516
self .connection = FinTSHTTPSConnection (server )
491
- self .systemid = 0
492
- super ().__init__ ()
517
+ super ().__init__ (bank_identifier = bank_identifier , user_id = user_id , customer_id = customer_id )
493
518
494
- def _new_dialog (self ):
495
- dialog = FinTSDialog ( self . blz , self . username , self . pin , self . systemid , self . connection )
496
- return dialog
519
+ def _new_dialog (self , lazy_init = False ):
520
+ if not lazy_init :
521
+ self . _ensure_system_id ()
497
522
498
- def _new_message (self , dialog : FinTSDialog , segments , tan = None ):
499
- return FinTSMessage (self .blz , self .username , self .pin , dialog .systemid , dialog .dialogid , dialog .msgno ,
523
+ return FinTSDialog (self , lazy_init = lazy_init )
524
+
525
+ # FIXME
526
+ # dialog = FinTSDialogOLD(self.blz, self.username, self.pin, self.systemid, self.connection)
527
+ # return dialog
528
+
529
+ def _new_message (self , dialog : FinTSDialogOLD , segments , tan = None ):
530
+ return FinTSMessageOLD (self .blz , self .username , self .pin , dialog .systemid , dialog .dialogid , dialog .msgno ,
500
531
segments , dialog .tan_mechs , tan )
532
+
533
+ def _ensure_system_id (self ):
534
+ if self .system_id != SYSTEM_ID_UNASSIGNED :
535
+ return
536
+
537
+ with self ._new_dialog (lazy_init = True ) as dialog :
538
+ response = dialog .init (
539
+ HKSYN3 (SynchronisationMode .NEW_SYSTEM_ID ),
540
+ )
541
+
542
+ seg = response .find_segment_first (HISYN4 )
543
+ if not seg :
544
+ raise ValueError ('Could not find system_id' )
545
+ self .system_id = seg .system_id
0 commit comments