Benutzerdefiniertes Reporting-Tool von HTML zu PDF: flexibel, einfach und gesetzeskonform

In der Geschäftswelt ist Reporting ein notwendiges Übel: Es wird akzeptiert bzw. muss gemacht werden, um ein besseres Ergebnis zu erzielen (z. B. organisatorische Übersicht, Standardisierung, Disziplin). Und als ob der Druck nicht so schon groß genug wäre, schreiben Aufsichtsbehörden immer mehr Auflagen vor.

Ein gutes Beispiel dafür ist die europäische Zahlungsdiensterichtlinie 2015/2366 (siehe die offizielle Seite, die Wikipedia-Seite und den offiziellen Umsetzungsplan der EU): Aufgrund des technologischen Wandels im Zahlungsverkehr zielt sie darauf ab„die Verbraucher besser zu schützen, wenn sie online bezahlen, die Entwicklung und Nutzung innovativer Online- und mobiler Zahlungen zu fördern und grenzüberschreitende europäische Zahlungsdienste sicherer zu machen.“ Im Klartext bedeutet dies, dass „ein einheitlicher, offener und sicherer europäischer technischer Standard für QR-Codes [that] entwickelt und umgesetzt werden soll, der die Akzeptanz und Interoperabilität von Sofortzahlungen fördern würde.“ Die endgültige Frist für die Umsetzung: 14. September 2019 (inoffiziell „wegen der Pandemie“ verlängert).

Für praktisch alle B2C-Unternehmen bedeutet dies, dass sie auf allen Rechnungen standardisierte Sicherheitsmerkmale (z. B. QR-Codes) anbringen müssen. Darüber hinaus bedeutet dies, dass die Zahlungssoftware aktualisiert werden muss, um den gesetzlichen Anforderungen gerecht zu werden und ein schlankeres Reporting zu erstellen, das den internen Anforderungen entspricht. Aber manche (sehr) alte Software lässt sich nicht einfach aktualisieren, und man muss wieder ganz von vorne anfangen.

Für einen Kunden aus dem Einzelhandelssektor haben wir ein maßgeschneidertes Reporting-Tool entwickelt, das:

  • gesetzeskonform ist: Es enthält alle Sicherheitselemente, die in der EU-Richtlinie 2015/2366 gefordert werden; und
  • programmatisch ist: Es definiert die Struktur aller benötigten Berichte, erstellt ihre Vorlagen und füllt sie dann automatisch aus, sobald ein Bericht benötigt wird.

Warum eigene Reports erstellen, wenn es auch fertige Reportgeneratoren gibt?

Zunächst einmal braucht man Zeit, um den Umgang mit einem Reportgenerator und vor allem die Kommunikation mit dessen APIs zu erlernen. Daraus ergibt sich ein hoher Installationsaufwand (und möglicherweise aufwendiges Warten zwischendurch).

Außerdem fallen die Kosten für einen wirklich guten, leistungsstarken Reportgenerator kaum höher aus als die Kosten für ein benutzerdefiniertes Reporting-Tool.

Wenn Sie Ihr eigenes Tool entwickeln, können Sie sich auf Ihre spezifischen Anforderungen konzentrieren, ohne den Aufwand mit einer Komplettlösung von der Stange zu haben. Dies führt zu höherer Flexibilität (bei Design, Datenquellen usw.) und unvergleichlicher Leistungsfähigkeit.

Unsere Lösung

Für den betreffenden Kunden hatten wir bereits ein Cashbox-Modul als ersten Teil eines größeren ERP-Systems entwickelt. Die Reporting-Funktionen waren eine natürliche Erweiterung in Form eines zusätzlichen Microservices, der auch von anderen Modulen genutzt werden konnte.

Für jeden benötigten Report haben wir einen dreistufigen Prozess entwickelt:

  • Erstellung einer statischen HTML-Vorlage
  • Einfügen dynamischer Daten
  • Konvertierung des HTML in .pdf

