Öffnungszeiten: Einschalten im Wartungs-Abo
POST /api/funktionen/zeiten/einschalten (Gratisweg, Spec 3.2): Abo frisch prüfen, Zustimmung (consentFrei) und Nachweis ohne Stripe-Sitzung, Zustand setup mit unlockedBy abo, dann Neubau, Einbau-Ticket und K24; ein zweiter Klick holt Fehlendes nach (holeZeitenNach, eigene Sperre). GET /api/funktionen liefert das Angebot (zeiten), features.zeiten mit unlockedBy, aboEndeAt, einbau, loeschungAm sowie zeitenStatus und zeitenLeer je Projekt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
8d670ca5dc
commit
66793d06d5
9 files changed
+1124
-58
No files matched your search
@@ -1,6 +1,6 @@
|
||||
import { config } from '../config.js'
|
||||
import { aboMemo } from './aboStatus.js'
|
||||
import { oeffentlicheAnsicht } from './featureRegistry.js'
|
||||
import { aboMemo, bestellbar } from './aboStatus.js'
|
||||
import { featureConfig } from './featureRegistry.js'
|
||||
import { istObjekt } from './termine/ablauf.js'
|
||||
import { holeOderNull, ladeEinstellung } from './termine/kontext.js'
|
||||
|
||||
@@ -31,6 +31,16 @@ const ZUSTIMMUNG_FEHLT = 'Bitte stimme der Erklärung zu, um zu bestellen.'
|
||||
const FASSUNG_VERALTET = 'Die Erklärungen wurden inzwischen geändert. Bitte lies sie noch einmal und stimme neu zu.'
|
||||
const MAX_HAKEN = 50
|
||||
|
||||
/**
|
||||
* Sicht der Zustimmung fuer den Browser (Bestellseite, Antwort bei veralteter Fassung): Fassung, Knopf und alle Texte; ob ein Punkt
|
||||
* einen Haken braucht, steht je Punkt. Fuer feature.consent und, beim Gratisweg, freiweg(feature).consent.
|
||||
*/
|
||||
export const oeffentlicheConsent = (consent) => ({
|
||||
version: consent.version,
|
||||
knopf: consent.knopf,
|
||||
items: consent.items.map((item) => ({ id: item.id, text: item.text, haken: Boolean(item.haken) })),
|
||||
})
|
||||
|
||||
const fehlt = () => bestellFehler(400, ZUSTIMMUNG_FEHLT, 'zustimmung_fehlt')
|
||||
const hatWert = (wert) => wert !== undefined && wert !== null
|
||||
|
||||
@@ -63,7 +73,8 @@ export function pruefeZustimmung(feature, eingabe) {
|
||||
const { version, haken } = eingabe.zustimmung
|
||||
if (typeof version !== 'string' || !Array.isArray(haken) || haken.length > MAX_HAKEN) throw fehlt()
|
||||
if (version !== feature.consent.version) {
|
||||
throw bestellFehler(409, FASSUNG_VERALTET, 'fassung_veraltet', { consent: oeffentlicheAnsicht(feature.key).consent })
|
||||
// die Fassung des geprueften Eintrags (beim Gratisweg die Gratis-Zustimmung), nicht die des Registers
|
||||
throw bestellFehler(409, FASSUNG_VERALTET, 'fassung_veraltet', { consent: oeffentlicheConsent(feature.consent) })
|
||||
}
|
||||
if (!haken.every((id) => typeof id === 'string' && ids.includes(id))) throw fehlt()
|
||||
const gesetzt = new Set(haken)
|
||||
@@ -132,3 +143,33 @@ export async function pruefeAbo(feature, customerId) {
|
||||
if (!Object.hasOwn(ABO_TEXTE, grund) || grund === 'unbekannt') throw new Error('Abo-Prüfung ohne gültiges Ergebnis')
|
||||
throw bestellFehler(409, aboMeldung(feature, grund), grund, { grund })
|
||||
}
|
||||
|
||||
const GRATIS_ABO_TEXT = {
|
||||
kein_abo_gekuendigt: (name, preis) => `Ohne laufendes Wartungs-Abo kannst Du ${name} für einmalig ${preis} bestellen.`,
|
||||
nicht_aktiv: () => 'Einschaltbar, sobald Dein Abo aktiv ist.',
|
||||
}
|
||||
const GERADE_NICHT_PRUEFBAR = 'Gerade nicht prüfbar – bitte versuch es gleich noch einmal.'
|
||||
|
||||
/**
|
||||
* Abo-Pruefung des Gratisweges "Einschalten" von Oeffnungszeiten live (Spec 3.2 Schritt 1): immer frisch gelesen (bestellbar direkt,
|
||||
* nie aboMemo), gedacht fuer den Aufruf unter der Sperre feature-order. Laeuft das Wartungs-Abo und ist es aktiv, geschieht nichts.
|
||||
* kein_abo und gekuendigt: 409 (code und body.grund wie der Grund) mit dem Hinweis auf den 30-Euro-Weg; nicht_aktiv: 409 "Einschaltbar,
|
||||
* sobald Dein Abo aktiv ist." Ein Lesefehler ist 503 (fail-closed, nie stillschweigend erlaubt); im Log stehen nur Art und Status.
|
||||
*/
|
||||
export async function pruefeAboFuerEinschalten(customerId) {
|
||||
const feature = featureConfig('zeiten')
|
||||
let ergebnis
|
||||
try {
|
||||
ergebnis = await bestellbar(customerId)
|
||||
} catch (err) {
|
||||
console.error(`[funktionen] Abo für das Einschalten nicht lesbar: ${err?.name ?? 'Fehler'} ${err?.status ?? ''}`.trim())
|
||||
throw Object.assign(new Error(GERADE_NICHT_PRUEFBAR), { status: 503 })
|
||||
}
|
||||
if (ergebnis?.ok === true) return
|
||||
const grund = ergebnis?.grund
|
||||
if (grund === 'nicht_aktiv') throw bestellFehler(409, GRATIS_ABO_TEXT.nicht_aktiv(), grund, { grund })
|
||||
if (grund === 'kein_abo' || grund === 'gekuendigt') {
|
||||
throw bestellFehler(409, GRATIS_ABO_TEXT.kein_abo_gekuendigt(feature.label.name, feature.priceLabel), grund, { grund })
|
||||
}
|
||||
throw new Error('Abo-Prüfung ohne gültiges Ergebnis')
|
||||
}
|
||||
Reference in new issue
Block a user