XRechnung, Teil 1: Rechnungen generieren

XRechnung ist ein Standard für das Einreichen von Rechnungen bei öffentlichen Aufträgen. Sinn und Zweck dieses Standards ist, das Übermitteln von Rechnungsdaten auf eine Weise zu erlauben, dass diese automatisiert eingelesen und verarbeitet werden können. Wie das X in XRechnung vermuten lässt, steckt dahinter ein Austausch der Daten im XML-Format. Die Vorlage für XRechnung liefert die europäische Norm EN 16931. Wie die Daten strukturiert werden müssen, haben wir dem Dokument “Spezifikation XRechnung Standard und Extension” entnommen, das öffentlich einsehbar ist. Als Grundlage für die Zusammenstellung der XRechnung verwenden wir ein herkömmliches Datenmodell einer Bestell- oder Rechnungsverwaltung. In diesem ersten Teil zeigen wir, wie die Daten aus den Tabellen der Beispieldatenbank in ein XRechnung-kompatibles XML-Dokument umgewandelt werden und wie Sie dieses validieren können.

Das Dokument Spezifikation XRechnung Standard und Extension liefert alle für die Erstellung von XRechnungen erforderlichen Informationen. Dazu gehören die Struktur, die zu verwendenden Datentypen et cetera. Wir werden in diesem Beitrag nicht alle Aspekte umsetzen, daher hier der Link zu diesem Dokument für den Fall, dass Sie weitere Elemente hinzufügen möchten:

https://www.xoev.de/sixcms/media.php/13/200-XRechnung-2020-06-30.pdf

Sollte das Dokument nicht mehr unter diesem Link verfügbar sein, suchen Sie im Internet nach dem Titel des Dokuments XRechnung Standard und Extension.

Validieren der XRechnung

Bevor wir beginnen, den Code zum Zusammenstellen der XRechnung zu programmieren, schauen wir uns an, wo wir die erstellen XRechnungen validieren können. Immerhin wollen wir wissen, ob das Ergebnis auch vom Rechnungsempfänger verarbeitet werden kann. Es gibt eine Reihe Online-Validatoren, zum Beispiel diesen hier:

https://www.epoconsulting.com/erechnung/xrechnung-validator

Hier geben Sie das zu prüfende XML-Dokument in ein Textfeld ein und klicken auf die Schaltfläche Validieren.

Praktischerweise stellt diese Webseite auch zwei Beispielrechnungen im XML-Format bereit, die Sie kopieren und validieren können.

Aufbau des Datenmodells

Das Datenmodell haben wir so aufgebaut, dass wir die Daten leicht für die Erstellung der XRechnung im XML-Format nutzen können. Es sieht in der Übersicht wie in Bild 1 aus.

Datenmodell der Beispieldatenbank

Bild 1: Datenmodell der Beispieldatenbank

Die Tabelle tblKontakte enthält die Daten der Rechnungssteller und der Rechnungsempfänger. Da in der XRechnung der Ansprechpartner in einem Feld angegeben wird, haben wir es uns hier einfach gemacht und auch nur ein Feld namens Ansprechpartner statt Vorname und Nachname hinterlegt. In dieser Tabelle sind auch die Kontodaten des jeweiligen Kontakts hinterlegt.

Die Tabelle tblRechnungen enthält die Basisdaten einer Rechnung. Dazu gehören das Rechnungsdatum, die Währung und ein Feld für die Kundenreferenz. Außerdem wählen wir einige Informationen aus Nachschlagefeldern für Fremdschlüsselfelder aus. Dazu gehört der Rechnungstyp. Die dafür verfügbaren Werte speichern wir in einer weiteren Tabelle namens tblRechnungstypen (siehe Bild 2).

Tabelle mit den Rechnungstypen

Bild 2: Tabelle mit den Rechnungstypen

Außerdem enthält die Tabelle tblRechnungen zwei Fremdschlüsselfelder, mit denen Daten aus der Tabelle tblKontakte ausgewählt werden können – und zwar Ver-kaeu-fer-ID und KaeuferID. Um dies im Beziehungen-Fenster abzubilden, haben wir die Tabelle tblKontakte zwei Mal hinzugefügt, einmal unter dem Originalnamen und einmal unter tblKontakte_1.

Die Tabelle tblPositionen nimmt die Rechnungspositionen auf. Zum Zuordnen einer Position zu einer Rechnung enthält sie das Fremdschlüsselfeld RechnungID. Außerdem speichert sie die Bezeichnung der Position, den Einzelpreis, den Mehrwertsteuersatz und die Menge. Den Mehrwertsteuersatz geben wir durch die Auswahl eines der Einträge der Tabelle tblMehrwertsteuersaetze für das Fremdschlüsselfeld MehrwertsteuersatzID an.

