Terminplaner-Banner und Meldung nach Abschaltgrund mit Löschdatum
GET /api/termine/einstellungen liefert bei abgeschalteter Funktion
abgeschaltet { grund, seit, loeschungAm }. Die 409-Meldung bei
Schreibversuchen (Code abgeschaltet) und der Banner im Portal
unterscheiden Wartungs-Abo beendet, Abo vor der Einrichtung beendet und
die übrigen Gründe und nennen das Löschdatum, wenn es eines gibt.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d66dd60a76
commit
36b36bcc4d
7 files changed
+268
-26
No files matched your search
@@ -10,6 +10,7 @@ import { verschiebeZeiten } from '../services/termine/ablauf.js'
|
||||
import { aktion, entsperre, manuell, sperre } from '../services/termine/ablaufBetrieb.js'
|
||||
import { EXPORT_SEITE, exportSeite, hole, imZeitraum, zaehleAnfragen, zaehleKuenftige } from '../services/termine/buchungen.js'
|
||||
import { fehlendePflichtangaben, neuerFeedToken, normalisiereEinstellung } from '../services/termine/einstellungen.js'
|
||||
import { loeschTag, loeschzeit } from '../services/termine/frist.js'
|
||||
import { kontextPerFeature, speichereEinstellung } from '../services/termine/kontext.js'
|
||||
import { zustellFehler } from '../services/termine/outbox.js'
|
||||
import { termineFuerKunde } from '../services/termine/schalter.js'
|
||||
@@ -57,6 +58,8 @@ const MELDUNG = {
|
||||
nichtVerfuegbar: 'Der Terminplaner ist noch nicht verfügbar.',
|
||||
nichtAktiv: 'Der Terminplaner ist für diese Website nicht aktiv.',
|
||||
abgeschaltet: 'Der Terminplaner ist für diese Website abgeschaltet. Du kannst Termine nur noch ansehen und exportieren.',
|
||||
abgeschaltetAboEnde: 'Der Terminplaner ist abgeschaltet, weil dein Wartungs-Abo beendet ist. Du kannst Termine nur noch ansehen und exportieren.',
|
||||
abgeschaltetVorEinrichtung: 'Der Terminplaner ist abgeschaltet, weil dein Wartungs-Abo endete, bevor der Terminplaner eingerichtet war.',
|
||||
keineEinstellung: 'Bitte richte den Terminplaner zuerst ein.',
|
||||
zuViele: 'Zu viele Änderungen in kurzer Zeit. Bitte versuch es in einer Stunde wieder.',
|
||||
vorlage: 'Bitte wähle eine Vorlage aus der Liste.',
|
||||
@@ -125,6 +128,25 @@ export function resetTermineRoutesForTests() {
|
||||
|
||||
// ---------------------------------------------------------------- Projekt und Funktion
|
||||
|
||||
/**
|
||||
* Was die Abschaltung dem Kunden sagt: Grund (disabledReason, ohne Grund = Altbestand => null), Zeitpunkt (disabledAt) und der Berliner
|
||||
* Tag, ab dem die Daten gelöscht werden dürfen (loeschungAm, 30 Tage nach disabledAt, termine/frist.js; null ohne lesbaren Zeitpunkt).
|
||||
* Diese Route gehört nur zum Terminplaner, deshalb gibt es das Löschdatum hier immer (R105: bei News nie, siehe siteFeatures.featureView).
|
||||
*/
|
||||
const abgeschaltetAnsicht = (feature) => ({
|
||||
grund: feature.disabledReason || null,
|
||||
seit: feature.disabledAt || null,
|
||||
loeschungAm: loeschTag(feature.disabledAt),
|
||||
})
|
||||
|
||||
/** Antwort auf einen Schreibversuch bei abgeschalteter Funktion (409, Code `abgeschaltet`): je Grund ein Satz, mit Löschdatum, wenn es eines gibt. */
|
||||
function abgeschaltetMeldung(feature) {
|
||||
const grund = feature.disabledReason
|
||||
const satz = grund === 'abo_ende' ? MELDUNG.abgeschaltetAboEnde : grund === 'abo_ende_vor_einrichtung' ? MELDUNG.abgeschaltetVorEinrichtung : MELDUNG.abgeschaltet
|
||||
const ende = loeschzeit(feature.disabledAt)
|
||||
return ende ? `${satz} Ab dem ${datumKurz(ende)} löschen wir die Daten.` : satz
|
||||
}
|
||||
|
||||
const projectIdOf = (req) => {
|
||||
const raw = req.query.projectId ?? req.body?.projectId
|
||||
return typeof raw === 'string' ? raw : ''
|
||||
@@ -149,7 +171,7 @@ const withTermine = ({ schreiben = false } = {}) => route(async (req, _res, next
|
||||
const feature = await getFeatureDoc(customerId, projectId, 'termine')
|
||||
const ctx = feature && ['active', 'disabled'].includes(feature.state) ? await kontextPerFeature(feature.$id) : null
|
||||
if (!ctx || ctx.feature.customerId !== customerId) throw stop(409, MELDUNG.nichtAktiv, 'nicht_aktiv')
|
||||
if (schreiben && ctx.feature.state !== 'active') throw stop(409, MELDUNG.abgeschaltet, 'abgeschaltet')
|
||||
if (schreiben && ctx.feature.state !== 'active') throw stop(409, abgeschaltetMeldung(ctx.feature), 'abgeschaltet')
|
||||
req.ctx = ctx
|
||||
return next()
|
||||
})
|
||||
@@ -203,6 +225,7 @@ router.get('/einstellungen', requireSession, withTermine(), route(async (req, re
|
||||
const e = ctx.einstellung
|
||||
res.json({
|
||||
state: ctx.feature.state,
|
||||
abgeschaltet: ctx.feature.state === 'disabled' ? abgeschaltetAnsicht(ctx.feature) : null,
|
||||
aktivSeit: ctx.feature.activatedAt || null,
|
||||
einstellung: e ? fuerKunde(e) : null,
|
||||
version: ctx.version,
|
||||
|
||||
Reference in new issue
Block a user