Files
Webklar-Kundenbereich/docs/superpowers/plans/2026-10-04-oeffnungszeiten-live-livegang.md
T
ClaudeandClaude Opus 5.5 c10e50d0b3 Öffnungszeiten: Livegang-Notiz
Reihenfolge nach Spec 13.6 mit allen LIVEGANG-Hinweisen, Probelauf, Rechtsrunde
und Checkliste der offenen Code-Punkte.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 08:32:59 +00:00

112 lines
17 KiB
Markdown
Raw 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 | enthält TP2 bis `a629b25`; TP2 steht inzwischen auf `af63b69` (34 Commits weiter) |
| WOMS | `ticket.webklar.com/dev-zeiten`, `feature/oeffnungszeiten` | `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 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).
## 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 (`git merge main`; ist TP2 noch nicht in `main`: `git merge feature/terminplaner-tp2`). Es gibt einen Merge statt Rebase, damit die Commit-Nummern des Protokolls gültig bleiben. Bei der ersten Zusammenführung (`1d5a565`) überlappten 14 Dateien, das gleiche ist wieder zu erwarten (`server/config.js`, `siteFeatures.js`, `routes/funktionen.js`, `public/funktionen.js` …). 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, dann `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`, `k24At`. Wichtig: `activate()` schreibt jetzt `aboEndeAt` und `behaltenSessionId` bei **jeder** Funktion; ohne die Felder scheitert die Team-Aktivierung von News und Terminplaner mit „Unknown attribute“. 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). Erst `--check`, dann ohne:
```
docker inspect webklar-kundenbereich-server -f '{{range .Config.Env}}{{println .}}{{end}}' > /root/zeiten-env.tmp && chmod 600 /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
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.** 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}`. Neue Umgebungswerte gelten erst nach `docker compose up -d kundenbereich-server`, ein bloßes `docker restart` liest sie nicht (nach dem Deploy in Schritt 9 ausführen).
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, wenn sich `runner/` ändert und die Warteschlange leer ist. `ZEITEN_PUBLIC_BASE` nicht setzen: die Vorgabe `https://project.webklar.com/embed/` stimmt. Im Deploy-Log nach „Preview-Runner: runner/ geaendert, starte neu…“ suchen; steht dort „Neustart uebersprungen“, später von Hand `docker restart webklar-preview-runner` (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):
1. Portal samt Runner (`main` des Portal-Repos).
2. WOMS erst **nach** dem Portal, sonst antwortet das Portal auf die neuen Zeiten-Aktionen mit 404. Der WOMS-Webhook liefert jeden gepushten Branch aus: `feature/oeffnungszeiten` also nie allein pushen, solange das Portal nicht live ist. Danach `node scripts/setup-legal.mjs` im WOMS (legt die Rechtliches-Karte `leistungsbeschreibung-zeiten` an, Schlüssel ≤ 36 Zeichen).
3. Relaunch (`main`). Die Station „Öffnungszeiten live“ steht dort auf `bald: true` („Bald verfügbar“) und bleibt so bis Schritt C.
10. **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 K24, `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 K24 bis K27 ansehen (K25 über einen Feiertag in 1–7 Tagen, K26/K27 nur mit einem Abo-Ende; sonst über die Tests bewertet).
7. Aufräumen: Funktion am Testprojekt abschalten, Testdaten und Merker löschen lassen, Ticket schließen.
## 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), Tests und Build, du pushst. Das Handy-Bild `phone-12-zeiten` enthält den Einleitungstext des Editors: nach der Textrunde neu aufnehmen, wenn er sich geändert hat.
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 ZQ9); 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 § 6d** (`docs/rechtliches/zeiten-datenschutz-6d-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); Fassung hochzählen. → Code, siehe Abschnitt E, N1.
6. **Widerruf-Seite:** `public/widerrufen.html` und `server/routes/abo.js` (`FEATURE_OF_ORDER`) kennen „Öffnungszeiten live“ nicht; ein zahlender Verbraucher, der widerruft, würde sonst der News-Pinnwand zugeordnet. → Code, siehe N2.
7. **Fragen an die Kanzlei:** `docs/rechtliches/zeiten-kanzlei-fragen.md` (ZQ1 bis ZQ10) 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).
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.
11. **Mailtexte K24 bis K27** und die Texte im Editor in deiner Textrunde: K25 sagt „deine Zeiten gelten normal“, obwohl die Seite an gesetzlichen Feiertagen „geschlossen“ zeigt, wenn du nichts änderst; K24-Variante „wieder eingeschaltet“ (ohne „per Ticket“ / „von Hand“); T19-Text spricht von „Einrichtung“; `disabledMessage` schreibt „du“ klein; eigene Texte des Editors (Preishinweis, Vorschau); Betreffzeile K26 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**
- [ ] N1 Wertersatz-Satz im Beginn-Haken `features.zeiten.consent` (`server/config.js`) und Fassung hochzählen; Pins in `test/featureConsentTexte.test.js`, `test/zeitenRegister.test.js`, `test/zeitenRecht.test.js`.
- [ ] N2 „Öffnungszeiten live“ in `ORDERS`, `FEATURE_OF_ORDER` und im Bestell-Label von `server/routes/abo.js`, im Radioknopf von `public/widerrufen.html`, in `public/legal-form.js` und den Pins (`widerrufen_html_radio`).
- [ ] 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)**
- [ ] N5 Website-Skript: im JSON-LD `<` als `<` schreiben (`public/embed/zeiten.js`, plus Test); Kopf-Elemente behalten bei Scroll ≥ Leistenhöhe ihr Inline-`top` (bei klassengesteuertem Verstecken beim Scrollen wiederherstellen).
- [ ] N6 Abo-Abgleich: wenn `disable()` gelingt, aber das Leeren von `aboEndeMailAt` scheitert, laufen K27 und die Ersatz-Tabelle nie (K27 als offen werten, solange `aboEndeMailAt` < `disabledAt`, oder beides in einem Schreibzugriff); `nachAbschaltung` „fehler“ wiederholen oder melden; `activatedAt` bei der Löschung leeren.
- [ ] 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.
- [ ] N8 WOMS „Ticket nachlegen“ bei Abo- oder Team-Zeiten ohne `ticketId`: Portal über `holeZeitenNach` routen (heute 409 bis zum nächsten 5-Minuten-Lauf); Texte „keine Zahlung vermerkt“ 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.
**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).
## 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.