Etappe 7. Die Ausgabeschicht - alles, was diese Anwendung nach aussen abgibt. Der Jahresbericht nach § 1863 BGB ist ein ENTWURF zum Ueberarbeiten, kein fertiger Text. Er zieht sich die fuenf Gliederungspunkte aus Kontaktprotokollen, Gesundheitsereignissen und Vorgangsstatus zusammen; was davor abgegeben wird, entscheidet der Betreuer. Das sagt auch die Oberflaeche. Ausgabe als PDF und als DOCX, damit sich weiterschreiben laesst. Vollexport als ZIP-Strom mit Gegendruck, damit auch grosse Bestaende ohne Speicherausbruch durchgehen: Originaldateien nach Jahren sortiert, alle Tabellen als entschluesseltes JSON, die erzeugten Berichte, ein LIESMICH und SHA-256 je Datei. Damit laesst sich die Anwendung verlassen - kein Datensilo. Der Export ist eine sensible Aktion: nur BETREUER und ADMIN, nur nach frischer Anmeldung, immer mit Audit-Eintrag. Ein Mitleser kommt nicht heran. 37 Hinweiskarten in daten/hinweise.json, Zod-geprueft und ohne Neustart pflegbar. Jede mit Rechtsgrundlage, Link und dem stehenden Vermerk, dass sie keine Rechtsberatung ersetzt. Modulweise Freigaben fuer Mitleser, mit ausdruecklicher Warnung bei Gesundheit, Finanzen und Sozialhilfe. Rollen- und Freigabeaenderungen erfordern eine frische Anmeldung und stehen im Audit-Log. Nebenbei geloest: Webpack loest .tsx vor .ts auf, TypeScript und Vitest umgekehrt. Ein Modul, das beide Endungen traegt, importiert sich unter einem der beiden Werkzeuge selbst. Deshalb heissen die Bausteine jetzt eindeutig (jahresbericht.entwurf.ts, .inhalt.tsx, .ausgabe.ts) und werden ueber eine gemeinsame Fassade weitergereicht. 41 Tests. Etappe: 7 Phase: Ausgabe
5.4 KiB
Schema-Wünsche aus der Ausgabeschicht (Etappe 7)
prisma/schema.prisma wurde nicht angefasst. Was beim Bauen der Berichte,
des Exports und der Freigaben gefehlt hat, steht hier – mit Begründung und
mit dem Behelf, der jetzt an seiner Stelle steht.
1. Mehrere Fassungen je Bericht
Jetzt: Bericht trägt genau ein objektSchluessel / dateiname /
pruefsumme. Wird das PDF nach einer Rückfrage des Gerichts neu erzeugt,
überschreibt es die Angaben der Fassung, die bereits vorgelegt wurde.
Warum das stört: Die Leitfrage der Tabelle ist „was habe ich wann in welcher Fassung abgegeben?“. Genau die lässt sich nach dem zweiten Erzeugen nicht mehr beantworten. Fragt der Rechtspfleger in zwei Jahren nach, welcher Text ihm vorlag, steht der neue da.
Wunsch:
model Berichtsfassung {
id String @id @default(cuid())
berichtId String
bericht Bericht @relation(fields: [berichtId], references: [id], onDelete: Cascade)
nummer Int // 1, 2, 3 …
format String // "pdf" | "docx"
objektSchluessel String
dateiname String
pruefsumme String
erstelltAm DateTime @default(now())
abgegebenAm DateTime? @db.Date
abgegebenAn String?
@@unique([berichtId, nummer, format])
}
Behelf bis dahin: Die PDF-Fassung wird am Bericht vermerkt, die
DOCX-Arbeitsfassung bewusst nicht – sonst verdrängte sie die abgegebene
Fassung. Die Word-Datei liegt im Objektspeicher und taucht im Vollexport
auf, ist aber über die Oberfläche nur bis zum nächsten Erzeugen erreichbar.
2. Zusammenhang zwischen Unterlagenpaket und Nachforderung
Jetzt: Beim Erzeugen eines Pakets werden die beigefügten
Nachforderung-Zeilen auf UEBERSANDT gesetzt und bekommen ein
uebersandtAm. In welchem Paket sie mitgegangen sind, steht nirgends.
Warum das stört: Der Träger fragt regelmäßig nach einer einzelnen Position („zu Nr. 7 fehlt uns noch etwas“). Ohne Rückbezug muss man raten, welches Anschreiben Nr. 7 enthielt.
Wunsch: Nachforderung.uebersandtMitBerichtId String? mit Relation auf
Bericht.
Behelf: Das Anschreiben nennt die laufende Nummer, und der
Bericht-Datensatz trägt Datum und Positionsanzahl im Titel.
3. Betreuungsjahr am Bericht
Wunsch: Bericht.betreuungsjahr Int? – die laufende Nummer des
Betreuungsjahres, aus dem der Jahresbericht stammt.
Warum: Das Betreuungsjahr läuft ab dem Tag der Bestellung. Zeiträume allein sind für die Dublettenprüfung unhandlich, sobald jemand den Vorschlag im Formular um einen Tag verschiebt.
Behelf: Geprüft wird auf art + zeitraumVon + zeitraumBis. Ein um
einen Tag verschobener Zeitraum erzeugt deshalb einen zweiten Entwurf.
4. Index für die Dublettenprüfung
Wunsch: @@index([betreuteId, art, zeitraumVon, zeitraumBis]) auf
Bericht. Vorhanden ist nur @@index([betreuteId, art]).
5. Aufbewahrungsfrist am Papierkorb
Wunsch: geloeschtVonId String? auf allen Modellen mit geloeschtAm.
Warum: Das Audit-Log weiß, wer gelöscht hat, aber die Papierkorbliste müsste dafür je Zeile einen Log-Eintrag suchen. Für die Frage „wer hat das weggeräumt?“ direkt an der Zeile wäre ein Feld billiger.
Entscheidungen, die keine Schemaänderung brauchen
Zwei gleichnamige Dateien: jahresbericht.ts und jahresbericht.tsx
Der Bauplan verlangt beide. Beide nebeneinander sind aber eine Falle: Beim
Import ohne Endung probiert Webpack .tsx vor .ts, TypeScript und Vitest
umgekehrt. Derselbe Importpfad wäre je nach Werkzeug eine andere Datei –
und ein export * from './jahresbericht' innerhalb der .tsx würde unter
Webpack die Datei sich selbst importieren.
Gelöst über eindeutige Namen für die Bausteine:
| Datei | Inhalt |
|---|---|
jahresbericht.entwurf.ts |
Textbausteine, rein, ohne Datenbank – das, was getestet wird |
jahresbericht.inhalt.tsx |
Satz für das PDF (React) |
jahresbericht.ausgabe.ts |
Daten laden, PDF erzeugen |
jahresbericht.ts |
reicht alle drei weiter |
jahresbericht.tsx |
reicht alle drei weiter (identisch) |
Damit bekommt jeder Aufrufer von @/lib/berichte/jahresbericht dieselbe
Schnittstelle, gleich welches Werkzeug auflöst. Wer nur die Textbausteine
braucht – die Tests, die DOCX-Ausgabe – importiert direkt
@/lib/berichte/jahresbericht.entwurf und spart sich Datenbank und
PDF-Bibliothek.
Alternative, falls das später stört: allowImportingTsExtensions: true
in der tsconfig.json (mit noEmit: true unbedenklich, das ist bereits
gesetzt). Dann ließen sich die Endungen ausschreiben und die beiden
weiterreichenden Dateien entfielen.
Vollexport ohne Serverkopie
Das ZIP wird als Strom ausgeliefert und nicht im Objektspeicher
abgelegt. Ein Bericht-Datensatz der Art VOLLEXPORT hält nur fest, dass
und wann exportiert wurde; objektSchluessel bleibt leer.
Grund: Eine unverschlüsselte Vollkopie des gesamten Bestands dauerhaft auf dem Server liegen zu lassen, wäre die größte Angriffsfläche der ganzen Anwendung – und niemand bräuchte sie ein zweites Mal.
Landesrecht ohne Bundesgesetz-Link
Zwei Hinweiskarten betreffen bayerisches Landesrecht bzw. den
Rundfunkbeitragsstaatsvertrag (landespflegegeld,
rundfunkbeitragsbefreiung). Beides steht nicht auf
gesetze-im-internet.de; die Karten verlinken deshalb auf
gesetze-bayern.de. Das Schema verlangt nur einen https-Link.