Die Anschreiben gab es bisher nur als Text zum Kopieren. Danach war
Handarbeit: in Word nachbauen, ausdrucken, abschicken - und noch einmal
von Hand als Dokument in die Akte legen. Der letzte Schritt wurde am
haeufigsten vergessen, und dann fehlt der Nachweis im Bericht.
Neu unter /schreiben/neu: bearbeiten, als Word-Datei herunterladen, in
die Akte legen, Wiedervorlage setzen - in einem Zug. Eine Seite fuer alle
Arten und alle drei Empfaengerwege, statt die Bearbeitung dreimal an
Vertrag, Vorgang und Freitext nachzubauen.
Der dritte Weg ist der eigentliche Zugewinn: Schreiben an eine Stelle,
zu der es noch keinen Datensatz gibt. Am Anfang einer Betreuung ist das
der Normalfall - man fragt ja an, um zu erfahren, ob dort ueberhaupt
etwas besteht. Aus der freien Anschrift laesst sich mit einem Klick ein
Kontakt anlegen.
Zwei Entscheidungen, die erklaert gehoeren:
Die Akte bekommt ein PDF, heruntergeladen wird .docx. Der
Objektspeicher nimmt nur PDF und Bilder an, geprueft an den Magic Bytes.
Eine .docx ist ein ZIP-Archiv; sie durchzulassen hiesse jedes ZIP
durchzulassen, und diese Pruefung ist eine bewusste Zusage der
Anwendung. Sachlich ist PDF fuer den Nachweis ohnehin richtiger: Es
aendert sich nicht mehr. Die Oberflaeche sagt dazu, dass eine spaeter in
Word geaenderte Fassung von der Akte abweicht.
Der Berichtssatz in berichte/docx.ts bleibt unangetastet. Er setzt einen
Bericht mit Kopfzeilen und Unterschriftenblock; ein Brief braucht
Absender, Anschriftenfeld, Datumszeile und Grussformel. Beides in eine
Funktion zu zwingen haette beide Ausgaben schlechter gemacht. Der
Umsetzungshinweis im Issue ("muss nur angebunden werden") war insoweit
falsch - von mir selbst geschrieben.
Die vorhandenen Erzeuger (Rueckkaufswert, Kuendigung, Sachstandsanfrage)
bleiben unveraendert; ihr Wortlaut ist getestet und erprobt.
Geprueft: zehn Einheitstests, darunter der Gleichschritt der
Wiedervorlagefristen ueber zwei Module hinweg. Fuenf End-to-End-Tests
auf beiden Geraeteprofilen: Entwurf, Vertragsbezug, Word-Download,
Ablage samt Wiedervorlage, Kontakt aus freier Anschrift.
Refs #4
254 lines
9.4 KiB
TypeScript
254 lines
9.4 KiB
TypeScript
'use client'
|
||
|
||
import Link from 'next/link'
|
||
import { useActionState, useState } from 'react'
|
||
import { Coins, FileDown, Mail, Plus } from 'lucide-react'
|
||
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
|
||
import { Button } from '@/components/ui/button'
|
||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||
import { Input } from '@/components/ui/input'
|
||
import { Select } from '@/components/ui/select'
|
||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
|
||
import { Textarea } from '@/components/ui/textarea'
|
||
import { Feld } from '@/components/feld'
|
||
import { Leerzustand } from '@/components/seiten-kopf'
|
||
import { Absendeknopf, Textbaustein, Zustandsmeldung } from '@/components/substanz-formular'
|
||
import { WERTQUELLEN_VERTRAG } from '@/lib/vertraege/konstanten'
|
||
import { rueckkaufswertAnfordern, rueckkaufswertErfassen } from '@/server/actions/vertraege'
|
||
import { formatCent, formatDatum } from '@/lib/utils'
|
||
import type { Formularzustand } from '@/lib/validierung/substanz'
|
||
|
||
export type RueckkaufswertZeile = {
|
||
id: string
|
||
stichtag: string
|
||
wertCent: number
|
||
wertquelle: string
|
||
bemerkung: string | null
|
||
}
|
||
|
||
export function Rueckkaufswert({
|
||
betreuteId,
|
||
vertragId,
|
||
werte,
|
||
angefragtAm,
|
||
erinnerungFaellig,
|
||
tageSeitAnfrage,
|
||
anschreiben,
|
||
stichtagVorschlag,
|
||
darfSchreiben,
|
||
}: {
|
||
betreuteId: string
|
||
vertragId: string
|
||
werte: RueckkaufswertZeile[]
|
||
angefragtAm: string | null
|
||
erinnerungFaellig: boolean
|
||
tageSeitAnfrage: number | null
|
||
anschreiben: string
|
||
stichtagVorschlag: string
|
||
darfSchreiben: boolean
|
||
}) {
|
||
const [erfassen, erfassenAktion] = useActionState<Formularzustand, FormData>(
|
||
rueckkaufswertErfassen,
|
||
{},
|
||
)
|
||
const [anfrage, anfrageAktion] = useActionState<Formularzustand, FormData>(
|
||
rueckkaufswertAnfordern,
|
||
{},
|
||
)
|
||
const [briefOffen, setBriefOffen] = useState(false)
|
||
const [erfassenOffen, setErfassenOffen] = useState(false)
|
||
|
||
return (
|
||
<Card>
|
||
<CardHeader>
|
||
<CardTitle className="flex items-center gap-2">
|
||
<Coins className="size-5" aria-hidden="true" />
|
||
Rückkaufswert
|
||
</CardTitle>
|
||
<CardDescription>
|
||
Der Bezirk verlangt zu jedem Versicherungsvertrag den Rückkaufswert zu einem
|
||
Stichtag — schriftlich von der Gesellschaft, nicht geschätzt. Die Antwort dauert
|
||
erfahrungsgemäß drei bis sechs Wochen.
|
||
</CardDescription>
|
||
</CardHeader>
|
||
|
||
<CardContent className="space-y-5">
|
||
{angefragtAm && (
|
||
<Alert variant={erinnerungFaellig ? 'warnung' : 'hinweis'}>
|
||
<Mail aria-hidden="true" />
|
||
<AlertTitle>
|
||
Angefragt am {formatDatum(angefragtAm)}
|
||
{tageSeitAnfrage !== null && ` — vor ${tageSeitAnfrage} Tagen`}
|
||
</AlertTitle>
|
||
<AlertDescription>
|
||
{erinnerungFaellig
|
||
? 'Die vier Wochen sind um. Ruf an und halte das Gespräch im Kontaktprotokoll fest — schriftlich nachfassen dauert wieder vier Wochen.'
|
||
: 'Die Wiedervorlage steht in den Fristen. Kommt die Antwort früher, hak die Frist dort ab.'}
|
||
</AlertDescription>
|
||
</Alert>
|
||
)}
|
||
|
||
{werte.length === 0 ? (
|
||
<Leerzustand
|
||
titel="Noch kein Rückkaufswert erfasst"
|
||
beschreibung="Fordere ihn bei der Gesellschaft an. Frag im selben Schreiben nach der beitragsfreien Versicherungssumme — sonst musst du für den Vergleich ein zweites Mal schreiben."
|
||
icon={Coins}
|
||
aktion={
|
||
darfSchreiben && (
|
||
<Button type="button" onClick={() => setBriefOffen(true)}>
|
||
<Mail aria-hidden="true" />
|
||
Anschreiben erzeugen
|
||
</Button>
|
||
)
|
||
}
|
||
/>
|
||
) : (
|
||
<Table>
|
||
<TableHeader>
|
||
<TableRow>
|
||
<TableHead>Stichtag</TableHead>
|
||
<TableHead className="text-right">Rückkaufswert</TableHead>
|
||
<TableHead>Quelle</TableHead>
|
||
<TableHead>Bemerkung</TableHead>
|
||
</TableRow>
|
||
</TableHeader>
|
||
<TableBody>
|
||
{werte.map((zeile) => (
|
||
<TableRow key={zeile.id}>
|
||
<TableCell className="whitespace-nowrap">{formatDatum(zeile.stichtag)}</TableCell>
|
||
<TableCell className="text-right font-medium tabular-nums">
|
||
{formatCent(zeile.wertCent)}
|
||
</TableCell>
|
||
<TableCell>{zeile.wertquelle}</TableCell>
|
||
<TableCell className="text-sm text-muted-foreground">
|
||
{zeile.bemerkung ?? '–'}
|
||
</TableCell>
|
||
</TableRow>
|
||
))}
|
||
</TableBody>
|
||
</Table>
|
||
)}
|
||
|
||
{darfSchreiben && (
|
||
<div className="flex flex-wrap gap-2">
|
||
<Button type="button" variant="outline" onClick={() => setBriefOffen((o) => !o)}>
|
||
<Mail aria-hidden="true" />
|
||
{briefOffen ? 'Anschreiben schließen' : 'Rückkaufswert anfordern'}
|
||
</Button>
|
||
<Button type="button" variant="outline" onClick={() => setErfassenOffen((o) => !o)}>
|
||
<Plus aria-hidden="true" />
|
||
{erfassenOffen ? 'Eingabe schließen' : 'Mitgeteilten Wert eintragen'}
|
||
</Button>
|
||
</div>
|
||
)}
|
||
|
||
{darfSchreiben && briefOffen && (
|
||
<div className="space-y-4 rounded-md border p-4">
|
||
<Textbaustein
|
||
text={anschreiben}
|
||
beschriftung="Anschreiben an die Gesellschaft"
|
||
hoehe="min-h-[32rem]"
|
||
/>
|
||
|
||
{/* Der Weg, der nicht beim Kopieren endet: bearbeiten,
|
||
Word-Datei, Ablage in der Akte, Wiedervorlage. */}
|
||
<Button asChild variant="outline">
|
||
<Link href={`/schreiben/neu?art=RUECKKAUFSWERT&vertragId=${vertragId}`}>
|
||
<FileDown aria-hidden="true" />
|
||
Bearbeiten, als Word-Datei und in die Akte
|
||
</Link>
|
||
</Button>
|
||
|
||
<form action={anfrageAktion} className="space-y-4" noValidate>
|
||
<input type="hidden" name="betreuteId" value={betreuteId} />
|
||
<input type="hidden" name="vertragId" value={vertragId} />
|
||
|
||
<Zustandsmeldung zustand={anfrage} />
|
||
|
||
<div className="grid gap-4 sm:grid-cols-2">
|
||
<Feld
|
||
id="anfrage-stichtag"
|
||
beschriftung="Stichtag der Anfrage"
|
||
erklaerung="Der Tag, zu dem der Wert mitgeteilt werden soll. Für die Sozialhilfe meist der Tag der Antragstellung."
|
||
fehler={anfrage.felder?.stichtag}
|
||
pflicht
|
||
>
|
||
<Input
|
||
name="stichtag"
|
||
type="date"
|
||
defaultValue={stichtagVorschlag}
|
||
required
|
||
/>
|
||
</Feld>
|
||
</div>
|
||
|
||
<Absendeknopf>Anfrage vermerken und Wiedervorlage anlegen</Absendeknopf>
|
||
<p className="text-sm text-muted-foreground">
|
||
Setzt das Datum der Anfrage am Vertrag und legt eine Wiedervorlage in vier
|
||
Wochen an. Den Brief selbst schickst du von Hand ab — die App verschickt nichts.
|
||
</p>
|
||
</form>
|
||
</div>
|
||
)}
|
||
|
||
{darfSchreiben && erfassenOffen && (
|
||
<form action={erfassenAktion} className="space-y-4 rounded-md border p-4" noValidate>
|
||
<input type="hidden" name="betreuteId" value={betreuteId} />
|
||
<input type="hidden" name="vertragId" value={vertragId} />
|
||
|
||
<Zustandsmeldung zustand={erfassen} />
|
||
|
||
<div className="grid gap-4 sm:grid-cols-2">
|
||
<Feld
|
||
id="rkw-stichtag"
|
||
beschriftung="Stichtag"
|
||
fehler={erfassen.felder?.stichtag}
|
||
pflicht
|
||
>
|
||
<Input name="stichtag" type="date" required />
|
||
</Feld>
|
||
|
||
<Feld
|
||
id="rkw-wert"
|
||
beschriftung="Rückkaufswert"
|
||
fehler={erfassen.felder?.wert}
|
||
pflicht
|
||
>
|
||
<Input name="wert" inputMode="decimal" placeholder="4.812,55" required />
|
||
</Feld>
|
||
|
||
<Feld
|
||
id="rkw-quelle"
|
||
beschriftung="Quelle"
|
||
erklaerung="Woher stammt die Zahl? Eine telefonische Auskunft ist besser als nichts, aber kein Nachweis."
|
||
fehler={erfassen.felder?.wertquelle}
|
||
pflicht
|
||
className="sm:col-span-2"
|
||
>
|
||
<Select name="wertquelle" defaultValue={WERTQUELLEN_VERTRAG[0]}>
|
||
{WERTQUELLEN_VERTRAG.map((q) => (
|
||
<option key={q} value={q}>
|
||
{q}
|
||
</option>
|
||
))}
|
||
</Select>
|
||
</Feld>
|
||
</div>
|
||
|
||
<Feld
|
||
id="rkw-bemerkung"
|
||
beschriftung="Bemerkung"
|
||
erklaerung="z. B. die beitragsfreie Versicherungssumme, wenn sie mitgeteilt wurde."
|
||
fehler={erfassen.felder?.bemerkung}
|
||
>
|
||
<Textarea name="bemerkung" rows={2} maxLength={1000} />
|
||
</Feld>
|
||
|
||
<Absendeknopf>Wert aufnehmen</Absendeknopf>
|
||
</form>
|
||
)}
|
||
</CardContent>
|
||
</Card>
|
||
)
|
||
}
|