Files
betreuungs-cockpit/src/app/(app)/pflege/page.tsx
T
Florian Wilde b69a12da8f 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
2026-08-22 06:22:24 +02:00

253 lines
9.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Link from 'next/link'
import { Building, HeartPulse, Pencil, ScrollText } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Leerzustand, SeitenKopf } from '@/components/seiten-kopf'
import { Datenzeile } from '@/components/substanz-formular'
import { AbwesenheitKarte } from '@/components/pflege/abwesenheit-karte'
import { Impfstatus } from '@/components/pflege/impfstatus'
import { LeistungszuschlagKarte } from '@/components/pflege/leistungszuschlag-karte'
import { PflegegradKarte } from '@/components/pflege/pflegegrad-karte'
import { aktiveBetreute, erfordereBenutzer, erfordereZugang } from '@/lib/auth/sitzung'
import { centZuZahl, db, nichtGeloescht } from '@/lib/db'
import { oeffnen } from '@/lib/crypto/feld'
import { alsIsoTag, heuteAlsTag, plusMonate } from '@/lib/fristen/datum'
import { abwesenheitstageGesamt, abwesenheitstageJeJahr } from '@/lib/pflege/abwesenheit'
import { impfstatusBerechnen } from '@/lib/pflege/impfungen'
import { leistungszuschlagBerechnen } from '@/lib/pflege/leistungszuschlag'
import { HOEHERSTUFUNG_ABSTAND_MONATE } from '@/lib/pflege/konstanten'
import { verlaufAuswerten, verschlechterungErkannt } from '@/lib/pflege/verlauf'
import { formatCent, formatDatum } from '@/lib/utils'
export const dynamic = 'force-dynamic'
export default async function PflegeUebersicht() {
const benutzer = await erfordereBenutzer('/pflege')
const aktive = await aktiveBetreute(benutzer)
if (!aktive) return null
const { darfSchreiben } = await erfordereZugang(aktive.id, 'GESUNDHEIT', 'lesen')
const schreiben = darfSchreiben('GESUNDHEIT')
const [heimvertrag, ereignisse, pruefFrist] = await Promise.all([
db.heimvertrag.findUnique({ where: { betreuteId: aktive.id } }),
db.gesundheitsereignis.findMany({
where: { betreuteId: aktive.id, ...nichtGeloescht },
orderBy: { datum: 'desc' },
}),
db.frist.findUnique({
where: {
betreuteId_systemSchluessel: {
betreuteId: aktive.id,
systemSchluessel: 'pflegegrad-pruefung',
},
},
}),
])
const heute = heuteAlsTag()
const zuschlag = leistungszuschlagBerechnen({
einzugAm: heimvertrag?.einzugAm ?? aktive.heimEinzugAm,
eigenanteilCent: heimvertrag?.pflegeEigenanteilCent ?? null,
pflegegrad: aktive.pflegegrad,
stichtag: heute,
})
const abwesend = abwesenheitstageJeJahr(
ereignisse.map((e) => ({
datum: e.datum,
bisDatum: e.bisDatum,
abwesenheitstage: e.abwesenheitstage,
art: e.art,
})),
)
const jahre = verlaufAuswerten(
ereignisse.map((e) => ({
datum: e.datum,
bisDatum: e.bisDatum,
art: e.art,
abwesenheitstage: e.abwesenheitstage,
})),
)
const befund = verschlechterungErkannt(jahre, heute.getUTCFullYear())
const impfungen = impfstatusBerechnen(
ereignisse.filter((e) => e.art === 'IMPFUNG').map((e) => ({ datum: e.datum, titel: e.titel })),
heute,
)
const offenHeim = heimvertrag ? oeffnen('Heimvertrag', heimvertrag) : null
const gesamtCent =
(heimvertrag?.pflegeEigenanteilCent ?? 0n) +
(heimvertrag?.unterkunftVerpflegungCent ?? 0n) +
(heimvertrag?.investitionskostenCent ?? 0n) +
(heimvertrag?.ausbildungsumlageCent ?? 0n)
return (
<div className="space-y-6">
<SeitenKopf
titel="Heim & Pflege"
beschreibung="Heimvertrag, Leistungszuschlag, Pflegegrad und der Gesundheitsverlauf — die Grundlage für Jahresbericht und Höherstufung."
pfad={[{ titel: 'Übersicht', href: '/heute' }, { titel: 'Heim & Pflege' }]}
aktionen={
<div className="flex flex-wrap gap-2">
<Button asChild variant="outline">
<Link href="/pflege/wille">
<ScrollText aria-hidden="true" />
Wille & Vorsorge
</Link>
</Button>
<Button asChild>
<Link href="/pflege/verlauf">
<HeartPulse aria-hidden="true" />
Gesundheitsverlauf
</Link>
</Button>
</div>
}
/>
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Building className="size-5" aria-hidden="true" />
Heimvertrag
</CardTitle>
<CardDescription>
Einrichtung, Träger und die aktuellen Sätze. Historische Werte stehen in der
monatlichen Gegenüberstellung des Sozialhilfemoduls.
</CardDescription>
</CardHeader>
<CardContent>
{!heimvertrag ? (
<Leerzustand
titel="Noch kein Heimvertrag erfasst"
beschreibung="Ohne Einzugsdatum lässt sich der Leistungszuschlag nach § 43c SGB XI nicht berechnen — und der ist bares Geld."
icon={Building}
aktion={
schreiben && (
<Button asChild>
<Link href="/pflege/heimvertrag">Heimvertrag erfassen</Link>
</Button>
)
}
/>
) : (
<div className="grid gap-x-8 sm:grid-cols-2">
<dl className="divide-y">
<Datenzeile beschriftung="Einrichtung">{heimvertrag.einrichtung}</Datenzeile>
<Datenzeile beschriftung="Träger">{heimvertrag.traeger ?? '–'}</Datenzeile>
<Datenzeile beschriftung="Anschrift">
{[heimvertrag.strasse, [heimvertrag.plz, heimvertrag.ort].filter(Boolean).join(' ')]
.filter(Boolean)
.join(', ') || '–'}
</Datenzeile>
<Datenzeile beschriftung="Vertragsdatum">
{formatDatum(heimvertrag.vertragAm)}
</Datenzeile>
<Datenzeile beschriftung="Einzug">{formatDatum(heimvertrag.einzugAm)}</Datenzeile>
<Datenzeile beschriftung="Kündigungsfrist">
{heimvertrag.kuendigungsfristTage !== null
? `${heimvertrag.kuendigungsfristTage} Tage`
: '–'}
</Datenzeile>
</dl>
<dl className="divide-y">
<Datenzeile beschriftung="Pflegebedingter Eigenanteil">
{formatCent(heimvertrag.pflegeEigenanteilCent)}
</Datenzeile>
<Datenzeile beschriftung="Unterkunft und Verpflegung">
{formatCent(heimvertrag.unterkunftVerpflegungCent)}
</Datenzeile>
<Datenzeile beschriftung="Investitionskosten">
{formatCent(heimvertrag.investitionskostenCent)}
</Datenzeile>
<Datenzeile beschriftung="Ausbildungsumlage">
{formatCent(heimvertrag.ausbildungsumlageCent)}
</Datenzeile>
<Datenzeile beschriftung="Heimkosten im Monat">
<strong>{formatCent(gesamtCent)}</strong>
</Datenzeile>
</dl>
{offenHeim?.notizen && (
<div className="mt-4 rounded-md border bg-muted/40 p-3 sm:col-span-2">
<p className="whitespace-pre-line text-sm">{offenHeim.notizen}</p>
</div>
)}
{schreiben && (
<div className="mt-4 sm:col-span-2">
<Button asChild variant="outline">
<Link href="/pflege/heimvertrag">
<Pencil aria-hidden="true" />
Heimvertrag bearbeiten
</Link>
</Button>
</div>
)}
</div>
)}
</CardContent>
</Card>
<LeistungszuschlagKarte
zuschlag={{
berechenbar: zuschlag.berechenbar,
verweildauerMonate: zuschlag.verweildauerMonate,
prozent: zuschlag.prozent,
stufeBezeichnung: zuschlag.stufe?.bezeichnung ?? null,
zuschlagCent: centZuZahl(zuschlag.zuschlagCent),
verbleibtCent: centZuZahl(zuschlag.verbleibtCent),
eigenanteilCent: centZuZahl(heimvertrag?.pflegeEigenanteilCent ?? null),
naechsteStufe: zuschlag.naechsteStufe
? {
prozent: zuschlag.naechsteStufe.stufe.prozent,
bezeichnung: zuschlag.naechsteStufe.stufe.bezeichnung,
abDatum: zuschlag.naechsteStufe.abDatum.toISOString(),
tageBis: zuschlag.naechsteStufe.tageBis,
mehrCent: centZuZahl(zuschlag.naechsteStufe.mehrCent),
}
: null,
hinweise: zuschlag.hinweise,
}}
/>
<PflegegradKarte
betreuteId={aktive.id}
darfSchreiben={schreiben}
pflegegrad={aktive.pflegegrad}
pflegegradSeit={aktive.pflegegradSeit?.toISOString() ?? null}
pflegegradBescheidVom={aktive.pflegegradBescheidVom?.toISOString() ?? null}
pruefungAm={pruefFrist?.datum.toISOString() ?? null}
vorschlagPruefungAm={alsIsoTag(plusMonate(heute, HOEHERSTUFUNG_ABSTAND_MONATE))}
verschlechterung={befund.begruendung}
begutachtungen={ereignisse
.filter((e) => e.art === 'PFLEGEBEGUTACHTUNG')
.map((e) => ({
id: e.id,
datum: e.datum.toISOString(),
titel: e.titel,
einrichtung: e.einrichtung,
}))}
/>
<AbwesenheitKarte jahre={abwesend} gesamt={abwesenheitstageGesamt(abwesend)} />
<Impfstatus
zeilen={impfungen.map((i) => ({
schluessel: i.empfehlung.schluessel,
bezeichnung: i.empfehlung.bezeichnung,
begruendung: i.empfehlung.begruendung,
letzteImpfung: i.letzteImpfung?.toISOString() ?? null,
faelligAm: i.faelligAm?.toISOString() ?? null,
lage: i.lage,
text: i.text,
}))}
/>
</div>
)
}