Öffnungszeiten: Fixrunde Doku (Datenschutz-Entwurf, Einbau-Anleitung, Kanzlei-Fragen, Livegang-Notiz)
- 6d-Entwurf § 7: Stripe-Sitzung nur bei Bestellung, IP-Adresse und Browserkennung bei jeder Zustimmung (auch Gratisweg); Offener Punkt 4 beantwortet - Einbau-Anleitung: CSP braucht auch style-src, Portal mit docker compose up -d neu erstellen, Satz zu Einzelseiten-Apps, data-api nur am Script-Tag - Kanzlei-Fragen: ZQ11 (Team-Aktivierung ohne Zustimmung), ZQ12 (Pflichtinformationen als Haken und in K15); ZQ3 und Schlussnotiz an den Stand von Wertersatz-Satz und Widerruf-Seite angepasst - Livegang-Notiz: Zusammenführung über origin/main mit erwarteten Konflikten, genaue Push-Refs und Webhook-Warnung, Sicherungen und Prüfungen vor der Compose-Änderung, Schema-Check nach dem Anlegen, Runner-Neustart prüfen, setup-legal als ungetestet, Live-Checks und Rückweg, Handrezept für Testdaten, Test-Anpassung bei bald:false, Checkliste nachgeführt Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
aa9ee35ab6
commit
ea4457e6a4
4 files changed
+84
-40
No files matched your search
@@ -6,37 +6,52 @@ Stand 05.10.2026, für Kenso. Grundlage: Spec Abschnitt 13.6, das Protokoll der
|
||||
|
||||
| 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) |
|
||||
| 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 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).
|
||||
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 (`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:
|
||||
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, 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**.
|
||||
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`, `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:
|
||||
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`. 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):
|
||||
```
|
||||
docker inspect webklar-kundenbereich-server -f '{{range .Config.Env}}{{println .}}{{end}}' > /root/zeiten-env.tmp && chmod 600 /root/zeiten-env.tmp
|
||||
(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.** 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.
|
||||
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`
|
||||
|
||||
@@ -48,14 +63,14 @@ Nur dein Konto sieht die Kachel. Beachte: Das Portal prüft nur „Leistungsbesc
|
||||
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.
|
||||
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), 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.
|
||||
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.
|
||||
|
||||
@@ -64,15 +79,15 @@ Erst wenn Abschnitt B ohne offene Fehler und Abschnitt D vollständig erledigt i
|
||||
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).
|
||||
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 § 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).
|
||||
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.
|
||||
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 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`).
|
||||
|
||||
@@ -82,17 +97,17 @@ Mit ☐ markiert, was noch im Code offen ist; die Fixrunde des Controllers kann
|
||||
|
||||
**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`).
|
||||
- [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)**
|
||||
|
||||
- [ ] 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.
|
||||
- [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: K27 gilt als offen, solange `aboEndeMailAt` vor `disabledAt` liegt (das Zurücksetzen nach `disable()` entfällt); K27 geht erst nach dem Ersatz-Ticket raus, scheitert es, wiederholt der nächste Lauf beides (K27 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.
|
||||
- [ ] 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“).
|
||||
- [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.
|
||||
|
||||
@@ -101,6 +116,14 @@ Mit ☐ markiert, was noch im Code offen ist; die Fixrunde des Controllers kann
|
||||
- [ ] 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.
|
||||
|
||||
Reference in new issue
Block a user