Als ich mein erstes Girokonto Anfang der 2000er Jahre eröffnet habe, war die Bankenwelt eine ganz andere. Um Überweisungen zu tätigen, musste ich am Bankenschalter in den altehrwürdigen Hallen der örtlichen Sparkassenfiliale vorstellig werden und das Überweisungsformular mit Durchschlag ausfüllen. Ausfüllhilfen gabs nicht – mit meiner Sauklaue hatte ich mich besonders ins Zeug zu legen, damit das angewiesene Geld dort landete, wo ich es hinüberweisen wollte. Bei jeder weiteren Überweisung ging die Schönschriftübung wieder von vorne los. Kontoauszüge gabs damals auch nur bei der Bank, im Mini A5-Format auf blassrosa Thermopapier. Aus Ausgleich hierzu gab es allerdings recht üppige Guthabenzinsen auf dem Girokonto.
Ein paar Jährchen später stieg ich auf das Onlinebanking um – Features wie Vorlagen oder die Möglichkeit, neue Überweisungen aus bestehenden Umsätzen heraus zu erstellen, mochte ich seitdem nicht mehr missen. Auch die Sache mit den Kontoauszügen ist bequemer geworden, da sie im Onlinebanking als PDF-Dateien zur Verfügung standen. Nach und nach kamen weitere Features hinzu – so ist es beispielsweise seit einigen Jahren möglich, Aufträge elektronisch in Form von SEPA XML Dateien direkt bei der Bank einzureichen. Die Erstellung solcher XML Dateien greife ich als Thema in diesem Beitrag auf und stelle meinen SEPA Generator vor.
pain Meldungstypen
In der Finanz/Steuern/Zahlungsverkehr-Bubble, in der ich mich bewege, stolpere ich immer wieder über Produkte und Verfahren, deren Bezeichnungen an Wörter oder Namen angelehnt sind. Hier sind ein paar Beispiele:
- Elster (Elektronische Steuererklärung)
- ERiC (Elster Rich Client)
- ZUGFeRD (Zentraler User Guide des Forums elektronische Rechnung Deutschland)
- ELENA (elektronisches Entgeltnachweis-Verfahren)
Der Begriff pain ist da ähnlich (ich vermute allerdings eher unbewusst) gelagert. Wie man sich schon denken kann, steckt dahinter nicht die wörtliche Übersetzung des Wortes „Schmerz“. Ebenso wenig ist damit das gleichnamige Lied von Fan Factory gemeint (beim Schreiben dieser Zeile kommt ein wenig die Nostalgie und die Erinnerung an die ersten Diskobesuche Mitte der 90er hoch). Pain setzt sich aus den Anfangsbuchstaben von payment initiation und ist ein XML-basierendes standardisiertes Datenaustauschformat (ähnlich wie camt) aus dem Bereich Zahlungsverkehr, welches unterschiedliche Zahlungsvorgangsarten beschreibt:
- pain.001 (SCT – Sepa Credit Transfer – Überweisungen)
- pain.008 (SDD – Sepa Direct Debit – Lastschriften)
- pain.013 (SRFP – Sepa Request For Pay – Zahlungsaufforderung)
Klassenbibliothek
Während meiner Recherche zum Thema SEPA XML und pain Meldungstypen fand ich zahlreiche Tools mit grafischer Benutzeroberfläche (das Thema ist ja schließlich schon mehr als 10 Jahren präsent), die man für die Generierung von Überweisungs- und Lastschriftdateien sowie für Konvertierungen zwischen unterschiedlichen pain Versionen nutzen kann.
Mein Ziel war es jedoch nicht, ein schickes Tool für den Endanwender, sondern einen SEPA Generator in Form einer Klassenbibliothek zu entwickeln, die ich maschinell aus meinen VBA-Anwendungen (überwiegend Excel und Access) oder aus .NET-Anwendungen (andere Klassenbibliotheken oder Konsolenprogramme) heraus aufrufen kann, um SEPA XML Dateien mit Sammelaufträgen zu generieren. Natürlich gibt es auf dem Markt und im OpenSource-Bereich bereits bestehende Lösungen, die das gleiche leisten (oder eigentlich eher viel viel mehr). Tja, was soll ich sagen… Den Drang zur Eigenentwicklung, den ich schon in diesem verlinkten Beitrag beleuchtet habe, werde ich wohl nie ablegen können:-)
Wie auch immer. Durch eine einfach zu nutzende Klassenbibliothek erhoffte ich mir, ähnlich wie beim Einsatz vom QR Code Generator, meine eigenen Prozesse zu verbessern und darin enthaltene Medienbrüche zu reduzieren.
Da für meine eigenen Zwecke aktuell nur Überweisungen eine Rolle spielen, habe ich mich ausschließlich auf das Format pain.001 in der aktuellsten Version 001.001.09 beschränkt. Ebenso habe ich erstmal darauf verzichtet, nicht benötigte Kann-Attribute der pain.001-Spezifikation zu implementieren. Perspektivisch werde ich die Bibliothek weiter entwickeln – denn bei der Recherche bin ich auf die Meldung pain.013 gestoßen, der elektronische Zahlungsaufforderungen (Sepa Request for Pay) beschreibt und recht viele interessante Anwendungsgebiete hat. Und nun zurück zum eigentlichen Thema.
öffentliche Methoden der Klassenbibliothek
Aufgrund der vorher erwähnten Festlegungen ist die Klasse recht schlank geworden und hat in ihrer API lediglich 4 Methoden, die nach außen sichtbar sind. Im ersten Auszug ist das in C# geschriebene Interface zu sehen, das die Basis für die DLL darstellt, welche die Nutzung des SEPA Generators aus der VBA-Welt ermöglicht.
public interface ISEPAGeneratorVBALibrary
{
void CreateSEPAGenerator(string initiatorName);
void SetPaymentInformation(string debitorName, string debitorIBAN, DateTime executionDate);
void AddPaymentTransaction(decimal amount, string creditorName, string creditorIBAN, string paymentText);
void SaveXml(string directoryPath, string fileName);
}
In diesem kleinen VBA-Codeausschnitt, der den Generator in Form einer DLL-Datei verwendet, werden alle seine Methoden aufgerufen. Gehen wir die der Reihe nach durch, um die Funktionsweise nachvollziehen zu können.
Dim objSepa As Object
Set objSepa = CreateObject("SEPAGeneratorVBALibrary")
With objSepa
Call .CreateSEPAGenerator("Meine Wenigkeit")
Call .SetPaymentInformation("Meine Wenigkeit", "DE55123456789012345678", CDate("30.01.2024"))
Call .AddPaymentTransaction(50, "Gandalf", "DE11987654321098765432", "Pfeifenkrauteinkauf")
Call .SetPaymentInformation("Meine Wenigkeit", "DE55123456789012345678", CDate("05.02.2024"))
Call .AddPaymentTransaction(10.50, "Raj", "DE22987654321098765432", "Grashopper")
Call .AddPaymentTransaction(2500.99, "Leonard", "DE33987654321098765432", "Hadronenbeschleuniger")
Call .saveXml(ThisWorkbook.path, "Sepa.xml")
End With
CreateSEPAGenerator
Der Sinn der Konstrukturmethode namens CreateSEPAGenerator dürfte klar sein – die erzeugt ein Objekt der Klasse SEPAGenerator und gibt dieses zurück, sodass wir damit interagieren können. Als einzigen Parameter erwartet sie den Namen des Zahlungsinitiators.
SetPaymentInformation
Der Aufruf der nächsten Methode SetPaymentInformation legt das zu belastende Konto mit Angaben zum Inhaber und zur IBAN sowie das Überweisungsdatum fest. Diese Informationen werden im Generatorobjekt abgelegt und für alle darauf folgenden Überweisungen herangezogen. Solange eben, bis mit einem neuen Aufruf der Methode SetPaymentInformation neue Angaben zum Inhaber / IBAN / Überweisungsdatum übermittelt werden.
AddPaymentTransaction
Für die Anlage von Überweisungen ist die Methode AddPaymentTransaction zuständig, welcher der Betrag, die Angaben zum Konto des Empfängers (Name und IBAN), sowie der Verwendungszweck übergeben werden. Im Codebeispiel sieht man, dass die Methode SetPaymentInformation zwei Mal aufgerufen wurde, da die erste Überweisung für den 30.01 und die zweite und die dritte für den 05.02. vorgesehen wurden. Das hat den Hintergrund, dass in pain.001-Dateien das zu belastende Konto in Verbindung mit dem Ausführungsdatum eine logische Einheit (Knoten PmtInf) bilden, unter der alle Überweisungen aufgeführt werden, die von diesem Konto zu diesem Ausführungsdatum überwiesen werden. In unserem Beispiel gibt es aufgrund von zwei unterschiedlichen Ausführungstagen eben zwei logische Einheiten.
Als Nutzer der Klasse muss man sich mit diesen Implementierungsfeinheiten natürlich nicht herumplagen und stattdessen einfach generell für jede Überweisung die Methode SetPaymentInformation und anschließend AddPaymentTransaction aufrufen (auch dann, wenn alle Überweisungen dem selben Konto zum selben Überweisungsdatum belastet werden sollen). Die Klasse „erkennt“, dass es sich um eine bereits vorhandene logische Einheit handelt und legt diese nicht erneut an, sondern verschiebt lediglich den „Zeiger“, mit dem darauffolgende Überweisungen in diese bestehende logische Einheit eingefügt werden.
SaveXml
Nachdem alle Überweisungsdaten an die Methodenaufrufe übergeben wurden, kann mit der Methode SaveXml die fertige SEPA XML Datei auf der Festplatte gespeichert werden, die dann in etwa wie die angehängte Beispieldatei aussieht.
<?xml version="1.0" encoding="UTF-8"?>
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.09" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:schemaLocation="urn:iso:std:iso:20022:tech:xsd:pain.001.001.09 pain.001.001.09.xsd">
<CstmrCdtTrfInitn>
<GrpHdr>
<MsgId>26140ac570f2f7c0057c790b6f029234</MsgId>
<CreDtTm>2024-01-29T04:32:58</CreDtTm>
<NbOfTxs>3</NbOfTxs>
<CtrlSum>2561.49</CtrlSum>
<InitgPty>
<Nm>Meine Wenigkeit</Nm>
</InitgPty>
</GrpHdr>
<PmtInf>
<PmtInfId>0aedcc267c2e013ccf6235d0136c8df4</PmtInfId>
<PmtMtd>TRF</PmtMtd>
<NbOfTxs>1</NbOfTxs>
<CtrlSum>50</CtrlSum>
<ReqdExctnDt>
<Dt>2024-01-30</Dt>
</ReqdExctnDt>
<Dbtr>
<Nm>Meine Wenigkeit</Nm>
</Dbtr>
<DbtrAcct>
<Id>
<IBAN>DE55123456789012345678</IBAN>
</Id>
</DbtrAcct>
<DbtrAgt>
<FinInstnId>
<Othr>
<Id>NOTPROVIDED</Id>
</Othr>
</FinInstnId>
</DbtrAgt>
<CdtTrfTxInf>
<PmtId>
<EndToEndId>9fcd0dcc3c433402ac8a4ad6746aadf9</EndToEndId>
</PmtId>
<Amt>
<InstdAmt Ccy="EUR">50</InstdAmt>
</Amt>
<Cdtr>
<Nm>Gandalf</Nm>
</Cdtr>
<CdtrAcct>
<Id>
<IBAN>DE11987654321098765432</IBAN>
</Id>
</CdtrAcct>
<RmtInf>
<Ustrd>Pfeifenkrauteinkauf</Ustrd>
</RmtInf>
</CdtTrfTxInf>
</PmtInf>
<PmtInf>
<PmtInfId>a8fb375a36d969d1c11ab180a24a0d45</PmtInfId>
<PmtMtd>TRF</PmtMtd>
<NbOfTxs>2</NbOfTxs>
<CtrlSum>2511.49</CtrlSum>
<ReqdExctnDt>
<Dt>2024-02-05</Dt>
</ReqdExctnDt>
<Dbtr>
<Nm>Meine Wenigkeit</Nm>
</Dbtr>
<DbtrAcct>
<Id>
<IBAN>DE55123456789012345678</IBAN>
</Id>
</DbtrAcct>
<DbtrAgt>
<FinInstnId>
<Othr>
<Id>NOTPROVIDED</Id>
</Othr>
</FinInstnId>
</DbtrAgt>
<CdtTrfTxInf>
<PmtId>
<EndToEndId>b5f8904e85e71e21ea0205dc15645e41</EndToEndId>
</PmtId>
<Amt>
<InstdAmt Ccy="EUR">10.5</InstdAmt>
</Amt>
<Cdtr>
<Nm>Raj</Nm>
</Cdtr>
<CdtrAcct>
<Id>
<IBAN>DE22987654321098765432</IBAN>
</Id>
</CdtrAcct>
<RmtInf>
<Ustrd>Grashopper</Ustrd>
</RmtInf>
</CdtTrfTxInf>
<CdtTrfTxInf>
<PmtId>
<EndToEndId>cd05d2795cc87a03ad172fe3d83ebeef</EndToEndId>
</PmtId>
<Amt>
<InstdAmt Ccy="EUR">2500.99</InstdAmt>
</Amt>
<Cdtr>
<Nm>Leonard</Nm>
</Cdtr>
<CdtrAcct>
<Id>
<IBAN>DE33987654321098765432</IBAN>
</Id>
</CdtrAcct>
<RmtInf>
<Ustrd>Hadronenbeschleuniger</Ustrd>
</RmtInf>
</CdtTrfTxInf>
</PmtInf>
</CstmrCdtTrfInitn>
</Document>
private Methode der Klassenbibliothek
Ein Kapitel, welches private Methoden einer Bibliothek beschreibt, ist zugegebenermaßen recht ungewöhnlich. Denn private Methoden verrichten ihren Dienst im Verborgenen, treten für den Aufrufer nicht in Erscheinung und sind für das Verständnis der Klassenbibliothek an sich komplett irrelevant. Eine bestimmte private Methode beschreibe ich aber trotzdem, denn bei ihrer Entwicklung bin ich auf unerwartete Erkenntnisse gestoßen (aus der Kategorie „Der Teufel steckt im Detail“), die ich nicht unerwähnt lassen möchte.
Der Spannungsbogen dürfte nun genug aufgebaut sein – es geht um die Generierung von IDs. Der von mir umgesetzte Teil der Pain 001 Spezifikation sieht insgesamt 3 Knoten vor, welche eindeutige Schlüssel benötigen:
- MsgId
- PmtInfId
- EndToEndId
Die ursprüngliche GetGeneratedId-Methode hat bei jedem Aufruf ein Array fester Länge mit zufälligen Byte-Werten erzeugt und daraus einen MD5-Hash errechnet, der an den Aufrufer zur Verwendung als Knoten-ID zurückgeben wurde. Das MD5-Hashverfahren habe ich herangezogen, weil es hinsichtlich seiner Länge (32Bit) am nächsten zur ID-Längenbegrenzung (35 Zeichen) war.
MD5 Hasher = MD5.Create();
StringBuilder stringBuilderId = new StringBuilder();
Random RandGen = new Random();
byte[] generatedRandomChars = new byte[SepaNode.RANDOM_STRING_LENGTH];
RandomNumberGenerator.NextBytes(generatedRandomChars);
byte[] generatedRandomCharsHashed = Hasher.ComputeHash(generatedRandomChars);
for (int i = 0; i < generatedRandomCharsHashed.Length; i++)
{
stringBuilderId.Append(generatedRandomCharsHashed[i].ToString("x2"));
}
return stringBuilderId.ToString();
Der Testlauf sah recht gut aus und hat eine valide Sepa XML erzeugt. Doch bei der genauen Analyse der Datei fiel mir auf, dass die generierten Hashwerte, die als ID’s fungieren, alle identisch waren, was „etwas“ den Einsatzzweck der ID’s torpedierte. Für einen aussagekräftigeren Test habe ich die Bibliothek mit 10.000 Überweisungen gefüttert, die sie zu einem 5MB-XML-Päckchen zusammengeschnürt und eine neue Erkenntnis zutage gefördert hat. In der Datei waren nicht alle ID’s identisch – es waren insgesamt 10 unterschiedliche, sodass die Häppchen zu je ca. 1.000 Überweisungen, die nacheinander an die Bibliothek übergeben wurden, sich eine gemeinsame ID teilten.
Doch was ist da genau passiert?
Die Rätsels Lösung fand ich in der wirklich ausführlichen Beschreibung der Random-Klasse auf der Microsoftwebseite. Wenn innerhalb eines sehr kleinen Zeitfensters mehrere Instanzen dieser Klasse erstellt werden, dann verwenden diese den identischen, auf der internen Systemzeit, basierenden Seed. Dies führt dazu, dass diese Klasseninstanzen die selben „Zufallszahlen“ in der selben Reihenfolge generieren. Hier bin ich natürlich der Empfehlung gefolgt und habe mehrfache Instanziierung unterbunden, indem ich die Variable, welche die Referenz auf das Objekt der Klasse Random verwaltet, auf static umgestellt und dort eine Prüfung auf das Vorhandensein der Referenz eingebaut habe. Der erneute Testlauf (diesmal mit 400.000 Überweisungen, die innerhalb von rund 30 Sekunden zu einer 180 MB-Datei verpackt wurden) bestätigte, dass nun jede ID wirklich eindeutig war.
Um noch mehr Zufall in die Generierung der ID’s zu bringen, habe ich die Methode an einer weiteren Stelle angepasst. Ursprünglich hat sie, wie oben bereits erwähnt, ein Bytearray fester Länge erzeugt, welches dem Hashverfahren übergeben wurde. Die Arraygröße war durch die Konstante SepaNode.RANDOM_STRING_LENGTH vorgegeben und betrug immer 200 (Dieser Wert war nicht das Ergebnis von äußert komplexen mathematischen Berechnungsverfahren, sondern einfach die erste Zahl aus der Kategorie „weder zu klein, noch zu groß“, die mir in den Sinn kam).
Nach der Anpassung wird nun bei jedem Aufruf der GetGeneratedId-Methode ein interner Zähler um 1 erhöht(C++). Die Summe dieses Zählers und der oben erwähnten Konstante bilden die Arraygröße für die jeweilige ID. So wird für jede ID ein Bytearray unterschiedlicher Länge „verhasht“. Um die Arraygröße nicht ins Unermessliche steigen zu lassen, wird geprüft, ob diese den doppelten Wert der Konstante bereits erreicht hat. Ist es der Fall, dann wird der interne Zähler wieder initialisiert, sodass das Array für die nächste ID wieder bei der Größe von 200 Bytes beginnt.
Einsatz des SEPA Generators
Wie es in der Softwareentwicklung fast immer der Fall ist, gibt es nur selten eine Lösung, deren Einsatz in jeder Konstellation sinnvoll ist. In diesem Kapitel beschreibe ich, wann der Einsatz des Generators keinen Sinn macht und wann er durchaus hilfreich sein kann.
Immer dann, wenn die Überweisungsdaten nicht maschinell durch ein führendes System erzeugt und geliefert werden können, sondern durch einen Sachbearbeiter manuell eingetippt oder mit Copy and Paste eingefügt werden müssen, ist der Einsatz des SEPA Generators nicht wirklich sinnvoll. Rein technisch wäre die Anbindung des Generators an beispielsweise eine grafische Benutzeroberfläche natürlich möglich. Am Ende würde auch eine SEPA XML Datei generiert werden, die man im Onlinebanking elektronisch einreichen könnte. Allerdings hätten wir dadurch nichts gewonnen – wir würden lediglich den Erfassungsaufwand vom Überweisungsformular auf der Webseite der Bank in die externe grafische Benutzeroberfläche verlagern.
Wie man sich schon denken kann, ist die Nutzung des Generator deutlich sinnvoller, wenn vom führenden System alle relevanten Überweisungsdaten generiert und geliefert werden. Hier ein Beispiel aus der Praxis:
Aktuell bin ich gerade dabei, diese Bibliothek an meine kürzlich fertiggestellte Wohnungsverwaltungsanwendung anzubinden. So soll die Anwendung automatisch eine SEPA XML Datei erstellen, wenn es im Rahmen einer Nebenkostenabrechnung mindestens für ein Mietverhältnis eine Erstattung gibt bzw. wenn für ein gekündigtes Mietverhältnis nach der Verrechnung der Nebenkosten noch Kaution auszuzahlen ist.
Da sämtliche Überweisungsdaten (meine Kontoverbindung, Erstattungsbetrag, Kontoverbindung des Mieters, Zahlungsdatum, Verwendungszweck), die für die Generierung der SEPA Datei benötigt werden, in der Wohnungsverwaltungsanwendung vorliegen (diese Daten sind quasi ein „Nebenprodukt“ der Nebenkostenabrechnung), ist dafür keine zusätzliche Datenerfassung nötig. So lässt sich die Bibliothek in den Freigabeprozess einer Nebenkostenabrechnung integrieren und erzeugt mit den oben beschriebenen Daten medienbruchlos eine SEPA XML Überweisungsdatei, die ich anschließend lediglich in der Bankingsoftware einzureichen und freizugeben habe.
Relevante Attribute
Zum Schluss liste ich noch die relevantesten Knoten der SEPA pain.001 Spezifikation auf und erläutere kurz deren fachliche Bedeutung sowie Besonderheiten der Implementierung.
NbOfTxs | In diesem Knoten wird die Anzahl der Transaktionen gespeichert. Dieser ist auf zwei unterschiedlichen Hierarchiestufen vorhanden. Je nach Position innerhalb des SEPA-Dokumentes zeigt er entweder die Anzahl aller im Dokument befindlichen Transaktionen oder die Anzahl der Transaktionen einer logischen Einheit (alle Überweisungen von einem bestimmten Debitor mit einem bestimmten Ausführungsdatum). Im ersten Fall befindet sich NbOfTxs unterhalb des Knotens GrpHdr (Groupheader). Im zweiten Fall liegt es unter PmtInf (Paymentinformation). Die Befüllung dieser Knoten findet am Ende der Verarbeitung statt, nämlich unmittelbar bevor der SEPA Generator die XML Datei physisch ablegt. Dabei wird jeder Knoten PmtInf durchlaufen und mit Hilfe einer XPath-Count-Funktion die Anzahl der innerhalb des jeweiligen Knotens befindlichen Transaktionen ermittelt und gleich dort im Knoten NbOfTxs abgelegt. Anschließend wird auf alle eben befüllten NbOfTxs Knoten eine XPath-Summen-Funktion abgesetzt, welche die Gesamtanzahl der Transaktionen ermittelt und diese dann im Knoten NbOfTxs auf der Dokumentenebene ablegt. |
CtrlSum | Hier wird das Transaktionsvolumen gespeichert. Genauso wie beim NbOfTxs bestimmt auch hier Position in der XML-Baumstruktur, ob sich darin das Volumen aller Transaktionen, oder nur das einer Teilmenge befindet. Die Summe in diesen Knoten wird, genauso wie die Anzahl der Transaktionen am Ende der Verarbeitung ermittelt. |
MsgId | Bei diesem Knoten handelt es sich um eine ID, die für jede Sepa XML auf Dokumentenebene vergeben wird. Anhand dieser ID kann die ausführende Bank die eingereichte XML erkennen und mehrfache Ausführung unterbinden. |
CreDtTm | Darin ist der Erstellungszeitpunkt der Sepa-Datei enthalten. |
Nm | Dieser Knoten befindet sich innerhalb des Knotens Dbtr und beherbergt den Namen des Debitors. So wie ich es gesehen habe, ist es der einzige darin enthaltene Kindknoten, sodass es sich mir nicht wirklich erschließt, welchen Vorteil diese verschachtelte Struktur gegenüber einem einfachen Knoten bietet, der beispielsweise DbtrNm heißen könnte. Das selbe Spiel haben wir auch bei Kreditoren, deren Name sich im Knoten Nm innerhalb des Knotens Cdtr befindet. Vermutlich lag der Hauptfokus der SEPA-Spezifikation eher auf wieder verwendbaren Knoten als auf schlanker Knotenstruktur. Ebenso gibt es diesen Knoten auch innerhalb von InitgPty, in dem der Name des Zahlungsinitiators abgelegt ist. In meinem Fall ist der Initiator einer Zahlung und der Kontoinhaber des zu belastenden Kontos immer die selbe Person, also ich. Ich kann mir vorstellen, dass es eher bei Firmen der Fall ist, dass sich in diesen beiden Knoten unterschiedliche Inhalte befinden. Beispielsweise, wenn der Zahlungsinitiator ein Mitarbeiter aus dem Rechnungswesen und der Kontoinhaber eben die Firma ist. |
EndToEndId | Darin ist eine ID enthalten, die eine bestimmte Überweisung eindeutig identifiziert und wie der Knotenname schon nahelegt, bis zum Ende der kompletten Zahlungsprozesskette durchgereicht wird und am Ende auf dem haptischen Kontoauszug, bzw. in der Kontoauszugsdatei vom Typ CAMT.052 / CAMT.053 erscheint. |
InstdAmt | In diesem Knoten befindet sich der Überweisungsbetrag. Als Trenner zwischer Euro und Cent wird dabei ein Punkt verwendet. Sprich, zwei Euro fünfzig werden als 2.50 abgelegt. |
Ustrd | Darin wird der Verwendungszweck der Überweisung hinterlegt. |
IBAN | Wie es unschwer zu erkennen ist, befindet sich in diesem Knoten die Angabe zur IBAN. Je nach dem, ob dieser Knoten ein Kindesknoten (genau genommen ein Enkelknoten) des Knotens DbtrAcct oder CdtrAcct ist, handelt es sich dabei dann um die IBAN des Debitor- bzw. des Kreditorkontos. |
BIC | Da die Angabe der BIC für Überweisungen im SEPA-Raum nicht mehr verpflichtend ist, habe ich mich entschieden, dies in der Methodenschnittstelle gar nicht erst anzubieten und stattdessen in diesem Kontext nur mit NONPROVIDED zu arbeiten. Hierzu ist vielleicht noch erwähnenswert, dass nicht direkt der BIC-Knoten mit NONPROVIDED belegt wird. Stattdessen wird im FinInstnId der Geschwisterknoten von BIC namens Othr mit dem darunter liegenden Knoten Id mit dem Wert NONPROVIDED belegt. |
Dt | Dieser Knoten, welches sich unterhalb von ReqdExctnDt befindet, beherbergt das gewünschte Ausführungsdatum der jeweiligen Überweisung |
Fazit
Bei solchen Projekten steht oft die Frage im Raum – „wann amortisiert sich denn das Ganze“? Würde ich meine eingesetzte Zeit gegenüber meiner künftigen Zeitersparnis bei Überweisungen gegenüberstellen und mir nur diesen Aspekt anschauen, wäre es aus betriebswirtschaftlicher Sicht natürlich ein absolutes Verlustgeschäft. Dem Recherche- und Implementierungsaufwand von rund einem Tag stünden in meinem Fall wenige Minuten Ersparnis pro Jahr.
Allerdings wäre es eine sehr kurzfristige Betrachtungsweise, denn andere Faktoren spielen ebenfalls eine Rolle. Es kann nämlich nie schaden, sich in neue Themengebiete einzuarbeiten. Schließlich weiß ich nicht, aus welcher fachlicher Richtung der nächste Auftrag kommt. Darüber hinaus finde ich das Themengebiet „Zahlungsverkehr“ enorm spannend, sodass sowohl die Recherche als auch die Implementierung sowie die Anbindung viel Spaß gemacht haben. Zu guter Letzt habe ich durch die Realisierung und die Anbindung auch einen besseren fachlichen Prozess, bei dem ein Medienbruch (der eine Fehlerquelle beim Abtippen dargestellt hat) eliminiert werden konnte.
Also ein klassischer Win-Win-Win-Fall 🙂
Hallo, klingt nach einer super Lösung. Würde die DLL gerne für unseren Förderverein der Feuerwehr verwenden. Wie kommt man an die DLL?
LG
Hi Ralf, ich kann dir die Bibliothek gerne zur kostenfreien Nutzung bereitstellen. Wie im Blogartikel ersichtlich, ist die Funktionalität aufs Wesentliche (ausschließlich Generierung von SEPA-Überweisungen) beschränkt.
Ich hab die schon seit einer Weile im Einsatz – Fehler sind mir bislang keine aufgefallen – dennoch ist die Nutzung natürlich auf deine eigene Gefahr.
Sag mir einfach bescheid, ob du die 32/64Bit-Version benötigst, dann schicke ich dir die Dateien:
dll mit der fachlichen Logik
dll als VBA-Wrapper
xsd zur automatisierten Validierung der erzeugten SEPA-Xmls
Registierungsscript
Viele Grüße
Anton
Hallo Anto,
ich habe mir deinen Blog zu Theme Sepa-XML Generator durchgelesen und finde das eine super Anwendung. Ich würde gerne deine Dll benutzen, um bei meinem VBA-Code etwas aufzuräumen. Da ich auch nur Überweisungen generiere. Könntes du mir deine DLL zur Verfügung stellen?
Hallo Ulrich, ich danke dir für deinen Kommentar. Das ist kein Problem, ich schicke dir die DLL’s per Mail zur freien Nutzung zu. Für den Aufruf in der VBA-Welt ist eine einmalige Registrierung/Bekanntmachung der DLL in der Registry des Zielrechners erforderlich.
Dies kannst du entweder händisch vornehmen oder dafür den mitgelieferten Powershell-Script für deine Office-Version (32Bit/64Bit) als Administrator laufen lassen. Anschließend steht die DLL zur Auswahl in den Verweisen deiner VBA-Umgebung zur Verfügung.
Wenn du die DLL in der C#-Welt nutzen willst, reicht es, diese in deinem VisualStudio-Projekt einzubinden.
Bei der Nutzung der DLL ist zu beachten, dass die saveXml-Methode Exceptions wirft, wenn die erzeugte SEPA-Datei gegen die XSD-Datei verstößt – beispielweise wenn die übergebene IBAN nicht valide ist oder wenn die Transaktionsmethode aufgerufen wurde. Daher ist ein entsprechendes Errorhandling in deiner VBA-Routine wichtig.
Viele Grüße
Anton