JasperReports ist ein leistungsstarkes und flexibles BI Werkzeug mit dem sich anspruchsvolle, pixelperfekte Berichte in zahlreichen Zielformaten erstellen lassen. Es ist möglich, unterschiedlichste Datenquellen anzubinden und Analyseergebnisse in eine Vielzahl von Zielsysteme auszugeben oder zu integrieren. Doch auch ein so leistungsstarkes Tool hat seine EigenheitenEine solche stelle ich in diesem Blog-Beitrag ausführlich vor:  Erstellt man mehrseitige Dokumente, zeigt JasperReports inkonsistentes Verhalten beim Laden des letzten Datensatzes am Ende einer Seite. 

Anwendungsfall: Generieren mehrseitiger Rechnungsdokumente mit JasperReports

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 Artikelzu 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. 

Beispiel eines mehrseitigen Rechnungsdokuments mit Übertrag (Fall 1)

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.

Tabelle mit Detailinformationen zu Rechnungsartikeln und Zwischensummen

Warum stimmen die kummulierten Werte am Seitenende mehrseitiger Dokumente manchmal nicht?

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. Zwei Fälle können auftreten:

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

Fehlerhafte Rechnungssumme auf der Ausgangseite einer mehrseitigen Rechnung (Fall 2)

Wie geht man mit dieser Inkonsistenz um?

Umgehen einer „echten“ Lösung durch Weglassen von Informationen

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

Da Textfelder bei JasperReports dynamische Höhen haben werden Seiten nur selten bis zum letzten Pixel gefüllt, wie oben in Fall  1 geschildert. Viel häufiger wird ein Datensatz zwar noch in den Speicher geladen, aber erst auf die Folgeseite gedruckt, so dass sich falsche Zwischensummen ergeben. Um die Anzahl falscher Zwischensummen zu reduzieren, könnte man den Prozess einfach umkehren: Man gibt auf der Ausgangsseite den Wert von subtotal_excl an.

Wünschenswerte Funktionen von JasperReports für korrekte kummulierte Werte in mehrseitigen Dokumenten

Eine allgemeingültige(?) 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 erfordern, 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 das beschriebene 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 1] und funktioniert nicht wie erhofft für Fußzeilen.

Wie eine voll funktionsfähige Lösung des Problems aussieht, erläutere ich ausführlich in einem weiteren Beitrag.