In meinem ersten Beitrag über das Thema Rechnungsstellung habe ich über Herausforderungen bei der Zuordnung von Zahlungen zu offenen Rechnungen berichtet. Der Lösungsansatz, den ich bereits umgesetzt habe, war die Verwendung von QR Codes auf Rechnungen, um bequemere Überweisungen für meine Kunden und fehlerfreie Zuordnungen für meine Buchhaltung zu ermöglichen. Nun möchte ich einen Blick hinter die Kulissen auf ein weiteres Modul gewähren, welches Kontoauszugsdateien im Camt 052-Format entgegennimmt, aus den darin enthaltenen Kontoumsätzen relevante Zahlungseingänge ermittelt und diese ins Rechnungsprogramm für den Zahlungsabgleich importiert.

Was ist Camt 052?

Bevor wir beginnen, möchte ich als erstes ein paar Worte über das Camt-Format verlieren. Es steht für Cashmanagement und ist ein in der Finanzwelt verbreitetes Standard zum Datenaustausch zwischen Finanzinstituten und Kunden. Camt-Dateien basieren auf der XML-Technologie und bildet je nach Meldungstyp einen gewissen Sachverhalt ab. In diesem Beitrag konzentriere ich mich lediglich auf die Meldung Camt.052, die für untertägige Umsatzaufstellungen steht.

Obwohl das Camt-Format seit der SEPA-Einführung im Jahre 2014 allgegenwärtig (zumindest im Hintergrund) und auch in nahezu allen Banking-Apps und Programmen als Exportdateiformat verfügbar ist, ist es im Privatkundenbereich so gut wie unbekannt.

Im Prinzip ist es absolut nachvollziehbar. Denn exportiert man eine Umsatzaufstellung im PDF- und im Camt 052-Format, dann stellen diese zwar die selben Buchungen dar, tun dies aber auf eine absolut unterschiedliche Art und Weise. Während beim PDF-Format der Hauptfokus auf gute Lesbarkeit für Menschen liegt, ist das Camt-Format hierarchisch aufgebaut und ausschließlich auf maschinelle Verarbeitung ausgelegt.

Um eine Vorstellung vom Camt-Aufbau zu bekommen, poste ich hier einen Auszug daraus. In diesem Beitrag erläutere ich allerdings nicht den kompletten Aufbau aller Knoten, sondern konzentriere mich ausschließlich auf Ntry-Knoten, die Zahlungsinformationen beinhalten. Der Camt 052 Datensatz enthält natürlich viele weitere nützliche Daten, wie beispielsweise:

  • Kontobezeichnung
  • Kontoinhaber
  • IBAN und BIC des Kontos
  • Betrachtungszeitraum der Zahlungsaufstellung
  • Saldo am Anfang und am Ende des Betrachtungszeitraums

Für den Import und den späteren Zuordnungsprozess sind diese Daten jedoch weniger relevant und werden daher nicht weiter betrachtet.

Dieser nachfolgende Ausschnitt stellt eine echte Buchung aus meinem Geschäftskonto dar (schützenswerte Daten habe ich natürlich anonymisiert). Für die Beschreibung dieser einen einzigen Buchung sind in diesem Format ganze 50 Zeilen erforderlich. Platzsparend ist es nicht gerade – selbst mit meinen überschaubaren Kontobewegungen hätte mein Drucker sehr viel zu tun, wenn ich die CAMT-Datei anstelle einer PDF-Datei drucken würde.

<Ntry>
	<Amt Ccy="EUR">528.00</Amt>
	<CdtDbtInd>CRDT</CdtDbtInd>
	<Sts>BOOK</Sts>
	<BookgDt>
		<Dt>2021-12-20</Dt>
	</BookgDt>
	<ValDt>
		<Dt>2021-12-20</Dt>
	</ValDt>
	<BkTxCd>
		<Prtry>
			<Cd>835</Cd>
			<Issr>DK</Issr>
		</Prtry>
	</BkTxCd>
	<NtryDtls>
		<TxDtls>
			<Refs>
				<AcctSvcrRef>46c7fc6483e54e21841d7556e2559980</AcctSvcrRef>
			</Refs>
			<BkTxCd>
				<Prtry>
					<Cd>NMSC+835</Cd>
					<Issr>DK</Issr>
				</Prtry>
			</BkTxCd>
			<RltdPties>
				<Dbtr>
					<Nm>Name des Kunden</Nm>
				</Dbtr>
				<DbtrAcct>
					<Id>
						<IBAN>DE12345678901234567890</IBAN>
					</Id>
				</DbtrAcct>
			</RltdPties>
			<RltdAgts>
				<DbtrAgt>
					<FinInstnId>
						<BIC>GENODES1VBK</BIC>
					</FinInstnId>
				</DbtrAgt>
			</RltdAgts>
			<RmtInf>
				<Ustrd>rechnung 2021-00001-00005</Ustrd>
			</RmtInf>
		</TxDtls>
	</NtryDtls>