Aufbau einer XRechnung

Wie bei XML-Dokumenten üblich, ist die XRechnung hierarchisch aufgebaut. Das Root-Element heißt dabei Invoice und definiert gleich noch die für das Dokument gültigen Namespaces:

<ubl:Invoice xmlns:ubl="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2" xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2 http://docs.oasis-open.org/ubl/os-UBL-2.1/xsd/maindoc/UBL-Invoice-2.1.xsd">

Danach folgen einige Elemente, welche Basisdaten einer Rechnung abbilden. Da wir zunächst eine valide Rechnung erstellen und nicht direkt alle möglichen Elemente einbeziehen wollen, werfen wir zunächst einen Blick auf die notwendigen Elemente.

Das Dokument XRechnung Standard und Extension liefert im Kapitel Detailbeschreibung eine Übersicht der Elemente und vor allem der erwarteten Anzahl der Elemente. Diese wird dort in der Übersicht in eckigen Klammern angegeben. Die Rechnungsnummer zum Beispiel muss immer angegeben werden:

-Invoice number : Identifier 

Die Angabe des Zahlungsziels hingegen ist nicht verpflichtend:

-Payment due date : Date [0..1]

[0..1] bedeutet, dass das Element gar nicht oder einmal vorkommen darf.

Auf diese Weise finden wir heraus, ob Elemente überhaupt angegeben werden müssen und stellen so unsere minimal benötigten Elemente zusammen. Der Hintergrund ist, dass wir die dort einzufügenden Informatinen auch in unserem Datenmodell finden müssen.

Gegebenfalls könnten wir die Daten der Rechnung, die immer gleich sind, weil es sich um die Daten des Rechnungsstellers handelt, einfach fest im Code verdrahten. Aber wir wollen nicht, dass wir für jede Änderung den Entwurf des Codes anpassen müssen.

Abweichungen zwischen Standard und Umsetzung

Wenn wir die Bezeichnungen der Felder im PDF-Dokument XRechnung Standard und Extension anschauen und diese mit denen vergleichen, die als Beispieldokumente unter https://www.epoconsulting.com/erechnung/xrechnung-validator bereitstehen, stellen wir einige Unterschiede fest. Diese sind zu erklären, da wohl das .xsl-Dokument unter folgendem Link zum Transformieren des einen in das andere Dokument verwendet wird:

https://github.com/itplr-kosit/xrechnung-visualization/blob/master/src/xsl/ubl-invoice-xr.xsl

Wir werden also per VBA direkt in das Format der Beispieldokumente exportieren.

Erster Test

Wir wollen uns an das finale Dokument herantasten und erstellen erstmal das Invoice-Element mit einigen Unterelementen. Dazu erstellen wir ein provisorisches Formular mit einigen Rechnungsdaten aus dem Datenmodell (siehe Bild 3). Das Hauptformular zeigt die Daten der Tabelle –tblRechnungen an und erlaubt die Auswahl der verknüpften Daten für die Felder RechnungstypID, VerkaeuferID und KaeuferID per Kombinationsfeld.

Formular zum Zusammenstellen einer Rechnung

Bild 3: Formular zum Zusammenstellen einer Rechnung

Das Unterformular zeigt die Daten der verknüpften Tabelle tblPositionen an.

Ein weiteres Formular, das wir hier nicht abgebildet haben, heißt frmKontakt und erlaubt die einfache Eingabe neuer Kontakte.

Auf Komfort wie das Anzeigen dieses Formulars vom Formular frmRechnung haben wir an dieser Stelle verzichtet.

Für die Schaltfläche cmdXRechnungErstellen hinterlegen wir die Prozedur aus Listing 1. Diese verwendet zwei Hilfsfunktionen:

