JasperReports Tips und Tricks

From ADempiere
Revision as of 14:05, 26 May 2012 by Tbayen (Talk) (new parameters that you can use inside Jasper Reports)

Jump to: navigation, search
This Wiki is read-only for reference purposes to avoid broken links.

[Jasperreports] ist ein Reportgenerator, dessen Möglichkeiten weit über die der in Adempiere eingebauten Report-Fähigkeiten hinausgehen. Es ist gut in ADempiere integriert.


Links zum Thema

Die folgenden Links verweisen auf Anleitungen, die eine gute Einführung von ADempiere und Jasperreports anbieten.

Wie legt man los?

Zuerst sollte man ein aktuelles "iReport" herunterladen. Ohne grosse Anmeldung geht das von https://sourceforge.net/projects/ireport/ aus. Unter http://jasperforge.org/projects/ireport findet sich die eigentliche Projektseite mit einigen Hinweisen. Ansonsten empfehle ich auch das Buch "Ultimate Guide", was man da kaufen kann.

Grobe Gliederung der ersten Schritte: (aus dem Gedächtnis, bitte nach dem ersten Durchlauf verbessern!)

  • Ggf. Konfiguration eines Benutzers, der auf das benutzte Datenbank-Schema zugreifen kann ohne dieses immer angeben zu müssen. Dazu u.U. setzen der PostreSQL-Variable search_path fest im Benutzerprfil (per PgAdmin3)
  • Anlegen einer Datenquelle in iReport, d.h. Datenbank-Zugangsparameter
  • Anlegen eines neuen Reports entweder über Templates oder eines leeren Blattes.
  • Anlegen eines Parameters zur Übergabe einer Datensatznummer von ADempiere nach JasperReports
    • Dieser Parameter heisst "RECORD_ID" (von ADempiere vorgegeben).
    • Man kann iReport so einstellen, das bei jedem Test (ausserhalb von ADempiere) diese Nummer von Hand eingegeben werden muss (etwas lästig) und/oder das ein Default-Wert verwendet wird. Hier kann man einen vorhandenen Datensatz für Tests fest verdrahten. Dazu den Parameter anklicken und in die Einstellungen zu diesem Element schauen.
  • Eingeben eines Datenbank-Queries
    • Dabei $P{RECORD_ID} als Platzhalter verwenden. (Der Query-Generator hilft übrigens durch Drag-and-Drop dabei, wenn man einen Parameter-Namen vergessen hat.)
    • Im Query definiert man Felder, die aus dem Select in eine Art lokale Variablen ("Felder") geladen werden.
    • Diese Felder kann man links im Dokumentenbaum aufklappen und dann in den Bericht ziehen. Der einzelne Datensatz kommt in den "Detail"-Bereich, die Überschriften z.B. in den "Page Header".
  • Subreports: siehe unten "Subreport ADempiere-freundlich einrichten"


Übergabe von Parametern

Ein JasperReport wird in ADempiere über das Application-Dictionary in der Tabelle "Bericht & Prozeß" eingerichtet. Einen solchen Prozeß kann man dann in das Menü einhängen oder mit einem Button verknüpfen. Man kann hier auch Parameter angeben. Wird das getan, öffnet sich vor dem Start des eigentlichen Berichts ein kleines Eingabefenster, in dem man Parameter eingeben kann. Dabei sind die üblichen Möglichkeiten von ADempiere, um Datentypen zu definieren, vorhanden. Das bedeutet, man kann also Texte oder Zahlen eingeben aber auch Datensätze aus einer Combobox oder mit Hilfe eines Suchfensters auswählen. Der Name des Parameters kann dort konfiguriert werden. Diesen Namen kann man dann in iReport als Parameter konfigurieren. Im Fall eines ausgewählten Datensatzes wird dann z.B. die ID dieses Satzes als Integer-Wert übergeben. Wird ein Report über das Druck-Icon der Toolbar aufgerufen, so wird automatisch der Parameter RECORD_ID mit der ID des momentanen Datensatzes gefüllt.