</Ntry>

Einsatzbereich von Camt

Nach dem man dieses ausufernde Zeilenkonstrukt gesehen hat, mag sich der eine oder andere fragen, wozu solche Dateiformate denn gut sind.

Schlüpfen wir uns für die Beantwortung dieser Frage beispielsweise in die Rolle eines E-Commerce-Unternehmers, der jeden Monat tausende Artikel auf Rechnung verkauft. Bevor die Ware versandt werden kann, muss zuerst geprüft werden, ob die dazugehörige Zahlung in richtiger Höhe auf dem Konto angekommen ist. Bei Tausend Zahlungen kann man in etwa vorstellen, welchen Aufwand es verursachen würde, jede dieser eingegangenen Zahlungen händisch zu offenen Rechnungen zuzuordnen. In einer idealen Welt, wo keine Fehler passieren, mag es ein leichtes Unterfangen sein. In der Realität gibt es alle erdenklichen Konstellationen, die Zahlungszuordnungen relativ aufwändig machen können:

  • Zahlendreher bei Rechnungsnummern
  • eine einzelne kumulierte Überweisung, die gleich mehrere Rechnungen begleicht
  • mehrere Teilüberweisungen zu einer Rechnung
  • Zahlendreher bei Rechnungsbeträgen

Aus diesem Grund ist eine automatisierte Zahlungsverarbeitung, die einen überwiegenden Teil der Zuordnungen maschinell durchführt, die einzige Möglichkeit, den administrativen Aufwand in Grenzen zu halten.

Genau für diesen Anwendungsfall können Camt-Dateien eingesetzt werden. Bevor mir aber jemand vorwirft, nicht politisch korrekt zu sein und maschinell verarbeitbare CSV oder JSON-Dateien durch Nichterwähnung zu diskriminieren, stimme ich zu, dass das Camt-Format kein Monopol auf maschinelles Einlesen von Zahlungsinformationen hat. Dies lässt sich auch mit allen gängigen strukturierten Ausgabeformaten umsetzen.

Aber im Ernst jetzt. Es ist letztendlich Geschmackssache bzw. Architekturvorgabe, mit welchen Dateiformaten der Zahlungsimport für Zuordnungen erfolgt. Ich ziehe das XML und somit das Camt-Format fast immer CSV und anderen Formaten vor, vor allem wegen seiner umfangreichen Validierungs- und Selektionsmöglichkeiten. Spielt der Speicherbedarf der Dateien eine wichtige Rolle, dann ist Camt-Format aufgrund seiner „Geschwätzigkeit“ eher ungünstig – in diesem Fall würde ich dann auf andere strukturierte Formate ausweichen.

Ich persönlich käme bei meiner Buchhaltung gut ohne automatisiere Zahlungszuordnung aus. Im Gegensatz zu E-Commerce-Unternehmern habe ich als nebenberuflicher Softwareentwickler keine Tausende Zahlungseingänge im Monat (schade eigentlich). Der Turnus meiner Zahlungseingänge ist um Faktor von etwa 500 niedriger. Es ist eher der Forschungsdrang und die Möglichkeit, im Programmieren und Konzeption besser zu werden, die mich zur Weiterentwicklung des Rechnungsprogramm bewogen haben. Sollte mein Laden durch die Decke gehen, bin ich verarbeitungstechnisch darauf schon mal gut vorbereitet.

Modul zum Import von Zahlungsdaten

