Konzept: Parameter-Katalog im Report-Paket — vom Berichtsentwickler zur Variablen-Einrichtung¶
Kontext und Problem¶
JasperReports-Vorlagen tragen ihre Anpassungsparameter bereits in sich: jede
Vorlage deklariert <parameter>-Elemente ($P{…}), über die Fußzeilen, Logos,
Labor-Identität, Textbausteine oder Schaltjahre des Layouts gesteuert werden.
Konfiguriert werden diese Parameter in calServer über Berichtsvariablen
(master_report_variable), die der Admin unter
Administration → Berichtsverwaltung → Berichtsvariablen anlegt.
Der Weg vom Berichtsentwickler zum konfigurierten System ist heute jedoch ein reiner Handarbeits- und Zuruf-Prozess:
- Der Entwickler muss die Parameternamen, ihre Bedeutung, erwartete Formate und
sinnvolle Standardwerte außerhalb des Pakets kommunizieren (README, E-Mail,
Doku). Das Paket-Inspect-Feature (
ReportPackageInspector) zeigt zwar eine gebündelte README an — maschinenlesbar ist davon nichts. - Der Admin tippt im Dialog „Berichtsvariable erstellen" den Variablennamen frei ein. Tippfehler oder falsche Groß-/Kleinschreibung fallen erst auf, wenn der Bericht leer oder falsch rendert.
- Welche Parameter eine Vorlage überhaupt hat, ist im System nirgends sichtbar — weder beim Hochladen noch bei der Variablenpflege.
Ziel dieses Konzepts: Die Parameter samt Beschreibung reisen im Report-ZIP mit. Nach Auswahl des Berichts im Dialog „Berichtsvariable hinzufügen" bietet das System die im Paket deklarierten Parameter mit Beschreibung, Typ und Standardwert zur Auswahl an — statt eines leeren Freitextfelds.
Ist-Zustand (Kurzfassung)¶
| Baustein | Heute |
|---|---|
| Report-Paket | ZIP mit {Pkg}/main_reports/{Pkg}.jrxml + subreports/, Upload über DMS (AdminReportSettingController::upload()), Inspektion über GET …/report-settings/{id}/readme (ReportPackageInspector) |
| Berichtsvariablen | master_report_variable (V1-geteilte Tabelle): variable_name, variable_label, content, Geltungsbereich global (all) / Berichtstyp / einzelner Bericht; Pflege im Tab „Berichtsvariablen" (frontend-v2/pages/admin/reports.vue) |
| Injektion in Jasper | ReportParameterBuilder::build() flacht Variablen zu Jasper-Parametern ab: $P{ucfirst(variable_name)} — Variable company_name füllt Parameter Company_name |
| Laufzeit-Prompt | ReportParameterPromptService parst das Haupt-JRXML und fragt isForPrompting-Parameter beim Generieren ab (Nutzer-Eingabe, kein Admin-Setup) |
| System-Parameter | Reportpath, PrefixTable, Entity-Parameter (CTAG, InventoryId, Auftragsnummer, …) werden automatisch gesetzt und dürfen nie konfigurierbar sein |
Wichtig für alles Folgende: master_report_variable wird mit V1 geteilt —
neue Spalten (z. B. „Beschreibung") sind tabu. Der Parameter-Katalog muss also
aus dem Paket kommen, nicht aus der Datenbank. Das ist kein Nachteil,
sondern der Kern des Konzepts: das Paket ist die einzige Quelle der Wahrheit
über seine eigenen Parameter.
Leitidee: Das selbstbeschreibende Report-Paket¶
Das Report-Paket liefert einen Parameter-Katalog mit: eine maschinenlesbare Beschreibung aller konfigurierbaren Parameter. calServer liest den Katalog direkt aus dem hochgeladenen ZIP (wie heute schon die README) und bietet ihn überall dort an, wo Parameter konfiguriert werden.
Es gibt zwei Quellen, mit klarer Vorrangregel:
- Manifest
parameters.jsonim Paketwurzelverzeichnis (kanonisch, empfohlen für alle neuen Pakete): volle Metadaten inkl. Beschreibung, Eingabetyp, Auswahloptionen, Gruppierung, Mehrsprachigkeit. - JRXML-Fallback (für Bestandspakete ohne Manifest): calServer leitet den
Katalog aus den
<parameter>-Deklarationen des Haupt-JRXML ab —<parameterDescription>und optionalecalserver.*-Properties werden ausgewertet.
Entscheidungsregel: Liegt ein Manifest bei, gilt nur das Manifest (beim Upload gegen das JRXML validiert, Abweichungen als Warnung). Ohne Manifest wird der Katalog aus dem JRXML abgeleitet. Kein Merge zweier Quellen — das hält das Verhalten vorhersagbar und die Implementierung schlank.
Berichtsentwickler (Jaspersoft Studio)
|
| deklariert Parameter im JRXML
| beschreibt sie in parameters.json (oder <parameterDescription>)
v
Report-ZIP ──upload──> calServer DMS
|
| GET /admin/report-settings/{id}/parameters
v
ReportParameterCatalogService (liest ZIP, kein Extract auf Platte)
|
v
Dialog "Berichtsvariable hinzufügen"
→ Parameter-Auswahl mit Beschreibung, Typ, Default
→ Variable wird vorbefüllt angelegt
Das Manifest: parameters.json¶
Liegt im Paket neben main_reports/ (Wurzel des Bundles, gleiche Ebene wie die
README). Beispiel:
{
"$schema": "https://calhelp.github.io/calServer-reports/schema/report-parameters.schema.json",
"version": 1,
"parameters": [
{
"name": "Company_footer",
"label": { "de": "Fußzeile", "en": "Footer" },
"description": {
"de": "Text am unteren Rand jeder Seite, z. B. Firmenname und Akkreditierungshinweis.",
"en": "Text at the bottom of every page, e.g. company name and accreditation notice."
},
"role": "variable",
"scope": "report",
"input": "textarea",
"default": "calHelp – Kalibrierlaboratorium",
"required": false,
"group": "Layout",
"example": "Musterfirma GmbH · D-K-12345-01-00"
},
{
"name": "Show_uncertainty",
"label": { "de": "Messunsicherheit anzeigen" },
"role": "variable",
"scope": "type",
"input": "boolean",
"default": "true"
},
{
"name": "Start_Date",
"label": { "de": "Startdatum" },
"description": { "de": "Beginn des Auswertungszeitraums." },
"role": "prompt",
"input": "date"
}
]
}
Felder¶
| Feld | Pflicht | Bedeutung |
|---|---|---|
name |
ja | Exakter JRXML-Parametername ($P{…}). Muss im Haupt-JRXML deklariert sein (Validierung, s. u.) |
label |
ja | Anzeigename; String oder Objekt je Sprache (de, en, …). Vorbefüllt variable_label |
description |
empfohlen | Erklärtext für den Admin; String oder Sprachobjekt. Wird im Dialog unter dem Feld angezeigt, nicht gespeichert (keine Spalte in der V1-Tabelle nötig) |
role |
nein (Default variable) |
variable = Admin-Konfiguration (Berichtsvariable) · prompt = Nutzerabfrage beim Generieren (Paket-7-Dialog) · system = wird automatisch versorgt, nie anbieten |
scope |
nein (Default report) |
Empfohlener Geltungsbereich: report, type, global. Nur Vorauswahl im Dialog — der Admin bleibt frei |
input |
nein (Default text) |
UI-Eingabetyp: text, textarea, select, boolean, number, date, color, image |
options |
bei select |
Liste erlaubter Werte: ["A4", "Letter"] oder [{"value": "A4", "label": {"de": "DIN A4"}}] |
default |
nein | Standardwert als String (befüllt content vor) |
required |
nein (Default false) |
Kennzeichnung im Dialog; der Bulk-Import (s. u.) legt Pflichtparameter ohne Default nicht stillschweigend leer an |
group / order |
nein | Gruppierung und Reihenfolge in Auswahllisten (z. B. „Layout", „Labor-Identität") |
example |
nein | Beispielwert als Platzhaltertext im Eingabefeld |
Das JSON-Schema wird im Repository
calhelp/calServer-reports
veröffentlicht und versioniert ("version": 1 als Format-Kennung, Erweiterungen
bleiben abwärtskompatibel).
Namenskonvention und die ucfirst-Stolperfalle¶
ReportParameterBuilder injiziert Variablen als $P{ucfirst(variable_name)}.
Damit ein Parameter überhaupt per Berichtsvariable erreichbar ist, muss sein
JRXML-Name mit einem Großbuchstaben beginnen (Company_footer, nicht
company_footer — Letzteres kann von keiner Variable getroffen werden).
Daraus folgen zwei Regeln:
- Ableitung: Der vorgeschlagene
variable_nameistlcfirst(name)— ParameterCompany_footer→ Variablecompany_footer. So stimmt die Auflösung zur Laufzeit garantiert (Round-Trip überucfirst). - Validierung: Parameter mit
role: variable, deren Name mit einem Kleinbuchstaben beginnt, erzeugen beim Upload und im CI-Check eine Warnung („von der Variablenauflösung nicht erreichbar").
Reservierte Namen (data_contract, Reportpath, PrefixTable, die
REPORT_*-Standardparameter sowie die Entity-Parameter des jeweiligen Grids wie
CTAG, MTAG, InventoryId, Auftragsnummer, Auftragsid) werden vom
Katalog immer als system behandelt — egal, was das Manifest behauptet.
JRXML-Fallback (Pakete ohne Manifest)¶
Für Bestandspakete leitet calServer den Katalog aus dem Haupt-JRXML ab. Das nutzt ausschließlich Bordmittel von JasperReports, die in Jaspersoft Studio direkt editierbar sind:
<parameter name="Company_footer" class="java.lang.String">
<property name="calserver.role" value="variable"/>
<property name="calserver.scope" value="report"/>
<property name="calserver.input" value="textarea"/>
<parameterDescription><![CDATA[Text am unteren Rand jeder Seite.]]></parameterDescription>
<defaultValueExpression><![CDATA["calHelp – Kalibrierlaboratorium"]]></defaultValueExpression>
</parameter>
Abgeleitet wird:
| Katalogfeld | Quelle im JRXML |
|---|---|
name |
name-Attribut |
label |
calserver.label-Property, sonst der Parametername |
description |
<parameterDescription> |
Typ/input |
class-Attribut (Mapping wie in ReportParameterPromptService::TYPE_MAP), übersteuerbar per calserver.input |
default |
Literal aus <defaultValueExpression> (nur einfache String-/Zahl-Literale, wie heute im Prompt-Service) |
role |
calserver.role-Property; ohne Property greift die Heuristik: reservierte/Entity-Namen → system, isForPrompting="true" → prompt, alle übrigen → variable |
Die JRXML-Parsing-Logik existiert in ReportParameterPromptService bereits zur
Hälfte (XPath über <parameter>, Typ-Mapping, Default-Extraktion,
Ausschlussliste). Sie wird in einen gemeinsamen Baustein extrahiert
(JrxmlParameterParser in app/Services/Report/), den Katalog- und
Prompt-Service teilen — keine zweite Parser-Implementierung.
Backend-Umsetzung¶
Neuer Service: ReportParameterCatalogService¶
app/Services/Report/ReportParameterCatalogService.php, nach dem Muster von
ReportPackageInspector (liest die DMS-Kopie des ZIPs direkt, kein Entpacken
auf Platte, harte Größenlimits — Manifest max. 256 KB, JRXML wie bisher 4 MB):
parameters.jsonim Bundle suchen (Wurzel, ersatzweisemain_reports/), parsen, gegen das Schema validieren.- Ohne Manifest: Haupt-JRXML (Konvention
main_reports/, wie im Inspector) über den gemeinsamen Parser auswerten. - Reservierte/System-/Entity-Parameter herausfiltern (gemeinsame Konstante mit
ReportParameterPromptService::IGNORED_PARAMETERS+ Entity-Parameternamen je Grid). - Normalisierte Katalogeinträge zurückgeben und mit dem Ist-Zustand der Variablen anreichern.
Ergebnisform je Eintrag:
[
'name' => 'Company_footer', // JRXML-Parametername
'variable_name' => 'company_footer', // abgeleiteter Vorschlag (lcfirst)
'label' => 'Fußzeile', // in Anfragesprache aufgelöst
'description' => 'Text am unteren Rand jeder Seite …',
'role' => 'variable',
'scope_suggestion' => 'report',
'input' => 'textarea',
'options' => null,
'default' => 'calHelp – Kalibrierlaboratorium',
'required' => false,
'group' => 'Layout',
'source' => 'manifest', // 'manifest' | 'jrxml'
'configured' => [ // existierende master_report_variable-Zeilen
'report' => false, 'type' => true, 'global' => false,
],
]
Neuer Endpoint¶
Neben dem bestehenden …/readme im AdminReportSettingController (gleiche
Permission admin_settings-Route-Gruppe, gleiche 404-Semantik ohne Paket).
Antwort: data.parameters (Liste wie oben), data.source
(manifest/jrxml/none) und data.warnings (Validierungsbefunde, s. u.).
Kein neuer Datenbankstand: Der Katalog wird bei Abruf aus dem Paket gelesen
(wie der README-Endpoint heute — die Zugriffsfrequenz ist ein Admin-Dialog,
kein Hot Path). Falls nötig, später Cache::remember je DMS-Dateiversion —
kein Bestandteil der ersten Ausbaustufe.
Validierung beim Upload¶
uploadReportPackage() ruft den Katalog-Service nach erfolgreichem Upload
einmal auf und gibt Warnungen (nicht blockierend!) in der Upload-Antwort
zurück:
- Manifest-Parameter, der im Haupt-JRXML nicht deklariert ist (Tippfehler, Drift nach Umbenennung).
- JRXML-Parameter mit
role: variable, der kleingeschrieben beginnt (ucfirst-Falle). - Nicht parsebares Manifest (→ Fallback auf JRXML, mit Hinweis).
Ein fehlerhaftes Manifest darf niemals Upload oder Berichtsgenerierung blockieren — schlimmstenfalls verhält sich das System wie heute (kein Katalog).
Tests¶
- Unit:
ReportParameterCatalogServiceTest(Manifest-Parsing, JRXML-Fallback, Vorrangregel, Filterung,lcfirst-Ableitung, i18n-Auflösung, defekte Manifeste). - Feature:
AdminReportParameterCatalogTest(Endpoint, Permissions, 404 ohne Paket,configured-Anreicherung) — analogAdminReportPackageInspectTest.
Frontend-Umsetzung (pages/admin/reports.vue)¶
Dialog „Berichtsvariable erstellen"¶
Der bestehende Dialog bleibt; er wird um eine Auswahlstufe ergänzt:
- Der Admin wählt wie bisher den Bericht (Geltungsbereich). Sobald ein
konkreter Bericht gewählt ist, lädt das Frontend
GET …/report-settings/{id}/parameters(useReportAdmin-Composable, nebenfetchReportPackageInfo). - Über dem Feld „Variablenname" erscheint ein neues Select „Parameter aus
dem Report-Paket": die Katalogeinträge mit
role: variable, gruppiert nachgroup, mit Label + technischem Namen; bereits konfigurierte Einträge sind markiert (✓ und Geltungsbereich, z. B. „bereits als Typ-Variable gesetzt — hier anlegen überschreibt für diesen Bericht"). - Auswahl eines Parameters befüllt das Formular vor:
variable_name(abgeleitet),variable_label(Label),content(Default), Geltungsbereich (Scope-Empfehlung). Die Beschreibung wird als Hilfetext unter dem Inhaltsfeld angezeigt;inputsteuert das Eingabe-Widget (Textarea, Select mitoptions, DatePicker, ToggleSwitch, …). - Freitext bleibt möglich — Pakete ohne Katalog, Alt-Bundles und
Sonderfälle funktionieren unverändert. Das Select ist ein Angebot, kein
Zwang. Ist kein Katalog vorhanden (
source: none), erscheint das Select gar nicht — der Dialog sieht aus wie heute.
Paket-Inspektion¶
Der bestehende „Welche Jasper ist das?"-Dialog (README + Dateiliste) erhält einen Abschnitt „Parameter": tabellarisch Name, Label, Typ, Default, Beschreibung, Status (konfiguriert/fehlend) sowie die Upload-Warnungen. Damit sieht der Admin unmittelbar nach dem Hochladen, was das Paket erwartet.
Bulk-Import (Ausbaustufe)¶
Aktion „Fehlende Variablen aus Paket anlegen" am Report: legt für alle
Katalogeinträge mit role: variable, Default und ohne existierende Variable
die report-bezogenen Variablen in einem Schritt an (Bestätigungsdialog mit
Checkbox-Liste). Pflichtparameter ohne Default werden gelistet, aber nicht
automatisch angelegt — sie brauchen eine bewusste Eingabe.
Workflow des Berichtsentwicklers (calServer-reports)¶
- Parameter wie gewohnt im JRXML deklarieren (Jaspersoft Studio); Namen für konfigurierbare Parameter mit Großbuchstaben beginnen.
parameters.jsonim Bundle-Verzeichnis pflegen. Einstiegshürde senken: ein Generator-Skript (scripts/generate_parameters_manifest.pyim calServer-reports-Repo) erzeugt ein Manifest-Gerüst aus den JRXML-Deklarationen — der Entwickler ergänzt nur Beschreibungen und Rollen.- CI-Check im calServer-reports-Repo (analog
check_jasper_version.sh): validiert Manifest gegen Schema und JRXML (Namensabgleich,ucfirst-Regel,options/input-Konsistenz). Drift zwischen Manifest und JRXML wird so im Pull Request sichtbar, nicht erst beim Kunden. - Der bestehende Paket-Build nimmt das Manifest automatisch mit (es liegt im Bundle-Verzeichnis); an Build und Download-Seite ändert sich nichts.
- Autoren-Doku: neue Seite in
docs-v2/admin/berichte/(„Parameter im Report-Paket beschreiben") + Verweis ausv2-json-reports.mdund der Vorlagen-Doku im calServer-reports-Repo.
Kompatibilität¶
| System | Verhalten |
|---|---|
| V1 (Yii) | Ignoriert parameters.json vollständig — das ZIP bleibt V1-tauglich. Keine Yii-Migration nötig (keine Schemaänderung) |
| report-runner | Unverändert. ReportPackage.locateMain() sucht nur .jrxml; eine zusätzliche JSON-Datei stört weder Extraktion noch Kompilierung |
| Alt-Pakete | Ohne Manifest greift der JRXML-Fallback; ohne auswertbare Deklarationen verhält sich alles wie heute |
| Datenbank | Keine Änderung. master_report_variable bleibt unangetastet (V1-geteilt); Beschreibungen werden nie persistiert, sondern stets live aus dem Paket gelesen |
| Laufzeit-Prompt (Paket-7) | Unverändert; profitiert später von derselben Quelle: Einträge mit role: prompt können den Prompt-Dialog um Label/Beschreibung anreichern (Ausbaustufe) |
Sicherheit und Robustheit¶
- Manifest ist untrusted input: Größenlimit (256 KB), striktes JSON-Parsing, Schema-Validierung; Label/Beschreibung werden im Frontend als reiner Text gerendert (kein HTML/Markdown), Längen serverseitig gekappt.
- JRXML-Parsing wie bisher mit
LIBXML_NONET(kein XXE, kein Netzzugriff). - ZIP-Zugriff nur lesend über
ZipArchiveauf die DMS-Kopie mit Byte-Limits — identisches Härtungsniveau wieReportPackageInspector. - Der Katalog ist rein beratend: er beeinflusst die Generierung nicht.
ReportParameterBuilderund die Vorrangkette der Variablen bleiben unverändert; ein manipuliertes Manifest kann keine Systemparameter (CTAG,Reportpath, …) überschreiben, weil diese Namen serverseitig immer alssystemgefiltert werden und die Injektionsreihenfolge im Builder ohnehin zuletzt greift.
Umsetzungsphasen¶
| Phase | Inhalt | Abhängigkeiten |
|---|---|---|
| 1 — Backend-Katalog | JrxmlParameterParser extrahieren, ReportParameterCatalogService, Endpoint …/parameters, Upload-Warnungen, Tests |
keine |
| 2 — Frontend-Dialog | Parameter-Select im Variablen-Dialog, Vorbefüllung, Beschreibungsanzeige, Inspect-Dialog-Abschnitt | Phase 1 |
| 3 — Authoring-Kette | JSON-Schema veröffentlichen, Generator-Skript, CI-Validator, Manifeste für die bestehenden Bundles, Autoren-/Admin-Doku | Phase 1 (Schema-Festlegung) |
| 4 — Ausbaustufen | Bulk-Import „fehlende Variablen anlegen", Beschreibungs-Spalte im Variablen-Grid (live aus Katalog), Prompt-Dialog-Anreicherung (role: prompt) |
Phasen 1–3 |
Phasen 1 und 2 liefern den Kern des gewünschten Verhaltens; Phase 3 macht ihn für Berichtsentwickler nachhaltig nutzbar. Jede Phase ist einzeln shipbar.
Nicht-Ziele¶
- Kein neues Speichermodell für Variablen — die V1-geteilte Tabelle und die bestehende Vorrangkette bleiben, wie sie sind.
- Keine automatische, ungefragte Anlage von Variablen beim Upload — der Admin entscheidet (Bulk-Import ist explizit und bestätigungspflichtig).
- Kein Ersatz des Laufzeit-Prompts — Konfigurationsvariablen (Admin) und Generierungs-Prompts (Nutzer) bleiben getrennte Konzepte mit gemeinsamer Metadatenquelle.
Offene Punkte¶
variable_name-Override im Manifest? Aktuell bewusst weggelassen (Ableitung perlcfirstdeckt die Konvention ab). Bei Bedarf als optionales Feld nachrüstbar, ohne Format-Bruch.- Sprachauflösung: Anfragesprache (
Accept-Language/User-Locale) mit Fallback-Kettede → en → erste vorhandene— abzustimmen mit der bestehenden i18n-Strategie des Admin-Frontends. - Manifest-Ort bei Einzel-JRXML-Uploads (ohne ZIP): erste Ausbaustufe unterstützt nur den JRXML-Fallback; ein Sidecar-Manifest für Einzeldateien lohnt erst bei nachgewiesenem Bedarf.