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:
1 parent
9c04f806eb
commit
72b5ac5265
12 files changed
+362
-66
No files matched your search
@@ -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,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { readFile } from 'node:fs/promises'
|
||||
import { config } from '../config.js'
|
||||
import { getDocument } from './appwriteAdmin.js'
|
||||
import { eventIdFor, getContractEvent, listContractEvents, recordContractEvent, updateContractEvent } from './contractEvents.js'
|
||||
import { sendAdminNotice, sendCustomerMail } from './mailer.js'
|
||||
import { contractAttachmentFor, describeItems } from './products.js'
|
||||
import { describeItems } from './products.js'
|
||||
import { anhangFehlt, ladeVertragsAnhang } from './vertragsAnhang.js'
|
||||
import { getFeatureDoc } from './siteFeatures.js'
|
||||
import {
|
||||
addMonths,
|
||||
@@ -276,23 +276,17 @@ async function previewFor(ctx, prorationDate) {
|
||||
}
|
||||
}
|
||||
|
||||
/** Vertragsunterlagen (PDF) in der zugestimmten Fassung; ohne Datei geht K12 ohne Anhang raus (Fehler im Log). */
|
||||
async function contractPdfAttachment(termsVersion) {
|
||||
const { filename, path } = contractAttachmentFor(termsVersion)
|
||||
const content = await readFile(path).catch((err) => {
|
||||
console.error(`[wechsel] Vertragsunterlagen ${filename} nicht lesbar:`, err.message)
|
||||
return null
|
||||
})
|
||||
return content ? [{ filename, content, contentType: 'application/pdf' }] : []
|
||||
}
|
||||
|
||||
/** K12 an den Kunden: Vertragsbestaetigung mit PDF. Ein Mailfehler bricht den Wechsel nicht ab. */
|
||||
/**
|
||||
* K12 an den Kunden: Vertragsbestaetigung mit PDF der zugestimmten Fassung. Ein Mailfehler bricht den Wechsel nicht ab.
|
||||
* Fehlt das PDF der Fassung 6, geht K12 ohne Anhang und ohne die Behauptung "im Anhang" raus (Block ohne_anhang, F5); die Rueckgabe
|
||||
* { anhangFehlt } sagt es dem Aufrufer fuer T12 (Block anhang_fehlt). Ohne Datei der alten Fassungen bleibt es beim bisherigen Verhalten.
|
||||
*/
|
||||
async function sendSwitchConfirmation(contact, consent, facts, blocks) {
|
||||
if (!contact.email) {
|
||||
console.error('[wechsel] K12 nicht verschickt: keine E-Mail-Adresse zum Kunden')
|
||||
return
|
||||
return { anhangFehlt: false }
|
||||
}
|
||||
const attachments = await contractPdfAttachment(consent.termsVersion)
|
||||
const { attachments, fehlt } = await ladeVertragsAnhang(consent.termsVersion, 'wechsel')
|
||||
await sendCustomerMail(contact.email, {
|
||||
template: 'K12',
|
||||
values: {
|
||||
@@ -304,9 +298,10 @@ async function sendSwitchConfirmation(contact, consent, facts, blocks) {
|
||||
bedingungen_version: consent.termsVersion,
|
||||
zustimmung_zeitpunkt: fmtDateTime(consent.consentAt),
|
||||
},
|
||||
blocks,
|
||||
blocks: { ...blocks, anhang: !fehlt, ohne_anhang: fehlt },
|
||||
attachments,
|
||||
}).catch((err) => console.error(`[wechsel] K12 an ${contact.email} fehlgeschlagen:`, err.message))
|
||||
return { anhangFehlt: fehlt }
|
||||
}
|
||||
|
||||
/** T12 ans Team (sendAdminNotice protokolliert Fehler selbst und wirft nie). */
|
||||
@@ -468,10 +463,10 @@ async function performSwitch(customerId, current, consent, prorationDate, expect
|
||||
const facts = { switchedAt, amountDueCents, monthlyCents }
|
||||
const lastschrift = sepa && effective && !paid
|
||||
// K12 sofort; zahlung_offen nur bei zurueckgehaltener Aenderung (Karte), lastschrift bei SEPA (F37); sofortverlangen nur mit Zeitstempel (kein Betrieb in Fassung 6)
|
||||
await sendSwitchConfirmation(contact, consent, facts, { zahlung_offen: pending, lastschrift, zusatzspeicher: ctx.storageUnits > 0, sofortverlangen: Boolean(consent.earlyStartConsentAt) })
|
||||
const { anhangFehlt: ohneAnhang } = await sendSwitchConfirmation(contact, consent, facts, { zahlung_offen: pending, lastschrift, zusatzspeicher: ctx.storageUnits > 0, sofortverlangen: Boolean(consent.earlyStartConsentAt) })
|
||||
// T12 erst, wenn der Wechsel wirksam ist - ohne Nachweis sofort, damit das Team ihn von Hand verfolgt
|
||||
if (effective || !event) {
|
||||
await sendSwitchNotice(contact, doc.$id, facts, { wirksam: effective, lastschrift, nachweis_fehlt: !event })
|
||||
await sendSwitchNotice(contact, doc.$id, facts, { wirksam: effective, lastschrift, nachweis_fehlt: !event, anhang_fehlt: ohneAnhang })
|
||||
}
|
||||
return { subscription, paymentUrl, event }
|
||||
}
|
||||
@@ -544,7 +539,8 @@ async function completeSwitch(eventId, contact, doneBy = 'stripe') {
|
||||
doneBy,
|
||||
details: { ...(wasEffective ? {} : { effectiveAt: now }), paidAt: now },
|
||||
})
|
||||
if (!wasEffective) await sendSwitchNotice(contact, event.subscriptionId, factsOf(event), { wirksam: true })
|
||||
// Das T12 "wirksam" kommt hier erst nach K12: fehlen die PDFs der Fassung 6 noch, sagt es auch dieses T12 dem Team (F5)
|
||||
if (!wasEffective) await sendSwitchNotice(contact, event.subscriptionId, factsOf(event), { wirksam: true, anhang_fehlt: await anhangFehlt(event.termsVersion) })
|
||||
return { completed: true, eventId }
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Vertragsunterlagen (PDF) als Mail-Anhang der Bestaetigungen K01 (Abo), K12 (Wechsel) und K13 (Zusatzspeicher-Plus), mit der Auskunft, ob sie fehlen.
|
||||
*
|
||||
* Fassung 6 (config.abo.termsVersionNeu) liefert die PDFs erst nach der Freigabe (build-legal-pdfs.sh abo6); bis dahin fehlt die Datei. Dann darf die
|
||||
* Kundenmail nicht behaupten, die Unterlagen haengten an (Block ohne_anhang statt anhang), und das Team bekommt in T02, T12 bzw. T13 den Block
|
||||
* anhang_fehlt und sendet sie von Hand nach (Muster K38/T24). Alle aelteren Fassungen bleiben, wie sie waren: ohne Datei geht die Mail ohne Anhang
|
||||
* raus, Vorlage und Text unveraendert (fehlt bleibt false).
|
||||
*/
|
||||
|
||||
import { access, readFile } from 'node:fs/promises'
|
||||
import { contractAttachmentFor } from './products.js'
|
||||
import { istNeueFassung } from './vertragsNachweis.js'
|
||||
|
||||
/**
|
||||
* @param {string} termsVersion zugestimmte Fassung (die Fassung der Zustimmung, nicht die des Abos)
|
||||
* @param {string} tag Kennung fuer das Log, z. B. 'abo'
|
||||
* @returns {Promise<{ attachments: Array<{ filename: string, content: Buffer, contentType: string }>, fehlt: boolean }>}
|
||||
* fehlt: nur bei Fassung 6 und nicht lesbarer Datei
|
||||
*/
|
||||
export async function ladeVertragsAnhang(termsVersion, tag) {
|
||||
const { filename, path } = contractAttachmentFor(termsVersion)
|
||||
const content = await readFile(path).catch((err) => {
|
||||
console.error(`[${tag}] Vertragsunterlagen ${filename} nicht lesbar:`, err.message)
|
||||
return null
|
||||
})
|
||||
return { attachments: content ? [{ filename, content, contentType: 'application/pdf' }] : [], fehlt: !content && istNeueFassung(termsVersion) }
|
||||
}
|
||||
|
||||
/** Fehlen die PDFs der Fassung 6 (ohne Log, ohne Lesen der Datei)? Für ein später gesendetes T12; ältere Fassungen: nie. */
|
||||
export async function anhangFehlt(termsVersion) {
|
||||
if (!istNeueFassung(termsVersion)) return false
|
||||
const { path } = contractAttachmentFor(termsVersion)
|
||||
return access(path).then(() => false, () => true)
|
||||
}
|
||||
Reference in new issue
Block a user