===== Factur-X 1.0 aka ZUGFeRD 2.2 ===== Das ZUGFeRD-Datenformat basiert auf der Richtlinie 2014/55/EU vom 16. April 2014 über die elektronische Rechnungsstellung bei öffentlichen Aufträgen und auf der am 28. Juni 2017 veröffentlichten Norm EN 16931. Zudem werden die Cross-Industry-Invoice (CII) ((Cross Industry Invoice\\ \\ )) von UN/CEFACT ((United Nations Centre for Trade Facilitation and Electronic Business\\ \\ )) und die ISO-Norm 19005-3:2012 (PDF/A-3) bei ZUGFeRD 2.2.0 berücksichtigt. ZUGFeRD 2.2 und Factur-X 1.0 sind vollständig kompatible und technisch identische Formate, die bereits seit dem 24. März 2020 gemeinsam die Kennung Factur-X nutzen. Beide Formate sind grundsätzlich für den Rechnungsaustausch zwischen Unternehmen, der öffentlichen Verwaltung und Verbrauchern geeignet. In der hybriden Ausprägung beinhaltet das Rechnungsformat ZUGFeRD die strukturierten Rechnungsdaten in einer PDF/A-3 Datei, die die Sichtkomponente der Rechnung bildet. Die strukturierten XML-Rechnungsdaten können vom Rechnungs(-)empfänger ausgelesen und verarbeitet werden. Die EU-Richtlinie EN 16931 gibt die Verwendung des strukturierten Datenformats XML für den elektronischen Rechnungs(-)austausch vor, welches eine automatisierte Rechnungsverarbeitung ermöglicht. Ein standardisiertes semantisches Datenmodell beschreibt die Informationselemente einer Rechnung und deren gegenseitige Beziehung und Datentypen. Die Vorgabe der Syntax stellt eine einheitliche technische Umsetzung der E-Rechnung in der EU sicher. Insofern ist die im Tip [[print2forms:tips:tip69|ZUGFeRD]] vorgestellte Lösung nicht mehr aktuell. \\ \\ ==== Update auf Factur-X ==== {{print2forms:tips:0092-1.png }} Die grundlegenden Komponenten der ZUGFeRD-Lösung können für Factur-X übernommen werden. Deshalb setzen die nachfolgenden Ausführungen die Kenntnisse aus dem Tip [[print2forms:tips:tip69|ZUGFeRD]] voraus. Insbesondere die dort vorgestellten Hinweise zur Inbetriebnahme einer Lösung sind auch im Falle der Factur-X gültig. Eine Besonderheit von Factur-X ist, dass es in fünf sogenannten **Profilen** zur Verfügung steht: Minimum, Basic WL, BASIC, EN16931 und Extended, sowie ein Referenzprofil XRechnung. Da die ersten drei Pofile in Deutschland keine gültige Rechnung darstellen, sondern lediglich eine Buchungshilfe, wurde für die nachfolgenden Ausführungen das Profil **EN 16931** als Grundlage gewählt. Gegenüber der bisherigen Lösung wurde das Beispieldokument um die E-Mail-Adressen von Lieferant und Kunden erweitert. Die E-Mail-Adresse des Rechnungsem(-)pfängers befindet sich unterhalb des Adressfelds, die des Rechungsausstellers ganz unten rechts bei den Geschäftsangaben. \\ ==== Anpassungen ==== Das hier verwendete Skript macht zur Analyse der Kontrolldatei des (p2f)-Gateways ausgiebigen Gebrauch von einer Hilfsdatei, in der eine spezielle Klasse **factXrech** definiert ist. Diese Klasse reduziert den Aufwand des eigentlichen Skripts für die Factur-X erheblich und abstrahiert auch viele technische Details. Das eigentliche Skript hat den Name **[[print2forms:tips:tip92c|factur-x.php]]** und wird im nachfolgenden schrittweise vorgestellt. Die eben genannte Klasse wird im Rahmen dieses Textes nur insoweit informal beschrieben, als die Methoden und Eigenschaften vom Skript //factur-x.php// genutzt werden. Eine genaue Beschreibung findet sich im Tip [[print2forms:tips:tip97|factXrech-class.php]]. \\ \\ ==== Ablauf ==== Die ersten paar Zeilen des Skripts **factur-x.php** bereiten die eigentliche Arbeit vor. Als allererstes wird das zweite Skript einkopiert (inkludiert), sodass die dort gemachte Definition der **Klasse** //factXrech// zur Verfügung steht. Anschliessend wird das aktuelle Arbeitsverzeichnis auf das Skriptverzeichnis gesetzt, damit zur Vereinfachung im weiteren auch relative Pfadangaben benutzt werden können. Es wird geprüft, ob das Skript mit einem Parameter aufgerufen wurde, wobei das der Pfad und Namen der zu bearbeitenden Datei ist. ((Der für ZUGFeRD benötigte zweite Parameter mit dem Lizenzschlüssel wird nicht mehr benötigt. Die Factur-X-Lösung ist inzwischen lizenzfrei. Factur-X kommt auch ohne das bisher notwendige Java-Programm aus, weil eine eventuell geforderte Einbettung der %%XML%%-Datei in ein %%PDF%%-Dokument inzwischen von GhostScript mit Hilfe von etwas **PostScript**-Code selbst übernommen werden kann - siehe [[https://ghostscript.com/blog/zugferd.html|GhostScript ZUGFeRD]]. \\ \\ )) Evenuelle Fehlermeldungen werden in einer Datei **factxrech.log** gesammelt, die sich im gleichen Verzeichnis wie das Skript befindet. ((Der Namespace 'factXrech::' vor dem Aufruf der Funktion ist notwendig, weil zu diesem Zeitpunkt noch kein Objekt dieser Klasse instanziiert wurde.\\ \\ )) \\ \\ \\ Die nächsten Zeilen starten jetzt das Aufsammeln der Rechnungsdaten, die für die Factur-X %%XML%%-Datei benötigt werden. Dazu wird als erstes ein Objekt //$fxr// angelegt. Der **Konstruktor** (//new//) des Objekts bekommt als Parameter ein Kennzeichen für die **Factur-X** ((Die Klasse **factXrech** ist so geschrieben, dass sie sowohl %%XML%%-Dateien für den Standard **Factur-X** erzeugen kann, als auch die für den Standard **Factur-X 1.0** (ZUGFeRD 2.2). Deshalb definiert der erste Parameter, welcher Standard genutzt werden soll.\\ \\ )), eine Zeitzone und den Namen der Indexdatei. Der Konstruktor wird dann die Indexdatei lesen und für einen schnellen Zugriff in einer internen Struktur abspeichern. Die Information zur Zeitzone wird benötigt, um beim eventuellen Einbetten des %%XML%%-Dokuments in ein %%PDF%% eine korrekte Zeitinformation der Einbettung zu erzeugen. Ausserdem sorgt die korrekte Zeitzone für korrekte Zeitstempel in der Log-Datei. Die Rechnungsinformationen selbst werden in einem Feld (Array) mit dem Namen //$vars// gesammelt. Als initialen Inhalt wird lediglich der Platzhalter //ITEMS// ((Im Platzhalter //ITEMS// werden die einzelnen Rechnungspositionen bereits als %%XML%%-Daten aufgesammelt. Daher ist es wichtig, dass der Name des Platzhalters in Grossbuchstaben geschrieben wird. Das schützt die Rechnugspositionen vor einer zerstörenden zweiten Umkodierung beim Einbetten in die Vorlage der eigentlichen Rechnung.\\ \\ )) definiert und mit einer leeren Zeichenkette vorbesetzt. Die Methode //fetch// holt vom angegebenen Index unter Verwendung des angegebenen regulären Ausdrucks den Wert für den als letztes notierten Platzhalter und speichert ihn in der Variablen //$vars//. Der reguläre Ausdruck wird verwendet, um aus der Indexdatei nur die Daten zu lesen, die wirklich gebraucht werden. Gleich beim ersten Platzhalter (die Währung) wird nur ein Teil der Indexinformation ausgewertet. Konkret wird aus der Tabellenüberschrift der letzten Spalte das 'EUR' geholt. Mit dem Namen des Dokuments und des Identifikationsnummer wird ganz ähnlich verfahren. \\ \\ /* Create instance and init, collect data for Factur-X template */ $fxr = new factXrech (factXrech::FACTURX, "Europe/Berlin", "$document.CTL"); $vars = array ("ITEMS" => ""); /* start with no items present */ $fxr->fetch ("0105D20985", $vars, "in ([A-Z]+)", "currencyID"); $fxr->fetch ("0100E200C6", $vars, "(.*)", "HeaderExchangedDocumentName"); $fxr->fetch ("01012D0846", $vars, "(.*)", "DocumentID"); \\ Die nächsten Zeilen holen die restlichen Rechnungsinformationen. Neu ist hier, dass als fünfter, offensichtlich optionaler Parameter für die Methode //fetch// eine Konstante aus der Klasse übergeben wird. Damit wird die Formatierung der Information gesteuert. Wie der Name bereits suggeriert, sorgt //factXrech::DATE_FIELD// dafür, dass eine Zeichenkette der Form '04.03.2019' in der Variable //$vars// als '20190304' (YYYYMMDD) landet. Entsprechend macht //factXrech::CURRENCY_FIELD// das mit Währungsinformationen, die im Factur-X-Standard mit einem Dezimalpunkt und zwei Nachkommastellen erwartet werden. //factXrech::COUNTRY_FIELD// ist deutlich komplexer, weil es zum Beispiel 'Deutschland' in 'DE' übersetzen muss. Das regelt die Klasse unter Zuhilfenahme von entsprechenden Tabellen, die als externe Dateien im Hintergrund automatisch bereitgestellt werden. Das neue Format //factXrech::MAIL_FIELD// stellt sicher, dass es sich bei dem zu prüfenden Wert um eine gültige E-Mail-Adresse handelt. ((Dazu wird eine %%PHP%%-Filterfunktion genutzt, die Adressen nach RFC-822 prüft.\\ \\ )) \\ \\ $fxr->fetch ("0101720846", $vars, "(.*)", "IssueDate", factXrech::DATE_FIELD); $fxr->fetch ("0101B70846", $vars, "(.*)", "ActualDeliveryDate", factXrech::DATE_FIELD); $fxr->fetch ("0101FC0846", $vars, "(.*)", "PaymentReference"); $fxr->fetch ("0102860846", $vars, "(.*)", "BuyerReference"); $fxr->fetch ("0102410846", $vars, "(.*)", "CustomerID"); $fxr->fetch ("01036C00C6", $vars, "(.*)", "BuyerRegistrationName"); $fxr->fetch ("0103B100C6", $vars, "(.*)", "BuyerStreetName"); $fxr->fetch ("0103F600C6", $vars, "[A-Za-z]*-?([0-9]+) .*", "BuyerPostcodeCode"); $fxr->fetch ("0103F600C6", $vars, "[A-Za-z]*-?[0-9]+ (.*)", "BuyerCityName"); $fxr->fetch ("01043B00C6", $vars, "(.*)", "CountryIC", factXrech::COUNTRY_FIELD); $fxr->fetch ("010D1A0889", $vars, "([0-9,]+) .*", "TaxableAmount", factXrech::CURRENCY_FIELD); $fxr->fetch ("010D5F0889", $vars, "([0-9,]+) .*", "TaxAmount", factXrech::CURRENCY_FIELD); $fxr->fetch ("010DA40889", $vars, "([0-9,]+) .*", "PayableAmount", factXrech::CURRENCY_FIELD); $fxr->fetch ("010D5F00C6", $vars, ".+ ([0-9,]+)% .*", "TaxPercent"); $fxr->fetch ("01050A0229", $vars, "(.*)", "BuyerEndpointID", factXrech::MAIL_FIELD); \\ Was jetzt noch fehlt, ist die Information zu der gewünschten Zahlungsweise. Hier kommt es nun zu einer Situation, in der der Windows-Druckertreiber eine längere Zeile durch eine Neupositionierung teilt. ((Hier im Beispiel wurde die Rechnung nicht (!) durch (p2f) aufbereitet. Beim Drucken aus Host-Umgebungen (%%i5/OS, z/OS, Unix%%) ist die Ausgangslage meist einfacher, weil meist dicktengleiche Schriften zum Einsatz kommen. Diese bedingen keine Repositionierungen zum Ausgleich von Laufweiten(-)unterschieden wegen fehlender oder unterschiedlicher Kerning-Tabellen. Wenn die reale Rechnung durch (p2f) aus Text- oder %%XML%%-Daten aufbereitet wurde, werden ebenfalls keine Neupositionierungen eingebaut.\\ \\ )) Hier zur Verdeutlichung der Besonderheit der entsprechende Ausschnitt aus der Indexdatei: ... [010E7300C6]innerhalb 30 Tag [010E730281]en netto bis 04.07.2018, innerhalb 14 Tagen 2% Skonto bis 19.06.2018 ... Für diesen Anwendungsfall gibt es eine Methode //getLine//, die alle Texte aufsammelt, die über eine Zeile verstreut sind. Das wird anhand der Ziffern eins bis sechs des Indexes erkannt. Nachdem die Zeile zur Verfügung steht, wird aus ihr das Datum für den Ablauf der Zahlungsfrist extrahiert, und direkt in das Feld //$vars// eingetragen. Da für ein Datum in Factur-X ein bestimmtes Format vorgeschrieben ist, muss vorher noch die Methode //formatDate// direkt aufgerufen werden. Neben //formatDate// (entspricht factXrech::DATE_FIELD) gibt es noch die Methoden //formatQuantity// (entspricht factXrech::QUANTITY_FIELD) und //formatCurrency// (entspricht factXrech::CURRENCY_FIELD), die für eigene Konvertierungen aufgerufen werden können, wenn, so wie hier, die Methode //fetch// nicht zum Einsatz kommen kann. \\ \\ /* Handling of payment is identical for all items */ $vars ["PaymentTerms"] = $fxr->getLine ("010E7300C6"); $vars ["DueDate"] = $fxr->formatDate (substr ($vars ["PaymentTerms"], 29, 10)); \\ Als nächstes erfolgt die Bearbeitung der Tabelle mit den einzelnen Rechnungsposten. Dazu werden zwei weitere Methoden benötigt //getIndex// und //setIndex//. Innerhalb der Klasse wird ein Zeiger innerhalb der Indexdatei verwaltet, der mit den beiden Methoden abgefragt oder gesetzt werden kann. Wichtig zu wissen ist auch, dass die Methode //fetch// mit diesem Zeiger arbeitet und ihn immer weiter schiebt, wenn sie ohne einen Index aufgerufen wird. Im folgenden wird also erst der Zeiger auf die erste Zeile der Indexdatei mit Tabelleninformationen gesetzt. In unserem Fall ist dies die erste Positionsnummer in der Tabelle mit dem Index //01063100FD//. Um das Ende der Tabelle zu finden, wird in der Schleife auf die erste Zeile getestet, die nicht mehr zur Tabelle gehört - am Index //010D1A00C6// steht 'Rechnungssumme Netto (exkl. USt.)'. Für jeden Rechnungsposten wird ein eigenes Feld //$itemvars// für die ausgelesenen Informationen angelegt. Das Feld wird mit der Währungsbezeichnung und dem Umsatzsteuersatz der Rechnung fest vorbesetzt. Für jede der acht Tabellenspalten wird jetzt einmal die Methode //fetch// mit einem leeren Index aufgerufen, und so das Feld //$itemvars// Schritt für Schritt gefüllt. Am Ende jeder Tabellenzeile werden die ausgelesenen Daten aus dem Feld //$itemvars// in die Vorlage für [[print2forms:tips:tip92b|Rechnungspositionen]] an Stelle der dort hinterlegten Platzhalter eingetragen. Dazu dient die Methode //substitute//, die als Parameter die Kennung der Vorlage (eine vordefinierte Konstante der Klasse) und die Variable mit den Daten erwartet. Die Methode gibt die resultierende %%XML%%-Zeichenkette zurück, die einfach an den aktuellen Wert des Platzhalters //ITEMS// in den Rechnungsinformationen angehängt wird. Am Ende der Schleife steht in //$vars// für den Platzhalter //ITEMS// der gesamte %%XML%%-Text für die vorgefundenen Rechnungs(-)positionen. \\ \\ $fxr->setIndex ("01063100FD"); /* position to start with item table */ while ($fxr->getIndex () != "010D1A00C6") /* loop through list of items */ { /* Process list of invoice items, all items have fix VAT and currency */ $itemvars = array ("currencyID" => $vars ["currencyID"], "TaxPercent" => $vars ["TaxPercent"]); $fxr->fetch ("", $itemvars, "(.*)", "LineID"); $fxr->fetch ("", $itemvars, "(.*)", "SellerAssignedID"); $fxr->fetch ("", $itemvars, "(.*)", "BuyerAssignedID"); $fxr->fetch ("", $itemvars, "(.*)", "Name"); $fxr->fetch ("", $itemvars, "(.*)", "PriceAmount", factXrech::CURRENCY_FIELD); $fxr->fetch ("", $itemvars, "(.*)", "UnitCode", factXrech::UNIT_FIELD); $fxr->fetch ("", $itemvars, "(.*)", "InvoicedQuantity", factXrech::QUANTITY_FIELD); $fxr->fetch ("", $itemvars, "(.*)", "LineExtensionAmount", factXrech::CURRENCY_FIELD); $vars ["ITEMS"] .= $fxr->substitute (factXrech::ITEM_TEMPLATE, $itemvars); } \\ Jetzt sind alle Informationen für Factur-X in //$vars// verfügbar. Jetzt werden durch erneuten Aufruf der Methode //substitute// diese Daten ihrerseits in die Vorlage für die eigentliche [[print2forms:tips:tip92a|Rechnung]] eingesetzt. Die resultierende Zeichenkette wird in eine temporäre %%XML%%-Datei geschrieben. Falls gewünscht, kann der zweite Parameter der Methode //createXML// mit dem Wert '1' dafür sorgen, dass die erzeugte %%XML%%-Datei gleich validiert wird. ((Dies erfolgt mit dem frei verfügbaren [[https://github.com/itplr-kosit/validator|KoSIT Validator]]. Voraussetzung dafür ist allerdings das Vorhandensein einer Java-Laufzeitumgebung auf dem ausführenden Rechner. Der KoSIT Validator ist lokal installiert, das heisst, die %%XML%%-Datei verlässt nicht (!) den lokalen Rechner! Die Ausführungszeiten des Validators bewegen sich im Bereich von vielen Sekunden, weil viele zeitaufwändige Transformationen von %%XML%%-Dateien mittels XSLT durchgeführt werden. Das senkt den möglichen Durchsatz des Gateways erheblich!\\ \\ )) Falls die Validierung scheitert, wird intern eine Fehlermeldung in die Log-Datei geschrieben, und die nachfolgende Abfrage auf Fehler verhindert die weitere Bearbeitung. ((Das Prüfprotokoll der Validierung steht im Skript-Verzeichnis und hat den Namen 'factur-x-report.html'. Es kann mit jedem Browser zur Anzeige gebracht werden.\\ \\ )) Als nächstes muss die Einbettung in eine %%PDF%%-Datei erfolgen - das Zusammenfassen der %%PDF%%-Datei und der %%XML%%-Datei zu einer Factur-X konformen %%PDF/A3%%-Datei. Die Methode //createPDF// benötigt die %%PCL%%-Datei des Gateways, einen Firmennamen, einen Titel für die Datei und Schlüsselworte. Diese Informationen werden in die Meta-Daten der %%PDF%%-Datei eingebaut und unterstützen so die automatische Weiterverarbeitung der Datei. Als allerletzte Aufgabe verbleibt, die intern angelegte %%PDF%%-Datei mit einem entsprechenden Namen an den gewünschten Zielort zu kopieren wird. \\ \\ /* create and validate xml file using external kosit validator (java) */ $fxr->createXML ($fxr->substitute (factXrech::INVOICE_TEMPLATE, $vars), 1); if ($fxr->errorsfound () == 0) /* nothing went wrong so far */ { $invoicedate = substr ($vars ["IssueDate"], 8, 2) . "." . substr ($vars ["IssueDate"], 5, 2) . "." . substr ($vars ["IssueDate"], 0, 4); $title = "Rechnung {$vars ["DocumentID"]} vom $invoicedate"; $keywords = "Muster_GmbH; {$vars ["DocumentID"]}; $invoicedate; {$vars ["CustomerID"]}"; $resultfile = $fxr->createPDF ("$document.pcl", "Muster GmbH", $title, $keywords); /* save and rename just generated pdf file */ copy ($resultfile, "Rechnung_{$vars ["DocumentID"]}_$invoicedate.pdf"); } //array_map ('unlink', glob ("$document.*")); /* uncomment after test */ ?> \\ \\ ==== Inbetriebnahme vorbereiten ==== Gleich vorweg: es ist keine gute Idee, die Factur-X-Lösung ohne die Hilfe einer entsprechenden Entwicklungsumgebung in Betrieb nehmen zu wollen. Das Skript ist nicht ganz trivial, und spätestens, wenn es an die realen Bedingungen einer konkreten Kundeninstallation angepasst werden soll, ist das mit reinem 'Print'-Debugging extrem ineffizient. Es wird deshalb noch einmal ausdrücklich auf die Tips [[print2forms:tips:tip70|Visual Studio Code für PHP installieren]], [[print2forms:tips:tip71|Test von PHP-Skripten]] und [[print2forms:tips:tip72|PHP-Skript ohne Gateway testen]] verwiesen. In den nachfolgenden Ausführungen wird davon ausgegangen, dass die Lösung mit Microsoft **Visual Studio Code** in Betrieb genommen wird. \\ \\ {{ print2forms:tips:0094-1.png}}Alle benötigten Dateien lassen sich hier einfach als {{print2forms:tips:p2fFactur-X.zip|Archiv herunterladen}}, und müssen nicht im Wiki aufgesammelt werden. Das vermeidet auch eventuelle Probleme mit der Kodierung der einzelnen Textdateien. Vorzugsweise sollte das Archiv im Skriptverzeichnis von (p2f) entpackt werden - in der Regel ''C:\Users\Public\Documents\SPE Systemhaus GmbH\print2forms\p2fScript''. Im Archiv sind auch die entsprechenden Konfigurationsdateien für Visual Studio Code enthalten, sodass der jetzt neu erscheinende Ordner //p2fFactur-X// direkt in der Entwicklungsumgebung geöffnet werden kann. Es sollte sich im Dateiexplorer die rechts abgebildete Struktur ergeben - zunächst aber mit einem unvollständigen Unterordner //converter//. Aus lizenzrechtlichen Gründen dürfen die Programme [[https://www.ghostscript.com/releases/gpcldnld.html|GhostPCL]] und [[https://www.ghostscript.com/releases/gsdnld.html|GhostScript]] der Firma Artifex nicht mit (p2f) zusammen ausgeliefert werden. ((Diese beiden Programme werden nur für den Fall benötigt, dass eine %%PDF%%-Datei der Rechnung erzeugt werden soll. Geht es nur um die %%XML%%-Datei mit der Factur-X, sind die Programme entbehrlich. Die Methode //createXPF// darf dann allerdings auch nicht aufgerufen werden.\\ \\ )) Nachdem die Programme von den hier angegebenen Web-Adressen geladen wurden, müssen für GhostPCL die beiden Dateien //gpcl6win64.exe// und //gpcl6win64.dll// (für 64-Bit Systeme, ansonsten //gpcl6win32.exe// und //gpcl6win32.dll// für 32-Bit Systeme) in den Ordner //converter// kopiert werden. Für GhostScript müssen die beiden Dateien //gswin64.exe// und //gsdll64.dll// (für 64-Bit Systeme, ansonsten //gswin32.exe// und //gsdll32.dll// für 32-Bit Systeme) kopiert werden. Die beiden Dateien im Unterordner //test// sind bereits von einem (p2f)-Gateway erzeugte Testdaten aus der Beispielrechnung. Durch die Verwendung dieser Dateien wird verhindert, dass das Office Paket auf der Testmaschine die Rechnung abweichend druckt. ((Beispielsweise wenn Microsoft Office statt Open Office verwendet wird. Das führt wegen der Dokumentkonvertierung meist zu einer geringfügig anderen Indexdatei und das Skript müsste somit erst angepasst werden.\\ \\ )) Die Dateien im Unterordner //mappings// dienen der Übersetzung von Länder- und Sprach(-)bezeichnungen, sowie Masseinheiten in die im Factur-X-Standard vorgegebenen Kodes. Die Dateien im Unterordner //validator// werden für die eventuell gewünschte Validation der %%XML%%-Dateien benötigt. Ansonsten findet sich im Verzeichnis noch die eigentliche Office-Datei mit der Rechnung //Formular.odt//. Damit ist alles eingerichtet, und die ersten Funktionstests mit Visual Studio Code können starten. \\ \\ ==== Eigene Tests mit Gateway ==== Bisher erfolgte der ganze Test des Skriptes ohne jedes Zutun eines (p2f)-Gateways. Soll jetzt Factur-X mit eigenen Dokumenten genutzt werden, ist die Kommandozeile des betreffenden Gateways entsprechend zu konfigurieren. Das Skript **factur-x.php** erwartet nur einen Parameter: den Namen des zu bearbeitenden Dokuments. Demzufolge sieht die Kommandozeile in etwa wie folgt aus: "C:\PHP8\php.exe" "%1/factur-x.php" "%2\%4" Der Pfad zum Aufruf des %%PHP%%-Interpreters ist der gleiche Pfad wie der in der Datei //lauch.json//. \\ \\ ==== Weiterverarbeitung ==== In der Praxis ist das eben entwickelte Beispielskript nur die halbe Miete, weil die erzeugte %%PDF%%-Datei noch - möglichst automatisch - weiter an den Rechnungsempfänger übermittelt werden muss. Für den Fall, dass der Rechnungsempfänger die Rechnung über den **Upload** auf einem speziellen **Portal** erwartet, ist je nach Portal eine mehr oder minder aufwändige zusätzliche Programmierung notwendig, die weit über den Umfang dieses Wikis hinausgeht. Für den Fall, dass die Rechnung vom Rechnungsempfänger als E-Mail entgegengenommen wird, kann das Skript jedoch leicht entsprechend erweitert werden. Grundlage dafür liefern die Informationen im Wiki Artikel [[print2forms:tips:tip93|Dokument via E-Mail in PHP versenden]]. \\ \\ ==== Hinweise ==== * Das Skript **factur-x.php** macht natürlich eine ganze Reihe von Annahmen, um den Einstieg in das Thema nicht komplizierter zu machen als unbedingt notwendig. Eine der Annahmen ist, dass die Rechnung einseitig ist. Es ist klar, dass die Schleife zum Auslesen der Rechnungsposten auch für Folgeseiten (eventuell etwas modifiziert) ausgeführt werden muss. * Die beiden Dateien im Unterverzeichnis //test// müssen nicht (!) in das Spool-Verzeichnis von (p2f) kopiert werden, weil in der Datei //lauch.json// das Spool-Verzeichnis für das Projekt so konfiguriert ist, dass das Unter(-)verzeichnis //test// benutzt wird. * Eine weitere Annahme ist, dass beim Drucken der Rechnungsposten wirklich sequenziell Zeile für Zeile ausgegeben wird. Das ist manchmal nicht der Fall, weil die Anwendung, die die Rechnung erzeugt z.B. spaltenweise vorgeht. Das ist aber durchaus lösbar. * Auch zusätzliche **Umsatzsteuersätze** oder auch die **Altteilsteuer** werden hier im Rahmen des Wikis vereinfachend ausser Acht gelassen. Wenn solche Anforderungen im Raum stehen, sprechen Sie uns für eine Lösung bitte direkt an. * Im Gegensatz zur bisherigen Lösung für [[print2forms:tips:tip69|ZUGFeRD]] ist für die hier vorgestellten neuen Skripte **keinerlei Lizenzierung** notwendig. Ausser den Erstellungskosten für Skriptanpassungen entstehen keine weiteren Kosten. \\ \\