Funktionen: Freischalt-Hindernisse und Freigabe-Mail ausgelagert
freischaltHindernis (Abo zuerst, dann Einstellung; Abo-Lesefehler = abo_unbekannt) und meldeFreigeschaltet (K16, Fehler geloggt, mit attachments) in termine/freischaltung.js. Der Standardhaken freischaltbar fuer termine delegiert an freischaltHindernis, runActivation an meldeFreigeschaltet. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
2db3eca357
commit
8fe14c2fe6
4 files changed
+425
-21
No files matched your search
@@ -27,14 +27,15 @@ async function termineBestellpruefung({ customerId, projectId, feature }) {
|
||||
|
||||
/**
|
||||
* Freischaltpruefung des Terminplaners (Spec 4.5: die Aktivierung bleibt gesperrt, bis ein Abo laeuft): die Einrichtung
|
||||
* ist erst freizugeben, wenn ein Wartungs-Abo laeuft und eine Einstellung gespeichert ist. Fail-closed: ein Lesefehler
|
||||
* (Abo, Einstellung) wirft, activateClosedTickets haelt das Dokument dann in 'setup'. Der Aufrufer liest das Dokument
|
||||
* unter der Sperre frisch und uebergibt es; auch das Abo wird hier frisch gelesen, nie aus einem Zwischenspeicher.
|
||||
* ist erst freizugeben, wenn ein Wartungs-Abo laeuft und eine Einstellung gespeichert ist. Das entscheidet
|
||||
* freischaltHindernis (termine/freischaltung.js, auch die Gruende dort); dieser Haken gibt nur bei null frei. Fail-closed:
|
||||
* ein nicht lesbares Abo ('abo_unbekannt') gibt nie frei, ein Lesefehler der Einstellung wirft, activateClosedTickets haelt
|
||||
* das Dokument dann in 'setup'. Der Aufrufer liest das Dokument unter der Sperre frisch und uebergibt es; auch das Abo wird
|
||||
* hier frisch gelesen (freischaltHindernis ohne eingereichten Zwischenspeicher), nie aus einem Zwischenspeicher.
|
||||
*/
|
||||
async function termineFreischaltbar(doc) {
|
||||
const [{ ladeEinstellung }, { laeuft }] = await Promise.all([import('./termine/kontext.js'), import('./aboStatus.js')])
|
||||
if (!(await laeuft(doc.customerId))) return false
|
||||
return (await ladeEinstellung(doc.$id)) !== null
|
||||
const { freischaltHindernis } = await import('./termine/freischaltung.js')
|
||||
return (await freischaltHindernis(doc)) === null
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1010,22 +1010,15 @@ export async function handleFeatureCheckoutEvent(event) {
|
||||
}
|
||||
}
|
||||
|
||||
/** Freischalt-Job: geschlossene Einrichtungs-Tickets -> 'active', K16 an jeden betroffenen Kunden. */
|
||||
/**
|
||||
* Freischalt-Job: geschlossene Einrichtungs-Tickets -> 'active', K16 an jeden betroffenen Kunden (meldeFreigeschaltet: Fehler
|
||||
* werden geloggt und halten weder die Aktivierung noch die uebrigen Mails auf). termine/freischaltung.js importiert diese
|
||||
* Datei, darum wird es hier erst im Aufruf geladen.
|
||||
*/
|
||||
export async function runActivation({ now = new Date(), customerId = null } = {}) {
|
||||
const activated = await activateClosedTickets({ now, customerId })
|
||||
for (const doc of activated) {
|
||||
try {
|
||||
const customer = await customerOf(doc.customerId)
|
||||
if (!customer?.email) continue
|
||||
const feature = featureConfig(doc.featureKey)
|
||||
if (!feature) throw new Error(`Funktion ${doc.featureKey} nicht im Register`)
|
||||
await sendCustomerMail(
|
||||
customer.email,
|
||||
bereitMail(feature, { kundeName: customer.name || '', projekt: await projectName(doc.projectId) })
|
||||
)
|
||||
} catch (err) {
|
||||
console.error(`[funktionen] K16 an Kunde ${doc.customerId} fehlgeschlagen:`, err.message)
|
||||
}
|
||||
}
|
||||
if (activated.length === 0) return activated
|
||||
const { meldeFreigeschaltet } = await import('./termine/freischaltung.js')
|
||||
for (const doc of activated) await meldeFreigeschaltet(doc)
|
||||
return activated
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import { aboMemo } from '../aboStatus.js'
|
||||
import { featureConfig } from '../featureRegistry.js'
|
||||
import { sendCustomerMail } from '../mailer.js'
|
||||
import { bereitMail, customerOf, projectName } from '../siteFeatures.js'
|
||||
import { ladeEinstellung } from './kontext.js'
|
||||
|
||||
// Freischalt-Hindernisse und Freigabe-Mail des Terminplaners (TP2 D6, aus B11 herausgeloest). Beide Funktionen stehen hier,
|
||||
// damit der Standardhaken freischaltbar (featureRegistry.js), der Freischalt-Job (siteFeatures.runActivation) und die
|
||||
// Staff-Aktivierung (F13a) dieselbe Antwort und denselben Mailweg nutzen. siteFeatures.js und featureRegistry.js laden diese
|
||||
// Datei erst im Aufruf (dynamisches import()), weil sie selbst siteFeatures.js importiert.
|
||||
|
||||
/**
|
||||
* Was hindert die Freischaltung dieses Termine-Dokuments? null = nichts; sonst der Grund:
|
||||
* 'kein_abo' kein laufendes Wartungs-Abo (inkl. beendet, Wechsel-Widerruf E1; past_due und vorgemerkte Kuendigung laufen noch)
|
||||
* 'keine_einstellung' Abo laeuft, aber es ist noch keine Einstellung gespeichert
|
||||
* 'abo_unbekannt' das Abo war nicht lesbar: nie freischalten, wenn etwas nicht lesbar war (der Fehler wird geloggt)
|
||||
* Reihenfolge: Abo zuerst, dann Einstellung; ohne Abo wird die Einstellung gar nicht gelesen. Ein Lesefehler bei der
|
||||
* Einstellung wirft (anders als beim Abo steht dafuer kein Grundcode bereit): der Aufrufer, activateClosedTickets, haelt das
|
||||
* Dokument dann in 'setup'.
|
||||
* abo: Fragen an das Abo, { laeuft(customerId) }; Standard aboMemo() je Aufruf, also frisch gelesen. Wer viele Dokumente
|
||||
* im Listenlauf prueft, uebergibt einen gemeinsamen aboMemo(); Pruefungen unter einer Sperre lesen frisch (Standard).
|
||||
* @param {{ $id: string, customerId: string, featureKey?: string }} doc portalFeatures-Dokument
|
||||
* @returns {Promise<null | 'kein_abo' | 'keine_einstellung' | 'abo_unbekannt'>}
|
||||
*/
|
||||
export async function freischaltHindernis(doc, { abo = aboMemo() } = {}) {
|
||||
let laeuft
|
||||
try {
|
||||
laeuft = await abo.laeuft(doc.customerId)
|
||||
} catch (err) {
|
||||
console.error(`[funktionen] Freischaltung ${doc.featureKey || 'termine'} (${doc.$id}) nicht prüfbar:`, err.message)
|
||||
return 'abo_unbekannt'
|
||||
}
|
||||
if (!laeuft) return 'kein_abo'
|
||||
if ((await ladeEinstellung(doc.$id)) === null) return 'keine_einstellung'
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* K16 (Funktion bereit) an den Kunden eines freigeschalteten Dokuments, Werte aus dem Register-Eintrag der Funktion (bereitMail).
|
||||
* Fehler (Kunde, Register, Versand) werden geloggt und nie geworfen: die Freischaltung ist dann schon geschehen, und eine Mail,
|
||||
* die nicht rausgeht, darf die uebrigen Dokumente des Laufs nicht aufhalten. Kunde ohne Adresse: keine Mail, kein Fehler.
|
||||
* attachments: Anhaenge der Mail, z. B. die PDFs der Staff-Aktivierung (F13a).
|
||||
* @param {{ customerId: string, projectId: string, featureKey: string }} doc portalFeatures-Dokument
|
||||
* @param {{ attachments?: object[] }} [optionen]
|
||||
*/
|
||||
export async function meldeFreigeschaltet(doc, { attachments = [] } = {}) {
|
||||
try {
|
||||
const customer = await customerOf(doc.customerId)
|
||||
if (!customer?.email) return
|
||||
const feature = featureConfig(doc.featureKey)
|
||||
if (!feature) throw new Error(`Funktion ${doc.featureKey} nicht im Register`)
|
||||
const mail = bereitMail(feature, { kundeName: customer.name || '', projekt: await projectName(doc.projectId) })
|
||||
await sendCustomerMail(customer.email, { ...mail, attachments })
|
||||
} catch (err) {
|
||||
console.error(`[funktionen] K16 an Kunde ${doc.customerId} fehlgeschlagen:`, err.message)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user