Aufruf in ADempiere

Man erzeugt einen neuen Datensatz im Fenster "Bericht & Prozess". Dort schreibt man in das Feld "Jasperbericht" den Namen seines Berichtes. Dieser Name muss den Pfad enthalten und auf ".jasper" enden. (Die Endung ".jrxml" sollte grundsätzlich auch gehen - einerseits verlangsamt das den Start unnötig andererseits kann eine *.jasper-Datei, die mit einer anderen Version (also z.B. aus einem neueren iReport) übersetzt ist, zu Ärger führen). Der Name kann auch mit "http:", "https:", "attachment:", "resource:", "file:" anfangen, um zu erklären, wo er herkommt. Je nach Auswahl kann es übrigens zu einigen Besonderheiten kommen, was das Nachladen von Unterdateien (Subreports, Bilder) betrifft. In dem Fall empfehle ich das Studium des Sourcecodes von ReportStarter.java.

Man muss noch bei "Bericht" ein Häkchen machen. Dieses Häkchen führt einerseits dazu, das immer die Klasse org.compiere.report.ReportStarter.java automatisch ausgeführt wird. Es muss also keine Prozess-Klasse mehr angegeben werden. Andererseits wird hierdurch der Bericht in einigen Pull-Down-Menüs überhaupt erst angezeigt, wo man Berichte auswählen kann.

Man kann übrigens auch noch eine Prozess-Klasse angeben, diese wird dann zusätzlich ausgeführt. Das ist eigentlich blöde, wenn man einen Bericht auf eine andere Weise als mit dem ReportStarter starten will, weil man dazu "Bericht" ausschalten muss und dann nicht mehr in den Pull-Down-Menüs steht (Es hilft wahrscheinlich, es erst einzuschalten, dann auszuwählen und dann auszuschalten).

Ein solcher "Bericht & Prozess" kann nun im Fenster "Fenster, Register & Feld" in einem Register als "Vorgang" angegeben werden. Ist das geschehen, ist im entsprechenden Fenster der "Print Preview" und der "Print" Button aktiviert und der Report wird gestartet.

Nach der Philosophie von ADempiere ist der Report, den man mit dem Print-Button aufruft übrigens immer einer, der einen einzelnen Datensatz ausgibt. Will man eine Liste ausgeben, nimmt man dazu den Bericht-Button (links daneben). Dieser startet normalerweise den ADempiere-internen Berichtsgenerator. Der ist auch ganz nett, was hier aber nicht das Thema ist. Man kann dort daher mit dem Werkzeug-Icon den Bericht konfigurieren und im sich öffnenden Fenster "Druckformat" einen "Jasperprozess" auswählen (also einen Prozess, bei dem "Bericht" eingeschaltet ist). Ab diesem Moment wird man nicht mehr vom internen Berichtsgenerator behelligt.

Wer das später anpassen oder vorher direkt konfigurieren will, kann das im Fenster "Druck Format" machen. Die aus dem Programm heraus konfigurierten Einträge werden nicht global gespeichert und können auch nur von einem Mandanten aus wieder geändert werden. Ich denke jedoch, das man auch im System-Mandanten globale Druck-Formate einstellen kann. Ich habe noch nicht raus, wie ein bestimmtes Register-Tab nun mit einem bestimmten Druckformat verbunden ist. Vielleicht geht das nur über die gemeinsame Tabelle.

Wer will, das ein Report möglichst dem Inhalt des Fensters ähnlich sieht, sollte sich JasperReports with Window Sql-Clauses ansehen.

Subreports

Ein Sureport ist ein "Report im Report", also eine Art Unterliste. Wenn ich z.B. einen Report erzeuge, der meine Rechnungen ausgibt (und dazu über Datensätze aus der Tabelle C_Order iteriert), braucht man einen solchen Subreport, um die Liste der Artikelzeilen anzuzeigen.


Subreport ADempiere-freundlich einrichten