Zunächst haben wir die Layouts mit HTML definiert, da dies die gängigste Sprache für die Beschreibung von Layouts ist. Für jeden Reporttyp benötigten wir drei HTML-Dateien: einen Hauptteil, eine Kopfzeile und eine Fußzeile. (Der Grund dafür ist, dass die verschiedenen Reports des Kunden alle unterschiedliche Kopfzeilen, Textkörper und Fußzeilen benötigten.) Wir konnten die Report-Modelle leicht allein in HTML erstellen und ändern, um bereits in der Anfangsphase nützliches Feedback vom Kunden einzuholen.

Nachdem wir das Layout definiert hatten, bestand der nächste Schritt darin, die dynamischen Daten einzufügen. Wir verwendeten eine heterogene Datenquelle (SQL-Tabellen, json-Dateien und In-Memory-Daten). Als die Daten zur Verfügung standen, haben wir Razor als Template-Engine verwendet, um die Daten in HTML einzubinden.

Um Razor in unsere Software zu integrieren, haben wir das NuGet-Paket verwendet und RazorEngine 3.10.0 installiert. Unten sehen Sie ein Beispiel dafür, wie Razor in einer HTML-Datei verwendet wird. In diesem Beispiel erstellt der Code eine Artikeltabelle mit drei Spalten, der Position des Artikels, dem Code des Artikels und dem Preis.

<table>
    @foreach(var article in @Model.Items){
    <tr>
        <td>
            <span> @article.Position</span>
            <span> @article.Code</span>
        </td>
        <td>
            <span> @article.Price €</span>
        </td>
    </tr>
    }
</table>

@Model stellt die Wurzel des Modells dar, in der Razor die Daten sucht. Im Code weisen wir Razor an, das angepasste ReportModel, C#-Objekt als @Model zu verwenden.

Wir nennen die Razor-Methode RunCompile mit den folgenden Parametern:

  • Inhalt der HTML-Vorlage als String
  • Template-Identifikator zur Unterscheidung eines Template-Typs von einem anderen
  • Typ des Datenmodells
  • Instanz des Datenmodells

Diese Methode liefert den HTML-String mit den eingespeisten Daten als endgültiges HTML-Ergebnis.

    var result = Engine.Razor.RunCompile(File.ReadAllText(htmlPath), templateIdentifier,typeof(ReportModel), reportModel);
ReportModel sieht wie folgt aus:
public class ReportModel
{
 
    public List<Item> Items {get; set;}
}
 
public class Item
{
    public int Position {get; set;}
    public double Price {get; set;}
    public string Code {get; set;}
}

Das Ergebnis dieser Arbeitsschritte ist eine vollständige HTML-Datei, die im letzten Schritt in einen PDF-Report umgewandelt wird.

Die Umwandlung von HTML in PDF mag wie ein einfacher Vorgang klingen, und das Internet bietet eine Menge kostenloser Tools, die genau das tun. Aber ein Report ist nicht einfach nur eine PDF-Datei, sondern viel mehr als das. Er verfügt über Funktionen wie das Hinzufügen von Kopf- und Fußzeilen, Paginierung und Druckunterstützung.

Also fingen wir an, kommerzielle HTML-zu-PDF-Bibliotheken zu evaluieren, die unsere Anforderungen an professionelle Reports erfüllen könnten. Wir probierten IronPDF, Spire.PDF und EVO PDF aus, die allesamt ein vergleichbares Preisniveau haben (etwa 500 €/Instance).

Schließlich entschieden wir uns für EVO PDF, weil (A) die generierte .pdf-Datei wie gewünscht aussah, mit integrierter Kopf- und Fußzeile (auch beim Drucken), und (B) die Dokumentation auf dem neuesten Stand und einfach zu bedienen war (sehr wichtig). Ein weiterer Pluspunkt: Man muss EVO PDF in der Entwicklungsphase nicht kaufen, da es eine voll funktionsfähige kostenlose Version gibt, die lediglich ein rotes Wasserzeichen in den Report einfügt.

So verwenden Sie EVO PDF

Zunächst mussten wir EVO PDF als NuGet-Paket in unser Projekt integrieren. EVO PDF bietet übrigens Installationsoptionen für mehrere Plattformen wie .NET, .NET CORE, Java und andere.

