feat: Jahresbericht, Vollexport, Wissensbasis und Mitleser-Freigaben
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
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
# 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:**
|
||||
|
||||
```prisma
|
||||
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.
|
||||
Reference in New Issue
Block a user