]> git.ipfire.org Git - thirdparty/python-fints.git/commitdiff
First working debit (multiple, COR1)
authorHenryk Plötz <henryk@ploetzli.ch>
Sun, 26 Aug 2018 20:06:47 +0000 (22:06 +0200)
committerRaphael Michel <mail@raphaelmichel.de>
Mon, 3 Dec 2018 18:34:29 +0000 (19:34 +0100)
fints/client.py
fints/dialog.py
fints/formals.py
fints/segments/debit.py

index 26d5ce7ef2660c385841abd5f4324c029c787361..c370efc5db1b1571d93505b6e4eea93ccdf172e9 100644 (file)
@@ -4,7 +4,6 @@ from decimal import Decimal
 from contextlib import contextmanager
 from collections import OrderedDict
 
-from fints.segments.debit import HKDME, HKDSE
 from mt940.models import Balance
 from sepaxml import SepaTransfer
 
@@ -14,6 +13,7 @@ from .formals import (
     KTI1, Account3, BankIdentifier,
     SynchronisationMode, TwoStepParametersCommon,
     TANMediaType2, TANMediaClass4, CUSTOMER_ID_ANONYMOUS,
+    DescriptionRequired,
 )
 from .message import FinTSInstituteMessage
 from .models import (
@@ -25,10 +25,11 @@ from .security import (
     PinTanTwoStepAuthenticationMechanism,
 )
 from .segments import HIBPA3, HIRMG2, HIRMS2, HIUPA4, HIPINS1, HKKOM4
-from .segments.accounts import HISPA1, HKSPA, HKSPA1
-from .segments.auth import HKTAB, HKTAN, HKTAB4, HKTAB5, HKTAN3, HKTAN5
+from .segments.accounts import HISPA1, HKSPA1
+from .segments.auth import HKTAB4, HKTAB5, HKTAN3, HKTAN5
 from .segments.depot import HKWPD5, HKWPD6
 from .segments.dialog import HISYN4, HKSYN3
+from .segments.debit import HKDSE1, HKDSE2, HKDME1, HKDME2, HKDMC1
 from .segments.saldo import HKSAL5, HKSAL6, HKSAL7
 from .segments.statement import HKKAZ5, HKKAZ6, HKKAZ7
 from .segments.transfer import HKCCM1, HKCCS1
@@ -435,7 +436,7 @@ class FinTS3Client:
         :param control_sum: Sum of all transfers (required if there are multiple)
         :param currency: Transfer currency
         :param book_as_single: Kindly ask the bank to put multiple transactions as separate lines on the bank statement (defaults to ``False``)
-        :return: Returns a TANChallenge object
+        :return: Returns a TANChallenge object  FIXME Wrong
         """
 
         with self._get_dialog() as dialog:
@@ -445,7 +446,7 @@ class FinTS3Client:
                 command_class = HKCCS1
 
             hiccxs, hkccx = self._find_highest_supported_command(
-                HKCCM1 if multiple else HKCCS1,
+                command_class,
                 return_parameter_segment=True
             )
 
@@ -456,7 +457,7 @@ class FinTS3Client:
             )
 
             if multiple:
-                if hiccxs.parameter.sum_amount_required and not control_sum:
+                if hiccxs.parameter.sum_amount_required and control_sum is None:
                     raise ValueError("Control sum required.")
                 if book_as_single and not hiccxs.parameter.single_booking_allowed:
                     # FIXME Only do a warning and fall-back to book_as_single=False?
@@ -475,22 +476,9 @@ class FinTS3Client:
             # FIXME Properly find return code
             return True
 
-    def _get_start_sepa_debit_message(self, dialog, account: SEPAAccount, pain_message: str, tan_method,
-                                      tan_description, multiple, control_sum, currency, book_as_single):
-        if multiple:
-            if not control_sum:
-                raise ValueError("Control sum required.")
-            segreq = HKDME(3, account, pain_message, control_sum, currency, book_as_single)
-        else:
-            segreq = HKDSE(3, account, pain_message)
-        segtan = HKTAN(4, '4', '', tan_description, tan_method.version)
-        return self._new_message(dialog, [
-            segreq,
-            segtan
-        ])
-
-    def start_sepa_debit(self, account: SEPAAccount, pain_message: str, tan_method, tan_description='',
-                         multiple=False, control_sum=None, currency='EUR', book_as_single=False):
+    def start_sepa_debit(self, account: SEPAAccount, pain_message: str, multiple=False, cor1=False,
+                         control_sum=None, currency='EUR', book_as_single=False,
+                         pain_descriptor='urn:iso:std:iso:20022:tech:xsd:pain.008.003.01'):
         """
         Start a custom SEPA debit.
 
@@ -502,25 +490,51 @@ class FinTS3Client:
         :param control_sum: Sum of all debits (required if there are multiple)
         :param currency: Debit currency
         :param book_as_single: Kindly ask the bank to put multiple transactions as separate lines on the bank statement (defaults to ``False``)
-        :return: Returns a TANChallenge object
+        :return: Returns a TANChallenge object  FIXME Wrong
         """
-        dialog = self._get_dialog()
-        dialog.sync()
-        dialog.tan_mechs = [tan_method]
-        dialog.init()
-
-        with self.pin.protect():
-            logger.debug('Sending: {}'.format(self._get_start_sepa_debit_message(
-                dialog, account, pain_message, tan_method, tan_description, multiple, control_sum, currency,
-                book_as_single
-            )))
-
-        resp = dialog.send(self._get_start_sepa_debit_message(
-            dialog, account, pain_message, tan_method, tan_description, multiple, control_sum, currency,
-            book_as_single
-        ))
-        logger.debug('Got response: {}'.format(resp))
-        return self._tan_requiring_response(dialog, resp)
+
+        with self._get_dialog() as dialog:
+            if multiple:
+                if cor1:
+                    command_candidates = (HKDMC1, )
+                else:
+                    command_candidates = (HKDME1, HKDME2)
+            else:
+                if cor1:
+                    raise Exception("Can't process multiple=False cor1=True")
+                else:
+                    command_candidates = (HKDSE1, HKDSE2)
+
+            hidxxs, hkdxx = self._find_highest_supported_command(
+                *command_candidates,
+                return_parameter_segment=True
+            )
+
+            seg = hkdxx(
+                account=hkdxx._fields['account'].type.from_sepa_account(account),
+                sepa_descriptor=pain_descriptor,
+                sepa_pain_message=pain_message,
+            )
+
+            if multiple:
+                if hidxxs.parameter.sum_amount_required and control_sum is None:
+                    raise ValueError("Control sum required.")
+                if book_as_single and not hidxxs.parameter.single_booking_allowed:
+                    # FIXME Only do a warning and fall-back to book_as_single=False?
+                    raise ValueError("Single booking not allowed by bank.")
+
+                if control_sum:
+                    seg.sum_amount.amount = control_sum
+                    seg.sum_amount.currency = currency
+
+                if book_as_single:
+                    seg.request_single_booking = True
+
+            return self._send_with_possible_retry(dialog, seg, self._continue_start_sepa_debit)
+
+    def _continue_start_sepa_debit(self, command_seg, response):
+            # FIXME Properly return something
+            return True
 
     def add_response_callback(self, cb):
         # FIXME document
@@ -748,6 +762,8 @@ class FinTS3PinTanClient(FinTS3Client):
                 for resp in response.responses(tan_seg):
                     if resp.code == '0030':
                         return NeedTANResponse(command_seg, response.find_segment_first('HITAN'), resume_func)
+                    if resp.code.startswith('9'):
+                        raise Exception("Error response: {!r}".format(response))
             else:
                 response = dialog.send(command_seg)
 
index 3f731b213a24db5e787cf7795dda93e906829100..6a020f1e9531c12df4efe380d449a90797eea47e 100644 (file)
@@ -8,8 +8,8 @@ from .formals import (
 from .message import (
     FinTSCustomerMessage, FinTSMessage, MessageDirection,
 )
-from .segments.auth import HKIDN, HKIDN2, HKSYN, HKVVB, HKVVB3
-from .segments.dialog import HKEND, HKEND1
+from .segments.auth import HKIDN2, HKVVB3
+from .segments.dialog import HKEND1
 from .segments.message import HNHBK3, HNHBS1
 from .utils import compress_datablob, decompress_datablob
 
index b3791fde3d497b0f7f6ff796401592967726bc46..47c39a96673689286b5de2635b43a14cc3a2852c 100644 (file)
@@ -704,3 +704,62 @@ class CommunicationParameter2(DataElementGroup):
     address_adjunct = DataElementField(type='an', max_length=512, required=False, _d="Kommunikationsadresszusatz")
     filter_function = DataElementField(type='an', length=3, required=False, _d="Filterfunktion")
     filter_function_version = DataElementField(type='num', max_length=3, required=False, _d="Version der Filterfunktion")
+
+class ScheduledDebitParameter1(DataElementGroup):
+    """Parameter terminierte SEPA-Einzellastschrift einreichen, version 1
+
+    Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Messages -- Multibankfähige Geschäftsvorfälle """
+    min_advance_notice_FNAL_RCUR = DataElementField(type='num', max_length=4, _d="Minimale Vorlaufzeit FNAL/RCUR")
+    max_advance_notice_FNAL_RCUR = DataElementField(type='num', max_length=4, _d="Maximale Vorlaufzeit FNAL/RCUR")
+    min_advance_notice_FRST_OOFF = DataElementField(type='num', max_length=4, _d="Minimale Vorlaufzeit FRST/OOFF")
+    max_advance_notice_FRST_OOFF = DataElementField(type='num', max_length=4, _d="Maximale Vorlaufzeit FRST/OOFF")
+
+class ScheduledDebitParameter2(DataElementGroup):
+    """Parameter terminierte SEPA-Einzellastschrift einreichen, version 2
+
+    Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Messages -- Multibankfähige Geschäftsvorfälle """
+    min_advance_notice = DataElementField(type='num', max_length=4, _d="Minimale Vorlaufzeit SEPA-Lastschrift")
+    max_advance_notice = DataElementField(type='num', max_length=4, _d="Maximale Vorlaufzeit SEPA-Lastschrift")
+    allowed_purpose_codes = DataElementField(type='an', max_length=4096, required=False, _d="Zulässige purpose codes")
+    supported_sepa_formats = DataElementField(type='an', max_length=256, max_count=9, required=False, _d="Unterstützte SEPA-Datenformate")
+
+class ScheduledBatchDebitParameter1(DataElementGroup):
+    """Parameter terminierte SEPA-Sammellastschrift einreichen, version 1
+
+    Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Messages -- Multibankfähige Geschäftsvorfälle """
+    min_advance_notice_FNAL_RCUR = DataElementField(type='num', max_length=4, _d="Minimale Vorlaufzeit FNAL/RCUR")
+    max_advance_notice_FNAL_RCUR = DataElementField(type='num', max_length=4, _d="Maximale Vorlaufzeit FNAL/RCUR")
+    min_advance_notice_FRST_OOFF = DataElementField(type='num', max_length=4, _d="Minimale Vorlaufzeit FRST/OOFF")
+    max_advance_notice_FRST_OOFF = DataElementField(type='num', max_length=4, _d="Maximale Vorlaufzeit FRST/OOFF")
+    max_debit_count = DataElementField(type='num', max_length=7, _d="Maximale Anzahl DirectDebitTransfer TransactionInformation")
+    sum_amount_required = DataElementField(type='jn', _d="Summenfeld benötigt")
+    single_booking_allowed = DataElementField(type='jn', _d="Einzelbuchung erlaubt")
+
+class ScheduledBatchDebitParameter2(DataElementGroup):
+    """Parameter terminierte SEPA-Sammellastschrift einreichen, version 2
+
+    Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Messages -- Multibankfähige Geschäftsvorfälle """
+    min_advance_notice = DataElementField(type='num', max_length=4, _d="Minimale Vorlaufzeit SEPA-Lastschrift")
+    max_advance_notice = DataElementField(type='num', max_length=4, _d="Maximale Vorlaufzeit SEPA-Lastschrift")
+    max_debit_count = DataElementField(type='num', max_length=7, _d="Maximale Anzahl DirectDebitTransfer TransactionInformation")
+    sum_amount_required = DataElementField(type='jn', _d="Summenfeld benötigt")
+    single_booking_allowed = DataElementField(type='jn', _d="Einzelbuchung erlaubt")
+    allowed_purpose_codes = DataElementField(type='an', max_length=4096, required=False, _d="Zulässige purpose codes")
+    supported_sepa_formats = DataElementField(type='an', max_length=256, max_count=9, required=False, _d="Unterstützte SEPA-Datenformate")
+
+
+class ScheduledCOR1BatchDebitParameter1(DataElementGroup):
+    """Parameter terminierte SEPA-COR1-Sammellastschrift, version 1
+
+    Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Messages -- Multibankfähige Geschäftsvorfälle """
+    max_debit_count = DataElementField(type='num', max_length=7, _d="Maximale Anzahl DirectDebitTransfer TransactionInformation")
+    sum_amount_required = DataElementField(type='jn', _d="Summenfeld benötigt")
+    single_booking_allowed = DataElementField(type='jn', _d="Einzelbuchung erlaubt")
+    min_advance_notice_FNAL_RCUR = DataElementField(type='num', max_length=4, _d="Minimale Vorlaufzeit FNAL/RCUR")
+    max_advance_notice_FNAL_RCUR = DataElementField(type='num', max_length=4, _d="Maximale Vorlaufzeit FNAL/RCUR")
+    min_advance_notice_FRST_OOFF = DataElementField(type='num', max_length=4, _d="Minimale Vorlaufzeit FRST/OOFF")
+    max_advance_notice_FRST_OOFF = DataElementField(type='num', max_length=4, _d="Maximale Vorlaufzeit FRST/OOFF")
+    allowed_purpose_codes = DataElementField(type='an', max_length=4096, required=False, _d="Zulässige purpose codes")
+    supported_sepa_formats = DataElementField(type='an', max_length=256, max_count=9, required=False, _d="Unterstützte SEPA-Datenformate")
+
+
index d55fe1d3345ee7ceb59416cb857ca3e7e75e0cbc..b26e66923f45e2a6bb690f90253b9dbd752f4391 100644 (file)
-from . import FinTS3SegmentOLD
+from . import FinTS3Segment
 from ..models import SEPAAccount
+from ..fields import DataElementField, DataElementGroupField
+from ..formals import ScheduledCOR1BatchDebitParameter1, KTI1, Amount1, ScheduledBatchDebitParameter1, ScheduledBatchDebitParameter2, ScheduledDebitParameter1, ScheduledDebitParameter2
+from . import ParameterSegment
 
+class HKDSE1(FinTS3Segment):
+    """Terminierte SEPA-Einzellastschrift einreichen, version 1
 
-class HKDSE(FinTS3SegmentOLD):
-    """
-    HKDSE (Einreichung terminierter SEPA-Einzellastschrift)
-    Section C.10.2.5.4.1
-    """
-    type = 'HKDSE'
-
-    def __init__(self, segno, account: SEPAAccount, pain_msg):
-        self.version = 1
-        sepa_descriptor = 'urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.008.003.02'
-        msg = ':'.join([
-            account.iban,
-            account.bic
-        ])
-        data = [
-            msg,
-            sepa_descriptor,
-            '@{}@{}'.format(len(pain_msg), pain_msg)
-        ]
-        super().__init__(segno, data)
-
-
-class HKDME(FinTS3SegmentOLD):
-    """
-    HKDME (Einreichung terminierter SEPA-Sammellastschrift)
-    Section C.10.3.2.2.1
-    """
-    type = 'HKDME'
-
-    def __init__(self, segno, account: SEPAAccount, pain_msg, control_sum, currency, book_as_single):
-        self.version = 1
-        sepa_descriptor = 'urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.008.003.02'
-        msg = ':'.join([
-            account.iban,
-            account.bic
-        ])
-        data = [
-            msg,
-            str(control_sum).replace('.', ',') + ':' + currency,
-            'J' if book_as_single else '',
-            sepa_descriptor,
-            '@{}@{}'.format(len(pain_msg), pain_msg)
-        ]
-        super().__init__(segno, data)
+    Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Messages -- Multibankfähige Geschäftsvorfälle """
+    account = DataElementGroupField(type=KTI1, _d="Kontoverbindung international")
+    sepa_descriptor = DataElementField(type='an', max_length=256, _d="SEPA Descriptor")
+    sepa_pain_message = DataElementField(type='bin', _d="SEPA pain message")
+
+class HKDSE2(FinTS3Segment):
+    """Terminierte SEPA-Einzellastschrift einreichen, version 2
+
+    Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Messages -- Multibankfähige Geschäftsvorfälle """
+    account = DataElementGroupField(type=KTI1, _d="Kontoverbindung international")
+    sepa_descriptor = DataElementField(type='an', max_length=256, _d="SEPA Descriptor")
+    sepa_pain_message = DataElementField(type='bin', _d="SEPA pain message")
+
+class BatchDebitBase(FinTS3Segment):
+    account = DataElementGroupField(type=KTI1, _d="Kontoverbindung international")
+    sum_amount = DataElementGroupField(type=Amount1, _d="Summenfeld")
+    request_single_booking = DataElementField(type='jn', _d="Einzelbuchung gewünscht")
+    sepa_descriptor = DataElementField(type='an', max_length=256, _d="SEPA Descriptor")
+    sepa_pain_message = DataElementField(type='bin', _d="SEPA pain message")
+
+class HKDME1(BatchDebitBase):
+    """Einreichung terminierter SEPA-Sammellastschrift, version 1
+
+    Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Messages -- Multibankfähige Geschäftsvorfälle """
+
+class HKDME2(BatchDebitBase):
+    """Einreichung terminierter SEPA-Sammellastschrift, version 2
+
+    Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Messages -- Multibankfähige Geschäftsvorfälle """
+
+class HKDMC1(BatchDebitBase):
+    """Terminierte SEPA-COR1-Sammellastschrift einreichen, version 1
+
+    Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Messages -- Multibankfähige Geschäftsvorfälle """
+
+
+class DebitResponseBase(FinTS3Segment):
+    task_id = DataElementField(type='an', max_length=99, required=False, _d="Auftragsidentifikation")
+
+class HIDSE1(DebitResponseBase):
+    """Einreichung terminierter SEPA-Einzellastschrift bestätigen, version 1
+
+    Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Messages -- Multibankfähige Geschäftsvorfälle """
+
+class HIDSE2(DebitResponseBase):
+    """Einreichung terminierter SEPA-Einzellastschrift bestätigen, version 2
+
+    Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Messages -- Multibankfähige Geschäftsvorfälle """
+
+class HIDME1(DebitResponseBase):
+    """Einreichung terminierter SEPA-Sammellastschrift bestätigen, version 1
+
+    Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Messages -- Multibankfähige Geschäftsvorfälle """
+
+class HIDME2(DebitResponseBase):
+    """Einreichung terminierter SEPA-Sammellastschrift bestätigen, version 2
+
+    Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Messages -- Multibankfähige Geschäftsvorfälle """
+
+class HIDMC1(DebitResponseBase):
+    """Einreichung terminierter SEPA-COR1-Sammellastschrift bestätigen, version 1
+
+    Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Messages -- Multibankfähige Geschäftsvorfälle """
+
+class HIDSES1(ParameterSegment):
+    """Terminierte SEPA-Einzellastschrift einreichen Parameter, version 1
+
+    Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Messages -- Multibankfähige Geschäftsvorfälle """
+    parameter = DataElementGroupField(type=ScheduledDebitParameter1, _d="Parameter terminierte SEPA-Sammellastschrift einreichen")
+
+class HIDSES2(ParameterSegment):
+    """Terminierte SEPA-Einzellastschrift einreichen Parameter, version 2
+
+    Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Messages -- Multibankfähige Geschäftsvorfälle """
+    parameter = DataElementGroupField(type=ScheduledDebitParameter2, _d="Parameter terminierte SEPA-Sammellastschrift einreichen")
+
+class HIDMES1(ParameterSegment):
+    """Terminierte SEPA-Sammellastschrift einreichen Parameter, version 1
+
+    Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Messages -- Multibankfähige Geschäftsvorfälle """
+    parameter = DataElementGroupField(type=ScheduledBatchDebitParameter1, _d="Parameter terminierte SEPA-Sammellastschrift einreichen")
+
+class HIDMES2(ParameterSegment):
+    """Terminierte SEPA-Sammellastschrift einreichen Parameter, version 2
+
+    Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Messages -- Multibankfähige Geschäftsvorfälle """
+    parameter = DataElementGroupField(type=ScheduledBatchDebitParameter2, _d="Parameter terminierte SEPA-Sammellastschrift einreichen")
+
+class HIDMCS1(ParameterSegment):
+    """Terminierte SEPA-COR1-Sammellastschrift Parameter, version 1
+
+    Source: FinTS Financial Transaction Services, Schnittstellenspezifikation, Messages -- Multibankfähige Geschäftsvorfälle """
+    parameter = DataElementGroupField(type=ScheduledCOR1BatchDebitParameter1, _d="Parameter terminierte SEPA-COR1-Sammellastschrift")