Siehe hierzu zuerst einmal die entsprechende Dokumentation von iReport. Die Subreport Expression ist:

 $P{SUBREPORT_DIR} + "Kundendaten_subreport1.jasper"

und hat die Class java.lang.String. Dieser Ausdruck sorgt dafür, das der Subreport im gleichen Verzeichnis gesucht wird wie der Hauptreport - unabhängig davon, ob es sich um einen Pfad bei dem einen oder dem anderen Entwickler, aus iReport oder innerhalb ADempiere oder sogar aus einem Java JAR-File heraus handelt.

Die Connection Expression ist $P{REPORT_CONNECTION} (also die gleiche Datenbank-Verbindung benutzen wie der Hauptreport).

Dann kann man Parameters definieren. Das ist eigentlich das wichtigste am Subreport, weil hier die Verbindung zum Mutter-Report hergestellt wird. Wenn wir hier einen Parameter RECORD_ID einrichten und dort unser Schlüsselfeld eintragen (z.B. C_BPartner_ID), sorgen wir dafür, das sich das für den Subreport genauso anfühlt, als sei er ganz alleine für sich aus ADempiere heraus aufgerufen worden. Der Subreport kann also auch alleine aus ADempiere heraus aufgerufen werden. Wird diese Konvention überall beachtet, erhält man so eine Sammlung von Modulen, die alle austauschbar und ineinander schachtelbar sind.

Über das Kontext-Menü des Subreport-Elements kann man nun den Subreport öffnen und selber definieren. Hier macht es u.U. Sinn, alle möglichen Footer- und Header-Bereiche auszublenden. Anosnsten erzeugt man auch hier zuerst einen Parameter mit dem Namen "RECORD_ID" und verwendet diesen dann in einem Query in der Form $P{RECORD_ID}. Also alles wie beim Mutter-Report.

weitere Probleme beim Auffinden von Subreports

Leider zeigten meine Tests, das das Auffinden von Subreports mit Hilfe des Parameters SUBREPORT_DIR nicht so gut funktioniert, wie man sich das denkt. Ein Blick in den Quelltext ergab, das man in JasperReports/src/org/compiere/report/ReportStarter.java suchen muss, wenn man Antworten haben will. Ich konnte dort nicht finden, wo (und ob) SUBREPORT_DIR gesetzt wurde.

Ich habe den Kern des Problems erst nach einigen Versuchen und dem Quellcode-Studium verstanden und versuche mal, es hier zu beschreiben: Wenn ein Report einen Subreport aufrufet, enthält dieser einen Ausdruck, der den Subreport beschreibt. Das ist im Normalfall ein String mit einem Dateinamen. Nun liegen bei einer ADempiere-Installation, die per Webstart gestartet wird, die *.jrxml- und die *.jasper-Dateien ja nicht einfach in der Gegend herum, sondern sind normalerweise im Classpath (oder vielleicht auf einem Server per HTTP). Außerdem weiss man ja auch nie, ob eine *.jrxml oder eine *.jasper vorliegt, ob also noch etwas compiliert werden muss. Also müssen diese Dateien zuerst heruntergeladen und aufbereitet werden. Dann liegen sie in einem definierten Verzeichnis (und zwar /tmp) und können ausgeführt werden. Dabei gibt es nun zwei Probleme:

1. ADempiere muss wissen, welche Dateien es herunterladen und aufbereiten muss 2. Reporte müssen etwas über die Umgebung, in der sie laufen, wissen (den Pfad der Subreporte)

Der erste Punkt hört sich erstmal trivial an, ist er aber nicht (und ist damit der Kern des Problems). Sowohl im Web als auch im Classpath gibt es keine Möglichkeit, ein Verzeichnis aller Dateien zu bekommen (Man könnte die Information über die Subreports aus dem Hauptreport extrahieren, hat man aber nicht). Also gibt es folgende Konvention (im Quellcode ab Zeile 474, Code von trifon): Zu einem im Paket "de.bayen.freibier.reports" befindlichen Report "MeinBericht.jrxml" sucht ADempiere eigenständig die Datei "MeinBerichtSubreport1.jrxml" und wiederholt das mit den Zahlen 1-9. Diese Datei wird dann ggf. übersetzt und auch in das Zielverzeichnis (also /tmp) kopiert. Dann wird der Dateipfad in den Parameter "de_bayen_freibier_reporte_MeinBerichtSubreport1" geschrieben.

