import Link from 'next/link' import { ArrowRight, Banknote, FileDown, Landmark, Plus, Repeat, Upload } from 'lucide-react' import { Alert, AlertDescription } from '@/components/ui/alert' 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 { Belegzeichen, Betrag, Hinweisliste, Kennzahl, Vermischungswarnung, } from '@/components/finanzen/bausteine' import { centZuZahl } from '@/lib/db' import { formatCent, formatDatum } from '@/lib/utils' import { finanzKontext } from '@/lib/finanzen/seite' import { heute, monatePlus, tagePlus } from '@/lib/finanzen/datum' import { belegDurchVertrag, betragPruefen, kennzahlen, vermischungsWarnung } from '@/lib/finanzen/kassenbuch' import { KATEGORIE_BESCHRIFTUNG } from '@/lib/finanzen/kategorien' import { laufendesBetreuungsjahr } from '@/lib/finanzen/betreuungsjahr' import { buchungenLaden, kontenLaden, kontostaendeZumStichtag, vermoegenLaden } from '@/lib/berichte/gemeinsam' import { offeneZahlungsgruppen } from '@/lib/finanzen/wiederkehrend-laden' export const metadata = { title: 'Vermögen & Kasse' } /** * Übersichtsseite des Finanzmoduls. * * Sie beantwortet die vier Fragen, die man sich beim Öffnen stellt: * Was ist da? Wo liegt es? Was ist zuletzt passiert? Und: Wo fehlt noch * ein Beleg? */ export default async function FinanzenSeite() { const { betreute, darfSchreiben } = await finanzKontext() const schreiben = darfSchreiben('FINANZEN') const stichtag = heute() const [konten, buchungen, vermoegen] = await Promise.all([ kontenLaden(betreute.id), buchungenLaden(betreute.id), vermoegenLaden(betreute.id, stichtag), ]) const eigeneKonten = konten.filter((k) => !k.istBetreuerkonto) const staende = kontostaendeZumStichtag(eigeneKonten, stichtag) const summeKontenCent = staende.reduce((s, z) => s + (z.betragCent ?? 0n), 0n) const zwoelfMonate = tagePlus(monatePlus(stichtag, -12), 1) const letzteZwoelf = buchungen.filter((b) => b.datum >= zwoelfMonate) const zahlen = kennzahlen(letzteZwoelf) const offeneGruppen = await offeneZahlungsgruppen(betreute.id) const letzte = [...buchungen].sort((a, b) => b.datum.getTime() - a.datum.getTime()).slice(0, 8) const jahr = betreute.bestellungAm ? laufendesBetreuungsjahr(betreute.bestellungAm) : null const hinweise: string[] = [] for (const stand of staende) { if (stand.betragCent === null) { hinweise.push(`Für „${stand.bezeichnung}“ ist noch kein Kontostand erfasst.`) } else if (stand.stichtag && stand.stichtag < monatePlus(stichtag, -3)) { hinweise.push( `Der Stand von „${stand.bezeichnung}“ ist vom ${formatDatum(stand.stichtag)} und damit ` + 'älter als drei Monate.', ) } } if (!betreute.bestellungAm) { hinweise.push( 'In den Stammdaten fehlt das Datum der Bestellung. Ohne dieses Datum lässt sich das ' + 'Betreuungsjahr nicht bestimmen – und damit auch keine Rechnungslegung erzeugen.', ) } return (
) : undefined } />
0 ? `${zahlen.beleg.zuPruefen} Zahlung(en) weichen vom hinterlegten Beitrag ab` : zahlen.beleg.ohneNachweis > 0 ? `${zahlen.beleg.ohneNachweis} Buchung(en) ohne Beleg und ohne Begründung` : zahlen.beleg.durchVertrag > 0 ? `Belegt, begründet oder durch Vertrag gedeckt (${zahlen.beleg.durchVertrag} Dauerzahlungen)` : 'Alle Buchungen sind belegt oder begründet' } ton={zahlen.beleg.ohneNachweis > 0 ? 'achtung' : 'gut'} href="/finanzen/buchungen?nurOhneBeleg=on" />
{offeneGruppen > 0 && ( )} {zahlen.vermischungen > 0 && } {/* ------------------------------------------------------------- */} Konten Stand zum {formatDatum(stichtag)} – jeweils der jüngste erfasste Saldostand. {konten.length === 0 ? ( {/* ------------------------------------------------------------- */}
Zuletzt gebucht Die acht jüngsten Einträge im Kassenbuch.
{schreiben && ( )}
{letzte.length === 0 ? (
) : undefined } /> ) : ( Datum Vorgang Beleg Betrag {letzte.map((buchung) => ( {formatDatum(buchung.datum)} {buchung.gegenstelle ?? KATEGORIE_BESCHRIFTUNG[buchung.kategorie]} {KATEGORIE_BESCHRIFTUNG[buchung.kategorie]} {!buchung.betrifftBetreute && ' · eigene Auslage'} {buchung.vermoegenswirksam && ' · vermögenswirksam'} {vermischungsWarnung(buchung) && ( )} ))}
)} ) }