Terminplaner: Aktionen des Betriebs, manuelle Termine, Sperren

Neues Modul ablaufBetrieb.js: aktion (annehmen, ablehnen, absagen, verschieben,
nichterschienen, anonymisieren), manuell, sperre, entsperre. ablauf.js exportiert
die gemeinsamen Bausteine (Pruefungen, Meldungen, mailSicher, pruefeSlot,
verschiebeKern). R31: brauchtZustimmungVorzeitig folgt der Berliner Widerrufsfrist
(withdrawalDeadline ab now + Haltezeit); bei einer Anfrage mit Preis immer.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Sonnet 5.5 committed 2026-10-03 08:17:43 +00:00
1 parent c4ec24448e
commit be49d38e0c
8 files changed
+1563 -79

No files matched your search

+18 -7
View File
@@ -1,11 +1,14 @@
import { randomBytes } from 'node:crypto'
import { config } from '../../config.js'
import { withdrawalDeadline } from '../deadlines.js'
import { BUNDESLAENDER } from './feiertage.js'
import { heute, zuUtc } from './zeit.js'
/**
* Terminplaner-Einstellungen (Spezifikation 2026-10-02-terminplaner-design.md, Abschnitt 2, termineConfig.einstellung).
* Reine Funktionen ohne Appwrite und ohne config: Pruefen und Normalisieren der Betriebs-Einstellung, Texte fuer
* Knoepfe und Preise, die oeffentliche Sicht fuer GET /config. `now` kommt immer als Parameter.
* Reine Funktionen ohne Appwrite (config nur fuer die Haltezeit in brauchtZustimmungVorzeitig): Pruefen und
* Normalisieren der Betriebs-Einstellung, Texte fuer Knoepfe und Preise, die oeffentliche Sicht fuer GET /config.
* `now` kommt immer als Parameter.
* Fehler sind { [pfad]: text } mit Pfaden wie 'betrieb.name' oder 'ressourcen.0.plan.1'; Texte in Du-Form (Portal).
*/
@@ -19,7 +22,7 @@ const RESERVIERTE_SCHLUESSEL = ['__proto__', 'constructor', 'prototype']
const VORLAGE_RE = /^[a-z]{1,20}$/
const FEED_TOKEN_RE = /^[A-Za-z0-9_-]{43}$/
const ID_ALPHABET = 'abcdefghijklmnopqrstuvwxyz0123456789'
const VORZEITIG_TAGE = 14
const MIN_MS = 60000
const istObjekt = (wert) => wert !== null && typeof wert === 'object' && !Array.isArray(wert)
const idRe = (praefix) => new RegExp(`^${praefix}_[a-z0-9]{8}$`)
@@ -576,12 +579,20 @@ export function istVerbraucher(einstellung) {
}
/**
* Pflicht-Zustimmung zum vorzeitigen Beginn (§ 356 Abs. 5 Nr. 2 BGB): verbindlich + Preis + Verbraucher und
* Terminbeginn vor now + 14 Tagen. Ein unbrauchbarer Beginn verlangt die Zustimmung lieber.
* Pflicht-Zustimmung zum vorzeitigen Beginn (§ 356 Abs. 5 Nr. 2 BGB): Preis + Verbraucher und
* - sofort buchbar: Terminbeginn bis zum Ende der Widerrufsfrist (withdrawalDeadline, Berliner Kalender, §§ 187, 188, 193
* BGB). Der Vertrag kommt spaetestens nach der Haltezeit (config.termine.holdMinutes) zustande, gerechnet wird mit
* `now` plus Haltezeit.
* - Anfrage: immer. Den Tag der Annahme kennt niemand im Voraus, die Frist beginnt erst dann; die Zustimmung zu viel zu
* verlangen schadet nicht.
* Ein unbrauchbarer Beginn verlangt die Zustimmung lieber.
*/
export function brauchtZustimmungVorzeitig(einstellung, leistung, start, now) {
if (leistung?.buchungsart !== 'sofort' || !hatPreis(leistung) || !istVerbraucher(einstellung)) return false
return !(start.getTime() >= now.getTime() + VORZEITIG_TAGE * 86400000)
if (!hatPreis(leistung) || !istVerbraucher(einstellung)) return false
if (leistung.buchungsart === 'anfrage') return true
if (leistung.buchungsart !== 'sofort') return false
const fristEnde = withdrawalDeadline(new Date(now.getTime() + config.termine.holdMinutes * MIN_MS)).endsAt.getTime()
return !(start.getTime() > fristEnde)
}
export function preisHinweis(einstellung) {