Einmal installiert, ist die Verwendung des Pakets einfach und gut dokumentiert.

Hier ist ein Beispielcode aus unserer Implementierung:

    var htmlToPdfConverter = new HtmlToPdfConverter(); 
// next 5 settings are to optimize the size of the pdf file
    htmlToPdfConverter.ConversionDelay = 0;
    htmlToPdfConverter.PdfDocumentOptions.EmbedFonts = true;
    htmlToPdfConverter.PdfDocumentOptions.CompressCrossReference = true;
    htmlToPdfConverter.PdfDocumentOptions.PdfCompressionLevel = PdfCompressionLevel.Best;
    htmlToPdfConverter.PdfDocumentOptions.ImagesScalingEnabled = true;
 
// Razor method is the same that we explained above
// injecting dynamic data in header 
    var headerPathAfterRazor = _razorService.Razor(headerPath, templatesDirectoryPath, reportModel); 
// injecting dynamic data in footer
    var footerPathAfterRazor = _razorService.Razor(footerPath,
 
    var headerHtml = new HtmlToPdfElement(headerPathAfterRazor);
    var footerHtml = new HtmlToPdfElement(footerPathAfterRazor);
    htmlToPdfConverter.PdfDocumentOptions.ShowHeader = true;
    htmlToPdfConverter.PdfDocumentOptions.ShowFooter = true;
 
// setting the desired height for header / footer
    htmlToPdfConverter.PdfHeaderOptions.HeaderHeight = 100;  
    htmlToPdfConverter.PdfFooterOptions.FooterHeight = 110;
    headerHtml.FitHeight = true;
    footerHtml.FitHeight = true;
// adding the header and the footer to the pdf report
    htmlToPdfConverter.PdfHeaderOptions.AddElement(headerHtml);  
    htmlToPdfConverter.PdfHeaderOptions.AddElement(footerHtml);  
 
// injecting dynamic data in body 
    var bodyPathAfterRazor = _razorService.RazorbodyPath,                templatesDirectoryPath, reportModel);      
// converting html to pdf
  var pdfAsBytes = htmlToPdfConverter.ConvertUrl(bodyPathAfterRazor);
 
Nach der Erstellung des PDF-Dokuments war der Standard-Windows-Befehl für die Druckfunktion zuverlässig und entsprach gut genug unseren Anforderungen.
print /d: C:\inetpub\reportsservice\myReport.pdf 

Aufgetretene Probleme

Reports können schnell sehr groß werden, selbst bei einfachen Seiten. Die Größe kann Hunderte von Kilobytes erreichen, vor allem wenn große Bilder oder spezielle Schriftarten verwendet werden.

Außerdem kann es einige Zeit dauern, bis Razor eine Vorlage zum ersten Mal generiert hat. Deshalb ist es besser, die Vorlage im Voraus zu erstellen und nicht während der Entstehung des eigentlichen Reports. In dieser Phase ist das Report-Modell leer, da es hier keine Rolle spielt. Nach der Erstellung der Vorlage wird die nächste Erstellung der gleichen Vorlage automatisch erfolgen.

    var result = Engine.Razor.RunCompile(File.ReadAllText(htmlPath), templateIdentifier,typeof(ReportModel), dummyReportModel);

Fazit

Bei einer maßgeschneiderten Lösung sind Reporting-Tools nicht mehr nur notwendiges Übel, denn sie

  • passen sich an den spezifischen Kontext und die Bedürfnisse der Kunden an
  • sind relativ einfach zu entwickeln und zu implementieren
  • können sehr flexibel auf Änderungen der Vorschriften und/oder der Kundenspezifika reagieren.


Wenn Sie weitere Informationen oder Anregungen für die Entwicklung Ihrer eigenen Reporting-Tools erhalten möchten, wenden Sie sich bitte an uns.

29 Jahre im Geschäft | 2700 Software-Projekte | 760 Kunden | 24 Länder

Wir verwandeln Ideen in Software. Wie lautet Ihre Idee?

Kontakt aufnehmen

15 + 8 =