Schreiben erzeugen, ausgeben und ablegen
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
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
import type { Metadata } from 'next'
|
||||
import { redirect } from 'next/navigation'
|
||||
import { Alert, AlertDescription } from '@/components/ui/alert'
|
||||
import { AlertCircle, Scale } from 'lucide-react'
|
||||
import { SeitenKopf } from '@/components/seiten-kopf'
|
||||
import { SchreibenEditor } from '@/components/schreiben/schreiben-editor'
|
||||
import { aktiveBetreute, erfordereBenutzer, erfordereZugang } from '@/lib/auth/sitzung'
|
||||
import { db, nichtGeloescht } from '@/lib/db'
|
||||
import { entwurfBauen } from '@/lib/schreiben/entwurf'
|
||||
import { SCHREIBEN_EIGENSCHAFTEN, istSchreibenArt } from '@/lib/schreiben/arten'
|
||||
|
||||
export const metadata: Metadata = { title: 'Schreiben' }
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
/**
|
||||
* Ein Schreiben erzeugen, bearbeiten, ausgeben und ablegen.
|
||||
*
|
||||
* Eine Seite für alle Arten und alle drei Empfängerwege — statt die
|
||||
* Bearbeitung dreimal an Vertrag, Vorgang und Freitext nachzubauen. Die
|
||||
* bestehenden Seiten verlinken hierher.
|
||||
*
|
||||
* Der Fall ohne Bezug ist ausdrücklich vorgesehen: Am Anfang einer
|
||||
* Betreuung schreibt man an Stellen, zu denen es noch keinen Datensatz
|
||||
* gibt — man fragt ja gerade an, um zu erfahren, ob dort etwas besteht.
|
||||
*/
|
||||
export default async function SchreibenSeite({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: Promise<Record<string, string | string[] | undefined>>
|
||||
}) {
|
||||
const parameter = await searchParams
|
||||
const benutzer = await erfordereBenutzer('/schreiben/neu')
|
||||
const aktive = await aktiveBetreute(benutzer)
|
||||
if (!aktive) redirect('/ersteinrichtung')
|
||||
|
||||
const { darfSchreiben } = await erfordereZugang(aktive.id, 'DOKUMENTE', 'lesen')
|
||||
if (!darfSchreiben('DOKUMENTE')) redirect('/dokumente')
|
||||
|
||||
const text = (name: string): string => {
|
||||
const wert = parameter[name]
|
||||
return typeof wert === 'string' ? wert.trim() : ''
|
||||
}
|
||||
|
||||
const rohArt = text('art')
|
||||
const art = istSchreibenArt(rohArt) ? rohArt : 'FREI'
|
||||
const eigenschaften = SCHREIBEN_EIGENSCHAFTEN[art]
|
||||
|
||||
let entwurf
|
||||
try {
|
||||
entwurf = await entwurfBauen({
|
||||
betreuteId: aktive.id,
|
||||
benutzerName: benutzer.name,
|
||||
art,
|
||||
vertragId: text('vertragId') || null,
|
||||
vorgangId: text('vorgangId') || null,
|
||||
})
|
||||
} catch (ausnahme) {
|
||||
// Fehlender oder fremder Bezug: Das ist kein Absturz, sondern eine
|
||||
// Angabe, die nicht stimmt. Die Seite sagt es und bietet den freien
|
||||
// Weg an, statt den Benutzer vor eine Fehlerseite zu setzen.
|
||||
const meldung =
|
||||
ausnahme instanceof Error ? ausnahme.message : 'Der Entwurf ließ sich nicht bauen.'
|
||||
return (
|
||||
<div className="max-w-3xl space-y-6">
|
||||
<SeitenKopf titel="Schreiben" beschreibung={eigenschaften.beschriftung} />
|
||||
<Alert variant="fehler">
|
||||
<AlertCircle aria-hidden="true" />
|
||||
<AlertDescription>
|
||||
{meldung} Du kannst stattdessen ein{' '}
|
||||
<a href="/schreiben/neu?art=FREI" className="underline">
|
||||
freies Schreiben
|
||||
</a>{' '}
|
||||
erzeugen.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const kontakte = await db.kontakt.findMany({
|
||||
where: { betreuteId: aktive.id, ...nichtGeloescht },
|
||||
select: { id: true, name: true, organisation: true, strasse: true, plz: true, ort: true },
|
||||
orderBy: [{ organisation: 'asc' }, { name: 'asc' }],
|
||||
take: 200,
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="max-w-3xl space-y-6">
|
||||
<SeitenKopf
|
||||
titel={eigenschaften.beschriftung}
|
||||
beschreibung="Bearbeiten, als Word-Datei herunterladen, in die Akte legen — und eine Wiedervorlage setzen, damit das Warten nicht vergessen geht."
|
||||
pfad={[{ titel: 'Dokumente', href: '/dokumente' }, { titel: 'Schreiben' }]}
|
||||
/>
|
||||
|
||||
<SchreibenEditor
|
||||
betreuteId={aktive.id}
|
||||
art={art}
|
||||
entwurf={{
|
||||
betreff: entwurf.betreff,
|
||||
text: entwurf.text,
|
||||
empfaengerName: entwurf.empfaengerName,
|
||||
wiedervorlageAm: entwurf.wiedervorlageAm,
|
||||
}}
|
||||
kontakte={kontakte}
|
||||
vertragId={entwurf.vertragId}
|
||||
vorgangId={entwurf.vorgangId}
|
||||
fristBegruendung={eigenschaften.fristBegruendung}
|
||||
warnungen={entwurf.warnungen}
|
||||
/>
|
||||
|
||||
<Alert variant="hinweis">
|
||||
<Scale aria-hidden="true" />
|
||||
<AlertDescription>
|
||||
Der vorgeschlagene Wortlaut ist ein Entwurf, keine Rechtsberatung. Bei einem
|
||||
genehmigungspflichtigen Geschäft gehört die Genehmigung des Betreuungsgerichts vor den
|
||||
Versand — nicht danach.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user