Private Sub cmdXRechnungErstellen_Click()
     Dim objXML As DOMDocument60
     Dim objInvoice As IXMLDOMNode
     Set objXML = New DOMDocument60
     Set objInvoice = CreateSubElement(objXML, "ubl:Invoice")
     CreateAttribute objInvoice, "xmlns:ubl", "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
     CreateAttribute objInvoice, "xmlns:cac", "urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
     CreateAttribute objInvoice, "xmlns:cbc", "urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"
     CreateAttribute objInvoice, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"
     CreateAttribute objInvoice, "xsi:schemaLocation", "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2 " _
         & "http://docs.oasis-open.org/ubl/os-UBL-2.1/xsd/maindoc/UBL-Invoice-2.1.xsd"
     CreateSubElement objInvoice, "cbc:CustomizationID", "urn:cen.eu:en16931:2017#compliant#urn:" _
         & "xoev-de:kosit:standard:xrechnung_1.2"
     CreateSubElement objInvoice, "cbc:ID", Me!RechnungID
     CreateSubElement objInvoice, "cbc:IssueDate", Format(Me!Rechnungsdatum, "yyyy-mm-dd")
     CreateSubElement objInvoice, "cbc:InvoiceTypeCode", Me!RechnungstypID
     objXML.Save CurrentProject.Path & "XRechnung.xml"
End Sub

Listing 1: Erster Test zum Zusammenstellung der XRechnung

  • CreateSubElement legt ein neues Element unterhalb des mit dem ersten Parameter übergebenen Elements an. Der zweite Parameter gibt den Namen an, der dritte den Wert, soweit dieser zugewiesen werden soll. Die Funktion gibt das neue IXMLDomNode-Element als Ergebnis zurück.
  • CreateAttribute legt ein Attribut für das mit dem ersten Parameter übergebene Element an. Der zweite Parameter nimmt den Namen des zu erstellenden Attributs entgegen, der dritte den Wert.

Die Prozedur cmdXRechnungErstellen_Click erstellt ein neues XML-Dokument des Typs DOMDocument60. Damit dies gelingt, müssen wir noch einen Verweis auf die Bibliothek Microsoft XML, v6.0 hinzufügen. Das erledigen Sie mit dem Verweise-Dialog, den Sie mit dem Menüeintrag Extras|Verweise des VBA-Editors öffnen (siehe Bild 4).

Verweis auf die XML-Bibliothek

Bild 4: Verweis auf die XML-Bibliothek

Danach fügt die Prozedur mit der Funktion CreateSubElement ein neues Element des Typs ubl:Invoice hinzu und referenziert dieses mit der Variablen objInvoice.

Die Referenz benötigen wir, damit wir unter dem ubl:Invoice-Element noch weitere Attribute und Elemente hinzufügen können.

Das erledigen wir mit den folgenden Aufrufen der CreateAttribute-Funktion. Anschließend fügen wir die Elemente CustomizationID mit dem Wert hinzu, den wir in der Beispielrechnung gefunden haben.

Außerdem fügen wir aus der Tabelle tblRechnungen die Werte der Felder RechnungID, Rechnungsdatum und RechnungstypID hinzu. Das Datum formatieren wir noch entsprechend auf yyyy-mm-dd.

Danach speichern wir das neue XML-Dokument mit der Save-Methode von objXML im Verzeichnis der aktuellen Datenbank. Die so gespeicherte Datei laden wir dann zum Validieren auf die Seite aus Bild 5 hoch (oder die Seite eines anderen Anbieters, falls gewünscht).

Validieren der Rechnung

Bild 5: Validieren der Rechnung

Die Validierung dauert einige Sekunden länger, als man erwarten würde – vermutlich möchte der Anbieter verhindern, dass die Validierung automatisiert genutzt wird.

Das Ergebnis liefert uns eine Liste der Elemente, die noch fehlen beziehungsweise die ungültige Werte enthalten. Hieran wollen wir uns nun orientieren und die fehlenden Informationen hinzufügen beziehungsweise die falschen Daten korrigieren.

Allerdings zeigt sich schon nach dem Hinzufügen eines der fehlenden Elemente, dass die Meldung wohl immer angezeigt wird, wenn auch nur ein Element der aufgelisteten Elemente fehlt. Außerdem finden wir im Dokument XRechnung Standard und Extension heraus, dass das Note-Element beispielsweise gar kein Pflichtelement ist.

Das macht die Korrektur auf Basis des Validierungsergebnisses nicht leichter. Also arbeiten wir uns lieber anhand des Beispieldokuments, das erfolgreich validiert werden konnte, durch die geforderten Daten.

Wir fügen nun zunächst alle noch fehlenden Elemente hinzu, die im Dokument XRechnung Standard und Extension als Pflichtelement angegeben werden.

Erweiterung um die fehlenden Elemente

Bevor wir die Funktionalität erweitern, gliedern wir die Anweisungen zum Erstellen der XRechnung in eine eigene Prozedur namens XRechnungErstellen aus, der wir lediglich den Primärschlüssel für die Rechnung übergeben. Den Aufruf dieser Funktion fügen wir dann in der Ereignisprozedur ein:

