Files
Webklar-Kundenbereich/docs/superpowers/plans/2026-10-04-oeffnungszeiten-live-livegang.md
ClaudeandClaude Opus 5.5 50f854ef1c Öffnungszeiten: Datenschutz-Abschnitt heißt 6e (6d ist seit dem Webshop vergeben); Härtungspunkt N13
Entwurf umbenannt (zeiten-datenschutz-6e-entwurf.md), Leistungsbeschreibung, Kanzlei-Fragen,
Livegang-Notiz und Test-Pins ziehen mit. N13: Rückgabe-Marker nach dem Löschen fehlt (vor on).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 06:30:03 +00:00

136 lines
26 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Öffnungszeiten live – Livegang-Notiz
Stand 05.10.2026, für Kenso. Grundlage: Spec Abschnitt 13.6, das Protokoll der Umsetzung (`.superpowers/sdd/2026-10-04-oeffnungszeiten-live/progress.md`, alle LIVEGANG-Hinweise und Rechtsrunde-Punkte) und die Gesamtprüfung (Task 37, Bericht `task-37-report.md` im selben Ordner). Nichts davon ist gepusht. Keine Rechtsberatung.
## Stand der drei Branches
| Repo | Worktree / Branch | Stand | Basis und Abstand |
|---|---|---|---|
| Portal (+ Runner) | `dev-zeiten`, `feature/oeffnungszeiten` | Inhalt von `90e8607` plus diese Notiz und die Fixrunde nach der Abschlussprüfung (`ecf67d6` Server, `aa9ee35` Skript und Editor, danach die Doku) | enthält TP2 bis `a629b25`; TP2 steht inzwischen auf `af63b69` (34 Commits weiter) |
| WOMS | `ticket.webklar.com/dev-zeiten`, `feature/oeffnungszeiten` | `919d96f` (Fixrunde: Statustext der Einrichtung) auf `3d67aa8` | auf WOMS-TP2 `72eedeb`; TP2 steht inzwischen auf `4c3f67e` (5 Commits weiter) |
| Relaunch | `webklar.com/dev-zeiten-station`, `feature/zeiten-station` | `8f5d522` | auf `main` `4139f31` (kein Abstand, Stand 05.10.) |
Letzte Prüfung vor der Fixrunde 05.10.2026, alles grün: Portal 4544/4545 (der eine Fehler war der bekannte wacklige `previewDevRouter`-Test, einzeln 12/12), Portal unter Node 20 (wie in Produktion) 4543/4543 ohne `analyticsContract`, Runner 177/177, UI-Prüfungen 31/31 + 8/8 + alle OK, WOMS 377/377 + `vite build`, Relaunch 349/349 + Build (Haupt-JS 128 084 Byte gz, Grenze 133 120).
Nach der Fixrunde (Abschlussprüfung, 05.10.2026), alles grün: Portal 4564/4564 (`npm test`), Runner 177/177, UI-Prüfungen Skript 34/34 und Portal 9/9, WOMS 378/378 + `vite build`. Der Node-20-Lauf (Schritt A.2) und die Relaunch-Prüfung (A.4) stehen für nach der TP2-Zusammenführung an, sie laufen ohnehin neu.
## A. Reihenfolge am Livegang-Tag
1. **TP2 geht zuerst live** (Portal und WOMS) nach seiner eigenen Reihenfolge (Kanzlei-Paket Abschnitt 7.6, mit `termine-setup.mjs` und `feature-setup.mjs`). Öffnungszeiten live baut auf TP2 auf und braucht dessen Schema (`aboEndeMailAt`, `disabledReason`, `featureConsents`).
2. **Portal neu aufsetzen.** In `dev-zeiten`: das jetzt live gegangene TP2 einmal hineinführen, mit `git fetch origin && git merge origin/main`. **Nicht** `git merge main`: die lokale Ref `main` steht noch auf `f1b43ca` (vor TP2), das gäbe „Already up to date“, und der Push würde abgelehnt. Ist TP2 noch nicht in `origin/main`: `git merge feature/terminplaner-tp2`. Es gibt einen Merge statt Rebase, damit die Commit-Nummern des Protokolls gültig bleiben. Eine Probe-Zusammenführung mit TP2 `af63b69` (`git merge-tree --write-tree`) ergab im Portal drei Textkonflikte, die so zu lösen sind (bei der ersten Zusammenführung `1d5a565` überlappten 14 Dateien):
- `server/routes/admin/funktionenAdmin.js`: unser Zeiten-Zweig (`zeitenAktivieren`, `zeitenAbschalten`, `ersatzTicket` in der Antwort) **und** TP2s Textform-/K16-Warnung (`warnung`) bleiben beide; die Antwort trägt beides.
- `server/services/retention.js`: unser `zeitenIds` (Zeiten vor dem Abschalten des Kunden löschen) **und** TP2s `consentIds`/`featureFieldIds` bleiben beide. TP2 `9a2a70b` („Fehlschlag hält den Kunden für den nächsten Lauf“) löst vermutlich die Retention-Hälfte von N9 (eine fehlgeschlagene `loescheZeitenVonKunde` wird nicht wiederholt): bei der Konfliktlösung prüfen, ob `loescheZeitenVonKunde` bei jedem Lauf aufgerufen wird, und nicht vorher separat patchen.
- `server/services/siteFeatures.js`: Textkonflikt, beide Seiten behalten. TP2 `b974b3a` legt `disable()` unter die Sperre `feature-activate:<id>` (Sperren sind nicht wiederbetretbar): nach der Zusammenführung prüfen, dass **kein Halter von `feature-activate` `disable()` aufruft** (`aboAbgleich` und `zeitenAbschalten` rufen es unter `feature-admin` auf: erlaubte Reihenfolge).
Danach alles erneut laufen lassen:
- `nice npm test` (nur `previewDevRouter` darf wackeln, dann einzeln wiederholen), `cd runner && nice npm test`
- Node 20: `docker run --rm --cpus 3 -v /home/webklar/apps/kundenbereich.webklar.com/dev-zeiten:/app:ro -w /app node:20 sh -c 'node --test --test-concurrency=3 $(ls test/*.test.js | grep -v analyticsContract)'`
- UI, je einzeln mit `nice` und vorher `free -m`: `test/ui/zeiten-embed.ui.mjs`, `test/ui/zeiten-portal.ui.mjs`, `test/ui/funktionen.ui.mjs`
3. **WOMS neu aufsetzen.** In `ticket.webklar.com/dev-zeiten`: die WOMS-Hauptlinie (`test`, mit TP2) hineinführen (`git fetch origin && git merge origin/test`). Erwarteter Konflikt (1 Datei): `src/components/customer/CustomerFunktionen.jsx`; dort bleiben unser `ersatzTicket` **und** TP2s `speicherStatus`/`K16_WARNUNG` (TP2 `4c3f67e` ändert dieselben `save()`-Zeilen sowie das Ende von `src/lib/funktionenView.js` und seinen Test, das Portal fügt dieselbe POST-Route um `warnung` ein). `K16_WARNUNG` nennt einen „AVV“, den Öffnungszeiten live nicht hat: nach der Zusammenführung prüfen und für die Zeiten anpassen. Danach `nice npm test` und `nice npx vite build`. `node_modules` ist dort im Git verfolgt und wurde von `npm ci` verändert: **nie stagen**.
4. **Relaunch prüfen.** `git log --oneline 4139f31..main` im Relaunch-Repo; gibt es neue Commits, `feature/zeiten-station` darauf aufsetzen, dann `nice npm test`, `nice npm run build` und `gzip -9 -c dist/assets/index-*.js | wc -c` (höchstens 133 120).
5. **Schema anlegen, vor dem Portal-Deploy.** `scripts/zeiten-setup.mjs` legt `oeffnungszeiten` und `zeitenErinnerungen` an und ergänzt `portalFeatures` um `unlockedBy`, `aboEndeAt`, `einbau`, `behaltenSessionId`, `einschaltMailAt`. Seit der Fixrunde schreibt `activate()` `aboEndeAt` und `behaltenSessionId` nur noch für Öffnungszeiten live (Parameter `extra`); News und Terminplaner brauchen die Felder nicht mehr und laufen auch vor dem Skript. Trotzdem erst das Schema, dann der Deploy: die Zeiten-Pfade schreiben die Felder. Das Skript braucht den neuen Code, also aus dem Worktree in einem Wegwerf-Container (kein `docker compose run`: der Container bekäme die Traefik-Labels des Portals). Die Env-Datei enthält Zugangsdaten: mit `umask 077` anlegen, nicht erst danach `chmod`. Erst `--check` (beim ersten Lauf Exit 1, erwartet: es fehlt ja noch alles), dann ohne, dann **noch einmal `--check`** (muss grün sein; die Index-Reihenfolge der Schema-Skripte hat am 30.09. schon einmal überrascht):
```
(umask 077; docker inspect webklar-kundenbereich-server -f '{{range .Config.Env}}{{println .}}{{end}}' > /root/zeiten-env.tmp)
docker run --rm --env-file /root/zeiten-env.tmp --network webklar-network -v /home/webklar/apps/kundenbereich.webklar.com/dev-zeiten:/app:ro -w /app node:20-alpine node scripts/zeiten-setup.mjs --check
docker run --rm --env-file /root/zeiten-env.tmp --network webklar-network -v /home/webklar/apps/kundenbereich.webklar.com/dev-zeiten:/app:ro -w /app node:20-alpine node scripts/zeiten-setup.mjs
docker run --rm --env-file /root/zeiten-env.tmp --network webklar-network -v /home/webklar/apps/kundenbereich.webklar.com/dev-zeiten:/app:ro -w /app node:20-alpine node scripts/zeiten-setup.mjs --check
rm /root/zeiten-env.tmp
```
(Der Aufruf ist so noch nie gegen die Produktion gelaufen; er folgt dem Muster der früheren Schema-Skripte. Das Skript ist idempotent, `--check` schreibt nichts.)
6. **Stripe prüfen.** `node scripts/stripe-products.mjs --pruefen` (gleicher Wegwerf-Container). Erwartet wird ein Preis `webklar_zeiten_einrichtung_3000` (30 €, einmalig, `tax_behavior` inclusive, Standard-Produktbild). Fehlt er: `--anlegen`; danach `--pruefen` nochmal.
7. **Einstellungen, vor dem Push.** Zuerst Sicherungen: `cp /home/webklar/apps/docker-compose.yml /home/webklar/apps/docker-compose.yml.bak-2026-10-05-zeiten` und `(umask 077; cp /home/webklar/apps/.env /home/webklar/apps/.env.bak-2026-10-05-zeiten)` (`.env` enthält Zugangsdaten). Dann in `/home/webklar/apps/.env`: `ZEITEN_ENABLED=pilot`, `ZEITEN_PILOT_EMAILS=kenso.gri@gmail.com`, `ZEITEN_KEIN_NEUBAU=emmas-schatzkiste`. In `/home/webklar/apps/docker-compose.yml`, Dienst `kundenbereich-server`, drei Zeilen neben `TERMINE_ENABLED`: `- ZEITEN_ENABLED=${ZEITEN_ENABLED:-off}`, `- ZEITEN_PILOT_EMAILS=${ZEITEN_PILOT_EMAILS:-}`, `- ZEITEN_KEIN_NEUBAU=${ZEITEN_KEIN_NEUBAU:-emmas-schatzkiste}`. Prüfen mit `docker compose -f /home/webklar/apps/docker-compose.yml config -q` (keine Ausgabe = gültig), dann übernehmen mit `docker compose -f /home/webklar/apps/docker-compose.yml up -d --no-deps kundenbereich-server`. Ein bloßes `docker restart` liest neue Umgebungswerte nicht. Das alles **vor dem Push** (Schritt 9): dann startet der Container mit dem alten Code und den neuen Werten (die kennt er nicht, harmlos), und der Neustart durch `deploy.sh` nach dem Push ist der einzige mit dem neuen Code (jeder Containerstart führt `apk add` und `npm install` aus).
8. **Runner: kein Image-Bau nötig.** Der Runner ist `node:20-alpine` mit eingehängtem `build/runner` (das Protokoll sagt „Image neu bauen“, das stimmt für die echte Konfiguration nicht). `deploy.sh` startet ihn neu, **nur wenn sich `runner/` in der Deploy-Spanne ändert** und die Warteschlange leer ist. `ZEITEN_PUBLIC_BASE` nicht setzen: die Vorgabe `https://project.webklar.com/embed/` stimmt. **Nach dem Push prüfen:** `docker inspect -f '{{.State.StartedAt}}' webklar-preview-runner` muss nach dem Push liegen (vorher 2026-10-02T16:17Z); sonst von Hand `docker restart webklar-preview-runner` (das Deploy-Log im Container `webklar-kundenbereich-build` wird täglich um 00:00 rotiert und ist als Beleg unzuverlässig; „naechster Deploy versucht es erneut“ stimmt nicht, ohne Änderung an `runner/` kommt kein Neustart). Ein alter Runner verwirft das neue Feld `zeiten` stillschweigend, der Einbau meldete dann „angestoßen“, setzt aber nichts ein.
9. **Pushen, in dieser Reihenfolge** (du pushst, ich nicht). Die Ziel-Ref steht immer dabei (`<lokaler Branch>:<Ziel>`), damit nichts unter dem falschen Namen ankommt:
1. Portal samt Runner: `cd /home/webklar/apps/kundenbereich.webklar.com/dev-zeiten && git push origin feature/oeffnungszeiten:main`. **Der Portal-Webhook deployt jeden gepushten Branch** (`webhook-server.js` setzt `DEPLOY_BRANCH` aus `payload.ref`, `deploy.sh` macht `reset --hard origin/<branch>`): den Feature-Branch nie unter seinem eigenen Namen pushen (`git push origin feature/oeffnungszeiten`), das würde ihn auf die Produktion bringen.
2. WOMS erst **nach** dem Portal, sonst antwortet das Portal auf die neuen Zeiten-Aktionen mit 404: `cd /home/webklar/apps/ticket.webklar.com/dev-zeiten && git push origin feature/oeffnungszeiten:test`. Der WOMS-Webhook deployt nur `test` (`TICKET_DEPLOY_BRANCH`, Vorgabe `test`; Pushes auf andere Branches ignoriert er); `test` also nie vor dem Portal. Danach `setup-legal.mjs` (legt die Rechtliches-Karte `leistungsbeschreibung-zeiten` an, Schlüssel ≤ 36 Zeichen): **ungetestet gegen die Produktion**, zuerst `node scripts/setup-legal.mjs --dry-run` im WOMS-Worktree, dann `APPWRITE_API_KEY=… APPWRITE_ENDPOINT=… node scripts/setup-legal.mjs`. Der Schlüssel (`APPWRITE_API_KEY`) ist nötig; der Standard-Endpoint des Skripts ist `https://appwrite.webklar.com/v1`, das WOMS-Frontend nutzt in der Vorgabe `https://ticket.webklar.com/v1`: den Endpoint vorher gegen den ausliefernden Container prüfen und ausdrücklich setzen.
3. Relaunch: `cd /home/webklar/apps/webklar.com/dev-zeiten-station && git push origin feature/zeiten-station:main`. Die Station „Öffnungszeiten live“ steht dort auf `bald: true` („Bald verfügbar“) und bleibt so bis Schritt C.
10. **Live-Checks nach dem Deploy** (Portal und WOMS sind da):
- Öffentliche Schnittstelle kennt einen unbekannten Schlüssel nicht: `curl -s https://project.webklar.com/api/public/zeiten/v1/aaaaaaaaaaaaaaaa` antwortet `{"aktiv":false}`.
- Skript: `curl -sI https://project.webklar.com/embed/zeiten.js` ist 200, mit `Access-Control-Allow-Origin` und `Cache-Control`.
- Leistungsbeschreibung: `curl -sI https://project.webklar.com/vertragsunterlagen/zeiten-leistungsbeschreibung.html` ist 200.
- WOMS: Kunde → Reiter „Funktionen“ lädt ohne Fehler (auch für ein Projekt ohne Zeiten).
- Portal-Log: `docker logs --since 15m webklar-kundenbereich-server 2>&1 | grep -i "unknown attribute"` bleibt leer (sonst fehlt ein Schema-Feld, Schritt 5).
- Stripe: Webhook-Adresse und Ereignisliste im Dashboard sind unverändert (die Funktion nutzt dieselben Ereignisse wie News und Terminplaner; es kommt nichts Neues dazu).
- **Rückweg**, falls etwas hakt: `ZEITEN_ENABLED=off` in `.env`, dann `docker compose -f /home/webklar/apps/docker-compose.yml up -d --no-deps kundenbereich-server`. Die Kachel verschwindet, die Schnittstelle antwortet `aktiv: false`, die Platzhalter zeigen ihr Ersatz-HTML, der Abo-Abgleich läuft weiter. Das Schema ist rein additiv (neue Collections und Felder), es gibt nichts zurückzubauen; ein Code-Rollback ist bei `off` nicht nötig.
11. **Pilot-Probelauf** (Abschnitt B), danach Rechtsrunde abschließen (Abschnitt D), dann Abschnitt C.
## B. Probelauf mit `ZEITEN_ENABLED=pilot` auf `test-website`
Nur dein Konto sieht die Kachel. Beachte: Das Portal prüft nur „Leistungsbeschreibung vorhanden“, nicht „freigegeben“ (`unterlagenBereit` ist für Zeiten immer wahr). Der Pilot ist deshalb der einzige Schutz vor dem Entwurfstext, bis `on` kommt.
1. Kachel im Funktionen-Tab sichtbar. Je nach Konto: „Einschalten“ (Wartungs-Abo aktiv) oder „30 € einmalig“. Den Bezahlweg nur mit echter 30-€-Zahlung testen (danach erstatten) oder weglassen; die Tests decken ihn ab.
2. Einschalten: Ticket „Öffnungszeiten live einbauen“ in WOMS, Mail K27, `einbau` = `angestossen` (Runner-Log) oder `manuell`.
3. Editor: Wochenplan mit Fenstern und Mitternacht, zweiter Plan, Bundesland und Feiertage, Urlaub (Hinweis-Vorschlag), Hinweis-Leiste, Kontakt-Leiste mit Telefon und Adresse, „Für Google kopieren“, Uhrzeit-Regler.
4. Preview der `test-website` nach dem Neubau: im Quelltext `<script data-wk-zeiten … data-site="…">`; Hinweis-Leiste oben (schiebt den Header?), Kontakt-Leiste auf dem Handy, JSON-LD im Quelltext (kein Duplikat), Platzhalter mit Status, Tabelle und Badge; Ersatz-HTML aus WOMS („Ersatz-HTML kopieren“) in die Platzhalter. Dazu der Datenschutz-Absatz aus `docs/funktionen/website-datenschutz-absatz.md` (Entwurf) auf der Test-Seite.
5. WOMS: Panel (Zeiten-Einstellungen `navSelektor`, Leisten aus), „Aktivieren ohne Zahlung“ und „Abschalten“ am Testprojekt; Ticket schließen ⇒ nach spätestens 5 Minuten `active` und Mail K16; Abschalten ⇒ Ticket „Ersatz-Tabelle aktualisieren“ (nur wenn der Wochenplan vom Ersatz-HTML abweicht).
6. Mails K27 bis K30 ansehen (K28 über einen Feiertag in 1–7 Tagen, K29/K30 nur mit einem Abo-Ende; sonst über die Tests bewertet).
7. Aufräumen: Funktion am Testprojekt abschalten, Ticket schließen. Das Abschalten durch das Team löscht **nie** Daten; die Testdaten löschst du von Hand in der Appwrite-Konsole (Datenbank des Portals): im Dokument der Funktion in `portalFeatures` (`featureKey` = `zeiten`, `projectId` = das Testprojekt) die ID notieren; in der Collection `oeffnungszeiten` das Dokument mit genau dieser ID löschen (es trägt die Eingaben, das Team-Feld und den Ersatz-Hash); in `zeitenErinnerungen` alle Dokumente mit `featureId` = dieser ID löschen (die Erinnerungs-Merker). Danach ist die Schnittstelle des Testprojekts nach spätestens 60 Sekunden (Zwischenspeicher) wieder leer. (Automatisch gelöscht wird nur beim Ende des Abos nach 30 Tagen und beim Löschen des Kundenkontos.)
## C. Schalter `on` (letzter Schritt)
Erst wenn Abschnitt B ohne offene Fehler und Abschnitt D vollständig erledigt ist:
1. `ZEITEN_ENABLED=on` in `.env`, `docker compose up -d kundenbereich-server`.
2. Relaunch: in `src/components/home/Kundenportal.tsx` den Eintrag `oeffnungszeiten` auf `bald: false` (die Marke „Bald verfügbar“ verschwindet). **Dabei den Test anpassen**: `relaunch-kundenportal.test.tsx` (um Zeile 142 bis 156) pinnt genau zwei „Bald verfügbar“; nach dem Umschalten ist es eines weniger. Dann Tests und Build, du pushst. Das Handy-Bild `phone-12-zeiten` zeigt den Einleitungstext des Editors (und einen laufenden Urlaubs-Hinweis): nach der Textrunde **neu aufnehmen**, bevor `bald: false` live geht.
3. Logs beobachten (`[zeiten]`, `[zeiten-jobs]`): Abo-Abgleich täglich, Feiertags-Erinnerung stündlich zwischen 08:00 und 20:00.
4. Zurück auf `off`: Kachel verschwindet, die öffentliche Schnittstelle antwortet `aktiv: false`, die Platzhalter zeigen ihr Ersatz-HTML; der Abo-Abgleich läuft weiter.
## D. Rechtsrunde und Texte (Voraussetzung für `on`)
Reihenfolge laut Spec Abschnitt 9: gemeinsame TP2-Bausteine, Leistungsbeschreibung, Datenschutz, Oberflächen- und Mailtexte, Fragen an die Kanzlei, Uploads, `on`.
1. **Gemeinsame TP2-Bausteine** (Block F: Widerrufsbelehrung, AVV) zuerst.
2. **Leistungsbeschreibung** `public/vertragsunterlagen/zeiten-leistungsbeschreibung.html`: Entwurf („ENTWURF – vor Livegang Rechtsrunde“ im Kopf). Wortlaut-Punkte der Gesamtprüfung: Nr. 4 Abs. 2 „sobald das Wartungs-Abo aktiv ist“; Nr. 1 Abs. 1 (wann die Vereinbarung bei Team-Aktivierung entsteht, siehe ZQ11); Nr. 3 Abs. 2 „oder stehen sollen“ streichen; Nr. 3 Abs. 3 „bestätigt den Einbau als abgeschlossen“ und „Tag, der nicht geschlossen ist“; Nr. 8 Abs. 7 zweiter Satz unklar; „Behalten nach Abschaltung“. Danach Fassung festlegen (Register `leistungsbeschreibung.version` und `consent.version` hochzählen, Tests nachziehen).
3. **Datenschutz Portal § 6e (6d ist seit dem Webshop vergeben)** (`docs/rechtliches/zeiten-datenschutz-6e-entwurf.md`) in `public/datenschutz.html` einbauen, mit dem Anker `id="zeiten"`, plus der Satz in § 6 (Öffnungszeiten im Kundenauftrag). **Er muss live sein, bevor der Link der Leistungsbeschreibung funktioniert.** Danach `scripts/build-legal-pdfs.sh datenschutz` (nicht `all`), Prüfsumme nachziehen, `test/legalTexts.test.js` und `test/zeitenRecht.test.js` anpassen. Wortlaut-Punkte: „Ihre Eingaben“ gegen „der Kunde“, „keine Werbung“ gegen ZQ6.
4. **Widerrufsbelehrung für das 30-€-Produkt freigeben.** Die Bestellseite muss genau `featureHaken('zeiten').belehrung()` vor dem Knopf zeigen (Ruling R32), sonst belegt der Nachweis einen Text, den der Kunde nie sah.
5. **Wertersatz-Satz** (§ 357a Abs. 2 BGB) im Beginn-Haken der bezahlten Bestellung, wie bei News und Terminplaner (ZQ3): im Code ergänzt (Fixrunde, Fassung `2026-10-05`); der Wortlaut bleibt Entwurf bis zur Rechtsrunde, danach die Fassung auf den Tag der Freigabe setzen (Pins in `test/zeitenRegister.test.js`).
6. **Widerruf-Seite:** in der Fixrunde ergänzt (`public/widerrufen.html`, `server/routes/abo.js` mit `ORDERS`, `FEATURE_OF_ORDER`, `orderLabel`); ein zahlender Verbraucher, der widerruft, wird jetzt den Öffnungszeiten zugeordnet. Nichts mehr zu tun, außer beim Livegang die Seite einmal zu öffnen.
7. **Fragen an die Kanzlei:** `docs/rechtliches/zeiten-kanzlei-fragen.md` (ZQ1 bis ZQ12) ins Kanzlei-Paket des Terminplaners als Q27 ff. übernehmen (AVV Funktionen per Haken, Behalten-Haken, Wertersatz, Haftung ohne Abo, Ende mit dem Abo, Mails, Einstellung bei Unternehmern, Website-Absatz, Vertragsrahmen ohne Abo, Preisangaben in der Hinweis-Leiste, Aktivierung durch das Team ohne Zustimmung, Pflichtinformationen als Haken und in K15).
8. **K02/K03:** Entwurf für den Block `funktionen` in `docs/rechtliches/zeiten-k02-k03-entwurf.md`. Nach der Freigabe die Vorlagen ändern und die Pins nachziehen (`test/mailTemplates.test.js`, `test/publicForms.test.js` `FUNKTIONEN_SATZ`).
9. **Website-Absatz** `docs/funktionen/website-datenschutz-absatz.md` (Varianten A/B, Baustein Route und WhatsApp nur wenn die Leiste die Knöpfe zeigt): freigeben; der Einbau auf einer Kundenseite erst, wenn er in deren Datenschutzerklärung steht.
10. **K15 für bezahlte Öffnungszeiten** trägt keine Vertragsunterlagen und keine Belehrung als Anhang (dauerhafter Datenträger): entscheiden, ob nötig (Frage ZQ12 zusammen mit dem Haken `information`).
11. **Mailtexte K27 bis K30** und die Texte im Editor in deiner Textrunde: K28 sagt „deine Zeiten gelten normal“, obwohl die Seite an gesetzlichen Feiertagen „geschlossen“ zeigt, wenn du nichts änderst; K27-Variante „wieder eingeschaltet“ (ohne „per Ticket“ / „von Hand“); T19-Text spricht von „Einrichtung“; `disabledMessage` schreibt „du“ klein; eigene Texte des Editors (Preishinweis, Vorschau); Betreffzeile K29 nennt „Wartungs-Abo“ (so gewollt).
12. **Uploads:** die Fassungen (Leistungsbeschreibung, Datenschutzerklärung) in WOMS „Rechtliches“ hochladen (Karte `leistungsbeschreibung-zeiten`).
## E. Offene Code-Punkte (Checkliste für die letzte Fixrunde)
Mit ☐ markiert, was noch im Code offen ist; die Fixrunde des Controllers kann einzelne schließen (dann hier abhaken).
**Vor `on` nötig**
- [x] N1 Wertersatz-Satz im Beginn-Haken `features.zeiten.consent` (`server/config.js`), Fassung `2026-10-05`, Pins in `test/zeitenRegister.test.js` (Fixrunde `ecf67d6`). Der Wortlaut bleibt Entwurf (ZQ3).
- [x] N2 „Öffnungszeiten live“ in `ORDERS`, `FEATURE_OF_ORDER` und im Bestell-Label von `server/routes/abo.js`, im Radioknopf von `public/widerrufen.html` und in den Pins (`widerrufen_html_radio`, neuer Test `widerruf_zeiten`); `public/legal-form.js` braucht keine Änderung, es liest `data-text` (Fixrunde `ecf67d6`).
- [ ] N3 Rechtstexte aus den Entwürfen übernehmen (Abschnitt D, Nr. 2 und 3) und den Entwurfskommentar der Leistungsbeschreibung entfernen.
- [ ] N4 Entscheidung zu Anhängen in K15 für Zeiten (D, Nr. 10); K02/K03-Block (D, Nr. 8).
**Härtung (vor `on` empfohlen, für den Pilot nicht nötig)**
- [x] N5 Website-Skript: im JSON-LD `<` als `\u003c` schreiben (`jsonLdText`, Test); Kopf-Elemente bekommen bei Scroll ≥ Leistenhöhe ihren eigenen Inline-`top` zurück (UI-Prüfung `fester_header_versteckt_beim_scrollen`); dazu späte Platzhalter (Einzelseiten-Apps) über einen entprellten MutationObserver (Fixrunde `aa9ee35`).
- [x] N6 Abo-Abgleich: K30 gilt als offen, solange `aboEndeMailAt` vor `disabledAt` liegt (das Zurücksetzen nach `disable()` entfällt); K30 geht erst nach dem Ersatz-Ticket raus, scheitert es, wiederholt der nächste Lauf beides (K30 so höchstens einmal); `activatedAt` wird bei der Löschung geleert (Fixrunde `ecf67d6`).
- [ ] N7 Team: Sperre `zeiten-ersatz:<docId>` gegen zwei Ersatz-Tickets (paralleles Abschalten und Abgleich); `PATCH …/zeiten/team` nur für `setup`/`active`/`disabled` (409 sonst); `createFeatureTicket` mit `fallbackDetails` für das lange Ersatz-Ticket.
- [x] N8 WOMS „Ticket nachlegen“ bei Abo- oder Team-Zeiten ohne `ticketId`: das Portal geht über `holeZeitenNach` (Fixrunde `ecf67d6`). Offen bleiben die Texte „keine Zahlung vermerkt“ (bei gekauften Zeiten ohne Zahlungsvermerk) und der 409 der Abnahme („noch keine Öffnungszeiten eingetragen“).
- [ ] N9 Löschen: in `loescheFunktion` erst die Merker, dann das Dokument; fehlgeschlagene Löschung in `retention.js` beim nächsten Lauf wiederholen.
- [ ] N10 `heileZeiten` in `try/finally`, wenn `activateClosedTickets` wirft.
- [ ] N13 Nach dem Löschen (Abo-Ende + 30 Tage, Kundenlöschung) setzt `loesche()` keinen Rückgabe-Marker wie TP2 (`purchaseStatus: 'zurueckgesetzt'`): eine von Stripe erneut zugestellte `checkout.session.completed` einer früher bezahlten Zeiten-Bestellung würde neu einrichten (Ticket, K15, T16). Im Pilot praktisch ausgeschlossen (Merge-Bericht 06.10.2026, Punkt 3).
**Qualität (kann nach `on`)**
- [ ] N11 Zeitlogik: `wechselListe` um Schwellen (Ende minus 24 h und minus 7 Tage) ergänzen (Texte wie „bis Sonntag Mitternacht“ bleiben sonst den ganzen Sonntag stehen); Mitternachts-Einträge nicht zusammenfassen („heute“ hinkt sonst bis zu 10 Minuten nach).
- [ ] N12 Oberfläche: Portal-Knopf auf dem Desktop 40 px hoch (Ziel 44 px); Relaunch-Stationsliste kann bei 721–780 px Fensterhöhe überlaufen (Schwelle prüfen).
**Weitere Punkte der Fixrunde nach der Abschlussprüfung (erledigt)**
- [x] `activate()` schreibt die Zeiten-Felder nur noch über `extra` (Abo-Rückkehr und Team-Aktivierung der Zeiten): News und Terminplaner brechen auch vor dem Schema-Skript nicht mehr mit „Unknown attribute“.
- [x] Die Einschalten-Route (`/api/funktionen/zeiten/einschalten`) nimmt nur `{ zustimmung }`, nicht die Altform `{ consent: true }` ohne Fassungsprüfung.
- [x] Die Team-Aktivierung behält `kauf` und `unlockedByPurchase`, wenn das Dokument vorher gekauft war (die Widerrufs-Zuordnung bleibt richtig; `einbau` bleibt dabei stehen, weil für gekaufte Dokumente das Nachholen nicht neu baut).
- [x] WOMS: „Leisten live – Einbau läuft“ nur bei `einbau` = `angestossen`, sonst „Einbau läuft (Team setzt die Skript-Zeile von Hand)“ (`919d96f`).
- [x] Editor: die Bestätigung klebt unter der Kopfleiste und bleibt nach dem Speichern eines unteren Abschnitts sichtbar; Website-Skript: Links mit Zugangsdaten werden verworfen, die Adresse der Schnittstelle bestimmt nur das eigene Script-Tag.
## F. Bewusste Abweichungen und Hinweise
- Schema der neuen `portalFeatures`-Felder liegt in `scripts/zeiten-setup.mjs` (nicht in `feature-setup.mjs`), weil dessen Tests genau drei Attribute festschreiben.
- Das Angebot liefert `GET /api/funktionen` als `zeiten` auf oberster Ebene plus `zeitenStatus` je Projekt (die Spec nennt `zeitenAngebot` je Projekt); die Kachel entscheidet nach `inklusive` und `grund` (Ruling R20).
- Der Editor einer abgeschalteten Funktion (`disabled`) ist nur lesbar (Spec Z13 sagt „nie gesperrt“); die Website zeigt dann ohnehin nichts mehr.
- Der Block `funktionen` in K02/K03 ist bewusst nur ein Entwurf (Rechtstext mit Pins).
- Schalter aus (`off`): `GET /api/public/zeiten/v1/<siteKey>` antwortet `aktiv: false`; die Jobs für Abo-Abgleich laufen unabhängig vom Schalter (ein beendetes Abo muss abschalten und löschen können).
- Wie im Terminplaner-Livegang: den Code zu einer ruhigen Zeit deployen.