Was heisst das nun?

Wir nennen unseren Subreport zu "MeinBericht" schon in iReport "MeinBerichtSubreport1". Dann erzeugen wir im Hauptreport einen Parameter mit dem Namen "de_bayen_freibier_reporte_MeinBerichtSubreport1" und dem Default-Wert "./MeinBerichtSubreport1.jasper". Das läuft dann sowohl in iReport als auch in ADempiere, wenn man das in das angegebene Paket installiert.

Was es dazu noch zu sagen gibt:

Übrigens habe ich gesehen, das auch eine System-Property "org.compiere.report.path" gesetzt wird. Diese enthält dann "/tmp". Aber wie gesagt - da ist eh nur das drin, was ADempiere vorher bereits identifizieren konnte. Also fällt das z.B. für eingebettete Grafiken etc. aus.

Alles in allem ist die Methode, wie hier Dinge nachgeladen werden, nicht so toll. Eigentlich kann man in JasperReports hierfür Handler definieren. Dann könnte man z.B. auch eingebettete Grafiken etc. aus dem Classpath laden. Hier gibt es noch Raum für Verbesserungen.

Zur Benutzung des /tmp-Verzeichnisses siehe auch ADempiere on a terminalserver.

Reporte komplett modularisieren

Wenn man Reporte komplett modularisieren will, bedeutet das, das der Report, den man offiziell aufruft, eigentlich keinen einzigen Tintenklecks Inhalt enthält, sondern lediglich Subreports aufruft, die dann z.B. den Header, den Footer und verschiedene, kombinierbare Inhalte enthält. Dabei gibt es verschiedene Dinge zu beachten:

  • Wie rufe ich die Unterreporte richtig auf? (siehe oben zu Suchpfaden für externe Dateien)
  • Wie übergebe ich Parameter an den Subreport?
  • Wie definiere ich Queries in Subreporten (genauso wie immer, aber halt im Subreport und nicht im Hauptreport)
  • Wie sorge ich für ein anständiges Seitenlayout? (Siehe unten)

Um ein ordentliches Seitenlayout hinzubekommen, bietet es sich an, z.B. den Header als einen eigenen Subreport zu implementieren. Hier kann man dann einen eigenen Query benutzen, um den Namen der Firma, den Benutzer, ein Logo oder sonstige Informationen zu erhalten. Der Vorteil ist hier vor allem, das man - auch wenn man sehr viele Reporte erstellt - diesen Seitenkopf recht schnell an einer einzigen Stelle anpassen kann.

Wenn man nun aber seine eigentlichen Reporte auch modularisiert, kann man diese später mit mehreren zusammensetzen. Dazu ruft man den Report, der die eigentliche Logik (den Query etc.) enthält, als Subreport auf. Dieser Subreport enthält dann keine Seitenheader und -footer. Er hat eine Breite, die bereits direkt ohne Rand gerechnet ist (der Rand ist im Haupt-Report definiert). Ein Trick hierbei liegt in der Frage, wo dieser Subreport nun hingehört. Man kann ihn nicht in die Bänder "Title" oder "Summary" setzen. Wird der Subreport länger als eine Seite, wird dann nämlich kein Seitenheader und -footer erzeugt. Also muss er zwischen Header und Footer. Als "Columnheader" kann man ihn auch nicht nehmen. Dieser darf (wie der Seitenheader) nicht länger als eine Druckseite sein (logisch, weil die neue Seite ja einen neuen Header auslösen würde). Also muss er in das "Detail"-Band. Dieses wird allerdings nur ausgegeben, wenn es Daten gibt (und zwar für jede Zeile einmal). Also muss ich dafür sorgen, das der Hauptreport genau eine Zeile Daten enthält. Dazu stelle ich den Query dort auf "SELECT 1;". Dann geht's! :-)

