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.
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).
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.
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).
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).
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
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ß
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.