Private Sub cmdXRechnungErstellen_Click()
     XRechnungErstellen Me!RechnungID
End Sub

Wir erweitern die Prozedur, die durch die Schaltfläche cmdXRechnungErstellen_Click aufgerufen wird, zunächst um einige Variablen.

Die meisten davon sollen Elemente des Typs IXMLDomNode aufnehmen – entweder, weil wir noch weitere Unterelemente anlegen wollen, für die wir das übergeordnete Element referenzieren müssen und/oder weil wir Attribute zu dem referenzierten Element hinzufügen wollen:

Public Sub XRechnungErstellen(lngRechnungID As Long)
     Dim objXML As DOMDocument60
     Dim objInvoice As IXMLDOMNode
     Dim objOrderreference As IXMLDOMNode
     Dim objContractDocumentReference As IXMLDOMNode
     Dim objProjectReference As IXMLDOMNode
     Dim objAccountingSupplierParty As IXMLDOMNode
     Dim objParty As IXMLDOMNode
     Dim objPostalAddress As IXMLDOMNode
     Dim objCountry As IXMLDOMNode
     Dim objPartyTaxScheme As IXMLDOMNode
     Dim objTaxScheme As IXMLDOMNode
     Dim objPartyLegalEntity As IXMLDOMNode
     Dim objContact As IXMLDOMNode
     Dim objAccountingCustomerParty As IXMLDOMNode
     Dim objPaymentMeans As IXMLDOMNode
     Dim objPayeeFinancialAccount As IXMLDOMNode
     Dim objPaymentTerms As IXMLDOMNode
     Dim objFinancialInstitutionBranch As IXMLDOMNode
     Dim objTaxTotal As IXMLDOMNode
     Dim objTaxAmount As IXMLDOMNode
     Dim objTaxSubtotal As IXMLDOMNode
     Dim objTaxableAmount As IXMLDOMNode
     Dim objTaxCategory As IXMLDOMNode
     Dim objLegalMonetaryTotal As IXMLDOMNode
     Dim objLineExtensionAmount As IXMLDOMNode
     Dim objTaxExclusiveAmount As IXMLDOMNode
     Dim objTaxInclusiveAmount As IXMLDOMNode
     Dim objPayableAmount As IXMLDOMNode
     Dim objInvoiceLine As IXMLDOMNode
     Dim objInvoicedQuantity As IXMLDOMNode
     Dim objItem As IXMLDOMNode
     Dim objSellersItemIdentification As IXMLDOMNode
     Dim objClassifiedTaxCategory As IXMLDOMNode
     Dim objPrice As IXMLDOMNode
     Dim objPriceAmount As IXMLDOMNode
     Dim db As DAO.Database
     Dim rstRechnung As DAO.Recordset
     Dim rstPositionen As DAO.Recordset
     Dim rstVerkaeufer As DAO.Recordset
     Dim rstKaeufer As DAO.Recordset
     Dim rstMehrwertsteuersaetze As DAO.Recordset

rstPositionen schließlich soll die Rechnungspositionen aufnehmen, die wir in einem Unterformular anzeigen. Diese durchlaufen wir, weil wir auch die Rechnungspositionen in Form geeigneter Elemente zum XML-Dokument hinzufügen wollen.

rstVerkaeufer nimmt die Daten des Eintrags der Tabelle tblKontakte auf, dessen Primärschlüsselfeld mit dem Wert des Feldes VerkaeuferID des aktuellen Datensatzes aus rstRechnung übereinstimmt.

Ähnlich sieht es beim Recordset rstKaeufer aus, der den passenden Datensatz zum Feld KaeuferID aus rstRechnung aufweist. Mit rstMehrwertsteuersaetze durchlaufen wir später beim Summieren der Mehrwertsteuer die Positionen verschiedener Mehrwertsteuersätze.

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

2 Kommentare

  1. Hallo, ich habe mit der Beispieldatei eine Rechnung erzeugt und diese dann validiert. Folgendes Ergebnis habe ich erhalten: “Konformitätsprüfung: Das geprüfte Dokument entspricht keinem zulässigen Dokumenttyp und ist damit nicht konform zu den formalen Vorgaben.” Welche Anpassungen sind notwendig? Danke und Gruß

    1. Hallo,

      das ist leicht aufgelöst: Der Validator verwendet eine Version, die ab 1.1.2024 gültig ist. Da kommt die Version aus dem Artikel vermutlich nicht mit. Ich schaue, ob ich dazu mal ein Update machen kann.

Schreibe einen Kommentar