Nun genug der Worte – wir tauchen nun in den Quellcode des Zahlungsdatenimportmoduls des Access-Rechnungsprogramms ein. Mein Hauptfokus bei der Erklärung lege ich dabei nicht auf den VBA-Code, der relativ selbsterklärend ist, sondern viel mehr auf die Methode SelectNodes, die den XPath-Ausdruck entgegennimmt und verarbeitet, sowie auf den XPath-Ausdruck selbst. Der XPath-Ausdruck ist an der Stelle die zentrale Vorselektion, die dafür sorgt, dass nur relevante Zahlungen selektiert werden.

Dim strAusdruck                     As String
Dim xmlBuchungsdatei                As MSXML2.DOMDocument30
Dim xmlRelevanteZahlungsknoten      As MSXML2.IXMLDOMSelection
Dim xmlEinzelZahlungsknoten         As MSXML2.IXMLDOMNode
   
Set xmlBuchungsdatei = New MSXML2.DOMDocument
xmlBuchungsdatei.Load (activeWorkbook.Path & "/buchungsdatei.xml")
	
strAusdruck = "/Document/BkToCstmrAcctRpt/Rpt/Ntry[Amt>0.00 and Sts='BOOK' and CdtDbtInd='CRDT' and NtryDtls/TxDtls/Refs/AcctSvcrRef!='' and NtryDtls/TxDtls/RltdPties/DbtrAcct/Id/IBAN!='']"

Set xmlRelevanteZahlungsknoten = xmlBuchungsdatei.SelectNodes(strAusdruck)

For Each xmlEinzelZahlungsknoten in xmlRelevanteZahlungsknoten
    'hier startet die Prüfung, ob es sich um eine neue Zahlung handelt (die bisher nicht importiert worden ist)
	call zahlungsimportDurchfuehren(xmlEinzelZahlungsknoten)
Next

Zahlungsselektion mit XPath-Ausdruck

Damit dieser einfacher zu verstehen ist, habe ich den XPath-Ausdruck aus der Zeile 9 (siehe weiter oben) mit Umbrüchen und Einrückungen etwas umformatiert und gehe diesen nun Zeile für Zeile ausführlich durch.

/Document/BkToCstmrAcctRpt/Rpt/Ntry
[
   Amt>0.00 and 
   Sts='BOOK' and 
   CdtDbtInd='CRDT' and 
   NtryDtls/TxDtls/Refs/AcctSvcrRef!='' and 
   NtryDtls/TxDtls/RltdPties/DbtrAcct/Id/IBAN!=''
]

Mit Hilfe des Ausdrucks in der ersten Zeile legen wir fest, welche Knoten ausgegeben werden sollen. In diesem Fall sind es Knoten namens Ntry, die alle relevanten technischen und fachlichen Zahlungsdaten enthalten, die für den Import benötigt werden.

Mit der öffnenden eckigen Klammer in der Zeile 2 leiten wir eine Selektionsabfrage ein, die auf alle Knoten Ntry abgesetzt und die mit der schließenden eckigen Klammern in der Zeile 8 geschlossen wird. Das bedeutet, dass nur diejenigen Ntry-Knoten selektiert werden, die sämtlichen Bedingungen aus den Zeilen 3-7 entsprechen (ausgedrückt durch die logischen UND-Verknüpfungen).

  • Das Attribut Amt Es steht für Amount, also den Betrag der Zahlung. In der Zeile 3 legen wir fest, dass nur die Zahlungen selektiert werden sollen, die mindestens 1 Cent betragen. An dieser Stelle und auch bei der späteren Verarbeitung muss berücksichtigt werden, dass dieses Attribut dezimale Punkte als Trennzeichen zwischen Euro- und Centbeträgen verwendet.
  • Hinter dem Attribut Sts (Status), welches die Ausprägungen PDNG oder BOOK haben kann, verbirgt sich die Info, ob die Zahlung vollständig gebucht wurde. Da für mich bei der Zahlungszuordnung nur tatsächlich bereits gebuchten Zahlungen relevant sind, wird dies in der Zeile 4 entsprechend eingegrenzt
  • Das Attribut CdtDbtInd steht für Credit Debit Indicator und gibt an, ob es sich bei der Zahlung um eine Gutschrift oder eine Belastung handelt. Da ich bei der Zahlungszuordnung zu meinen Rechnungen nur Gutschriften ermitteln möchte, grenze ich in der Zeile 5 darauf ein.
  • Das Attribut AcctSvcrRef bedeutet ausgeschrieben Account Servicer Reference und stellt eine eindeutige Transaktionsnummer dar. Das Rechnungsprogramm stellt anhand dieser Nummer fest, ob es sich um eine noch nicht importierte Zahlung handelt. Aus diesem Grund grenze ich in der Zeile 6 nur auf die Knoten ein, bei denen eine Transaktionsnummer gefüllt ist. Entgegen den vorherigen Attributen fällt hier auf, dass hier ein deutlich längerer Attributspfad angegeben ist. Dies liegt daran, dass die bisherigen Attrubute direkt im Knoten Ntry positioniert sind, während dieses Attribut viel weiter unten in der Hierarchie liegt.
  • Im Attribut IBAN befindet sich die IBAN-Kontonummer der Zahlung. Da diese Angabe für die Weiterverarbeitung ebenfalls erforderlich ist, wird auch bei diesem Attribut in der Zeile 7 dafür gesorgt, dass nur die Knoten mit einer gefüllten IBAN selektiert werden.

