Kundenansicht neu E-Fix F5: Fassung 6 ohne PDF behauptet keinen Anhang, das Team bekommt den Hinweis

Die PDFs der Fassung 6 entstehen erst nach der Freigabe. Bis dahin gingen K01, K12 und K13 ohne Anhang raus, sagten aber "im Anhang" bzw.
"beigefügt", und das Team erfuhr es nicht (nur K38/T24 hatten dafür den Block anhang_fehlt).

- Neuer Helfer vertragsAnhang.js: lädt das PDF der zugestimmten Fassung und meldet fehlt nur bei Fassung 6 ohne lesbare Datei.
- K01, K12, K13: Block anhang (Anhangsaussagen) und ohne_anhang (Satz, dass die Unterlagen gesondert kommen).
- T02, T12, T13: Block anhang_fehlt "Unterlagen von Hand nachsenden" (T12 auch beim späteren Wirksamwerden, T13 auch ohne Aufgabe).
- Alte Fassungen unverändert: Hash-Pins auf die Wiedergabe der Vorlagen vor der Änderung, Anhang und Texte wie bisher.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Sonnet 5.5 committed 2026-10-16 21:38:54 +00:00
1 parent 9c04f806eb
commit 72b5ac5265
12 files changed
+362 -66

No files matched your search

+16 -19
View File
@@ -1,9 +1,9 @@
import { readFile } from 'node:fs/promises'
import { config } from '../config.js'
import { getDocument, updateDocument } from './appwriteAdmin.js'
import { listContractEvents, recordContractEvent, updateContractEvent } from './contractEvents.js'
import { sendAdminNotice, sendCustomerMail } from './mailer.js'
import { contractAttachmentFor, describeItems, priceIdsOfInvoice, regularStorageLine } from './products.js'
import { describeItems, priceIdsOfInvoice, regularStorageLine } from './products.js'
import { ladeVertragsAnhang } from './vertragsAnhang.js'
import {
ACTIVE_STATUSES,
ensurePrice,
@@ -315,16 +315,6 @@ async function supersedeStorageTasks(events, change) {
for (const e of superseded) await updateContractEvent(e.$id, { status: 'erledigt', doneAt: now, doneBy: 'ersetzt' })
}
/** Vertragsunterlagen (PDF) in der zugestimmten Fassung als Mail-Anhang; ohne Datei geht die Mail ohne Anhang raus. */
async function contractPdfAttachment(termsVersion) {
const { filename, path } = contractAttachmentFor(termsVersion)
const content = await readFile(path).catch((err) => {
console.error(`[speicher] Vertragsunterlagen ${filename} nicht lesbar:`, err.message)
return null
})
return content ? [{ filename, content, contentType: 'application/pdf' }] : []
}
/**
* K13 an den Kunden (Plus: mit PDF der zugestimmten Fassung, F22) und T13 ans Team (Aufgabe nach Art, dazu
* die Bloecke teilweise und nachweis_fehlt). Ueber das Kuendigungsformular bestaetigt K03, K13 entfaellt dort
@@ -335,8 +325,13 @@ async function sendStorageMails({ doc, change, consent, viaCancellation, proofMi
const customer = await getDocument(config.collections.customers, doc.customerId).catch(() => null)
const email = customer?.email || doc.email || ''
const dueLabel = fmtDate(change.dueAt)
let anhangFehlt = false
if (!viaCancellation && email) {
const attachments = plus ? await contractPdfAttachment(consent.termsVersion) : []
// Plus: Vertragsunterlagen der zugestimmten Fassung. Fehlt das PDF der Fassung 6, geht K13 ohne Anhang und ohne die Behauptung "beigefügt"
// raus (Block ohne_anhang), T13 sagt es dem Team (anhang_fehlt, F5); ältere Fassungen wie bisher
const unterlagen = plus ? await ladeVertragsAnhang(consent.termsVersion, 'speicher') : { attachments: [], fehlt: false }
anhangFehlt = unterlagen.fehlt
const attachments = unterlagen.attachments
await sendCustomerMail(email, {
template: 'K13',
values: {
@@ -348,16 +343,17 @@ async function sendStorageMails({ doc, change, consent, viaCancellation, proofMi
},
// plus: Einrichtung mit anteiliger Berechnung; plus_bezahlt: bis zur schon bezahlten Menge (keine Kosten);
// bestellung: Unterlagen und Widerruf bei beiden; minus: Verringerung zum Periodenende
blocks: { plus: plus && change.kind === 'einrichten', plus_bezahlt: plus && change.kind !== 'einrichten', bestellung: plus, minus: !plus },
blocks: { plus: plus && change.kind === 'einrichten', plus_bezahlt: plus && change.kind !== 'einrichten', bestellung: plus, minus: !plus, anhang: !anhangFehlt, ohne_anhang: anhangFehlt },
attachments,
}).catch((e) => console.error(`[speicher] K13 an ${email} fehlgeschlagen:`, e.message))
}
// Ohne Auftrag fuers Team (Plus genau zurueck auf die bezahlte Menge) kein T13 - es gibt nichts zu tun
if (!change.task && !proofMissing) return
// Ohne Auftrag fuers Team (Plus genau zurueck auf die bezahlte Menge) kein T13 - es gibt nichts zu tun; ausser die Unterlagen muessen nachgesendet werden
if (!change.task && !proofMissing && !anhangFehlt) return
const nurUnterlagen = !change.task && !proofMissing
await sendAdminNotice({
template: 'T13',
values: {
aktion: change.kind === 'einrichten' ? 'einrichten' : `reduzieren zum ${dueLabel}`,
aktion: nurUnterlagen ? 'Unterlagen nachsenden' : change.kind === 'einrichten' ? 'einrichten' : `reduzieren zum ${dueLabel}`,
email,
name: customer?.name || '',
stripe_abo_id: doc.$id,
@@ -368,10 +364,11 @@ async function sendStorageMails({ doc, change, consent, viaCancellation, proofMi
faellig: dueLabel,
},
blocks: {
einrichten: change.kind === 'einrichten',
periodenende: change.kind === 'periodenende',
einrichten: !nurUnterlagen && change.kind === 'einrichten',
periodenende: !nurUnterlagen && change.kind === 'periodenende',
teilweise: change.partial,
nachweis_fehlt: proofMissing,
anhang_fehlt: anhangFehlt,
},
})
}