Mails: Vertragsbestätigung je Fassung, Zustimmung zum AVV, kleinere Textkorrekturen

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Fable 5.1 committed 2026-09-29 14:46:41 +00:00
1 parent 63d3bb11bf
commit 12a24514a4
10 files changed
+172 -36

No files matched your search

+13 -7
View File
@@ -18,7 +18,7 @@ import {
reconcilePendingSwitch,
switchToWartung,
} from '../services/planSwitch.js'
import { PRODUCT_KEYS, contractAttachmentFor, describeItems, productLabel, productView, termsDocFor } from '../services/products.js'
import { PRODUCT_KEYS, contractAttachmentFor, describeItems, isTermsV4, productLabel, productView, termsDocFor } from '../services/products.js'
import {
ACTIVE_STATUSES,
cancelAtPeriodEnd,
@@ -416,12 +416,15 @@ async function customerNameOf(sub, fallback = '') {
/**
* K01 an den Kunden: Vertragsbestaetigung je Produkt (Katalogname, Preis, Liste "Enthalten" ueber die Bloecke
* hosting/wartung) mit den Vertragsunterlagen der Fassung, der der Kunde zugestimmt hat (§ 312f BGB,
* contractAttachmentFor: v3 Archiv-PDF, v4 aktuelles PDF). Ohne lesbare Datei geht die Mail ohne Anhang raus.
* hosting/wartung, fuer ein Wartungs-Abo nach Fassung 3 wartung_v3 mit der Zeile der Fassung 3) mit den
* Vertragsunterlagen der Fassung, der der Kunde zugestimmt hat (§ 312f BGB, contractAttachmentFor: v3 Archiv-PDF,
* v4 aktuelles PDF). Versionsangabe und Anhang kommen aus derselben Quelle (termsDocFor.version): ohne gespeicherte
* Fassung beides Fassung 3. Ohne lesbare Datei geht die Mail ohne Anhang raus.
*/
async function sendContractConfirmation(subDoc, fallbackName = '') {
if (!subDoc || subDoc.confirmationSentAt || !subDoc.email) return
const attachment = contractAttachmentFor(subDoc.termsVersion)
const terms = termsDocFor(subDoc.termsVersion)
const attachment = contractAttachmentFor(terms.version)
const pdf = await readFile(attachment.path).catch((err) => {
console.error(`[abo] Vertragsunterlagen ${attachment.filename} nicht lesbar:`, err.message)
return null
@@ -439,13 +442,14 @@ async function sendContractConfirmation(subDoc, fallbackName = '') {
leistungsbeginn: fmtDate(lateStart ? subDoc.trialEnd : subDoc.startedAt),
// Abos nach Bedingungen ohne Mindestlaufzeit (vor v3) entstehen nicht mehr; sie hätten hier "–"
mindestlaufzeit_bis: fmtDate(minTermEndOf(subDoc)),
bedingungen_version: subDoc.termsVersion || config.abo.termsVersion,
bedingungen_version: terms.version,
zustimmung_zeitpunkt: fmtDateTime(subDoc.termsAcceptedAt || Date.now()),
},
blocks: {
angebot: Boolean(subDoc.offerText),
hosting: productKey === 'hosting',
wartung: productKey === 'wartung',
wartung: productKey === 'wartung' && isTermsV4(terms.version),
wartung_v3: productKey === 'wartung' && !isTermsV4(terms.version),
sofortbeginn: !lateStart,
beginn_nach_widerrufsfrist: lateStart,
},
@@ -626,6 +630,8 @@ async function handleStripeEvent(event) {
await sendAdminNotice({
template: 'T07',
values: { email: sub?.email || '', produkt: productLabelOf(sub), stripe_abo_id: obj.id, status: obj.status, vertragsende_paragraph: endParagraphOf(sub) },
// Hinweis auf nicht genutzte Arbeitszeit nur beim Wartungs-Abo (Fix 1)
blocks: { wartung: isWartung(sub) },
})
return
}
@@ -927,7 +933,7 @@ router.post('/public/cancel', async (req, res) => {
kundennummer: Boolean(d.customerNumber),
grund: Boolean(d.reason),
abo: !storage,
// Stundenguthaben nur im Wartungs-Abo (Block innerhalb von abo)
// Hinweis auf nicht genutzte Arbeitszeit nur im Wartungs-Abo (Block innerhalb von abo)
wartung: !storage && isWartung(sub),
automatisch_gekuendigt: !storage && Boolean(effectiveAt) && d.kind !== 'geschaeftsaufgabe',
sonderkuendigung: !storage && Boolean(effectiveAt) && d.kind === 'geschaeftsaufgabe',