Öffnungszeiten: nach Abo-Ende für 30 € behalten

Neuer Modus neben Bestellen und Einschalten (Spec 3.3): POST /api/funktionen/zeiten/behalten
startet einen Checkout mit zweck 'behalten', ohne den Zustand zu ändern; der Webhook-Zweig
bucht vor markPaid/adoptPaidSession unter feature-admin zum Kauf (Frist weg, stripeSessionId =
Sitzung), schaltet eine abgeschaltete Funktion wieder ein (Neubau nach der Sperre), ohne neues
Ticket, mit K15 im Behalten-Wortlaut. Passt die Sitzung nicht, geht T19 nach der Sperre raus.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-10-05 02:54:39 +00:00
1 parent 97e6971276
commit 53253f0451
6 files changed
+1116 -12

No files matched your search

+43
View File
@@ -20,6 +20,7 @@ import { actingPerson, speichereNachweisAusAnfrage, zustimmungFuerSitzung } from
import { termineFuerKunde } from '../services/termine/schalter.js'
import { zeitenFuerKunde } from '../services/zeiten/schalter.js'
import { einschaltenDeps, einschaltenZeiten, zeitenProjektAnsicht } from '../services/zeiten/einschalten.js'
import { starteBehalten } from '../services/zeiten/behalten.js'
import { aboMemo } from '../services/aboStatus.js'
import { featureConfig, featureHaken, featureKeys, featureSperre, freiweg, oeffentlicheAnsicht } from '../services/featureRegistry.js'
import { bestellFehler, istFortsetzen, oeffentlicheConsent, pruefeAbo, pruefeInklusive, pruefeZustimmung } from '../services/featureOrder.js'
@@ -374,6 +375,48 @@ router.post('/zeiten/einschalten', requireSession, async (req, res) => {
}
})
const BEHALTEN_ERROR = 'Das Behalten konnte gerade nicht gestartet werden. Bitte versuch es später noch einmal oder schreib uns im Chat.'
// Behalten (Spec 3.3): nach dem Ende des Wartungs-Abos Öffnungszeiten live fuer einmalig 30 € behalten, ohne neuen Einbau. Vorpruefungen wie im
// Checkout, jede Stufe ohne Seiteneffekt: 1 Kundenansicht, 2 Zustimmung (die der Bestellung, beide Haken), 3 Projekt (eigen; eine laufende Funktion
// zu behalten ist keine Neubestellung, ein archiviertes Projekt bleibt erlaubt), 4 Schalter/Pilot, 5 Unterlagen. Ob behalten werden darf, entscheidet
// das Dokument unter der Sperre (starteBehalten: aboEndeAt, sonst 409); den Zustand aendert die Route nicht, erst die bezahlte Sitzung (Webhook).
// Die Route steht nicht auf der Erlaubnisliste der Kundenansicht (Mitarbeiter bestellen nicht fuer den Kunden); die Pruefung hier ist die zweite Sicherung.
router.post('/zeiten/behalten', requireSession, async (req, res) => {
// Zeit des Klicks: Eingang der Anfrage, Anker des Nachweises und der Metadaten (consentAt) und damit der Widerrufsfrist
const now = new Date()
try {
// 1 Kundenansicht
if (req.session.viewAs) return res.status(403).json({ error: 'In der Kundenansicht kann nicht bestellt werden.' })
const feature = featureConfig('zeiten')
const body = req.body || {}
// 2 Zustimmung: alle Pflicht-Haken der Bestellung und die aktuelle Fassung
pruefeZustimmung(feature, body)
// 3 Projekt
const { projectId } = body
if (typeof projectId !== 'string' || !projectId) return res.status(400).json({ error: 'Bitte wähle ein Projekt.' })
const customerId = getSessionCustomerId(req)
await assertOwnProject(customerId, projectId)
// 4 Schalter (ZEITEN_ENABLED und Pilotliste)
const sperre = featureSperre('zeiten', req.session.email)
if (sperre) throw bestellFehler(409, sperre, 'nicht_verfuegbar')
// 5 Vertragsunterlagen abrufbar
const haken = featureHaken('zeiten')
await pruefeUnterlagen(haken)
const customer = await getDocument(config.collections.customers, customerId)
const { url } = await starteBehalten({
customer,
projectId,
now,
nachweis: (sessionId) => speichereNachweisAusAnfrage(req, { feature, haken, customerId, projectId, sessionId, klickAt: now.toISOString() }),
})
res.json({ url })
} catch (err) {
fail(res, err, BEHALTEN_ERROR)
}
})
const analyseGuard = [requireSession, requireAnalyseEnabled]
router.post('/analyse/activate', analyseGuard, async (req, res) => {