Files
Webklar-Kundenbereich/server/services/fristen.js
T

81 lines
3.3 KiB
JavaScript

import { config } from '../config.js'
import { getDocument } from './appwriteAdmin.js'
import { listContractEvents, openWithdrawals } from './contractEvents.js'
import { withdrawalDeadline } from './deadlines.js'
import { FEATURE_LABELS, paidFeatureOrders } from './siteFeatures.js'
import { listAuftraege } from './auftraege/auftragStore.js'
import { auftraegeAktiv } from './auftraege/schalter.js'
import { kundenartWirksam } from './kundenart.js'
/**
* Gesammelte Widerrufsfristen eines Kunden (V6, Spec 5.6): jede noch offene Frist aus Abo-Abschluss, Wechsel ins
* Wartungs-Abo, Zusatzspeicher (nur Plus), bezahlten Funktionen (Altbestand mit Kauf) und Auftragsbestätigungen
* (nur mit Schalter und Snapshot-Kundenart privat). Offen ist eine Frist bis einschließlich ihres Endes
* (23:59:59.999 Berliner Zeit am letzten Tag). `anzeigen` ist nur für Privatkunden wahr; Betriebe haben kein
* Widerrufsrecht und sehen keine Fristen (die Liste bleibt trotzdem gefüllt). Lesefehler werden weitergereicht.
*/
const BEZEICHNUNG = {
abschluss: 'Abo-Abschluss',
wechsel: 'Wechsel ins Wartungs-Abo',
speicher: 'Zusatzspeicher',
}
const zeitpunkt = (wert) => (wert instanceof Date ? wert.getTime() : Date.parse(wert))
/** Kundendokument einmal lesen; ein unbekannter Kunde ergibt {}. */
async function leseKunde(customerId) {
try {
return await getDocument(config.collections.customers, customerId)
} catch (err) {
if (err.status === 404) return {}
throw err
}
}
/** Ende der Frist zu einem Vertragsschluss; kein lesbarer Zeitpunkt ergibt null (keine Frist, nie ein Fehler). */
function fristEnde(vertragAt) {
const ms = zeitpunkt(vertragAt || '')
return Number.isNaN(ms) ? null : withdrawalDeadline(new Date(ms)).endsAt
}
/**
* @returns {Promise<{ fristen: Array<{ quelle: 'abschluss'|'wechsel'|'speicher'|'funktion'|'projekt', bezeichnung: string, bis: string, featureKey?: string, projektName?: string }>, fruehestesEnde: string|null, anzeigen: boolean }>}
*/
export async function sammleFristen(customerId, { now = new Date() } = {}) {
const kunde = await leseKunde(customerId)
const fristen = []
for (const f of openWithdrawals(await listContractEvents(customerId), now)) {
fristen.push({ quelle: f.type, bezeichnung: BEZEICHNUNG[f.type], bis: new Date(zeitpunkt(f.until)).toISOString() })
}
for (const order of await paidFeatureOrders(customerId)) {
const ende = fristEnde(order.vertragAt)
if (!ende || now.getTime() > ende.getTime()) continue
fristen.push({
quelle: 'funktion',
bezeichnung: FEATURE_LABELS[order.featureKey]?.name || order.featureKey,
bis: ende.toISOString(),
featureKey: order.featureKey,
projektName: order.projekt,
})
}
if (auftraegeAktiv(kunde)) {
for (const auftrag of await listAuftraege({ kundenId: customerId })) {
if (auftrag.kunde?.kundenart !== 'privat' || !['versendet', 'bestaetigt'].includes(auftrag.status)) continue
const ende = fristEnde(auftrag.gesendetAm)
if (!ende || now.getTime() > ende.getTime()) continue
fristen.push({ quelle: 'projekt', bezeichnung: `Auftrag ${auftrag.nummer}`, bis: ende.toISOString() })
}
}
fristen.sort((a, b) => Date.parse(a.bis) - Date.parse(b.bis))
return {
fristen,
fruehestesEnde: fristen[0]?.bis ?? null,
anzeigen: kundenartWirksam(kunde) === 'privat' && fristen.length > 0,
}
}