Import der Zahlungsdaten

Als Ergebnis erhalten wir von der Methode SelectNodes die Auflistung xmlRelevanteZahlungsknoten mit allen allen Zahlungen, die schon mal alle grundsätzlichen Voraussetzungen erfüllen. Vor dem tatsächlichen Import wird noch jede Zahlung im Rechnungsprogramm „nachgeschlagen“, ob diese eventuell schon früher importiert worden ist. Bei dieser Überprüfung kommt der Transaktionsnummer eine entscheidende Rolle zu und sorgt dafür, dass Zahlungen tatsächlich immer nur einmalig importiert werden, auch wenn ein und die selbe Camt 052 Datei mehrmals eingelesen wird. Diese Verarbeitung findet in der Methode zahlungsimportDurchfuehren() statt. Handelt es sich um eine noch nicht importierte Zahlung, werden vom übergebenen Ntry-Knoten die für die spätere Zahlungszuordnung relevante Attribute ausgelesen und importiert.

Hier an der Stelle möchte ich noch erwähnen, dass die Bezeichnung „Attribute“ an der Stelle vielleicht etwas irreführend sind. Denn aus XML-Sicht sind es natürlich keine Attribute, sondern ganz normale Knoten. Die Bezeichnung Attribut ist hier eher im fachlichen Zusammenhang zu verstehen.

  • Das Attribut Amt ist auch gleichzeitig eines der Selektionskriterien und wurde dort bereits vorgestellt.
  • Hinter dem Attribut Dt verbirgt sich das Wertstellungsdatum der Zahlung, sprich, das Datum, an dem die Zahlung wirksam geworden ist.
  • Einen Preis für die sprechendste Bezeichnung gewinnt das Attribut Nm mit Sicherheit nicht – dahinter ist der Name des Kontoinhabers der Zahlung zu finden. Dieses Attribut befindet sich sehr weit tief in der Hierarchie des Ntry-Knotens unter NtryDtls/TxDtls/RltdPties/Dbtr/Nm.
  • Das Attribut Ustrd beinhaltet das bei der Zahlung angegebene Verwendungszwecke und enthält (hoffentlich) eine Rechnungsnummer, mit der eine Zahlungszuordnung am einfachsten zu bewerkstelligen ist. Auch dieses Attribut ist relativ weit in der Hierarchie unter NtryDtls/TxDtls/RmtInf/Ustrd zu finden.
  • Auch das Attribut IBAN wurde bei den Selektionskriterien bereits beschrieben.

Fazit

Nach dem die Zahlungen importiert sind, stehen sie dann für die Zuordnung zu noch offenen Rechnungen zur Verfügung. Das Zuordnungsverfahren mit SQL- und VBA-Mitteln, das ich ausgearbeitet habe und an dem ich immer wieder kleinere Verbesserungen vornehme, ist relativ umfangreich, sodass ich dieses zu einem späteren Zeitpunkt vorstelle.

Der hier aufgeführte Quellcode zum Datenimport ist natürlich keine einzig richtige Patentlösung. Mir fallen spontan einige Möglichkeiten ein, wie man den Import anders organisieren kann. Letztendlich ist es genau das, was die Softwareentwicklung so spannend macht – die vielen Möglichkeiten ans Ziel zu kommen.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert