feat: Sozialhilfe-Modul mit Nachforderungs-Tracker und Fristpruefung
Etappe 5. Bildet das laufende Verfahren beim Bezirk Oberfranken ab. Rechtsbehelfsfrist: § 187 Abs. 1, § 188 Abs. 2 und 3 BGB, danach § 64 Abs. 3 SGG - hier verschieben Wochenende und Feiertag das Fristende auf den naechsten Werktag. Das ist genau umgekehrt zu den Betreuerfristen, wo bewusst nicht verschoben wird, und deshalb liegt die Feiertagsliste getrennt. Zwei bayerische Sonderfaelle, die haeufig falsch gemacht werden: Mariae Himmelfahrt verschiebt NICHT (Art. 1 Abs. 1 S. 2 BayFTG - der Feiertag gilt nur in ueberwiegend katholischen Gemeinden, Bayreuth ist keine), und der Buss- und Bettag ist in Bayern seit 1995 kein Feiertag mehr. Beide erzeugen stattdessen einen ausdruecklichen Hinweis, damit man die Frist im Zweifel selbst nachrechnet. Der Rechenweg wird als Schrittliste ausgegeben. Heimkosten: Der Dashboard-Agent hatte einen fachlichen Widerspruch gefunden - der einrichtungseinheitliche Eigenanteil auf der Heimrechnung ist bereits um die Leistung der Pflegekasse gekuerzt. Wird sie zusaetzlich als Deckung erfasst, entsteht eine Doppelzaehlung von ueber tausend Euro im Monat. Das Modul rechnet jetzt ausdruecklich nach der Lesart EIGENANTEIL_NETTO, fuehrt die Pflegekassenleistung nur nachrichtlich mit und warnt, wenn sie doch belegt ist. Die Oberflaeche sagt auf einer eigenen Karte, was hier gegen was gerechnet wird. Die Schonvermoegens-Bewertung liefert Argumentationshilfen zum Uebernehmen, keine Automatik - die Einordnung obliegt dem Traeger und gegebenenfalls dem Sozialgericht. Das steht an jeder dieser Karten. Alle jaehrlich veraenderlichen Betraege stehen an einer Stelle mit Stand-Datum und Prueftermin. Die Bekleidungspauschale bewusst ohne Zahl: sie ist regional und steht im Bescheid. 46 Tests. Etappe: 5 Phase: Behoerde
This commit is contained in:
@@ -1,149 +1,149 @@
|
||||
import { notFound } from 'next/navigation'
|
||||
import { Trash2 } from 'lucide-react'
|
||||
import { SeitenKopf } from '@/components/seiten-kopf'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { AktionMitRueckfrage } from '@/components/sozialhilfe/bausteine'
|
||||
import { BescheidFormular } from '@/components/sozialhilfe/bescheid-formular'
|
||||
import { BescheidPruefung } from '@/components/sozialhilfe/bescheid-pruefung'
|
||||
import { oeffnen } from '@/lib/crypto/feld'
|
||||
import { db, nichtGeloescht } from '@/lib/db'
|
||||
import { alsIsoTag, heuteAlsTag } from '@/lib/fristen/datum'
|
||||
import { BESCHEID_ART_BESCHRIFTUNG } from '@/lib/sozialhilfe/bescheide'
|
||||
import { centZuEingabe } from '@/lib/sozialhilfe/betrag'
|
||||
import {
|
||||
fristAmpel,
|
||||
rechtsbehelfsfristBerechnen,
|
||||
zugangsvermutung,
|
||||
} from '@/lib/sozialhilfe/rechtsbehelf'
|
||||
import { sozialhilfeKontext } from '@/lib/sozialhilfe/zugang'
|
||||
import { bescheidLoeschen } from '@/server/actions/sozialhilfe'
|
||||
import { formatDatum } from '@/lib/utils'
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
/**
|
||||
* Bescheid-Prüfung.
|
||||
*
|
||||
* Der Rechenweg der Widerspruchsfrist steht ausgeschrieben da - die
|
||||
* Anwendung kann sich irren, und wer eine Frist verpasst, soll sehen
|
||||
* können, wo die Rechnung abgebogen ist.
|
||||
*/
|
||||
export default async function BescheidSeite({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ antragId: string; bescheidId: string }>
|
||||
}) {
|
||||
const { antragId, bescheidId } = await params
|
||||
const { betreuteId, darfSchreiben } = await sozialhilfeKontext()
|
||||
|
||||
const bescheid = await db.bescheid.findFirst({
|
||||
where: { id: bescheidId, antragId, antrag: { betreuteId }, ...nichtGeloescht },
|
||||
include: { antrag: { select: { id: true, traeger: true, aktenzeichen: true } } },
|
||||
})
|
||||
if (!bescheid) notFound()
|
||||
|
||||
const offen = oeffnen('Bescheid', bescheid)
|
||||
const heute = heuteAlsTag()
|
||||
|
||||
const ergebnis = rechtsbehelfsfristBerechnen({
|
||||
zugegangenAm: bescheid.zugegangenAm,
|
||||
bescheidVom: bescheid.bescheidVom,
|
||||
fristMonate: bescheid.rechtsbehelfsfristMonate,
|
||||
heute,
|
||||
})
|
||||
|
||||
const vorschlag = zugangsvermutung(bescheid.bescheidVom)
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<SeitenKopf
|
||||
titel={`${BESCHEID_ART_BESCHRIFTUNG[bescheid.art]} vom ${formatDatum(bescheid.bescheidVom)}`}
|
||||
beschreibung={
|
||||
<>
|
||||
{bescheid.antrag.traeger}
|
||||
{bescheid.antrag.aktenzeichen ? ` · Az. ${bescheid.antrag.aktenzeichen}` : ''} · Zugang{' '}
|
||||
{formatDatum(bescheid.zugegangenAm)}
|
||||
</>
|
||||
}
|
||||
pfad={[
|
||||
{ titel: 'Start', href: '/heute' },
|
||||
{ titel: 'Sozialhilfe', href: '/sozialhilfe' },
|
||||
{ titel: 'Antragsakte', href: `/sozialhilfe/${antragId}` },
|
||||
{ titel: 'Bescheid' },
|
||||
]}
|
||||
/>
|
||||
|
||||
<BescheidPruefung
|
||||
bescheidId={bescheid.id}
|
||||
pruefStatus={bescheid.pruefStatus}
|
||||
widerspruchEingelegtAm={
|
||||
bescheid.widerspruchEingelegtAm ? alsIsoTag(bescheid.widerspruchEingelegtAm) : ''
|
||||
}
|
||||
darfSchreiben={darfSchreiben}
|
||||
rechenweg={{
|
||||
berechenbar: ergebnis.berechenbar,
|
||||
widerspruchMoeglichBis: ergebnis.widerspruchMoeglichBis
|
||||
? alsIsoTag(ergebnis.widerspruchMoeglichBis)
|
||||
: null,
|
||||
rechnerischesEnde: ergebnis.rechnerischesEnde
|
||||
? alsIsoTag(ergebnis.rechnerischesEnde)
|
||||
: null,
|
||||
verschoben: ergebnis.verschoben,
|
||||
tageVerbleibend: ergebnis.tageVerbleibend,
|
||||
abgelaufen: ergebnis.abgelaufen,
|
||||
ampel: fristAmpel(ergebnis),
|
||||
schritte: ergebnis.schritte,
|
||||
hinweise: ergebnis.hinweise,
|
||||
}}
|
||||
/>
|
||||
|
||||
{offen.notizen && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Notizen</CardTitle>
|
||||
<CardDescription>Verschlüsselt gespeichert.</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="whitespace-pre-line text-sm">{offen.notizen}</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{darfSchreiben && (
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-lg font-semibold">Bescheid bearbeiten</h2>
|
||||
<BescheidFormular
|
||||
antragId={antragId}
|
||||
zugangsvorschlag={{ datum: alsIsoTag(vorschlag.datum), hinweis: vorschlag.hinweis }}
|
||||
werte={{
|
||||
id: bescheid.id,
|
||||
art: bescheid.art,
|
||||
bescheidVom: alsIsoTag(bescheid.bescheidVom),
|
||||
zugegangenAm: bescheid.zugegangenAm ? alsIsoTag(bescheid.zugegangenAm) : '',
|
||||
zeitraumVon: bescheid.zeitraumVon ? alsIsoTag(bescheid.zeitraumVon) : '',
|
||||
zeitraumBis: bescheid.zeitraumBis ? alsIsoTag(bescheid.zeitraumBis) : '',
|
||||
betrag: centZuEingabe(bescheid.betragCent),
|
||||
rechtsbehelfsfristMonate: bescheid.rechtsbehelfsfristMonate,
|
||||
widerspruchEingelegtAm: bescheid.widerspruchEingelegtAm
|
||||
? alsIsoTag(bescheid.widerspruchEingelegtAm)
|
||||
: '',
|
||||
notizen: offen.notizen ?? '',
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className="border-t pt-6">
|
||||
<AktionMitRueckfrage
|
||||
aktion={bescheidLoeschen.bind(null, bescheid.id)}
|
||||
frage="Diesen Bescheid in den Papierkorb legen? Er bleibt dort 30 Tage."
|
||||
variant="outline"
|
||||
size="default"
|
||||
>
|
||||
<Trash2 aria-hidden="true" />
|
||||
Bescheid löschen
|
||||
</AktionMitRueckfrage>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
import { notFound } from 'next/navigation'
|
||||
import { Trash2 } from 'lucide-react'
|
||||
import { SeitenKopf } from '@/components/seiten-kopf'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { AktionMitRueckfrage } from '@/components/sozialhilfe/bausteine'
|
||||
import { BescheidFormular } from '@/components/sozialhilfe/bescheid-formular'
|
||||
import { BescheidPruefung } from '@/components/sozialhilfe/bescheid-pruefung'
|
||||
import { oeffnen } from '@/lib/crypto/feld'
|
||||
import { db, nichtGeloescht } from '@/lib/db'
|
||||
import { alsIsoTag, heuteAlsTag } from '@/lib/fristen/datum'
|
||||
import { BESCHEID_ART_BESCHRIFTUNG } from '@/lib/sozialhilfe/bescheide'
|
||||
import { betragFormatieren } from '@/lib/finanzen/betrag'
|
||||
import {
|
||||
fristAmpel,
|
||||
rechtsbehelfsfristBerechnen,
|
||||
zugangsvermutung,
|
||||
} from '@/lib/sozialhilfe/rechtsbehelf'
|
||||
import { sozialhilfeKontext } from '@/lib/sozialhilfe/zugang'
|
||||
import { bescheidLoeschen } from '@/server/actions/sozialhilfe'
|
||||
import { formatDatum } from '@/lib/utils'
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
/**
|
||||
* Bescheid-Prüfung.
|
||||
*
|
||||
* Der Rechenweg der Widerspruchsfrist steht ausgeschrieben da - die
|
||||
* Anwendung kann sich irren, und wer eine Frist verpasst, soll sehen
|
||||
* können, wo die Rechnung abgebogen ist.
|
||||
*/
|
||||
export default async function BescheidSeite({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ antragId: string; bescheidId: string }>
|
||||
}) {
|
||||
const { antragId, bescheidId } = await params
|
||||
const { betreuteId, darfSchreiben } = await sozialhilfeKontext()
|
||||
|
||||
const bescheid = await db.bescheid.findFirst({
|
||||
where: { id: bescheidId, antragId, antrag: { betreuteId }, ...nichtGeloescht },
|
||||
include: { antrag: { select: { id: true, traeger: true, aktenzeichen: true } } },
|
||||
})
|
||||
if (!bescheid) notFound()
|
||||
|
||||
const offen = oeffnen('Bescheid', bescheid)
|
||||
const heute = heuteAlsTag()
|
||||
|
||||
const ergebnis = rechtsbehelfsfristBerechnen({
|
||||
zugegangenAm: bescheid.zugegangenAm,
|
||||
bescheidVom: bescheid.bescheidVom,
|
||||
fristMonate: bescheid.rechtsbehelfsfristMonate,
|
||||
heute,
|
||||
})
|
||||
|
||||
const vorschlag = zugangsvermutung(bescheid.bescheidVom)
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<SeitenKopf
|
||||
titel={`${BESCHEID_ART_BESCHRIFTUNG[bescheid.art]} vom ${formatDatum(bescheid.bescheidVom)}`}
|
||||
beschreibung={
|
||||
<>
|
||||
{bescheid.antrag.traeger}
|
||||
{bescheid.antrag.aktenzeichen ? ` · Az. ${bescheid.antrag.aktenzeichen}` : ''} · Zugang{' '}
|
||||
{formatDatum(bescheid.zugegangenAm)}
|
||||
</>
|
||||
}
|
||||
pfad={[
|
||||
{ titel: 'Start', href: '/heute' },
|
||||
{ titel: 'Sozialhilfe', href: '/sozialhilfe' },
|
||||
{ titel: 'Antragsakte', href: `/sozialhilfe/${antragId}` },
|
||||
{ titel: 'Bescheid' },
|
||||
]}
|
||||
/>
|
||||
|
||||
<BescheidPruefung
|
||||
bescheidId={bescheid.id}
|
||||
pruefStatus={bescheid.pruefStatus}
|
||||
widerspruchEingelegtAm={
|
||||
bescheid.widerspruchEingelegtAm ? alsIsoTag(bescheid.widerspruchEingelegtAm) : ''
|
||||
}
|
||||
darfSchreiben={darfSchreiben}
|
||||
rechenweg={{
|
||||
berechenbar: ergebnis.berechenbar,
|
||||
widerspruchMoeglichBis: ergebnis.widerspruchMoeglichBis
|
||||
? alsIsoTag(ergebnis.widerspruchMoeglichBis)
|
||||
: null,
|
||||
rechnerischesEnde: ergebnis.rechnerischesEnde
|
||||
? alsIsoTag(ergebnis.rechnerischesEnde)
|
||||
: null,
|
||||
verschoben: ergebnis.verschoben,
|
||||
tageVerbleibend: ergebnis.tageVerbleibend,
|
||||
abgelaufen: ergebnis.abgelaufen,
|
||||
ampel: fristAmpel(ergebnis),
|
||||
schritte: ergebnis.schritte,
|
||||
hinweise: ergebnis.hinweise,
|
||||
}}
|
||||
/>
|
||||
|
||||
{offen.notizen && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Notizen</CardTitle>
|
||||
<CardDescription>Verschlüsselt gespeichert.</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="whitespace-pre-line text-sm">{offen.notizen}</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{darfSchreiben && (
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-lg font-semibold">Bescheid bearbeiten</h2>
|
||||
<BescheidFormular
|
||||
antragId={antragId}
|
||||
zugangsvorschlag={{ datum: alsIsoTag(vorschlag.datum), hinweis: vorschlag.hinweis }}
|
||||
werte={{
|
||||
id: bescheid.id,
|
||||
art: bescheid.art,
|
||||
bescheidVom: alsIsoTag(bescheid.bescheidVom),
|
||||
zugegangenAm: bescheid.zugegangenAm ? alsIsoTag(bescheid.zugegangenAm) : '',
|
||||
zeitraumVon: bescheid.zeitraumVon ? alsIsoTag(bescheid.zeitraumVon) : '',
|
||||
zeitraumBis: bescheid.zeitraumBis ? alsIsoTag(bescheid.zeitraumBis) : '',
|
||||
betrag: bescheid.betragCent === null ? '' : betragFormatieren(bescheid.betragCent),
|
||||
rechtsbehelfsfristMonate: bescheid.rechtsbehelfsfristMonate,
|
||||
widerspruchEingelegtAm: bescheid.widerspruchEingelegtAm
|
||||
? alsIsoTag(bescheid.widerspruchEingelegtAm)
|
||||
: '',
|
||||
notizen: offen.notizen ?? '',
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className="border-t pt-6">
|
||||
<AktionMitRueckfrage
|
||||
aktion={bescheidLoeschen.bind(null, bescheid.id)}
|
||||
frage="Diesen Bescheid in den Papierkorb legen? Er bleibt dort 30 Tage."
|
||||
variant="outline"
|
||||
size="default"
|
||||
>
|
||||
<Trash2 aria-hidden="true" />
|
||||
Bescheid löschen
|
||||
</AktionMitRueckfrage>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,318 +1,383 @@
|
||||
import Link from 'next/link'
|
||||
import { SeitenKopf } from '@/components/seiten-kopf'
|
||||
import { Rechtshinweis } from '@/components/rechtshinweis'
|
||||
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { HeimkostenFormular } from '@/components/sozialhilfe/heimkosten-formular'
|
||||
import { HeimkostenTabelle, type MonatsZeile } from '@/components/sozialhilfe/heimkosten-tabelle'
|
||||
import { centZuEingabe } from '@/lib/sozialhilfe/betrag'
|
||||
import { db, nichtGeloescht } from '@/lib/db'
|
||||
import { heuteAlsTag } from '@/lib/fristen/datum'
|
||||
import {
|
||||
BARBETRAG_CENT,
|
||||
BEKLEIDUNGSPAUSCHALE_HINWEIS,
|
||||
KONSTANTEN_NAECHSTE_PRUEFUNG,
|
||||
KONSTANTEN_STAND,
|
||||
} from '@/lib/sozialhilfe/konstanten'
|
||||
import {
|
||||
heimkostenAuswerten,
|
||||
jahresverlaufBilden,
|
||||
leistungszuschlagProzent,
|
||||
leistungszuschlagStufe,
|
||||
monatsname,
|
||||
pflegekassePauschaleCent,
|
||||
pflegekasseRechtsgrundlage,
|
||||
verweildauerMonate,
|
||||
} from '@/lib/sozialhilfe/heimkosten'
|
||||
import { sozialhilfeKontext } from '@/lib/sozialhilfe/zugang'
|
||||
import { formatCent } from '@/lib/utils'
|
||||
import { Info, TrendingDown } from 'lucide-react'
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
/**
|
||||
* Heimkosten-Gegenüberstellung.
|
||||
*
|
||||
* Die eine Zahl, um die es geht, ist die Deckungslücke: Was bleibt übrig,
|
||||
* nachdem Pflegekasse, Rente und Sozialhilfe gezahlt haben? Sie steht je
|
||||
* Monat und als Jahressumme da - denn eine Lücke von 180 Euro im Monat
|
||||
* sind über ein Jahr mehr als zweitausend.
|
||||
*/
|
||||
export default async function HeimkostenSeite({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: Promise<{ jahr?: string }>
|
||||
}) {
|
||||
const { betreuteId, betreute } = await sozialhilfeKontext()
|
||||
const parameter = await searchParams
|
||||
|
||||
const heute = heuteAlsTag()
|
||||
const gewaehltesJahr = Number.parseInt(parameter.jahr ?? '', 10)
|
||||
const jahr = Number.isFinite(gewaehltesJahr) ? gewaehltesJahr : heute.getUTCFullYear()
|
||||
|
||||
const [monate, heimvertrag] = await Promise.all([
|
||||
db.heimkostenmonat.findMany({
|
||||
where: { betreuteId, jahr },
|
||||
orderBy: { monat: 'asc' },
|
||||
}),
|
||||
db.heimvertrag.findUnique({ where: { betreuteId } }),
|
||||
])
|
||||
|
||||
const ergebnisse = monate.map((m) =>
|
||||
heimkostenAuswerten({
|
||||
jahr: m.jahr,
|
||||
monat: m.monat,
|
||||
pflegeEigenanteilCent: m.pflegeEigenanteilCent,
|
||||
unterkunftVerpflegungCent: m.unterkunftVerpflegungCent,
|
||||
investitionskostenCent: m.investitionskostenCent,
|
||||
ausbildungsumlageCent: m.ausbildungsumlageCent,
|
||||
zusatzleistungenCent: m.zusatzleistungenCent,
|
||||
pflegekasseCent: m.pflegekasseCent,
|
||||
leistungszuschlagCent: m.leistungszuschlagCent,
|
||||
renteCent: m.renteCent,
|
||||
sonstigeEinkuenfteCent: m.sonstigeEinkuenfteCent,
|
||||
sozialhilfeCent: m.sozialhilfeCent,
|
||||
eigenmittelCent: m.eigenmittelCent,
|
||||
barbetragCent: m.barbetragCent,
|
||||
bekleidungspauschaleCent: m.bekleidungspauschaleCent,
|
||||
abwesenheitstage: m.abwesenheitstage,
|
||||
}),
|
||||
)
|
||||
|
||||
const verlauf = jahresverlaufBilden(jahr, ergebnisse)
|
||||
|
||||
const zeilen: MonatsZeile[] = Array.from({ length: 12 }, (_, i) => {
|
||||
const monat = i + 1
|
||||
const ergebnis = ergebnisse.find((e) => e.monat === monat)
|
||||
const roh = monate.find((m) => m.monat === monat)
|
||||
return {
|
||||
jahr,
|
||||
monat,
|
||||
heimkostenCent: ergebnis?.heimkostenCent ?? 0n,
|
||||
gesamtbedarfCent: ergebnis?.gesamtbedarfCent ?? 0n,
|
||||
deckungCent: ergebnis?.deckungCent ?? 0n,
|
||||
deckungslueckeCent: ergebnis?.deckungslueckeCent ?? 0n,
|
||||
ueberdeckungCent: ergebnis?.ueberdeckungCent ?? 0n,
|
||||
abwesenheitstage: roh?.abwesenheitstage ?? 0,
|
||||
erfasst: ergebnis !== undefined,
|
||||
}
|
||||
})
|
||||
|
||||
// Vorschläge fürs Formular: Pauschale der Pflegekasse zum Pflegegrad
|
||||
// und der gesetzliche Mindest-Barbetrag.
|
||||
const pauschale = pflegekassePauschaleCent(betreute.pflegegrad)
|
||||
const vorschlagPflegekasse = pauschale
|
||||
? {
|
||||
betrag: centZuEingabe(pauschale),
|
||||
text: `Pflegegrad ${betreute.pflegegrad}: ${formatCent(pauschale)} monatlich (${pflegekasseRechtsgrundlage(betreute.pflegegrad)}, Stand ${KONSTANTEN_STAND}).`,
|
||||
}
|
||||
: null
|
||||
|
||||
const einzug = heimvertrag?.einzugAm ?? betreute.heimEinzugAm ?? null
|
||||
const verweildauer = einzug ? verweildauerMonate(einzug, heute) : null
|
||||
|
||||
const naechsterOffenerMonat =
|
||||
zeilen.find((z) => !z.erfasst && z.monat <= (jahr === heute.getUTCFullYear() ? heute.getUTCMonth() + 1 : 12))
|
||||
?.monat ?? (jahr === heute.getUTCFullYear() ? heute.getUTCMonth() + 1 : 1)
|
||||
|
||||
const jahre = Array.from({ length: 6 }, (_, i) => heute.getUTCFullYear() - 3 + i)
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<SeitenKopf
|
||||
titel={`Heimkosten ${jahr}`}
|
||||
beschreibung="Kosten gegen Deckung, Monat für Monat. Zum Bedarf zählen auch Barbetrag und Bekleidungspauschale — sie müssen der betreuten Person verbleiben."
|
||||
pfad={[
|
||||
{ titel: 'Start', href: '/heute' },
|
||||
{ titel: 'Sozialhilfe', href: '/sozialhilfe' },
|
||||
{ titel: 'Heimkosten' },
|
||||
]}
|
||||
aktionen={
|
||||
<nav aria-label="Jahr wählen" className="flex flex-wrap gap-1">
|
||||
{jahre.map((j) => (
|
||||
<Link
|
||||
key={j}
|
||||
href={`/sozialhilfe/heimkosten?jahr=${j}`}
|
||||
aria-current={j === jahr ? 'page' : undefined}
|
||||
className={`inline-flex h-11 min-w-11 items-center justify-center rounded-md px-3 text-sm font-medium ${
|
||||
j === jahr
|
||||
? 'bg-primary text-primary-foreground'
|
||||
: 'border border-input hover:bg-accent'
|
||||
}`}
|
||||
>
|
||||
{j}
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
}
|
||||
/>
|
||||
|
||||
<div className="grid gap-4 sm:grid-cols-3">
|
||||
<Card>
|
||||
<CardContent className="pt-5">
|
||||
<p className="text-sm text-muted-foreground">Bedarf {jahr}</p>
|
||||
<p className="text-xl font-semibold tabular-nums">
|
||||
{formatCent(verlauf.summeBedarfCent)}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card>
|
||||
<CardContent className="pt-5">
|
||||
<p className="text-sm text-muted-foreground">Deckung {jahr}</p>
|
||||
<p className="text-xl font-semibold tabular-nums">
|
||||
{formatCent(verlauf.summeDeckungCent)}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card className={verlauf.summeDeckungslueckeCent > 0n ? 'border-ampel-rot/40' : undefined}>
|
||||
<CardContent className="pt-5">
|
||||
<p className="text-sm text-muted-foreground">Deckungslücke {jahr}</p>
|
||||
<p
|
||||
className={`text-xl font-semibold tabular-nums ${
|
||||
verlauf.summeDeckungslueckeCent > 0n ? 'text-ampel-rot' : 'text-ampel-gruen'
|
||||
}`}
|
||||
>
|
||||
{formatCent(verlauf.summeDeckungslueckeCent)}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{verlauf.groessteLuecke && (
|
||||
<Alert variant="warnung">
|
||||
<TrendingDown aria-hidden="true" />
|
||||
<AlertTitle>
|
||||
Größte Lücke im {monatsname(verlauf.groessteLuecke.monat)}:{' '}
|
||||
{formatCent(verlauf.groessteLuecke.deckungslueckeCent)}
|
||||
</AlertTitle>
|
||||
<AlertDescription>
|
||||
Prüfe, ob der Bescheid für diesen Monat den vollen Bedarf abdeckt. Weicht die Rechnung
|
||||
vom Bescheid ab, ist das der Anlass für eine Rückfrage beim Träger — und gegebenenfalls
|
||||
für einen Widerspruch.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Jahresverlauf</CardTitle>
|
||||
<CardDescription>
|
||||
Gesamtbedarf ist die Heimrechnung plus Barbetrag und Bekleidungspauschale.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<HeimkostenTabelle zeilen={zeilen} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{ergebnisse.some((e) => e.hinweise.length > 0) && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Was auffällt</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ul className="space-y-3">
|
||||
{ergebnisse.flatMap((e) =>
|
||||
e.hinweise.map((hinweis, i) => (
|
||||
<li key={`${e.monat}-${i}`} className="text-sm">
|
||||
<span className="font-medium">{monatsname(e.monat)}: </span>
|
||||
{hinweis}
|
||||
</li>
|
||||
)),
|
||||
)}
|
||||
</ul>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{ergebnisse.some((e) => e.abwesenheit.abwesenheitstage > 0) && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Abwesenheitstage</CardTitle>
|
||||
<CardDescription>
|
||||
Was die Abwesenheit voraussichtlich bringt — eine Schätzung nach dem Rahmenvertrag,
|
||||
nicht die Abrechnung.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ul className="space-y-3">
|
||||
{ergebnisse
|
||||
.filter((e) => e.abwesenheit.abwesenheitstage > 0)
|
||||
.map((e) => (
|
||||
<li key={e.monat} className="text-sm">
|
||||
<span className="font-medium">{monatsname(e.monat)}: </span>
|
||||
{e.abwesenheit.erklaerung} Geschätzte Minderung{' '}
|
||||
<span className="font-medium tabular-nums">
|
||||
{formatCent(e.abwesenheit.geschaetzteMinderungCent)}
|
||||
</span>
|
||||
, die Lücke sänke damit auf{' '}
|
||||
<span className="font-medium tabular-nums">
|
||||
{formatCent(e.deckungslueckeNachAbwesenheitCent)}
|
||||
</span>
|
||||
.
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<Rechtshinweis className="mt-4" rechtsgrundlage="§ 87a Abs. 1 SGB XI, § 75 SGB XI" kurz />
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
<Alert variant="hinweis">
|
||||
<Info aria-hidden="true" />
|
||||
<AlertTitle>Woher die Vorschlagswerte kommen</AlertTitle>
|
||||
<AlertDescription className="space-y-1">
|
||||
<p>
|
||||
Die hinterlegten Beträge haben den Stand {KONSTANTEN_STAND} und sind bis spätestens{' '}
|
||||
{KONSTANTEN_NAECHSTE_PRUEFUNG} nachzuprüfen. Maßgeblich ist immer die Heimrechnung und
|
||||
der Bescheid des Trägers.
|
||||
</p>
|
||||
{verweildauer !== null && (
|
||||
<p>
|
||||
Verweildauer im Heim: {verweildauer}{' '}
|
||||
{verweildauer === 1 ? 'vollendeter Monat' : 'vollendete Monate'} — Leistungszuschlag{' '}
|
||||
{leistungszuschlagProzent(verweildauer)} % ({leistungszuschlagStufe(verweildauer)},
|
||||
§ 43c SGB XI).
|
||||
</p>
|
||||
)}
|
||||
<p>{BEKLEIDUNGSPAUSCHALE_HINWEIS}</p>
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-lg font-semibold">Monat erfassen oder ändern</h2>
|
||||
<HeimkostenFormular
|
||||
jahre={jahre}
|
||||
vorschlagPflegekasse={vorschlagPflegekasse}
|
||||
vorschlagBarbetrag={{
|
||||
betrag: centZuEingabe(BARBETRAG_CENT),
|
||||
text: `Mindestens ${formatCent(BARBETRAG_CENT)} (27 % der Regelbedarfsstufe 1, § 27b Abs. 2 SGB XII, Stand ${KONSTANTEN_STAND}). Steht im Bescheid mehr, gilt der Bescheid.`,
|
||||
}}
|
||||
werte={{
|
||||
jahr,
|
||||
monat: naechsterOffenerMonat,
|
||||
pflegeEigenanteil: centZuEingabe(heimvertrag?.pflegeEigenanteilCent ?? null),
|
||||
unterkunftVerpflegung: centZuEingabe(heimvertrag?.unterkunftVerpflegungCent ?? null),
|
||||
investitionskosten: centZuEingabe(heimvertrag?.investitionskostenCent ?? null),
|
||||
ausbildungsumlage: centZuEingabe(heimvertrag?.ausbildungsumlageCent ?? null),
|
||||
zusatzleistungen: '',
|
||||
pflegekasse: pauschale ? centZuEingabe(pauschale) : '',
|
||||
leistungszuschlagProzent:
|
||||
verweildauer !== null ? leistungszuschlagProzent(verweildauer) : 0,
|
||||
leistungszuschlag: '',
|
||||
rente: '',
|
||||
sonstigeEinkuenfte: '',
|
||||
sozialhilfe: '',
|
||||
eigenmittel: '',
|
||||
barbetrag: centZuEingabe(BARBETRAG_CENT),
|
||||
bekleidungspauschale: '',
|
||||
abwesenheitstage: 0,
|
||||
bemerkung: '',
|
||||
}}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<Rechtshinweis rechtsgrundlage="§ 43 SGB XI, § 43c SGB XI, § 27b SGB XII" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
import Link from 'next/link'
|
||||
import { SeitenKopf } from '@/components/seiten-kopf'
|
||||
import { Rechtshinweis } from '@/components/rechtshinweis'
|
||||
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { HeimkostenFormular } from '@/components/sozialhilfe/heimkosten-formular'
|
||||
import { HeimkostenTabelle, type MonatsZeile } from '@/components/sozialhilfe/heimkosten-tabelle'
|
||||
import { betragFormatieren } from '@/lib/finanzen/betrag'
|
||||
import { db, nichtGeloescht } from '@/lib/db'
|
||||
import { heuteAlsTag } from '@/lib/fristen/datum'
|
||||
import {
|
||||
BARBETRAG_CENT,
|
||||
BEKLEIDUNGSPAUSCHALE_HINWEIS,
|
||||
KONSTANTEN_NAECHSTE_PRUEFUNG,
|
||||
KONSTANTEN_STAND,
|
||||
} from '@/lib/sozialhilfe/konstanten'
|
||||
import {
|
||||
heimkostenAuswerten,
|
||||
jahresverlaufBilden,
|
||||
KOSTENLESART_BESCHRIFTUNG,
|
||||
KOSTENLESART_ERKLAERUNG,
|
||||
KOSTENLESART_STANDARD,
|
||||
leistungszuschlagProzent,
|
||||
leistungszuschlagStufe,
|
||||
monatsname,
|
||||
pflegekassePauschaleCent,
|
||||
pflegekasseRechtsgrundlage,
|
||||
verweildauerMonate,
|
||||
} from '@/lib/sozialhilfe/heimkosten'
|
||||
import { sozialhilfeKontext } from '@/lib/sozialhilfe/zugang'
|
||||
import { formatCent } from '@/lib/utils'
|
||||
import { Info, TrendingDown } from 'lucide-react'
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
/**
|
||||
* Heimkosten-Gegenüberstellung.
|
||||
*
|
||||
* Die eine Zahl, um die es geht, ist die Deckungslücke: Was bleibt übrig,
|
||||
* nachdem Pflegekasse, Rente und Sozialhilfe gezahlt haben? Sie steht je
|
||||
* Monat und als Jahressumme da - denn eine Lücke von 180 Euro im Monat
|
||||
* sind über ein Jahr mehr als zweitausend.
|
||||
*/
|
||||
export default async function HeimkostenSeite({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: Promise<{ jahr?: string }>
|
||||
}) {
|
||||
const { betreuteId, betreute } = await sozialhilfeKontext()
|
||||
const parameter = await searchParams
|
||||
|
||||
const heute = heuteAlsTag()
|
||||
const gewaehltesJahr = Number.parseInt(parameter.jahr ?? '', 10)
|
||||
const jahr = Number.isFinite(gewaehltesJahr) ? gewaehltesJahr : heute.getUTCFullYear()
|
||||
|
||||
const [monate, heimvertrag] = await Promise.all([
|
||||
db.heimkostenmonat.findMany({
|
||||
where: { betreuteId, jahr },
|
||||
orderBy: { monat: 'asc' },
|
||||
}),
|
||||
db.heimvertrag.findUnique({ where: { betreuteId } }),
|
||||
])
|
||||
|
||||
const ergebnisse = monate.map((m) =>
|
||||
heimkostenAuswerten({
|
||||
jahr: m.jahr,
|
||||
monat: m.monat,
|
||||
pflegeEigenanteilCent: m.pflegeEigenanteilCent,
|
||||
unterkunftVerpflegungCent: m.unterkunftVerpflegungCent,
|
||||
investitionskostenCent: m.investitionskostenCent,
|
||||
ausbildungsumlageCent: m.ausbildungsumlageCent,
|
||||
zusatzleistungenCent: m.zusatzleistungenCent,
|
||||
pflegekasseCent: m.pflegekasseCent,
|
||||
leistungszuschlagCent: m.leistungszuschlagCent,
|
||||
renteCent: m.renteCent,
|
||||
sonstigeEinkuenfteCent: m.sonstigeEinkuenfteCent,
|
||||
sozialhilfeCent: m.sozialhilfeCent,
|
||||
eigenmittelCent: m.eigenmittelCent,
|
||||
barbetragCent: m.barbetragCent,
|
||||
bekleidungspauschaleCent: m.bekleidungspauschaleCent,
|
||||
abwesenheitstage: m.abwesenheitstage,
|
||||
}),
|
||||
)
|
||||
|
||||
const verlauf = jahresverlaufBilden(jahr, ergebnisse)
|
||||
|
||||
const zeilen: MonatsZeile[] = Array.from({ length: 12 }, (_, i) => {
|
||||
const monat = i + 1
|
||||
const ergebnis = ergebnisse.find((e) => e.monat === monat)
|
||||
const roh = monate.find((m) => m.monat === monat)
|
||||
return {
|
||||
jahr,
|
||||
monat,
|
||||
heimkostenCent: ergebnis?.heimkostenCent ?? 0n,
|
||||
gesamtbedarfCent: ergebnis?.gesamtbedarfCent ?? 0n,
|
||||
deckungCent: ergebnis?.deckungCent ?? 0n,
|
||||
deckungslueckeCent: ergebnis?.deckungslueckeCent ?? 0n,
|
||||
ueberdeckungCent: ergebnis?.ueberdeckungCent ?? 0n,
|
||||
abwesenheitstage: roh?.abwesenheitstage ?? 0,
|
||||
erfasst: ergebnis !== undefined,
|
||||
}
|
||||
})
|
||||
|
||||
// Vorschläge fürs Formular: Pauschale der Pflegekasse zum Pflegegrad
|
||||
// und der gesetzliche Mindest-Barbetrag.
|
||||
const pauschale = pflegekassePauschaleCent(betreute.pflegegrad)
|
||||
|
||||
// Bewusst nur ein Hinweis, kein Vorschlagswert zum Uebernehmen: Unter der
|
||||
// Lesart EIGENANTEIL_NETTO steckt die Pauschale schon im Eigenanteil.
|
||||
// Wer sie hier zusaetzlich eintraegt, zaehlt dasselbe Geld zweimal.
|
||||
const hinweisPflegekasse = pauschale
|
||||
? `Pflegegrad ${betreute.pflegegrad}: ${formatCent(pauschale)} monatlich ` +
|
||||
`(${pflegekasseRechtsgrundlage(betreute.pflegegrad)}, Stand ${KONSTANTEN_STAND}). ` +
|
||||
`Dieser Betrag ist im Eigenanteil oben bereits abgezogen — lass das Feld deshalb auf 0. ` +
|
||||
`Es dient nur der Erinnerung, was die Pflegekasse direkt an die Einrichtung zahlt.`
|
||||
: 'Pauschale nach § 43 SGB XI. Sie ist im Eigenanteil bereits abgezogen — lass das Feld ' +
|
||||
'auf 0. Hinterlege den Pflegegrad in den Stammdaten, dann steht hier der Betrag.'
|
||||
|
||||
const einzug = heimvertrag?.einzugAm ?? betreute.heimEinzugAm ?? null
|
||||
const verweildauer = einzug ? verweildauerMonate(einzug, heute) : null
|
||||
|
||||
const naechsterOffenerMonat =
|
||||
zeilen.find((z) => !z.erfasst && z.monat <= (jahr === heute.getUTCFullYear() ? heute.getUTCMonth() + 1 : 12))
|
||||
?.monat ?? (jahr === heute.getUTCFullYear() ? heute.getUTCMonth() + 1 : 1)
|
||||
|
||||
const jahre = Array.from({ length: 6 }, (_, i) => heute.getUTCFullYear() - 3 + i)
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<SeitenKopf
|
||||
titel={`Heimkosten ${jahr}`}
|
||||
beschreibung="Kosten gegen Deckung, Monat für Monat. Zum Bedarf zählen auch Barbetrag und Bekleidungspauschale — sie müssen der betreuten Person verbleiben."
|
||||
pfad={[
|
||||
{ titel: 'Start', href: '/heute' },
|
||||
{ titel: 'Sozialhilfe', href: '/sozialhilfe' },
|
||||
{ titel: 'Heimkosten' },
|
||||
]}
|
||||
aktionen={
|
||||
<nav aria-label="Jahr wählen" className="flex flex-wrap gap-1">
|
||||
{jahre.map((j) => (
|
||||
<Link
|
||||
key={j}
|
||||
href={`/sozialhilfe/heimkosten?jahr=${j}`}
|
||||
aria-current={j === jahr ? 'page' : undefined}
|
||||
className={`inline-flex h-11 min-w-11 items-center justify-center rounded-md px-3 text-sm font-medium ${
|
||||
j === jahr
|
||||
? 'bg-primary text-primary-foreground'
|
||||
: 'border border-input hover:bg-accent'
|
||||
}`}
|
||||
>
|
||||
{j}
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
}
|
||||
/>
|
||||
|
||||
<div className="grid gap-4 sm:grid-cols-3">
|
||||
<Card>
|
||||
<CardContent className="pt-5">
|
||||
<p className="text-sm text-muted-foreground">Bedarf {jahr}</p>
|
||||
<p className="text-xl font-semibold tabular-nums">
|
||||
{formatCent(verlauf.summeBedarfCent)}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card>
|
||||
<CardContent className="pt-5">
|
||||
<p className="text-sm text-muted-foreground">Deckung {jahr}</p>
|
||||
<p className="text-xl font-semibold tabular-nums">
|
||||
{formatCent(verlauf.summeDeckungCent)}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card className={verlauf.summeDeckungslueckeCent > 0n ? 'border-ampel-rot/40' : undefined}>
|
||||
<CardContent className="pt-5">
|
||||
<p className="text-sm text-muted-foreground">Deckungslücke {jahr}</p>
|
||||
<p
|
||||
className={`text-xl font-semibold tabular-nums ${
|
||||
verlauf.summeDeckungslueckeCent > 0n ? 'text-ampel-rot' : 'text-ampel-gruen'
|
||||
}`}
|
||||
>
|
||||
{formatCent(verlauf.summeDeckungslueckeCent)}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{verlauf.groessteLuecke && (
|
||||
<Alert variant="warnung">
|
||||
<TrendingDown aria-hidden="true" />
|
||||
<AlertTitle>
|
||||
Größte Lücke im {monatsname(verlauf.groessteLuecke.monat)}:{' '}
|
||||
{formatCent(verlauf.groessteLuecke.deckungslueckeCent)}
|
||||
</AlertTitle>
|
||||
<AlertDescription>
|
||||
Prüfe, ob der Bescheid für diesen Monat den vollen Bedarf abdeckt. Weicht die Rechnung
|
||||
vom Bescheid ab, ist das der Anlass für eine Rückfrage beim Träger — und gegebenenfalls
|
||||
für einen Widerspruch.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Was hier gegen was gerechnet wird</CardTitle>
|
||||
<CardDescription>
|
||||
Lesart: {KOSTENLESART_BESCHRIFTUNG[KOSTENLESART_STANDARD]}. Ohne diese Festlegung
|
||||
lässt sich die Rechnung nicht mit der Heimrechnung vergleichen.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4 text-sm">
|
||||
<p>{KOSTENLESART_ERKLAERUNG[KOSTENLESART_STANDARD]}</p>
|
||||
|
||||
<div className="grid gap-4 sm:grid-cols-2">
|
||||
<div className="rounded-md border p-3">
|
||||
<p className="font-semibold">Bedarf</p>
|
||||
<ul className="mt-1 list-inside list-disc text-muted-foreground">
|
||||
<li>einrichtungseinheitlicher Eigenanteil (bereits netto)</li>
|
||||
<li>Unterkunft und Verpflegung</li>
|
||||
<li>Investitionskosten</li>
|
||||
<li>Ausbildungsumlage</li>
|
||||
<li>Zusatzleistungen</li>
|
||||
<li>Barbetrag (§ 27b Abs. 2 SGB XII)</li>
|
||||
<li>Bekleidungspauschale</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="rounded-md border p-3">
|
||||
<p className="font-semibold">Deckung</p>
|
||||
<ul className="mt-1 list-inside list-disc text-muted-foreground">
|
||||
<li>Leistungszuschlag (§ 43c SGB XI)</li>
|
||||
<li>Rente</li>
|
||||
<li>sonstige Einkünfte</li>
|
||||
<li>Anteil der Sozialhilfe</li>
|
||||
<li>Eigenmittel</li>
|
||||
</ul>
|
||||
<p className="mt-2 text-muted-foreground">
|
||||
<span className="font-medium">Nicht dabei:</span> die Pauschale der Pflegekasse
|
||||
nach § 43 Abs. 2 SGB XI. Sie ist im Eigenanteil schon abgezogen und würde hier ein
|
||||
zweites Mal zählen.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-muted-foreground">
|
||||
Barbetrag und Bekleidungspauschale stehen bewusst auf der Bedarfsseite: Sie müssen der
|
||||
betreuten Person tatsächlich verbleiben. Wer sie herauslässt, hält die Deckung für
|
||||
besser, als sie ist.
|
||||
</p>
|
||||
|
||||
<Rechtshinweis rechtsgrundlage="§ 43 Abs. 2 SGB XI, § 43c SGB XI, § 27b SGB XII" kurz />
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Jahresverlauf</CardTitle>
|
||||
<CardDescription>
|
||||
Gesamtbedarf ist die Heimrechnung plus Barbetrag und Bekleidungspauschale.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<HeimkostenTabelle zeilen={zeilen} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{ergebnisse.some((e) => e.hinweise.length > 0) && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Was auffällt</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ul className="space-y-3">
|
||||
{ergebnisse.flatMap((e) =>
|
||||
e.hinweise.map((hinweis, i) => (
|
||||
<li key={`${e.monat}-${i}`} className="text-sm">
|
||||
<span className="font-medium">{monatsname(e.monat)}: </span>
|
||||
{hinweis}
|
||||
</li>
|
||||
)),
|
||||
)}
|
||||
</ul>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{ergebnisse.some((e) => e.abwesenheit.abwesenheitstage > 0) && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Abwesenheitstage</CardTitle>
|
||||
<CardDescription>
|
||||
Was die Abwesenheit voraussichtlich bringt — eine Schätzung nach dem Rahmenvertrag,
|
||||
nicht die Abrechnung.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ul className="space-y-3">
|
||||
{ergebnisse
|
||||
.filter((e) => e.abwesenheit.abwesenheitstage > 0)
|
||||
.map((e) => (
|
||||
<li key={e.monat} className="text-sm">
|
||||
<span className="font-medium">{monatsname(e.monat)}: </span>
|
||||
{e.abwesenheit.erklaerung} Geschätzte Minderung{' '}
|
||||
<span className="font-medium tabular-nums">
|
||||
{formatCent(e.abwesenheit.geschaetzteMinderungCent)}
|
||||
</span>
|
||||
, die Lücke sänke damit auf{' '}
|
||||
<span className="font-medium tabular-nums">
|
||||
{formatCent(e.deckungslueckeNachAbwesenheitCent)}
|
||||
</span>
|
||||
.
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<Rechtshinweis className="mt-4" rechtsgrundlage="§ 87a Abs. 1 SGB XI, § 75 SGB XI" kurz />
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
<Alert variant="hinweis">
|
||||
<Info aria-hidden="true" />
|
||||
<AlertTitle>Woher die Vorschlagswerte kommen</AlertTitle>
|
||||
<AlertDescription className="space-y-1">
|
||||
<p>
|
||||
Die hinterlegten Beträge haben den Stand {KONSTANTEN_STAND} und sind bis spätestens{' '}
|
||||
{KONSTANTEN_NAECHSTE_PRUEFUNG} nachzuprüfen. Maßgeblich ist immer die Heimrechnung und
|
||||
der Bescheid des Trägers.
|
||||
</p>
|
||||
{verweildauer !== null && (
|
||||
<p>
|
||||
Verweildauer im Heim: {verweildauer}{' '}
|
||||
{verweildauer === 1 ? 'vollendeter Monat' : 'vollendete Monate'} — Leistungszuschlag{' '}
|
||||
{leistungszuschlagProzent(verweildauer)} % ({leistungszuschlagStufe(verweildauer)},
|
||||
§ 43c SGB XI).
|
||||
</p>
|
||||
)}
|
||||
<p>{BEKLEIDUNGSPAUSCHALE_HINWEIS}</p>
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-lg font-semibold">Monat erfassen oder ändern</h2>
|
||||
<HeimkostenFormular
|
||||
jahre={jahre}
|
||||
hinweisPflegekasse={hinweisPflegekasse}
|
||||
vorschlagBarbetrag={{
|
||||
betrag: betragFormatieren(BARBETRAG_CENT),
|
||||
text: `Mindestens ${formatCent(BARBETRAG_CENT)} (27 % der Regelbedarfsstufe 1, § 27b Abs. 2 SGB XII, Stand ${KONSTANTEN_STAND}). Steht im Bescheid mehr, gilt der Bescheid.`,
|
||||
}}
|
||||
werte={{
|
||||
jahr,
|
||||
monat: naechsterOffenerMonat,
|
||||
pflegeEigenanteil: eingabefeld(heimvertrag?.pflegeEigenanteilCent ?? null),
|
||||
unterkunftVerpflegung: eingabefeld(heimvertrag?.unterkunftVerpflegungCent ?? null),
|
||||
investitionskosten: eingabefeld(heimvertrag?.investitionskostenCent ?? null),
|
||||
ausbildungsumlage: eingabefeld(heimvertrag?.ausbildungsumlageCent ?? null),
|
||||
zusatzleistungen: '',
|
||||
// Absichtlich leer: siehe hinweisPflegekasse.
|
||||
pflegekasse: '',
|
||||
leistungszuschlagProzent:
|
||||
verweildauer !== null ? leistungszuschlagProzent(verweildauer) : 0,
|
||||
leistungszuschlag: '',
|
||||
rente: '',
|
||||
sonstigeEinkuenfte: '',
|
||||
sozialhilfe: '',
|
||||
eigenmittel: '',
|
||||
barbetrag: betragFormatieren(BARBETRAG_CENT),
|
||||
bekleidungspauschale: '',
|
||||
abwesenheitstage: 0,
|
||||
bemerkung: '',
|
||||
}}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<Rechtshinweis rechtsgrundlage="§ 43 SGB XI, § 43c SGB XI, § 27b SGB XII" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/** Cent fuer ein Eingabefeld, null wird zu leer. */
|
||||
function eingabefeld(cent: bigint | null): string {
|
||||
return cent === null ? '' : betragFormatieren(cent)
|
||||
}
|
||||
|
||||
@@ -1,229 +1,232 @@
|
||||
import { SeitenKopf } from '@/components/seiten-kopf'
|
||||
import { Rechtshinweis } from '@/components/rechtshinweis'
|
||||
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
|
||||
import { Card, CardContent } from '@/components/ui/card'
|
||||
import { Vermoegensliste, type ListenPosten } from '@/components/sozialhilfe/vermoegensliste'
|
||||
import { oeffnen } from '@/lib/crypto/feld'
|
||||
import { centZuZahl, db, nichtGeloescht } from '@/lib/db'
|
||||
import { alsIsoTag } from '@/lib/fristen/datum'
|
||||
import { VERMOEGENSART_BESCHRIFTUNG, VERMOEGENSRELEVANTE_VERTRAGSARTEN, VERTRAGSART_BESCHRIFTUNG } from '@/lib/sozialhilfe/arten'
|
||||
import { BEWERTUNG_BESCHRIFTUNG } from '@/lib/sozialhilfe/schonvermoegen'
|
||||
import { freibetragVergleichen, uebersichtBilden } from '@/lib/sozialhilfe/vermoegensuebersicht'
|
||||
import { sozialhilfeKontext } from '@/lib/sozialhilfe/zugang'
|
||||
import { formatCent } from '@/lib/utils'
|
||||
import { AlertCircle } from 'lucide-react'
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
/**
|
||||
* Vermögens-Bewertungsansicht.
|
||||
*
|
||||
* Sie zählt zusammen und stellt gegenüber - sie entscheidet nichts. Was
|
||||
* geschützt ist, sagt der Träger; bleibt man uneins, das Sozialgericht.
|
||||
* Deshalb heißt die Spalte "Einschätzung" und nicht "Bewertung".
|
||||
*/
|
||||
export default async function VermoegensbewertungSeite() {
|
||||
const { betreuteId, darfSchreiben } = await sozialhilfeKontext()
|
||||
|
||||
const [posten, vertraege] = await Promise.all([
|
||||
db.vermoegensposten.findMany({
|
||||
where: { betreuteId, ...nichtGeloescht },
|
||||
include: { wertstaende: { orderBy: { stichtag: 'desc' }, take: 1 } },
|
||||
orderBy: { bezeichnung: 'asc' },
|
||||
}),
|
||||
db.vertrag.findMany({
|
||||
where: {
|
||||
betreuteId,
|
||||
art: { in: [...VERMOEGENSRELEVANTE_VERTRAGSARTEN] },
|
||||
...nichtGeloescht,
|
||||
},
|
||||
orderBy: { bezeichnung: 'asc' },
|
||||
}),
|
||||
])
|
||||
|
||||
// Verträge, die schon als Vermögensposten geführt werden, nicht doppelt
|
||||
// zeigen - sonst wäre die Summe zweimal drin.
|
||||
const bereitsAlsPosten = new Set(
|
||||
posten.map((p) => p.vertragId).filter((id): id is string => id !== null),
|
||||
)
|
||||
|
||||
const zeilen: ListenPosten[] = [
|
||||
...posten.map((p) => {
|
||||
const geoeffnet = oeffnen('Vermoegensposten', p)
|
||||
const stand = p.wertstaende[0]
|
||||
return {
|
||||
id: p.id,
|
||||
bezeichnung: p.bezeichnung,
|
||||
herkunft: 'VERMOEGEN' as const,
|
||||
art: p.art,
|
||||
artBeschriftung: VERMOEGENSART_BESCHRIFTUNG[p.art],
|
||||
bewertung: p.bewertungSozialhilfe,
|
||||
wertCent: stand ? centZuZahl(stand.wertCent) : null,
|
||||
wertStichtag: stand ? alsIsoTag(stand.stichtag) : null,
|
||||
begruendung: geoeffnet.bewertungBegruendung ?? '',
|
||||
zusatz: stand ? stand.wertquelle : null,
|
||||
}
|
||||
}),
|
||||
...vertraege
|
||||
.filter((v) => !bereitsAlsPosten.has(v.id))
|
||||
.map((v) => {
|
||||
const geoeffnet = oeffnen('Vertrag', v)
|
||||
return {
|
||||
id: v.id,
|
||||
bezeichnung: v.bezeichnung,
|
||||
herkunft: 'VERTRAG' as const,
|
||||
art: v.art,
|
||||
artBeschriftung: VERTRAGSART_BESCHRIFTUNG[v.art],
|
||||
bewertung: v.bewertungSozialhilfe,
|
||||
// Der Rückkaufswert steht am Vertrag nicht als Betrag; er wird
|
||||
// als Vermögensposten mit Wertstand geführt. Bis dahin: offen.
|
||||
wertCent: null,
|
||||
wertStichtag: null,
|
||||
begruendung: geoeffnet.bewertungBegruendung ?? '',
|
||||
zusatz: v.gesellschaft,
|
||||
}
|
||||
}),
|
||||
]
|
||||
|
||||
const uebersicht = uebersichtBilden(
|
||||
zeilen.map((z) => ({
|
||||
id: z.id,
|
||||
bezeichnung: z.bezeichnung,
|
||||
herkunft: z.herkunft,
|
||||
artBeschriftung: z.artBeschriftung,
|
||||
bewertung: z.bewertung,
|
||||
wertCent: z.wertCent === null ? null : BigInt(z.wertCent),
|
||||
wertStichtag: null,
|
||||
begruendung: z.begruendung,
|
||||
href: '#',
|
||||
})),
|
||||
)
|
||||
|
||||
const vergleich = freibetragVergleichen(uebersicht.jeBewertung.VERWERTBAR.summeCent)
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<SeitenKopf
|
||||
titel="Vermögen bewerten"
|
||||
beschreibung="Jeder Posten bekommt eine Einschätzung und — das ist der wichtige Teil — eine Begründung. Ohne Begründung hilft die Einordnung im Widerspruchsverfahren nicht weiter."
|
||||
pfad={[
|
||||
{ titel: 'Start', href: '/heute' },
|
||||
{ titel: 'Sozialhilfe', href: '/sozialhilfe' },
|
||||
{ titel: 'Vermögen' },
|
||||
]}
|
||||
/>
|
||||
|
||||
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
|
||||
<Summenkachel
|
||||
titel={BEWERTUNG_BESCHRIFTUNG.VERWERTBAR}
|
||||
betrag={uebersicht.jeBewertung.VERWERTBAR.summeCent}
|
||||
anzahl={uebersicht.jeBewertung.VERWERTBAR.anzahl}
|
||||
ohneWert={uebersicht.jeBewertung.VERWERTBAR.ohneWert}
|
||||
farbe="text-ampel-rot"
|
||||
/>
|
||||
<Summenkachel
|
||||
titel={BEWERTUNG_BESCHRIFTUNG.GESCHUETZT}
|
||||
betrag={uebersicht.jeBewertung.GESCHUETZT.summeCent}
|
||||
anzahl={uebersicht.jeBewertung.GESCHUETZT.anzahl}
|
||||
ohneWert={uebersicht.jeBewertung.GESCHUETZT.ohneWert}
|
||||
farbe="text-ampel-gruen"
|
||||
/>
|
||||
<Summenkachel
|
||||
titel={BEWERTUNG_BESCHRIFTUNG.STRITTIG}
|
||||
betrag={uebersicht.jeBewertung.STRITTIG.summeCent}
|
||||
anzahl={uebersicht.jeBewertung.STRITTIG.anzahl}
|
||||
ohneWert={uebersicht.jeBewertung.STRITTIG.ohneWert}
|
||||
farbe="text-ampel-orange"
|
||||
/>
|
||||
<Summenkachel
|
||||
titel={BEWERTUNG_BESCHRIFTUNG.UNGEPRUEFT}
|
||||
betrag={uebersicht.jeBewertung.UNGEPRUEFT.summeCent}
|
||||
anzahl={uebersicht.jeBewertung.UNGEPRUEFT.anzahl}
|
||||
ohneWert={uebersicht.jeBewertung.UNGEPRUEFT.ohneWert}
|
||||
farbe="text-muted-foreground"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Card>
|
||||
<CardContent className="space-y-2 pt-5">
|
||||
<div className="flex flex-wrap items-baseline justify-between gap-3">
|
||||
<span className="text-sm text-muted-foreground">
|
||||
Vermögensfreibetrag (§ 90 Abs. 2 Nr. 9 SGB XII)
|
||||
</span>
|
||||
<span className="text-lg font-semibold tabular-nums">
|
||||
{formatCent(vergleich.freibetragCent)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-wrap items-baseline justify-between gap-3">
|
||||
<span className="text-sm text-muted-foreground">Über dem Freibetrag</span>
|
||||
<span
|
||||
className={`text-lg font-semibold tabular-nums ${
|
||||
vergleich.ausgeschoepft ? 'text-ampel-rot' : 'text-ampel-gruen'
|
||||
}`}
|
||||
>
|
||||
{formatCent(vergleich.ueberFreibetragCent)}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground">{vergleich.hinweis}</p>
|
||||
<Rechtshinweis rechtsgrundlage="§ 90 SGB XII" kurz />
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{uebersicht.ungeprueft.length > 0 && (
|
||||
<Alert variant="warnung">
|
||||
<AlertCircle aria-hidden="true" />
|
||||
<AlertTitle>
|
||||
{uebersicht.ungeprueft.length}{' '}
|
||||
{uebersicht.ungeprueft.length === 1 ? 'Posten ist' : 'Posten sind'} noch ungeprüft
|
||||
</AlertTitle>
|
||||
<AlertDescription>
|
||||
Vor dem Antrag sollte hier nichts mehr stehen — der Träger fragt sonst nach, und das
|
||||
kostet Wochen.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{uebersicht.ohneBegruendung.length > 0 && (
|
||||
<Alert variant="warnung">
|
||||
<AlertCircle aria-hidden="true" />
|
||||
<AlertTitle>Bewertung ohne Begründung</AlertTitle>
|
||||
<AlertDescription>
|
||||
{uebersicht.ohneBegruendung.map((p) => p.bezeichnung).join(', ')} — trag nach, worauf
|
||||
sich die Einschätzung stützt. Im Widerspruchsverfahren zählt nur, was begründet ist.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<Vermoegensliste posten={zeilen} darfSchreiben={darfSchreiben} />
|
||||
|
||||
<Rechtshinweis rechtsgrundlage="§ 90 SGB XII, § 91 SGB XII" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function Summenkachel({
|
||||
titel,
|
||||
betrag,
|
||||
anzahl,
|
||||
ohneWert,
|
||||
farbe,
|
||||
}: {
|
||||
titel: string
|
||||
betrag: bigint
|
||||
anzahl: number
|
||||
ohneWert: number
|
||||
farbe: string
|
||||
}) {
|
||||
return (
|
||||
<Card>
|
||||
<CardContent className="pt-5">
|
||||
<p className="text-sm text-muted-foreground">{titel}</p>
|
||||
<p className={`text-xl font-semibold tabular-nums ${farbe}`}>{formatCent(betrag)}</p>
|
||||
<p className="mt-1 text-xs text-muted-foreground">
|
||||
{anzahl} {anzahl === 1 ? 'Posten' : 'Posten'}
|
||||
{ohneWert > 0 && `, davon ${ohneWert} ohne Wert`}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
import { SeitenKopf } from '@/components/seiten-kopf'
|
||||
import { Rechtshinweis } from '@/components/rechtshinweis'
|
||||
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
|
||||
import { Card, CardContent } from '@/components/ui/card'
|
||||
import { Vermoegensliste, type ListenPosten } from '@/components/sozialhilfe/vermoegensliste'
|
||||
import { oeffnen } from '@/lib/crypto/feld'
|
||||
import { centZuZahl, db, nichtGeloescht } from '@/lib/db'
|
||||
import { alsIsoTag } from '@/lib/fristen/datum'
|
||||
import { VERMOEGENS_ART_BESCHRIFTUNG } from '@/lib/finanzen/kategorien'
|
||||
import { MIT_RUECKKAUFSWERT, VERTRAG_ART_BESCHRIFTUNG } from '@/lib/vertraege/konstanten'
|
||||
import { BEWERTUNG_BESCHRIFTUNG } from '@/lib/sozialhilfe/schonvermoegen'
|
||||
import { freibetragVergleichen, uebersichtBilden } from '@/lib/sozialhilfe/vermoegensuebersicht'
|
||||
import { sozialhilfeKontext } from '@/lib/sozialhilfe/zugang'
|
||||
import { formatCent } from '@/lib/utils'
|
||||
import { AlertCircle } from 'lucide-react'
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
/**
|
||||
* Vermögens-Bewertungsansicht.
|
||||
*
|
||||
* Sie zählt zusammen und stellt gegenüber - sie entscheidet nichts. Was
|
||||
* geschützt ist, sagt der Träger; bleibt man uneins, das Sozialgericht.
|
||||
* Deshalb heißt die Spalte "Einschätzung" und nicht "Bewertung".
|
||||
*/
|
||||
export default async function VermoegensbewertungSeite() {
|
||||
const { betreuteId, darfSchreiben } = await sozialhilfeKontext()
|
||||
|
||||
const [posten, vertraege] = await Promise.all([
|
||||
db.vermoegensposten.findMany({
|
||||
where: { betreuteId, ...nichtGeloescht },
|
||||
include: { wertstaende: { orderBy: { stichtag: 'desc' }, take: 1 } },
|
||||
orderBy: { bezeichnung: 'asc' },
|
||||
}),
|
||||
db.vertrag.findMany({
|
||||
where: {
|
||||
betreuteId,
|
||||
// Nur Vertraege, die ueberhaupt einen Wert darstellen koennen. Ein
|
||||
// Stromvertrag hat keinen Rueckkaufswert und wuerde die Liste zumuellen.
|
||||
art: { in: [...MIT_RUECKKAUFSWERT] },
|
||||
...nichtGeloescht,
|
||||
},
|
||||
orderBy: { bezeichnung: 'asc' },
|
||||
}),
|
||||
])
|
||||
|
||||
// Verträge, die schon als Vermögensposten geführt werden, nicht doppelt
|
||||
// zeigen - sonst wäre die Summe zweimal drin.
|
||||
const bereitsAlsPosten = new Set(
|
||||
posten.map((p) => p.vertragId).filter((id): id is string => id !== null),
|
||||
)
|
||||
|
||||
const zeilen: ListenPosten[] = [
|
||||
...posten.map((p) => {
|
||||
const geoeffnet = oeffnen('Vermoegensposten', p)
|
||||
const stand = p.wertstaende[0]
|
||||
return {
|
||||
id: p.id,
|
||||
bezeichnung: p.bezeichnung,
|
||||
herkunft: 'VERMOEGEN' as const,
|
||||
art: p.art,
|
||||
artBeschriftung: VERMOEGENS_ART_BESCHRIFTUNG[p.art],
|
||||
bewertung: p.bewertungSozialhilfe,
|
||||
wertCent: stand ? centZuZahl(stand.wertCent) : null,
|
||||
wertStichtag: stand ? alsIsoTag(stand.stichtag) : null,
|
||||
begruendung: geoeffnet.bewertungBegruendung ?? '',
|
||||
zusatz: stand ? stand.wertquelle : null,
|
||||
}
|
||||
}),
|
||||
...vertraege
|
||||
.filter((v) => !bereitsAlsPosten.has(v.id))
|
||||
.map((v) => {
|
||||
const geoeffnet = oeffnen('Vertrag', v)
|
||||
return {
|
||||
id: v.id,
|
||||
bezeichnung: v.bezeichnung,
|
||||
herkunft: 'VERTRAG' as const,
|
||||
art: v.art,
|
||||
artBeschriftung: VERTRAG_ART_BESCHRIFTUNG[v.art],
|
||||
bewertung: v.bewertungSozialhilfe,
|
||||
// Der Rückkaufswert steht am Vertrag nicht als Betrag; er wird
|
||||
// als Vermögensposten mit Wertstand geführt. Bis dahin: offen.
|
||||
wertCent: null,
|
||||
wertStichtag: null,
|
||||
begruendung: geoeffnet.bewertungBegruendung ?? '',
|
||||
zusatz: v.gesellschaft,
|
||||
}
|
||||
}),
|
||||
]
|
||||
|
||||
const uebersicht = uebersichtBilden(
|
||||
zeilen.map((z) => ({
|
||||
id: z.id,
|
||||
bezeichnung: z.bezeichnung,
|
||||
herkunft: z.herkunft,
|
||||
artBeschriftung: z.artBeschriftung,
|
||||
bewertung: z.bewertung,
|
||||
wertCent: z.wertCent === null ? null : BigInt(z.wertCent),
|
||||
wertStichtag: null,
|
||||
begruendung: z.begruendung,
|
||||
href: '#',
|
||||
})),
|
||||
)
|
||||
|
||||
const vergleich = freibetragVergleichen(uebersicht.jeBewertung.VERWERTBAR.summeCent)
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<SeitenKopf
|
||||
titel="Vermögen bewerten"
|
||||
beschreibung="Jeder Posten bekommt eine Einschätzung und — das ist der wichtige Teil — eine Begründung. Ohne Begründung hilft die Einordnung im Widerspruchsverfahren nicht weiter."
|
||||
pfad={[
|
||||
{ titel: 'Start', href: '/heute' },
|
||||
{ titel: 'Sozialhilfe', href: '/sozialhilfe' },
|
||||
{ titel: 'Vermögen' },
|
||||
]}
|
||||
/>
|
||||
|
||||
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
|
||||
<Summenkachel
|
||||
titel={BEWERTUNG_BESCHRIFTUNG.VERWERTBAR}
|
||||
betrag={uebersicht.jeBewertung.VERWERTBAR.summeCent}
|
||||
anzahl={uebersicht.jeBewertung.VERWERTBAR.anzahl}
|
||||
ohneWert={uebersicht.jeBewertung.VERWERTBAR.ohneWert}
|
||||
farbe="text-ampel-rot"
|
||||
/>
|
||||
<Summenkachel
|
||||
titel={BEWERTUNG_BESCHRIFTUNG.GESCHUETZT}
|
||||
betrag={uebersicht.jeBewertung.GESCHUETZT.summeCent}
|
||||
anzahl={uebersicht.jeBewertung.GESCHUETZT.anzahl}
|
||||
ohneWert={uebersicht.jeBewertung.GESCHUETZT.ohneWert}
|
||||
farbe="text-ampel-gruen"
|
||||
/>
|
||||
<Summenkachel
|
||||
titel={BEWERTUNG_BESCHRIFTUNG.STRITTIG}
|
||||
betrag={uebersicht.jeBewertung.STRITTIG.summeCent}
|
||||
anzahl={uebersicht.jeBewertung.STRITTIG.anzahl}
|
||||
ohneWert={uebersicht.jeBewertung.STRITTIG.ohneWert}
|
||||
farbe="text-ampel-orange"
|
||||
/>
|
||||
<Summenkachel
|
||||
titel={BEWERTUNG_BESCHRIFTUNG.UNGEPRUEFT}
|
||||
betrag={uebersicht.jeBewertung.UNGEPRUEFT.summeCent}
|
||||
anzahl={uebersicht.jeBewertung.UNGEPRUEFT.anzahl}
|
||||
ohneWert={uebersicht.jeBewertung.UNGEPRUEFT.ohneWert}
|
||||
farbe="text-muted-foreground"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Card>
|
||||
<CardContent className="space-y-2 pt-5">
|
||||
<div className="flex flex-wrap items-baseline justify-between gap-3">
|
||||
<span className="text-sm text-muted-foreground">
|
||||
Vermögensfreibetrag (§ 90 Abs. 2 Nr. 9 SGB XII)
|
||||
</span>
|
||||
<span className="text-lg font-semibold tabular-nums">
|
||||
{formatCent(vergleich.freibetragCent)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-wrap items-baseline justify-between gap-3">
|
||||
<span className="text-sm text-muted-foreground">Über dem Freibetrag</span>
|
||||
<span
|
||||
className={`text-lg font-semibold tabular-nums ${
|
||||
vergleich.ausgeschoepft ? 'text-ampel-rot' : 'text-ampel-gruen'
|
||||
}`}
|
||||
>
|
||||
{formatCent(vergleich.ueberFreibetragCent)}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground">{vergleich.hinweis}</p>
|
||||
<Rechtshinweis rechtsgrundlage="§ 90 SGB XII" kurz />
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{uebersicht.ungeprueft.length > 0 && (
|
||||
<Alert variant="warnung">
|
||||
<AlertCircle aria-hidden="true" />
|
||||
<AlertTitle>
|
||||
{uebersicht.ungeprueft.length}{' '}
|
||||
{uebersicht.ungeprueft.length === 1 ? 'Posten ist' : 'Posten sind'} noch ungeprüft
|
||||
</AlertTitle>
|
||||
<AlertDescription>
|
||||
Vor dem Antrag sollte hier nichts mehr stehen — der Träger fragt sonst nach, und das
|
||||
kostet Wochen.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{uebersicht.ohneBegruendung.length > 0 && (
|
||||
<Alert variant="warnung">
|
||||
<AlertCircle aria-hidden="true" />
|
||||
<AlertTitle>Bewertung ohne Begründung</AlertTitle>
|
||||
<AlertDescription>
|
||||
{uebersicht.ohneBegruendung.map((p) => p.bezeichnung).join(', ')} — trag nach, worauf
|
||||
sich die Einschätzung stützt. Im Widerspruchsverfahren zählt nur, was begründet ist.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<Vermoegensliste posten={zeilen} darfSchreiben={darfSchreiben} />
|
||||
|
||||
<Rechtshinweis rechtsgrundlage="§ 90 SGB XII, § 91 SGB XII" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function Summenkachel({
|
||||
titel,
|
||||
betrag,
|
||||
anzahl,
|
||||
ohneWert,
|
||||
farbe,
|
||||
}: {
|
||||
titel: string
|
||||
betrag: bigint
|
||||
anzahl: number
|
||||
ohneWert: number
|
||||
farbe: string
|
||||
}) {
|
||||
return (
|
||||
<Card>
|
||||
<CardContent className="pt-5">
|
||||
<p className="text-sm text-muted-foreground">{titel}</p>
|
||||
<p className={`text-xl font-semibold tabular-nums ${farbe}`}>{formatCent(betrag)}</p>
|
||||
<p className="mt-1 text-xs text-muted-foreground">
|
||||
{anzahl} {anzahl === 1 ? 'Posten' : 'Posten'}
|
||||
{ohneWert > 0 && `, davon ${ohneWert} ohne Wert`}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user