Ein namentlich adressiertes Schreiben landet bei der Person, die den Vorgang kennt, statt in der Poststelle. Am Vorgang laesst sich jetzt ein Kontakt aus dem Verzeichnis als Ansprechpartner benennen; die Sachstandsanfrage redet ihn an, und sein Name steht ueber der Behoerde. Die Sachstandsanfrage konnte das schon - sie bekam den Namen nur nie uebergeben. entwurf.ts lud einen der verknuepften Kontakte und verwarf ihn wieder. Wer angeredet wird, ist jetzt eine Entscheidung und kein Zufall. Aufbau: - Migration: ansprechpartnerId am Vorgang (SetNull - ein Vorgang darf nicht daran haengen, dass es den Kontakt noch gibt) und anrede am Kontakt. - Die Anrede als reine Funktion in kontakte/anrede.ts. Geraten wird nie: ohne hinterlegte Anrede wird mit vollem Namen angeredet. Ein leerer Name mit gesetzter Anrede faellt auf die allgemeine Anrede zurueck - "Sehr geehrte Frau ," darf nie herausgehen. - Der Wechsel des Ansprechpartners steht mit NAMEN im Protokoll. Zwei cuids nebeneinander waeren protokolliert, aber nicht nachvollziehbar, und nachvollziehbar war die Anforderung. - Der Ansprechpartner wird gegen die Akte geprueft, bevor er gespeichert wird - sein Name steht anschliessend im Briefkopf. Nebenbei behoben: Der Schreiben-Editor verwarf die empfaengerAnschrift aus dem Entwurf, sein State startete immer leer. Jetzt kommt die Anschrift des Ansprechpartners im Formular an. Geprueft: neun Einheitstests fuer die Anrede, fuenf End-to-End-Tests von der Auswahl bis zum Protokolleintrag. Refs #9
1763 lines
52 KiB
Plaintext
1763 lines
52 KiB
Plaintext
// =============================================================================
|
|
// Betreuungs-Cockpit - Datenmodell
|
|
// =============================================================================
|
|
//
|
|
// Konventionen in diesem Schema:
|
|
//
|
|
// 1. Geldbetraege liegen IMMER als BigInt in Cent vor ("...Cent"). Kein Float,
|
|
// keine Decimal-Konvertierung im Anzeigepfad - die Rechnungslegung nach
|
|
// § 1865 BGB darf keine Rundungsfehler erben.
|
|
//
|
|
// 2. Felder mit dem Suffix "Verschluesselt" enthalten einen AES-256-GCM-
|
|
// Umschlag (siehe src/lib/crypto/feld.ts), niemals Klartext. Sie lassen
|
|
// sich nicht filtern oder sortieren. Wo trotzdem gesucht werden muss,
|
|
// steht daneben ein "...Index"-Feld mit einem HMAC-Blindindex.
|
|
//
|
|
// 3. Alles, was fachlich zu einer betreuten Person gehoert, traegt eine
|
|
// betreuteId. Der Zugriffsschutz prueft grundsaetzlich gegen dieses Feld
|
|
// (Row-Level-Pruefung), nie nur im UI.
|
|
//
|
|
// 4. Loeschen ist ein Papierkorb: "geloeschtAm" wird gesetzt, der Datensatz
|
|
// bleibt 30 Tage bestehen und wird danach hart entfernt.
|
|
//
|
|
// 5. Datumsfelder ohne Uhrzeitbezug (Fristen, Bescheide, Stichtage) sind
|
|
// @db.Date - sonst verschiebt die Zeitzone eine Frist um einen Tag.
|
|
//
|
|
// =============================================================================
|
|
|
|
generator client {
|
|
provider = "prisma-client-js"
|
|
}
|
|
|
|
datasource db {
|
|
provider = "postgresql"
|
|
url = env("DATABASE_URL")
|
|
}
|
|
|
|
// =============================================================================
|
|
// Benutzer, Rollen, Zugriff
|
|
// =============================================================================
|
|
|
|
enum Rolle {
|
|
BETREUER // voller Zugriff auf die zugeordneten Betreuten
|
|
MITLESER // nur lesend, je Modul freigegeben (z.B. Geschwister)
|
|
ADMIN // Benutzerverwaltung, Systemeinstellungen
|
|
}
|
|
|
|
enum Modul {
|
|
STAMMDATEN
|
|
VORGAENGE
|
|
FRISTEN
|
|
DOKUMENTE
|
|
FINANZEN
|
|
SOZIALHILFE
|
|
IMMOBILIE
|
|
VERTRAEGE
|
|
GESUNDHEIT
|
|
KONTAKTE
|
|
BERICHTE
|
|
}
|
|
|
|
model User {
|
|
id String @id @default(cuid())
|
|
email String @unique
|
|
name String
|
|
|
|
// Argon2id. Die Parameter stehen im Hash-String selbst.
|
|
passwortHash String
|
|
passwortGeaendertAm DateTime @default(now())
|
|
|
|
// TOTP ist Pflicht. Das Secret liegt verschluesselt, nicht im Klartext.
|
|
totpSecretVerschluesselt String?
|
|
totpAktiviertAm DateTime?
|
|
// Einmal-Wiederherstellungscodes, einzeln Argon2id-gehasht.
|
|
wiederherstellungscodes String[] @default([])
|
|
|
|
rolle Rolle @default(BETREUER)
|
|
aktiv Boolean @default(true)
|
|
letzteAnmeldung DateTime?
|
|
|
|
// Wird hochgezaehlt, um alle bestehenden Sitzungen ungueltig zu machen
|
|
// (Passwortwechsel, Rollenaenderung, Verdacht auf Kompromittierung).
|
|
sitzungsGeneration Int @default(0)
|
|
|
|
// Oberflaechen-Einstellungen
|
|
theme String @default("system") // "system" | "hell" | "dunkel"
|
|
|
|
erstelltAm DateTime @default(now())
|
|
aktualisiertAm DateTime @updatedAt
|
|
geloeschtAm DateTime?
|
|
|
|
zugaenge BetreuungsZugang[]
|
|
passkeys Passkey[]
|
|
auditEintraege AuditLog[]
|
|
angelegteVorgaenge Vorgang[] @relation("VorgangAngelegtVon")
|
|
berichte Bericht[]
|
|
|
|
@@index([email])
|
|
@@map("benutzer")
|
|
}
|
|
|
|
/// Verbindet einen Benutzer mit einer betreuten Person und legt fest,
|
|
/// welche Module ein Mitleser sehen darf.
|
|
model BetreuungsZugang {
|
|
id String @id @default(cuid())
|
|
|
|
userId String
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
betreuteId String
|
|
betreute Betreute @relation(fields: [betreuteId], references: [id], onDelete: Cascade)
|
|
|
|
rolle Rolle @default(BETREUER)
|
|
|
|
/// Nur fuer MITLESER ausgewertet. BETREUER sieht immer alles.
|
|
freigegebeneModule Modul[] @default([])
|
|
|
|
erstelltAm DateTime @default(now())
|
|
aktualisiertAm DateTime @updatedAt
|
|
|
|
@@unique([userId, betreuteId])
|
|
@@index([betreuteId])
|
|
@@map("betreuungs_zugang")
|
|
}
|
|
|
|
/// Fehlgeschlagene und erfolgreiche Anmeldeversuche - Grundlage fuer das
|
|
/// Rate-Limiting am Login. Wird nach 30 Tagen aufgeraeumt.
|
|
model LoginVersuch {
|
|
id String @id @default(cuid())
|
|
email String
|
|
ipHash String?
|
|
erfolg Boolean
|
|
grund String?
|
|
zeitpunkt DateTime @default(now())
|
|
|
|
@@index([email, zeitpunkt])
|
|
@@index([zeitpunkt])
|
|
@@map("login_versuch")
|
|
}
|
|
|
|
/// Ein registrierter Passkey (WebAuthn/FIDO2).
|
|
///
|
|
/// Der private Schluessel verlaesst das Geraet nie - hier liegt nur der
|
|
/// oeffentliche Teil. Ein gestohlener Datenbankabzug erlaubt deshalb keine
|
|
/// Anmeldung, anders als bei einem Passworthash, den man offline angreifen
|
|
/// kann.
|
|
///
|
|
/// Ein Passkey zaehlt bei uns als ZWEI Faktoren: Besitz des Geraets plus
|
|
/// die Nutzerverifikation darauf (Fingerabdruck, Gesicht oder Geraete-PIN).
|
|
/// Deshalb wird bei der Anmeldung "userVerified" erzwungen - siehe
|
|
/// src/lib/auth/passkey.ts. Ohne diese Pruefung waere es nur ein Faktor.
|
|
model Passkey {
|
|
id String @id @default(cuid())
|
|
|
|
userId String
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
/// Kennung des Authenticators, base64url. Eindeutig ueber alle Benutzer -
|
|
/// derselbe Schluessel darf nicht an zwei Zugaengen haengen.
|
|
credentialId String @unique
|
|
|
|
/// Oeffentlicher Schluessel im COSE-Format. Kein Geheimnis.
|
|
publicKey Bytes
|
|
|
|
/// Signaturzaehler des Authenticators. Zaehlt er nicht hoch, obwohl er
|
|
/// es vorher tat, ist der Schluessel womoeglich geklont worden.
|
|
/// BigInt, weil der Zaehler bis 2^32-1 geht.
|
|
counter BigInt @default(0)
|
|
|
|
/// "usb", "nfc", "ble", "internal", "hybrid" - hilft dem Browser beim
|
|
/// naechsten Mal, das richtige Geraet vorzuschlagen.
|
|
transports String[] @default([])
|
|
|
|
/// Vom Benutzer vergeben ("iPhone von Florian"). Bei mehreren Geraeten
|
|
/// ist das die einzige Moeglichkeit, sie auseinanderzuhalten.
|
|
name String
|
|
|
|
/// "singleDevice" = liegt nur auf diesem Geraet, "multiDevice" = wird vom
|
|
/// Schluesselbund synchronisiert. Steht in der Uebersicht, weil es den
|
|
/// Unterschied macht, ob ein verlorenes Geraet den Zugang mitnimmt.
|
|
geraeteArt String?
|
|
|
|
/// Ob der Anbieter den Schluessel gesichert hat (iCloud, Google, Bitwarden).
|
|
gesichert Boolean @default(false)
|
|
|
|
erstelltAm DateTime @default(now())
|
|
zuletztBenutztAm DateTime?
|
|
|
|
@@index([userId])
|
|
@@map("passkey")
|
|
}
|
|
|
|
enum PasskeyAnfrageArt {
|
|
REGISTRIERUNG
|
|
ANMELDUNG
|
|
}
|
|
|
|
/// Kurzlebiger Speicher fuer die Challenge einer laufenden Passkey-Aktion.
|
|
///
|
|
/// Die Challenge muss serverseitig liegen und genau einmal gelten, sonst
|
|
/// liesse sich eine mitgeschnittene Antwort erneut einspielen. Verbraucht
|
|
/// wird sie durch DELETE - dass genau eine Zeile geloescht wurde, ist der
|
|
/// Beweis, dass sie noch nicht benutzt war.
|
|
///
|
|
/// Bewusst ohne Fremdschluessel auf User: die Zeile lebt fuenf Minuten und
|
|
/// ist kein Fachdatum. Der Wartungslauf raeumt Liegengebliebenes weg.
|
|
model PasskeyAnfrage {
|
|
id String @id @default(cuid())
|
|
|
|
/// base64url, zufaellig. Eindeutig, damit das Verbrauchen atomar ist.
|
|
challenge String @unique
|
|
|
|
art PasskeyAnfrageArt
|
|
|
|
/// Bei der Registrierung bekannt. Bei der Anmeldung nicht - dort weiss
|
|
/// der Server erst nach der Antwort, wer sich anmeldet.
|
|
userId String?
|
|
|
|
ablaufAm DateTime
|
|
erstelltAm DateTime @default(now())
|
|
|
|
@@index([ablaufAm])
|
|
@@map("passkey_anfrage")
|
|
}
|
|
|
|
// =============================================================================
|
|
// Betreute Person
|
|
// =============================================================================
|
|
|
|
enum Aufgabenkreis {
|
|
VERMOEGENSSORGE
|
|
GESUNDHEITSSORGE
|
|
AUFENTHALTSBESTIMMUNG
|
|
WOHNUNGSANGELEGENHEITEN
|
|
BEHOERDENANGELEGENHEITEN
|
|
POST_UND_FERNMELDEVERKEHR
|
|
RECHTS_ANTRAGS_UND_BEHOERDENVERTRETUNG
|
|
ENTSCHEIDUNG_UEBER_FREIHEITSENTZIEHENDE_MASSNAHMEN
|
|
SONSTIGES
|
|
}
|
|
|
|
model Betreute {
|
|
id String @id @default(cuid())
|
|
|
|
// --- Person -------------------------------------------------------------
|
|
vorname String
|
|
nachname String
|
|
geburtsname String?
|
|
geburtsdatum DateTime @db.Date
|
|
geburtsort String?
|
|
anrede String? // "Frau", "Herr", ...
|
|
|
|
// --- Aufenthalt / Heim ---------------------------------------------------
|
|
heimName String?
|
|
heimStrasse String?
|
|
heimPlz String?
|
|
heimOrt String?
|
|
heimZimmer String?
|
|
heimTelefon String?
|
|
heimEinzugAm DateTime? @db.Date
|
|
|
|
// Letzte eigene Wohnanschrift - bleibt fuer Behoerden relevant.
|
|
fruehereStrasse String?
|
|
frueherePlz String?
|
|
fruehererOrt String?
|
|
|
|
// --- Gesundheit (besondere Kategorie nach Art. 9 DSGVO) ------------------
|
|
krankenkasse String?
|
|
krankenversichertennummerVerschluesselt String?
|
|
pflegekasse String?
|
|
pflegegrad Int?
|
|
pflegegradSeit DateTime? @db.Date
|
|
pflegegradBescheidVom DateTime? @db.Date
|
|
schwerbehinderungGdB Int?
|
|
schwerbehinderungMerkzeichen String?
|
|
diagnosenVerschluesselt String?
|
|
|
|
/// Der nach § 1821 BGB zu ermittelnde Wille der betreuten Person.
|
|
/// Seit der Reform 2023 Leitschnur des Betreuerhandelns.
|
|
ermittelterWilleVerschluesselt String?
|
|
|
|
patientenverfuegungVorhanden Boolean @default(false)
|
|
vorsorgevollmachtVorhanden Boolean @default(false)
|
|
|
|
// --- Betreuungsgericht ---------------------------------------------------
|
|
betreuungsgericht String?
|
|
aktenzeichenGericht String?
|
|
/// Steuert das Betreuungsjahr und damit fast alle gesetzlichen Fristen.
|
|
bestellungAm DateTime? @db.Date
|
|
bestellungBis DateTime? @db.Date
|
|
aufgabenkreise Aufgabenkreis[] @default([])
|
|
aufgabenkreiseFreitext String?
|
|
|
|
/// Befreiter Betreuer nach § 1859 BGB (Angehoerige) - steuert, ob die
|
|
/// knappe Vermoegensuebersicht oder die volle Rechnungslegung faellig ist.
|
|
befreiterBetreuer Boolean @default(true)
|
|
ehrenamtlich Boolean @default(true)
|
|
|
|
// --- Sozialleistungstraeger ---------------------------------------------
|
|
aktenzeichenBezirk String?
|
|
aktenzeichenSozialamt String?
|
|
rentenversicherungsnummerVerschluesselt String?
|
|
steuerIdVerschluesselt String?
|
|
|
|
notizenVerschluesselt String?
|
|
|
|
erstelltAm DateTime @default(now())
|
|
aktualisiertAm DateTime @updatedAt
|
|
geloeschtAm DateTime?
|
|
|
|
zugaenge BetreuungsZugang[]
|
|
vorgaenge Vorgang[]
|
|
fristen Frist[]
|
|
dokumente Dokument[]
|
|
konten Konto[]
|
|
buchungen Buchung[]
|
|
zahlungsgruppen ZahlungsgruppeGeklaert[]
|
|
schreibvorlagen Schreibvorlage[]
|
|
vermoegensposten Vermoegensposten[]
|
|
immobilien Immobilie[]
|
|
vertraege Vertrag[]
|
|
kontakte Kontakt[]
|
|
kontaktprotokolle Kontaktprotokoll[]
|
|
gesundheitsereignisse Gesundheitsereignis[]
|
|
genehmigungen Genehmigung[]
|
|
sozialhilfeAntraege SozialhilfeAntrag[]
|
|
leistungen Leistung[]
|
|
heimkosten Heimkostenmonat[]
|
|
heimvertrag Heimvertrag?
|
|
tags Tag[]
|
|
verknuepfungen DokumentVerknuepfung[]
|
|
auditEintraege AuditLog[]
|
|
berichte Bericht[]
|
|
|
|
@@index([nachname, vorname])
|
|
@@map("betreute")
|
|
}
|
|
|
|
// =============================================================================
|
|
// Vorgaenge (Fallakte) und Genehmigungen
|
|
// =============================================================================
|
|
|
|
enum VorgangKategorie {
|
|
GERICHT
|
|
SOZIALHILFE
|
|
IMMOBILIE
|
|
VERSICHERUNG
|
|
HEIM_PFLEGE
|
|
VERTRAEGE
|
|
STEUERN
|
|
SONSTIGES
|
|
}
|
|
|
|
enum VorgangStatus {
|
|
OFFEN
|
|
IN_ARBEIT
|
|
WARTET_AUF_DRITTE
|
|
WARTET_AUF_GENEHMIGUNG
|
|
ERLEDIGT
|
|
}
|
|
|
|
model Vorgang {
|
|
id String @id @default(cuid())
|
|
|
|
betreuteId String
|
|
betreute Betreute @relation(fields: [betreuteId], references: [id], onDelete: Cascade)
|
|
|
|
titel String
|
|
beschreibungVerschluesselt String?
|
|
kategorie VorgangKategorie
|
|
status VorgangStatus @default(OFFEN)
|
|
prioritaet Int @default(2) // 1 hoch, 2 normal, 3 niedrig
|
|
|
|
/// Behoerde oder Gegenstelle, die am Zug ist.
|
|
gegenstelle String?
|
|
aktenzeichen String?
|
|
|
|
/// Die Person bei der Gegenstelle, die den Vorgang bearbeitet. Ein
|
|
/// namentlich adressiertes Schreiben landet bei ihr statt in der
|
|
/// Poststelle - beim Bezirk ist das der Unterschied zwischen zwei
|
|
/// Wochen und zwei Monaten. SetNull: Ein Vorgang darf nicht daran
|
|
/// haengen, dass es den Kontakt noch gibt.
|
|
ansprechpartnerId String?
|
|
ansprechpartner Kontakt? @relation("VorgangAnsprechpartner", fields: [ansprechpartnerId], references: [id], onDelete: SetNull)
|
|
|
|
verantwortlich String?
|
|
angelegtVonId String?
|
|
angelegtVon User? @relation("VorgangAngelegtVon", fields: [angelegtVonId], references: [id], onDelete: SetNull)
|
|
|
|
/// "Was ist der naechste konkrete Handgriff?" - treibt das Dashboard.
|
|
naechsterSchritt String?
|
|
faelligAm DateTime? @db.Date
|
|
|
|
/// Wann wurde zuletzt mit der Gegenstelle gesprochen/geschrieben?
|
|
/// Grundlage fuer die Kachel "wartet auf andere" und die Sachstandsanfrage.
|
|
letzterKontaktAm DateTime? @db.Date
|
|
|
|
/// Loest die feste Ablaufkette Mitteilung -> Antrag -> Anhoerung ->
|
|
/// Beschluss -> Rechtskraft -> Vollzug aus.
|
|
genehmigungspflichtig Boolean @default(false)
|
|
|
|
/// Aus welcher Vorlage wurde der Vorgang erzeugt (fuer Auswertung)?
|
|
vorlageSchluessel String?
|
|
|
|
erledigtAm DateTime?
|
|
erstelltAm DateTime @default(now())
|
|
aktualisiertAm DateTime @updatedAt
|
|
geloeschtAm DateTime?
|
|
|
|
fristen Frist[]
|
|
buchungen Buchung[]
|
|
genehmigungen Genehmigung[]
|
|
kontaktprotokolle Kontaktprotokoll[]
|
|
aufgaben Aufgabe[]
|
|
verknuepfungen DokumentVerknuepfung[]
|
|
tags Tag[] @relation("VorgangTags")
|
|
kontakte Kontakt[] @relation("VorgangKontakte")
|
|
|
|
@@index([betreuteId, status])
|
|
@@index([betreuteId, kategorie])
|
|
@@index([faelligAm])
|
|
@@map("vorgang")
|
|
}
|
|
|
|
/// Einzelner Haken innerhalb eines Vorgangs. Vorgangs-Vorlagen bringen
|
|
/// ihre Aufgabenliste vorbefuellt mit.
|
|
model Aufgabe {
|
|
id String @id @default(cuid())
|
|
|
|
vorgangId String
|
|
vorgang Vorgang @relation(fields: [vorgangId], references: [id], onDelete: Cascade)
|
|
|
|
titel String
|
|
beschreibung String?
|
|
erledigt Boolean @default(false)
|
|
erledigtAm DateTime?
|
|
faelligAm DateTime? @db.Date
|
|
reihenfolge Int @default(0)
|
|
|
|
erstelltAm DateTime @default(now())
|
|
|
|
@@index([vorgangId])
|
|
@@map("aufgabe")
|
|
}
|
|
|
|
enum GenehmigungStatus {
|
|
GEPLANT // erkannt, noch nichts unternommen
|
|
MITGETEILT // Mitteilung ans Gericht raus
|
|
BEANTRAGT
|
|
ANGEHOERT // Anhoerung der Betreuten / des Verfahrenspflegers erfolgt
|
|
ERTEILT
|
|
VERSAGT
|
|
RECHTSKRAEFTIG
|
|
VOLLZOGEN // erst moeglich, wenn ein Beschlussdokument hinterlegt ist
|
|
}
|
|
|
|
model Genehmigung {
|
|
id String @id @default(cuid())
|
|
|
|
betreuteId String
|
|
betreute Betreute @relation(fields: [betreuteId], references: [id], onDelete: Cascade)
|
|
vorgangId String
|
|
vorgang Vorgang @relation(fields: [vorgangId], references: [id], onDelete: Cascade)
|
|
|
|
titel String
|
|
|
|
/// Freitext, z.B. "§ 1850 Nr. 1 BGB" - bewusst nicht als Enum, damit
|
|
/// auch seltene Tatbestaende erfasst werden koennen.
|
|
rechtsgrundlage String
|
|
status GenehmigungStatus @default(GEPLANT)
|
|
|
|
mitteilungAm DateTime? @db.Date
|
|
antragAm DateTime? @db.Date
|
|
anhoerungAm DateTime? @db.Date
|
|
beschlussAm DateTime? @db.Date
|
|
rechtskraftAm DateTime? @db.Date
|
|
vollzugAm DateTime? @db.Date
|
|
|
|
/// Ohne dieses Beschlussdokument darf der Status nicht auf VOLLZOGEN
|
|
/// wechseln (§ 1858 BGB: Genehmigung vor dem Rechtsgeschaeft).
|
|
beschlussDokumentId String? @unique
|
|
beschlussDokument Dokument? @relation("Beschlussdokument", fields: [beschlussDokumentId], references: [id], onDelete: SetNull)
|
|
|
|
notizenVerschluesselt String?
|
|
|
|
erstelltAm DateTime @default(now())
|
|
aktualisiertAm DateTime @updatedAt
|
|
geloeschtAm DateTime?
|
|
|
|
verknuepfungen DokumentVerknuepfung[]
|
|
|
|
@@index([betreuteId, status])
|
|
@@index([vorgangId])
|
|
@@map("genehmigung")
|
|
}
|
|
|
|
// =============================================================================
|
|
// Fristen
|
|
// =============================================================================
|
|
|
|
enum FristTyp {
|
|
GESETZLICH // z.B. Jahresbericht § 1863 BGB
|
|
BEHOERDLICH // vom Bezirk oder Gericht gesetzt
|
|
SELBST_GESETZT
|
|
WIEDERVORLAGE
|
|
}
|
|
|
|
enum FristStatus {
|
|
OFFEN
|
|
ERLEDIGT
|
|
VERSCHOBEN
|
|
ENTFALLEN
|
|
}
|
|
|
|
model Frist {
|
|
id String @id @default(cuid())
|
|
|
|
betreuteId String
|
|
betreute Betreute @relation(fields: [betreuteId], references: [id], onDelete: Cascade)
|
|
vorgangId String?
|
|
vorgang Vorgang? @relation(fields: [vorgangId], references: [id], onDelete: SetNull)
|
|
|
|
titel String
|
|
beschreibung String?
|
|
datum DateTime @db.Date
|
|
typ FristTyp @default(SELBST_GESETZT)
|
|
status FristStatus @default(OFFEN)
|
|
|
|
/// Freitext, z.B. "§ 1863 Abs. 3 BGB".
|
|
rechtsgrundlage String?
|
|
|
|
/// Tage vor dem Termin, an denen gewarnt wird.
|
|
vorwarnungTage Int[] @default([60, 30, 7, 1])
|
|
|
|
/// iCalendar-RRULE, z.B. "FREQ=YEARLY;INTERVAL=1". Beim Erledigen einer
|
|
/// wiederkehrenden Frist wird daraus die naechste erzeugt.
|
|
wiederholungRrule String?
|
|
|
|
/// Zeigt auf die Frist, aus der diese hervorgegangen ist.
|
|
vorgaengerFristId String? @unique
|
|
vorgaengerFrist Frist? @relation("Fristkette", fields: [vorgaengerFristId], references: [id], onDelete: SetNull)
|
|
nachfolgerFrist Frist? @relation("Fristkette")
|
|
|
|
/// Ausschlussfrist: laeuft sie ab, ist der Anspruch weg
|
|
/// (z.B. Aufwandspauschale § 1878 BGB). Wird deutlich rot markiert.
|
|
ausschlussfrist Boolean @default(false)
|
|
|
|
/// Automatisch aus dem Bestellungsdatum abgeleitete Pflichtfrist.
|
|
/// Verhindert doppeltes Anlegen beim erneuten Ableiten.
|
|
systemSchluessel String?
|
|
|
|
/// Wann wurde zuletzt eine Erinnerung verschickt (Vorwarnstufe in Tagen)?
|
|
letzteErinnerungStufe Int?
|
|
|
|
erledigtAm DateTime?
|
|
erstelltAm DateTime @default(now())
|
|
aktualisiertAm DateTime @updatedAt
|
|
geloeschtAm DateTime?
|
|
|
|
verknuepfungen DokumentVerknuepfung[]
|
|
|
|
@@unique([betreuteId, systemSchluessel])
|
|
@@index([betreuteId, status, datum])
|
|
@@index([datum])
|
|
@@map("frist")
|
|
}
|
|
|
|
// =============================================================================
|
|
// Dokumente
|
|
// =============================================================================
|
|
|
|
enum DokumentRichtung {
|
|
EINGANG
|
|
AUSGANG
|
|
INTERN
|
|
}
|
|
|
|
enum DokumentArt {
|
|
BESCHEID
|
|
ANTRAG
|
|
SCHREIBEN
|
|
RECHNUNG
|
|
KONTOAUSZUG
|
|
VERTRAG
|
|
BESCHLUSS
|
|
URKUNDE
|
|
GUTACHTEN
|
|
AUSWEIS
|
|
PROTOKOLL
|
|
QUITTUNG
|
|
MAHNUNG
|
|
VOLLMACHT
|
|
ATTEST
|
|
GRUNDBUCHAUSZUG
|
|
FOTO
|
|
SONSTIGES
|
|
}
|
|
|
|
enum PosteingangStatus {
|
|
NEU
|
|
GESICHTET
|
|
ZUGEORDNET
|
|
BEANTWORTET
|
|
}
|
|
|
|
model Dokument {
|
|
id String @id @default(cuid())
|
|
|
|
betreuteId String
|
|
betreute Betreute @relation(fields: [betreuteId], references: [id], onDelete: Cascade)
|
|
|
|
titel String
|
|
art DokumentArt @default(SONSTIGES)
|
|
richtung DokumentRichtung @default(EINGANG)
|
|
|
|
/// Datum des Schreibens (steht oben auf dem Brief).
|
|
schreibenVom DateTime? @db.Date
|
|
/// Wann es bei mir eingegangen ist - massgeblich fuer Rechtsbehelfsfristen.
|
|
eingangAm DateTime? @db.Date
|
|
versandAm DateTime? @db.Date
|
|
|
|
absender String?
|
|
empfaenger String?
|
|
aktenzeichen String?
|
|
|
|
// --- Datei im Objektspeicher --------------------------------------------
|
|
/// Schluessel im S3-Bucket. Ausgeliefert wird nur ueber kurzlebige
|
|
/// signierte URLs, niemals oeffentlich.
|
|
objektSchluessel String?
|
|
dateiname String?
|
|
mimeTyp String?
|
|
groesseBytes Int?
|
|
/// SHA-256 der Datei - Nachweis der Unveraendertheit gegenueber Gericht.
|
|
pruefsumme String?
|
|
seitenzahl Int?
|
|
|
|
/// Volltext aus der OCR (Tesseract, deutsch). Bleibt im Klartext, weil er
|
|
/// sonst nicht durchsuchbar waere; die Datei selbst liegt verschluesselt
|
|
/// im Objektspeicher. In der README als bewusste Abwaegung dokumentiert.
|
|
ocrText String?
|
|
ocrStatus String? // "ausstehend" | "laeuft" | "fertig" | "fehlgeschlagen" | "nicht_verfuegbar"
|
|
|
|
/// Von Postgres per Trigger gepflegter Suchvektor (deutsche Konfiguration).
|
|
suchvektor Unsupported("tsvector")?
|
|
|
|
posteingangStatus PosteingangStatus @default(NEU)
|
|
|
|
/// Wo liegt das Original im Papierordner? Papier bleibt Papier.
|
|
ablageortPapier String?
|
|
|
|
/// Wiedervorlage bzw. Ablaufdatum (z.B. Gueltigkeit Betreuerausweis).
|
|
wiedervorlageAm DateTime? @db.Date
|
|
ablaufAm DateTime? @db.Date
|
|
|
|
notizenVerschluesselt String?
|
|
|
|
erstelltAm DateTime @default(now())
|
|
aktualisiertAm DateTime @updatedAt
|
|
geloeschtAm DateTime?
|
|
|
|
verknuepfungen DokumentVerknuepfung[]
|
|
belegFuer Buchung[] @relation("Buchungsbeleg")
|
|
beschlussFuer Genehmigung? @relation("Beschlussdokument")
|
|
nachweisFuer Wertstand[] @relation("Wertnachweis")
|
|
tags Tag[] @relation("DokumentTags")
|
|
|
|
@@index([betreuteId, posteingangStatus])
|
|
@@index([betreuteId, eingangAm])
|
|
@@index([suchvektor], type: Gin)
|
|
@@map("dokument")
|
|
}
|
|
|
|
/// Verbindet ein Dokument mit genau einer Fachentitaet. Bewusst als eigene
|
|
/// Tabelle mit echten Fremdschluesseln - eine Akte muss referenzsicher sein.
|
|
/// Die Anwendung stellt sicher, dass genau eine Ziel-Spalte gesetzt ist.
|
|
model DokumentVerknuepfung {
|
|
id String @id @default(cuid())
|
|
|
|
betreuteId String
|
|
betreute Betreute @relation(fields: [betreuteId], references: [id], onDelete: Cascade)
|
|
|
|
dokumentId String
|
|
dokument Dokument @relation(fields: [dokumentId], references: [id], onDelete: Cascade)
|
|
|
|
vorgangId String?
|
|
vorgang Vorgang? @relation(fields: [vorgangId], references: [id], onDelete: Cascade)
|
|
fristId String?
|
|
frist Frist? @relation(fields: [fristId], references: [id], onDelete: Cascade)
|
|
buchungId String?
|
|
buchung Buchung? @relation(fields: [buchungId], references: [id], onDelete: Cascade)
|
|
vermoegenspostenId String?
|
|
vermoegensposten Vermoegensposten? @relation(fields: [vermoegenspostenId], references: [id], onDelete: Cascade)
|
|
immobilieId String?
|
|
immobilie Immobilie? @relation(fields: [immobilieId], references: [id], onDelete: Cascade)
|
|
vertragId String?
|
|
vertrag Vertrag? @relation(fields: [vertragId], references: [id], onDelete: Cascade)
|
|
kontaktprotokollId String?
|
|
kontaktprotokoll Kontaktprotokoll? @relation(fields: [kontaktprotokollId], references: [id], onDelete: Cascade)
|
|
gesundheitsereignisId String?
|
|
gesundheitsereignis Gesundheitsereignis? @relation(fields: [gesundheitsereignisId], references: [id], onDelete: Cascade)
|
|
genehmigungId String?
|
|
genehmigung Genehmigung? @relation(fields: [genehmigungId], references: [id], onDelete: Cascade)
|
|
nachforderungId String?
|
|
nachforderung Nachforderung? @relation(fields: [nachforderungId], references: [id], onDelete: Cascade)
|
|
bescheidId String?
|
|
bescheid Bescheid? @relation(fields: [bescheidId], references: [id], onDelete: Cascade)
|
|
kontaktId String?
|
|
kontakt Kontakt? @relation(fields: [kontaktId], references: [id], onDelete: Cascade)
|
|
|
|
bemerkung String?
|
|
|
|
erstelltAm DateTime @default(now())
|
|
|
|
@@index([dokumentId])
|
|
@@index([betreuteId])
|
|
@@index([vorgangId])
|
|
@@map("dokument_verknuepfung")
|
|
}
|
|
|
|
model Tag {
|
|
id String @id @default(cuid())
|
|
|
|
betreuteId String
|
|
betreute Betreute @relation(fields: [betreuteId], references: [id], onDelete: Cascade)
|
|
|
|
name String
|
|
farbe String @default("slate")
|
|
|
|
erstelltAm DateTime @default(now())
|
|
|
|
dokumente Dokument[] @relation("DokumentTags")
|
|
vorgaenge Vorgang[] @relation("VorgangTags")
|
|
|
|
@@unique([betreuteId, name])
|
|
@@map("tag")
|
|
}
|
|
|
|
// =============================================================================
|
|
// Geld: Konten, Kassenbuch, Vermoegen
|
|
// =============================================================================
|
|
|
|
enum KontoArt {
|
|
GIRO
|
|
SPAR
|
|
TAGESGELD
|
|
FESTGELD
|
|
DEPOT
|
|
VERWAHRGELD_HEIM // Taschengeldkonto in der Einrichtung
|
|
BARKASSE
|
|
SONSTIGES
|
|
}
|
|
|
|
model Konto {
|
|
id String @id @default(cuid())
|
|
|
|
betreuteId String
|
|
betreute Betreute @relation(fields: [betreuteId], references: [id], onDelete: Cascade)
|
|
|
|
bezeichnung String
|
|
bank String?
|
|
art KontoArt @default(GIRO)
|
|
|
|
ibanVerschluesselt String?
|
|
/// HMAC-Blindindex der IBAN - erlaubt "gibt es dieses Konto schon?",
|
|
/// ohne die IBAN im Klartext zu speichern.
|
|
ibanIndex String?
|
|
bic String?
|
|
|
|
/// Konto des Betreuers, nicht der Betreuten. Buchungen darauf werden
|
|
/// als Auslage gefuehrt und loesen eine Warnung aus.
|
|
istBetreuerkonto Boolean @default(false)
|
|
|
|
eroeffnetAm DateTime? @db.Date
|
|
geschlossenAm DateTime? @db.Date
|
|
|
|
erstelltAm DateTime @default(now())
|
|
aktualisiertAm DateTime @updatedAt
|
|
geloeschtAm DateTime?
|
|
|
|
buchungen Buchung[]
|
|
saldostaende Saldostand[]
|
|
vermoegensposten Vermoegensposten[]
|
|
|
|
@@index([betreuteId])
|
|
@@index([ibanIndex])
|
|
@@map("konto")
|
|
}
|
|
|
|
/// Kontostand zu einem Stichtag. Wird nie ueberschrieben - jede
|
|
/// Vermoegensuebersicht muss spaeter reproduzierbar bleiben.
|
|
model Saldostand {
|
|
id String @id @default(cuid())
|
|
|
|
kontoId String
|
|
konto Konto @relation(fields: [kontoId], references: [id], onDelete: Cascade)
|
|
|
|
stichtag DateTime @db.Date
|
|
betragCent BigInt
|
|
quelle String? // "Kontoauszug Nr. 3", "Online-Banking", ...
|
|
|
|
erstelltAm DateTime @default(now())
|
|
|
|
@@unique([kontoId, stichtag])
|
|
@@index([kontoId, stichtag])
|
|
@@map("saldostand")
|
|
}
|
|
|
|
enum BuchungRichtung {
|
|
EINNAHME
|
|
AUSGABE
|
|
UMBUCHUNG
|
|
}
|
|
|
|
enum BuchungKategorie {
|
|
RENTE
|
|
PFLEGEKASSE
|
|
SOZIALHILFE
|
|
LANDESPFLEGEGELD
|
|
ZINSEN
|
|
MIETEINNAHME
|
|
SONSTIGE_EINNAHME
|
|
HEIMKOSTEN
|
|
BARBETRAG
|
|
BEKLEIDUNG
|
|
ARZT_UND_MEDIKAMENTE
|
|
ZUZAHLUNG
|
|
VERSICHERUNG
|
|
IMMOBILIE_BETRIEBSKOSTEN
|
|
IMMOBILIE_INSTANDHALTUNG
|
|
GRUNDSTEUER
|
|
STEUERN
|
|
GEBUEHREN_GERICHT
|
|
PORTO_UND_KOPIEN
|
|
FAHRTKOSTEN
|
|
TELEFON_INTERNET
|
|
RUNDFUNKBEITRAG
|
|
BESTATTUNGSVORSORGE
|
|
BANKGEBUEHREN
|
|
SONSTIGE_AUSGABE
|
|
UMBUCHUNG
|
|
}
|
|
|
|
/// Eine Gruppe wiederkehrender Zahlungen, zu der bewusst KEIN Vertrag
|
|
/// gefuehrt wird - Miete, Strom, Rundfunkbeitrag, Finanzamt.
|
|
///
|
|
/// Die Entscheidung haengt am normalisierten Namen der Gegenstelle, nicht
|
|
/// an den Buchungen. Nur so ueberlebt sie den naechsten Kontoauszug: Beim
|
|
/// Import entstehen neue Buchungszeilen, der Empfaenger bleibt derselbe.
|
|
///
|
|
/// ACHTUNG: Der Schluessel wird von normalisieren() in
|
|
/// src/lib/finanzen/wiederkehrend.ts gebildet. Aendert sich diese Funktion,
|
|
/// passen bestehende Schluessel nicht mehr und abgehakte Gruppen tauchen
|
|
/// wieder auf. Das ist unangenehm, aber ungefaehrlich - schlimmer waere,
|
|
/// eine Gruppe stillschweigend weiter zu verbergen, die inzwischen anders
|
|
/// zugeschnitten ist.
|
|
/// Eine eigene Schreibvorlage.
|
|
///
|
|
/// Die mitgelieferten Vorlagen (Rueckkaufswert, Kuendigung,
|
|
/// Sachstandsanfrage) stehen bewusst NICHT in dieser Tabelle - sie sind
|
|
/// Code, getestet und unveraenderlich. "Wiederherstellen" heisst: die
|
|
/// mitgelieferte Fassung als neue Kopie hierher holen.
|
|
///
|
|
/// Der Text traegt Platzhalter im Format {{name}} - aufgeloest in
|
|
/// src/lib/schreiben/platzhalter.ts. Er ist bewusst NICHT verschluesselt:
|
|
/// Eine Vorlage ist ein Textbaustein ohne Fallbezug; die Fachdaten kommen
|
|
/// erst beim Erzeugen hinein.
|
|
model Schreibvorlage {
|
|
id String @id @default(cuid())
|
|
|
|
betreuteId String
|
|
betreute Betreute @relation(fields: [betreuteId], references: [id], onDelete: Cascade)
|
|
|
|
name String
|
|
betreff String @default("")
|
|
text String
|
|
|
|
/// Beim Speichern bestaetigt, dass der fehlende Bestellungsabsatz
|
|
/// Absicht ist - dann wird nicht bei jedem Bearbeiten erneut gewarnt.
|
|
ohneBestellungsabsatz Boolean @default(false)
|
|
|
|
erstelltAm DateTime @default(now())
|
|
aktualisiertAm DateTime @updatedAt
|
|
geloeschtAm DateTime?
|
|
|
|
@@index([betreuteId])
|
|
@@map("schreibvorlage")
|
|
}
|
|
|
|
model ZahlungsgruppeGeklaert {
|
|
id String @id @default(cuid())
|
|
|
|
betreuteId String
|
|
betreute Betreute @relation(fields: [betreuteId], references: [id], onDelete: Cascade)
|
|
|
|
/// Normalisierter Name der Gegenstelle.
|
|
schluessel String
|
|
|
|
/// Die Schreibweise, wie sie beim Abhaken auf dem Bildschirm stand -
|
|
/// damit spaeter noch erkennbar ist, worum es ging.
|
|
anzeigename String
|
|
|
|
/// Warum kein Vertrag? Freiwillig, aber in einem Jahr Gold wert.
|
|
begruendung String?
|
|
|
|
erstelltAm DateTime @default(now())
|
|
|
|
@@unique([betreuteId, schluessel])
|
|
@@index([betreuteId])
|
|
@@map("zahlungsgruppe_geklaert")
|
|
}
|
|
|
|
model Buchung {
|
|
id String @id @default(cuid())
|
|
|
|
betreuteId String
|
|
betreute Betreute @relation(fields: [betreuteId], references: [id], onDelete: Cascade)
|
|
|
|
kontoId String?
|
|
konto Konto? @relation(fields: [kontoId], references: [id], onDelete: SetNull)
|
|
|
|
vorgangId String?
|
|
vorgang Vorgang? @relation(fields: [vorgangId], references: [id], onDelete: SetNull)
|
|
|
|
datum DateTime @db.Date
|
|
wertstellung DateTime? @db.Date
|
|
betragCent BigInt
|
|
richtung BuchungRichtung
|
|
kategorie BuchungKategorie @default(SONSTIGE_AUSGABE)
|
|
gegenstelle String?
|
|
verwendungszweckVerschluesselt String?
|
|
|
|
belegId String?
|
|
beleg Dokument? @relation("Buchungsbeleg", fields: [belegId], references: [id], onDelete: SetNull)
|
|
/// Buchung ohne Beleg wird in der Liste markiert - Belegzwang.
|
|
belegFehltBegruendung String?
|
|
|
|
/// Dauerzahlung zu einem Vertrag. Bei einem Dauerschuldverhaeltnis IST
|
|
/// der Vertrag der Beleg - fuer zwoelf gleiche Lastschriften gibt es
|
|
/// keine zwoelf Quittungen, sondern eine Police.
|
|
///
|
|
/// onDelete: SetNull ist Absicht und Teil der Zusage: Verschwindet der
|
|
/// Vertrag, faellt die Buchung zurueck in "Beleg fehlt". Ein Nachweis,
|
|
/// der auf etwas zeigt, das es nicht mehr gibt, waere schlimmer als
|
|
/// gar keiner.
|
|
vertragId String?
|
|
vertrag Vertrag? @relation("Vertragszahlung", fields: [vertragId], references: [id], onDelete: SetNull)
|
|
|
|
/// Wurde die Zuordnung beim Import automatisch gesetzt? Dann ist sie
|
|
/// eine Vermutung der Maschine und in der Liste als solche erkennbar.
|
|
vertragAutomatisch Boolean @default(false)
|
|
|
|
/// false = Auslage des Betreuers, die erstattet werden muss.
|
|
betrifftBetreute Boolean @default(true)
|
|
/// Verschiebt das Vermoegen (z.B. Verkaufserloes), nicht nur laufende
|
|
/// Einnahme/Ausgabe. Relevant fuer die Vermoegensuebersicht.
|
|
vermoegenswirksam Boolean @default(false)
|
|
|
|
/// Herkunft eines CSV-/CAMT-Imports, verhindert Doppelbuchungen.
|
|
importReferenz String?
|
|
|
|
erstelltAm DateTime @default(now())
|
|
aktualisiertAm DateTime @updatedAt
|
|
geloeschtAm DateTime?
|
|
|
|
verknuepfungen DokumentVerknuepfung[]
|
|
|
|
@@unique([kontoId, importReferenz])
|
|
@@index([betreuteId, datum])
|
|
@@index([betreuteId, kategorie])
|
|
@@index([vertragId])
|
|
@@map("buchung")
|
|
}
|
|
|
|
enum VermoegensArt {
|
|
KONTO
|
|
IMMOBILIE
|
|
VERSICHERUNG
|
|
FAHRZEUG
|
|
WERTGEGENSTAND
|
|
FORDERUNG
|
|
VERBINDLICHKEIT
|
|
SONSTIGES
|
|
}
|
|
|
|
/// Bewertung eines Postens im Sozialhilfeverfahren. Das ist MEINE
|
|
/// Dokumentation und Argumentation, keine automatische Rechtsauskunft.
|
|
enum SozialhilfeBewertung {
|
|
UNGEPRUEFT
|
|
VERWERTBAR
|
|
GESCHUETZT
|
|
STRITTIG
|
|
}
|
|
|
|
model Vermoegensposten {
|
|
id String @id @default(cuid())
|
|
|
|
betreuteId String
|
|
betreute Betreute @relation(fields: [betreuteId], references: [id], onDelete: Cascade)
|
|
|
|
bezeichnung String
|
|
art VermoegensArt
|
|
|
|
// Optionale Verbindung zum konkreten Objekt.
|
|
kontoId String?
|
|
konto Konto? @relation(fields: [kontoId], references: [id], onDelete: SetNull)
|
|
immobilieId String?
|
|
immobilie Immobilie? @relation(fields: [immobilieId], references: [id], onDelete: SetNull)
|
|
vertragId String?
|
|
vertrag Vertrag? @relation(fields: [vertragId], references: [id], onDelete: SetNull)
|
|
|
|
/// Passiva werden als eigener Posten mit Art VERBINDLICHKEIT gefuehrt;
|
|
/// der Wert bleibt positiv, das Vorzeichen ergibt sich aus der Art.
|
|
bewertungSozialhilfe SozialhilfeBewertung @default(UNGEPRUEFT)
|
|
/// Warum halte ich den Posten fuer geschuetzt? Verweis auf § 90 SGB XII,
|
|
/// Schonvermoegen, Bestattungsvorsorge, selbst bewohntes Hausgrundstueck.
|
|
bewertungBegruendungVerschluesselt String?
|
|
|
|
notizenVerschluesselt String?
|
|
|
|
erstelltAm DateTime @default(now())
|
|
aktualisiertAm DateTime @updatedAt
|
|
geloeschtAm DateTime?
|
|
|
|
wertstaende Wertstand[]
|
|
verknuepfungen DokumentVerknuepfung[]
|
|
|
|
@@index([betreuteId, art])
|
|
@@map("vermoegensposten")
|
|
}
|
|
|
|
/// Ein Wert zu einem Stichtag. Historie, nichts wird ueberschrieben.
|
|
model Wertstand {
|
|
id String @id @default(cuid())
|
|
|
|
vermoegenspostenId String
|
|
vermoegensposten Vermoegensposten @relation(fields: [vermoegenspostenId], references: [id], onDelete: Cascade)
|
|
|
|
stichtag DateTime @db.Date
|
|
wertCent BigInt
|
|
/// Woher stammt der Wert? "Rueckkaufswertmitteilung vom 12.03.2026",
|
|
/// "Maklergutachten", "Bodenrichtwert", "eigene Schaetzung".
|
|
wertquelle String
|
|
|
|
nachweisDokumentId String?
|
|
nachweisDokument Dokument? @relation("Wertnachweis", fields: [nachweisDokumentId], references: [id], onDelete: SetNull)
|
|
|
|
bemerkung String?
|
|
erstelltAm DateTime @default(now())
|
|
|
|
@@unique([vermoegenspostenId, stichtag])
|
|
@@index([vermoegenspostenId, stichtag])
|
|
@@map("wertstand")
|
|
}
|
|
|
|
// =============================================================================
|
|
// Immobilie
|
|
// =============================================================================
|
|
|
|
enum ImmobilienStatusSozialhilfe {
|
|
UNGEKLAERT
|
|
SELBST_BEWOHNT_GESCHUETZT
|
|
HAERTEFALL_GELTEND_GEMACHT
|
|
DARLEHEN_91_SGB_XII
|
|
VERKAUF_LAEUFT
|
|
VERWERTET
|
|
}
|
|
|
|
model Immobilie {
|
|
id String @id @default(cuid())
|
|
|
|
betreuteId String
|
|
betreute Betreute @relation(fields: [betreuteId], references: [id], onDelete: Cascade)
|
|
|
|
bezeichnung String
|
|
strasse String?
|
|
plz String?
|
|
ort String?
|
|
|
|
grundbuchamt String?
|
|
grundbuchBlatt String?
|
|
gemarkung String?
|
|
flurstueck String?
|
|
eigentumsanteil String? // "1/1", "1/2", ...
|
|
wohnflaecheQm Int?
|
|
grundstueckQm Int?
|
|
baujahr Int?
|
|
|
|
bewohntStatus String? // "leerstehend", "vermietet", "selbst bewohnt"
|
|
leerstandSeit DateTime? @db.Date
|
|
|
|
statusSozialhilfe ImmobilienStatusSozialhilfe @default(UNGEKLAERT)
|
|
statusBegruendungVerschluesselt String?
|
|
|
|
notizenVerschluesselt String?
|
|
|
|
erstelltAm DateTime @default(now())
|
|
aktualisiertAm DateTime @updatedAt
|
|
geloeschtAm DateTime?
|
|
|
|
belastungen Belastung[]
|
|
checklistenPunkte ImmobilienCheckpunkt[]
|
|
vermoegensposten Vermoegensposten[]
|
|
verknuepfungen DokumentVerknuepfung[]
|
|
|
|
@@index([betreuteId])
|
|
@@map("immobilie")
|
|
}
|
|
|
|
model Belastung {
|
|
id String @id @default(cuid())
|
|
|
|
immobilieId String
|
|
immobilie Immobilie @relation(fields: [immobilieId], references: [id], onDelete: Cascade)
|
|
|
|
art String // "Grundschuld", "Wohnrecht", "Niessbrauch", "Reallast"
|
|
abteilung String? // Grundbuch-Abteilung II oder III
|
|
laufendeNr String?
|
|
glaeubiger String?
|
|
betragCent BigInt?
|
|
bemerkung String?
|
|
geloeschtImGrundbuchAm DateTime? @db.Date
|
|
|
|
erstelltAm DateTime @default(now())
|
|
|
|
@@index([immobilieId])
|
|
@@map("belastung")
|
|
}
|
|
|
|
/// Pflichten bei Leerstand: Versicherung informiert, Frostschutz,
|
|
/// Winterdienst, Zaehlerstaende, Schluesselverzeichnis, Fotodoku.
|
|
model ImmobilienCheckpunkt {
|
|
id String @id @default(cuid())
|
|
|
|
immobilieId String
|
|
immobilie Immobilie @relation(fields: [immobilieId], references: [id], onDelete: Cascade)
|
|
|
|
titel String
|
|
beschreibung String?
|
|
erledigt Boolean @default(false)
|
|
erledigtAm DateTime? @db.Date
|
|
faelligAm DateTime? @db.Date
|
|
/// Wiederkehrend (z.B. Kontrollgang, Zaehlerstand) als RRULE.
|
|
wiederholungRrule String?
|
|
reihenfolge Int @default(0)
|
|
|
|
erstelltAm DateTime @default(now())
|
|
|
|
@@index([immobilieId])
|
|
@@map("immobilien_checkpunkt")
|
|
}
|
|
|
|
// =============================================================================
|
|
// Versicherungen und Vertraege
|
|
// =============================================================================
|
|
|
|
enum VertragArt {
|
|
LEBENSVERSICHERUNG
|
|
STERBEGELDVERSICHERUNG
|
|
BESTATTUNGSVORSORGE
|
|
RENTENVERSICHERUNG_PRIVAT
|
|
UNFALLVERSICHERUNG
|
|
HAFTPFLICHT
|
|
HAUSRAT
|
|
WOHNGEBAEUDE
|
|
KRANKENZUSATZ
|
|
PFLEGEZUSATZ
|
|
RECHTSSCHUTZ
|
|
STERBEKASSE
|
|
STROM
|
|
GAS
|
|
WASSER
|
|
TELEFON_INTERNET
|
|
RUNDFUNKBEITRAG
|
|
MITGLIEDSCHAFT
|
|
ABONNEMENT
|
|
SONSTIGES
|
|
}
|
|
|
|
enum VertragStatus {
|
|
AKTIV
|
|
BEITRAGSFREI
|
|
RUHEND
|
|
GEKUENDIGT
|
|
ABGETRETEN
|
|
VERWERTET
|
|
ABGELAUFEN
|
|
}
|
|
|
|
enum ZahlRhythmus {
|
|
MONATLICH
|
|
VIERTELJAEHRLICH
|
|
HALBJAEHRLICH
|
|
JAEHRLICH
|
|
EINMALIG
|
|
}
|
|
|
|
model Vertrag {
|
|
id String @id @default(cuid())
|
|
|
|
betreuteId String
|
|
betreute Betreute @relation(fields: [betreuteId], references: [id], onDelete: Cascade)
|
|
|
|
bezeichnung String
|
|
gesellschaft String?
|
|
art VertragArt
|
|
status VertragStatus @default(AKTIV)
|
|
|
|
vertragsnummerVerschluesselt String?
|
|
|
|
beginnAm DateTime? @db.Date
|
|
endeAm DateTime? @db.Date
|
|
/// Kuendigungsfrist in Monaten zum Ende der Laufzeit.
|
|
kuendigungsfristMonate Int?
|
|
kuendigungZum DateTime? @db.Date
|
|
gekuendigtAm DateTime? @db.Date
|
|
|
|
beitragCent BigInt?
|
|
rhythmus ZahlRhythmus?
|
|
|
|
/// Bewertung fuer das Sozialamt - meine Argumentation, keine Auskunft.
|
|
bewertungSozialhilfe SozialhilfeBewertung @default(UNGEPRUEFT)
|
|
bewertungBegruendungVerschluesselt String?
|
|
|
|
/// Wann wurde zuletzt ein Rueckkaufswert angefordert? Steuert die
|
|
/// Wiedervorlage nach vier Wochen.
|
|
rueckkaufswertAngefragtAm DateTime? @db.Date
|
|
|
|
notizenVerschluesselt String?
|
|
|
|
erstelltAm DateTime @default(now())
|
|
aktualisiertAm DateTime @updatedAt
|
|
geloeschtAm DateTime?
|
|
|
|
vermoegensposten Vermoegensposten[]
|
|
verknuepfungen DokumentVerknuepfung[]
|
|
zahlungen Buchung[] @relation("Vertragszahlung")
|
|
|
|
@@index([betreuteId, status])
|
|
@@map("vertrag")
|
|
}
|
|
|
|
// =============================================================================
|
|
// Kontakte und Kontaktprotokoll
|
|
// =============================================================================
|
|
|
|
/// Anrede fuer die Briefanrede eines Kontakts.
|
|
enum KontaktAnrede {
|
|
OHNE
|
|
FRAU
|
|
HERR
|
|
}
|
|
|
|
enum KontaktRolle {
|
|
RECHTSPFLEGER
|
|
RICHTER
|
|
BETREUUNGSBEHOERDE
|
|
SACHBEARBEITER_BEZIRK
|
|
SACHBEARBEITER_SOZIALAMT
|
|
HEIMLEITUNG
|
|
PFLEGEDIENSTLEITUNG
|
|
SOZIALDIENST_HEIM
|
|
HAUSARZT
|
|
FACHARZT
|
|
KRANKENKASSE
|
|
PFLEGEKASSE
|
|
RENTENVERSICHERUNG
|
|
NOTAR
|
|
MAKLER
|
|
VERSICHERUNGSMAKLER
|
|
VERSICHERUNG
|
|
STEUERBERATER
|
|
RECHTSANWALT
|
|
BANK
|
|
HANDWERKER
|
|
ANGEHOERIGER
|
|
SONSTIGES
|
|
}
|
|
|
|
model Kontakt {
|
|
id String @id @default(cuid())
|
|
|
|
betreuteId String
|
|
betreute Betreute @relation(fields: [betreuteId], references: [id], onDelete: Cascade)
|
|
|
|
name String
|
|
organisation String?
|
|
rolle KontaktRolle @default(SONSTIGES)
|
|
/// Fuer die Briefanrede. Ohne Angabe wird mit vollem Namen angeredet -
|
|
/// nie geraten, das faellt dem Empfaenger sofort auf.
|
|
anrede KontaktAnrede @default(OHNE)
|
|
|
|
telefon String?
|
|
mobil String?
|
|
fax String?
|
|
email String?
|
|
strasse String?
|
|
plz String?
|
|
ort String?
|
|
/// Aktenzeichen, unter dem dieser Kontakt den Vorgang fuehrt.
|
|
aktenzeichen String?
|
|
|
|
notizenVerschluesselt String?
|
|
|
|
erstelltAm DateTime @default(now())
|
|
aktualisiertAm DateTime @updatedAt
|
|
geloeschtAm DateTime?
|
|
|
|
protokolle Kontaktprotokoll[]
|
|
vorgaenge Vorgang[] @relation("VorgangKontakte")
|
|
betreuteVorgaenge Vorgang[] @relation("VorgangAnsprechpartner")
|
|
verknuepfungen DokumentVerknuepfung[]
|
|
|
|
@@index([betreuteId, rolle])
|
|
@@map("kontakt")
|
|
}
|
|
|
|
enum KontaktArt {
|
|
BESUCH
|
|
TELEFONAT
|
|
EMAIL
|
|
BRIEF
|
|
VIDEOCALL
|
|
FAX
|
|
VORSPRACHE
|
|
}
|
|
|
|
/// Grundlage des Jahresberichts nach § 1863 BGB: persoenliche Kontakte,
|
|
/// Wuensche der betreuten Person, daraus folgende Aufgaben.
|
|
model Kontaktprotokoll {
|
|
id String @id @default(cuid())
|
|
|
|
betreuteId String
|
|
betreute Betreute @relation(fields: [betreuteId], references: [id], onDelete: Cascade)
|
|
|
|
kontaktId String?
|
|
kontakt Kontakt? @relation(fields: [kontaktId], references: [id], onDelete: SetNull)
|
|
vorgangId String?
|
|
vorgang Vorgang? @relation(fields: [vorgangId], references: [id], onDelete: SetNull)
|
|
|
|
datum DateTime
|
|
art KontaktArt
|
|
dauerMinuten Int?
|
|
gespraechspartner String?
|
|
/// true = persoenlicher Kontakt zur betreuten Person selbst.
|
|
mitBetreuter Boolean @default(false)
|
|
|
|
inhaltVerschluesselt String?
|
|
/// Geaeusserte Wuensche - fuer § 1821 BGB und den Jahresbericht.
|
|
wuenscheVerschluesselt String?
|
|
folgeaufgabenVerschluesselt String?
|
|
|
|
erstelltAm DateTime @default(now())
|
|
aktualisiertAm DateTime @updatedAt
|
|
geloeschtAm DateTime?
|
|
|
|
verknuepfungen DokumentVerknuepfung[]
|
|
|
|
@@index([betreuteId, datum])
|
|
@@map("kontaktprotokoll")
|
|
}
|
|
|
|
// =============================================================================
|
|
// Gesundheit und Pflege
|
|
// =============================================================================
|
|
|
|
enum GesundheitsArt {
|
|
ARZTBESUCH
|
|
KRANKENHAUS
|
|
EXAZERBATION
|
|
STURZ
|
|
MEDIKATIONSAENDERUNG
|
|
SAUERSTOFFVERSORGUNG
|
|
INHALATION
|
|
HILFSMITTEL
|
|
IMPFUNG
|
|
REHA
|
|
PFLEGEBEGUTACHTUNG
|
|
HEILMITTEL
|
|
SONSTIGES
|
|
}
|
|
|
|
model Gesundheitsereignis {
|
|
id String @id @default(cuid())
|
|
|
|
betreuteId String
|
|
betreute Betreute @relation(fields: [betreuteId], references: [id], onDelete: Cascade)
|
|
|
|
datum DateTime @db.Date
|
|
bisDatum DateTime? @db.Date // z.B. Ende eines Krankenhausaufenthalts
|
|
art GesundheitsArt
|
|
|
|
titel String
|
|
beschreibungVerschluesselt String?
|
|
behandler String?
|
|
einrichtung String?
|
|
|
|
/// Abwesenheitstage mindern den Heimeigenanteil - deshalb hier zaehlbar.
|
|
abwesenheitstage Int?
|
|
|
|
erstelltAm DateTime @default(now())
|
|
aktualisiertAm DateTime @updatedAt
|
|
geloeschtAm DateTime?
|
|
|
|
verknuepfungen DokumentVerknuepfung[]
|
|
|
|
@@index([betreuteId, datum])
|
|
@@map("gesundheitsereignis")
|
|
}
|
|
|
|
model Heimvertrag {
|
|
id String @id @default(cuid())
|
|
|
|
betreuteId String @unique
|
|
betreute Betreute @relation(fields: [betreuteId], references: [id], onDelete: Cascade)
|
|
|
|
einrichtung String
|
|
traeger String?
|
|
strasse String?
|
|
plz String?
|
|
ort String?
|
|
|
|
vertragAm DateTime? @db.Date
|
|
einzugAm DateTime? @db.Date
|
|
auszugAm DateTime? @db.Date
|
|
kuendigungsfristTage Int? @default(14)
|
|
|
|
// Aktuelle Saetze - historische Werte stehen in Heimkostenmonat.
|
|
pflegeEigenanteilCent BigInt? // einrichtungseinheitlicher Eigenanteil
|
|
unterkunftVerpflegungCent BigInt?
|
|
investitionskostenCent BigInt?
|
|
ausbildungsumlageCent BigInt?
|
|
|
|
notizenVerschluesselt String?
|
|
|
|
erstelltAm DateTime @default(now())
|
|
aktualisiertAm DateTime @updatedAt
|
|
|
|
@@map("heimvertrag")
|
|
}
|
|
|
|
/// Monatliche Gegenueberstellung Heimrechnung gegen Deckungsquellen.
|
|
model Heimkostenmonat {
|
|
id String @id @default(cuid())
|
|
|
|
betreuteId String
|
|
betreute Betreute @relation(fields: [betreuteId], references: [id], onDelete: Cascade)
|
|
|
|
jahr Int
|
|
monat Int // 1..12
|
|
|
|
// Kostenseite
|
|
pflegeEigenanteilCent BigInt @default(0)
|
|
unterkunftVerpflegungCent BigInt @default(0)
|
|
investitionskostenCent BigInt @default(0)
|
|
ausbildungsumlageCent BigInt @default(0)
|
|
zusatzleistungenCent BigInt @default(0)
|
|
|
|
// Deckungsseite
|
|
pflegekasseCent BigInt @default(0) // § 43 SGB XI
|
|
/// Leistungszuschlag nach § 43c SGB XI, abhaengig von der Verweildauer.
|
|
leistungszuschlagProzent Int @default(0)
|
|
leistungszuschlagCent BigInt @default(0)
|
|
renteCent BigInt @default(0)
|
|
sonstigeEinkuenfteCent BigInt @default(0)
|
|
sozialhilfeCent BigInt @default(0)
|
|
eigenmittelCent BigInt @default(0)
|
|
|
|
// Der betreuten Person verbleibende Betraege
|
|
barbetragCent BigInt @default(0)
|
|
bekleidungspauschaleCent BigInt @default(0)
|
|
|
|
abwesenheitstage Int @default(0)
|
|
|
|
rechnungDokumentId String?
|
|
bemerkung String?
|
|
|
|
erstelltAm DateTime @default(now())
|
|
aktualisiertAm DateTime @updatedAt
|
|
|
|
@@unique([betreuteId, jahr, monat])
|
|
@@index([betreuteId, jahr])
|
|
@@map("heimkostenmonat")
|
|
}
|
|
|
|
// =============================================================================
|
|
// Sozialhilfe (Bezirk Oberfranken)
|
|
// =============================================================================
|
|
|
|
enum NachforderungStatus {
|
|
OFFEN
|
|
IN_BESCHAFFUNG
|
|
UEBERSANDT
|
|
ANERKANNT
|
|
NICHT_VORHANDEN
|
|
}
|
|
|
|
enum BescheidArt {
|
|
BEWILLIGUNG
|
|
ABLEHNUNG
|
|
AENDERUNG
|
|
AUFHEBUNG
|
|
ERSTATTUNG
|
|
VORSCHUSS
|
|
WIDERSPRUCHSBESCHEID
|
|
}
|
|
|
|
enum BescheidPruefStatus {
|
|
UNGEPRUEFT
|
|
GEPRUEFT_OK
|
|
WIDERSPRUCH_EINGELEGT
|
|
WIDERSPRUCH_ERLEDIGT
|
|
FRIST_VERSTRICHEN
|
|
}
|
|
|
|
model SozialhilfeAntrag {
|
|
id String @id @default(cuid())
|
|
|
|
betreuteId String
|
|
betreute Betreute @relation(fields: [betreuteId], references: [id], onDelete: Cascade)
|
|
|
|
traeger String @default("Bezirk Oberfranken")
|
|
leistungsart String @default("Hilfe zur Pflege nach dem 7. Kapitel SGB XII")
|
|
aktenzeichen String?
|
|
|
|
sachbearbeiter String?
|
|
telefon String?
|
|
email String?
|
|
|
|
antragAm DateTime? @db.Date
|
|
eingangBeimTraegerAm DateTime? @db.Date
|
|
bewilligtVon DateTime? @db.Date
|
|
bewilligtBis DateTime? @db.Date
|
|
|
|
notizenVerschluesselt String?
|
|
|
|
erstelltAm DateTime @default(now())
|
|
aktualisiertAm DateTime @updatedAt
|
|
geloeschtAm DateTime?
|
|
|
|
nachforderungen Nachforderung[]
|
|
bescheide Bescheid[]
|
|
|
|
@@index([betreuteId])
|
|
@@map("sozialhilfe_antrag")
|
|
}
|
|
|
|
/// Jede vom Traeger angeforderte Unterlage als eigene Zeile.
|
|
model Nachforderung {
|
|
id String @id @default(cuid())
|
|
|
|
antragId String
|
|
antrag SozialhilfeAntrag @relation(fields: [antragId], references: [id], onDelete: Cascade)
|
|
|
|
bezeichnung String
|
|
erlaeuterung String?
|
|
status NachforderungStatus @default(OFFEN)
|
|
|
|
angefordertAm DateTime? @db.Date
|
|
fristBis DateTime? @db.Date
|
|
uebersandtAm DateTime? @db.Date
|
|
/// Wenn die Unterlage nicht existiert: warum nicht? Geht als Erklaerung
|
|
/// mit ins Unterlagenpaket.
|
|
begruendungNichtVorhanden String?
|
|
|
|
reihenfolge Int @default(0)
|
|
/// Vorbefuellte Standardunterlage aus der Checkliste.
|
|
ausCheckliste Boolean @default(false)
|
|
|
|
erstelltAm DateTime @default(now())
|
|
aktualisiertAm DateTime @updatedAt
|
|
geloeschtAm DateTime?
|
|
|
|
verknuepfungen DokumentVerknuepfung[]
|
|
|
|
@@index([antragId, status])
|
|
@@map("nachforderung")
|
|
}
|
|
|
|
model Bescheid {
|
|
id String @id @default(cuid())
|
|
|
|
antragId String
|
|
antrag SozialhilfeAntrag @relation(fields: [antragId], references: [id], onDelete: Cascade)
|
|
|
|
art BescheidArt
|
|
bescheidVom DateTime @db.Date
|
|
/// Massgeblich fuer den Fristbeginn - nicht das Datum auf dem Bescheid.
|
|
zugegangenAm DateTime? @db.Date
|
|
zeitraumVon DateTime? @db.Date
|
|
zeitraumBis DateTime? @db.Date
|
|
betragCent BigInt?
|
|
|
|
/// In der Regel ein Monat ab Bekanntgabe (§ 84 SGG). Bei fehlender oder
|
|
/// falscher Rechtsbehelfsbelehrung ein Jahr - deshalb speicherbar.
|
|
rechtsbehelfsfristMonate Int @default(1)
|
|
widerspruchMoeglichBis DateTime? @db.Date
|
|
pruefStatus BescheidPruefStatus @default(UNGEPRUEFT)
|
|
widerspruchEingelegtAm DateTime? @db.Date
|
|
|
|
notizenVerschluesselt String?
|
|
|
|
erstelltAm DateTime @default(now())
|
|
aktualisiertAm DateTime @updatedAt
|
|
geloeschtAm DateTime?
|
|
|
|
verknuepfungen DokumentVerknuepfung[]
|
|
|
|
@@index([antragId, bescheidVom])
|
|
@@map("bescheid")
|
|
}
|
|
|
|
enum LeistungStatus {
|
|
NICHT_GEPRUEFT
|
|
PRUEFUNG_LAEUFT
|
|
BEANTRAGT
|
|
BEWILLIGT
|
|
ABGELEHNT
|
|
LAEUFT
|
|
NICHT_ZUTREFFEND
|
|
}
|
|
|
|
/// Katalog weiterer Leistungen: Landespflegegeld, Zuzahlungsbefreiung,
|
|
/// Rundfunkbeitragsbefreiung, Schwerbehindertenausweis, Grundsicherung.
|
|
model Leistung {
|
|
id String @id @default(cuid())
|
|
|
|
betreuteId String
|
|
betreute Betreute @relation(fields: [betreuteId], references: [id], onDelete: Cascade)
|
|
|
|
bezeichnung String
|
|
traeger String?
|
|
rechtsgrundlage String?
|
|
status LeistungStatus @default(NICHT_GEPRUEFT)
|
|
|
|
beantragtAm DateTime? @db.Date
|
|
bewilligtAm DateTime? @db.Date
|
|
gueltigBis DateTime? @db.Date
|
|
betragCent BigInt?
|
|
rhythmus ZahlRhythmus?
|
|
|
|
/// Muss jaehrlich neu beantragt werden (Landespflegegeld, Zuzahlung).
|
|
jaehrlichNeuBeantragen Boolean @default(false)
|
|
|
|
/// Stabiler Schluessel aus dem Katalog, verhindert Dubletten beim Seed.
|
|
katalogSchluessel String?
|
|
|
|
notizen String?
|
|
|
|
erstelltAm DateTime @default(now())
|
|
aktualisiertAm DateTime @updatedAt
|
|
geloeschtAm DateTime?
|
|
|
|
@@unique([betreuteId, katalogSchluessel])
|
|
@@index([betreuteId, status])
|
|
@@map("leistung")
|
|
}
|
|
|
|
// =============================================================================
|
|
// Berichte
|
|
// =============================================================================
|
|
|
|
enum BerichtArt {
|
|
VERMOEGENSVERZEICHNIS // § 1835 BGB
|
|
JAHRESBERICHT // § 1863 BGB
|
|
VERMOEGENSUEBERSICHT // § 1859 BGB (befreiter Betreuer)
|
|
RECHNUNGSLEGUNG // § 1865 BGB
|
|
BELEGMAPPE
|
|
UNTERLAGENPAKET_BEZIRK
|
|
VOLLEXPORT
|
|
}
|
|
|
|
/// Erzeugte Berichte werden protokolliert - was habe ich wann in welcher
|
|
/// Fassung abgegeben? Der Entwurfstext bleibt fuer die Ueberarbeitung stehen.
|
|
model Bericht {
|
|
id String @id @default(cuid())
|
|
|
|
betreuteId String
|
|
betreute Betreute @relation(fields: [betreuteId], references: [id], onDelete: Cascade)
|
|
|
|
art BerichtArt
|
|
titel String
|
|
zeitraumVon DateTime? @db.Date
|
|
zeitraumBis DateTime? @db.Date
|
|
stichtag DateTime? @db.Date
|
|
|
|
/// Vom Nutzer ueberarbeiteter Freitext (Jahresbericht) - verschluesselt.
|
|
entwurfVerschluesselt String?
|
|
|
|
/// Abgelegte PDF-Fassung im Objektspeicher.
|
|
objektSchluessel String?
|
|
dateiname String?
|
|
pruefsumme String?
|
|
|
|
erstelltVonId String?
|
|
erstelltVon User? @relation(fields: [erstelltVonId], references: [id], onDelete: SetNull)
|
|
|
|
abgegebenAm DateTime? @db.Date
|
|
abgegebenAn String?
|
|
|
|
erstelltAm DateTime @default(now())
|
|
aktualisiertAm DateTime @updatedAt
|
|
geloeschtAm DateTime?
|
|
|
|
@@index([betreuteId, art])
|
|
@@map("bericht")
|
|
}
|
|
|
|
// =============================================================================
|
|
// Audit-Log
|
|
// =============================================================================
|
|
|
|
enum AuditAktion {
|
|
ERSTELLT
|
|
GEAENDERT
|
|
GELOESCHT
|
|
WIEDERHERGESTELLT
|
|
ENDGUELTIG_GELOESCHT
|
|
ANGESEHEN
|
|
EXPORTIERT
|
|
DOKUMENT_HERUNTERGELADEN
|
|
ANMELDUNG
|
|
ANMELDUNG_FEHLGESCHLAGEN
|
|
ABMELDUNG
|
|
ZWEITER_FAKTOR_EINGERICHTET
|
|
PASSKEY_REGISTRIERT
|
|
PASSKEY_ENTFERNT
|
|
PASSWORT_GEAENDERT
|
|
ROLLE_GEAENDERT
|
|
FREIGABE_GEAENDERT
|
|
}
|
|
|
|
/// Unveraenderlich und nur anhaengend. Ein Datenbank-Trigger
|
|
/// (prisma/sql/audit-schutz.sql) verhindert UPDATE und DELETE.
|
|
/// Zugleich mein Nachweis gegenueber Gericht und moeglichen Miterben.
|
|
model AuditLog {
|
|
id String @id @default(cuid())
|
|
zeitpunkt DateTime @default(now())
|
|
|
|
userId String?
|
|
user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
|
|
/// Momentaufnahme - bleibt lesbar, auch wenn der Benutzer geloescht wird.
|
|
userEmail String?
|
|
userName String?
|
|
|
|
betreuteId String?
|
|
betreute Betreute? @relation(fields: [betreuteId], references: [id], onDelete: SetNull)
|
|
|
|
aktion AuditAktion
|
|
entitaet String // Modellname, z.B. "Betreute"
|
|
entitaetId String?
|
|
bezeichnung String? // lesbarer Titel des betroffenen Datensatzes
|
|
|
|
/// Geaenderte Felder als JSON {feld: {alt, neu}} - verschluesselt, weil
|
|
/// dort Gesundheits- und Finanzdaten stehen koennen.
|
|
aenderungVerschluesselt String?
|
|
|
|
ipHash String?
|
|
userAgent String?
|
|
|
|
@@index([betreuteId, zeitpunkt])
|
|
@@index([userId, zeitpunkt])
|
|
@@index([entitaet, entitaetId])
|
|
@@map("audit_log")
|
|
}
|