|
<< Klicken Sie, um das Inhaltsverzeichnis anzuzeigen >> Navigation: Scripts > Berichte Scripts |
Der Berichtegenerator von EMIL erzeugt Berichte aus Daten der Datenbank. Anders als ein Formular-Script wird ein Berichtscript in drei Phasen aufgerufen. Dieses Kapitel beschreibt den Aufbau eines Berichtscripts, das Einbetten von SQL-Abfragen und die Befehlsreferenz. Vorausgesetzt werden die Scripting-Grundlagen.
Ein Berichtscript besteht aus einem äußeren begin/end-Block, der drei vom Berichtegenerator gerufene Prozeduren bzw. Funktionen definiert. Der eigentliche Aufruf der drei Routinen erfolgt durch EMIL:
1.DefineParameters - wird einmal vor der Anzeige der Parametereingabe-Maske gerufen. In dieser Phase registriert das Script über AddReportParam, welche Parameter der Benutzer eingeben soll.
2.CheckParameters - wird gerufen, sobald der Benutzer die Parameter ausgefüllt und auf Bericht erstellen geklickt hat. Liefert die Funktion einen leeren String zurück, gilt die Eingabe als gültig. Andernfalls wird der zurückgegebene Text als Warnmeldung angezeigt und der Benutzer muss die Parameter korrigieren.
3.CreateContent - wird gerufen, wenn alle Parameter gültig sind, und erzeugt den eigentlichen Bericht. Der erste Parameter ist der Titel des Berichts (kann ignoriert werden).
Skelett eines Berichtscripts:
begin
procedure DefineParameters;
begin
AddReportParam('Von Datum', 'vondat', '01.01.' + IntToStr(YearOf(now)), 'D', nil);
AddReportParam('Bis Datum', 'bisdat', 'heute', 'D', nil);
end;
function CheckParameters: String;
begin
if lowestDateFrom(GetParam('vondat')) > highestDateFrom(GetParam('bisdat')) then
result := 'Das Von-Datum liegt nach dem Bis-Datum.'
else
result := '';
end;
procedure CreateContent(const aTitle: String);
var
ds: TDataset;
begin
initReport('Aufnahmen', 'Stand: ' + DateToStr(now), '', false);
ds := getDataset(getSQLVar('sql1'),
[lowestDateFrom(GetParam('vondat')), highestDateFrom(GetParam('bisdat'))]);
while not ds.eof do
begin
PrintContent(ds['name'], DateToStr(ds['visit_date']), true);
ds.next;
end;
end;
{ sql1 = select s.name, v.visit_date
from subject s
join visit v on v.subject_id = s.id
where v.visit_date between :p1 and :p2
order by v.visit_date }
end.
SQL-Statements können direkt als Stringliteral an getDataset übergeben werden. Längere Abfragen schreiben Sie besser als benannten Kommentar in der Form { name = SQL-Statement } in den Scriptcode, wie im Skelett oben. EMIL liest diese Kommentare vor der Ausführung aus; getSQLVar(name) liefert dann das Statement. So bleibt der Code lesbar und mehrzeiliges SQL lässt sich frei formatieren.
Die SQL-Statements verwenden positionell benannte Parameter :p1, :p2, ... (oder beliebige andere Namen). Im Aufruf von getDataset wird das Array aParams in der gleichen Reihenfolge übergeben:
ds := getDataset(getSQLVar('sql1'), [aVonDatum, aBisDatum]);
Den Profilfilter hängen Sie per String-Verkettung an. ProfileSQL liefert eine geklammerte Bedingung ohne führendes and; im benannten Kommentar endet das SQL deshalb mit where oder and:
{ sql2 = select s.name from subject s where }
ds := getDataset(getSQLVar('sql2') + ProfileSQL('s'), []);
Listenparameter (Typ L oder M) erwarten eine TStringList mit Einträgen der Form Anzeige+#255+Code, die Sie manuell aufbauen oder per GetCodeList aus einer SQL-Abfrage füllen:
procedure DefineParameters;
begin
AddReportParam('Indikation', 'indi', '', 'L',
GetCodeList('select name, id from indication order by name'));
end;
GetParam liefert den ausgewählten Code, GetParamDecode den Anzeigetext, z.B. für den Berichtkopf.
Zusätzlich zu den Standardfunktionen stehen in Berichtscripts diese Funktionen zur Verfügung. Format und roundTo sind bei den Standardfunktionen beschrieben.
procedure initReport(const aHeader, aFooter, aSubHeader: String; landscape: Boolean);
Initialisiert die Berichteengine für einen neuen Bericht mit Kopf-/Fußzeile und optionaler Unterzeile. Mit landscape = true wird Querformat verwendet.
initReport('Patientenliste', 'Stand: ' + DateToStr(now), '', false);
procedure setTabs(const aTabs: array of byte);
Setzt Tabulatorpositionen (in Prozent der Seitenbreite) für tabellarische Berichte.
setTabs([20, 40, 60, 80]);
procedure printTabHeader(const Content: String);
Erzeugt eine Titelzeile im Seitenkopf für tabellarische Berichte. Die Spalten werden durch Tab-Zeichen (#9) getrennt und an den mit setTabs definierten Positionen ausgegeben.
printTabHeader('Name'#9'Geb.-Datum'#9'Diagnose'#9'Datum');
procedure PrintSubHeader(const Content: String; centered: Boolean);
Erzeugt eine Zwischenüberschrift zur Abtrennung von Unterbereichen im Bericht; mit centered = true wird zentriert ausgegeben.
PrintSubHeader('Abteilung Rheumatologie', false);
procedure PrintContent(const aLabel, aContent: String; AltColors: Boolean);
Gibt eine Druckzeile mit fettem Label und Inhalt aus. Mit AltColors = true wird ein gestreiftes Zebra-Layout zur besseren Lesbarkeit erzeugt.
PrintContent('Gewicht (kg)', Format(gewicht, 1), true);
procedure AddToWorkList(const aText: String; aId: Int64);
Fügt einen Eintrag zur Arbeitsliste hinzu. Die Liste wird mit jedem Berichtsaufruf geleert und neu befüllt, damit Patienten im Bericht durch Anklicken direkt aus der Liste geöffnet werden können.
AddToWorkList(ds['name'], ds['subject_id']);
procedure AddReportParam(const aTitle, aVarname, aPreset: String; aType: Char; aList: TStringList);
Fügt einen Berichtsparameter hinzu, der im Berichtcode bei der Ausführung über GetParam mit dem in aVarname angegebenen Namen abgefragt werden kann. aTitle ist die Beschriftung, unter der der Parameter vom Benutzer abgefragt wird, aPreset ein Vorschlagswert. aType legt den Parametertyp fest:
•'S' - String (Freitext)
•'F' - Fließkommawert
•'I' - Integer (Ganzzahl)
•'D' - Datum (unscharf, siehe lowestDateFrom/highestDateFrom)
•'L' - Einfachauswahl-Liste
•'M' - Mehrfachauswahl-Liste
Bei Verwendung von Listen ist aList als TStringList mit Einträgen der Form Anzeige+#255+Code anzugeben oder kann per SQL mit GetCodeList bzw. GetTypeCodeList gefüllt werden. Bei anderen Typen ist nil zu übergeben.
AddReportParam('Von Datum', 'vondat', '01.01.'+IntToStr(YearOf(now)), 'D', nil);
AddReportParam('Geschlecht', 'gen', '', 'L', GetTypeCodeList(10001));
procedure SetNewList(const aParam: String; aList: TStringList);
Tauscht die aktuelle Liste eines bereits registrierten Parameters aus. Hilfreich, wenn sich Listen gegenseitig bedingen - z.B. wenn die Auswahl einer Abteilung die Liste der Ärzte einschränkt.
SetNewList('arzt', GetCodeList(...));
function GetParam(const aName: String): String;
Liefert bei der Berichtausführung den aktuellen Wert eines Parameters (bei Listenparametern: den Code, nicht die Anzeige). Bei Mehrfachauswahl werden die Codes durch Komma getrennt zurückgegeben.
aVonDatum := lowestDateFrom(GetParam('vondat'));
aGenderCode := GetParam('gen');
function GetParamDecode(const aName: String): String;
Ermittelt zu einem Listenparameter den sprechenden Namen aus der Anzeigespalte - z.B. für den Berichtkopf.
initReport('Patienten Geschlecht: ' + GetParamDecode('gen'), '', '', false);
function lowestDateFrom(const s: String): TDateTime;
Ermittelt das kleinste Datum aus einer unscharfen Angabe - z.B. '2000' → 1.1.2000 oder '5.2010' → 1.5.2010. Damit lassen sich vom Benutzer eingegebene Zeiträume in SQL-Parameter umwandeln. Zu unscharfen Datumsangaben siehe Fuzzy Dates.
aVon := lowestDateFrom(GetParam('vondat'));
function highestDateFrom(const s: String): TDateTime;
Pendant zu lowestDateFrom: ermittelt das größte Datum aus einer unscharfen Angabe - '2000' → 31.12.2000, '5.2010' → 31.5.2010.
aBis := highestDateFrom(GetParam('bisdat'));
function getDataset(const aSQL: String; aParams: array of variant): TDataset;
Erzeugt und öffnet eine Datenmenge aus dem angegebenen SQL-Statement. Parameter im SQL (z.B. :p1, :p2) werden aus aParams in der angegebenen Reihenfolge gefüllt.
ds := getDataset('select * from subject where created_at > :p1', [aVon]);
while not ds.eof do begin ... ds.next; end;
function getSQLVar(const aName: String): String;
Liefert das SQL-Statement, das im Scriptcode als benannter Kommentar { name = SQL-Statement } hinterlegt ist (siehe Einbetten von SQL-Abfragen).
ds := getDataset(getSQLVar('sql1'), [aVon, aBis]);
function GetCodeList(const aSQL: String): TStringList;
Führt ein SQL-Statement aus, das genau zwei Spalten liefert - erste Spalte Anzeigetext, zweite Spalte Code - und gibt das Ergebnis als TStringList im Format Anzeige+#255+Code zurück, geeignet für AddReportParam mit Listentyp. Die Verkettung mit chr(255) übernimmt die Funktion selbst, sie gehört nicht ins SQL.
AddReportParam('Abteilung', 'abt', '', 'L',
GetCodeList('select name, id from department order by name'));
Profilfunktionen im Überblick
Funktion |
Filtert / liefert |
Verwendung |
ProfileSQL(alias) |
Akten (subject.profile_codes) |
Bedingung für die Tabelle subject: blendet Akten anderer Indikationen aus. |
ActiveProfileSQL(alias) |
Dictionary (type.active_profile_codes) |
Bedingung für die Tabelle type: nur im Benutzerprofil freigeschaltete Einträge. |
CurrentProfile |
Profilcode des Benutzers (ein Zeichen) |
Parameter für type_profile/type_group_profile oder VTYPE. |
ActiveProfiles |
alle freigeschalteten Profilcodes |
Parameter für VTYPE, nur bei aktivem Rhadar oder Rabbit nötig. |
Hinweis: Das Feld type.enabled_flag wird nicht mehr gepflegt. Berichte, die noch darauf filtern, blenden neu angelegte Dictionary-Einträge fälschlich aus. Verwenden Sie stattdessen ActiveProfileSQL oder VTYPE mit CurrentProfile/ActiveProfiles.
function ProfileSQL(const aAlias: String): String;
Liefert eine geklammerte Bedingung ohne führendes and, die Akten anderer Indikationen ausblendet (Prüfung über subject.profile_codes). aAlias ist der Tabellenalias der subject-Tabelle im SQL-Statement.
ds := getDataset('select s.* from subject s where ' + ProfileSQL('s'), []);
function ActiveProfileSQL(const aAlias: String): String;
Liefert eine geklammerte Bedingung, die Dictionary-Einträge (Tabelle type) auf die im aktuellen Benutzerprofil freigeschalteten Einträge einschränkt (Prüfung über type.active_profile_codes). aAlias ist der Tabellenalias der type-Tabelle.
sl := GetCodeList('select t.title, t.id from type t ' +
'where ' + ActiveProfileSQL('t') + ' order by 1');
function CurrentProfile: String;
Liefert den Profilcode des angemeldeten Benutzers (ein Zeichen, z.B. r für Erwachsenenrheumatologie), etwa als SQL-Parameter beim Join auf profilabhängige Bezeichnungen in type_profile bzw. type_group_profile.
ds := getDataset('select coalesce(tp.title, t.title) from type t ' +
'left join type_profile tp on tp.type_id=t.id and tp.profile_code=:p1 ' +
'where ' + ActiveProfileSQL('t'), [CurrentProfile]);
function ActiveProfiles: String;
Liefert alle Profilcodes, unter denen für den angemeldeten Benutzer etwas freigeschaltet sein kann: das eigene Profil (an erster Stelle) plus c (kundenspezifisch, immer), z bei aktivem Rhadar und x bei aktivem Rabbit.
CurrentProfile und ActiveProfiles sind als Parameter für die Datenbankprozedur VTYPE gedacht. Sie liefert eine für ein Profil aufbereitete Sicht auf das Dictionary mit ACTIVE_FLAG, profilabhängigen Titeln (PROFILE_TITLE, TITLE_UNIT) und Gruppen (GROUP_TITLE, AREA), standardmäßig nur mit freigeschalteten Einträgen. Bei mehreren Profilcodes bestimmt das erste Zeichen, aus welchem Profil Titel und Gruppen stammen. Hat ein Benutzer wie üblich ein Profil und ist weder Rhadar noch Rabbit aktiv, genügt CurrentProfile, denn c ergänzt VTYPE selbst:
sl := GetCodeList('select title_unit, id from vtype(''' + CurrentProfile +
''') where type_ind in (''slist'',''mlist'') order by 1');
function GetTypeUnitName(id: Int64): String;
Liefert den aktuell eingestellten Einheitenbezeichner für einen Dictionary-Datentyp.
PrintContent('Einheit', GetTypeUnitName(40001), true);
function GetTypeTitle(id: Int64): String;
Liefert den aktuell eingestellten Titel eines Dictionary-Datentyps.
PrintContent(GetTypeTitle(40001), Format(value, 2), true);
function GetTypeTitleWithUnit(id: Int64): String;
Wie GetTypeTitle, aber mit angehängter Einheit - z.B. 'Gewicht (kg)'.
PrintContent(GetTypeTitleWithUnit(40001), Format(value, 2), true);
function GetTypeCodeList(id: Int64): TStringList;
Ermittelt die Code-Liste für einen Dictionary-Datentyp (z.B. ein Drop-Down-Feld mit fester Werteliste). Direkt für AddReportParam als Listenquelle verwendbar.
AddReportParam('Diagnose', 'diag', '', 'L', GetTypeCodeList(45000));
procedure Debug(content: Variant);
Gibt einen Wert zu Entwicklungs- oder Fehlersuchzwecken in das Konsolenfenster des Berichtsdesigners aus. Im Produktivbetrieb wirkungslos.
debug('sql1 = ' + getSQLVar('sql1'));