81 lines
3.3 KiB
JavaScript
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,
|
|
}
|
|
}
|