HSM2Med ist eine vollständig offline lauffähige Webanwendung, die Exportdateien aus dem Abbott/St. Jude Merlin-Programmiergerät sowie XML-Exporte von Biotronik (IEEE 11073-10103) importiert, strukturiert darstellt, als unveränderliche Berichte in MySQL speichert und daraus PDF-Berichte erzeugt – auch für historische Auslesungen, ausschließlich aus der Datenbank.
Wichtig: Die Anwendung führt keine medizinische Bewertung durch. Alle Werte werden exakt so angezeigt und gespeichert, wie sie in der Quelldatei stehen. Diagnose- und Therapieentscheidungen sind ausschließlich Aufgabe des ärztlichen Personals.
- Funktionsumfang
- Installation
- Konfiguration (.env)
- Start, Stopp, Aktualisierung
- Offline-Betrieb
- Bedienung der Weboberfläche
- Patientenakte
- Patientenausweis erstellen
- Brief zur Schrittmacher-/ICD-Abfrage
- Kommandozeile (CLI)
- Importformat
- Parserverhalten und Fehlerbehandlung
- Datenmodell, Snapshots und Versionierung
- PDF-Berichte
- Parameterzuordnung erweitern
- Sicherheitskonzept
- Datenschutz
- Tests
- Screenshots für die Dokumentation
- Backup und Wiederherstellung
- Fehlerbehebung
- Projektstruktur
- Upload von Merlin-Exportdateien (
.txt/.log) und Biotronik-XML-Exporten (.xml, IEEE 11073-10103) mit Prüfung vor dem Speichern (Vorschau mit Patient, Gerät, Sonden, Kategorien, Warnungen und Fehlern). - Verlustfreier Parser: bei Merlin ist der Feldtrenner ausschließlich ASCII 0x1C (File Separator),
bei Biotronik werden die
<value>-Knoten des XML gelesen; Werte, leere Felder, Einheiten, Dezimalzahlen und Datumsangaben bleiben unverändert. - Speicherung jedes Imports als unveränderlicher Bericht (Snapshot) in MySQL in einer einzigen Transaktion – bei Fehlern vollständiger Rollback.
- Dublettenerkennung per SHA-256 (erneuter Import nur nach expliziter Bestätigung).
- Berichtsübersicht mit Suche (Freitext, Patient, Patienten-ID, Seriennummer, Modell, Dateiname, Zeitraum), Importprotokoll und Systemstatus.
- PDF-Berichte (eigene, abhängigkeitsfreie PDF-Erzeugung) – optional mit Rohdatenanhang, jederzeit reproduzierbar aus der Datenbank.
- Patientenakte: Patienten lassen sich vor dem Import anlegen und pflegen. Anamnese, Vormedikation, Befund, Epikrise und Notiz sowie die Schrittmacher-/ICD-Abfrage werden als eigene, versionierte Bausteine am Patienten gespeichert – analog zu Berichten aus dem Import oder Patientenausweisen. Die Abfrage bildet den Wunschkatalog des ärztlichen Dienstes als geräteabhängiges Formular ab und lässt sich aus dem letzten Bericht vorbelegen.
- Patientenausweis (zwei Seiten DIN A4) aus einem importierten Bericht: Assistent in sechs
Schritten, Identitätsprüfung über Nachname + Vorname + Geburtsdatum, Konfliktentscheidung je
Feld, zwei ausdrückliche Bestätigungen, unveränderliche PDF-Snapshots und Historie. Seite 1
nennt unter anderem die MRT-Tauglichkeit (Auswahlwert mit optionaler Zusatzangabe). Seite 2
zeigt die Messwerte der aktuellen und der letzten sechs Untersuchungen anhand der Vorlage
config/patient_card_measurements.php. - Praxis-Informationen im Bereich System (
/system/settings): Praxisname, Anschrift, Telefon, Fax, E-Mail, Internetseite, Logo sowie die Rücksendeangaben (Name, Straße, PLZ, Ort) an einer Stelle pflegen. Diese Angaben sind die gemeinsame Datenquelle für Briefe und Patientenausweise: Name, Anschrift und Logo erscheinen im Briefkopf, die Kontaktangaben als Kontaktzeile und die Rücksendeangaben als Absenderzeile im Brief. Jede Speicherung erzeugt eine neue, unveränderliche Fassung; bereits erzeugte Briefe und Ausweise bleiben unverändert. - Globale Stammdaten für den Ausweis (Logo, Nachsorgezentrum, Hinweis- und Flugsicherheitstexte) mit eigener Fassung je Ausweis.
- Brief zur Schrittmacher-/ICD-Abfrage: automatisch erzeugter Brief aus der Patientenakte. Er enthält Anamnese, Vormedikation, Befund und Epikrise als Textteile, direkt unter den Patientendaten den Baustein Aggregat – Hersteller, Modell, Seriennummer, Status, Implantationsdatum, Implantationsort und Betriebsart des letzten Imports zweispaltig, darunter die Sonden (Elektroden) derselben Abfrage je Sonde in einer Zeile (Kammer, Hersteller, Modell, Seriennummer, Typ, Implantationsdatum) –, den Baustein Berichte – den Befundteil „Schrittmacher-/ICD-Abfrage" des zugeordneten Berichts als Anhang unter der Grußformel auf einer neuen Seite (nur wenn ein Bericht zugeordnet wurde) – und als Anhang die vollständige Tabelle der Schrittmacher-/ICD-Abfrage samt MRT-Tauglichkeit aus dem Patientenausweis. Die letzte Seite zeigt die Messwerttabelle: erste Spalte die Messwertbezeichner, erste Zeile die Untersuchungsdaten, die aktuelle Untersuchung ganz rechts und davor die letzten früheren Untersuchungen (Anzahl in der Briefvorlage einstellbar); welche Messwerte erscheinen, wird je Brief per Checkbox-Liste aus den vorhandenen Werten gewählt und mit dem Brief eingefroren. Assistent in sieben Schritten, zwei ausdrückliche Bestätigungen, unveränderlicher Snapshot mit SHA-256-geprüftem PDF. Der Brief darf mehrseitig sein – die Zwei-Seiten-Grenze gilt ausschließlich für den Patientenausweis. Briefe folgen DIN 5008 (Form B). Empfänger per Checkbox: Patient, Hausarzt und Überweisender Arzt – je Empfänger entsteht ein eigener Brief mit dessen Anschrift und dessen Anrede aus den Stammdaten. Ist weder für den Hausarzt noch für den Überweisenden Arzt eine Anschrift hinterlegt, bietet der Assistent zusätzlich Arztbrief generisch erstellen: Der Brief geht dann an „An die weiterbehandelnden Ärztinnen und Ärzte" und wird mit „Sehr geehrte Kollegin, sehr geehrter Kollege," angeredet.
- Vorlageneditor für Briefe (System → Briefvorlage bearbeiten, öffnet in neuem Tab): alle festen Texte bearbeiten, Bausteine per Drag and Drop anordnen, Live-Vorschau und PDF-Vorschau. Vorlagen werden je Empfängerart getrennt (Patient, Hausarzt, Überweisender Arzt, Arztbrief generisch) und versioniert gepflegt; einzelne Bausteine lassen sich per Rechtsklick aus einer anderen Vorlage übernehmen. Jeder Brief kann mit seiner ursprünglichen Vorlage reproduziert oder – auf ausdrücklichen Wunsch – mit der aktuellen Vorlage neu ausgefertigt werden. Technische Referenz: docs/editor-referenz.md.
- Vorlageneditor für Patientenausweise (System → Ausweisvorlage bearbeiten, öffnet in neuem Tab): derselbe Editor für die festen Texte und den Aufbau des Patientenausweises (Kopf- und Fußzeile, Bausteine in Spalten auf Seite 1 und untereinander auf Seite 2, Live-Vorschau und PDF-Vorschau mit Beispieldaten). Ausweisvorlagen sind ebenfalls versioniert; jeder Ausweis trägt die beim Erzeugen gültige Fassung eingefroren in seinem Snapshot. Das Recht heißt Ausweisvorlagen (Editor).
- CLI für Import, PDF-Export, Migrationen, Schema-Erzeugung und das Anlegen des Administrators.
- Anmeldung und Benutzerverwaltung: Ohne Anmeldung ist die Anwendung nicht bedienbar. Beim Aufruf wird die Oberfläche geladen und erscheint hinter dem Anmeldefenster leicht abgedunkelt und deutlich unscharf; das Fenster ist das einzige bedienbare Element. Benutzerkonten werden Gruppen zugeordnet; welche Gruppe welche Bereiche des Funktionsbands sehen und öffnen darf, wird als Rechtematrix in der Oberfläche gepflegt (nicht im Code). Vorgegeben sind die Gruppen Admin (Vollzugriff einschließlich Benutzerverwaltung), MFA (Tagesgeschäft: Import, Akten, Ausweise, Briefe) und Arzt (alle medizinischen Bereiche, Vorlagen, Systeminformationen und Protokoll). Ein von der Administration vergebenes Kennwort muss beim nächsten Login ersetzt werden; die Kennwortfelder zeigen dabei eine Kennwortstärke an.
- Keine externen Abhängigkeiten zur Laufzeit: kein CDN, keine Webfonts, keine Composer-Pakete.
Technik: PHP 8.5 (nativ, strict_types, PDO), MySQL 9.7, Apache, Docker Compose.
Voraussetzungen: Docker Engine mit Docker Compose v2.
git clone <repository> hsm2med
cd hsm2med
cp .env.example .env # danach Passwörter in .env ändern!
docker compose up -d --buildDie Oberfläche ist anschließend unter http://127.0.0.1:8080 erreichbar
(standardmäßig nur lokal, siehe WEB_BIND_ADDRESS). Beim Start des Web-Containers
werden ausstehende Datenbankmigrationen automatisch ausgeführt und das Administratorkonto aus
ADMIN_USERNAME/ADMIN_PASSWORD angelegt. Die erste Anmeldung erfolgt mit diesen Werten;
das Kennwort ist danach unter Kennwort ändern zu ersetzen (die Oberfläche weist darauf hin).
Statusprüfung: curl http://127.0.0.1:8080/health → {"status":"ok","database":"ok"}.
Alternativ kann das Schema ohne Migrator eingespielt werden:
docker compose exec -T db sh -c 'exec mysql -u root -p"$MYSQL_ROOT_PASSWORD" hsm2med' < database/schema.sql.
database/schema.sql wird mit php bin/build-schema.php aus den Migrationen erzeugt
(ein Test stellt sicher, dass beide identisch sind).
| Variable | Standard | Bedeutung |
|---|---|---|
DB_HOST, DB_PORT |
db, 3306 |
Datenbankserver |
DB_DATABASE, DB_USERNAME |
hsm2med |
Datenbank und Anwendungsbenutzer |
DB_PASSWORD |
– (Pflicht) | Passwort des Anwendungsbenutzers |
DB_ROOT_PASSWORD |
– (Pflicht) | MySQL-Root-Passwort (Initialisierung, Backups) |
APP_ENV |
production |
production, development oder testing; Fehlerdetails nur außerhalb von production |
APP_TIMEZONE |
Europe/Berlin |
Zeitzone für Zeitstempel |
UPLOAD_MAX_SIZE |
5M |
Maximale Uploadgröße (Bytes oder K/M/G); PHP-Limits werden beim Start daraus abgeleitet |
PDF_RAW_APPENDIX_DEFAULT |
0 |
Rohdatenanhang im PDF standardmäßig anfügen |
SESSION_SECURE_COOKIE |
0 |
1 setzen, wenn ein HTTPS-Reverse-Proxy vorgeschaltet ist |
ADMIN_USERNAME |
admin |
Anmeldename des Administratorkontos, das beim Start angelegt wird |
ADMIN_PASSWORD |
bitte-aendern-admin-passwort |
Startkennwort dieses Kontos – unbedingt ändern |
AUTH_IDLE_MINUTES |
30 |
Automatische Abmeldung nach dieser Zeit ohne Bedienung (1–1440) |
WEB_BIND_ADDRESS, WEB_PORT |
127.0.0.1, 8080 |
Adresse/Port auf dem Host |
.env enthält Zugangsdaten und darf nicht eingecheckt werden (steht in .gitignore).
ADMIN_PASSWORD wird nur beim ersten Anlegen des Kontos verwendet; spätere Änderungen an
.env wirken nicht auf ein bestehendes Konto (das Kennwort wird in der Anwendung geändert).
Ist kein Administratorkonto mehr erreichbar, setzt docker compose exec web php bin/seed-admin.php --force
das Kennwort auf den Wert aus .env zurück.
Mit APP_ENV=production wird ein Konto nicht mit dem Vorgabekennwort aus .env.example
angelegt (auch nicht per --force): der Web-Container bricht den Start dann mit einem Hinweis
ab, bis in .env ein eigenes ADMIN_PASSWORD steht. Ein bereits bestehendes Konto bleibt
unberührt (nur Hinweis).
docker compose up -d # starten
docker compose ps # Status (inkl. Healthcheck)
docker compose logs -f web # Protokolle
docker compose down # stoppen (Daten bleiben in den Volumes erhalten)
docker compose up -d --build # nach einer Aktualisierung neu bauen; Migrationen laufen automatischPersistente Daten liegen in Docker-Volumes:
| Volume | Inhalt |
|---|---|
hsm2med_mysql_data |
MySQL-Datenbank (Berichte, Parameter, Importprotokoll) |
hsm2med_import_data |
Archiv der Originaldateien (<sha256>.txt, schreibgeschützt) |
hsm2med_application_data |
Anwendungsprotokoll, Sessions, zwischengespeicherte Uploads |
Zur Laufzeit wird kein Internetzugang benötigt: Alle Assets (CSS/JS) liegen lokal, PDFs
nutzen die PDF-Standardschriften, es gibt keine externen Aufrufe. Das Datenbanknetz
(backend) ist als internal konfiguriert.
Installation auf einem Rechner ohne Internet:
# auf einem Rechner mit Internet
docker compose build
docker pull mysql:9.7
docker save -o hsm2med-images.tar hsm2med-web:latest mysql:9.7
# Datei und Projektverzeichnis auf den Zielrechner kopieren, dort:
docker load -i hsm2med-images.tar
cp .env.example .env # Passwörter setzen
docker compose up -d # ohne --buildDie Oberfläche ist wie ein Office-Programm aufgebaut: oben ein Funktionsband (Ribbon)
mit Reitern, darunter der Arbeitsbereich der jeweiligen Seite und am unteren Rand eine
Statusleiste mit dem aktiven Patienten, dem angemeldeten Benutzerkonto, dem Hinweis zur
Verwendung der Daten und der Autoren-Info (HSM2Med by Daniel-André Reinelt).
Funktionsband und Statusleiste
bleiben am oberen bzw. unteren Bildschirmrand stehen und sind damit auch bei langen
Seiten ohne Scrollen sichtbar. Ein Klick auf die Autoren-Info öffnet
einen Hinweistext zu Anspruch und Entstehung der Anwendung; das Overlay lässt sich
ausschließlich über das Schließen-Kreuz oben rechts verlassen.
Die Anwendung ist ohne Anmeldung nicht bedienbar. Beim Aufruf einer beliebigen Seite wird die Oberfläche geladen, aber gesperrt und hinter dem Anmeldefenster dargestellt: der Hintergrund ist leicht abgedunkelt und deutlich unscharf, das Fenster ist das einzige bedienbare Element. Nach der Anmeldung wird die ursprünglich aufgerufene Seite geöffnet.
- Ohne Bedienung meldet die Anwendung nach
AUTH_IDLE_MINUTESautomatisch ab. - Nach fünf Fehlanmeldungen wird ein Konto für 15 Minuten gesperrt.
- Über die Statusleiste sind Unterschrift (
/account/signature, für jedes Konto), Kennwort ändern (/account/password, für jedes Konto) und Abmelden erreichbar. - Ein Konto, das noch das Kennwort aus
.envverwendet, wird in der Benutzerverwaltung ausdrücklich gekennzeichnet. - Wurde das Kennwort von der Administration vergeben (neues Konto oder neu gesetztes Kennwort),
führt die Anmeldung zuerst auf Kennwort ändern (
/account/password). Bis das eigene Kennwort gesetzt ist, bleibt nur dieser Bereich (und die Unterschrift sowie Abmelden) erreichbar; die Oberfläche erklärt den Grund. Mit dem eigenen Kennwort entfällt die Pflicht automatisch, in der Benutzerverwaltung verschwindet dann die Kennzeichnung Kennwortwechsel offen.
Sichtbar und erreichbar nur für Gruppen mit dem Recht Benutzerverwaltung (vorgegeben: Admin).
- Benutzerkonten (
/system/users): Anmeldename, Anzeigename, Titel, Vorname, Nachname, Gruppen, Aktivstatus; Kennwörter werden ausschließlich als Hash gespeichert und lassen sich nur neu setzen (/system/users/{id}/password), nie auslesen. Die Option Muss Kennwort beim nächsten Login ändern ist beim Anlegen vorgegeben und wird beim Setzen eines fremden Kennworts wieder gesetzt; sie lässt sich je Konto abschalten. Unter jedem Kennwortfeld zeigt eine Anzeige die Kennwortstärke (Länge, Groß-/Kleinbuchstaben, Ziffer, Sonderzeichen). Die Anzeige ist eine Hilfe beim Vergeben – verbindlich prüft der Server die Regel „mindestens 12 Zeichen, nicht gleich dem Anmeldenamen". - Unterschrift je Konto (
/system/users/{id}/signature, eigene Seite/account/signature): JPG, PNG oder BMP (max. 1 MiB, max. 2000 × 2000 Pixel), Prüfung ausschließlich über den Dateiinhalt. Das Bild wird nicht unter dem Originalnamen gespeichert und bei gleichem Inhalt (SHA-256) nur einmal abgelegt; die Skalierung auf die Briefgröße (60 × 22 mm) übernimmt der Brief. Unter der Unterschrift steht im Brief immer Titel, Vorname und Name der unterzeichnenden Person. Das Entfernen der Unterschrift löscht die Bilddatei nicht, weil bereits erstellte Briefe auf sie verweisen. - Gruppen und Rechte (
/system/users/groups): Gruppen anlegen, umbenennen und löschen; die Rechtematrix je Gruppe bestimmt, welche Bereiche des Funktionsbands sichtbar und aufrufbar sind. Die Matrix ist die einzige Quelle für Rechte – es gibt keine Rechte im Code. Die drei vorgegebenen Gruppen Admin, MFA und Arzt sind Systemgruppen: sie lassen sich in den Rechten ändern, aber nicht löschen. - Rechte werden bei jedem Aufruf serverseitig geprüft. Verweise in Bereiche ohne Recht erscheinen gar nicht erst; ein direkter Aufruf der Adresse wird abgewiesen.
- Die letzte Gruppe mit dem Recht Benutzerverwaltung kann nicht so geändert oder gelöscht werden, dass niemand mehr die Benutzerverwaltung öffnen könnte.
- Die eigene Admin-Rolle kann niemand entfernen: Beim Bearbeiten des eigenen Kontos sind Gruppen mit dem Recht Benutzerverwaltung gesperrt. Ein anderer Administrator kann die Rolle weiterhin entziehen.
Funktionsband (Ribbon) – jeder Reiter bündelt die Funktionen eines Themas in Gruppen mit großen Symbolen und Beschriftung:
| Reiter | Gruppen und Funktionen |
|---|---|
| Start | Überblick (Dashboard, Berichte, Patientenakten, Patientenausweise, Briefe); Neu anlegen (Patient anlegen, Ausweis erstellen, Brief erstellen); Nachschlagen (Importprotokoll, Systeminformationen) |
| Import | Merlin-Export einlesen (Datei importieren, Importprotokoll); Weiter zur Auswertung (Berichte ansehen, Patientenausweise) |
| Berichte | Berichte (Berichtsübersicht, Importprotokoll); Aus dem Bericht erstellen (Patientenausweis, Arztbrief) |
| Patientenakte | Akten (Patientenübersicht, Patient anlegen); Weiterverarbeiten (Ausweise und Nachsorge, Briefe); Nachschlagen (Importprotokoll) |
| Patientenausweise | Ausweise (Ausweisübersicht, Ausweis erstellen, Ausweis-Stammdaten); Quellen (Patientenakten, Berichte) |
| Briefe | Briefe (Briefübersicht, Brief erstellen); Vorlagen (Praxis-Informationen, Briefvorlage); Quellen (Patientenakten, Berichte) |
| System | Praxis (Praxis-Informationen); Zugang (Benutzerverwaltung, Eigenes Kennwort); Betrieb (Systeminformationen, Fehlerprotokoll, Importprotokoll); Vorlagen (Briefvorlage, Ausweisvorlage); Daten und Datenschutz (Datenschutz, Berichte) |
Der jeweils aktuelle Reiter ist hervorgehoben; welcher Reiter zu einer Seite gehört, steuert
src/Http/Ribbon.php über den $active-Schlüssel der Seite. Angezeigt werden nur Reiter und
Schaltflächen, für die das angemeldete Konto das Recht hat; Grundlage ist dieselbe Zuordnung,
die der Server durchsetzt (App\User\Permission::forPath()).
Weitere Bedienelemente der Kopfzeile:
- Schnellzugriff – die vier häufigsten Aktionen (Import, Patient anlegen, Ausweis erstellen, Brief erstellen) sind zusätzlich oben rechts erreichbar.
- Dokumenttitel – zeigt den Namen der aktuellen Seite und, sofern sinnvoll, den Datensatz (z. B. Patient und Geburtsdatum).
- Aktiver Patient – zeigt links in der Titelleiste den aktiven Patienten (Name und Geburtsdatum) oder den Hinweis Kein Patient gewählt; siehe Der Patientenvorgang ist führend.
- Datenschutz – Schaltfläche zum Datenschutz-Abschnitt der Systemseite.
Das Dashboard zeigt zusätzlich fünf große Einstiegskacheln für die häufigsten
Arbeitsabläufe. Alle Symbole sind eingebettete SVG-Grafiken (src/Http/Icon.php); es werden
keine externen Ressourcen geladen. Das Funktionsband ist reine Navigation und funktioniert
auch ohne JavaScript. Beim Drucken werden Funktionsband, Statusleiste und Einstiegskacheln
automatisch ausgeblendet.
| Bereich | Beschreibung |
|---|---|
| Dashboard | Kennzahlen und zuletzt importierte Berichte |
| Import | Datei wählen → Datei prüfen → Vorschau → Import endgültig speichern oder Verwerfen |
| Berichte | Liste und Suche; Detailansicht mit Patient, Gerät, Sonden, Kategorien, Importprotokoll und Originaldaten |
| Patienten | Patientenakte: Patienten vor dem Import anlegen, Stammdaten pflegen, Anamnese/Vormedikation/Befund/Epikrise/Notiz und Schrittmacher-/ICD-Abfrage als versionierte Bausteine |
| Briefe | Brief zur Schrittmacher-/ICD-Abfrage aus der Akte erzeugen, durchsuchen und als unveränderliches PDF abrufen |
| Importprotokoll | Alle Importe inkl. fehlgeschlagener, mit Warnungen/Fehlern je Datensatz |
| Benutzerverwaltung | Konten, Gruppen und die Rechtematrix je Gruppe (nur mit entsprechendem Recht) |
| Systeminformationen | Versionen, Datenbank- und Migrationsstatus, Limits |
Alle Vorgänge, die einen Patientenbezug herstellen, setzen einen aktiven Patienten voraus:
Import, Ausweis erstellen und Brief erstellen. Ohne Auswahl eines Patienten sind diese
Einträge im Funktionsband und im Schnellzugriff ausgegraut und nicht anklickbar; ein direkter
Aufruf der Adresse (/import, /patient-cards/new, /letters/new) leitet auf die
Patientenübersicht um und weist auf die fehlende Auswahl hin.
Der aktive Patient steht in der Titelleiste und in der Statusleiste. Ausgewählt wird er in der
Patientenübersicht über Auswählen oder in der Akte über Als aktiven Patienten wählen;
Auswahl aufheben entfernt die Zuordnung wieder. Ein Rechtsklick auf die Kachel des aktiven
Patienten in der Titelleiste öffnet zusätzlich ein Kontextmenü mit Patientenauswahl
zurücksetzen; ohne aktiven Patienten erscheint dieses Menü nicht. Das Anlegen eines Patienten wählt ihn
automatisch als aktiven Patienten – nach dem Anlegen kann der Import also sofort erfolgen.
Ebenso führt Brief erstellen mit aktivem Patienten direkt in den Briefassistenten; die
Patientenauswahl ist dort über Anderen Patienten wählen weiterhin erreichbar. Auch
Ausweis erstellen springt mit aktivem Patienten direkt in den Ausweis-Assistenten des
neuesten, mit ihm verknüpften Berichts; die Berichtsauswahl bleibt über Anderen Bericht
wählen bzw. /patient-cards/new?select=1 erreichbar. Liegt für den aktiven Patienten noch
kein Bericht vor, erscheint die Berichtsauswahl mit einem entsprechenden Hinweis.
Die Auswahl gilt für die laufende Sitzung. Listen, Übersichten und Auswertungen
(Patientenübersicht, Berichte, Importprotokoll, Ausweis- und Briefübersichten,
Systeminformationen) bleiben auch ohne aktiven Patienten erreichbar, ebenso das Anlegen eines
Patienten unter /patients/new.
1. Upload und Prüfung – die Datei wird validiert und analysiert, aber noch nicht gespeichert:
2. Dubletten und ungültige Dateien – eine bereits importierte Datei kann nur nach ausdrücklicher Bestätigung erneut importiert werden; Dateien ohne erkanntes Format werden abgelehnt:
3. Berichte – Übersicht mit Suchfiltern und Detailansicht mit den Schaltflächen PDF anzeigen, PDF herunterladen und PDF mit Rohdatenanhang:
4. Importprotokoll und Systemstatus:
5. Fehlerprotokoll (/system/logs, Reiter „System"): zeigt die Einträge des
Anwendungsprotokolls (storage/logs/app.log), neueste zuerst, mit Suche nach Referenz (auch
Anfang der Referenz), Stufe und Text. Die Fehlerseite verlinkt direkt auf den Eintrag zu ihrer
Referenz; Ausnahme, Ort und Stacktrace stehen in den technischen Details. Durchsucht werden die
neuesten 8 MiB des Protokolls.
Patienten können unabhängig von einem Import angelegt werden. Damit sind Anamnese, Vormedikation, Befund und Epikrise schon vor dem Importprozess erfassbar; der Import ordnet den Bericht später über die Identität demselben Patienten zu. Die Akte selbst erzeugt keine medizinischen Bewertungen: gespeichert wird ausschließlich, was eingegeben wurde. Aus diesen Bausteinen erzeugt die Anwendung den Brief zur Schrittmacher-/ICD-Abfrage. Da der Patientenvorgang führend ist, wird ein neu angelegter Patient automatisch als aktiver Patient gesetzt.
1. Patient anlegen (/patients/new) – Pflichtfelder sind Nachname, Vorname und
Geburtsdatum; optional sind Patienten-ID, Anschrift, Telefon, Indikation sowie Hausarzt und
Überweisender Arzt (je Name, Praxis, Straße, PLZ, Ort, Telefon – die Anschriften der
Briefempfänger). Das Geburtsdatum
wird als TT.MM.JJJJ erfasst (zusätzlich erkannt: JJJJ-MM-TT, TT/MM/JJJJ, TT-MM-JJJJ) und
als ISO-Datum gespeichert; unplausible oder in der Zukunft liegende Daten werden abgelehnt.
Das Formular meldet Fehler je Feld mit HTTP 422 und behält die Eingaben. Nach dem Speichern
führt es auf die Akte; der neue Patient ist dort als aktiver Patient gekennzeichnet.
Über Stammdaten bearbeiten in der Akte werden Hausarzt und überweisender Arzt nachgetragen. Die Hausarztangaben aus dem Patientenausweis-Assistenten stehen dort bereits:
In denselben Stammdaten steht die Anrede für die Briefe: beim Patienten Herr, Frau oder
Divers, bei Hausarzt und überweisendem Arzt Kollege, Kollegin oder Unpersönlich
(Praxis/Klinik, „Sehr geehrte Damen und Herren,"). Sie wird je Empfänger gepflegt und in jedem
Brief über den Platzhalter {salutation} eingesetzt (siehe
Briefvorlage). Ohne Angabe druckt der Brief „Sehr geehrte
Damen und Herren,"; der Brief-Assistent weist darauf hin.
2. Dublettenprüfung – die Identität eines Patienten ist Nachname + Vorname + Geburtsdatum (Groß-/Kleinschreibung und umgebende Leerzeichen bleiben ohne Bedeutung). Existiert dazu bereits ein Patient, listet das Formular die Treffer auf und verlangt die ausdrückliche Bestätigung „Es handelt sich um einen anderen Patienten". Ohne diese Bestätigung wird nichts gespeichert. Die Patienten-ID ist ebenfalls eindeutig: eine bereits vergebene ID wird abgewiesen.
Ein Baustein ist ein benannter Teil der Akte, der als unveränderliche Fassung gespeichert wird – analog zu den Berichten aus dem Import und zu den Patientenausweisen. Je Patient und Bausteintyp existiert genau ein aktueller Stand, dazu die vollständige Historie.
| Baustein | Erfassung |
|---|---|
| Anamnese | Freitext (Beschwerden, Vorerkrankungen, Implantationsgrund) |
| Vormedikation | Tabelle (Wirkstoff, Dosis, Einheit, Einnahme, Grund, von, bis) mit ergänzendem Freitext |
| Befund | Freitext (ärztlicher Befund zur Untersuchung) |
| Epikrise | Freitext (Zusammenfassung des Verlaufs) |
| Notiz | Freitext (freie Anmerkung zur Akte) |
| Schrittmacher-/ICD-Abfrage | Geräteabhängiges Formular nach Vorlage config/device_check_template.php (siehe unten) |
Jede Speicherung erzeugt eine neue Fassung mit laufender Nummer, Zeitstempel und optionalem
Autor (Fassung 2, vom 07.10.2026, erfasst von Dr. med. Anna Beispiel). Inhaltsgleiche
Eingaben erzeugen keine neue Fassung; verglichen wird ein SHA-256 über den
normalisierten Inhalt. Freitext ist auf 20 000 Zeichen begrenzt, der Autor auf 255 Zeichen,
die Vormedikation auf 50 Zeilen. Datumsangaben in Medikamentenzeilen werden wie das
Geburtsdatum normalisiert gespeichert und in der Akte als TT.MM.JJJJ angezeigt; zusätzlich
werden TTMMJJJJ, JJJJMMTT, MM/JJJJ, MM.JJJJ, MM-JJJJ, JJJJ-MM, JJJJ/MM und
JJJJ.MM erkannt, wobei ohne Tagesangabe der Monatserste gilt. Eine leere Spalte bis bedeutet
„fortlaufend".
Der Baustein Schrittmacher-/ICD-Abfrage (record_type = device_check) setzt den
Wunschkatalog des ärztlichen Dienstes als eigenes, versioniertes Formular um. Vorlage ist
config/device_check_template.php; die Vorlagenfassung (Vorlage 1.0.0) wird mit jedem
gespeicherten Inhalt festgehalten, sodass ältere Abfragen unverändert bleiben. Alle Feldwerte
sind Freitext mit der Einheit in der Beschriftung (Output (V/ms) → 2,5/0,4); es findet
keine Zahleninterpretation und keine medizinische Bewertung statt.
Die Art des Geräts steuert die sichtbaren Abschnitte und ist beim Speichern erforderlich:
| Abschnitt | Felder | Gilt für |
|---|---|---|
| Gerät | Hersteller, Modell, Seriennummer, Implantationsdatum, MRT-Tauglichkeit (mit Zusatzangabe) | alle |
| Sonden (Elektroden) | Modell, Lokalisation, Implantationsdatum, Impedanz, Wahrnehmung, Reizschwelle, Schockimpedanz | alle; Schockimpedanz nur ICD/CRT-D |
| Batterie | Status, verbleibende Laufzeit, Magnetfrequenz | alle |
| Bradykardie | Betriebsart, untere Grenzfrequenz, Hysteresefrequenz, max. Synch.frequenz, max. Sensorfrequenz, PMT-Intervention, R-Funktion | alle |
| RA (Vorhofsonde) | Output, Empfindlichkeit, Wahrnehmungs- und Stimulationspolarität, Ausblendzeit, Refraktärzeit, Refraktärzeit (PVARP) | alle |
| RV (Ventrikelsonde) | Output, Empfindlichkeit, Wahrnehmungs- und Stimulationspolarität, Ausblendzeit, Refraktärzeit | alle |
| AV | Stim. AV-Intervall, wahrg. AV-Intervall, AV-Suchhysterese, ModeSwitch Betriebsart, ModeSwitch Frequenz | alle |
| LV (linksventrikuläre Sonde) | Wahrnehmung, Reizschwelle, Impedanz, Output, Empfindlichkeit, Wahrnehmungs- und Stimulationspolarität | nur CRT-P/CRT-D |
| Tachykardie | je VT1, VT2, VF: Erkennung (Frequenz, Zykluslänge) und Therapie (Maßnahmen) | nur ICD/CRT-D |
Grenzen: bis zu 12 Sondenzeilen, 120 Zeichen je Feldwert, 4 000 Zeichen Bemerkungen. Vollständig leere Sondenzeilen werden verworfen; nicht zum Gerätetyp passende Werte werden weder gespeichert noch ausgegeben.
Vorbelegung aus dem letzten Bericht: die Schaltfläche Werte aus dem letzten Bericht
übernehmen füllt nur leere Felder aus dem neuesten importierten Bericht des Patienten
(z. B. Betriebsart, Grenzfrequenzen, Sondenmodell, Implantationsdatum, Polaritäten). Die
Zuordnung steht als sources (Parameter-IDs und Bezeichnungen aus dem Merlin-Quellformat) bzw.
from_summary (Zeilen der Berichtszusammenfassung) in der Vorlage; vorhandene Eingaben bleiben
erhalten, es gibt keinen Bericht → entsprechender Hinweis.
Die MRT-Tauglichkeit führt der Patientenausweis. Enthält der neueste Ausweis eine Angabe, wird sie in der Abfrage nur lesend übernommen und dort gepflegt (Feld gesperrt, Wert wird dennoch mitgesendet). Ohne Angabe im Ausweis bleibt das Feld in der Abfrage erfassbar. Auch beim Speichern gilt der Ausweis: ein abweichender Wert aus dem Formular wird verworfen. Da der Ausweis dauerhaft auf zwei Seiten begrenzt ist, bleibt die Zusatzangabe auf 120 Zeichen beschränkt.
Die Übersicht (/patients) sucht nach Name (Freitext), Patienten-ID und Geburtsdatum
(TT.MM.JJJJ) und zeigt je Patient die Anzahl verknüpfter Berichte und Bausteine sowie den
Zeitpunkt der letzten Bausteinänderung. Ein Bericht aus dem Import wird über dieselbe
Identität zugeordnet; die Akte verlinkt auf die Ausweise und die Nachsorge des Patienten
(/patient-cards/patients/{id}).
| Route | Zweck |
|---|---|
GET /patients |
Übersicht mit Suche und Seitenaufteilung |
GET /patients/new |
Formular für einen neuen Patienten |
POST /patients |
Patient anlegen (bei Dublette nur mit Bestätigung) |
GET /patients/{id} |
Akte mit Stammdaten, Bausteinen und Berichten |
GET /patients/{id}/edit |
Stammdaten bearbeiten |
POST /patients/{id} |
Stammdaten speichern |
GET /patients/{id}/records/{slug} |
Baustein bearbeiten (anamnesis, premedication, epicrisis, note, device_check) |
POST /patients/{id}/records/{slug} |
Baustein speichern (neue Fassung) |
POST /patients/{id}/records/device_check/prefill |
Leere Felder der Abfrage aus dem letzten Bericht vorbelegen |
Der Patientenausweis ist ein zweiseitiges DIN-A4-PDF („Schrittmacher - Patientenausweis" / „Patient Identification Card"), das aus einem bereits importierten Nachsorgebericht erzeugt wird. Es werden keine medizinischen Bewertungen abgeleitet oder ergänzt: übernommen werden nur Patient, Gerät, Sonden, Mess- und Nachsorgeangaben aus dem Bericht sowie die Angaben, die im Assistenten erfasst werden.
1. Stammdaten pflegen (/patient-cards/settings, Reiter Patientenausweise unter Ausweise)
– die drei Texte des Ausweises: Hinweise, Achtung Flugsicherheit auf Deutsch, Attention Airline
Security auf Englisch. Praxisname, Anschrift, Kontaktangaben, Logo und Rücksendeangaben werden
hier nur angezeigt und im Bereich System → Praxis-Informationen (/system/settings) gepflegt;
beim Speichern der Ausweistexte bleiben die dortigen Angaben unverändert erhalten. Jede Speicherung
erzeugt eine neue, unveränderliche Fassung; das Logo wird über seinen SHA-256 erkannt und nicht
mehrfach gespeichert. Die Texte sind nicht im PDF-Generator hinterlegt, sondern werden je Ausweis
mitgespeichert.
2. Bericht wählen (/patient-cards/new bzw. Schaltfläche Patientenausweis erstellen in
der Berichtsansicht) – mit aktivem Patienten entfällt dieser Schritt: Ausweis erstellen
öffnet direkt den Assistenten des neuesten, mit dem Patienten verknüpften Berichts. Die
Auswahl erscheint über Anderen Bericht wählen im Assistenten oder über
/patient-cards/new?select=1:
3. Assistent in sechs Schritten (/patient-cards/reports/{id}) – Schritt 1 Patient
identifizieren, 2 Patientendaten ergänzen, 3 Notfallkontakt, 4 Hausarzt, 5 Nachsorge und
Kontrolle, 6 Zusammenfassung und Bestätigung. In Schritt 2 stehen neben Adresse, Telefon und
Implantationsort auch die MRT-Tauglichkeit (Auswahl aus MRT-tauglich, MRT-bedingt
tauglich, nicht MRT-tauglich, unbekannt oder „nicht angegeben") und eine optionale
Zusatzangabe (max. 120 Zeichen) bereit; beide Angaben erscheinen auf Seite 1 des Ausweises:
Ohne JavaScript sind alle sechs Abschnitte gleichzeitig sichtbar und absendbar; mit JavaScript wird je Schritt umgeschaltet. Verbindlich ist immer die serverseitige Prüfung: bei Fehlern antwortet der Server mit HTTP 422 und springt zu dem Schritt, in dem der erste Fehler steht.
4. Zusammenfassung und zwei Bestätigungen – der Ausweis wird nur erzeugt, wenn beide Häkchen ausdrücklich gesetzt sind („Ja, dies ist der richtige Patient." und „Ich bestätige, dass die angezeigten Daten zum richtigen Patienten gehören und zusammengeführt werden dürfen."):
5. Konflikte – stimmen bereits gespeicherte Angaben nicht mit den neuen Eingaben überein, zeigt der Assistent beide Werte nebeneinander und verlangt für jedes Feld eine Entscheidung (neuer Wert oder gespeicherter Wert). Nichts wird stillschweigend überschrieben; ohne Entscheidung wird nichts gespeichert:
6. Ergebnis – Ausweisdetail mit Download von Ausweis-PDF und Ausgangsbericht, plus Ausweisübersicht, Patientensicht und Ausweisübersicht am Bericht:
Ein Patient wird ausschließlich über Nachname + Vorname + Geburtsdatum zugeordnet
(patients.identity_key). Seriennummer, Patienten-ID, Import-ID oder Berichts-ID dienen nie
als alleiniges Merkmal. Ergebnis: kein Treffer → neuer Patient, ein Treffer → dieser Patient
wird verwendet, mehrere Treffer → der Benutzer muss den Patienten ausdrücklich auswählen.
- Das PDF wird als Blob mit SHA-256 und Größe im Datensatz gespeichert und danach nur noch ausgeliefert – es wird nie neu berechnet.
- Jeder Ausweis verweist auf die beim Erzeugen gültige Stammdatenfassung. Spätere Änderungen an Logo, Nachsorgezentrum, Hinweistexten, Patientendaten oder weiteren Importen verändern bestehende Ausweise nicht.
- Für jeden Bericht können mehrere Ausweisfassungen existieren (
card_version); jede Fassung bleibt erhalten. Die Berichtsansicht listet alle Fassungen mit PDF-Link und Verlauf, die Patientensicht zusätzlich alle Nachsorgeuntersuchungen des Patienten (gespeist aus den unveränderlichen Bericht-Snapshots). - Dateiname:
Patientenausweis_<Nachname>_<Vorname>_<Datum>[_Nr<laufende Nummer>].pdf.
System → Ausweisvorlage bearbeiten (Kopf der Systemseite und Funktionsband System → Vorlagen) öffnet den Vorlageneditor in einem neuen Tab. Er ist derselbe Editor wie für die Briefe (docs/editor-referenz.md, Abschnitt 11): links der Aufbau mit den Bausteinen (Drag and Drop, ↑/↓, Ein-/Ausblenden), in der Mitte eine Seitenvorschau beider A4-Seiten mit Beispieldaten, rechts die Eigenschaften des gewählten Bausteins bzw. Bereichs. Ein Textbaustein lässt sich ergänzen; die Bausteine selbst sind fest an ihren Bereich gebunden (linke/rechte Spalte auf Seite 1, Abschlussblock am Fuß von Seite 1, Messwerttabelle und freie Texte auf Seite 2).
- Kopfbereich: Logo und Überschrift, Unterzeile und die Kopfzeile ab Seite 2.
- Fußzeile: Hinweis zur automatischen Erzeugung, Zusatzzeile mit Erstellungsdatum und
Ausweisfassung, Seitenangabe (
{page},{pages}). - Bausteine: Patientendaten, Notfallkontakt, Hausarzt, Nachsorgezentrum, Implantate, MRT-Tauglichkeit, Hinweise, Abschlussblock und Messwerttabelle – jeweils mit eigenen Überschriften, Beschriftungen und Optionen (z. B. Elektrodentabelle, Flugsicherheit, Barcode, Hinweise zur Messwerttabelle).
- Platzhalter: z. B.
{patient_name},{date_of_birth},{device_model},{center_name},{report_date},{card_version}; ein Klick fügt sie an der Cursorposition ein. - PDF-Vorschau erzeugt mit Beispieldaten ein PDF der ungespeicherten Vorlage.
- Speichern erzeugt eine neue, unveränderliche Fassung mit Änderungsnotiz; wurde die Vorlage zwischenzeitlich von jemand anderem geändert, meldet der Editor einen Konflikt und bietet die Fassungsliste zum Laden an. Unveränderter Inhalt wird als solcher gemeldet.
Vorlagenänderungen wirken nur auf neu erstellte Ausweise: Jeder Ausweis trägt die beim
Erzeugen gültige Fassung vollständig in seinem Snapshot und wird immer daraus gerendert.
Das Recht für den Editor heißt Ausweisvorlagen (Editor) (patient_card_templates);
vorgegeben haben es Admin und Arzt.
| Methode | Pfad | Zweck |
|---|---|---|
| GET | /patient-cards |
Übersicht mit Suche (Patient, Dateiname, Seriennummer) |
| GET | /patient-cards/new |
Bericht für einen neuen Ausweis wählen; mit aktivem Patienten direkt in den Assistenten (?select=1 erzwingt die Auswahl) |
| GET | /patient-cards/reports/{id} |
Assistent (Schritt über ?step=1..6) |
| POST | /patient-cards/reports/{id} |
Ausweis erzeugen (CSRF, beide Bestätigungen) |
| GET | /patient-cards/{id} |
Ausweisdetail mit Verlauf |
| GET | /patient-cards/{id}/pdf |
Ausweis-PDF (inline, ?download=1 als Download) |
| GET | /patient-cards/patients/{patient} |
Alle Ausweise und Nachsorgeuntersuchungen |
| GET | /patient-cards/settings |
Hinweistexte des Ausweises (Praxis-Informationen nur lesbar) |
| POST | /patient-cards/settings |
Hinweistexte speichern (neue Fassung) |
| GET | /patient-cards/settings/logo |
Hinterlegtes Logo ausliefern |
Seite 1: Kopfbereich mit Logo, Ausweistitel, Patientendaten (Identitätsangaben), Notfallkontakt,
Hausarzt, betreuendem Nachsorgezentrum, Implantate-/Elektroden-Tabellen (Modell, Impl.Ort bzw.
Lokalisation, Impl.Datum), MRT-Tauglichkeit (Auswahlwert, optional mit Zusatzangabe),
Hinweis- und Flugsicherheitstexten (deutsch/englisch) sowie dem
Abschlussblock „Sonstiges/Bemerkung/Arzt/Nächste Kontrolle in“ mit Code-39-Barcode der
Patient-ID. Aufbau, Reihenfolge und Beschriftungen folgen der Vorlage .reference/idcard_ann.png
(Schwarz auf Weiß, ohne rotes Achtung-Feld). Der Ausweis ist dauerhaft auf zwei Seiten
begrenzt: Seite 1 wird beim Erzeugen gegen die Seitengrenze geprüft, die MRT-Zusatzangabe wird
dafür bei Bedarf gekürzt; eine dritte Seite wird nie erzeugt.
Seite 2: Messwerttabelle der aktuellen Untersuchung und der bis zu sechs letzten früheren
Untersuchungen desselben Patienten. Aufbau, Reihenfolge und Beschriftungen der Zeilen folgen
1:1 der Vorlage config/patient_card_measurements.php; jede Wertspalte ist mit dem Datum der
zugehörigen Untersuchung überschrieben, die aktuelle Untersuchung steht immer in der ersten
Spalte und ist als solche gekennzeichnet. Die Werte stammen aus den unveränderlichen
Bericht-Snapshots der jeweiligen Untersuchung; Zellen ohne Wert bleiben leer. Die Zuordnung
von Merlin-Parametern (IDs und Bezeichnungen) zu den Zeilen der Vorlage ist zentral in dieser
Konfigurationsdatei hinterlegt. Reicht der Platz nicht, bricht die Erzeugung mit einer klaren
Meldung ab, statt Inhalte abzuschneiden; die Ausgabe erfolgt ausschließlich über den eigenen,
abhängigkeitsfreien PDF-Writer.
Der Brief wird automatisch aus der Patientenakte erzeugt – aus den versionierten Bausteinen Anamnese, Vormedikation, Befund und Epikrise, dem Baustein Aggregat direkt unter den Patientendaten (Gerätedaten des letzten Imports und die Sonden der Schrittmacher-/ICD-Abfrage, siehe Aggregat und Sonden), dem Befundteil der gewählten Untersuchung als Anhang unter der Grußformel (Baustein Berichte, immer auf einer neuen Seite), auf Wunsch der vollständigen Schrittmacher-/ICD-Abfrage als Anhang (Opt-in im Assistenten) und der Messwerttabelle auf der letzten Seite. Er enthält keine medizinische Bewertung und keine Diagnose; übernommen wird ausschließlich, was erfasst wurde.
Der Baustein Aggregat steht in der Standardvorlage direkt unter den Patientendaten und druckt die Daten des letzten Imports des Patienten:
| Angabe | Quelle |
|---|---|
| Hersteller | Geräteherkunft des letzten Berichts |
| Modell | Modell und Modellnummer (HSM Zweikammer … 407155) |
| Seriennummer | Seriennummer des Geräts |
| Status | immer „aktiv" (der Import kennt kein Statusfeld) |
| Implantationsdatum | Implantationsdatum des Geräts |
| Implantationsort | Implantationsort aus den Stammdaten |
| Betriebsart (intraoperativ) | programmierter Modus des letzten Imports |
Die Angaben stehen zweispaltig (links die Bezeichnung, rechts der Wert), damit der Block wenig Platz braucht. Darunter folgen die Sonden (Elektroden) derselben Abfrage – je Sonde eine Zeile mit Kammer, Hersteller, Modell, Seriennummer, Typ und Implantationsdatum. Fehlt ein Import, druckt der Block „nicht angegeben"; sind keine Sonden erfasst, erscheint ein entsprechender Hinweis. Beide Angaben sind im Vorlageneditor ein- und ausblendbar.
1. Patient wählen (/letters/new?select=1) – gesucht wird in Nachname, Vorname und
Anzeigename. Mit aktivem Patienten entfällt dieser Schritt: Brief erstellen öffnet den
Assistenten direkt für den aktiven Patienten; die Auswahl bleibt über Anderen Patienten wählen
erreichbar.
2. Bericht zuordnen (optional) – der Bericht liefert den Baustein Berichte (Befundteil des Berichts). Wird kein Bericht gewählt, entfällt dieser Anhang; der Brief wird trotzdem erzeugt. Der Assistent zeigt vorab, welche Bausteine fehlen:
3. Bausteine prüfen – je Textbaustein die aktuelle Fassung mit Fassungsnummer, erfassender Person und Zeitpunkt sowie die Hinweise, die der Brief enthalten wird. Die Checkbox Anhang mit der vollständigen Abfrage in diesen Brief aufnehmen ist nicht vorausgewählt (Opt-in); ohne sie enthält der Brief nur den Brieftext und die Messwerttabelle:
4. Messwerte wählen – Checkbox-Liste der Messwerte, die in der Tabelle auf der letzten Seite erscheinen (siehe Messwerttabelle auf der letzten Seite). Angeboten wird der feste Katalog der Briefvorlage, gegliedert nach Abschnitt und Gruppe; Zeilen ohne Wert in den Untersuchungen sind deaktiviert und als „kein Wert in den Untersuchungen" gekennzeichnet. Die Checkbox Messwerttabelle auf der letzten Seite mitdrucken blendet die Seite für diesen Brief ab. Mindestens ein Messwert ist Pflicht, solange Werte vorliegen – sonst antwortet der Server mit HTTP 422 und öffnet Schritt 4. Wird die Auswahl geändert, fragt der Assistent „Die Messwertauswahl wurde geändert. Einstellung merken?" – mit Ja, als Standard merken gilt die Auswahl applikationsweit für alle künftigen Briefe vorausgewählt (siehe Messwertauswahl als Standard); ohne JavaScript übernimmt das die Checkbox Diese Messwertauswahl als Standard für alle künftigen Briefe merken.
5. Empfänger wählen – Checkboxen für Patient, Hausarzt und Überweisender Arzt mit der Anschrift aus den Stammdaten. Für jeden gewählten Empfänger entsteht ein eigener Brief (eigene Briefnummer, Dokumentnummer und PDF) mit dessen Anschrift im Anschriftfeld, seiner Anrede aus den Stammdaten und der Vorlage seiner Empfängerart; Inhalt und Datengrundlage sind gleich. Wählbar ist ein Empfänger mit Name oder Praxis, PLZ und Ort (die Straße ist optional); fehlt etwas, nennt der Assistent die fehlenden Angaben und verlinkt die Stammdaten. Ist keine Anrede gepflegt, weist er darauf hin, dass der Brief „Sehr geehrte Damen und Herren,“ druckt. Vorausgewählt sind die Ärzte mit vollständiger Anschrift, der Patient nur auf Wunsch. Mindestens ein Empfänger ist Pflicht.
Hat weder der Hausarzt noch der Überweisende Arzt eine Anschrift in den Stammdaten, erscheint eine weitere Checkbox Arztbrief generisch erstellen (vorausgewählt). Der Brief geht dann an „An die weiterbehandelnden Ärztinnen und Ärzte" und wird mit „Sehr geehrte Kollegin, sehr geehrter Kollege," angeredet; er nutzt die eigene Vorlage Arztbrief generisch mit eigenem Fassungsverlauf. Sobald eine Arztanschrift gepflegt ist, verschwindet die Checkbox wieder – die generische Anschrift ist nur der Ersatz für einen fehlenden Empfänger.
6. Zusammenfassung – Patient, Briefnummer(n), Empfänger, Baustein Berichte, Textteile, Anhang, Messwerttabelle und MRT-Tauglichkeit vor dem Erzeugen:
7. Bestätigen und erzeugen – ohne beide Bestätigungen wird kein Brief gespeichert. Alle Briefe eines Vorgangs werden in einer Transaktion gespeichert; bei einem Brief führt die Anwendung auf dessen Detailseite, bei mehreren auf die Briefe des Patienten. Bei fehlender Bestätigung antwortet der Server mit HTTP 422 und zeigt die Meldungen am Feld:
Die Detailansicht zeigt Dokumentnummer, Briefdatum, Erstellungszeitpunkt, den zugeordneten Bericht, den Anhang, die eingefrorene Stammdaten- und Patientenfassung, Dateiname, Größe und SHA-256 des PDF, die eingefrorenen Bausteinfassungen, den Brieftext, den Baustein Berichte (Befundteil des Berichts, im PDF als Anhang unter der Grußformel auf einer neuen Seite) und die vollständige Abfragetabelle. Der Brief ist unveränderlich: Snapshot und PDF werden in einer Transaktion gespeichert und nie überschrieben; das PDF ist allein aus dem Snapshot reproduzierbar.
Der Brief erscheint sowohl in der Briefübersicht als auch in der Akte des Patienten; die Spalte Empfänger zeigt, an wen er gerichtet ist. Eine Neuausfertigung behält den Empfänger:
Die letzte Seite des Briefes zeigt die Messwerte als Tabelle: erste Spalte sind die
Messwertbezeichner, erste Zeile die Untersuchungsdaten. Gibt es noch keine frühere
Untersuchung des Patienten, steht die heutige Untersuchung in der zweiten Spalte. Gibt es frühere
Untersuchungen, stehen links davon die letzten N Untersuchungen, die des aktuellen Datums folgt
ganz rechts; unter ihrem Datum steht der Zusatz „aktuelle Untersuchung". N ist in der
Briefvorlage einstellbar (Standard 4, Werte 0–10; Zone measurement_table, Option previous).
Die Seite wird immer auf einer neuen Seite am Ende des Briefes gedruckt – nach dem Anhang.
Welche Messwerte erscheinen, wird beim Erstellen des Briefes in Schritt 4 per Checkbox-Liste
aus dem festen Katalog der Vorlage (derselbe Katalog wie im Patientenausweis,
MeasurementTemplate) angewählt; angeboten werden nur Werte, die in den Untersuchungen des
Patienten tatsächlich vorliegen (übrige Zeilen sind deaktiviert). Die Auswahl wird mit dem Brief
eingefroren (snapshot.measurements): eine geänderte Auswahl ergibt einen neuen Brief mit
neuer Briefnummer – der bestehende Brief bleibt unverändert. Liegen für den Patienten keine
Messwerte vor, druckt die Seite den Hinweis der Vorlage; mit abgewählter Checkbox bzw. show: false in der Vorlage entfällt sie vollständig. Die Kopftexte (Überschrift, Einleitung,
Spaltenkopf, Zusatz zur aktuellen Untersuchung, Hinweis ohne Messwerte) pflegt der
Vorlageneditor. Briefe der Fassungen 1 und 2 haben keine
Messwerttabelle und bleiben unverändert.
Die Messwertauswahl lässt sich applikationsweit als Standard speichern (Tabelle
letter_measurement_defaults, genau eine Zeile). Nach einer Änderung der Auswahl in Schritt 4
erscheint ein Overlay mit der Frage „Die Messwertauswahl wurde geändert. Einstellung merken?":
- Ja, als Standard merken – die angekreuzten Messwerte sind in allen künftig erstellten Briefen vorausgewählt.
- Nein, nur für diesen Brief – die Auswahl gilt nur für den aktuellen Brief; ein bestehender Standard bleibt unverändert.
Gespeichert werden die Schlüssel des festen Katalogs. Ein neuer Brief wählt den Standard vor, soweit die Werte in den Untersuchungen des Patienten vorliegen; bleibt davon nichts übrig (oder ist kein Standard gespeichert), sind wie bisher alle verfügbaren Messwerte vorausgewählt. Der Standard ändert keinen bestehenden Brief: jeder Brief friert seine eigene Auswahl ein. Wird der Brief nicht erzeugt (z. B. fehlende Bestätigung), bleibt der Standard unverändert – er wird in derselben Transaktion wie der Brief geschrieben. Ohne JavaScript bleibt die Checkbox im Schritt 4 sichtbar und wird beim Absenden ausgewertet.
Briefe ab Fassung 2 werden nach DIN 5008, Form B gesetzt: Briefkopf 45 mm, Anschriftfeld
85 × 45 mm ab 45 mm von oben (Schrift ab 25 mm links) mit Rücksendeangabe in der Zusatz- und
Vermerkzone, Informationsblock ab 125 mm links und 50 mm oben (Unser Zeichen, Patient,
Geburtsdatum, Patienten-ID, Brief-Nr., Stammdatenfassung, Datum – je Zeile per Häkchen in der
Vorlage ein- und ausblendbar), Betreff fett ohne das Wort „Betreff", Anrede, Brieftext,
Grußformel, darunter die Unterschrift der angemeldeten Person (max. 60 × 22 mm) mit Titel,
Vorname und Name; Falzmarken bei 105 mm und 210 mm sowie Lochmarke bei 148,5 mm; linker Rand
25 mm, rechter Rand 20 mm; Folgeseiten mit Kurzkopf und Seitenangabe. Inhalt, Reihenfolge und
alle festen Texte bestimmt die Briefvorlage, deren
Fassung im Snapshot des Briefes eingefroren wird. Briefkopf (Praxisname, Anschrift, optionale
Kontaktzeile mit Telefon, Fax, E-Mail und Internetseite) und Rücksendeangabe stammen aus den
Praxis-Informationen (/system/settings, Reiter System unter Praxis) und sind im
Snapshot des Briefes eingefroren.
Briefe der Fassung 1 (vor Einführung der Vorlagen) behalten ihren damaligen Aufbau:
Seite 1: Kopfbereich mit Logo und Nachsorgezentrum, Titel „Brief zur Schrittmacher-/ICD-Abfrage", Dokumentnummer, Briefdatum und Stammdatenfassung, Patientendaten (Name, Geburtsdatum, Patienten-ID, Anschrift), Anrede, die Textteile Anamnese, Vormedikation und Epikrise (jeweils mit Fassungsnummer, erfassender Person und Zeitpunkt), der Befundteil „Schrittmacher-/ICD-Abfrage" und die Grußformel.
Ab Seite 2: Anhang „Schrittmacher-/ICD-Abfrage (vollständige Tabelle)" mit Kopfzeile auf jeder Seite – Geräteart, Anzahl angegebener Werte und Herkunft der MRT-Tauglichkeit, danach die Abschnitte Gerät, Messdaten (Batterie, Sonden mit Impedanz, Wahrnehmung, Reizschwelle und Schockimpedanz), Programmierung (Bradykardie, RA, RV, AV, LV), bei ICD/CRT-D zusätzlich Tachykardie (VT1, VT2, VF mit Erkennung und Therapie) sowie Bemerkungen. Die MRT-Tauglichkeit stammt aus dem neuesten Patientenausweis; fehlt sie dort, wird die Angabe der Abfrage gedruckt.
Die folgenden Seiten zeigen einen Brief der Fassung 2, erzeugt mit der Standardvorlage: Seite 1 mit den Textteilen Anamnese, Vormedikation, Befund und Epikrise, Seite 2 mit der Grußformel, Seite 3 der Anhang Berichte (Befundteil des zugeordneten Berichts) und Seite 4 der Anhang mit der vollständigen Abfragetabelle. Briefe ab Fassung 3 führen zusätzlich die Messwerttabelle als letzte Seite (im Bild oben noch nicht enthalten).
Jede Seite trägt die Fußzeile „Automatisch erzeugter Brief auf Basis der Patientenakte – keine
medizinische Bewertung oder Diagnose.", den Erstellungszeitpunkt mit Dokumentnummer und
Brief-Fassung sowie „Seite n von m". Die Dokumentnummer hat die Form
HSM2Med-Brief-<JJJJMMTT>-<Patienten-ID>-<laufende Nummer>.
| Methode | Pfad | Zweck |
|---|---|---|
| GET | /letters |
Übersicht mit Suche (Patient, Patienten-ID, Bericht-Nr.) |
| GET | /letters/new |
Patient für einen neuen Brief wählen |
| GET | /letters/new?patient={id}&report={id} |
Assistent (Schritte 2–7) |
| POST | /letters |
Briefe erzeugen, je Eintrag in recipients[] (patient, family_doctor, referring_physician, generic) einer; Felder include_appendix, include_measurements und measurements[]/measurements_present (CSRF, beide Bestätigungen) |
| GET | /letters/patients/{patient} |
Alle Briefe eines Patienten |
| GET | /letters/{id} |
Briefdetail mit Snapshot |
| GET | /letters/{id}/pdf |
Brief-PDF (inline, ?download=1 als Download) |
| GET | /letters/{id}/reproduce |
PDF erneut aus dem Snapshot mit der damaligen Vorlage erzeugen (nichts wird gespeichert) |
| POST | /letters/{id}/regenerate |
Neuausfertigung als neuer Brief: template=original oder template=current (nur mit confirm_current_template=1) |
| GET | /system/settings |
Praxis-Informationen und Rücksendeangaben für Briefe und Ausweise |
| POST | /system/settings |
Praxis-Informationen speichern (neue Fassung, CSRF) |
| GET | /system/settings/logo |
Hinterlegtes Logo ausliefern |
| GET | /system/letter-templates |
Vorlageneditor (eigener Tab), `?type=patient |
| POST | /system/letter-templates |
Vorlage als neue Fassung speichern (JSON-Antwort, CSRF; Feld type) |
| POST | /system/letter-templates/preview |
PDF-Vorschau einer ungespeicherten Vorlage mit Beispieldaten |
| GET | /system/letter-templates/source |
Aktuelle Vorlage einer Empfängerart als JSON (Bausteine übernehmen) |
| GET | /system/letter-templates/versions/{id} |
Gespeicherte Fassung als JSON (zum Laden in den Editor) |
| GET | /system/patient-card-templates |
Vorlageneditor des Patientenausweises (eigener Tab) |
| POST | /system/patient-card-templates |
Ausweisvorlage als neue Fassung speichern (JSON-Antwort, CSRF) |
| POST | /system/patient-card-templates/preview |
PDF-Vorschau einer ungespeicherten Ausweisvorlage mit Beispieldaten |
| GET | /system/patient-card-templates/versions/{id} |
Gespeicherte Fassung der Ausweisvorlage als JSON |
Vorlagen werden je Empfängerart getrennt gepflegt: Patient, Hausarzt, Überweisender Arzt und Arztbrief generisch haben jeweils eine eigene Standardvorlage mit eigenem Fassungsverlauf. Die Art wird im Funktionsband gewählt; ein Wechsel fragt bei ungespeicherten Änderungen nach. Einzelne Bausteine lassen sich per Rechtsklick auf einen Baustein über Übernehmen aus Vorlage → Vorlagenname aus der Vorlage einer anderen Art übernehmen (eindeutige Bausteine ersetzen den gleichartigen, freie Textbausteine werden angehängt; wirksam wird das erst beim Speichern).
Die Anrede steht nicht in der Vorlage, sondern in den Stammdaten des Empfängers; die
Vorlage enthält dafür den Platzhalter {salutation}. Beim Anlegen bzw. Bearbeiten der
Stammdaten wird sie je Empfänger gepflegt:
| Empfänger | Auswahl | Anrede im Brief |
|---|---|---|
| Patient | Herr, Frau, Divers | Sehr geehrter Herr Mustermann, · Sehr geehrte Frau Mustermann, · Guten Tag Mustermann, Erika, |
| Hausarzt / Überweisender Arzt | Kollege, Kollegin, Unpersönlich (Praxis/Klinik) | Sehr geehrter Herr Kollege, · Sehr geehrte Frau Kollegin, · Sehr geehrte Damen und Herren, |
| Arztbrief generisch | keine Angabe (feste Anrede) | Sehr geehrte Kollegin, sehr geehrter Kollege, |
Ohne Angabe (oder bei fehlendem Nachnamen) druckt der Brief „Sehr geehrte Damen und Herren,“; der Brief-Assistent weist vor dem Erzeugen darauf hin.
Der Baustein Unterschrift steht in der Standardvorlage direkt unter der Grußformel. Er
druckt die Unterschrift der angemeldeten Person (gepflegt unter Unterschrift in der
Statusleiste bzw. in der Benutzerverwaltung), automatisch auf 60 × 22 mm skaliert, und darunter
immer Titel, Vorname und Name ({signer_name}). Beim Erzeugen werden Bild und Name im Brief
eingefroren; ein späterer Wechsel der Unterschrift ändert bereits erstellte Briefe nicht.
Der Editor öffnet sich über System → Briefvorlage bearbeiten in einem neuen Tab und folgt der Office-Oberfläche der Anwendung (Menüband, Statusleiste):
- Aufbau (links): Name der Vorlage, feste Bereiche nach DIN 5008 (Briefkopf,
Rücksendeangabe, Anschriftfeld, Informationsblock, Fußzeile und Seitenränder, Anhang,
Messwerttabelle) und die Bausteine des Brieftextes. Bausteine werden per Drag and Drop (am Griff, in der Liste oder
direkt auf der Seitenvorschau), mit den Pfeil-Schaltflächen oder mit
Alt+↑/↓verschoben und per Häkchen ein- oder ausgeblendet. Eigene Textbausteine (Überschrift + Text) lassen sich hinzufügen und löschen. - Seitenvorschau (Mitte): maßstabsgetreue A4-Seite mit Beispieldaten; ein Klick wählt den Bereich bzw. Baustein.
- Eigenschaften (rechts): alle festen Texte des gewählten Bereichs bzw. Bausteins,
Optionen (z. B. Falzmarken; Empfängertext für Briefe ohne Empfängerauswahl; je Zeile des
Informationsblocks ein Häkchen zum Ein- und Ausblenden; Schriftgröße je Baustein in den
Stufen Klein, Normal, Groß, Sehr groß) und Platzhalter wie
{salutation},{patient_name},{date_of_birth},{document_number},{letter_date};{page}und{pages}nur in der Seitenangabe. „Standard" setzt einen Text zurück. Für die Patientendaten und das Aggregat lässt sich je Zeile wählen, ob die Beschriftung (z. B. Name), der Wert, beides oder nichts fett gedruckt wird; Überschriften bleiben immer fett.
Versionierung: Als neue Fassung speichern legt eine neue, unveränderliche Fassung der gewählten Art an (optional mit Änderungsnotiz); neue Briefe an Empfänger dieser Art verwenden ab dann diese Fassung. Unter Fassungen lassen sich frühere Fassungen laden und als neue Fassung wiederherstellen. Gleichzeitige Bearbeitung wird erkannt (Speichern auf veralteter Grundlage wird abgelehnt).
Historische Briefe: Jeder Brief speichert die vollständige Vorlage im Snapshot. In der Briefdetailansicht kann das PDF jederzeit mit der damaligen Vorlage reproduziert werden (identisch zum gespeicherten PDF). Eine Neuausfertigung erzeugt einen neuen Brief aus derselben Datengrundlage – standardmäßig mit der ursprünglichen Vorlage, nur nach ausdrücklicher Bestätigung (Opt-in) mit der aktuellen Vorlage. Der Ausgangsbrief bleibt unverändert; die Neuausfertigung verweist im Informationsblock auf ihn.
Aufbau, Datenmodell, Regeln und Erweiterungspunkte des Editors für Entwickler und Agenten: docs/editor-referenz.md.
Alle Befehle laufen im Web-Container. Als Benutzer www-data ausführen, damit Archiv- und
Protokolldateien die richtigen Eigentümer erhalten. Dateien müssen im Container liegen:
# Import (Datei zuerst in den Container kopieren)
docker compose cp ./MERLIN_export.log web:/tmp/export.log
docker compose exec -u www-data web php bin/import.php /tmp/export.log --dry-run # nur prüfen
docker compose exec -u www-data web php bin/import.php /tmp/export.log # importieren
docker compose exec -u www-data web php bin/import.php /tmp/export.log --force # Dublette erzwingen
# Import eines Biotronik-XML-Exports (Format wird automatisch erkannt)
docker compose cp ./BIOIEEE_export.xml web:/tmp/BIOIEEE_export.xml
docker compose exec -u www-data web php bin/import.php /tmp/BIOIEEE_export.xml --dry-run
# PDF-Export eines gespeicherten Berichts (nur aus der Datenbank)
docker compose exec -u www-data web php bin/export-pdf.php 1 /tmp/bericht-1.pdf --raw
docker compose cp web:/tmp/bericht-1.pdf ./bericht-1.pdf
# Migrationen manuell ausführen
docker compose exec -u www-data web php bin/migrate.php
# Administrator anlegen bzw. Kennwort setzen (idempotent)
docker compose exec -u www-data web php bin/seed-admin.php
docker compose exec -u www-data web php bin/seed-admin.php --force # Kennwort neu setzen
docker compose exec -u www-data web php bin/seed-admin.php --wait=30 # auf die Datenbank wartenExit-Codes import.php: 0 Erfolg, 1 Import fehlgeschlagen, 2 Aufruffehler, 3 Dublette.
seed-admin.php legt die Gruppe Admin (alle Rechte), die Standardgruppen MFA und Arzt
sowie den Benutzer aus ADMIN_USERNAME/ADMIN_PASSWORD an. Ohne --force bleibt ein bereits
vorhandenes Kennwort unverändert; der Benutzer wird dann nur in die Gruppe Admin aufgenommen.
In production verweigert das Skript (Exit-Code 1) das Anlegen bzw. Zurücksetzen mit dem
Vorgabekennwort.
Merlin-Exportdateien bestehen aus Datensätzen mit vier Feldern, die jeweils mit dem Steuerzeichen 0x1C (ASCII File Separator, „FS“) abgeschlossen werden:
<Parameter-ID>FS<Bezeichnung>FS<Wert>FS<Einheit>FS<Zeilenumbruch>
Beispiel (FS als ␜ dargestellt):
202␜Device Serial Number␜5809481␜␜
302␜Base Rate␜60␜bpm␜
2431␜Patient Date of Birth␜10/21/1938 00:00:00␜␜
- Parameter-IDs sind Ziffernfolgen des Merlin-Quellformats. Sie sind keine
standardisierten Codes (z. B. IEEE 11073 oder LOINC); eine Zuordnung zu Standards wird
nicht behauptet und nur bei expliziter Dokumentation in
standard_codeshinterlegt. - Datumswerte liegen im US-Format
MM/DD/YYYY hh:mm:ssvor. Angezeigt wird zusätzlichTT.MM.JJJJ; gespeichert bleibt immer der Originaltext. - Unterstützte Kodierungen: UTF-8 (mit/ohne BOM), UTF-16 mit BOM, sonst Windows-1252 bzw. ISO-8859-1 (Fallback mit Warnung). Zeilenumbrüche CRLF, LF und CR werden akzeptiert.
XML-Exporte des Herstellers Biotronik (creator="BioICSConverter", Dateiname beginnt mit
BIOIEEE_) werden ebenfalls importiert. Der Parser wird automatisch anhand von Inhalt und
Endung gewählt.
<biotronik-ieee11073-export creator="BioICSConverter" format-version="4.10">
<dataset>
<section name="MDC">
<section name="IDC">
<section name="DEV">
<value code="720898" name="MODEL" type="ST">Enticos 4 SR</value>
<value code="730880" name="LOWRATE" type="NM" unit="{beats}/min">60</value>
</section>
</section>
</section>
</dataset>
</biotronik-ieee11073-export>- Jeder
<value>-Knoten ergibt einen Datensatz. Der Inhalt ist der Wert (nur außenliegende Leerzeichen werden entfernt),unitdie Einheit,namedie Bezeichnung. - Als Parameter-ID dient das Attribut
code(z. B.730880). Fehlt es (typisch für die Patientendaten), wird eine stabile Ersatz-ID aus dem Abschnittspfad gebildet (z. B.MDC.ATTR.PT.DOB); darauf weist die Warnungmissing_value_codehin. - Der Abschnittspfad wird zusätzlich als
sectiongespeichert. Sonden (Leads) werden über die inconfig/parameter_mapping.phphinterlegtenlead_sectionserkannt. - Datumswerte liegen im IEEE-Format
YYYYMMDDThhmmss[±hhmm]vor (z. B.20261008T123829+0200). Der Originaltext bleibt gespeichert; eine Zeitzone wird nicht umgerechnet. - Die Werte sind keine standardisierten IEEE-11073-Codes;
standard_codesbleibt leer. - Eine eingebettete XML-Signatur (
<Signature>) wird ignoriert.
- Getrennt wird ausschließlich an 0x1C; Tabulatoren, Semikolons, Kommas usw. sind
normale Zeichen. Werte werden nicht gerundet, konvertiert oder getrimmt (lediglich
Zeilenumbrüche vor der ID werden entfernt). Für XML-Exporte gilt die Trennerprüfung nicht;
dort werden ausschließlich die
<value>-Knoten ausgewertet. - Leere Werte und Einheiten bleiben leere Zeichenketten (niemals
NULLoder0). - Unbekannte Parameter werden vollständig übernommen (Kategorie „Sonstige / Nicht kategorisiert“).
- Mehrfach vorkommende IDs werden alle gespeichert (Warnung
duplicate_parameter_id). - Fehlerhafte Datensätze werden protokolliert und übersprungen; gültige Datensätze werden
importiert (Status
completed_with_errors).
| Code | Art | Bedeutung |
|---|---|---|
empty_file |
Fehler (blockiert) | Datei ist leer |
no_separator |
Fehler (blockiert) | Kein 0x1C enthalten – kein Merlin-Export |
invalid_parameter_id |
Fehler | ID ist keine Ziffernfolge – Datensatz übersprungen |
incomplete_record |
Fehler | Datensatz hat weniger als vier Felder |
unterminated_record |
Fehler | Unvollständiger Rest am Dateiende |
missing_final_separator |
Warnung | Letzter Trenner fehlt |
empty_parameter_name |
Warnung | Leere Bezeichnung |
line_break_in_field, nul_in_field |
Warnung | Steuerzeichen im Feld |
encoding_fallback |
Warnung | Kein gültiges UTF-8, Fallback-Kodierung genutzt |
duplicate_parameter_id |
Warnung | ID mehrfach vorhanden |
missing_device_serial, missing_patient, unparsed_date |
Warnung | Stammdaten fehlen bzw. Datum nicht erkannt |
invalid_xml |
Fehler (blockiert) | XML nicht lesbar (Biotronik) |
unexpected_root |
Fehler (blockiert) | Wurzelelement ist nicht <biotronik-ieee11073-export> |
no_values |
Fehler (blockiert) | XML enthält keine <value>-Knoten |
missing_value_name |
Fehler | <value> ohne Attribut name – Datensatz übersprungen |
missing_value_code |
Warnung | <value> ohne Attribut code – Ersatz-ID aus dem Abschnittspfad |
Importstatus: completed, completed_with_warnings, completed_with_errors, failed.
Fehlgeschlagene Importe werden mit Grund im Importprotokoll gespeichert.
erDiagram
imports ||--o| reports : erzeugt
imports ||--o{ import_errors : protokolliert
patients ||--o{ devices : besitzt
patients ||--o{ reports : betrifft
patients ||--o{ patient_records : fuehrt
patient_records ||--o{ patient_record_versions : fasst
devices ||--o{ leads : hat
devices ||--o{ reports : betrifft
reports ||--o{ report_leads : verweist
leads ||--o{ report_leads : zugeordnet
reports ||--o{ report_parameters : enthaelt
parameter_definitions ||--o{ report_parameters : beschreibt
patients ||--o{ patient_cards : erhaelt
patients ||--o{ patient_letters : erhaelt
reports ||--o{ patient_letters : befundteil
patient_card_settings_versions ||--o{ patient_letters : stammdaten
- reports enthält Snapshots aller Kopfdaten (Patient, Gerät, Zeitstempel als Original
und normalisiert) sowie
summary_snapshot(JSON) mit Patienten-, Geräte- und Sondendaten zum Importzeitpunkt. - report_parameters speichert jeden Datensatz unverändert (ID, Bezeichnung, Wert, Einheit, Rohdatensatz, Position) inklusive der Kategorie zum Importzeitpunkt.
- patients/devices/leads sind Stammdaten zur Verknüpfung; sie werden nur ergänzt, nie überschrieben. Historische Berichte verwenden ausschließlich ihre Snapshots.
- patient_records ist der Behälter je Patient und Bausteintyp (genau einer je
Kombination,
UNIQUE (patient_id, record_type)); der Inhalt liegt ausschließlich in patient_record_versions. Jede Änderung erzeugt dort eine neue Fassung mit fortlaufenderversion,content(JSON),content_text(Textfassung für Anzeige und spätere Verwendung) undcontent_hash(SHA-256 des kanonischen JSON, verhindert inhaltsgleiche neue Fassungen). Frühere Fassungen werden nie überschrieben oder gelöscht. - Alle Fremdschlüssel verwenden
ON DELETE RESTRICT; Berichte werden nicht gelöscht. - Jeder Bericht speichert
report_version,parser_versionundmapping_version. Die PDF-Erzeugung wählt das Layout anhand derreport_version. Eine Änderung der Zuordnung erzeugt eine neuemapping_versionund betrifft nur künftige Importe. parameter_countwird beim Laden geprüft: Abweichungen führen zu einem Fehler statt zu einem unvollständigen Bericht.- Schemaänderungen erfolgen ausschließlich über neue Dateien in
database/migrations/(eine Prüfsumme verhindert nachträgliche Änderungen angewendeter Migrationen); danachphp bin/build-schema.phpausführen. - Ausweise speichern ihre Layoutfassung als
card_versionim Snapshot (aktuell 2). Die Messwerttabelle wird beim Erzeugen aus den Bericht-Snapshots aufgelöst und mitgespeichert; spätere Änderungen anconfig/patient_card_measurements.phpbetreffen nur neue Ausweise. - patient_letters ist ein unveränderliches Dokument:
snapshot(JSON) friert Patientendaten, die Fassung der globalen Stammdaten (settings_version_id), die Fassungen der Bausteine (Anamnese, Vormedikation, Befund, Epikrise, Schrittmacher-/ICD-Abfrage), die Befunddaten des gewählten Berichts, die aufgelösten Anhangsabschnitte, das Aggregat samt Sonden (aggregat: Gerätedaten und Elektroden des letzten Imports), die Unterschrift samt Namen der unterzeichnenden Person (signature) sowie die Messwerttabelle (measurements: Spalten, Zeilen und die getroffene Auswahl) ein.pdf_content(MEDIUMBLOB) undpdf_sha256werden gemeinsam mit dem Snapshot in einer Transaktion gespeichert und nie überschrieben; das PDF ist allein aus dem Snapshot reproduzierbar.report_idist optional (NULL= kein Baustein „Berichte").sequence_noist die laufende Nummer je Patient,letter_versiondie Fassung je Patient und Bericht (UNIQUE (patient_id, sequence_no)). - letter_measurement_defaults ist der applikationsweite Standard der Messwertauswahl für
Briefe: genau eine Zeile (
id = 1) mit der JSON-Liste der Katalogschlüssel. Der Standard wird nur geschrieben, wenn der Anwender ihn beim Erzeugen bestätigt (remember_measurements), und dient ausschließlich der Vorauswahl im Assistenten – jeder Brief friert seine eigene Auswahl ein. - letter_template_versions ist der unveränderliche Fassungsverlauf der Briefvorlagen. Jede
Empfängerart (
template_type:patient,family_doctor,referring_physician,generic) hat ihren eigenen Verlauf (UNIQUE (template_type, version_no)); jede Speicherung fügt eine neue Fassung ein. Jeder Brief friert die Vorlage seiner Empfängerart samt Anrede (snapshot.recipient.salutation) ein. Die Anrede selbst steht inpatient_card_master_data(salutation,physician_salutation,referrer_salutation); der generische Arztbrief (generic) hat keine Anrede in den Stammdaten und wird fest angeredet. - user_signatures speichert die Unterschriften der Benutzer unveränderlich: Bildinhalt
(
content),mime_type,filename, Abmessungen undsha256(eindeutig, gleiche Datei wird nur einmal abgelegt).users.signature_idverweist auf die aktuelle Unterschrift (ON DELETE SET NULL);users.title,users.first_nameundusers.last_nameliefern den Namen unter der Unterschrift. Ein Brief friertsnapshot.signature(id,sha256,filename,name) ein und lädt die Bilddaten über die unveränderliche Kennung bzw. ersatzweise über die Prüfsumme; deshalb bleibt die Datei auch nach dem Entfernen der Unterschrift erhalten.
| Seite 1 | Seite 2 |
|---|---|
![]() |
![]() |
- Inhalt: Titel mit Hinweis „keine medizinische Bewertung“, Berichtsdaten, Patient, Gerät, Sonden, alle Parameter nach Kategorien (ID, Parameter, Wert, Einheit), Importprotokoll, optional Rohdatenanhang (Originaldatensätze in Dateireihenfolge, fehlerhafte markiert).
- Kopfzeile ab Seite 2 mit Bericht, Patient und Gerät; Fußzeile mit „Seite X von Y“, Erstellungszeitpunkt und Berichtsversion; Tabellenköpfe werden auf Folgeseiten wiederholt.
- Zeichen außerhalb von Windows-1252 werden als
[U+XXXX], Steuerzeichen als[0xNN]dargestellt – nichts wird stillschweigend entfernt. - Erzeugung ausschließlich aus der Datenbank; die Originaldatei wird nicht benötigt.
- Dateiname:
Bericht_<Nr>_<Datum>_SN<Seriennummer>.pdf. - Aufruf:
/reports/<id>/pdf(?raw=1mit Rohdatenanhang,?download=1als Download).
Die Zuordnung steht in config/parameter_mapping.php:
categories– Kategorien mit Bezeichnung und Sortierung.by_id– Zuordnung Parameter-ID → Kategorie (höchste Priorität; Merlin-IDs und Biotronik-code-Werte wie730880).by_name/name_patterns– Zuordnung über Bezeichnung bzw. reguläre Ausdrücke.fields/lead_fields– welche IDs bzw. Namen Kopf- und Sondendaten liefern (combinesetzt Werte aus mehreren Feldern zusammen, z. B. den Patientennamen).lead_sections– Abschnittspfade, die im Biotronik-XML je eine Sonde beschreiben.standard_codes– nur explizit dokumentierte Zuordnungen zu Standardcodes (leer).
Nach jeder inhaltlichen Änderung version erhöhen und den Web-Container neu bauen
(docker compose up -d --build). Bestehende Berichte bleiben unverändert.
Seite 2 des Ausweises zeigt die Messwerte der aktuellen Untersuchung und der bis zu sechs
letzten früheren Untersuchungen desselben Patienten. Aufbau, Reihenfolge und Beschriftungen
der Zeilen stehen 1:1 in config/patient_card_measurements.php:
version– Fassung der Vorlage; bei inhaltlichen Änderungen erhöhen.columns– Anzahl der Spalten (7 = aktuelle Untersuchung + sechs frühere).sections→groups→rows– Abschnitte, Gruppen und Zeilen der Tabelle.label– Beschriftung der Zeile (Einheit in der Beschriftung, z. B.Spannung [V]).chamber– optionale Kammerangabe (RA,RV) in der Spalte vor der Beschriftung.sources– woher der Wert kommt:ids(Merlin-Parameter-IDs, haben Vorrang) und/odernames(Parameterbezeichnungen, exakt, Groß-/Kleinschreibung egal). Mehrere Quellen werden mitglueverbunden, z. B. Amplitude und Pulsbreite zu0.5/0.4.
Zeilen ohne Quelle und Zellen ohne Wert bleiben leer – es werden keine Werte erfunden. Bereits erzeugte Ausweise bleiben unverändert, weil die aufgelöste Tabelle im Snapshot des Ausweises gespeichert ist. Eine bestehende Zeile ohne Treffer im Bericht kann unverändert bleiben; fehlt der Parameter im Merlin-Export, bleibt die Zelle leer, bis ein Bericht den Wert enthält.
Abschnitte, Felder und die Vorbelegung der Abfrage stehen 1:1 in
config/device_check_template.php:
version– Fassung der Vorlage; bei inhaltlichen Änderungen erhöhen. Die Fassung wird im Inhalt jeder gespeicherten Abfrage mitgeführt, ältere Fassungen bleiben dadurch unverändert.device_types– Gerätetypen in der Reihenfolge des Formulars (pacemaker,icd,crt_p,crt_d).lead_fields– Spalten der Sondentabelle (key,label, optionaloptions,type,maxlengthunddevices).sections→groups→fields– Abschnitte, Gruppen und Felder (key,label, optionaloptions,type=date,maxlength,devices).devices– für welche Gerätetypen ein Abschnitt oder Feld gilt; fehlt die Angabe, gilt der Eintrag für alle. Nicht zutreffende Werte werden weder gespeichert noch ausgegeben.sources– Vorbelegung aus dem Bericht wie inconfig/patient_card_measurements.php(idshaben Vorrang vornames, mehrere Quellen werden mitglueverbunden).from_summary– Vorbelegung aus einer Zeile der Berichtszusammenfassung (<Abschnitt>.<Zeile>), z. B.device.Modell.from_card– Feld wird aus dem neuesten Patientenausweis übernommen und dort gepflegt (aktuellmrt_compatibilityundmrt_compatibility_note).
Feldwerte sind Freitext mit der Einheit in der Beschriftung; es findet keine Zahleninterpretation und keine medizinische Bewertung statt. Bereits gespeicherte Abfragen bleiben unverändert, weil jede Fassung ihren Inhalt als Snapshot speichert.
- Netz: standardmäßig nur an
127.0.0.1gebunden; Datenbank nur im internen Netz. Für Zugriff im Praxisnetz einen Reverse-Proxy mit TLS vorschalten undSESSION_SECURE_COOKIE=1setzen. - Anmeldung und Rechte: Jede Seite außer
/healthsetzt eine Anmeldung voraus; ohne Anmeldung erscheint die Oberfläche gesperrt hinter dem Anmeldefenster. Kennwörter werden ausschließlich als Hash (bcrypt) gespeichert und sind nicht auslesbar. Nach fünf Fehlanmeldungen wird ein Konto 15 Minuten gesperrt; unbekannte Anmeldenamen liefern dieselbe Meldung wie ein falsches Kennwort. Rechte werden über Gruppen und die in der Oberfläche gepflegte Rechtematrix vergeben und bei jedem Aufruf serverseitig geprüft (App\User\Permission::forPath()); die Oberfläche blendet nur zusätzlich aus, was ohnehin verboten ist. NachAUTH_IDLE_MINUTESohne Bedienung wird die Sitzung beendet. Ein von der Administration vergebenes Kennwort gilt nur als Startkennwort: Bis zum eigenen Kennwort lässt der Kernel ausschließlich/account/password, die eigene Unterschrift und die Abmeldung zu (App\Http\Kernel::PASSWORD_CHANGE_PATHS). Es ist zu empfehlen, den Netzwerkzugriff zusätzlich über Proxy bzw. Firewall zu beschränken und das Kennwort aus.envsofort zu ändern. - Uploads: Größenlimit, nur
.txt/.log, Inhaltsprüfung (Magic Bytes, MIME-Typ, 0x1C-Pflicht, NUL-Anteil), bereinigte Dateinamen; Uploads werden außerhalb des Webroots unter zufälligem Token zwischengespeichert (Gültigkeit 1 h), das Archiv ist schreibgeschützt. - Web: CSRF-Token für alle POST-Anfragen, Session-Cookies
HttpOnly/SameSite=Strict, regelmäßige Erneuerung der Session-ID, strikte Content-Security-Policy ohne Inline-Skripte,X-Frame-Options: DENY,nosniff,no-referrer, kein Caching. - Daten: ausschließlich vorbereitete Statements, strikter SQL-Modus, Ausgabe-Escaping aller Werte, nur lokale Weiterleitungen.
- Sitzung: Die Kennung der angemeldeten Person liegt serverseitig in der Sitzung; ein
Wechsel des Kontos in der Datenbank wirkt sofort. Die automatische Abmeldung
(
AUTH_IDLE_MINUTES) misst die Zeit seit der letzten Bedienung. An- und Abmeldung (auch die automatische) verwerfen den gesamten Sitzungsinhalt – aktiver Patient, offene Uploads, CSRF-Token – und erneuern die Sitzungs-ID; ohne Anmeldung erscheinen keine Patientendaten im Rahmen. Eine Kennwortänderung beendet alle anderen Sitzungen des Kontos (Fingerabdruck des Kennwort-Hashes in der Sitzung). Das Anmeldefenster nimmt als Rücksprungziel ausschließlich anwendungsinterne Pfade an (kein//, kein Backslash, keine Steuerzeichen – kein offener Umleitungspfad). - Fehler: in
productionkeine technischen Details im Browser, nur eine Referenz-ID; Details stehen im Anwendungsprotokoll (/var/www/storage/logs/app.log) und sind unter/system/logs(Fehlerprotokoll) nach Referenz durchsuchbar.
Die verarbeiteten Daten sind Gesundheitsdaten (Art. 9 DSGVO). Betreiber sind verantwortlich
für Zugriffsschutz, Verschlüsselung von Datenträgern und Backups, Lösch- und
Aufbewahrungsfristen sowie das Verzeichnis der Verarbeitungstätigkeiten.
Die Anwendung überträgt keine Daten nach außen. Für Tests und Screenshots wird
ausschließlich die anonymisierte Beispieldatei (tests/fixtures/merlin_sample.log) verwendet.
Protokolldateien enthalten technische Meldungen, aber keine Parameterwerte.
Die Patientenakte enthält Freitextangaben zu Anamnese, Vormedikation, Befund und Epikrise; auch
frühere Fassungen werden aufbewahrt und unterliegen denselben Lösch- und Aufbewahrungsfristen.
docker compose --profile test run --rm tests # alle Tests
docker compose --profile test run --rm tests php tests/run.php Parser # nur passende Testklassen
docker compose --profile test down # Testdatenbank entfernenDie Tests verwenden einen eigenen Testrunner (tests/run.php) und eine separate,
flüchtige MySQL-Instanz (db-test, Daten im RAM). Abgedeckt sind u. a.:
- Parser (1–12): Normalformat, 0x1C als einziger Trenner, leere Werte/Einheiten, Dezimal- und Prozentwerte, Datumswerte, Sonderzeichen, unbekannte Parameter, fehlerhafte Datensätze, lange Bezeichnungen, Zeilenumbrüche, Referenzdatei.
- Biotronik-Parser: Referenzdatei
tests/fixtures/BIOTRONIC_ANN.xml(61 Datensätze), Parsererkennung, Abschnittspfade, Ersatz-IDs ohnecode, Einheiten, Duplikatnamen, IEEE-Datumsformate, leere Werte, Fehlerfälle und der vollständige Datenbankimport inklusive PDF. - Datenbank (13–18): erfolgreicher Import, Rollback, Fremdschlüssel, vollständige und unveränderte Parameter, Neuladen, Unveränderlichkeit historischer Berichte, außerdem Dubletten, Suche, Schema und Migrationen.
- PDF (19–24): Erzeugung, Mehrseitigkeit, Sonderzeichen, lange Tabellen, unbekannte Parameter, PDF aus der Datenbank ohne Originaldatei.
- Zusätzlich: Zuordnung/Zusammenfassung, Uploadprüfung, Escaping, Weiterleitungen, Upload-Zwischenspeicher, Konfiguration.
- Patientenausweis: Namenszerlegung und Identitätsschlüssel, Eingabeprüfung, Logo-Prüfung, PDF-Layout (Seitenzahl, Seitenumbruch), Erzeugung aus einem Bericht, Konflikterkennung, Unveränderlichkeit bestehender Ausweise.
- Patientenakte: Eingabeprüfung der Stammdaten (Datumsformate, Pflichtfelder, Patienten-ID-Länge, Telefonzeichen, Steuerzeichen, Dublettenbestätigung), Bausteine (Freitext, Vormedikationszeilen, Datumsnormalisierung, Grenzwerte, inhaltsgleiche Eingabe ohne neue Fassung), Anlegen ohne Import, Identität und Dubletten, Suche und Pagination sowie Fassungshistorie.
- Schrittmacher-/ICD-Abfrage: Vorlagenvertrag (Gerätetypen, Abschnitte, Feldtypen, Zuordnungstabellen, fehlerhafte Vorlagen), Eingabeprüfung (Gerätetyp, Auswahlwerte, Datum, Längen, Sondenzeilen), Vorbelegung aus einem echten Beispielbericht sowie die Regel, dass die MRT-Tauglichkeit aus dem Patientenausweis führt und ein abweichender Formularwert verworfen wird.
- Oberflächen (Integration): Die Templates werden mit den echten Controllern gerendert
(
tests/Integration/PatientCardViewTest.php,tests/Integration/PatientViewTest.php,tests/Integration/LetterViewTest.php,tests/Integration/SystemSettingsViewTest.php). Damit fallen Fehler in der HTML-Schicht (fehlende Template-Variablen, unbekannte Klassen, unvollständige Formulare, fehlende CSRF-Felder) im Test auf –php -lerkennt sie nicht. - Praxis-Informationen (System):
tests/Integration/SystemSettingsViewTest.phpprüft das Formular (Abschnitte, Felder, Logo-Vorschau und -Auslieferung, Fassungszahl), das Speichern als neue Fassung samt Erfolgsmeldung, die Ablehnung einer ungültigen E-Mail-Adresse (HTTP 422, keine neue Fassung, Hinweistexte bleiben erhalten) sowie die Verlinkung von den Systeminformationen. - Patientenvorgang (Integration/Unit):
tests/Integration/PatientFirstWorkflowTest.phpprüft, dass der aktive Patient beim Anlegen automatisch gesetzt wird, Wechsel und Aufheben funktionieren, eine veraltete Auswahl verworfen wird und die patientenbezogenen Routen ohne Auswahl gesperrt bleiben, während Listen und Übersichten erreichbar sind;tests/Unit/ActivePatientTest.phpdeckt die Sitzungslogik ab. - Oberflächengerüst (Unit):
tests/Unit/IconTest.phpprüft die Symbolbibliothek (src/Http/Icon.php) – jedes Symbol liefert eingebettetes SVG ohne externe Verweise, ein unbekannter Name fällt auf das Ersatzsymbol zurück, Klassennamen werden maskiert.tests/Unit/RibbonTest.phpprüft das Funktionsband (src/Http/Ribbon.php) – eindeutige Reiter, Beschriftungen, interne Ziele, auflösbare Symbole, die Zuordnung von$active-Schlüsseln zu Reitern, die unveränderten Routen der Ziele sowie die patientenbezogenen Ziele, die einen aktiven Patienten voraussetzen. - Messwerttabelle des Briefes (Unit/Integration):
tests/Unit/LetterMeasurementsTest.php(Auflösung des Katalogs gegen die Untersuchungen, aktuelle Untersuchung ganz rechts, Auswahl, Kürzen auf die in der Vorlage eingestellte Spaltenzahl, leere Teile, Grenzen vonprevious),tests/Unit/LetterPdfTest.php(Tabelle als letzte Seite, Hinweis ohne Werte, Abwählen je Brief, Vorlagenoptionshow, Briefe der Fassung 2 ohne Tabelle) sowie die Integrationstests intests/Integration/LetterTest.phpundtests/Integration/LetterViewTest.php(Checkbox-Liste im Assistenten, Spalten mit früherer und aktueller Untersuchung, eingefrorene Auswahl, Pflicht zur Auswahl, Anzeige in der Detailansicht, gespeicherter Standard samt Vorauswahl und Rückfrage). - Brief zur Schrittmacher-/ICD-Abfrage: Assistent (
prepare()) mit Warnungen für fehlende Bausteine, Anhang, Messwerte und Bericht, Erzeugung mit beiden Bestätigungen, Ablehnung ohne Bestätigung (HTTP 422, kein Datensatz), Snapshot und Unveränderlichkeit (Patientendaten, Bausteinfassungen und Stammdaten bleiben nach späteren Änderungen unverändert), Übernahme der Praxis-Informationen und Rücksendeangaben in Snapshot und PDF, laufende Briefnummer je Patient, Brief-Fassung, Suche und Seitenaufteilung, Anhangsabschnitte je Geräteart (Schrittmacher, ICD, CRT-P, CRT-D), PDF-Layout (Reihenfolge, Auslassungen, Seitenzahl, Fußzeile, Dateiname) sowie die Anzeige in Übersicht, Detailansicht, Patientenliste und Akte. - Briefvorlagen (Unit/Integration):
tests/Unit/LetterTemplateTest.php(Standardvorlage je Empfängerart,normalize(), Platzhalter, PDF folgt Reihenfolge und Texten,{salutation}aus dem Empfänger-Snapshot),tests/Unit/LetterSalutationTest.php(Anredetexte, Rückfall, Zuordnung zu den Stammdatenfeldern),tests/Integration/LetterTemplateMigrationTest.php(bestehende Installationen erhalten getrennte Vorlagen und{salutation}) sowietestTemplateEditorSeparatesRecipientTypesintests/Integration/LetterViewTest.php(getrennte Fassungen je Art, Rückfall auf die Patientenvorlage, Bausteinquelle als JSON). - Berechtigungen (Unit):
tests/Unit/PermissionTest.phpprüft, dass der Rechtekatalog genau die Bereiche des Funktionsbandes abbildet, jeder Eintrag eine Beschreibung hat,forPath()jeden Pfad auf das richtige Recht abbildet, spezifische Pfade ihre Präfixe schlagen und unbekannte Einträge beim Normalisieren entfallen. - Benutzerverwaltung (Integration):
tests/Integration/UserServiceTest.phpdeckt die Migration (Gruppen Admin, MFA, Arzt), das idempotente Anlegen und Zurücksetzen des Administrators samt Prüfung seiner Umgebungswerte, die Kennwortablage nur als Hash, Anlegen/Ändern/Löschen von Benutzern, Dubletten- und Gruppenprüfung, die Selbstsperre, die Kontosperre nach fünf Fehlversuchen samt Ablauf, das Setzen von Kennwörtern, die Rechte-Matrix je Gruppe, die Regel „der letzte Benutzerverwalter kann sich nicht aussperren", Systemgruppen und Gruppenzählung ab; dazu die Pflicht zum Kennwortwechsel (Vorgabe beim Anlegen, Aufhebung durch das eigene Kennwort, erneute Pflicht beim Setzen eines fremden Kennworts).tests/Integration/AuthTest.phpprüft Sitzung ohne Anmeldung, Anmeldung und Abmeldung, den Ablauf nach Leerlauf (und die Verlängerung durch Aktivität), einen Zeitstempel in der Zukunft, deaktivierte und gelöschte Konten, frisch gelesene Rechte je Anfrage, das eigene Kennwort, das vollständige Verwerfen der Sitzung bei An-/Abmeldung und Leerlauf, das Ende fremder Sitzungen nach einer Kennwortänderung sowie die Normalisierung des Benutzernamens bei der Anmeldung.tests/Unit/UserInputTest.phpdeckt Normalisierung und Prüfregeln der Eingaben ab (Benutzername, Kennwortlänge, Anzeigename, Gruppenzuordnung, Rechtevereinigung mehrerer Gruppen). - Unterschriften (Integration):
tests/Integration/SignatureTest.phpprüft die einmalige Ablage gleicher Dateien (SHA-256), das Setzen und Entfernen der Unterschrift am Konto, den Namen unter der Unterschrift (Titel, Vorname, Name; Rückfall auf den Anzeigenamen) und dass ein erzeugter Brief Unterschrift und Namen einfriert (auch nach einem Wechsel unverändert reproduzierbar). Die Dateiprüfung (PNG/JPEG/BMP, Größe, Abmessungen, Abwehr fremder Formate) deckttests/Unit/ImageUploadValidatorTest.phpab. - Anmeldung und Benutzeroberfläche (Integration):
tests/Integration/LoginViewTest.phpprüft das Anmeldefenster über der abgedunkelten und unscharfen Oberfläche, die Wahrung des angeforderten Ziels (und die Abwehr externer Ziele), die Fehlermeldung ohne Hinweis auf die Existenz eines Kontos, Benutzerliste und -formular (Gruppen, Kennwortregeln, Fehleranzeige), die Rechte-Matrix, das eigene Konto (auch ohne jedes Recht), die Statusleiste mit Anmeldung und Abmeldung, die Kennzeichnung Kennwortwechsel offen, die Anzeige der Kennwortstärke im Benutzer- und im eigenen Kennwortformular sowie die Sperre des Kernels, solange der Pflichtwechsel offen ist; dazu das Ausblenden von Funktionsband und Links ohne Recht.
Die Bilder in docs/screenshots/ werden automatisiert mit Docker erzeugt
(Playwright/Chromium für die Weboberfläche, pdftoppm für die PDF-Seiten). Dabei läuft
eine eigene, flüchtige Instanz (db-docs, web-docs); Produktivdaten werden nicht berührt:
docker compose --profile docs run --rm screenshots
docker compose --profile docs down -vDas Skript liegt in docs/screenshots/capture.py. Es legt Beispieldaten an (Patient mit
Anamnese, Vormedikation, Befund, Epikrise und Schrittmacher-/ICD-Abfrage, Import der Testdatei,
Stammdaten mit Beispiel-Logo, Patientenausweis, Brief zur Schrittmacher-/ICD-Abfrage) und erzeugt
daraus die Bilder 01–59, darunter das Anmeldefenster (54), die Benutzerverwaltung
(55–58), die Praxis-Informationen (59), Hausarzt und überweisender Arzt in den Stammdaten
(49), die Empfängerauswahl des Brief-Assistenten (50), den Vorlageneditor (51–53), die Sperre des Importvorgangs ohne Patienten (47), den
automatisch aktiven Patienten (48), Assistent, Konfliktdialog, Patientenakte, die Abfrage mit
Vorbelegung und Sperrung der MRT-Tauglichkeit, beide Seiten des Ausweis-PDF, der Brief-Assistent
(Schritte 2–6, drei Empfänger), Briefdetail, Briefübersicht, Briefliste am Patienten sowie die Seiten 1–4 des
Brief-PDF. Das Skript meldet sich zu Beginn über das Anmeldefenster an (Zugangsdaten der
Dokumentationsinstanz: ADMIN_USERNAME=docs-admin, ADMIN_PASSWORD=docs-screenshot-kennwort,
siehe docker-compose.yml) und bricht ab, wenn die Oberfläche ohne Anmeldung erreichbar wäre.
Das Skript prüft dabei zugleich die harten Anforderungen: Die Oberfläche ist ohne Anmeldung
gesperrt und zeigt das Anmeldefenster, die Benutzerverwaltung ist nach der Anmeldung erreichbar,
der Import leitet ohne
aktiven Patienten auf die Patientenübersicht um, das Anlegen eines Patienten setzt ihn als
aktiven Patienten, das Ausweis-PDF hat genau
zwei Seiten, Seite 1 nennt die MRT-Tauglichkeit, das MRT-Feld der Abfrage ist gesperrt und die
Abfrage wird gespeichert; das Brief-PDF hat mindestens vier Seiten und enthält Titel,
Anamnese, Vormedikation, Befund, Epikrise, den Baustein „Berichte" (Befundteil des Berichts als
Anhang unter der Grußformel auf einer neuen Seite), den Anhang, die MRT-Tauglichkeit und die
Tachykardie-Abschnitte sowie die Anschrift des Hausarztes im Anschriftfeld. Der Build des
Screenshot-Images benötigt einmalig Internetzugang; für den Betrieb der Anwendung ist er nicht
erforderlich. web-docs bindet das Projektverzeichnis nicht ein – nach Änderungen an
Templates, src/ oder public/assets/ ist ein Neubau erforderlich
(docker compose --profile docs run --rm --build screenshots). Ein
abgebrochener Lauf hinterlässt Daten in der flüchtigen Datenbank; vor einem neuen Versuch
docker compose --profile docs down -v ausführen.
Die Aufnahmen zeigen die Oberfläche im Office-Stil inklusive Funktionsband. Die Bilder werden
mit full_page=True erfasst und sind deshalb unterschiedlich hoch; das Erzeugungsskript
spricht die Bedienelemente ausschließlich über stabile CSS-Klassen und Formularnamen an, nicht
über Pixelpositionen.
Zu sichern sind die Datenbank und das Importarchiv (optional die Anwendungsdaten).
# Datenbank (konsistent, ohne Sperren)
docker compose exec -T db sh -c 'exec mysqldump -u root -p"$MYSQL_ROOT_PASSWORD" --single-transaction --routines --triggers --default-character-set=utf8mb4 --databases hsm2med' > backup-hsm2med-$(date +%F).sql
# Importarchiv und Anwendungsdaten
docker run --rm -v hsm2med_import_data:/data:ro -v "$PWD":/backup alpine tar czf /backup/import_data-$(date +%F).tar.gz -C /data .
docker run --rm -v hsm2med_application_data:/data:ro -v "$PWD":/backup alpine tar czf /backup/application_data-$(date +%F).tar.gz -C /data .Wiederherstellung:
docker compose up -d db
docker compose exec -T db sh -c 'exec mysql -u root -p"$MYSQL_ROOT_PASSWORD"' < backup-hsm2med-JJJJ-MM-TT.sql
docker run --rm -v hsm2med_import_data:/data -v "$PWD":/backup alpine sh -c 'tar xzf /backup/import_data-JJJJ-MM-TT.tar.gz -C /data && chown -R 33:33 /data'
docker compose up -dBackups enthalten Gesundheitsdaten: verschlüsselt ablegen und die Wiederherstellung
regelmäßig testen. Achtung: docker compose down -v löscht alle Volumes und damit
sämtliche Daten.
| Problem | Lösung |
|---|---|
DB_PASSWORD muss in .env gesetzt sein |
.env aus .env.example anlegen |
| Web-Container startet nicht / „unhealthy“ | docker compose logs web db; die Datenbank braucht beim ersten Start länger |
exec ... entrypoint.sh: no such file or directory |
Zeilenenden: Repository mit LF auschecken (.gitattributes), Image neu bauen |
| „Migration wurde nach dem Anwenden verändert“ | Angewendete Migrationen nie ändern – neue Migration anlegen |
| Upload abgelehnt (Größe) | UPLOAD_MAX_SIZE erhöhen, Container neu starten |
| Upload abgelehnt (Format) | Merlin: Datei muss 0x1C-Trenner enthalten und auf .txt/.log enden. Biotronik: .xml mit Wurzelelement <biotronik-ieee11073-export> |
| „Ungültiges Sicherheitstoken“ | Seite neu laden (Session abgelaufen) |
| Fehlerseite mit Referenz | „Im Fehlerprotokoll anzeigen" oder Referenz unter /system/logs suchen; alternativ docker compose exec web cat /var/www/storage/logs/app.log |
| Port belegt | WEB_PORT in .env ändern |
bin/ CLI: import, export-pdf, migrate, build-schema, seed-admin, php-limits
config/ parameter_mapping.php (Kategorien, Feldzuordnung),
patient_card_measurements.php, device_check_template.php
database/ migrations/ (maßgeblich) und schema.sql (generiert)
docker/ Apache-/PHP-Konfiguration, Entrypoint
docs/screenshots/ Screenshots und Erzeugungsskript (Playwright)
public/ Webroot: index.php, assets/ (CSS, JS)
assets/css/ app.css (Inhalte), office.css (Office-Gerüst)
src/ Anwendungscode (Namespace App\)
Http/ Kernel, Router, Controller, View, Icon (Symbole), Ribbon (Funktionsband)
Import/ Parser, Validierung, ImportService, Archiv
Letter/ Brief zur Schrittmacher-/ICD-Abfrage (Anhang, PDF, Service)
Mapping/ Parameterzuordnung
Patient/ Patientenakte (Patient, Bausteine, Fassungen)
Report/ Berichtsdaten, Zusammenfassung, PDF (Pdf/)
Repository/ Datenbankabfragen (Importe, Berichte, Benutzer, Gruppen)
Security/ Auth (Anmeldung, Ruhezeit), Session, CSRF, Uploadprüfung
User/ Benutzer, Gruppen, Rechte (Permission), Benutzerdienst
templates/ PHP-Templates (HTML; u. a. letters/, login/, users/, account/)
tests/ Testrunner, Unit-/Integrationstests, Fixtures






























































