In den ersten beiden Teilen dieser Beitragsreihe haben wir uns die Funktionen für das Einlesen von Informationen und das Konvertieren von Kontonummern in IBAN sowie das Ermitteln des Kontostandes angesehen. Im abschließenden, letzten Teil wird es spannend: Wir fügen Funktionen hinzu, mit denen Sie die Kontoumsätze abrufen und überweisungen tätigen können. Damit ist auch der aktuelle Leistungsumfang des hier abgebildeten Webservice der Firma B+S Banksysteme AG beschrieben. Die Nutzung ist weiterhin für private Zwecke kostenlos!
Umsätze abrufen
Diese Funktion ist natürlich etwas umfangreicher als das Einlesen des Kontostandes, da wir ja nicht nur einen einzelnen Wert einlesen, sondern gleich eine ganze Reihe von Werten. Dazu benötigen wir natürlich eine eigene Tabelle, die wir tblUmsaetze nennen, und welche im Entwurf wie in Bild 1 aussieht.
Bild 1: Tabelle zum Speichern der Umsätze
Auf die einzelnen Felder gehen wir gleich bei der Beschreibung des Zugriffs auf den Webservice ein. Wichtig ist an dieser Stelle, dass die Tabelle nicht mit der Tabelle tblKonten verknüpft ist. Stattdessen enthält sie die beiden Felder BankCode und Accountnumber, welche die Bank und das Konto, über das die Transaktionen ausgeführt wurden, eindeutig identifiziert.
Banken fusionieren ja heutzutage gern mal oder ändern ihre Bankleitzahl aus anderen Gründen. Bei der hier durchgeführten Methode behalten Sie auf jeden Fall die Originalbankdaten bei. Es steht Ihnen natürlich frei, die Datensätze der Tabelle tblUmsaetze über ein Fremdschlüsselfeld mit der Tabelle tblKonten zu verknüpfen.
Weiterhin ist hier zu beachten, dass wir für das Feld HashValue einen eindeutigen Index definiert haben. Damit stellen wir sicher, dass ein bereits eingelesener Umsatz nicht nochmals eingelesen wird.
Formularelemente zum Abruf der Umsätze
Nun benötigen wir noch ein Element in der Benutzeroberfläche, um die Umsätze abzurufen und auch anzuzeigen. Dazu erweitern wir einfach das Formular frmKonten, und zwar um eine Schaltfläche namens cmdUmsaetzeEinlesen, zwei Textfelder namens txtStartdatum und txtEnddatum, mit denen Sie den Zeitraum für die einzulesenden Umsätze einstellen können, sowie ein Unterformular zur Anzeige der Umsätze (s. Bild 2).
Bild 2: Anpassungen des Formulars frmKonten
Das Unterformular verwendet die Tabelle tblUmsaetze als Datenherkunft und zeigt davon die Felder Amount, Valuta, Purpose, RecBankID, RecAccountNr und RecName an.
Umsätze abrufen
Das Abrufen der Umsätze startet der Benutzer mit einem Klick auf die Schaltfläche cmdUmsaetzeEinlesen. Diese Prozedur sieht wie in Listing 1 aus und prüft zunächst, ob der Benutzer für das Kombinationsfeld cboKontakte überhaupt einen Wert ausgewählt hat.
Private Sub cmdUmsaetzeEinlesen_Click() Dim strContactData As String Dim strAccountId As String Dim strAccountnumber As String Dim strErrorCode As String Dim strErrorText As String Dim strXML As String If IsNull(Me!cboKontakte) Then MsgBox "Wählen Sie einen Kontakt aus." Me!cboKontakte.SetFocus Exit Sub End If strContactData = DLookup("ContactData", "tblKontakte", "KontaktID = " _ & Me!cboKontakte) ''wegen Längenbeschränkung auf 255 Zeichen strAccountId = Me!cboKonten.Column(5) strAccountnumber = Me!cboKonten.Column(2) strXML = StatementRequest(strContactData, strAccountId, strErrorCode, strErrorText, Nz(Me!txtStartdatum, 0), _ Nz(Me!txtEnddatum, 0)) If Len(GetXMLElement(strXML, "//SuccessText")) > 0 Then UmsaetzeVerarbeiten strXML, strAccountnumber Me!sfmUmsaetze.Form.Requery Else MsgBox "Fehler:" & vbCrLf & strErrorCode & vbCrLf & strErrorText End If End Sub
Listing 1: Start des Einlesevorgangs der Umsätze
Anderenfalls erscheint eine entsprechende Meldung und die Prozedur wird beendet.
Danach ermittelt die Prozedur den Wert des Feldes ContactData für den aktuellen Kontakt. Während die folgenden Werte direkt aus den weiteren Spalten des Kombinationsfeldes cboKontakte ausgelesen werden, entnehmen wir diesen Wert per DLookup-Funktion aus der Tabelle tblKontakte. Der Grund ist einfach: Jede Spalte des Kombinationsfeldes kann nur 255 Zeichen aufnehmen, ContactData ist aber länger.
Danach liest die Prozedur die Werte der Felder AccountID und Accountnumber der Tabelle tblKontakte ein, die wir aber bereits in den folgenden Spalten des aktuellen Eintrags des Kombinationsfeldes cboKonten gespeichert haben.
Dann setzt die Prozedur einen Aufruf der Funktion StatementRequest ab, welche ein XML-Dokument mit den angefragten Umsatz-Informationen zurückliefert. Die Funktion erwartet die Werte der Felder ContactData und AccountID, zwei Parameter, mit denen eventuelle Fehlerinformationen zurückgeliefert werden können, sowie das Start- und das Enddatum, wenn nur die Umsätze eines begrenzten Zeitraums eingelesen werden sollen.
Das zurückgelieferte XML-Dokument wertet die Prozedur dann gleich aus – zunächst, indem sie prüft, ob das Ergebnisdokument ein Element namens SuccessText enthält. Ist dies der Fall, war der Aufruf des Webservice erfolgreich und wir können uns an die Auswertung begeben. Das bedeutet in diesem Fall, dass wir den Inhalt des XML-Dokuments zusammen mit dem Wert von strAccountnumber an die Routine UmsaetzeVerarbeiten übergeben. Danach brauchen wir nur noch das Unterformular sfmUmsaetze zu aktualisieren, damit die neu eingelesenen Umsatzpositionen dort angezeigt werden.
Umsätze vom Webservice holen
Die Funktion StatementRequest aus Listing 2 erwartet die bereits erwähnten Parameter. Sie fragt als Erstes per InputBox den PIN für den Zugriff auf das Konto ab und speichert diesen in der Variablen strPIN.
Public Function StatementRequest(strContactData As String, strAccountId As String, strErrorCode As String, _ strErrorText As String, Optional datStart As Date, Optional datEnde As Date) As String Dim objXMLResponse As MSXML2.DOMDocument Dim strRequest As String, strResponse As String, strPIN As String Dim strStartdate As String, strEnddate As String, strFunction As String strFunction = "StatementRequest" strPIN = InputBox("PIN:") strRequest = " <ser:StatementRequestData>" & vbCrLf strRequest = strRequest & " <ser:ContactData>" & strContactData & "</ser:ContactData>" & vbCrLf strRequest = strRequest & " <ser:AccountId>" & strAccountId & "</ser:AccountId>" & vbCrLf If datStart > 0 Then strStartdate = Format(datStart, "yyyy-mm-dd") strRequest = strRequest & " <ser:StartDate>" & strStartdate & "</ser:StartDate>" & vbCrLf End If If datEnde > 0 Then strEnddate = Format(datEnde, "yyyy-mm-dd") strRequest = strRequest & " <ser:EndDate>" & strEnddate & "</ser:EndDate>" & vbCrLf End If strRequest = strRequest & " </ser:StatementRequestData>" & vbCrLf strRequest = strRequest & " <ser:Pin>" & strPIN & "</ser:Pin>" strRequest = CreateSoapRequest(strRequest, strFunction) Request strRequest, objXMLResponse strResponse = objXMLResponse.XML strErrorCode = GetXMLElement(strResponse, "//" & strFunction & "Result/Error/ErrorCode") strErrorText = GetXMLElement(strResponse, "//" & strFunction & "Result/Error/ErrorText") StatementRequest = FormatXML(objXMLResponse.XML) End Function
Listing 2: Zusammensetzen des Requests und Zurückgeben des Ergebnisses
Dann beginnt sie, den Request zusammenzusetzen. Dieses sieht anschließend beispielsweise wie folgt aus:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://service.ddbac.de/"> <soapenv:Body> <ser:StatementRequest> <ser:StatementRequestData> <ser:ContactData>GgxGb1115NitZ... </ser:ContactData> <ser:AccountId>5E3A4E4882B730A... </ser:AccountId> </ser:StatementRequestData> <ser:Pin>12345</ser:Pin> </ser:StatementRequest> </soapenv:Body> </soapenv:Envelope>
Wenn der Benutzer ein Start- oder Enddatum angegeben hat, wird die Anfrage noch entsprechend erweitert:
... <ser:StartDate>2015-01-01</ser:StartDate> <ser:EndDate>2015-05-31</ser:EndDate> ...
Danach ruft die Funktion die Routine Request auf und übergibt ihr den Request als ersten und ein leeres DOMDocument-Objekt als zweiten Parameter. Letzterer soll die Antwort des Webservice auf den Request aufnehmen.
Das Ergebnis dieses Aufrufs (die Routine Request haben wir bereits im ersten Teil der Beitragsreihe beschrieben) speichert die Prozedur dann in der String-Variablen strResponse. Die im Modul mdlWebservice befindliche Funktion GetXMLElement ermittelt aus dieser Antwort den Inhalt der Elemente Result/Error/ErrorCode und Result/Error/ErrorText und speichert diese, soweit gefüllt, in den beiden Variablen strErrorCode und strErrorText. Ein Response-Dokument mit diesen Fehlerinformationen sieht beispielsweise wie in Listing 3 aus. Sie können diesen Fehler beispielsweise hervorrufen, indem Sie im Testsystem als PIN einen Wert eingeben, der mit 0 beginnt (also etwa 01111). Die Prozedur gibt neben eventuell vorhandenen Fehlerinformationen das zurückgelieferte XML-Dokument als Funktionswert an die aufrufende Prozedur zurück.
<soap:Envelope ...> <soap:Body> <StatementRequestResponse xmlns="http://service.ddbac.de/"> <StatementRequestResult> <Error> <ErrorType>BUSINESS</ErrorType> <ErrorCode>9800</ErrorCode> <ErrorText>Der Dialog wurde abgebrochen. Bitte melden Sie sich erneut an. (9800);...</ErrorText> <ErrorCustomerText>Der Dialog wurde abgebrochen. Bitte melden Sie sich erneut an. (9800); Ungültige Dialogkennung. Bitte melden Sie sich erneut an. (9010)...</ErrorCustomerText> <NoFurtherRequestsPreferred>false</NoFurtherRequestsPreferred> </Error> </StatementRequestResult> </StatementRequestResponse> </soap:Body> </soap:Envelope>
Listing 3: Rückgabe für eine fehlerhafte Anfrage
Umsätze verarbeiten
Die Prozedur UmsaetzeVerarbeiten aus Listing 4 erwartet das XML-Dokument, das die Funktion StatementRequest geliefert hat, sowie die Kontonummer als Parameter.
Public Sub UmsaetzeVerarbeiten(strXML As String, strAccountnumber As String) Dim objXML As MSXML2.DOMDocument Dim db As DAO.Database Dim rst As DAO.Recordset Dim objBACStatementLine As MSXML2.IXMLDOMNode Dim strBankCode As String Dim strCustomerID As String Set objXML = New MSXML2.DOMDocument objXML.loadXML strXML strBankCode = objXML.selectSingleNode("//CustomerData/BankCode").nodeTypedValue strCustomerID = objXML.selectSingleNode("//CustomerData/CustomerId").nodeTypedValue Set db = CurrentDb Set rst = db.OpenRecordset("SELECT * FROM tblUmsaetze", dbOpenDynaset) For Each objBACStatementLine In objXML.selectNodes("//BACStatementLine") rst.AddNew rst!BankCode = strBankCode rst!AccountNumber = strAccountnumber rst!BusinessTransactionCode = objBACStatementLine.selectSingleNode("BusinessTransactionCode").nodeTypedValue rst!HashValue = objBACStatementLine.selectSingleNode("HashValue").nodeTypedValue rst!Amount = Eval(objBACStatementLine.selectSingleNode("Amount").nodeTypedValue) rst!StatementNr = objBACStatementLine.selectSingleNode("StatementNr").nodeTypedValue rst!BookingDate = DatumAusXML(objBACStatementLine.selectSingleNode("BookingDate").nodeTypedValue) rst!Currency = objBACStatementLine.selectSingleNode("Currency").nodeTypedValue rst!Valuta = DatumAusXML(objBACStatementLine.selectSingleNode("Valuta").nodeTypedValue) rst!BookingRef = objBACStatementLine.selectSingleNode("BookingRef").nodeTypedValue On Error Resume Next rst!Purpose = objBACStatementLine.selectSingleNode("Purpose").nodeTypedValue rst!RecBankId = objBACStatementLine.selectSingleNode("RecBankId").nodeTypedValue rst!RecAccountNr = objBACStatementLine.selectSingleNode("RecAccountNr").nodeTypedValue On Error GoTo 0 rst!RecName = objBACStatementLine.selectSingleNode("RecName").nodeTypedValue On Error Resume Next rst.Update Select Case Err.Number Case 0, 3022 Case Else MsgBox "Fehler " & Err.Number & ", " & Err.Description End Select On Error GoTo 0 Next objBACStatementLine End Sub
Listing 4: Auslesen und speichern des Inhalts des XML-Dokuments mit den Umsätzen
Sie erstellt zunächst ein neues DOMDocument-Objekt und füllt dieses mit dem Inhalt aus strXML, der etwa wie in Listing 5 aussieht. In diesem Dokument interessieren uns die einzelnen Elemente unterhalb des Elements StatementLines. Jedes Element namens BACStatementLine enthält nämlich genau einen Buchungssatz. Die einzelnen dort enthaltenen Elemente liefern jeweils eine Information zur Buchung, zum Beispiel den Betrag, die Währung oder das Buchungsdatum. Außerdem liefert das Dokument im Element CustomerData (hier zur Platzersparnis weggelassen) noch die Informationen zum Bankkonto.
<soap:Envelope ..."> <soap:Body> <StatementRequestResponse xmlns="http://service.ddbac.de/"> <StatementRequestResult> <CustomerData>...</CustomerData> <ContactData>GgxGb1115Nit...</ContactData> <AccountId>C29990EF1F05175C1E952DF2005498CC79829427</AccountId> <StatementLines> <BACStatementLine> <BusinessTransactionCode>51</BusinessTransactionCode> <HashValue>62F619DF4221AD05958F6AF06957438D4E6816C8</HashValue> <Amount>1</Amount> <StatementNr>54</StatementNr> <BookingDate>2015-06-26T00:00:00</BookingDate> <Currency>EUR</Currency> <Valuta>2015-06-26T00:00:00</Valuta> <BookingRef>NONREF</BookingRef> <BookingText>überweisungseingang</BookingText> <Purpose>Testlastschrift</Purpose> <RecBankId>DDBADEMM002</RecBankId> <RecAccountNr>DE95700009972000735160</RecAccountNr> <RecName>USER 735160</RecName> </BACStatementLine> </StatementLines> <SuccessText>Die Nachricht wurde entgegengenommen. (0010); Der Auftrag wurde ausgeführt. (0020) </SuccessText> </StatementRequestResult> </StatementRequestResponse> </soap:Body> </soap:Envelope>
Listing 5: XML-Dokument mit den Umsatzdaten, hier nur für eine Umsatzposition
Die Prozedur liest per selectSingleNode beispielsweise das Element CustomerData/BankCode ein und speichert es in der Variablen strBankCode. Gleiches geschieht mit dem Element CustomerData/CustomerID.
Danach durchläuft die Prozedur in einer For Each-Schleife alle BACStatementLine-Elemente. Für jedes Element legt sie im zuvor erstellten Recordset rst je einen neuen Datensatz an und trägt die Inhalte der einzelnen Unterelemente des BACStatementLine-Elements ein.
Der Einfachheit halber haben wir daher die Tabellenfelder nach den Elementnamen in der XML-Datei benannt.
Hier gibt es ein paar Besonderheiten. So kommen die Datumsangaben in den XML-Elementen beispielsweise in einem Format, das etwa so aussieht:
2015-06-26T00:00:00
Dies können wir leider nicht direkt in einem Datumsfeld einer Access-Datenbank speichern, sondern müssen es zuvor noch einer speziellen Behandlung unterziehen.
Daher haben wir noch eine kleine Funktion namens Datum-AusXML hinzugefügt, die wie folgt aussieht:
Public Function DatumAusXML(strDatum As String) As Date DatumAusXML = DateSerial(Left(strDatum, 4), _ Mid(strDatum, 6, 2), Mid(strDatum, 9, 2)) End Function
Es parst schlicht und einfach die einzelnen Datumsinformationen und fügt diese zu einem Access-kompatiblen Wert zusammen.
Interessant ist noch die Fehlerabfrage gegen Ende der Prozedur: Bevor wir den neuen Datensatz dort nämlich mit der Update-Methode speichern, deaktiviert die Prozedur die eingebaute Fehlerbehandlung.
Es kann nämlich geschehen, dass bereits vorhandene Umsätze nochmals eingelesen werden, was zu einem Fehler wegen eines doppelten Wertes im Feld HashValue führen würde, welches als eindeutiger Index festgelegt ist. Bereits vorhandene Umsätze werden in diesem Fall schlicht nicht nochmals angelegt.
Das Ergebnis sehen Sie in der Datenblattansicht der Tabelle tblUmsaetze (s. Bild 3).
Bild 3: Beispiele für eingelesene Umsätze
Damit das Unterformular sfmUmsaetze im Hauptformular frmKonten auch wie in Bild 4 die zum aktuell angezeigten Konto liefert, müssen wir noch zwei kleine änderungen vornehmen. Als Erstes legen Sie zwei Textfelder im Hauptformular an, welche die für die Verknüpfung herangezogenen Spalten aus den entsprechenden Spalten der beiden Kombinationsfelder ermitteln. Das erste Textfeld heißt txtBankCode und verwendet den folgenden Ausdruck als Steuerelementinhalt:
Bild 4: Ein Beispielumsatz im Unterformular
=[cboKontakte].[Column](2)
Das zweite Textfeld namens txtAccountnumber wiederum greift seinen Wert aus dem Kombinationsfeld cboKonten ab:
=[cboKonten].[Volumn](2)
Die Eigenschaft Sichtbar der beiden Textfelder können Sie nach einem Text, ob beide die gewünschten Werte anzeigen, auf den Wert Nein einstellen und die Textfelder somit ausblenden.
Nun legen Sie noch die Eigenschaften Verknüpfen von und Verknüpfen nach des Unterformular-Steuerelements fest. Dabei verknüpfen wir Haupt- und Unterformular jeweils nach zwei Feldern, und zwar (s. Bild 5):
Ende des frei verfügbaren Teil. Wenn Du mehr lesen möchtest, hole Dir ...
den kompletten Artikel im PDF-Format mit Beispieldatenbank
diesen und alle anderen Artikel mit dem Jahresabo