Im Schreiben-Editor gibt es jetzt den Umschalter Brief/E-Mail. Die E-Mail-Fassung wird aus dem - womoeglich bearbeiteten - Brieftext abgeleitet: ohne Absenderblock, Anschrift und Datumszeile, aber mit dem Bestellungsabsatz. Betreff und Text einzeln kopierbar. Die offene Entscheidung aus dem Issue (mailto oder .eml) ist nach Rueckfrage mit BEIDES beantwortet: - mailto fuer kurze Texte. Der Knopf verschwindet, sobald der kodierte Link 1800 Zeichen uebersteigt - mailto uebergibt den Text in der Adresszeile, deren Laenge nirgends zugesichert ist, und ein halbierter Brief faellt erst beim Empfaenger auf. Die Grenze wird NACH der URL-Kodierung gerechnet; 900 Umlaute sind roh harmlos und kodiert weit drueber. - .eml immer: RFC 5322 mit X-Unsent: 1, quoted-printable fuer den Rumpf, RFC-2047-Kodierung fuer Umlaute im Betreff. Bewusst ohne From-Kopf - ein erfundener Absender wuerde je nach Programm uebernommen. Beide Knoepfe erscheinen nur mit E-Mail-Adresse (aus dem Kontakt uebernommen oder von Hand). Ohne Adresse bleibt das Kopieren. Die Ablage kennt den Unterschied: Eine E-Mail heisst in der Akte "E-Mail: ...", und abgelegt wird die E-Mail-Fassung samt Empfaengeradresse im Kopf der PDF - in der Akte soll stehen, was tatsaechlich hinausging. Sicherung in emailFassung(): Findet sich keine Anrede (frei umgebauter Text), wird NICHTS entfernt. Lieber ein Briefkopf zu viel in der Mail als stillschweigend verlorener Inhalt. Geprueft: 20 Einheitstests, vier End-to-End-Tests auf beiden Geraeteprofilen. 590 Unit-Tests, 81 e2e, null Typfehler. Refs #5
114 lines
3.7 KiB
TypeScript
114 lines
3.7 KiB
TypeScript
/**
|
|
* Die Arten von Schreiben, die die Anwendung erzeugen kann.
|
|
*
|
|
* Rein und ohne Datenbank - der Entwurfstext entsteht anderswo
|
|
* (src/lib/schreiben/entwurf.ts), hier steht nur, was eine Art ausmacht.
|
|
*
|
|
* Warum ueberhaupt eine Aufzaehlung? Damit die Wiedervorlagefrist,
|
|
* die Beschriftung und der Dokumenttitel an EINER Stelle stehen. Vorher
|
|
* lagen sie verstreut: 28 Tage in rueckkaufswert.ts, 42 in
|
|
* sachstandsanfrage.ts, die Beschriftungen jeweils in der Seite.
|
|
*/
|
|
|
|
export type SchreibenArt = 'RUECKKAUFSWERT' | 'KUENDIGUNG' | 'SACHSTAND' | 'FREI'
|
|
|
|
export const SCHREIBEN_ARTEN: readonly SchreibenArt[] = [
|
|
'RUECKKAUFSWERT',
|
|
'KUENDIGUNG',
|
|
'SACHSTAND',
|
|
'FREI',
|
|
]
|
|
|
|
export type SchreibenEigenschaften = {
|
|
beschriftung: string
|
|
/**
|
|
* Vorgeschlagene Wiedervorlage in Tagen.
|
|
*
|
|
* null heisst: keine vorschlagen. Bei einem freien Schreiben weiss
|
|
* niemand, ob und wann eine Antwort kommt - eine erfundene Frist waere
|
|
* schlechter als keine.
|
|
*/
|
|
wiedervorlageTage: number | null
|
|
/** Woran haengt das Schreiben? Bestimmt, was die Seite laden muss. */
|
|
bezug: 'VERTRAG' | 'VORGANG' | 'KEINER'
|
|
/** Kurze Erklaerung, warum die Frist so lang ist. */
|
|
fristBegruendung: string | null
|
|
}
|
|
|
|
export const SCHREIBEN_EIGENSCHAFTEN: Record<SchreibenArt, SchreibenEigenschaften> = {
|
|
RUECKKAUFSWERT: {
|
|
beschriftung: 'Auskunft über Rückkaufswert',
|
|
// Dieselbe Zahl wie RUECKKAUFSWERT_WIEDERVORLAGE_TAGE - dort steht sie
|
|
// fuer die Anzeige am Vertrag, hier fuer die erzeugte Frist. Ein Test
|
|
// haelt beide zusammen.
|
|
wiedervorlageTage: 28,
|
|
bezug: 'VERTRAG',
|
|
fristBegruendung:
|
|
'Vier Wochen sind bei Versicherern die übliche Bearbeitungszeit. Kommt bis dahin ' +
|
|
'nichts, lohnt das Nachfassen.',
|
|
},
|
|
KUENDIGUNG: {
|
|
beschriftung: 'Kündigung',
|
|
// Kuerzer als beim Rueckkaufswert: Eine Kuendigung muss bestaetigt
|
|
// werden, und ohne Bestaetigung ist nicht belegt, dass sie ankam.
|
|
wiedervorlageTage: 21,
|
|
bezug: 'VERTRAG',
|
|
fristBegruendung:
|
|
'Drei Wochen. Eine Kündigung ohne schriftliche Bestätigung ist nicht nachgewiesen — ' +
|
|
'und wenn sie untergegangen ist, läuft die Frist weiter.',
|
|
},
|
|
SACHSTAND: {
|
|
beschriftung: 'Sachstandsanfrage',
|
|
wiedervorlageTage: 42,
|
|
bezug: 'VORGANG',
|
|
fristBegruendung:
|
|
'Sechs Wochen. Behörden brauchen länger als Versicherer; früher nachzufassen bringt ' +
|
|
'meist nur die Antwort, dass es noch dauert.',
|
|
},
|
|
FREI: {
|
|
beschriftung: 'Freies Schreiben',
|
|
wiedervorlageTage: null,
|
|
bezug: 'KEINER',
|
|
fristBegruendung: null,
|
|
},
|
|
}
|
|
|
|
/** Ist der Wert eine bekannte Art? Fuer Parameter aus der Adresszeile. */
|
|
export function istSchreibenArt(wert: string | null | undefined): wert is SchreibenArt {
|
|
return typeof wert === 'string' && (SCHREIBEN_ARTEN as readonly string[]).includes(wert)
|
|
}
|
|
|
|
/**
|
|
* Dateiname fuer die Word-Datei und die Ablage.
|
|
*
|
|
* Sprechend, sortierbar, ohne Zeichen, die ein Dateisystem nicht mag:
|
|
* "2026-08-23_Kuendigung_Allianz.docx"
|
|
*/
|
|
export function dateinameFuerSchreiben(
|
|
art: SchreibenArt,
|
|
empfaenger: string | null,
|
|
datum: Date,
|
|
endung: 'docx' | 'pdf' | 'eml',
|
|
): string {
|
|
const tag = datum.toISOString().slice(0, 10)
|
|
|
|
const sauber = (text: string) =>
|
|
text
|
|
.replace(/ä/g, 'ae')
|
|
.replace(/ö/g, 'oe')
|
|
.replace(/ü/g, 'ue')
|
|
.replace(/Ä/g, 'Ae')
|
|
.replace(/Ö/g, 'Oe')
|
|
.replace(/Ü/g, 'Ue')
|
|
.replace(/ß/g, 'ss')
|
|
.replace(/[^A-Za-z0-9]+/g, '-')
|
|
.replace(/^-+|-+$/g, '')
|
|
.slice(0, 40)
|
|
|
|
const teile = [tag, sauber(SCHREIBEN_EIGENSCHAFTEN[art].beschriftung)]
|
|
const ziel = empfaenger ? sauber(empfaenger) : ''
|
|
if (ziel) teile.push(ziel)
|
|
|
|
return `${teile.join('_')}.${endung}`
|
|
}
|