feat: Immobilie, Versicherungen und Heim mit COPD-Verlauf

Etappe 6. Das leerstehende Haus, die Vertraege und der
Gesundheitsverlauf - die drei Bereiche, in denen fuer diesen Fall das
meiste Geld und das meiste Risiko liegt.

Leerstands-Checkliste mit zehn Punkten, jeder mit Begruendung. Der
wichtigste: den Gebaeudeversicherer ueber den Leerstand informieren -
sonst droht Leistungsfreiheit, und zwar genau dann, wenn es brennt oder
eine Leitung platzt. Dazu Verkehrssicherungspflicht, Frostschutz zum
Oktober, Kontrollgaenge, Zaehlerstaende, Schluesselverzeichnis.

Der Kuendigungsrechner nennt den spaetesten ABSENDETAG, nicht den
Fristablauf: eine Kuendigung wirkt mit dem Zugang, nicht mit dem
Abschicken. Drei Tage Postlaufpuffer sind einkalkuliert.

Der Kuendigungs-Assistent beantwortet die Genehmigungsfrage nach
§ 1850 BGB nicht - er stellt sie und verlangt den Gang zum
Rechtspfleger. Der Versandnachweis ist Pflichtfeld: ohne ihn kommt die
Kuendigung nicht durch die Pruefung.

Leistungszuschlag nach § 43c SGB XI mit Stand 01.01.2024
(15/30/50/75 Prozent nach Verweildauer, erst ab Pflegegrad 2). Die
Oberflaeche zeigt, ab wann die naechste Stufe greift - das ist bares
Geld und wird regelmaessig uebersehen.

COPD-Schnellerfassung fuer Exazerbation, Sauerstoff, Sturz,
Krankenhaus, Hilfsmittel und Impfungen, mit STIKO-Plan fuer COPD.
Beschreibungen verschluesselt. Die Jahresauswertung ueber Exazerbationen
und Krankenhaustage wandert in den Jahresbericht und begruendet eine
Hoeherstufung.

74 Tests.

