feat: Konten, Kassenbuch, Vermoegen und die beiden Pflicht-PDFs
Etappe 4. Aus diesen Daten entstehen das Vermoegensverzeichnis nach § 1835 BGB und die Rechnungslegung nach §§ 1859, 1865 BGB - beides geht ans Betreuungsgericht, beides ohne dass eine Zahl abgetippt wird. Betreuungsjahr n laeuft von bestellungAm + (n-1) Jahre bis bestellungAm + n Jahre - 1 Tag. Bei Bestellung am 29. Februar endet Jahr 1 am 27.02. und Jahr 2 beginnt am 28.02.: lueckenlos und ohne Ueberschneidung, per Test abgesichert. Rechenprobe der Rechnungslegung: Der Anfangsbestand ist der juengste Saldostand bis zum VORTAG des Zeitraumbeginns - ein Saldostand gilt als Bestand am Tagesende, sonst zaehlte der erste Tag doppelt. Geht die Probe nicht auf, wird die Differenz im PDF ausgewiesen und jeder fehlende Saldostand namentlich benannt. Nie geglaettet: eine still korrigierte Rechnungslegung ist gegenueber dem Gericht wertlos. Posten ohne Wertstand zum Stichtag erscheinen als Luecke, nicht als 0,00 EUR - und Werte aus der Zukunft werden nie herangezogen. Konten kommen ueber ihren Saldostand ins Verzeichnis; Vermoegensposten der Art KONTO werden dort uebersprungen, sonst stuende das Geld doppelt drin. Der CSV- und CAMT-Import raet KEINE Kategorien, sondern bucht alles auf "Sonstige". Eine falsch geratene Kategorie faellt in der Jahresuebersicht niemandem auf und verfaelscht den Bericht. Fremdwaehrung wird abgelehnt statt umgerechnet. Der XML-Leser weist DOCTYPE ab (XXE). 68 Tests, darunter echte IBAN-Pruefziffern und die Rundung: es darf nirgends ein Cent verschwinden. Etappe: 4 Phase: Geld
This commit is contained in:
@@ -0,0 +1,214 @@
|
||||
import { notFound } from 'next/navigation'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { SeitenKopf } from '@/components/seiten-kopf'
|
||||
import { Rechtshinweis } from '@/components/rechtshinweis'
|
||||
import { Loeschknopf } from '@/components/finanzen/aktionen'
|
||||
import { Postenformular, Wertstandformular } from '@/components/finanzen/posten-formulare'
|
||||
import { Wertverlauf, type Verlaufszeile } from '@/components/finanzen/wertverlauf'
|
||||
import { centZuZahl, db, nichtGeloescht } from '@/lib/db'
|
||||
import { oeffnen } from '@/lib/crypto/feld'
|
||||
import { formatDatum } from '@/lib/utils'
|
||||
import { finanzKontext } from '@/lib/finanzen/seite'
|
||||
import { heute, tagAlsText } from '@/lib/finanzen/datum'
|
||||
import { wertverlauf } from '@/lib/finanzen/vermoegen'
|
||||
import {
|
||||
SOZIALHILFE_BEWERTUNG_BESCHRIFTUNG,
|
||||
VERMOEGENS_ART_EINZAHL,
|
||||
} from '@/lib/finanzen/kategorien'
|
||||
import { vermoegenspostenLoeschen, wertstandLoeschen } from '@/server/actions/finanzen'
|
||||
|
||||
export const metadata = { title: 'Vermögensposten' }
|
||||
|
||||
/**
|
||||
* Ein Vermögensposten mit seiner vollständigen Wert-Historie.
|
||||
*
|
||||
* Werte werden hier nur hinzugefügt, nie geändert. Das ist der Kern der
|
||||
* Nachvollziehbarkeit: Wenn das Gericht in zwei Jahren fragt, warum im
|
||||
* Verzeichnis 195.000 € standen, muss dieselbe Zahl mit derselben Quelle
|
||||
* wieder herauskommen.
|
||||
*/
|
||||
export default async function VermoegenspostenSeite({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ id: string }>
|
||||
}) {
|
||||
const { id } = await params
|
||||
const { betreute, darfSchreiben } = await finanzKontext()
|
||||
const schreiben = darfSchreiben('FINANZEN')
|
||||
|
||||
const posten = await db.vermoegensposten.findFirst({
|
||||
where: { id, betreuteId: betreute.id, ...nichtGeloescht },
|
||||
include: {
|
||||
wertstaende: {
|
||||
orderBy: { stichtag: 'asc' },
|
||||
include: { nachweisDokument: { select: { id: true, titel: true } } },
|
||||
},
|
||||
},
|
||||
})
|
||||
if (!posten) notFound()
|
||||
|
||||
const offen = oeffnen('Vermoegensposten', posten)
|
||||
|
||||
const [konten, dokumente] = await Promise.all([
|
||||
db.konto.findMany({
|
||||
where: { betreuteId: betreute.id, ...nichtGeloescht },
|
||||
select: { id: true, bezeichnung: true },
|
||||
orderBy: { bezeichnung: 'asc' },
|
||||
}),
|
||||
db.dokument.findMany({
|
||||
where: { betreuteId: betreute.id, ...nichtGeloescht },
|
||||
select: { id: true, titel: true },
|
||||
orderBy: { erstelltAm: 'desc' },
|
||||
take: 200,
|
||||
}),
|
||||
])
|
||||
|
||||
const verlauf = wertverlauf(
|
||||
posten.wertstaende.map((wert) => ({
|
||||
stichtag: wert.stichtag,
|
||||
wertCent: wert.wertCent,
|
||||
wertquelle: wert.wertquelle,
|
||||
nachweisDokumentId: wert.nachweisDokumentId,
|
||||
nachweisTitel: wert.nachweisDokument?.titel ?? null,
|
||||
bemerkung: wert.bemerkung,
|
||||
})),
|
||||
)
|
||||
|
||||
// wertverlauf() sortiert aufsteigend nach Stichtag - genau wie die
|
||||
// Abfrage oben. Die Indizes passen deshalb zueinander.
|
||||
const zeilen: Verlaufszeile[] = verlauf.map((punkt, i) => {
|
||||
const roh = posten.wertstaende[i]!
|
||||
return {
|
||||
id: roh.id,
|
||||
stichtag: tagAlsText(punkt.stichtag),
|
||||
cent: centZuZahl(punkt.wertCent) ?? 0,
|
||||
wertquelle: punkt.wertquelle,
|
||||
nachweisTitel: roh.nachweisDokument?.titel ?? null,
|
||||
bemerkung: roh.bemerkung,
|
||||
veraenderungCent: centZuZahl(punkt.veraenderungCent),
|
||||
aktion: schreiben ? (
|
||||
<Loeschknopf
|
||||
aktion={wertstandLoeschen}
|
||||
felder={{ betreuteId: betreute.id, wertstandId: roh.id }}
|
||||
frage={`Wert vom ${formatDatum(roh.stichtag)} endgültig entfernen? Er wandert nicht in den Papierkorb – eine damit erstellte Vermögensübersicht lässt sich danach nicht mehr nachbilden.`}
|
||||
/>
|
||||
) : undefined,
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<SeitenKopf
|
||||
titel={posten.bezeichnung}
|
||||
beschreibung={
|
||||
<span className="flex flex-wrap items-center gap-2">
|
||||
<Badge variant="outline">{VERMOEGENS_ART_EINZAHL[posten.art]}</Badge>
|
||||
<Badge variant={posten.bewertungSozialhilfe === 'GESCHUETZT' ? 'gruen' : 'secondary'}>
|
||||
Sozialhilfe: {SOZIALHILFE_BEWERTUNG_BESCHRIFTUNG[posten.bewertungSozialhilfe]}
|
||||
</Badge>
|
||||
<span className="text-sm">
|
||||
{posten.wertstaende.length} Wertstand/Wertstände erfasst
|
||||
</span>
|
||||
</span>
|
||||
}
|
||||
pfad={[
|
||||
{ titel: 'Vermögen & Kasse', href: '/finanzen' },
|
||||
{ titel: 'Vermögen', href: '/finanzen/vermoegen' },
|
||||
{ titel: posten.bezeichnung },
|
||||
]}
|
||||
/>
|
||||
|
||||
{offen.bewertungBegruendung && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Einordnung im Sozialhilfeverfahren</CardTitle>
|
||||
<CardDescription>
|
||||
Deine eigene Dokumentation und Argumentation gegenüber dem Bezirk – keine
|
||||
Rechtsauskunft.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="whitespace-pre-line text-sm">
|
||||
{offen.bewertungBegruendung}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Wertverlauf</CardTitle>
|
||||
<CardDescription>
|
||||
Jeder Eintrag ist eine Aussage über den Wert an einem bestimmten Tag, mit Angabe der
|
||||
Quelle. Nichts wird überschrieben.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-6">
|
||||
{zeilen.length === 0 ? (
|
||||
<p className="rounded-md border border-dashed p-4 text-sm text-muted-foreground">
|
||||
Noch kein Wert erfasst. Solange kein Wertstand da ist, taucht dieser Posten im
|
||||
Vermögensverzeichnis als Lücke auf – nicht mit 0,00 €.
|
||||
</p>
|
||||
) : (
|
||||
<Wertverlauf zeilen={zeilen} />
|
||||
)}
|
||||
|
||||
{schreiben && (
|
||||
<div className="border-t pt-6">
|
||||
<h3 className="mb-3 text-base font-semibold">Neuen Wert festhalten</h3>
|
||||
<Wertstandformular
|
||||
betreuteId={betreute.id}
|
||||
vermoegenspostenId={posten.id}
|
||||
dokumente={dokumente}
|
||||
standardStichtag={tagAlsText(heute())}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{offen.notizen && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Notizen</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="whitespace-pre-line text-sm">{offen.notizen}</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{schreiben && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Posten ändern</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-8">
|
||||
<Postenformular
|
||||
betreuteId={betreute.id}
|
||||
konten={konten}
|
||||
posten={{
|
||||
id: posten.id,
|
||||
bezeichnung: posten.bezeichnung,
|
||||
art: posten.art,
|
||||
kontoId: posten.kontoId,
|
||||
bewertungSozialhilfe: posten.bewertungSozialhilfe,
|
||||
bewertungBegruendung: offen.bewertungBegruendung,
|
||||
notizen: offen.notizen,
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className="border-t pt-6">
|
||||
<Loeschknopf
|
||||
aktion={vermoegenspostenLoeschen}
|
||||
felder={{ betreuteId: betreute.id, postenId: posten.id }}
|
||||
beschriftung="Posten in den Papierkorb"
|
||||
variante="outline"
|
||||
frage="Posten in den Papierkorb legen? Er bleibt 30 Tage wiederherstellbar; die Wertstände wandern mit."
|
||||
/>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
<Rechtshinweis rechtsgrundlage="§ 1835 BGB, § 90 SGB XII" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import { notFound } from 'next/navigation'
|
||||
import { SeitenKopf } from '@/components/seiten-kopf'
|
||||
import { Rechtshinweis } from '@/components/rechtshinweis'
|
||||
import { Postenformular } from '@/components/finanzen/posten-formulare'
|
||||
import { db, nichtGeloescht } from '@/lib/db'
|
||||
import { finanzKontext } from '@/lib/finanzen/seite'
|
||||
|
||||
export const metadata = { title: 'Vermögensposten anlegen' }
|
||||
|
||||
export default async function VermoegenNeuSeite() {
|
||||
const { betreute, darfSchreiben } = await finanzKontext()
|
||||
if (!darfSchreiben('FINANZEN')) notFound()
|
||||
|
||||
const konten = await db.konto.findMany({
|
||||
where: { betreuteId: betreute.id, ...nichtGeloescht },
|
||||
select: { id: true, bezeichnung: true },
|
||||
orderBy: { bezeichnung: 'asc' },
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="max-w-3xl space-y-6">
|
||||
<SeitenKopf
|
||||
titel="Vermögensposten anlegen"
|
||||
beschreibung="Erst die Sache benennen, dann bewerten. Den Wert trägst du gleich danach als Wertstand mit Stichtag und Quelle ein – so bleibt jede Vermögensübersicht später nachvollziehbar."
|
||||
pfad={[
|
||||
{ titel: 'Vermögen & Kasse', href: '/finanzen' },
|
||||
{ titel: 'Vermögen', href: '/finanzen/vermoegen' },
|
||||
{ titel: 'Neu' },
|
||||
]}
|
||||
/>
|
||||
|
||||
<Postenformular betreuteId={betreute.id} konten={konten} />
|
||||
|
||||
<Rechtshinweis rechtsgrundlage="§ 1835 BGB, § 90 SGB XII" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
import Link from 'next/link'
|
||||
import { Boxes, Plus } from 'lucide-react'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
|
||||
import { SeitenKopf, Leerzustand } from '@/components/seiten-kopf'
|
||||
import { Rechtshinweis } from '@/components/rechtshinweis'
|
||||
import { Betrag, Hinweisliste, Kennzahl, Summenzeile } from '@/components/finanzen/bausteine'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { centZuZahl } from '@/lib/db'
|
||||
import { formatCent, formatDatum } from '@/lib/utils'
|
||||
import { finanzKontext } from '@/lib/finanzen/seite'
|
||||
import { heute, tagAlsText, tagAusText } from '@/lib/finanzen/datum'
|
||||
import {
|
||||
SOZIALHILFE_BEWERTUNG_BESCHRIFTUNG,
|
||||
VERMOEGENS_ART_BESCHRIFTUNG,
|
||||
VERMOEGENS_ART_EINZAHL,
|
||||
} from '@/lib/finanzen/kategorien'
|
||||
import { vermoegenLaden } from '@/lib/berichte/gemeinsam'
|
||||
import type { SozialhilfeBewertung } from '@prisma/client'
|
||||
|
||||
export const metadata = { title: 'Vermögen' }
|
||||
|
||||
/**
|
||||
* Vermögensposten mit ihrem Wert an einem frei wählbaren Stichtag.
|
||||
*
|
||||
* Der Stichtag ist hier kein Beiwerk: Genau so entsteht später das
|
||||
* Vermögensverzeichnis. Wer den Stichtag zurücksetzt, sieht das
|
||||
* Vermögen so, wie es damals im Verzeichnis stand – und nicht so, wie
|
||||
* man es heute gerne hätte.
|
||||
*/
|
||||
export default async function VermoegenSeite({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: Promise<Record<string, string | string[] | undefined>>
|
||||
}) {
|
||||
const parameter = await searchParams
|
||||
const { betreute, darfSchreiben } = await finanzKontext()
|
||||
const schreiben = darfSchreiben('FINANZEN')
|
||||
|
||||
const gewaehlt =
|
||||
typeof parameter.stichtag === 'string' ? tagAusText(parameter.stichtag) : null
|
||||
const stichtag = gewaehlt ?? heute()
|
||||
|
||||
const bild = await vermoegenLaden(betreute.id, stichtag)
|
||||
|
||||
const hinweise = bild.ohneWert.map(
|
||||
(posten) =>
|
||||
`„${posten.bezeichnung}“ hat zum ${formatDatum(stichtag)} keinen Wert. ` +
|
||||
(posten.spaetereWertstaende > 0
|
||||
? 'Es gibt nur Wertstände nach diesem Stichtag – die dürfen hier nicht verwendet werden.'
|
||||
: 'Es ist noch kein Wertstand erfasst.'),
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<SeitenKopf
|
||||
titel="Vermögen"
|
||||
beschreibung="Grundbesitz, Versicherungen, Fahrzeuge, Forderungen und Verbindlichkeiten. Jeder Posten wird über Wertstände bewertet – nie über ein einzelnes Feld, das überschrieben wird."
|
||||
pfad={[{ titel: 'Vermögen & Kasse', href: '/finanzen' }, { titel: 'Vermögen' }]}
|
||||
aktionen={
|
||||
schreiben ? (
|
||||
<Button asChild>
|
||||
<Link href="/finanzen/vermoegen/neu">
|
||||
<Plus aria-hidden="true" />
|
||||
Posten anlegen
|
||||
</Link>
|
||||
</Button>
|
||||
) : undefined
|
||||
}
|
||||
/>
|
||||
|
||||
<form method="get" className="flex flex-wrap items-end gap-3 rounded-lg border p-4">
|
||||
<div className="min-w-[14rem]">
|
||||
<label htmlFor="stichtag" className="mb-1.5 block text-sm font-medium">
|
||||
Vermögen zum Stichtag
|
||||
</label>
|
||||
<Input id="stichtag" type="date" name="stichtag" defaultValue={tagAlsText(stichtag)} />
|
||||
</div>
|
||||
<Button type="submit" variant="secondary">
|
||||
Anzeigen
|
||||
</Button>
|
||||
<p className="w-full text-sm text-muted-foreground">
|
||||
Es wird je Posten der jüngste Wertstand verwendet, der nicht nach dem Stichtag liegt.
|
||||
Spätere Bewertungen bleiben außen vor – auch dann, wenn sie genauer wären.
|
||||
</p>
|
||||
</form>
|
||||
|
||||
<div className="grid gap-4 sm:grid-cols-3">
|
||||
<Kennzahl titel="Aktiva" wert={formatCent(centZuZahl(bild.summeAktivaCent))} />
|
||||
<Kennzahl titel="Verbindlichkeiten" wert={formatCent(centZuZahl(bild.summePassivaCent))} />
|
||||
<Kennzahl
|
||||
titel="Reinvermögen"
|
||||
wert={formatCent(centZuZahl(bild.reinvermoegenCent))}
|
||||
hinweis="ohne Kontoguthaben – die stehen unter „Konten“"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Hinweisliste hinweise={hinweise} titel="Diese Posten fehlen im Verzeichnis" />
|
||||
|
||||
{bild.aktivaGruppen.length === 0 && bild.passiva.length === 0 && bild.ohneWert.length === 0 ? (
|
||||
<Leerzustand
|
||||
icon={Boxes}
|
||||
titel="Noch kein Vermögensposten erfasst"
|
||||
beschreibung="Das Hausgrundstück und die Versicherungen gehören hierher – sie machen im Vermögensverzeichnis den größten Teil aus."
|
||||
aktion={
|
||||
schreiben ? (
|
||||
<Button asChild>
|
||||
<Link href="/finanzen/vermoegen/neu">
|
||||
<Plus aria-hidden="true" />
|
||||
Posten anlegen
|
||||
</Link>
|
||||
</Button>
|
||||
) : undefined
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<div className="space-y-6">
|
||||
{bild.aktivaGruppen.map((gruppe) => (
|
||||
<Card key={gruppe.art}>
|
||||
<CardHeader>
|
||||
<CardTitle>{VERMOEGENS_ART_BESCHRIFTUNG[gruppe.art]}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Postentabelle posten={gruppe.posten} />
|
||||
<Summenzeile
|
||||
beschriftung={`Summe ${VERMOEGENS_ART_BESCHRIFTUNG[gruppe.art]}`}
|
||||
cent={centZuZahl(gruppe.summeCent) ?? 0}
|
||||
betont
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
|
||||
{bild.passiva.length > 0 && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Verbindlichkeiten</CardTitle>
|
||||
<CardDescription>
|
||||
Werden mit positivem Betrag geführt; dass sie das Vermögen mindern, ergibt sich
|
||||
aus der Art des Postens.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Postentabelle posten={bild.passiva} />
|
||||
<Summenzeile
|
||||
beschriftung="Summe Verbindlichkeiten"
|
||||
cent={centZuZahl(bild.summePassivaCent) ?? 0}
|
||||
betont
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{bild.ohneWert.length > 0 && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Ohne Bewertung zum Stichtag</CardTitle>
|
||||
<CardDescription>
|
||||
Diese Posten sind vorhanden, gehen aber in keine Summe ein.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ul className="space-y-2">
|
||||
{bild.ohneWert.map((posten) => (
|
||||
<li key={posten.id} className="flex flex-wrap items-center gap-2">
|
||||
<Link
|
||||
href={`/finanzen/vermoegen/${posten.id}`}
|
||||
className="font-medium hover:underline"
|
||||
>
|
||||
{posten.bezeichnung}
|
||||
</Link>
|
||||
<Badge variant="gelb">{VERMOEGENS_ART_EINZAHL[posten.art]}</Badge>
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{posten.spaetereWertstaende > 0
|
||||
? `${posten.spaetereWertstaende} Wertstand/Wertstände, alle nach dem Stichtag`
|
||||
: 'noch kein Wertstand'}
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Rechtshinweis rechtsgrundlage="§ 1835 BGB, § 90 SGB XII" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function Postentabelle({
|
||||
posten,
|
||||
}: {
|
||||
posten: {
|
||||
id: string
|
||||
bezeichnung: string
|
||||
wertCent: bigint
|
||||
bewertetAm: Date
|
||||
wertquelle: string
|
||||
alterTage: number
|
||||
nachweisTitel: string | null
|
||||
bewertungSozialhilfe: string | null
|
||||
}[]
|
||||
}) {
|
||||
return (
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Posten</TableHead>
|
||||
<TableHead>Wertquelle</TableHead>
|
||||
<TableHead>Bewertet am</TableHead>
|
||||
<TableHead>Sozialhilfe</TableHead>
|
||||
<TableHead className="text-right">Wert</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{posten.map((eintrag) => (
|
||||
<TableRow key={eintrag.id}>
|
||||
<TableCell>
|
||||
<Link
|
||||
href={`/finanzen/vermoegen/${eintrag.id}`}
|
||||
className="font-medium hover:underline"
|
||||
>
|
||||
{eintrag.bezeichnung}
|
||||
</Link>
|
||||
</TableCell>
|
||||
<TableCell className="text-sm text-muted-foreground">
|
||||
<span className="block">{eintrag.wertquelle}</span>
|
||||
{eintrag.nachweisTitel && (
|
||||
<span className="block">Beleg: {eintrag.nachweisTitel}</span>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className="whitespace-nowrap text-sm text-muted-foreground">
|
||||
{formatDatum(eintrag.bewertetAm)}
|
||||
{eintrag.alterTage > 365 && (
|
||||
<Badge variant="gelb" className="ml-2">
|
||||
über ein Jahr alt
|
||||
</Badge>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className="text-sm text-muted-foreground">
|
||||
{eintrag.bewertungSozialhilfe
|
||||
? SOZIALHILFE_BEWERTUNG_BESCHRIFTUNG[
|
||||
eintrag.bewertungSozialhilfe as SozialhilfeBewertung
|
||||
]
|
||||
: '–'}
|
||||
</TableCell>
|
||||
<TableCell className="text-right">
|
||||
<Betrag cent={centZuZahl(eintrag.wertCent)} fett />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user