Etwas schwieriger ist es wohl, Informationen über die aktuelle Seitenzahl oder gar ein "Seite x von y" an einen Header-Subreport zu übergeben. Ich habe es noch nicht probiert, könnte mir aber vorstellen, das man mit der Übergabe entsprechender Parameter eine Seitenzahl schaffen könnte (glaube aber nicht, das das mit der Gesamtzahl auch geht).

Links mit Beispielen von Subreports und Datasets

Vorbereiten von *.jasper-Dateien mit unterschiedlichen JasperReports-Versionen

Das Erzeugen von Subreports in Jasperreports ist nicht ganz trivial, aber vielfach erklärt. Hierzu benutzt man iReport. Leider gibt es bei der Zusammenarbeit mit ADempiere noch einige weitere Hürden. Einen normalen Report kann man als *.jrxml-Datei im Filesystem oder im Classpath ablegen. Diese Datei wird dann von ADempiere automatisch in eine *.jasper-Datei kompiliert. Diese stellt dann den eigentlichen Report dar, der sodann Daten aus der Datenbank zieht und ein Ausgabedokument (zumeist ein PDF) erzeugt. Subreports werden nun allerdings durch einen String deklariert, der den Dateinamen des Subreports enthält. An dieser Stelle erwartet JasperReports (zumindest nach meiner Erfahrung) auf jeden Fall den Namen einer *.jasper-Datei. Also muss man diese vorübersetzen.

An sich ist das nicht schlimm, weil iReport das sowieso für uns macht. Dabei trat bei mir allerdings ein Problem auf: ADempiere enthält jasperreports-3.7.3.jar, während mein aktuell heruntergeladenes iReport bereits die Version 4.5 hat. Dadurch ergab sich nun leider nach der Installation der Datei in ADempiere eine Exception, weil irgendwelche Abhängigkeiten nicht aufgelöst werden konnten.

Um das zu beheben muss man also sicherstellen, das der in iReport erzeugte Report erstens grundsätzlich von den verwendeten Funktionen ger kompatibel mit der Version 3.7.3 ist und zweitens die *.jasper-Datei mit einer 3.7.3-VErsion übersetzen. Ersteres geht, indem wir in iReport in den Einstellungen (Menü Extras->Optionen) im Register "General" im Unterregister "Compatibility" einstellen, das wir *.jrxml-Dateien der Version 3.7.3 erstellen wollen. Meine Report-Dateien speichere ich dann in das Eclipse-Projekt meiner Anpassungen. Dort habe ich dann ein Ant-buildskript erstellt. Dieses kompiliert die XML-Datei in eine *.jasper-Datei und benutzt dazu die Version der Bibliothek, die sich im benachbarten Projekt der ADempiere-Basis befindet.

Hier meine build.xml:

<project default="jasperreports">
	
	<path id="classpath">
		<!--
		<fileset dir="../adempiere361/">
			<include name="**/lib/*.jar"/>
		</fileset>
		-->
		<fileset dir="../adempiere361/JasperReportsTools/lib">
			<include name="**/*.jar"/>
		</fileset>
		<fileset dir="../adempiere361/tools/lib">
			<include name="**/*.jar"/>
		</fileset>
	</path>
		
	<taskdef name="jrc" classname="net.sf.jasperreports.ant.JRAntCompileTask">
		<classpath refid="classpath"/>
	</taskdef>
	
	<target name="jasperreports">
		<jrc srcdir="src">
			<classpath refid="classpath"/>
			<include name="**/*.jrxml"/>
		</jrc>
	</target>
</project>


Images in Reporten

Es gibt relativ wenige Erklärungen im Netz zum Thema, wie man ein Bild in einen Report integriert. All diese Anleitungen erklären zuerst einmal, wie man statische Bilder in einen Report einfügt, um z.B. das Logo der eigenen Firma oben an den Rand jeder Seite oder auf ein Deckblatt zu drucken. Das ist aber natürlich nur der halbe Spaß! Ich möchte gerne Bilder aus der Datenbank in meinen Report übernehmen. Also z.B. ein Kunden-Stammblatt, auf dem das Logo des Kunden steht oder eine Preisliste mit Abbildungen meiner Produkte.

