Jeder Vorgang ist eine kleine Akte: Kopfdaten, Aufgabenliste zum Abhaken, Genehmigungsstufen und ein Zeitstrahl, der Dokumente, Kontaktprotokolle, Fristen, Buchungen und Statuswechsel zusammenfuehrt. Die abnahmerelevante Regel: Ein genehmigungspflichtiger Vorgang laesst sich nicht auf VOLLZOGEN setzen, solange kein Beschlussdokument hinterlegt ist. Geprueft wird in darfStatusWechseln() UND in der Server Action - nicht nur im UI, wo das Auswahlfeld die Stufe ohnehin gar nicht erst anbietet. Hintergrund ist § 1858 BGB: die Genehmigung muss vor dem Rechtsgeschaeft vorliegen, nicht danach. Neun Vorlagen mit durchdachter Aufgabenreihenfolge. Bei Wohnungs- aufloesung und Hausverkauf erzwingen sie die Kette Mitteilung -> Genehmigung -> Rechtsgeschaeft; ein Test prueft das. Kanban ohne Drag-and-Drop: Statuswechsel laufen ueber Auswahlfeld und Knopf. Am Handy ist das verlaesslicher, und am Schreibtisch kostet es einen Klick mehr statt einer verlorenen Karte. Zeitzonen: alsTag() bildet den oertlichen Kalendertag auf Mitternacht UTC ab. Ohne das rutscht in Deutschland jede Faelligkeit einen Tag zurueck. 33 Tests. Etappe: 2 Phase: Alltag
137 lines
4.7 KiB
Markdown
137 lines
4.7 KiB
Markdown
# Schema-Wünsche: Vorgänge, Aufgaben, Genehmigungen
|
|
|
|
Beim Bau von Etappe 2 („Vorgänge (Fallakte)“) sind vier Stellen aufgefallen,
|
|
an denen das Datenmodell die Fachlichkeit nur mit Umwegen abbildet. Nichts
|
|
davon blockiert die Umsetzung — alles ist im Code sauber umschifft. Die
|
|
Umwege stehen unten mit dabei, damit man beim Ändern weiß, was wegfällt.
|
|
|
|
`prisma/schema.prisma` wurde **nicht** angefasst.
|
|
|
|
---
|
|
|
|
## 1. `Genehmigung`: Die Versagung hat kein eigenes Datum
|
|
|
|
**Ist**: `mitteilungAm`, `antragAm`, `anhoerungAm`, `beschlussAm`,
|
|
`rechtskraftAm`, `vollzugAm` — für jede Stufe der Kette ein Feld. Nur der
|
|
Seitenausgang `VERSAGT` hat keins.
|
|
|
|
**Warum das stört**: Der Zeitstrahl der Fallakte baut die Chronik des
|
|
Genehmigungsverfahrens aus genau diesen Datumsfeldern. Eine Versagung ist
|
|
aber der Ausgang, der später am meisten Erklärung braucht — gegenüber dem
|
|
Gericht, gegenüber Miterben, und für die Frage, ob eine Beschwerde noch
|
|
läuft.
|
|
|
|
**Wunsch**:
|
|
|
|
```prisma
|
|
versagungAm DateTime? @db.Date
|
|
versagungsgrund String?
|
|
```
|
|
|
|
**Umweg bis dahin**: `src/lib/vorgaenge/zeitstrahl.ts` zeigt eine Versagung
|
|
am `beschlussAm` an — das Gericht versagt ja mit einem Beschluss. Der Grund
|
|
landet in `notizenVerschluesselt`.
|
|
|
|
---
|
|
|
|
## 2. `Aufgabe`: kein Papierkorb
|
|
|
|
**Ist**: `Aufgabe` hat kein `geloeschtAm`. Sie hängt per `onDelete: Cascade`
|
|
am Vorgang.
|
|
|
|
**Warum das stört**: Vorlagen bringen bis zu zehn Aufgaben mit. Manche davon
|
|
passen im Einzelfall nicht („Rundfunkbeitrag“, wenn nie einer gezahlt
|
|
wurde). Sie einfach abzuhaken ist unehrlich — sie wurden nicht erledigt,
|
|
sie waren gegenstandslos. Löschen wiederum ist im Rest der Anwendung immer
|
|
ein Papierkorb, hier wäre es endgültig.
|
|
|
|
**Wunsch**:
|
|
|
|
```prisma
|
|
geloeschtAm DateTime?
|
|
entfaellt Boolean @default(false)
|
|
entfallGrund String?
|
|
```
|
|
|
|
`entfaellt` wäre das Ehrlichere: Die Aufgabe bleibt in der Akte sichtbar,
|
|
zählt aber nicht gegen den Fortschritt.
|
|
|
|
**Umweg bis dahin**: Es gibt in dieser Etappe keine Möglichkeit, eine
|
|
Aufgabe zu entfernen. Nicht passende Aufgaben bleiben offen stehen.
|
|
|
|
---
|
|
|
|
## 3. Statuswechsel des Vorgangs stehen nur im Audit-Log
|
|
|
|
**Ist**: Der Zeitstrahl liest die Statuschronik eines Vorgangs aus
|
|
`AuditLog`, entschlüsselt `aenderungVerschluesselt` und sucht darin das
|
|
Feld `status`.
|
|
|
|
**Warum das stört**: Drei Dinge auf einmal —
|
|
|
|
1. Es werden bis zu 200 Log-Einträge je Aufruf entschlüsselt, nur um ein
|
|
Feld zu lesen. Das skaliert schlecht und lässt sich nicht filtern,
|
|
weil der Umschlag nicht durchsuchbar ist.
|
|
2. Das Audit-Log ist ein Nachweis, keine Fachtabelle. Es fachlich
|
|
auszuwerten vermischt zwei Zwecke.
|
|
3. Ein Log-Eintrag, der sich nicht entschlüsseln lässt (Schlüsselrotation),
|
|
reißt still ein Loch in die Chronik.
|
|
|
|
**Wunsch**: eine schmale Tabelle
|
|
|
|
```prisma
|
|
model VorgangStatuswechsel {
|
|
id String @id @default(cuid())
|
|
vorgangId String
|
|
vorgang Vorgang @relation(fields: [vorgangId], references: [id], onDelete: Cascade)
|
|
von VorgangStatus?
|
|
nach VorgangStatus
|
|
zeitpunkt DateTime @default(now())
|
|
userId String?
|
|
userName String?
|
|
@@index([vorgangId, zeitpunkt])
|
|
}
|
|
```
|
|
|
|
**Umweg bis dahin**: `src/app/(app)/vorgaenge/[id]/page.tsx` liest das
|
|
Audit-Log und fängt unlesbare Einträge einzeln ab. Die Schnittstelle von
|
|
`zeitstrahlBauen()` nimmt die Wechsel bereits als eigene Quelle entgegen —
|
|
ein Umstieg auf die Tabelle wäre eine Änderung in einer Datei.
|
|
|
|
---
|
|
|
|
## 4. `Vorgang`: kein Feld für den Ansprechpartner bei der Gegenstelle
|
|
|
|
**Ist**: `gegenstelle` (Text) und `aktenzeichen` (Text), dazu die n:m-
|
|
Beziehung `kontakte`. Wer bei der Behörde konkret zuständig ist, steht
|
|
nirgends direkt am Vorgang.
|
|
|
|
**Warum das stört**: Die Sachstandsanfrage
|
|
(`src/lib/vorgaenge/sachstandsanfrage.ts`) kann eine namentliche Anrede
|
|
erzeugen — genau die, die erfahrungsgemäß schneller beantwortet wird. Sie
|
|
bekommt den Namen aber nicht, ohne über `kontakte` zu raten, welcher der
|
|
verknüpften Kontakte gemeint ist.
|
|
|
|
**Wunsch**:
|
|
|
|
```prisma
|
|
/// Der eine Kontakt, der diesen Vorgang bei der Gegenstelle führt.
|
|
ansprechpartnerId String?
|
|
ansprechpartner Kontakt? @relation("VorgangAnsprechpartner", fields: [ansprechpartnerId], references: [id], onDelete: SetNull)
|
|
```
|
|
|
|
**Umweg bis dahin**: `sachstandsanfrageErzeugen()` nimmt den
|
|
Ansprechpartner als optionalen Parameter entgegen und fällt sonst auf
|
|
„Sehr geehrte Damen und Herren“ zurück. Die Seite übergibt ihn derzeit
|
|
nicht.
|
|
|
|
---
|
|
|
|
## Nicht gewünscht
|
|
|
|
Ausdrücklich **kein** Wunsch ist ein Enum für
|
|
`Genehmigung.rechtsgrundlage`. Der Freitext ist richtig: Die Auswahlliste
|
|
in `src/lib/vorgaenge/genehmigungskette.ts` deckt den Alltag ab, aber
|
|
seltene Tatbestände muss man eintragen können, ohne eine Migration zu
|
|
schreiben.
|