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

17 KiB
Raw Blame History

Ö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.