Hierzu benötigt man zuerst einmal das Image als Daten. In Adempiere (und überhaupt) gibt es verschiedene Systeme, wie Images abgespeichert werden. Die Logos der Geschäftspartner werden dabei standardmäßig im (meiner Meinung nach) sinnvollsten Format abgespeichert: Als Strom von Binärdaten, die in der Datenbank in einer eigenen Tabelle gespeichert werden. Warum ist das sinnvoll: Bilder sind genauso wie alle anderen Daten Teil der Summe aller Geschäftsdaten und gehören damit in die Datenbank (und nicht ins Filesystem, ins Internet oder sonstwohin). Speichert man sie in den normalen Datensatz (z.B. die Tabelle C_BPartner), so wird dieser allerdings extrem aufgebläht und somit verlangsamt sich der Zugriff auf Geschäftspartner sehr stark. Daher werden solche Bilder in ADempiere in die Tabelle AD_Image gespeichert. Wer möchte, kann diese Tabelle dann sogar auf eine eigene Festplatte auslagern, um so weder Performance noch Speicherplatz der eigentlichen Datenbank zu stören.

Also: Nun mus man also diese Daten, die mit Absicht aus der Hautp-Tabelle herausgehalten werden, mit einlesen. Das geht wie üblich mit so etwas wie:

  SELECT *
  FROM C_BPartner
  LEFT JOIN AD_Image ON(C_BPartner.logo_ID = AD_Image.AD_Image_ID)
WHERE
  C_BPartner.C_BPartner_ID=$P{RECORD_ID}

Ein derartiger Query-String wird in iReport als Haupt-Query eingegeben. Dann kann man ein ganz normales Image-Element aus dem Panel nehmen und in seinen Report ziehen. In den Eigenschaften dieses Images kann man nun die "Expression Class" einstellen. Falls man doch seine Bilder im Filesystem gespeichert hat (und eine URL in der Datenbank), kann man hier String angeben. Hat man die Bilder als Binärdaten z.B. aus AD_Image geholt, nimmt man am besten java.io.InputStream. Als Image Expression nimmt man in ersterem Fall einfach die entsprechende Feldvariable. Für Binärdaten nimmt man den folgenden String:

 new java.io.ByteArrayInputStream((byte[])$F{binarydata})

Dieser Ausdruck ist im Grunde das ganze Geheimnis. Damit läuft es dann auch schon - Viel Spaß! :-)


Parameter in Reporten

ADempiere übergibt verschiedene Parameter an JasperReports. Diese muss man nicht benutzen, kann es aber, indem man sie in iReport als Parameter definiert. Dabei muss man einfach einen Parameter des angegebenen Namens (Gross- und Kleinschreibung beachten) anlegen. Um seine Reporte auch in iReport sinnvoll testen zu können, sollte man dann noch einen intelligenten Default-Wert angeben, der einem beim Test halbwegs sinnvolle Daten in die Ausgabe zaubert. Natürlcih kann es von Fall zu Fall auch sinnvoll sein, einen Default-Wert im SQL-Code, der diese Parameter benutzt, z.B. mit COALESCE(...) zu erzeugen.

