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,104 @@
|
||||
import 'server-only'
|
||||
import * as React from 'react'
|
||||
import { Document, Page, StyleSheet, Text, View } from '@react-pdf/renderer'
|
||||
import { fuerStandardschrift, pdfErzeugen } from '@/lib/pdf/erzeugen'
|
||||
|
||||
/**
|
||||
* Das Schreiben als PDF - die Fassung, die in die Akte geht.
|
||||
*
|
||||
* Warum PDF und nicht die Word-Datei?
|
||||
*
|
||||
* Erstens der Ablageschutz: src/lib/speicher/dateityp.ts laesst nur PDF und
|
||||
* Bilder in den Objektspeicher, und zwar nach den Magic Bytes, nicht nach
|
||||
* Endung oder gemeldetem Typ. Eine .docx ist ein ZIP-Archiv; sie
|
||||
* durchzulassen hiesse, jedes ZIP durchzulassen. Diese Pruefung ist eine
|
||||
* bewusste Zusage der Anwendung, und sie fuer eine Bequemlichkeit
|
||||
* aufzuweichen waere der falsche Handel.
|
||||
*
|
||||
* Zweitens die Sache selbst: In der Akte liegt der Nachweis, was
|
||||
* hinausgegangen ist. Dafuer ist ein Format, das sich nicht mehr aendert,
|
||||
* das richtige. Die .docx ist die Arbeitsfassung zum Ausdrucken und
|
||||
* Unterschreiben - sie liegt beim Benutzer, nicht in der Akte.
|
||||
*
|
||||
* Zu beachten: Abgelegt wird der Text, wie er beim Ablegen im Browser
|
||||
* stand. Wer die Word-Datei danach noch aendert, hat in der Akte eine
|
||||
* andere Fassung als im Briefkasten. Die Oberflaeche sagt das dazu.
|
||||
*/
|
||||
|
||||
const stile = StyleSheet.create({
|
||||
seite: {
|
||||
paddingTop: 70,
|
||||
paddingBottom: 56,
|
||||
paddingLeft: 71, // 25 mm - da wird gelocht
|
||||
paddingRight: 57,
|
||||
fontSize: 10.5,
|
||||
lineHeight: 1.45,
|
||||
fontFamily: 'Helvetica',
|
||||
color: '#111111',
|
||||
},
|
||||
zeile: { marginBottom: 0 },
|
||||
leer: { height: 9 },
|
||||
betreff: { fontFamily: 'Helvetica-Bold', marginBottom: 4 },
|
||||
datum: { textAlign: 'right' },
|
||||
fuss: {
|
||||
position: 'absolute',
|
||||
bottom: 28,
|
||||
left: 71,
|
||||
right: 57,
|
||||
fontSize: 7.5,
|
||||
color: '#777777',
|
||||
textAlign: 'center',
|
||||
},
|
||||
})
|
||||
|
||||
function istBetreff(zeile: string): boolean {
|
||||
return /^Betreff:/i.test(zeile.trim())
|
||||
}
|
||||
|
||||
function istDatumszeile(zeile: string): boolean {
|
||||
return /^([A-Za-zÄÖÜäöüß .-]+,\s*)?\d{1,2}\.\d{1,2}\.\d{4}$/.test(zeile.trim())
|
||||
}
|
||||
|
||||
export function Briefdokument({
|
||||
text,
|
||||
erzeugtAm,
|
||||
}: {
|
||||
text: string
|
||||
erzeugtAm: Date
|
||||
}): React.JSX.Element {
|
||||
const zeilen = text.split('\n')
|
||||
|
||||
return (
|
||||
<Document>
|
||||
<Page size="A4" style={stile.seite}>
|
||||
{zeilen.map((zeile, i) => {
|
||||
if (zeile.trim() === '') return <View key={i} style={stile.leer} />
|
||||
|
||||
const stil = istBetreff(zeile)
|
||||
? [stile.zeile, stile.betreff]
|
||||
: istDatumszeile(zeile)
|
||||
? [stile.zeile, stile.datum]
|
||||
: stile.zeile
|
||||
|
||||
return (
|
||||
<Text key={i} style={stil}>
|
||||
{fuerStandardschrift(zeile)}
|
||||
</Text>
|
||||
)
|
||||
})}
|
||||
|
||||
<Text style={stile.fuss} fixed>
|
||||
{fuerStandardschrift(
|
||||
`Aus dem Betreuungs-Cockpit erzeugt am ${erzeugtAm.toLocaleDateString('de-DE')}. ` +
|
||||
'Diese Fassung liegt in der Akte.',
|
||||
)}
|
||||
</Text>
|
||||
</Page>
|
||||
</Document>
|
||||
)
|
||||
}
|
||||
|
||||
/** Fertiges PDF als Buffer, fuer die Ablage im Objektspeicher. */
|
||||
export async function briefPdfErzeugen(text: string, erzeugtAm: Date): Promise<Buffer> {
|
||||
return pdfErzeugen(<Briefdokument text={text} erzeugtAm={erzeugtAm} />)
|
||||
}
|
||||
Reference in New Issue
Block a user