JasperReports ist ein leistungsstarkes Werkzeug zur Erstellung anspruchsvoller Berichte in zahlreichen Zielformaten auf der Grundlage unterschiedlichster Datenquellen für verschiedene Zielsysteme. Dieser Blog-Beitrag befasst sich mit einem Problem, welches bei der Erstellung von mehrseitigen Berichten auftritt. Genauer gesagt: JasperReports zeigt inkonsistentes Verhalten beim Laden des letzten Datensatzes am Ende einer Seite.

In diesem ersten Teil des Beitrags wird das Problem skizziert, und zwei einfache, aber nicht zufriedenstellende Lösungen erläutert. Im zweiten Teil wird eine kompliziertere, aber voll funktionsfähige Lösung für das Problem demonstriert.

Setup

Betrachten Sie zunächst ein einfaches Dokument wie einen Rechnungsbrief, der an einen Kunden geschickt werden soll. Eine solche Rechnung enthält in der Regel mehrere Artikel, zu welchen jeweils detaillierte Informationen abgedruckt werden. In der Abbildung unten ist eine zweiseitige Rechnung dargestellt. In diesem Beispiel ist am Ende der ersten Seite eine Zwischensumme (blau) angegeben; der gleiche Wert wird am Anfang der zweiten Seite als Übertrag angezeigt.

Stellen Sie sich zusätzlich eine Datenquelle vor, die von einem externen System generiert wird, das alle monetären Berechnungen durchführt und einen fertigen Datensatz mit Beträgen und detaillierten Informationen für jeden Artikel liefert. Die Datenquelle liefert sogar zwei Zwischensummen für jeden Artikel (die eine enthält den Betrag des aktuellen Artikels bereits, die andere nicht). In der folgenden Tabelle sind die in der obigen Abbildung dargestellten Artikel aufgelistet.

name detail1 detail2 detail3 detail4 detail5 amount subtotal_excl subtotal_incl
1. Artikel Erste Zeile … Zweite Zeile … Dritte Zeile … Vierte Zeile … Fünfte Zeile … 10,00 € 0,00 € 10,00 €
2. Artikel Erste Zeile … Zweite Zeile … Dritte Zeile … Vierte Zeile … 10,00 € 10,00 € 20,00 €
3. Artikel Erste Zeile … Zweite Zeile … 10,00 € 20,00 € 30,00 €
4. Artikel Erste Zeile … Zweite Zeile … Dritte Zeile … Vierte Zeile … Fünfte Zeile … 10,00 € 30,00 € 40,00 €
5. Artikel Erste Zeile … Zweite Zeile … Dritte Zeile … Vierte Zeile … 10,00 € 40,00 € 50,00 €
6. Artikel Erste Zeile … Zweite Zeile … Dritte Zeile … Vierte Zeile … Fünfte Zeile … 10,00 € 50,00 € 60,00 €
7. Artikel Erste Zeile … Zweite Zeile … Dritte Zeile … Vierte Zeile … 10,00 € 60,00 € 70,00 €
8. Artikel Erste Zeile … Zweite Zeile … 10,00 € 70,00 € 80,00 €

Problem

Mit dem beschriebenen Setup könnte die Zwischensumme ausgedruckt werden, indem man den Wert von subtotal_incl des letzten Artikels der Seite verwendet. Für den Übertrag auf der folgenden Seite könnte der Wert aus subtotal_excl des ersten Artikels der Seite genutzt werden.

Wenn das Ende der Seite erreicht ist, zeigt JasperReports jedoch eine Inkonsistenz, die das Drucken der korrekten Zwischensumme erschwert. Es gibt zwei mögliche Fälle am Ende einer Seite:

  1. Die Seite wird komplett genutzt, es ist kein Pixel Platz mehr übrig. Der letzte auf der Seite gedruckte Artikel entspricht dem letzten auf dieser Seite in den Speicher geladenen Artikel.
  2. Die Seite wird nicht vollständig gefüllt, weil die Details des letzten Artikel zu umfangreich sind und der Artikel auf die Folgeseite verschoben wird. Der letzte auf der Seite gedruckte Eintrag entspricht nicht dem letzten auf dieser Seite in den Speicher geladenen Eintrag.

Im ersten Fall werden alle Inhalte korrekt gedruckt (siehe Beispiel oben). Im zweiten Fall sind die Werte des Artikels, der nicht auf die Seite passt, bereits in den Speicher geladen und werden für die Zwischensumme verwendet, was dazu führt, dass ein falscher Wert gedruckt wird (siehe Beispiel unten). Diese Inkonsistenz wirkt sich nicht auf den Seitenanfang aus, daher ist der Übertrag nach wie vor korrekt.

Lösung 1: Nicht wirklich eine Lösung

Akzeptieren der Unzulänglichkeiten von JasperReports und Weglassen von Zwischensummen. Offensichtlich ist dies nicht wirklich eine Lösung!

Lösung 2: Einfach, aber nicht zufriedenstellend

In den meisten Berichten tritt der erste Fall nur selten auf, da Textfelder dynamische Höhen haben und ein exaktes Füllen der Seite bis zum letzten Pixel unwahrscheinlich scheint. Eine einfache Lösung, um die Anzahl falscher Zwischensummen zu reduzieren, besteht darin, das Problem umzukehren und den Wert von subtotal_excl zu verwenden, um die korrekte Zwischensumme für den zweiten Fall (anstelle des ersten Falls) verfügbar zu machen.

Gibt es eine ordentliche Lösung?

Höchstwahrscheinlich gibt es sie, aber sie ist weder eine einfache Lösung noch eine offensichtliche!

Eine angemessene Lösung würde entweder ein anderes Verhalten von JasperReports am Ende einer Seite erfordern (z.B. Zurückgehen zum vorherigen Element, wenn das neu geladene Element auf die nächste Seite verschoben werden muss) oder die Fähigkeit, zwischen dem ersten und dem zweiten Fall zu unterscheiden (z.B. durch das Erkennen, dass die Seite bis zum letzten Pixel gefüllt wurde, oder durch die leichte Zugänglichkeit der Information, dass ein neu geladenes Element auf die nächste Seite verschoben werden muss).

Leider gibt es in JasperReports keine Funktionalitäten, um eine dieser Lösungen nativ, schnell und einfach zu implementieren. Es gibt zwar die isPrintWhenDetailOverflows Bedingung für Textfelder, deren Name suggeriert Abhilfe für dieses Problem zu schaffen, jedoch ist diese Bedingung dazu gedacht, “einige der bereits angezeigten Elemente auf der neuen Seite erneut zu drucken, um den Kontext, in dem der Seitenumbruch stattfand, wiederherzustellen” (übersetzt aus dem Englischen) und funktioniert nicht wie erhofft für Fußzeilen.

Im nächsten Teil dieses Beitrags wird eine voll funktionsfähige Lösung des Problems demonstriert und ausführlich erklärt!