Ich konnte keine Dokumentation zu den Parametern finden und habe dann JasperReports/src/org/compiere/report/ReportStarter.java als authoritative Quelle benutzt.

  • AD_CLIENT_ID - Erlaubt Zugriff auf den Mandanten, der momentan aktiv ist. Leider gibt es kein direktes Feld für die Organisations-ID, beide kann man allerdings auch über AD_PINSTANCE_ID extrahieren.
  • AD_ROLE_ID - Die Rolle, als der der Benutzer gerade eingeloggt ist. Rollen bieten Zugriff auf Rechte und erlauben es daher, in einem Report gewisse Abstufungen zu machen, auf welche Tabellen man zugreift oder nicht.
  • AD_USER_ID - Die Benutzer-ID erlaubt z.B. Zugriff auf den Benutzernamen.
  • AD_PINSTANCE_ID - Erlaubt Zugriff auf eine Tabelle für Prozess-Instanzen. Dort gibt es z.B. Zugriff auf die Organisation.
  • RECORD_ID - Wird nur ausgefüllt, wenn momentan ein Record ausgewählt ist. Das ist z.B. der Fall, wenn der Report aus einem normalen Datensatz-Fenster heraus mit einem Druck auf den "Drucken"-Button aufgerufen wird.
  • CURRENT_LANG - enthält den String der eingestellten Sprache (z.B. "en-US")
  • REPORT_LOCALE - enthält das java.util.Locale-Objekt zur eingestellten Sprache
  • RESOURCE - enthält ein Objekt vom Typ java.util.PropertyResourceBundle, aber nur, wenn eine entsprechende Properties-Datei der Art MeinReportName_de.properties vorhanden ist. Man kann hiermit Mehrsprachigkeit eines Reports verwirklichen, indem man statische Texte in entsprechende Properties-Dateien auslagert und dann über dieses Objekt anspricht. (Habe ich selber noch nicht ausprobiert)
  • Subreports - Für jeden erkannten Subreport wird ein Parameter mit dem entsprechenden Pfad angelegt (siehe oben).
  • Prozess-Parameter - Sind im ADempiere Application Dictionaty Parameter definiert, so werden diese selbstverständlich auch an den Report übergeben. Diese werden für gewöhnlich vom Benutzer eingegeben, nachdem er einen Report aus dem Menü heraus gestartet hat. Handelt es sich um From-To-Parameter, so werden z.B. aus "zeitraum" die Parameter "zeitraum1" und "zeitraum2" erzeugt. (Im Quelltext werden übrigens ProcessParameters und ProcessInfoParameters eingefügt, deren Unterschied mir bisher nicht ganz klar ist...)

Wer den patch von JasperReports with Window Sql-Clauses schon integriert hat, bekommt auch noch:

  • CONTEXT - This parameter keeps the ADempiere Context. This has nothing to do with the issue talked about at this page but you could use that if you want.
  • REPORT_WHERE - This parameter contains the whole WHERE clause that gives you the records that are actual shown in your tab. It does not contain the word "WHERE" so you can use it in a wider term and combine it with AND to filter it even more if you want. If there is not defined any search this parameter contains "TRUE" so you can safely insert it in your WHERE clause at any point.
  • REPORT_ORDERBY - This parameter contains the ORDER clause that is defined in the Application Directory's Tab definition. You can use it in your Query and combine inside a wider term too. If there is defined no order you get the term "0-1". In PostgreSQL (and I believe in Oracle too) you can use this inside a ORDER BY clause and it does nothing. So it does not harm if you use this variable to construct your query.
  • REPORT_SORT - If you changed the sorting inside the Tab by clicking on a column header you get a sort description here. It is similat to the REPORT_ORDERBY value. If you did not sort your table it contains also the value "0+1" so you can safely insert it into your query string.

iReport-Tips

ADempiere hat Probleme mit Groovy als Skriptsprache (aufgrund von Versionsdifferenzen). Einerseits erleichtert Groovy das Leben ganz allgemein und andererseits laufen Reporte in ADempiere eher, wenn man Java verwendet. Um die Skriptsprache umzustellen kann man in iReports in "Extras/Optionen" auf der Registerkarte "General", Unterkarte "Report Defaults" die Language umstellen. Diese Einstellung gilt dann für alle neuen Reporte. Die eigentliche Einstellung nimmt man im Objektbaum des Reports auf der allerobersten Ebene vor. Dort gibt es eine Einstellung "Language". Eigentlich hätte ich erwartet, das man diese Einstellung auch noch je Ausgabefeld einstellen kann, was aber scheinbar nicht geht.


