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
69 lines
2.6 KiB
TypeScript
69 lines
2.6 KiB
TypeScript
import { NextResponse } from 'next/server'
|
|
import { erfordereZugang, ZugriffVerweigert } from '@/lib/auth/sitzung'
|
|
import { briefDocxErzeugen } from '@/lib/schreiben/docx'
|
|
import { dateinameFuerSchreiben, istSchreibenArt } from '@/lib/schreiben/arten'
|
|
import { heuteAlsTag } from '@/lib/fristen/datum'
|
|
import { tagAusText } from '@/lib/finanzen/datum'
|
|
|
|
/**
|
|
* Das Schreiben als Word-Datei herunterladen.
|
|
*
|
|
* Warum eine Route und keine Server Action? Eine Action kann keinen
|
|
* Download ausloesen - sie muesste die Datei kodiert zurueckgeben und der
|
|
* Browser sie nachbauen. Eine Route liefert sie unmittelbar aus, mit
|
|
* richtigem Namen und Typ. Denselben Weg geht schon der Vollexport.
|
|
*
|
|
* POST und nicht GET, weil der bearbeitete Brieftext mitkommt und in eine
|
|
* Adresszeile weder hineinpasst noch hineingehoert: Er stuende sonst im
|
|
* Verlauf des Browsers und in jedem Zwischenspeicher.
|
|
*/
|
|
|
|
export const dynamic = 'force-dynamic'
|
|
|
|
const MIME_DOCX = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
|
|
|
|
export async function POST(anfrage: Request): Promise<Response> {
|
|
let formular: FormData
|
|
try {
|
|
formular = await anfrage.formData()
|
|
} catch {
|
|
return NextResponse.json({ fehler: 'Ungültige Anfrage.' }, { status: 400 })
|
|
}
|
|
|
|
const betreuteId = String(formular.get('betreuteId') ?? '')
|
|
const rohArt = String(formular.get('art') ?? '')
|
|
const text = String(formular.get('text') ?? '')
|
|
const empfaenger = String(formular.get('empfaengerName') ?? '').trim()
|
|
const versandRoh = String(formular.get('versandAm') ?? '').trim()
|
|
|
|
if (!betreuteId || !istSchreibenArt(rohArt) || text.trim().length < 30) {
|
|
return NextResponse.json({ fehler: 'Unvollständige Angaben.' }, { status: 400 })
|
|
}
|
|
|
|
try {
|
|
// Zugriff wird hier geprueft und nicht nur auf der Seite davor: Eine
|
|
// Route laesst sich unmittelbar aufrufen.
|
|
await erfordereZugang(betreuteId, 'DOKUMENTE', 'lesen')
|
|
} catch (ausnahme) {
|
|
if (ausnahme instanceof ZugriffVerweigert) {
|
|
return NextResponse.json({ fehler: ausnahme.message }, { status: 403 })
|
|
}
|
|
throw ausnahme
|
|
}
|
|
|
|
const datum = tagAusText(versandRoh) ?? heuteAlsTag()
|
|
const datei = await briefDocxErzeugen(text)
|
|
const dateiname = dateinameFuerSchreiben(rohArt, empfaenger || null, datum, 'docx')
|
|
|
|
return new NextResponse(new Uint8Array(datei), {
|
|
headers: {
|
|
'Content-Type': MIME_DOCX,
|
|
'Content-Disposition': `attachment; filename="${dateiname}"`,
|
|
'Content-Length': String(datei.length),
|
|
// Ein Schreiben aus einer Betreuungsakte gehoert in keinen
|
|
// Zwischenspeicher.
|
|
'Cache-Control': 'no-store, must-revalidate',
|
|
},
|
|
})
|
|
}
|