Etappe: 6
Phase: Substanz
This commit is contained in:
Florian Wilde
2026-08-22 06:22:24 +02:00
parent d35e24f830
commit b69a12da8f
28 changed files with 4309 additions and 917 deletions
+243
View File
@@ -0,0 +1,243 @@
'use client'
import { useActionState, useState } from 'react'
import { Coins, Mail, Plus } from 'lucide-react'
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Input } from '@/components/ui/input'
import { Select } from '@/components/ui/select'
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
import { Textarea } from '@/components/ui/textarea'
import { Feld } from '@/components/feld'
import { Leerzustand } from '@/components/seiten-kopf'
import { Absendeknopf, Textbaustein, Zustandsmeldung } from '@/components/substanz-formular'
import { WERTQUELLEN_VERTRAG } from '@/lib/vertraege/konstanten'
import { rueckkaufswertAnfordern, rueckkaufswertErfassen } from '@/server/actions/vertraege'
import { formatCent, formatDatum } from '@/lib/utils'
import type { Formularzustand } from '@/lib/validierung/substanz'
export type RueckkaufswertZeile = {
id: string
stichtag: string
wertCent: number
wertquelle: string
bemerkung: string | null
}
export function Rueckkaufswert({
betreuteId,
vertragId,
werte,
angefragtAm,
erinnerungFaellig,
tageSeitAnfrage,
anschreiben,
stichtagVorschlag,
darfSchreiben,
}: {
betreuteId: string
vertragId: string
werte: RueckkaufswertZeile[]
angefragtAm: string | null
erinnerungFaellig: boolean
tageSeitAnfrage: number | null
anschreiben: string
stichtagVorschlag: string
darfSchreiben: boolean
}) {
const [erfassen, erfassenAktion] = useActionState<Formularzustand, FormData>(
rueckkaufswertErfassen,
{},
)
const [anfrage, anfrageAktion] = useActionState<Formularzustand, FormData>(
rueckkaufswertAnfordern,
{},
)
const [briefOffen, setBriefOffen] = useState(false)
const [erfassenOffen, setErfassenOffen] = useState(false)
return (
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Coins className="size-5" aria-hidden="true" />
Rückkaufswert
</CardTitle>
<CardDescription>
Der Bezirk verlangt zu jedem Versicherungsvertrag den Rückkaufswert zu einem
Stichtag — schriftlich von der Gesellschaft, nicht geschätzt. Die Antwort dauert
erfahrungsgemäß drei bis sechs Wochen.
</CardDescription>
</CardHeader>
<CardContent className="space-y-5">
{angefragtAm && (
<Alert variant={erinnerungFaellig ? 'warnung' : 'hinweis'}>
<Mail aria-hidden="true" />
<AlertTitle>
Angefragt am {formatDatum(angefragtAm)}
{tageSeitAnfrage !== null && ` — vor ${tageSeitAnfrage} Tagen`}
</AlertTitle>
<AlertDescription>
{erinnerungFaellig
? 'Die vier Wochen sind um. Ruf an und halte das Gespräch im Kontaktprotokoll fest — schriftlich nachfassen dauert wieder vier Wochen.'
: 'Die Wiedervorlage steht in den Fristen. Kommt die Antwort früher, hak die Frist dort ab.'}
</AlertDescription>
</Alert>
)}
{werte.length === 0 ? (
<Leerzustand
titel="Noch kein Rückkaufswert erfasst"
beschreibung="Fordere ihn bei der Gesellschaft an. Frag im selben Schreiben nach der beitragsfreien Versicherungssumme — sonst musst du für den Vergleich ein zweites Mal schreiben."
icon={Coins}
aktion={
darfSchreiben && (
<Button type="button" onClick={() => setBriefOffen(true)}>
<Mail aria-hidden="true" />
Anschreiben erzeugen
</Button>
)
}
/>
) : (
<Table>
<TableHeader>
<TableRow>
<TableHead>Stichtag</TableHead>
<TableHead className="text-right">Rückkaufswert</TableHead>
<TableHead>Quelle</TableHead>
<TableHead>Bemerkung</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{werte.map((zeile) => (
<TableRow key={zeile.id}>
<TableCell className="whitespace-nowrap">{formatDatum(zeile.stichtag)}</TableCell>
<TableCell className="text-right font-medium tabular-nums">
{formatCent(zeile.wertCent)}
</TableCell>
<TableCell>{zeile.wertquelle}</TableCell>
<TableCell className="text-sm text-muted-foreground">
{zeile.bemerkung ?? '–'}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)}
{darfSchreiben && (
<div className="flex flex-wrap gap-2">
<Button type="button" variant="outline" onClick={() => setBriefOffen((o) => !o)}>
<Mail aria-hidden="true" />
{briefOffen ? 'Anschreiben schließen' : 'Rückkaufswert anfordern'}
</Button>
<Button type="button" variant="outline" onClick={() => setErfassenOffen((o) => !o)}>
<Plus aria-hidden="true" />
{erfassenOffen ? 'Eingabe schließen' : 'Mitgeteilten Wert eintragen'}
</Button>
</div>
)}
{darfSchreiben && briefOffen && (
<div className="space-y-4 rounded-md border p-4">
<Textbaustein
text={anschreiben}
beschriftung="Anschreiben an die Gesellschaft"
hoehe="min-h-[32rem]"
/>
<form action={anfrageAktion} className="space-y-4" noValidate>
<input type="hidden" name="betreuteId" value={betreuteId} />
<input type="hidden" name="vertragId" value={vertragId} />
<Zustandsmeldung zustand={anfrage} />
<div className="grid gap-4 sm:grid-cols-2">
<Feld
id="anfrage-stichtag"
beschriftung="Stichtag der Anfrage"
erklaerung="Der Tag, zu dem der Wert mitgeteilt werden soll. Für die Sozialhilfe meist der Tag der Antragstellung."
fehler={anfrage.felder?.stichtag}
pflicht
>
<Input
name="stichtag"
type="date"
defaultValue={stichtagVorschlag}
required
/>
</Feld>
</div>
<Absendeknopf>Anfrage vermerken und Wiedervorlage anlegen</Absendeknopf>
<p className="text-sm text-muted-foreground">
Setzt das Datum der Anfrage am Vertrag und legt eine Wiedervorlage in vier
Wochen an. Den Brief selbst schickst du von Hand ab — die App verschickt nichts.
</p>
</form>
</div>
)}
{darfSchreiben && erfassenOffen && (
<form action={erfassenAktion} className="space-y-4 rounded-md border p-4" noValidate>
<input type="hidden" name="betreuteId" value={betreuteId} />
<input type="hidden" name="vertragId" value={vertragId} />
<Zustandsmeldung zustand={erfassen} />
<div className="grid gap-4 sm:grid-cols-2">
<Feld
id="rkw-stichtag"
beschriftung="Stichtag"
fehler={erfassen.felder?.stichtag}
pflicht
>
<Input name="stichtag" type="date" required />
</Feld>
<Feld
id="rkw-wert"
beschriftung="Rückkaufswert"
fehler={erfassen.felder?.wert}
pflicht
>
<Input name="wert" inputMode="decimal" placeholder="4.812,55" required />
</Feld>
<Feld
id="rkw-quelle"
beschriftung="Quelle"
erklaerung="Woher stammt die Zahl? Eine telefonische Auskunft ist besser als nichts, aber kein Nachweis."
fehler={erfassen.felder?.wertquelle}
pflicht
className="sm:col-span-2"
>
<Select name="wertquelle" defaultValue={WERTQUELLEN_VERTRAG[0]}>
{WERTQUELLEN_VERTRAG.map((q) => (
<option key={q} value={q}>
{q}
</option>
))}
</Select>
</Feld>
</div>
<Feld
id="rkw-bemerkung"
beschriftung="Bemerkung"
erklaerung="z. B. die beitragsfreie Versicherungssumme, wenn sie mitgeteilt wurde."
fehler={erfassen.felder?.bemerkung}
>
<Textarea name="bemerkung" rows={2} maxLength={1000} />
</Feld>
<Absendeknopf>Wert aufnehmen</Absendeknopf>
</form>
)}
</CardContent>
</Card>
)
}