Fonts

Das Benutzen von eigenen Zeichensätzen wird in den Dokus immer so beschrieben, als ob es mit besonderen Fallstricken versehen wäre. Daher dokumentiere ich hier vorsichtshalber, was mir zu dem Thema so begegnet:

Welcher Font in einem Report wirklich benutzt wird, ist immer eine lange Geschichte. Eigentlich ist nur der Dejavu-Font in iReport selber enthalten. Dazu gibt es noch einige Fontbezeichnungen, die je nach Umgebung auf einen möglichst passenden Systemfont abgebildet werden. Diese sind "Monospaced", "Serif" und "SansSerif". Wenn ich versuche, einen Font einzustellen (z.B. bei den Eigenschaften eines Textfeldes), so stehen diese "ordentlich konfigurierten" Fonts ganz oben (über dem Strich). Darunter stehen dann noch die im aktuellen Betriebssystem installierten Fonts. Diese sollte man normalerweise nicht benutzen. Sie haben erstens den Nachteil (je nach Anwendungsfall), das sie nicht plattformübergreifend zur VErfügung stehen und zweitens, das sie nicht in PDF-Dokumenten verwendet werden.

Der moderne Weg, um Fonts in einem Report zu benutzen, ist, diese in eine sogenannte Font Extension zu packen. Das ist ein JAR-File, das den Font beschreibt. Dieses JAR-File kommt dann in den Classpath z.B. von iReport (geschieht ganz automatisch, wenn man einen Font richtig installiert). Man kann Fonts ausgehend von ganz normalen TTF-Dateien in den Einstellungen von iReport hinzufügen. Dort kann man dann aus einem solchen hinzugefügten Font auch eine Font Extension (sprich: ein JAR-File) machen. Dieses kann man dann ganz normal seiner Applikation beifügen. Für ADempiere/FreiBier heisst das z.B., das ich diese Datei nach packages/freibier/lib kopiere (es gibt verschiedene Wege, JARs zu ADempiere hinzuzufügen, Packages sind einer davon).

Früher konnte man wohl für die PDF-Ausgabe einen eigenen Font angeben, das scheint aber nicht mehr empfohlen zu werden. In den alten Zeiten war es wohl nicht so einfach (oder nicht möglich) einen eigenen Font in ein PDF zu integrieren. Außerdem finde ich es extrem blöde, wenn der normale, im Viewer angezeigte Report anders aussieht als dein PDF-Report. Zu überlegen ist allerdings, ob man den Font in das PDF-Dokument einbettet oder nicht. Ersteres garantiert immer eine ordentliche Anzeige auf allen Zielrechnern (und ist eigentlich der übliche Weg), zweiteres sorgt für kleinere PDF-Dateien und ist vielleicht platzsparender, wenn man im Unternehmenseinsatz sehr viele Dokumente erzeugt und diese dann auch archivieren will (in einem kleinen Test mit einem einseitigen Dokuemnt hatte ich 2,5KB für das blanke PDF und 7,1KB für das mit integrierem Font, allerdings wurden wohl nur die verwendeten ca. 10 Buchstaben eingebettet, der praktische Unterschied könnte also durchaus im Bereich von einigen -zig KB liegen).

Styles

Seit einiger Zeit gibt es in iReport sogenannte Styles. Damit kann man einen Schriftstil (inklusive Font) einmalig vorgeben und braucht ihn dann nicht für jedes Feld neu festzulegen. Vor allem kann man aber den Stil auch an einer Stelle global ändern.

Fragen, die bleiben:

  • Die speziellen Angaben für PDF-Fonts sind in meinem iReport vorhanden, aber durchgestrichen - was bedeutet das?
  • Ich sollte mal versuchen, einen ganz fremden TTF-Font zu integrieren und das dann dokumentieren, um das obige alles beweisen zu können

Auf der Seite Using Jasper Report Form in place of Standard Forms ist (ziemlich unten) auch ein ausführlicher Teil zur Arbeit mit einem eigenen Font.

verwandte Links