Öffnungszeiten: Rechtstext-Entwürfe und Einbau-Anleitung
Leistungsbeschreibung „Öffnungszeiten live“ als statische Seite unter /vertragsunterlagen/zeiten-leistungsbeschreibung.html (Register-Adresse, nur HTML-Kommentar ENTWURF), Entwürfe für Datenschutz § 6d mit Ergänzung § 6, den Block funktionen in K02/K03 und die Kanzlei-Fragen (⚖ AVV Funktionen, Behalten-Zustimmung), Absatz „Öffnungszeiten auf dieser Website“ für Variante A und B, Einbau-Anleitung für das Team. Keine Codeänderung, datenschutz.html und die Mailvorlagen bleiben unverändert. test/zeitenRecht.test.js. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6a9daa0583
commit
90e8607e26
7 files changed
+875
-3
No files matched your search
@@ -1,6 +1,6 @@
|
||||
# Mustertext: Datenschutzerklärung der Kundenwebsite (News-Pinnwand)
|
||||
# Mustertext: Datenschutzerklärung der Kundenwebsite (News-Pinnwand, Öffnungszeiten live)
|
||||
|
||||
Entwurf, Freigabe Nutzer ausstehend. Der Absatz gehört in die Datenschutzerklärung jeder Kundenwebsite, auf der die News-Pinnwand läuft. Bau die Pinnwand erst ein, wenn er dort steht.
|
||||
Entwurf, Freigabe Nutzer ausstehend. Der Absatz gehört in die Datenschutzerklärung jeder Kundenwebsite, auf der die News-Pinnwand läuft. Bau die Pinnwand erst ein, wenn er dort steht. Dasselbe gilt für „Öffnungszeiten live“: Der Absatz „Öffnungszeiten auf dieser Website“ (je Variante unten) gehört in die Erklärung jeder Website mit dieser Funktion; bau die Funktion erst ein, wenn er dort steht (Einbau-Anleitung: `docs/funktionen/zeiten-einbau.md`).
|
||||
|
||||
Hinweise für das Team:
|
||||
|
||||
@@ -8,7 +8,8 @@ Hinweise für das Team:
|
||||
- Im sichtbaren Text der Kundenwebsite keine Gedankenstriche (Wunsch von Emma's Schatzkiste). Den Text unverändert übernehmen oder ohne Gedankenstriche anpassen.
|
||||
- Anrede und Ton an die übrige Erklärung der Website anpassen (meist Sie). `[Überschrift]`-Nummerierung der Website beachten.
|
||||
- Die Löschfrist von 14 Tagen für Server-Protokolle entspricht Abschnitt 3 der Datenschutzerklärung von WEBklar (project.webklar.com/datenschutz.html). Ändert sich dort die Frist, hier mitändern.
|
||||
- Beide Varianten setzen voraus, dass das Skript keine Cookies setzt und nichts im Browser speichert. Das gilt für `news.js` (nur lesende Abrufe).
|
||||
- Beide Varianten setzen voraus, dass das Skript keine Cookies setzt und nichts im Browser speichert. Das gilt für `news.js` (nur lesende Abrufe) und für `zeiten.js` (ein Abruf je Seite, keine Anfragen an Dritte).
|
||||
- „Öffnungszeiten auf dieser Website“: den Absatz und nur die Bausteine übernehmen, die die Seite wirklich zeigt. Der Baustein „Route“ gehört dazu, wenn die Kontakt-Leiste den Knopf „Route“ hat, der Baustein „WhatsApp“, wenn sie den Knopf „WhatsApp“ hat. Der WhatsApp-Baustein ist der „WhatsApp-Hinweis“, den der Kunde im Portal mit einem Haken bestätigt, bevor die WhatsApp-Schaltfläche erscheint. Beide Dienste öffnen sich erst nach dem Klick des Besuchers. Anschriften von Google und WhatsApp vor der Übernahme prüfen. Auch hier gilt der Entwurfsstatus: erst nach der Freigabe durch den Inhaber (Rechtsrunde „Öffnungszeiten live“) verwenden.
|
||||
|
||||
## Variante A: Website wird von WEBklar gehostet
|
||||
|
||||
@@ -16,8 +17,32 @@ Hinweise für das Team:
|
||||
|
||||
Auf dieser Website zeigen wir unter „Neuigkeiten“ kurze Beiträge mit Text und Fotos. Beim Aufruf der Seite lädt Ihr Browser diese Beiträge und Bilder von dem Server, den die WEBklar GbR, Am Schwimmbad 10, 67722 Winnweiler, in unserem Auftrag betreibt und der bei der Hetzner Online GmbH in Falkenstein (Deutschland) steht. Dabei werden Ihre IP-Adresse, Datum und Uhrzeit des Aufrufs sowie Angaben zu Ihrem Browser und Betriebssystem übertragen und kurzzeitig in Server-Protokollen gespeichert. Die Pinnwand setzt keine Cookies und speichert nichts in Ihrem Browser. Wir brauchen diese Daten, um die Beiträge auszuliefern und den Betrieb sicher zu halten. Rechtsgrundlage ist Art. 6 Abs. 1 lit. f DSGVO. Unser berechtigtes Interesse liegt in der Darstellung aktueller Informationen und im sicheren, funktionsfähigen Betrieb der Website. Die Protokolle werden nach 14 Tagen gelöscht.
|
||||
|
||||
### Öffnungszeiten auf dieser Website
|
||||
|
||||
Auf dieser Website zeigen wir unsere Öffnungszeiten und ob wir gerade geöffnet haben. Dazu können Hinweise, zum Beispiel zu Betriebsurlaub, am Seitenanfang und auf dem Handy eine Leiste mit Knöpfen zum Anrufen und für die Route kommen. Beim Aufruf der Seite lädt Ihr Browser ein kleines Programm und unsere Öffnungszeiten von dem Server, den die WEBklar GbR, Am Schwimmbad 10, 67722 Winnweiler, in unserem Auftrag betreibt und der bei der Hetzner Online GmbH in Falkenstein (Deutschland) steht. Dabei werden Ihre IP-Adresse, Datum und Uhrzeit des Aufrufs sowie Angaben zu Ihrem Browser und Betriebssystem übertragen und kurzzeitig in Server-Protokollen gespeichert. Das Programm setzt keine Cookies und speichert nichts in Ihrem Browser. Wir brauchen diese Daten, um die Öffnungszeiten auszuliefern und den Betrieb sicher zu halten. Rechtsgrundlage ist Art. 6 Abs. 1 lit. f DSGVO. Unser berechtigtes Interesse liegt in der Darstellung aktueller Öffnungszeiten und im sicheren, funktionsfähigen Betrieb der Website. Die Protokolle werden nach 14 Tagen gelöscht. Für Suchmaschinen stehen unsere Öffnungszeiten außerdem in maschinenlesbarer Form im Seitencode.
|
||||
|
||||
#### Baustein Route (nur aufnehmen, wenn die Leiste den Knopf „Route“ zeigt)
|
||||
|
||||
Der Knopf „Route“ ist ein Link zu Google Maps. Erst wenn Sie darauf klicken, öffnet sich Google Maps (Google Ireland Limited, Gordon House, Barrow Street, Dublin 4, Irland) mit unserer Adresse als Ziel. Vorher werden keine Daten an Google übertragen. Nach dem Klick gelten die Datenschutzhinweise von Google.
|
||||
|
||||
#### Baustein WhatsApp (nur aufnehmen, wenn die Leiste den Knopf „WhatsApp“ zeigt)
|
||||
|
||||
Der Knopf „WhatsApp“ ist ein Link zu WhatsApp. Erst wenn Sie darauf klicken, öffnet sich WhatsApp (WhatsApp Ireland Limited, Merrion Road, Dublin 4, D04 X2K5, Irland, ein Unternehmen von Meta) mit unserer Nummer und gegebenenfalls einem vorgeschlagenen Starttext Ihrer Nachricht. Vorher werden keine Daten an WhatsApp oder Meta übertragen. Die Nachricht senden Sie selbst ab; nach dem Klick gelten die Datenschutzhinweise von WhatsApp.
|
||||
|
||||
## Variante B: Website liegt bei einem anderen Anbieter
|
||||
|
||||
### Neuigkeiten auf dieser Website
|
||||
|
||||
Auf dieser Website zeigen wir unter „Neuigkeiten“ kurze Beiträge mit Text und Fotos. Diese Beiträge und Bilder lädt Ihr Browser beim Aufruf der Seite von project.webklar.com, einem Server der WEBklar GbR, Am Schwimmbad 10, 67722 Winnweiler, der bei der Hetzner Online GmbH in Falkenstein (Deutschland) steht. Dabei werden Ihre IP-Adresse, Datum und Uhrzeit des Aufrufs sowie Angaben zu Ihrem Browser und Betriebssystem an project.webklar.com übertragen und kurzzeitig in Server-Protokollen gespeichert. Die Pinnwand setzt keine Cookies und speichert nichts in Ihrem Browser. Wir nutzen den Dienst, damit wir Ihnen aktuelle Informationen zeigen können, ohne die Website jedes Mal neu zu veröffentlichen. Rechtsgrundlage ist Art. 6 Abs. 1 lit. f DSGVO. Unser berechtigtes Interesse liegt in der einfachen und aktuellen Darstellung unserer Neuigkeiten. Die Protokolle werden nach 14 Tagen gelöscht. WEBklar verarbeitet die Daten in unserem Auftrag. [Prüfen: AVV mit WEBklar erforderlich]
|
||||
|
||||
### Öffnungszeiten auf dieser Website
|
||||
|
||||
Auf dieser Website zeigen wir unsere Öffnungszeiten und ob wir gerade geöffnet haben. Dazu können Hinweise, zum Beispiel zu Betriebsurlaub, am Seitenanfang und auf dem Handy eine Leiste mit Knöpfen zum Anrufen und für die Route kommen. Diese Angaben lädt Ihr Browser beim Aufruf der Seite als kleines Programm und als Öffnungszeiten von project.webklar.com, einem Server der WEBklar GbR, Am Schwimmbad 10, 67722 Winnweiler, der bei der Hetzner Online GmbH in Falkenstein (Deutschland) steht. Dabei werden Ihre IP-Adresse, Datum und Uhrzeit des Aufrufs sowie Angaben zu Ihrem Browser und Betriebssystem an project.webklar.com übertragen und kurzzeitig in Server-Protokollen gespeichert. Das Programm setzt keine Cookies und speichert nichts in Ihrem Browser. Wir nutzen den Dienst, damit unsere Öffnungszeiten auf der Website überall gleich und aktuell erscheinen, ohne dass wir die Website bei jeder Änderung neu veröffentlichen müssen. Rechtsgrundlage ist Art. 6 Abs. 1 lit. f DSGVO. Unser berechtigtes Interesse liegt in der einfachen und aktuellen Darstellung unserer Öffnungszeiten. Die Protokolle werden nach 14 Tagen gelöscht. Für Suchmaschinen stehen unsere Öffnungszeiten außerdem in maschinenlesbarer Form im Seitencode. WEBklar verarbeitet die Daten in unserem Auftrag. [Prüfen: AVV mit WEBklar erforderlich]
|
||||
|
||||
#### Baustein Route (nur aufnehmen, wenn die Leiste den Knopf „Route“ zeigt)
|
||||
|
||||
Der Knopf „Route“ ist ein Link zu Google Maps. Erst wenn Sie darauf klicken, öffnet sich Google Maps (Google Ireland Limited, Gordon House, Barrow Street, Dublin 4, Irland) mit unserer Adresse als Ziel. Vorher werden keine Daten an Google übertragen. Nach dem Klick gelten die Datenschutzhinweise von Google.
|
||||
|
||||
#### Baustein WhatsApp (nur aufnehmen, wenn die Leiste den Knopf „WhatsApp“ zeigt)
|
||||
|
||||
Der Knopf „WhatsApp“ ist ein Link zu WhatsApp. Erst wenn Sie darauf klicken, öffnet sich WhatsApp (WhatsApp Ireland Limited, Merrion Road, Dublin 4, D04 X2K5, Irland, ein Unternehmen von Meta) mit unserer Nummer und gegebenenfalls einem vorgeschlagenen Starttext Ihrer Nachricht. Vorher werden keine Daten an WhatsApp oder Meta übertragen. Die Nachricht senden Sie selbst ab; nach dem Klick gelten die Datenschutzhinweise von WhatsApp.
|
||||
@@ -0,0 +1,122 @@
|
||||
# Öffnungszeiten live auf der Kundenwebsite einbauen
|
||||
|
||||
Anleitung für das WEBklar-Team. „Öffnungszeiten live“ zeigt die Zeiten, die der Kunde im Portal einträgt (Tab „Funktionen“, Kachel „Öffnungszeiten live“), auf seiner Website: als Tabelle, als Live-Status und als Kurzanzeige, dazu eine Hinweis-Leiste, auf dem Handy eine Kontakt-Leiste und Google-Angaben (JSON-LD) im Seitencode. Den `siteKey` (16 Zeichen, a–z und 2–7) findest Du im WOMS: Kunde → Reiter „Funktionen“, Zeile „Öffnungszeiten live“ (mit Kopierknopf). Im Einrichtungs-Ticket „Öffnungszeiten live einbauen – <Projekt>“ stehen Einbau-Zeile, Ergebnis des automatischen Einbaus (`einbau`), Beispiele der Platzhalter mit den Plan-Kurznamen und die Prüfliste. Dieses Dokument ist die Referenz dahinter. Die Datenschutz-Absätze für die Kundenseite stehen in `docs/funktionen/website-datenschutz-absatz.md`.
|
||||
|
||||
Die Spec ist `docs/superpowers/specs/2026-10-04-oeffnungszeiten-live-design.md` (Abschnitte 4.2, 4.3, 4.5, 4.6).
|
||||
|
||||
## 1. Was automatisch geschieht und was Du tust
|
||||
|
||||
| Teil | Wer | Wie |
|
||||
| --- | --- | --- |
|
||||
| Einbau-Zeile (Skript) | Runner, automatisch | beim nächsten Veröffentlichen in jede HTML-Datei vor `</head>`, nur wenn die Funktion in `setup` oder `active` steht |
|
||||
| Hinweis-Leiste, Kontakt-Leiste, JSON-LD | Skript | kommen mit den Daten, ohne Platzhalter |
|
||||
| Platzhalter (Tabelle, Status, Badge) mit Ersatz-HTML | Du | an die Stellen setzen, an denen feste Zeiten stehen, und die festen Zeiten ersetzen |
|
||||
| Datenschutz-Absatz auf der Website | Du | Abschnitt 8, Punkt 5 |
|
||||
| Einbau-Zeile bei `einbau: manuell` oder wenn die Seite woanders liegt | Du | Abschnitt 9 |
|
||||
|
||||
Der Einbau ist **ein Einbau je Website** und ist im Preis (30 € oder im Wartungs-Abo ohne Aufpreis) enthalten. Er wird nicht auf das Stundenkontingent angerechnet. Sonderwünsche (zusätzliche Stellen, Sondergestaltung) gehen über ein Angebot oder, mit Abo, über Stunden.
|
||||
|
||||
## 2. Die Einbau-Zeile
|
||||
|
||||
```html
|
||||
<script data-wk-zeiten src="https://project.webklar.com/embed/zeiten.js" data-site="SITEKEY" defer></script>
|
||||
```
|
||||
|
||||
- Ins `<head>` jeder HTML-Seite, einmal pro Seite. Der Runner setzt sie vor `</head>` ein (liegt kein `</head>` vor, nach `<body>`); eine schon vorhandene Zeile mit `data-wk-zeiten` ersetzt er, der Einbau ist also wiederholbar und doppelt nichts.
|
||||
- `data-site` ist der `siteKey`. Optional `data-api="https://project.webklar.com"`; ohne Angabe nimmt das Skript die Herkunft seiner eigenen Adresse (das klappt nur bei einem normalen `<script src>`; bei Tag-Manager oder nachgeladenem Skript ist `data-api` Pflicht). Den siteKey darf auch ein Platzhalter tragen (`data-webklar-zeiten="SITEKEY"`).
|
||||
- Die Adresse des Skripts kommt aus der Runner-Variable `ZEITEN_PUBLIC_BASE` (Vorgabe `https://project.webklar.com/embed/`, muss mit `https://` beginnen und mit `/` enden). Für die Produktion nichts ändern.
|
||||
- Das Skript hat keine Abhängigkeiten, setzt keine Cookies (keine Cookies, kein `localStorage`, kein `sessionStorage`), fragt nur `project.webklar.com` ab und lädt nichts von Dritten. Kundentexte setzt es nur als Text ein, nie als HTML. Es wird 5 Minuten gecacht und darf von jeder Domain geladen werden. Ein Abruf je Seite für alle Teile, danach höchstens alle 10 Minuten.
|
||||
- Hat die Seite eine Content-Security-Policy, muss `project.webklar.com` in `script-src` und `connect-src` stehen.
|
||||
|
||||
## 3. Platzhalter
|
||||
|
||||
Ein Platzhalter ist ein Element mit dem Attribut `data-webklar-zeiten`. Innen steht das Ersatz-HTML (Abschnitt 4).
|
||||
|
||||
```html
|
||||
<div data-webklar-zeiten data-modus="tabelle" data-plan="oeffnungszeiten">…Ersatz-HTML…</div>
|
||||
<div data-webklar-zeiten data-modus="status" data-plan="oeffnungszeiten">Mo–Fr 9–18 Uhr</div>
|
||||
<span data-webklar-zeiten data-modus="badge" data-plan="oeffnungszeiten">Mo–Fr 9–18 Uhr</span>
|
||||
```
|
||||
|
||||
| `data-modus` | Zeigt | Gedacht für |
|
||||
| --- | --- | --- |
|
||||
| `tabelle` | Wochentabelle (echtes `<table>` mit Caption und Zeilenköpfen, heutiger Tag mit `aria-current="date"`), darunter „Abweichungen in den nächsten 14 Tagen“ | Kontaktseite, Footer |
|
||||
| `status` | Punkt und Text, zum Beispiel „Jetzt geöffnet · schließt um 18 Uhr“, ohne `aria-live` | Kopfbereich, Hero |
|
||||
| `badge` | Kurzform „Geöffnet“, „Geschlossen“, „Nach Vereinbarung“ | Menü, Header |
|
||||
|
||||
- `data-plan` ist der **Kurzname** des Zeitplans (Kleinbuchstaben, Ziffern und Bindestrich, 1–24 Zeichen, nach dem Anlegen unveränderlich). Der Hauptplan heißt `oeffnungszeiten`. Weitere Pläne (zum Beispiel `kueche`, `lieferung`) hat der Kunde im Editor angelegt; die Kurznamen stehen im Editor und im Einrichtungs-Ticket („Pläne (Kurznamen für data-plan)“). Ohne `data-plan` gilt der Hauptplan.
|
||||
- Ein **unbekannter** `data-plan` oder `data-modus` lässt das Ersatz-HTML stehen und schreibt eine Warnung in die Konsole. Es gibt kein stilles Umschalten auf den Hauptplan. Bei Fehlern, ohne JavaScript, bei Daten älter als 24 Stunden und nach dem Abschalten bleibt ebenfalls das Ersatz-HTML.
|
||||
- Das Skript füllt die Platzhalter ab `setup`, damit Du vor dem Schließen des Tickets prüfen kannst.
|
||||
- Gestaltung: Light DOM, alle Klassen beginnen mit `wkz-`, die Stile stehen in `@layer wkz`. **Jede** Regel der Kundenseite (ohne Layer) gewinnt also gegen die Standard-Stile. Schrift und Farben erbt die Seite. Variablen (alle optional, am Platzhalter oder auf `:root`): `--wkz-offen`, `--wkz-zu`, `--wkz-unklar` (Farben der Punkte), `--wkz-offen-hell`, `--wkz-zu-hell`, `--wkz-unklar-hell`, `--wkz-offen-text`, `--wkz-zu-text`, `--wkz-unklar-text` (Badge), `--wkz-rand`, `--wkz-heute`. Kontrast mindestens 4,5 : 1 prüfen.
|
||||
- Hat das Theme globale Regeln wie `table{…}` oder `td{…}`, greifen sie auch in der Tabelle. Prüfe das bei der Abnahme und überschreibe gezielt, zum Beispiel `[data-webklar-zeiten] table{ … }`.
|
||||
|
||||
## 4. Ersatz-HTML
|
||||
|
||||
Jeder Platzhalter behält eine **statische Ersatz-Tabelle**: Sie steht ohne JavaScript, bei einem Fehler und nach der Abschaltung. Ohne sie wäre die Stelle leer.
|
||||
|
||||
1. WOMS → Funktionen → Ersatz-HTML kopieren (je Plan ein Knopf; bei mehreren Plänen mit Namen dahinter). Erst nach dem ersten Speichern des Kunden; vorher meldet das WOMS „Der Kunde hat noch keine Zeiten eingetragen“ (die Schnittstelle antwortet 409: `GET /api/team/funktionen/:customerId/zeiten/ersatz?projectId=…&plan=<kurz>`).
|
||||
2. In jeden Platzhalter einsetzen. Das Ersatz-HTML ist eine schlichte Wochentabelle als reines ASCII mit Entities (`ä`, `–`), also in jeder Dateikodierung sicher. Ein Fensterende um Mitternacht steht als `00:00` („18:00–00:00“), ein Fenster von 00:00 bis 24:00 als „rund um die Uhr“.
|
||||
3. Die Tabelle zeigt den Wochenplan **zum Zeitpunkt des Einbaus**. Ändert der Kunde seinen Wochenplan später, bleibt das Ersatz-HTML zunächst stehen (mit Skript unsichtbar).
|
||||
4. **Beim Abschalten** der Funktion, wenn der aktuelle Wochenplan des Hauptplans vom Stand beim Einbau abweicht, entsteht ein WOMS-Ticket **„Ersatz-Tabelle aktualisieren – <Projekt>“** mit dem aktuellen Ersatz-HTML. Ersetze die Tabelle in allen Platzhaltern (gehört zum enthaltenen Einbau, Leistungsbeschreibung Nr. 4 Abs. 3). Die Mail K27 sagt dem Kunden, dass wieder feste Zeiten auf der Seite stehen. Liegt die Seite woanders, schicke dem Kunden den Text.
|
||||
|
||||
## 5. Header-Selektor (`navSelektor`)
|
||||
|
||||
Die Hinweis-Leiste steht als erstes Kind von `<body>` im Fluss und schiebt den Inhalt nach unten. Feste und klebende Elemente mit `top: 0` rückt das Skript, solange die Leiste sichtbar ist, um `max(0, Leistenhöhe − Scrollposition)` nach unten (Inline-`top`, beim Entfernen der Leiste zurückgesetzt) und erhöht `scroll-padding-top`.
|
||||
|
||||
Überlappt die Leiste trotzdem den Kopf der Seite (zum Beispiel ein fester Header mit `top: 12px`, mit `transform` oder in einem Wrapper), setze im WOMS bei Kunde → Funktionen → „Öffnungszeiten live“ das Feld **Header-Selektor** (Team-Einstellung `navSelektor`):
|
||||
|
||||
- ein CSS-Selektor des Headers, höchstens 120 Zeichen, erlaubt sind nur `A–Z a–z 0–9`, Leerzeichen und `# . - _ > : [ ] = " '`, nicht erlaubt `<`, `{`, `}` und `;`. Beispiel: `header.site-header` oder `#nav`.
|
||||
- Das Skript wendet ihn mit `querySelectorAll` in einem `try/catch` an; ein falscher Selektor bricht nichts, er bewirkt nur nichts.
|
||||
- Leer lassen = Standard (nur `top: 0`). Speichern mit `teamVersion` (bei 409 „Inzwischen geändert – bitte neu laden“).
|
||||
- Prüfe nach dem Setzen: oben auf der Seite, beim Scrollen (die Leiste scrollt weg, der Header klebt wieder oben) und auf dem Handy.
|
||||
|
||||
## 6. Leisten ausschalten
|
||||
|
||||
Im selben Bereich (WOMS → Funktionen → „Öffnungszeiten live“) gibt es zwei Haken: **`kontaktleisteAus`** („Kontakt-Leiste aus“) und **`hinweisleisteAus`** („Hinweis-Leiste aus“). Sie gelten je Website.
|
||||
|
||||
- Die **Kontakt-Leiste** erscheint nur bei `(max-width: 768px)`, unten fest mit `z-index: 40`, Knöpfe „Anrufen“, „Route“ und, wenn der Kunde sie eingerichtet hat, „WhatsApp“. Hat die Seite selbst ein festes Element am unteren Rand mit einem `a[href^="tel:"]`, bleibt sie **automatisch aus**. Setze den Haken, wenn die Seite eine eigene Anruf-Leiste hat, die das Skript nicht erkennt, oder der Kunde die Leiste nicht möchte.
|
||||
- WhatsApp und die Route (Google Maps) öffnen erst beim Klick. Den Haken „Ich habe den WhatsApp-Hinweis in meine Datenschutzerklärung aufgenommen“ setzt der Kunde im Portal; in der Kundenansicht setzt ihn kein Mitarbeiter.
|
||||
- Die **Hinweis-Leiste** zeigt zu jedem Zeitpunkt den gültigen Hinweis mit dem höchsten Rang (Wichtig, Urlaub, Aktion, Info). Sie hat keinen Schließen-Knopf (das Skript speichert nichts im Browser).
|
||||
|
||||
## 7. JSON-LD
|
||||
|
||||
Das Skript pflegt die Öffnungszeiten im Seitencode: Gibt es einen `application/ld+json`-Block mit einem Knoten vom Typ `LocalBusiness` oder einem Untertyp (auch in `@graph`), ersetzt es dort **nur** `openingHoursSpecification` und `specialOpeningHoursSpecification` (alles andere im Block bleibt); unlesbare Blöcke bleiben unberührt. Gibt es keinen, legt es einen eigenen Block `[data-wkz-jsonld]` mit `name`, `address` und `url` an. Prüfe auf der Seite, ob JSON-LD mit festen Öffnungszeiten an einer anderen Stelle steht (zum Beispiel als Mikrodaten im Footer); das würde dem Skript widersprechen.
|
||||
|
||||
## 8. Prüfliste
|
||||
|
||||
Das Einrichtungs-Ticket nennt dieselben fünf Punkte:
|
||||
|
||||
1. **Feste Zeiten im Repo suchen und ersetzen**: `grep -ril "öffnungszeit" .` und Ähnliches (`geöffnet`, `Mo-Fr`, `Uhr`), auch Footer, Kontaktseite, `<meta>` und eigene Status-Skripte. Jede Stelle wird ein Platzhalter mit Ersatz-HTML oder fällt weg.
|
||||
2. **Header**: Überlappt die Hinweis-Leiste den Kopf der Seite? Sonst `navSelektor` setzen (Abschnitt 5).
|
||||
3. **Anruf-Leiste**: Hat die Seite schon eine eigene feste Anruf-Leiste? Dann `kontaktleisteAus` (Abschnitt 6), damit sie nicht doppelt erscheint.
|
||||
4. **Vorhandenes JSON-LD** (Öffnungszeiten der Seite) prüfen, damit nichts doppelt oder widersprüchlich steht (Abschnitt 7).
|
||||
5. **Datenschutz-Absatz auf der Website**: den Absatz „Öffnungszeiten auf dieser Website“ aus `docs/funktionen/website-datenschutz-absatz.md` einsetzen (Variante A bei WEBklar-Hosting, Variante B bei Fremdhosting). Die Bausteine „Route“ und „WhatsApp“ nur, wenn die Kontakt-Leiste die Knöpfe zeigt. Liegt die Datenschutzerklärung in der von WEBklar betreuten Website, setzt Du den Absatz ein (gehört zum Einbau, nicht auf das Stundenkontingent anrechnen); liegt sie woanders (externer Generator), schicke dem Kunden den Textbaustein, er fügt ihn selbst ein. Die Texte sind Entwurf, bis die Rechtsrunde sie freigibt.
|
||||
|
||||
Zur Abnahme außerdem: Seite in der Vorschau öffnen und mit ausgeschaltetem JavaScript ansehen (Ersatz-Tabelle muss stehen), Konsole auf Warnungen zu `data-plan` prüfen, auf einem Handy (höchstens 768 px Breite) die Kontakt-Leiste, den Regler „Uhrzeit testen“ im Editor gegen die Anzeige halten, Kontrast und Tippflächen (mindestens 44 px) ansehen.
|
||||
|
||||
## 9. Sperrliste `ZEITEN_KEIN_NEUBAU` und Seiten ohne automatischen Einbau
|
||||
|
||||
**Neubau und Einsetzen.** Bei jedem Übergang in `setup` oder `active` (Einschalten im Abo, Zahlung, Aktivierung durch das Team, Rückkehr des Abos, Behalten) stößt das Portal einen Neubau der Seite an (höchstens einer gleichzeitig). Das Ergebnis steht in `einbau`: `angestossen` (Auftrag angenommen, die Zeile erscheint nach dem Veröffentlichen) oder `einbau: manuell`. Änderungen an Zeiten und Leisten brauchen keinen Neubau.
|
||||
|
||||
`einbau: manuell` heißt: kein Repo, Vorschau aus, Dev-Modus, Projekt auf der Sperrliste, Fehler oder Runner ausgelastet. Dann setzt Du die Einbau-Zeile von Hand ins Repo.
|
||||
|
||||
**Sperrliste.** `ZEITEN_KEIN_NEUBAU` (Umgebungsvariable des Portals, Liste von Subdomains, durch Komma getrennt): Für diese Projekte gibt es weder einen angestoßenen Neubau noch das automatische Einsetzen bei normalen Deploys; die Funktion zählt sie immer als manuell. Vorgabe ist `emmas-schatzkiste` (Emmas Seiten, deren Dateien teilweise nicht in Git liegen). **Achtung:** Setzt Du die Variable, **ersetzt** sie die Vorgabe. Willst Du eine Seite zusätzlich sperren, nenne `emmas-schatzkiste` mit in der Liste. Änderung in `compose` und `.env` des Portals, danach das Portal neu starten. Für gesperrte Seiten setzt Du die Zeile und die Platzhalter von Hand ein (im Repo, soweit die Dateien dort liegen, sonst direkt auf dem Server) und prüfst nach jedem Deploy, ob sie noch da sind.
|
||||
|
||||
**Seite liegt woanders** (anderer Hoster, keine WEBklar-Veröffentlichung): Das Ticket enthält die Einbau-Zeile; Du setzt sie ein, soweit WEBklar Zugriff auf die Seite hat. Hat WEBklar keinen Zugriff, schicke dem Kunden im Chat Einbau-Zeile, Platzhalter-Beispiele und Ersatz-HTML und bitte ihn, sie selbst oder durch seinen Anbieter einzufügen. Schließe das Ticket erst, wenn die Anzeige auf der Live-Seite stimmt: Öffne die Seite, sieh in den Netzwerk-Aufrufen nach der Antwort von `/api/public/zeiten/v1/<siteKey>` (`"aktiv": true`) und nach der gefüllten Tabelle.
|
||||
|
||||
## 10. Freischalten und Ende
|
||||
|
||||
- **Freischalten:** Der Job setzt `active` (und sendet K16), wenn das Ticket geschlossen ist und der Kunde Zeiten eingetragen hat (Hauptplan gespeichert, mindestens ein Tag nicht „zu“). Sonst bleibt `setup`, das WOMS zeigt „wartet auf die Zeiten des Kunden“. Solange der Kunde nichts eingetragen hat, kannst Du die Tabelle nicht einbauen.
|
||||
- **Ende des Abos** (Gratisweg): K26 mit Frist zum Behalten (30 Tage), danach `disabled` und K27, Eingaben noch 30 Tage, dann gelöscht. Das Ticket „Ersatz-Tabelle aktualisieren“ gehört zum Abschalten (Abschnitt 4).
|
||||
- **Abschalten durch das Team** (Grund `team`): keine automatische Löschung.
|
||||
|
||||
## 11. Häufige Fehlerbilder
|
||||
|
||||
| Symptom | Meist liegt es an |
|
||||
| --- | --- |
|
||||
| Es erscheint nur das Ersatz-HTML | Funktion nicht in `setup`/`active`, `einbau: manuell` und Zeile fehlt, unbekannter `data-plan`, kein JavaScript, Daten älter als 24 Stunden, CSP blockiert `project.webklar.com` |
|
||||
| Hinweis-Leiste verdeckt den Header | `navSelektor` nicht gesetzt (Abschnitt 5) |
|
||||
| Zwei Anruf-Leisten auf dem Handy | `kontaktleisteAus` setzen (Abschnitt 6) |
|
||||
| Kontakt-Leiste fehlt auf dem Handy | Seite hat schon eine feste Anruf-Leiste unten, oder Breite über 768 px, oder `kontaktleisteAus` |
|
||||
| Öffnungszeiten doppelt im Suchergebnis oder Test | festes JSON-LD an anderer Stelle (Abschnitt 7) |
|
||||
| Tabelle sieht fremd aus | globale Regeln des Themes (Abschnitt 3) |
|
||||
@@ -0,0 +1,50 @@
|
||||
# Datenschutzerklärung von WEBklar: Abschnitt 6d „Öffnungszeiten live auf Websites unserer Kunden“ (Entwurf)
|
||||
|
||||
Entwurf für die Rechtsrunde (Spec „Öffnungszeiten live“, Abschnitt 9 Nr. 3), Stand 05.10.2026. Keine Rechtsberatung. **`public/datenschutz.html` ist nicht geändert**: Der Abschnitt wird erst nach der Freigabe durch den Inhaber (und nach der Kanzlei, soweit sie die Fragen aus `zeiten-kanzlei-fragen.md` bearbeitet) in die Erklärung übernommen. Reihenfolge der Rechtsrunde: Leistungsbeschreibung, dann dieser Abschnitt samt Website-Absatz (`docs/funktionen/website-datenschutz-absatz.md`), dann Mailtexte, dann Schalter `ZEITEN_ENABLED=on`.
|
||||
|
||||
Aufbau wie Abschnitt 6c (`termine-datenschutz-6c.md`): Rolle, gespeicherte Angaben, E-Mails, Aufruf der Kundenwebsite, Löschfristen. Die Leistungsbeschreibung verweist auf diesen Abschnitt (`/datenschutz.html#zeiten`, Nr. 12 Abs. 2): **Sie darf erst live gehen, wenn der Abschnitt mit dem Anker `id="zeiten"` in der Erklärung steht.**
|
||||
|
||||
Einbau in `public/datenschutz.html`: zwischen 6c und „7. Abos und Zahlung über Stripe“. Beim Übernehmen ändert sich der Stand der Erklärung (Abschnitt 15); die Tests `test/legalTexts.test.js` (Abschnitt 6c und Kundenansicht) und `test/zeitenRecht.test.js` (dieser Entwurf) werden dann angepasst.
|
||||
|
||||
## Abschnitt 6d (HTML)
|
||||
|
||||
```html
|
||||
<h2 id="zeiten">6d. Öffnungszeiten live auf Websites unserer Kunden</h2>
|
||||
<p>Kunden können auf ihrer eigenen Website ihre Öffnungszeiten live anzeigen lassen („Öffnungszeiten live“): als Tabelle und als Status („Jetzt geöffnet“), als Hinweis-Leiste am Seitenanfang, auf dem Handy als Kontakt-Leiste mit Knöpfen zum Anrufen, für die Route und für WhatsApp sowie als Angaben für Suchmaschinen im Seitencode. Für die Besucher dieser Websites verarbeiten wir Daten ausschließlich als Auftragsverarbeiter nach den Weisungen des jeweiligen Kunden, der dafür Verantwortlicher ist. Wir verwenden diese Daten nicht für eigene Zwecke. Verantwortliche sind wir für die Angaben, die der Kunde im Kundenportal für die Funktion einträgt, für die E-Mails an den Kunden und für seine Bestellung. Diese Datenschutzerklärung gilt für unsere eigenen Seiten und das Kundenportal. Wer eine Kundenwebsite besucht, findet die Informationen zum Aufruf (Zwecke, Rechtsgrundlagen, Speicherdauer, Rechte) in der Datenschutzerklärung dieser Website.</p>
|
||||
<p>Im Kundenportal speichern wir, was der Kunde für die Funktion einträgt: seine Öffnungszeiten (benannte Zeitpläne, Zeiten je Wochentag, Abweichungen an Feiertagen, Betriebsurlaub und Sondertage sowie das gewählte Bundesland), Hinweise für die Hinweis-Leiste (Text, Art, Zeitraum und gegebenenfalls ein Knopf mit Link oder Telefonnummer), Kontaktangaben für die Kontakt-Leiste (Telefonnummer, Anschrift für die Route und auf Wunsch WhatsApp-Nummer und Starttext der Nachricht, dazu der Zeitpunkt, zu dem der Kunde bestätigt hat, den WhatsApp-Hinweis in seine Datenschutzerklärung aufgenommen zu haben), die Anzeige-Einstellungen, die unser Team für die Website des Kunden setzt, sowie Zeitpunkt und Urheber (Kunde oder Mitarbeiter) der letzten Änderung. Das sind überwiegend Betriebsdaten; personenbezogene Daten enthalten sie nur, soweit der Kunde sie einträgt, zum Beispiel die Telefonnummer eines Inhabers. Was auf der Website erscheinen soll (Zeiten, Hinweise, Telefonnummer, Anschrift, WhatsApp-Nummer, Name des Betriebs), ist dort öffentlich sichtbar. Rechtsgrundlage ist Art. 6 Abs. 1 lit. b DSGVO, wie bei den übrigen Portal-Daten (Abschnitt 6). Unsere Mitarbeiter können die Zeiten im Auftrag des Kunden ansehen und ändern (Abschnitt 6); den Haken für den WhatsApp-Hinweis setzen sie nicht. Die Bestellung und ihren Nachweis beschreibt Abschnitt 7.</p>
|
||||
<p>Hat der Kunde die Funktion eingeschaltet, erinnern wir ihn per E-Mail an die E-Mail-Adresse seines Kundenkontos vor Feiertagen: Die E-Mail nennt die Tage der nächsten sieben Tage, an denen ein Feiertag seines Bundeslands liegt oder an denen wir um Prüfung bitten, und fragt, ob seine Zeiten für diese Tage stimmen. Für jeden Tag senden wir höchstens eine Erinnerung; dafür speichern wir den Tag als Merker (ohne weitere Angaben) und löschen den Merker nach 30 Tagen. Außerdem senden wir dem Kunden E-Mails zum Stand der Funktion (eingeschaltet, Ende des Wartungs-Abos mit der Möglichkeit, die Funktion zu behalten, abgeschaltet). Der Versand läuft über den E-Mail-Dienst der Hetzner Online GmbH (Abschnitt 8). Die E-Mails enthalten keine Werbung. Rechtsgrundlage ist Art. 6 Abs. 1 lit. b DSGVO, weil die Erinnerung zur Funktion gehört.</p>
|
||||
<p>Beim Aufruf einer Kundenwebsite mit Öffnungszeiten live lädt der Browser ein kleines Programm von unserem Server (project.webklar.com) und die Öffnungszeiten des Kunden. Das Programm ruft die Daten einmal je Seitenaufruf ab und danach höchstens alle zehn Minuten erneut. Dabei gelten die Protokolle in Abschnitt 3. Das Programm setzt keine Cookies, speichert nichts im Browser des Besuchers und lädt nichts von anderen Anbietern. Die Knöpfe „Route“ und „WhatsApp“ sind Links: Erst der Klick des Besuchers öffnet Google Maps oder WhatsApp; vorher übertragen wir nichts an diese Dienste, und danach gelten deren Datenschutzhinweise. Zum Schutz vor Missbrauch begrenzen wir die Abrufe je IP-Adresse (höchstens 120 je Minute); dafür merken wir uns die Zeitpunkte der Abrufe je IP-Adresse nur im Arbeitsspeicher des Servers, nicht dauerhaft. Rechtsgrundlage ist Art. 6 Abs. 1 lit. f DSGVO; unser berechtigtes Interesse ist der sichere und störungsfreie Betrieb.</p>
|
||||
<p>Löschfristen: Haben Sie die Funktion im Wartungs-Abo ohne Aufpreis eingeschaltet und endet das Wartungs-Abo, läuft sie noch 30 Tage weiter; in dieser Zeit können Sie sie für einmalig 30 € behalten. Andernfalls schalten wir sie danach ab, bewahren Ihre Eingaben noch weitere 30 Tage auf und löschen sie dann; beginnt in dieser Zeit wieder ein Wartungs-Abo, schalten wir die Funktion mit den vorhandenen Eingaben wieder ein. Haben Sie die Funktion für 30 € bestellt, bleiben Ihre Eingaben, solange die Funktion läuft. Schalten wir sie auf Ihren Wunsch ab, löschen wir Ihre Eingaben auf Ihr Verlangen, spätestens mit der Löschung Ihres Kundenkontos. In unseren Datensicherungen bleiben gelöschte Daten noch bis zu sieben Tage erhalten und werden dann überschrieben. Die Daten liegen auf unserem Server bei Hetzner im Rechenzentrum Falkenstein (Deutschland), wie die übrigen Portal-Daten (Abschnitt 3).</p>
|
||||
```
|
||||
|
||||
## Ergänzung Abschnitt 6 (Mitarbeiter im Kundenauftrag)
|
||||
|
||||
Heute steht in Abschnitt 6 (wörtlich, `public/datenschutz.html`; der Satz ist durch `legal_datenschutz_kundenansicht` in `test/legalTexts.test.js` gepinnt):
|
||||
|
||||
> Um Sie zu unterstützen, können unsere Mitarbeiter Ihr Portal so öffnen, wie Sie es sehen, und dabei für Sie News-Beiträge anlegen, ändern oder löschen und, wenn Sie den Terminplaner nutzen, dessen Einstellungen und Termine ansehen und ändern.
|
||||
|
||||
Vorgeschlagen:
|
||||
|
||||
> Um Sie zu unterstützen, können unsere Mitarbeiter Ihr Portal so öffnen, wie Sie es sehen, und dabei für Sie News-Beiträge anlegen, ändern oder löschen, wenn Sie den Terminplaner nutzen, dessen Einstellungen und Termine ansehen und ändern sowie, wenn Sie Öffnungszeiten live nutzen, Ihre Öffnungszeiten, Hinweise und Kontaktangaben ansehen und ändern.
|
||||
|
||||
Der folgende Satz („Bestellungen, Zahlungen, Änderungen an Ihrem Passwort sowie Nachrichten oder Anträge in Ihrem Namen sind dabei ausgeschlossen.“) und das Protokoll (Mitarbeiter und Zeitpunkt, 12 Monate) bleiben unverändert und decken auch das Speichern der Zeiten durch Mitarbeiter ab (Protokollaktion `zeiten_gespeichert`). Beim Übernehmen die beiden Pins in `test/legalTexts.test.js` (`legal_datenschutz_kundenansicht`) auf den neuen Satz umstellen.
|
||||
|
||||
## Folgeänderung Abschnitt 7 (Nachweis beim Einschalten)
|
||||
|
||||
Abschnitt 7 nennt heute die Funktionen und den Klick auf „Zahlungspflichtig bestellen“. Beim Einschalten im Wartungs-Abo gibt es keine Zahlung und keine Stripe-Sitzung, gespeichert wird der Nachweis der Zustimmung trotzdem. Heute (wörtlich):
|
||||
|
||||
> Bestellen Sie eine Funktion (News-Pinnwand, Terminplaner), speichern wir als Nachweis die Texte und Fassungen, denen Sie zugestimmt haben, den Zeitpunkt Ihres Klicks auf „Zahlungspflichtig bestellen“, die Stripe-Sitzung, IP-Adresse und Browserkennung sowie Ihr Konto und die angemeldete Person. Rechtsgrundlagen sind Art. 6 Abs. 1 lit. b, c und f DSGVO; unser berechtigtes Interesse ist der Nachweis des Vertragsschlusses und der Zustimmungen, insbesondere für den Widerruf. Diese Angaben löschen wir drei Jahre nach Ablauf des Jahres, in dem Ihr letztes Abo endet. Den Entwurf Ihres Fragebogens speichern wir bis zu 90 Tage nach Ihrer letzten Änderung und löschen ihn danach, wenn Sie nichts bestellt haben.
|
||||
|
||||
Vorgeschlagen (Ergänzung der Klammer und des Klicks; der Rest bleibt):
|
||||
|
||||
> Bestellen Sie eine Funktion (News-Pinnwand, Terminplaner, Öffnungszeiten live) oder schalten Sie Öffnungszeiten live im Wartungs-Abo ein, speichern wir als Nachweis die Texte und Fassungen, denen Sie zugestimmt haben, den Zeitpunkt Ihres Klicks auf „Zahlungspflichtig bestellen“ oder „Einschalten“, bei einer Bestellung die Stripe-Sitzung, IP-Adresse und Browserkennung sowie Ihr Konto und die angemeldete Person.
|
||||
|
||||
Diese Folgeänderung steht nicht in Spec 9 Nr. 3; sie ergibt sich daraus, dass der Gratisweg einen Nachweis speichert. Ob beim Einschalten ebenfalls IP-Adresse und Browserkennung gespeichert werden, ist vor der Übernahme gegen `speichereZustimmung` zu prüfen und der Satz entsprechend zu fassen.
|
||||
|
||||
## Offene Punkte für die Rechtsrunde
|
||||
|
||||
1. **Rolle bei Websites ohne Abo oder bei Fremdhosting.** Der Entwurf sagt wie 6c: Wir sind Auftragsverarbeiter der Kunden für die Besucherdaten (Server-Protokolle). Mit Abo deckt das Anlage 1 der Abo-Bedingungen, ohne Abo oder bei Fremdhosting besteht kein Auftragsverarbeitungsvertrag (gleiche Lücke wie bei der News-Pinnwand). Frage `ZQ1` in `zeiten-kanzlei-fragen.md`.
|
||||
2. **Löschung bei Abschaltung durch das Team** (Spec Abschnitt 5: keine automatische Löschung bei Grund `team`). Der Entwurf sagt „auf Verlangen, spätestens mit der Löschung des Kundenkontos“. Soll stattdessen eine feste Frist gelten (zum Beispiel 30 Tage wie beim Ende des Abos)? Entscheidung des Inhabers; dann auch Leistungsbeschreibung Nr. 9 Abs. 5 und das Programm (`abschalten.js`) anpassen.
|
||||
3. **Erinnerungs-E-Mail.** Rechtsgrundlage Art. 6 Abs. 1 lit. b DSGVO (Teil der Funktion). Es ist keine Werbung; ob sie wegen des Hinweises „bei Google auch eintragen“ als Werbung gilt, ist nicht anzunehmen, steht aber in der Kanzlei-Frage `ZQ6`.
|
||||
4. **Nachweis beim Einschalten** (Folgeänderung Abschnitt 7, siehe oben): Umfang der gespeicherten Daten gegen den Code abgleichen.
|
||||
5. **Kontaktdaten öffentlich.** Die Telefonnummer und Anschrift stehen im Seitencode der Kundenwebsite (Abschnitt 6d, zweiter Absatz). Das ist die Entscheidung des Kunden; die Leistungsbeschreibung (Nr. 6 Abs. 3) verlangt, nur veröffentlichungsfähige Angaben einzutragen.
|
||||
@@ -0,0 +1,43 @@
|
||||
# K02 und K03: Block `funktionen` mit Öffnungszeiten live (Entwurf)
|
||||
|
||||
Entwurf für die Rechtsrunde (Spec „Öffnungszeiten live“, Abschnitte 7 und 9; Task 33), Stand 05.10.2026. **Keine Codeänderung:** Der Block `funktionen` in `server/mail-templates/K02 Kuendigungsbestaetigung.html` und `K03 Eingang Kuendigung.html` ist Rechtstext mit Pins; dieser Entwurf schlägt den neuen Satz nur vor. Der Inhaber entscheidet, die Kanzlei sieht ihn mit der Mailrunde (Spec 9: Oberflächen- und Mailtexte). Erst danach werden die Vorlagen und die Pins geändert.
|
||||
|
||||
Beide Vorlagen sind Kündigungsbestätigungen an den Kunden und stehen in der Sie-Form. Der Block erscheint nur beim Wartungs-Abo (`server/routes/abo.js`: K02 mit `funktionen: isWartung(sub)`, K03 mit `funktionen: !storage && isWartung(sub)`), nicht bei Hosting-Abo und nicht bei der Kündigung von Zusatzspeicher.
|
||||
|
||||
## Heute (gepinnt)
|
||||
|
||||
Der Block besteht aus diesem Satzpaar, in beiden Vorlagen wörtlich gleich:
|
||||
|
||||
> Die Website-Analyse und der Terminplaner gehören zum Wartungs-Abo und enden mit ihm. Die Termindaten löschen wir 30 Tage nach dem Abschalten; vorher können Sie Ihre Termine im Kundenportal ansehen und exportieren.
|
||||
|
||||
Pins, die der Satz heute hat:
|
||||
|
||||
- `test/mailTemplates.test.js`, Test „K02, K03: Block funktionen nennt Terminplaner und Löschfrist, nur mit Block (in K03 nur innerhalb von abo)“: prüft das ganze Satzpaar mit einem regulären Ausdruck (beginnend mit „Die Website-Analyse und der Terminplaner gehören zum Wartungs-Abo und enden mit ihm.“ bis „… ansehen und exportieren.“), dass der Block ohne `funktionen` fehlt und dass „Terminplaner“ bei Hosting-Abo und bei Speicher-Kündigung nirgends steht.
|
||||
- `test/publicForms.test.js`, Konstante `FUNKTIONEN_SATZ` (nur der erste Satz, „… und enden mit ihm.“).
|
||||
- Die Vorlagen-Kommentare (Zeile 12 beider Vorlagen) beschreiben den Block als „Hinweis, dass Website-Analyse und Terminplaner mit dem Wartungs-Abo enden“.
|
||||
|
||||
## Vorschlag
|
||||
|
||||
Der heutige Satz bleibt unverändert der Anfang (damit beide Pins ohne Änderung grün bleiben); angehängt wird ein Satz für Öffnungszeiten live, der die 30 Tage zum Behalten und die 30 Tage Datenfrist nach Z11 und Leistungsbeschreibung Nr. 9 nennt:
|
||||
|
||||
> Die Website-Analyse und der Terminplaner gehören zum Wartungs-Abo und enden mit ihm. Die Termindaten löschen wir 30 Tage nach dem Abschalten; vorher können Sie Ihre Termine im Kundenportal ansehen und exportieren. Haben Sie Öffnungszeiten live im Wartungs-Abo eingeschaltet, läuft die Funktion nach dem Ende des Abos noch 30 Tage weiter; in dieser Zeit können Sie sie im Kundenportal für einmalig 30 € behalten. Behalten Sie sie nicht, schalten wir sie danach ab, auf Ihrer Website stehen dann wieder feste Öffnungszeiten, und Ihre Eingaben löschen wir 30 Tage später.
|
||||
|
||||
Warum so:
|
||||
|
||||
- Der Satz ist bedingt („Haben Sie … eingeschaltet“), weil der Block bei jeder Kündigung eines Wartungs-Abos erscheint, auch bei Kunden ohne die Funktion.
|
||||
- Wer die Funktion für 30 € bestellt hat, behält sie ohne Abo (Leistungsbeschreibung Nr. 9 Abs. 2 letzter Satz); für ihn gilt der Satz nicht, was die Bedingung „im Wartungs-Abo eingeschaltet“ ausdrückt. Ein eigener Satz dazu wäre in einer Kündigungsbestätigung verwirrend.
|
||||
- Die Einzelheiten (Frist mit Datum, Knopf „Behalten“) sagt K26 („Wartungs-Abo beendet“), sobald das Ende des Abos erkannt ist; K02 und K03 sagen nur, dass es das gibt.
|
||||
- „Abschalten“ bedeutet: Hinweis-Leiste, Kontakt-Leiste, Status und Google-Angaben verschwinden, die feste Ersatz-Tabelle der Website bleibt stehen (Leistungsbeschreibung Nr. 9 Abs. 4).
|
||||
|
||||
Im HTML ändert sich nur der Inhalt der Zelle zwischen `<!-- WENN funktionen -->` und `<!-- ENDE funktionen -->`; Aufbau und Blockkommentare bleiben. Die Kopfkommentare beider Vorlagen sagen dann „Website-Analyse, Terminplaner und Öffnungszeiten live“.
|
||||
|
||||
## Alternative
|
||||
|
||||
Ein eigener Block `zeiten` (nur für Kunden, bei denen die Funktion im Abo läuft): kein Text für Kunden ohne Funktion und die Zeile steht ohne „Haben Sie …“. Dafür braucht das Programm in `server/routes/abo.js` an beiden Stellen eine zusätzliche Abfrage der Funktion, beide Vorlagen einen neuen Block, und die Pins im Mail-Pin-Test (K01 bis K27) ändern sich. Nur sinnvoll, wenn der Inhaber den bedingten Satz nicht möchte.
|
||||
|
||||
## Beim Übernehmen
|
||||
|
||||
1. Vorlagen K02 und K03 ändern (Zelle des Blocks, Kopfkommentar).
|
||||
2. `test/mailTemplates.test.js` (K02/K03-Test): zusätzlicher Wortlaut-Pin für den neuen Satz; die bestehenden Prüfungen bleiben.
|
||||
3. Den Mail-Pin-Test (K01 bis K27, ebenfalls `test/mailTemplates.test.js`) und, falls sich der Block ändert, die Prüfung in `test/publicForms.test.js` nachziehen.
|
||||
4. Kanzlei-Frage `ZQ5` (Ende mit dem Abo, 30 Tage zum Behalten) in `zeiten-kanzlei-fragen.md` abwarten, weil sie den Inhalt des Satzes betrifft.
|
||||
@@ -0,0 +1,114 @@
|
||||
# Fragen an die Kanzlei: Öffnungszeiten live (⚖)
|
||||
|
||||
Stand 05.10.2026, Entwurf (Spec „Öffnungszeiten live“, Abschnitt 9, und Task 33). Dies sind die Fragen zu den Texten dieser Funktion; sie ergänzen das Kanzlei-Paket des Terminplaners (`docs/rechtliches/termine-kanzlei-paket.md`, Fragen Q1 bis Q26).
|
||||
|
||||
**Warum eine eigene Datei.** Das Kanzlei-Paket des Terminplaners liegt auf dem Zweig `feature/terminplaner-tp2` und ist in diesen Zweig (`feature/oeffnungszeiten`) noch nicht eingeflossen; ändern wir es hier, entsteht beim Zusammenführen ein Konflikt. Die Fragen tragen deshalb die Kennung `ZQ1` bis `ZQ10`. Beim Zusammenführen: als `Q27` bis `Q36` in Abschnitt 4 des Kanzlei-Pakets übernehmen, die Fundstellen in Abschnitt 5 ergänzen, die Fassung der Leistungsbeschreibung in Abschnitt 3.4 daneben stellen. In veröffentlichten Dateien stehen keine ⚖-Marker.
|
||||
|
||||
Jede Frage hat vier Teile wie im Paket des Terminplaners: **Frage** (was wir wissen möchten), **Fundstelle im Text** (wo der Wortlaut steht), **Unsere Annahme** (der Stand, mit dem wir arbeiten; bitte bestätigen oder korrigieren), **Alternative** (was wir stattdessen täten). Einordnungen wie „Annahme“ sind keine Rechtsurteile. Die Texte, um die es geht: `public/vertragsunterlagen/zeiten-leistungsbeschreibung.html` (Leistungsbeschreibung, im Folgenden „LB“), `docs/rechtliches/zeiten-datenschutz-6d-entwurf.md` (Abschnitt 6d), `docs/rechtliches/zeiten-k02-k03-entwurf.md` (K02/K03), `docs/funktionen/website-datenschutz-absatz.md` (Website-Absatz) und die Haken im Register (`server/config.js`, `features.zeiten`).
|
||||
|
||||
Reihenfolge der Rechtsrunde (gemeinsam mit TP2): gemeinsame Bausteine (TP2 Block F) → Leistungsbeschreibung → Datenschutz § 6d/§ 6 und Website-Absatz → Oberflächen- und Mailtexte → diese Fragen ans Kanzlei-Paket → Inhaber lädt die Fassungen in WOMS „Rechtliches“ hoch → Schalter `ZEITEN_ENABLED=on`.
|
||||
|
||||
### ZQ1: ⚖ AVV Funktionen per Haken?
|
||||
|
||||
**Frage.** Für Öffnungszeiten live verarbeitet WEBklar beim Aufruf einer Kundenwebsite Besucherdaten (IP-Adresse, Zeitpunkt, Browserangaben in Server-Protokollen, 14 Tage). Mit einem Wartungs-Abo deckt Anlage 1 der Abo-Bedingungen (Auftragsverarbeitung, Server-Protokolle der Website) das ab. Beim 30-€-Weg ohne Abo oder bei Websites, die woanders liegen, gibt es keinen Auftragsverarbeitungsvertrag; das ist dieselbe Lücke wie bei der News-Pinnwand. Soll sie mit einem zusätzlichen Haken „AVV Funktionen“ beim Bestellen und Einschalten geschlossen werden (wie der Haken für den AVV Terminplaner und den AVV Website-Analyse), und genügt ein gemeinsamer AVV „Funktionen“ für News-Pinnwand und Öffnungszeiten live? Oder ist WEBklar für die Protokolle des eigenen Servers ohnehin selbst Verantwortlicher, sodass kein AVV nötig ist?
|
||||
|
||||
**Fundstelle im Text.** Spec Abschnitt 9 Nr. 5; Register `features.zeiten.avv: null`; Abschnitt 6d Absatz 1 (Rolle als Auftragsverarbeiter) und Offener Punkt 1; Website-Absatz Variante B (`[Prüfen: AVV mit WEBklar erforderlich]`); LB Nr. 12 Abs. 2.
|
||||
|
||||
**Unsere Annahme.** Mit Abo genügt Anlage 1. Ohne Abo und bei Fremdhosting braucht es einen AVV; wir würden einen kurzen AVV „Funktionen“ per Haken einführen (Aufbau und Nummern wie AVV Terminplaner Nr. 4 bis 9, ohne Termindaten, Unterauftragsverarbeiter nur Hetzner).
|
||||
|
||||
**Alternative.** Nichts ändern, weil die IP-Adresse in Server-Protokollen beim Abruf eines Skripts von `project.webklar.com` in der Verantwortung des Betreibers dieses Servers liegt; oder Öffnungszeiten live nur mit Wartungs-Abo oder bei WEBklar-Hosting verkaufen; oder der Rahmen-AVV mit Anlage je Funktion (TP2 Spec 7.4 „Später“).
|
||||
|
||||
### ZQ2: Behalten-Zustimmung nutzt den Haken „Einbau beginnt“
|
||||
|
||||
**Frage.** Endet das Wartungs-Abo, kann der Kunde Öffnungszeiten live im Kundenportal für einmalig 30 € behalten (LB Nr. 9 Abs. 2). Der Weg „Behalten“ verlangt dieselbe Zustimmung wie die Neubestellung, also auch den zweiten Haken (`beginn`): „Ich verlange ausdrücklich, dass WEBklar vor Ablauf der Widerrufsfrist mit dem Einbau beginnt. Mir ist bekannt, dass mein Widerrufsrecht mit vollständiger Erbringung der Leistung erlischt (§ 356 Abs. 5 Nr. 2 BGB).“ Beim Behalten gibt es keinen Einbau; die Funktion läuft bereits, es entsteht kein neues Ticket. Passt der Haken dann? Ist das Behalten ein eigener Vertrag mit eigenem Widerrufsrecht für Verbraucher (Frist ab dem Klick), und was ist die „Leistung“, deren vollständige Erbringung das Widerrufsrecht erlöschen lässt (der Weiterbetrieb ohne Abo hat kein Ende)?
|
||||
|
||||
**Fundstelle im Text.** Register `features.zeiten.consent.items` (Haken `leistung` und `beginn`); Programm: `POST /api/funktionen/zeiten/behalten` (`pruefeZustimmung(feature, body)`: „die der Bestellung, beide Haken“), `server/services/zeiten/behalten.js`; Spec Abschnitt 3.3; LB Nr. 9 Abs. 2 und Nr. 11.
|
||||
|
||||
**Unsere Annahme.** Der Haken ist für das Behalten unschädlich, weil er nur das Verlangen des Beginns und die Kenntnis vom Erlöschen bestätigt. Der Wortlaut „mit dem Einbau“ ist aber ungenau; wir würden für das Behalten „mit der Leistung“ oder „mit dem Weiterbetrieb“ sagen. Der Widerruf eines Behalten-Vertrags führt zur Abschaltung (LB Nr. 11 Abs. 3).
|
||||
|
||||
**Alternative.** Ein eigener Haken für das Behalten („Ich verlange, dass die Funktion nach dem Ende meines Wartungs-Abos ohne Unterbrechung weiterläuft …“); oder kein Beginn-Haken, wenn für das Behalten kein Widerrufsrecht besteht (Kunde ist Unternehmer, Leistung sofort).
|
||||
|
||||
### ZQ3: Beginn-Haken der bezahlten Bestellung: Wertersatz und Fassung
|
||||
|
||||
**Frage.** Der Haken `beginn` der Bestellung für 30 € lautet: „Ich verlange ausdrücklich, dass WEBklar vor Ablauf der Widerrufsfrist mit dem Einbau beginnt. Mir ist bekannt, dass mein Widerrufsrecht mit vollständiger Erbringung der Leistung erlischt (§ 356 Abs. 5 Nr. 2 BGB).“ Er enthält nicht den Satz zum Wertersatz, den die Haken der News-Pinnwand und des Terminplaners tragen („Widerrufe ich vor der vollständigen Erbringung, schulde ich einen angemessenen Betrag für die bis dahin erbrachte Leistung (§ 357a Abs. 2 BGB).“, TP2 Q3 und Q5). Die LB nennt den Wertersatz in Nr. 11 Abs. 2. Genügt das, oder muss der Satz in den Haken? Und die Fassung der Zustimmung (`2026-10-04`) weicht von der Fassung der übrigen Funktionen-Unterlagen (`2026-10-05`) ab: Ist das für den Nachweis unschädlich?
|
||||
|
||||
**Fundstelle im Text.** Register `features.zeiten.consent` (Fassung `2026-10-04`, Haken `leistung` und `beginn`) und `consentFrei`; LB Nr. 11 Abs. 2; Widerrufsbelehrung der Funktionen (Hinweise zum Widerruf, Wertersatz als Anteil am Gesamtpreis); Test `test/zeitenRegister.test.js` pinnt den heutigen Wortlaut.
|
||||
|
||||
**Unsere Annahme.** Der Wertersatz-Satz gehört wie beim Terminplaner in den Haken (Frage Q3 des Terminplaners gilt entsprechend); der Haken wird vor dem Livegang um ihn ergänzt und die Fassung auf den Tag der Freigabe gesetzt.
|
||||
|
||||
**Alternative.** Den Satz nur in der Belehrung und der LB lassen (Haken unverändert).
|
||||
|
||||
### ZQ4: Haftung, ausgeschrieben für Kunden ohne Abo (LB Nr. 8)
|
||||
|
||||
**Frage.** Die LB schreibt die Haftung aus, statt auf § 13 der Abo-Bedingungen zu verweisen, weil Kunden ohne Wartungs-Abo die Abo-Bedingungen nicht haben. Nr. 8 Abs. 4 und 5 geben § 13 Abs. 1 und 2 wörtlich wieder, Abs. 6 den Satz zugunsten der Mitarbeiter und Beauftragten (§ 13 Abs. 5) und den Hinweis, dass der Abschnitt auch für Kunden ohne Abo gilt. Abs. 1 bis 3 sagen: Die Live-Anzeige ist ein Hilfsmittel, die Richtigkeit der Angaben liegt beim Kunden, Fehler in Feiertagsdaten und Softwarefehler beseitigt WEBklar nach Meldung, für entgangenen Gewinn und Folgeschäden (etwa weil ein Besucher wegen einer Fehlanzeige vor verschlossener Tür steht) gelten Abs. 4 und 5. Fragen: (a) Ist die Klausel gegenüber Verbrauchern mit § 309 Nr. 7 BGB vereinbar und hinreichend transparent? (b) Braucht es gegenüber Unternehmern einen Höchstbetrag? (c) Fehlen § 13 Abs. 3 (Datenverlust, Wiederherstellungsaufwand) und Abs. 4 (kein Schadensersatz nach § 536a Abs. 1 BGB für anfängliche Mängel) für Kunden ohne Abo? (d) Ist die Zusage „Fehler beseitigt WEBklar in angemessener Frist“ (Nr. 7 Abs. 1, Nr. 8 Abs. 2) für Kunden ohne Abo zu weit?
|
||||
|
||||
**Fundstelle im Text.** LB Nr. 7 und Nr. 8; Abo-Bedingungen § 13 (`public/abo-bedingungen.html`); der Test `test/zeitenRecht.test.js` hält den Wortlaut beider Klauseln gleich; TP2 Q8 (Haftung für verpasste Termine).
|
||||
|
||||
**Unsere Annahme.** Die Spiegelung von § 13 Abs. 1 und 2 genügt; ein Höchstbetrag ist nicht nötig, weil Abs. 2 den Schaden auf den vertragstypischen, vorhersehbaren begrenzt. Die Abs. 3 und 4 sind für diese Funktion nicht nötig (die Funktion speichert keine Daten Dritter; ein Schadensersatz für anfängliche Mängel ist bei einem Einmalpreis von 30 € kein praktisches Thema).
|
||||
|
||||
**Alternative.** Abs. 3 und 4 des § 13 ebenfalls ausschreiben; einen Höchstbetrag (zum Beispiel den Preis der Funktion oder den Jahresbetrag des Wartungs-Abos) gegenüber Unternehmern festlegen.
|
||||
|
||||
### ZQ5: Ende mit dem Abo: 30 Tage zum Behalten, Abschalten, Rückkehr (LB Nr. 9)
|
||||
|
||||
**Frage.** Wer Öffnungszeiten live im Wartungs-Abo ohne Aufpreis eingeschaltet hat, behält die Funktion nach dem Ende des Abos noch 30 Tage; in dieser Zeit kann er sie für einmalig 30 € behalten, sonst schaltet WEBklar sie ab (auf der Website bleibt die feste Ersatz-Tabelle), die Eingaben bleiben noch weitere 30 Tage und werden dann gelöscht; beginnt in dieser Zeit wieder ein Wartungs-Abo, läuft die Funktion mit den Eingaben weiter. Wer sie für 30 € bestellt hat, behält sie auch ohne Abo. Fragen: (a) Ist die Kopplung der Gratis-Funktion an das Abo mit dieser Frist zulässig, obwohl sie ein „freiwilliges Extra“ ist und die Abo-Bedingungen unverändert bleiben (Z19)? (b) Darf die Rückkehr bei neuem Abo ohne neue Zustimmung automatisch erfolgen? (c) Frist und Anker der Löschung (30 Tage nach dem Abschalten) und der Sicherungsüberhang von sieben Tagen. (d) Bei Abschaltung durch WEBklar auf Wunsch des Kunden oder aus anderem Grund als dem Ende des Abos gibt es im Programm keine automatische Löschung; die LB sagt „auf Verlangen, spätestens mit dem Löschen des Kundenkontos“ (Nr. 9 Abs. 5). Genügt das, oder braucht es eine feste Frist? (e) Gehört das einmalige Aktualisieren der Ersatz-Tabelle beim Abschalten (Nr. 9 Abs. 4) zu einer Leistung, die WEBklar auch nach dem Ende des Abos schuldet?
|
||||
|
||||
**Fundstelle im Text.** LB Nr. 9 Abs. 1 bis 5, Nr. 4 Abs. 3; Abschnitt 6d (Löschfristen) und Offener Punkt 2; K02/K03 (`zeiten-k02-k03-entwurf.md`); Programm: `server/services/zeiten/aboAbgleich.js` (30 Tage Frist zum Behalten, 30 Tage Datenfrist), `abschalten.js`; Spec Z11 und Abschnitt 5; Parallele: TP2 Q7 und Q9 (Terminplaner endet mit dem Abo).
|
||||
|
||||
**Unsere Annahme.** Ja zu (a) bis (c): Der Kunde hat die Funktion nur durch das Abo ohne Aufpreis; die Frist zum Behalten und die Datenfrist sind angemessen, die Rückkehr stellt den früheren Stand wieder her. Zu (d) genügt „auf Verlangen“, weil die Eingaben Betriebsdaten sind und der Kunde die Löschung jederzeit verlangen kann. Zu (e) ja, weil es zum Einbau gehört, den der Preis enthält (Z20).
|
||||
|
||||
**Alternative.** Löschung auch bei Abschaltung durch das Team nach 30 Tagen (das Programm müsste dafür angepasst werden); keine Frist zum Behalten (Abschaltung mit dem Ende des Abos); Rückkehr nur auf Antrag.
|
||||
|
||||
### ZQ6: Mails zur Funktion: Erinnerung vor Feiertagen und Zustandsmails
|
||||
|
||||
**Frage.** WEBklar sendet dem Kunden E-Mails: K24 (eingeschaltet), K25 (Erinnerung 1 bis 7 Tage vor einem Feiertag oder einem Prüftag: „Stimmen Ihre Zeiten an diesen Tagen?“, mit einem Knopf zum Editor und dem Hinweis, die Zeiten auch bei Google einzutragen), K26 (Wartungs-Abo beendet, Frist, „für einmalig 30 € behalten“), K27 (abgeschaltet). Sind das Vertragsmails (Art. 6 Abs. 1 lit. b DSGVO) ohne Einwilligung, oder wirkt K25 wegen des Hinweises „bei Google auch eintragen“ oder des Verweises auf die Funktion als Werbung (§ 7 UWG, Art. 6 Abs. 1 lit. f DSGVO mit Widerspruchsmöglichkeit)? Muss K25 einen Hinweis zum Abbestellen tragen? Die Erinnerung lässt sich im Programm nicht abschalten; der Kunde kann nur die Funktion abschalten lassen.
|
||||
|
||||
**Fundstelle im Text.** Abschnitt 6d Absatz 3; LB Nr. 2 Abs. 2; Mailvorlagen K24 bis K27 (`server/mail-templates/`), Spec Abschnitt 7 (K25: eine Mail für alle neuen Tage, Merker, 08:00 bis 20:00 Uhr Berliner Zeit, kein BCC ans Team).
|
||||
|
||||
**Unsere Annahme.** Die Erinnerung ist Teil der vertraglichen Leistung (LB Nr. 2 Abs. 2) und deshalb eine Vertragsmail ohne Einwilligung; der Hinweis auf Google dient der Durchführung (die Zeiten dort aktuell zu halten), nicht der Werbung.
|
||||
|
||||
**Alternative.** Hinweis auf Google aus K25 streichen; einen Abbestell-Link („Erinnerungen ausschalten“) mit Einstellung im Editor vorsehen.
|
||||
|
||||
### ZQ7: Einstellung ohne Grund bei Unternehmern im Gratisweg (LB Nr. 10 Abs. 4)
|
||||
|
||||
**Frage.** Die LB folgt in Nr. 10 der Einstellungsregel der Website-Analyse (Nr. 8 dort): Beenden oder Einstellen mit vier Wochen oder drei Monaten nur bei Rechtslage oder nicht behebbarer Sicherheitslücke (Abs. 2), zusätzlich nach Abs. 4 **ohne** besonderen Grund mit drei Monaten gegenüber Unternehmern, die die Funktion im Wartungs-Abo ohne Aufpreis haben; der Kunde kann das Abo dann innerhalb von 30 Tagen zum Einstellungszeitpunkt kündigen, auch vor Ablauf der Mindestlaufzeit. Wer die Funktion für 30 € bestellt hat, kann sie nur aus den Gründen von Abs. 2 verlieren (Abs. 5). Fragen: (a) Ist die Einstellung ohne Grund für ein kostenloses Zusatzangebot zulässig, auch wenn die Funktion im Abo „enthalten“ beworben wird (Z4)? (b) Ändert das Sonderkündigungsrecht in Abs. 4 die Abo-Bedingungen so, dass das Verfahren nach § 15 nötig wäre (Z19: freiwilliges Extra, keine Änderung der Abo-Bedingungen)? (c) Braucht es bei Einstellung einer gekauften Funktion durch WEBklar eine (anteilige) Erstattung des Preises (vgl. TP2 Q8)?
|
||||
|
||||
**Fundstelle im Text.** LB Nr. 10 Abs. 1 bis 5; Abo-Bedingungen §§ 3 Abs. 7, 8, 15; LB der Website-Analyse Nr. 8 (`docs/rechtliches/leistungsbeschreibung-website-analyse.md`); Spec Abschnitt 9 Nr. 1 („Einstellung/Änderung nach Vorbild Analyse Nr. 8“).
|
||||
|
||||
**Unsere Annahme.** Ja zu (a) und (b) wie bei der Analyse: Das Sonderkündigungsrecht stellt den Kunden besser; die Abo-Bedingungen bleiben unverändert. Zu (c): Bei Einstellung aus den Gründen von Abs. 2 erstattet WEBklar nichts, weil die Funktion bis dahin lief; die Rechte aus § 327r BGB bleiben.
|
||||
|
||||
**Alternative.** Abs. 4 streichen (Einstellung nur aus Gründen); bei gekauften Funktionen anteilig erstatten.
|
||||
|
||||
### ZQ8: Website-Absatz: WhatsApp und Google Maps erst nach Klick, Haken als Nachweis
|
||||
|
||||
**Frage.** (a) Der Absatz „Öffnungszeiten auf dieser Website“ (Variante A und B) sagt, dass die Knöpfe „Route“ und „WhatsApp“ einfache Links sind, dass Google Maps und WhatsApp erst nach dem Klick geöffnet werden und dass vorher nichts an Google oder Meta übertragen wird; das Skript setzt keine Cookies und speichert nichts im Browser. Genügt das, oder braucht es für diese Links eine Einwilligung (§ 25 TDDDG, Art. 6 Abs. 1 lit. a DSGVO)? (b) Der Kunde bestätigt im Portal mit einem Haken, dass er den „WhatsApp-Hinweis“ (Baustein WhatsApp) in seine Datenschutzerklärung aufgenommen hat; der Zeitpunkt wird gespeichert, Mitarbeiter setzen den Haken in der Kundenansicht nicht. Reicht der Haken als Nachweis? (c) Wenn WEBklar den Absatz beim Einbau in die Datenschutzerklärung einfügt (LB Nr. 6 Abs. 4), ist das Einfügen eines Mustertextes eine Rechtsdienstleistung (§ 2 RDG)? Das ist dieselbe Regel wie TP2 Q18 und soll für alle Funktionen gleich bleiben. (d) Die Angaben im Seitencode (strukturierte Daten zu den Öffnungszeiten) enthalten keine Daten von Besuchern; ist ein Satz dazu nötig?
|
||||
|
||||
**Fundstelle im Text.** `docs/funktionen/website-datenschutz-absatz.md` (Variante A und B, Bausteine Route und WhatsApp); LB Nr. 2 Abs. 4, Nr. 6 Abs. 3 und 4; Abschnitt 6d Absatz 4; Programm: `public/embed/zeiten.js` (keine Cookies, kein Storage, keine Anfragen an Dritte; Links erst beim Klick), Spec Z23 (Route öffnet Google Maps erst beim Klick).
|
||||
|
||||
**Unsere Annahme.** (a) Ein Link, den der Besucher anklickt, ist keine Übertragung durch WEBklar oder den Kunden vor dem Klick; eine Einwilligung ist nicht nötig. (b) Der Haken genügt als Nachweis. (c) Wie TP2 Q18: Technik und Mustertext, keine Rechtsdienstleistung im Einzelfall. (d) Ein Satz genügt, wie im Entwurf.
|
||||
|
||||
**Alternative.** Vor dem ersten Klick einen Hinweis einblenden („Sie verlassen diese Website“); die Schaltfläche „WhatsApp“ nur auf ausdrücklichen Wunsch einschalten.
|
||||
|
||||
### ZQ9: Vertragsrahmen ohne Abo (LB Nr. 1 Abs. 3)
|
||||
|
||||
**Frage.** Für Kunden ohne Wartungs-Abo gilt die LB „allein“. Sie regelt Leistung, Preis, Haftung, Ende, Einstellung und Widerruf, verweist für den Widerruf auf die Belehrung der Funktionen und für Datenschutz auf die Datenschutzerklärung. Fehlen Regeln, die sonst die Abo-Bedingungen liefern (Zahlung und Zahlungsverzug, Mängelrechte für digitale Dienstleistungen §§ 327 ff. BGB, Verjährung, Gerichtsstand, Textform, Änderungen der Bedingungen)? Sollen die Abo-Bedingungen auch für Kunden ohne Abo teilweise vereinbart werden (Verweis auf bestimmte Paragrafen), oder reichen die gesetzlichen Regeln?
|
||||
|
||||
**Fundstelle im Text.** LB Nr. 1 Abs. 3, Nr. 4 (Zahlung über Stripe), Nr. 7 Abs. 1 (Mängel in angemessener Frist), Nr. 8 (Haftung ausgeschrieben); Spec Abschnitt 9 Nr. 1 und Z19.
|
||||
|
||||
**Unsere Annahme.** Die gesetzlichen Regeln genügen für eine Einmalleistung von 30 €; die Haftung ist wegen der AGB-Kontrolle ausgeschrieben.
|
||||
|
||||
**Alternative.** Ausgewählte Paragrafen der Abo-Bedingungen (Zahlung, Mängel, Textform, Änderungen) in die LB aufnehmen oder auf sie verweisen.
|
||||
|
||||
### ZQ10: Hinweis zu Preisangaben in der Hinweis-Leiste
|
||||
|
||||
**Frage.** Der Editor zeigt beim Anlegen eines Hinweises, dessen Text „%“ oder „statt“ enthält, einen Hinweis zu Preisangaben (Endpreise, bei Waren der niedrigste Preis der letzten 30 Tage). LB Nr. 6 Abs. 2 verpflichtet den Kunden, Preise und Rabatte in Hinweisen rechtmäßig anzugeben, zum Beispiel als Endpreise und „bei Preisermäßigungen für Waren, mit dem niedrigsten Preis der letzten 30 Tage“; Nr. 12 Abs. 1 schließt eine Rechtsberatung aus. Sind diese Aussagen zutreffend und ausreichend, insbesondere die Beschränkung der 30-Tage-Regel auf Waren (Preisangabenverordnung) und die Abgrenzung, dass die Angaben des Kunden nicht von WEBklar geprüft werden (Nr. 8 Abs. 1)? Braucht es einen Hinweis zur Preisangabe auch für Leistungen, die Dienstleistungen sind?
|
||||
|
||||
**Fundstelle im Text.** LB Nr. 6 Abs. 2, Nr. 8 Abs. 1, Nr. 12 Abs. 1; Spec Abschnitt 3.4 Nr. 4 (Hinweis-Leiste); die genaue Zeile im Portal-Editor steht in `public/zeiten-leisten.js`.
|
||||
|
||||
**Unsere Annahme.** Der Hinweis im Editor und die Pflicht in der LB reichen als Warnung; die Prüfung bleibt beim Kunden. Die Zitate zur Preisangabenverordnung werden mit der Freigabe geprüft.
|
||||
|
||||
**Alternative.** Keinen Hinweis im Editor, nur die Pflicht in der LB; oder den Hinweis auf Preisermäßigungen insgesamt ausweiten.
|
||||
|
||||
## Nicht Teil der Kanzleiprüfung
|
||||
|
||||
- Der Hinweis in der Hinweis-Leiste („Aktion“ verlangt ein Enddatum, Texte höchstens 140 Zeichen) ist Oberfläche, keine Rechtsaussage.
|
||||
- Technische Beobachtung beim Schreiben der Texte (kein Kanzlei-Thema): Die öffentliche Widerrufsseite `widerrufen.html` kennt die Auswahl „Terminplaner (Einrichtung)“, aber keine „Öffnungszeiten live“; ein Verbraucher, der sie für 30 € bestellt hat, wählt dort heute „Funktion (z. B. News-Pinnwand)“, und die Abwicklung ordnet das der News-Pinnwand zu (`FEATURE_OF_ORDER` in `server/routes/abo.js`). Das ist vor dem Livegang im Code zu ergänzen.
|
||||
@@ -0,0 +1,150 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<!-- ENTWURF – vor Livegang Rechtsrunde -->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Leistungsbeschreibung Öffnungszeiten live – WEBklar</title>
|
||||
<style>
|
||||
:root { color-scheme: light; }
|
||||
* { box-sizing: border-box; }
|
||||
body { margin: 0; background: #f7f6f3; color: #1d1d1f; font: 16px/1.6 system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; }
|
||||
main { max-width: 780px; margin: 0 auto; padding: 44px 20px 80px; }
|
||||
.brand { font-weight: 800; letter-spacing: 0.12em; font-size: 13px; color: #6d1fd1; }
|
||||
h1 { font-size: 30px; line-height: 1.2; margin: 6px 0 6px; }
|
||||
h2 { font-size: 20px; margin: 36px 0 10px; padding-top: 14px; border-top: 1px solid #e3e0da; }
|
||||
p, li { margin: 6px 0; }
|
||||
ul { padding-left: 24px; }
|
||||
.meta { color: #6b6b70; font-size: 14px; }
|
||||
.box { background: #fff; border: 1px solid #e3e0da; border-radius: 14px; padding: 16px 20px; margin: 18px 0; }
|
||||
table { border-collapse: collapse; width: 100%; font-size: 15px; }
|
||||
td { padding: 6px 10px 6px 0; border-bottom: 1px solid #eee; vertical-align: top; text-align: left; }
|
||||
td:first-child { font-weight: 600; width: 30%; }
|
||||
a { color: #6d1fd1; }
|
||||
nav.top { font-size: 14px; margin-bottom: 22px; }
|
||||
@media print {
|
||||
@page { size: A4; margin: 18mm 17mm 18mm; }
|
||||
body { background: #fff; font-size: 10pt; line-height: 1.45; }
|
||||
main { max-width: none; padding: 0; }
|
||||
nav.top { display: none; }
|
||||
h1 { font-size: 20pt; }
|
||||
h2 { font-size: 13pt; margin-top: 16pt; break-after: avoid; }
|
||||
a { color: inherit; text-decoration: none; }
|
||||
.box, tr { break-inside: avoid; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<nav class="top"><a href="/dashboard.html">← Zurück zum Kundenportal</a> · <a href="/abo-bedingungen.html">Abo-Bedingungen</a> · <a href="/datenschutz.html#zeiten">Datenschutz</a></nav>
|
||||
|
||||
<div class="brand">WEBKLAR</div>
|
||||
<h1>Leistungsbeschreibung: Öffnungszeiten live</h1>
|
||||
<p class="meta">Stand 04.10.2026</p>
|
||||
|
||||
<div class="box">
|
||||
<strong>Das Wichtigste auf einen Blick</strong>
|
||||
<table>
|
||||
<tr><td>Leistung</td><td>Sie tragen Ihre Öffnungszeiten einmal im Kundenportal ein. Ihre Website zeigt sie überall gleich an, als Tabelle und als Live-Status („Jetzt geöffnet · schließt um 18 Uhr“). Feiertage Ihres Bundeslands sind eingerechnet. Dazu kommen eine Hinweis-Leiste, auf dem Handy eine Kontakt-Leiste und Angaben für Suchmaschinen im Seitencode.</td></tr>
|
||||
<tr><td>Preis</td><td>30 € einmalig (ohne Umsatzsteuer gemäß § 19 UStG); mit laufendem Wartungs-Abo ohne Aufpreis</td></tr>
|
||||
<tr><td>Einbau</td><td>ein Einbau je Website ist enthalten; erfahrungsgemäß meist innerhalb weniger Tage (keine verbindliche Zusage)</td></tr>
|
||||
<tr><td>Ende</td><td>Haben Sie die Funktion im Wartungs-Abo eingeschaltet, läuft sie nach dem Ende des Abos noch 30 Tage weiter; Sie können sie dann für einmalig 30 € behalten (Nr. 9)</td></tr>
|
||||
<tr><td>Widerruf</td><td>Sind Sie Verbraucher, können Sie die Bestellung für 30 € innerhalb von 14 Tagen widerrufen; das Recht kann nach § 356 Abs. 5 Nr. 2 BGB erlöschen (Nr. 11)</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<h2 id="geltung">1. Geltung</h2>
|
||||
<p>(1) Diese Leistungsbeschreibung regelt die Funktion „Öffnungszeiten live“. Sie gilt für Kunden mit Wartungs-Abo, die die Funktion im Kundenportal einschalten, und für Kunden ohne Wartungs-Abo, die sie für 30 € bestellen. Vereinbart wird sie, wenn der Kunde im Kundenportal den Haken für diese Leistungsbeschreibung setzt und auf „Einschalten“ oder auf „Zahlungspflichtig bestellen“ klickt.</p>
|
||||
<p>(2) Die Funktion ist ein freiwilliges Zusatzangebot von WEBklar. Im Wartungs-Abo ist sie ohne Aufpreis enthalten; sie gehört nicht zum Leistungsumfang der Abo-Bedingungen. Leistungsbeschreibung B Nr. 3 der Abo-Bedingungen („Nicht enthalten“: neue Funktionen und Rechtstexte) gilt dafür nicht: Die Funktion und der Datenschutz-Absatz nach Nr. 6 Abs. 4 sind Teil dieser Leistung. Der Preis des Wartungs-Abos ändert sich nicht.</p>
|
||||
<p>(3) Im Übrigen gelten die Abo-Bedingungen, soweit der Kunde ein Abo hat. Bei Widersprüchen geht für Öffnungszeiten live diese Leistungsbeschreibung vor. Für Kunden ohne Wartungs-Abo gilt sie allein; deshalb stehen die Haftungsregeln in Nr. 8 ausgeschrieben da.</p>
|
||||
|
||||
<h2 id="leistung">2. Leistung</h2>
|
||||
<p>(1) Der Kunde trägt seine Öffnungszeiten einmal im Kundenportal ein: bis zu fünf benannte Zeitpläne, je Wochentag bis zu zwei Zeitfenster („rund um die Uhr“ und „nach Vereinbarung“ sind möglich), Abweichungen an Feiertagen, Betriebsurlaub und Sondertage. Die Website zeigt diese Zeiten überall gleich an und berechnet daraus den Live-Status („Jetzt geöffnet · schließt um 18 Uhr“).</p>
|
||||
<p>(2) An den Stellen der Website, die nach Nr. 3 Abs. 2 dafür vorgesehen sind, erscheinen die Zeiten als Tabelle, als Status oder als Kurzanzeige („Geöffnet“, „Geschlossen“, „Nach Vereinbarung“). Dazu gehören, soweit der Kunde sie einrichtet:</p>
|
||||
<ul>
|
||||
<li>eine Hinweis-Leiste am Seitenanfang für befristete Hinweise (Info, Urlaub, Aktion, Wichtig) mit Beginn und Ende und auf Wunsch einem Knopf (Link oder Anruf),</li>
|
||||
<li>auf dem Handy eine Kontakt-Leiste am unteren Rand mit den Knöpfen „Anrufen“, „Route“ und, wenn der Kunde sie einrichtet, „WhatsApp“,</li>
|
||||
<li>die Öffnungszeiten im Seitencode in maschinenlesbarer Form (strukturierte Daten), die Suchmaschinen lesen können,</li>
|
||||
<li>eine E-Mail vor Feiertagen, die den Kunden bittet, seine Zeiten für diese Tage zu prüfen, und</li>
|
||||
<li>im Kundenportal der Knopf „Für Google kopieren“, der die Zeiten in der Reihenfolge von Google in die Zwischenablage legt, damit der Kunde sie in sein Google-Unternehmensprofil einträgt.</li>
|
||||
</ul>
|
||||
<p>(3) Die Funktion rechnet die Feiertage des Bundeslands ein, das der Kunde wählt; je Feiertag kann der Kunde eigene Zeiten eintragen. Feiertage, die nur örtlich gelten, sowie Heiligabend und Silvester zeigt das Kundenportal als „bitte prüfen“. Alle Zeiten gelten in der Zeitzone Europe/Berlin.</p>
|
||||
<p>(4) Beim Aufruf der Website lädt der Browser des Besuchers ein kleines Programm und die Öffnungszeiten von project.webklar.com, einem Server der WEBklar GbR bei der Hetzner Online GmbH in Falkenstein (Deutschland). Dabei werden keine Cookies gesetzt, und es wird nichts im Browser gespeichert. Das Programm lädt nichts von anderen Anbietern. WhatsApp und Google Maps werden erst nach dem Klick des Besuchers auf „WhatsApp“ oder „Route“ geöffnet; vorher werden keine Daten an diese Dienste übertragen.</p>
|
||||
<p>(5) Das Team von WEBklar kann die Hinweis-Leiste und die Kontakt-Leiste für eine Website abschalten, etwa wenn die Seite schon eine eigene Anruf-Leiste hat. Auf Wunsch des Kunden können Mitarbeiter die Öffnungszeiten in der Kundenansicht für ihn ändern; ein Anspruch darauf besteht nicht (Nr. 5 Abs. 1). Den Haken für den WhatsApp-Hinweis (Nr. 6 Abs. 3) setzen sie nicht.</p>
|
||||
|
||||
<h2 id="einbau">3. Einbau und Freischaltung</h2>
|
||||
<p>(1) Der Einbau des Programms erfolgt automatisch nur bei Websites, die WEBklar hostet und über den eigenen Veröffentlichungsablauf bereitstellt: Beim nächsten Veröffentlichen setzt WEBklar die Einbau-Zeile in jede Seite ein, und Hinweis-Leiste, Kontakt-Leiste und Angaben für Suchmaschinen erscheinen ohne weiteren Aufwand. In allen anderen Fällen, etwa bei einer Website bei einem anderen Anbieter, setzt das Team die Einbau-Zeile von Hand ein. Hat WEBklar keinen Zugriff auf die Website, erhält der Kunde die Zeile und setzt sie selbst oder durch seinen Anbieter ein.</p>
|
||||
<p>(2) Die Tabelle, den Status und die Kurzanzeige setzt das Team an den Stellen ein, an denen auf der Website feste Öffnungszeiten stehen oder stehen sollen, und ersetzt dort die fest eingetragenen Zeiten. In jedem dieser Platzhalter bleibt eine statische Ersatz-Tabelle stehen. Sie erscheint, wenn der Browser kein JavaScript ausführt, wenn die Daten nicht erreichbar oder älter als 24 Stunden sind und nachdem die Funktion abgeschaltet wurde. Sie zeigt den Wochenplan zum Zeitpunkt des Einbaus.</p>
|
||||
<p>(3) WEBklar schaltet die Funktion frei, sobald der Einbau abgeschlossen ist und der Kunde Öffnungszeiten eingetragen hat (mindestens ein geöffneter Tag im Hauptplan). Solange der Kunde keine Zeiten eingetragen hat, kann das Team die Tabelle nicht einbauen.</p>
|
||||
<p>(4) Der Einbau dauert erfahrungsgemäß meist wenige Tage. Das ist eine Einschätzung und keine verbindliche Zusage eines Termins.</p>
|
||||
|
||||
<h2 id="preis">4. Preis</h2>
|
||||
<p>(1) Öffnungszeiten live kostet 30 € einmalig je Website-Projekt (ohne Umsatzsteuer gemäß § 19 UStG). Der Kunde bezahlt bei der Bestellung im Kundenportal über Stripe und erhält eine Rechnung.</p>
|
||||
<p>(2) Hat der Kunde ein laufendes Wartungs-Abo, ist die Funktion ohne Aufpreis enthalten; er schaltet sie dann im Kundenportal ein, statt sie zu bestellen. Der Einbau wird weder auf das Stundenkontingent angerechnet noch damit bezahlt. Der Preis des Wartungs-Abos ändert sich nicht.</p>
|
||||
<p>(3) Im Preis ist ein Einbau je Website enthalten, einschließlich der Aktualisierung der Ersatz-Tabelle beim Abschalten (Nr. 9 Abs. 4). Weitere Wünsche, etwa zusätzliche Stellen für die Anzeige, eine Sondergestaltung oder andere Änderungen an der Website, gehören nicht dazu. WEBklar führt solche Sonderwünsche nach Angebot aus, für Kunden mit Wartungs-Abo auf Wunsch nach Stunden (§ 4 der Abo-Bedingungen).</p>
|
||||
<p>(4) Solange ein Wartungs-Abo läuft und nicht gekündigt ist, nimmt WEBklar für Öffnungszeiten live keine Zahlung an; der Kunde schaltet die Funktion dann ohne Aufpreis ein.</p>
|
||||
|
||||
<h2 id="nicht-enthalten">5. Nicht enthalten</h2>
|
||||
<p>(1) Nicht enthalten sind:</p>
|
||||
<ul>
|
||||
<li>die Übertragung der Öffnungszeiten zu Google: WEBklar schreibt nichts automatisch in das Google-Unternehmensprofil des Kunden; der Kunde trägt die Zeiten dort selbst ein, der Knopf „Für Google kopieren“ und die Erinnerung vor Feiertagen helfen dabei,</li>
|
||||
<li>Feiertage einer Gemeinde und andere örtliche Besonderheiten (zum Beispiel Brückentage oder Stadtfeste); der Kunde trägt sie als Ausnahmen selbst ein,</li>
|
||||
<li>eine Zusage, dass Suchmaschinen die Angaben im Seitencode übernehmen oder wie sie sie anzeigen,</li>
|
||||
<li>die Pflege der Zeiten und der Hinweise durch WEBklar und</li>
|
||||
<li>Änderungen an der Website, die über den Einbau nach Nr. 3 hinausgehen (Nr. 4 Abs. 3).</li>
|
||||
</ul>
|
||||
|
||||
<h2 id="pflichten">6. Pflichten des Kunden</h2>
|
||||
<p>(1) Der Kunde prüft seine Zeiten, Feiertage und Hinweistexte vor dem Speichern und hält sie aktuell. Er wählt das richtige Bundesland und trägt örtliche Feiertage und Abweichungen selbst ein.</p>
|
||||
<p>(2) Preise und Rabatte in Hinweisen gibt er rechtmäßig an, zum Beispiel als Endpreise und, bei Preisermäßigungen für Waren, mit dem niedrigsten Preis der letzten 30 Tage. Hinweise zu Preisen sind Angaben des Kunden, nicht von WEBklar.</p>
|
||||
<p>(3) Die Kontaktangaben (Telefonnummer, Anschrift, WhatsApp-Nummer) sind auf der Website öffentlich sichtbar. Der Kunde trägt nur Angaben ein, die er veröffentlichen darf. Die WhatsApp-Schaltfläche schaltet er erst ein, nachdem er den WhatsApp-Hinweis in seine Datenschutzerklärung aufgenommen hat, und bestätigt das im Kundenportal mit einem Haken.</p>
|
||||
<p>(4) Liegt die Datenschutzerklärung in der von WEBklar betreuten Website, fügt WEBklar beim Einbau dort den Datenschutz-Absatz zu Öffnungszeiten live ein; das gehört zum Einbau und wird nicht auf das Stundenkontingent angerechnet. Liegt sie woanders, etwa bei einem externen Generator, erhält der Kunde den Absatz als Textbaustein und fügt ihn selbst ein. Für den übrigen Inhalt seiner Datenschutzerklärung bleibt der Kunde verantwortlich (für Kunden mit Wartungs-Abo § 6 Abs. 3 der Abo-Bedingungen). Fällt WEBklar beim Einfügen ein offensichtlicher Widerspruch auf, weist WEBklar den Kunden darauf hin; eine Pflicht zur Prüfung der Datenschutzerklärung entsteht daraus nicht.</p>
|
||||
<p>(5) Der Kunde prüft nach dem Einbau, ob die Anzeige auf seiner Website stimmt, und meldet Fehler unverzüglich im Chat des Kundenportals.</p>
|
||||
<p>(6) Er entfernt oder verändert das Programm von WEBklar nicht. Wünscht er die Hinweis-Leiste oder die Kontakt-Leiste nicht, teilt er das dem Team mit (Nr. 2 Abs. 5).</p>
|
||||
|
||||
<h2 id="verfuegbarkeit">7. Verfügbarkeit und Aktualität</h2>
|
||||
<p>(1) Für Kunden mit Wartungs-Abo gilt das Verfügbarkeitsziel nach § 5 der Abo-Bedingungen für die Website, nicht für die Live-Anzeige. Störungen der Anzeige behebt WEBklar in angemessener Frist.</p>
|
||||
<p>(2) Ist die Anzeige nicht erreichbar oder sind die Daten älter als 24 Stunden, bleibt auf der Website die Ersatz-Tabelle stehen (Nr. 3 Abs. 2); Hinweis-Leiste und Kontakt-Leiste erscheinen dann nicht.</p>
|
||||
<p>(3) Änderungen, die der Kunde im Kundenportal speichert, erscheinen nicht sofort auf der Website, sondern mit einer Verzögerung von einigen Minuten (bis zu etwa zehn Minuten). Der Status folgt der Zeitzone Europe/Berlin; geht die Uhr des Geräts des Besuchers um mehr als fünf Minuten falsch, rechnet das Programm mit der Serverzeit von WEBklar.</p>
|
||||
|
||||
<h2 id="haftung">8. Haftung</h2>
|
||||
<p>(1) Die Live-Anzeige ist ein Hilfsmittel. Die Richtigkeit der Zeiten, Ausnahmen, Hinweise und Kontaktangaben liegt beim Kunden: WEBklar zeigt die Angaben des Kunden an und prüft sie nicht auf Richtigkeit.</p>
|
||||
<p>(2) Die Feiertage stammen aus einer Feiertagsliste für das gewählte Bundesland. WEBklar beseitigt Fehler in den Feiertagsdaten und andere Softwarefehler nach Meldung in angemessener Frist (Nr. 7 Abs. 1). Für Schäden daraus gelten die Absätze 3 bis 5.</p>
|
||||
<p>(3) Für entgangenen Gewinn und Folgeschäden, etwa weil ein Besucher wegen einer falschen oder fehlenden Angabe vor verschlossener Tür steht, haftet WEBklar nur im Rahmen der Absätze 4 und 5.</p>
|
||||
<p>(4) WEBklar haftet unbeschränkt bei Vorsatz und grober Fahrlässigkeit, bei Verletzung von Leben, Körper oder Gesundheit, nach dem Produkthaftungsgesetz und soweit WEBklar eine Garantie übernommen hat.</p>
|
||||
<p>(5) Bei leicht fahrlässiger Verletzung einer wesentlichen Vertragspflicht haftet WEBklar nur für den vertragstypischen, vorhersehbaren Schaden. Wesentlich sind Pflichten, deren Erfüllung die ordnungsgemäße Durchführung des Vertrags überhaupt erst ermöglicht und auf deren Einhaltung der Kunde regelmäßig vertrauen darf. Im Übrigen ist die Haftung für leichte Fahrlässigkeit ausgeschlossen.</p>
|
||||
<p>(6) Diese Regeln gelten auch zugunsten der Mitarbeiter und Beauftragten von WEBklar. Dieser Abschnitt gilt auch für Kunden ohne Wartungs-Abo; bei Kunden mit Wartungs-Abo entspricht er § 13 Abs. 1 und 2 der Abo-Bedingungen.</p>
|
||||
<p>(7) Gesetzliche Rechte des Kunden, die sich nicht ausschließen lassen, bleiben unberührt. Der Haftungsrahmen des Kunden gegenüber seinen Besuchern und Kunden bleibt ebenfalls unberührt.</p>
|
||||
|
||||
<h2 id="ende">9. Ende der Leistung</h2>
|
||||
<p>(1) Auf Wunsch des Kunden in Textform (im Chat des Kundenportals oder per E-Mail) schaltet WEBklar Öffnungszeiten live jederzeit ab. Eine Erstattung des Preises folgt daraus nicht; für den Widerruf gilt Nr. 11.</p>
|
||||
<p>(2) Hat der Kunde die Funktion im Wartungs-Abo ohne Aufpreis eingeschaltet, endet sie mit dem Wartungs-Abo, aber nicht sofort: Endet das Wartungs-Abo (auch bei einem Wechsel ins Hosting-Abo und beim Widerruf eines Wechsels ins Wartungs-Abo), informiert WEBklar den Kunden per E-Mail, und die Funktion läuft noch 30 Tage weiter. In dieser Zeit kann der Kunde sie im Kundenportal für einmalig 30 € behalten; er behält sie dann unabhängig vom Abo. Ohne Zahlung schaltet WEBklar sie ab, sobald diese 30 Tage vorbei sind. Hat der Kunde die Funktion für 30 € bestellt, endet sie nicht mit einem Abo.</p>
|
||||
<p>(3) Sobald ein neues Wartungs-Abo beginnt, solange die Eingaben des Kunden noch gespeichert sind (Absatz 4), läuft die Funktion mit den vorhandenen Eingaben weiter oder WEBklar schaltet sie ohne weitere Kosten wieder ein.</p>
|
||||
<p>(4) Mit dem Abschalten verschwinden Hinweis-Leiste, Kontakt-Leiste, Live-Status und die Angaben für Suchmaschinen; auf der Website steht wieder die Ersatz-Tabelle (Nr. 3 Abs. 2). Hat sich der Wochenplan seit dem Einbau geändert, aktualisiert WEBklar die Ersatz-Tabelle einmal beim Abschalten; das gehört zum Einbau nach Nr. 4 Abs. 3. Hat WEBklar keinen Zugriff auf die Website, erhält der Kunde den aktuellen Text und setzt ihn selbst ein. Nach dem Abschalten wegen des Endes des Abos bleiben die Eingaben des Kunden noch weitere 30 Tage gespeichert und werden danach gelöscht; bis dahin kann der Kunde sie im Kundenportal ansehen und die Funktion für einmalig 30 € wieder einschalten. Nach der Löschung ist eine neue Bestellung nach Nr. 4 nötig.</p>
|
||||
<p>(5) Schaltet WEBklar die Funktion auf Wunsch des Kunden oder aus einem anderen Grund als dem Ende des Abos ab, löscht WEBklar die Eingaben des Kunden auf sein Verlangen in Textform, spätestens mit dem Löschen des Kundenkontos.</p>
|
||||
|
||||
<h2 id="aenderungen">10. Änderungen und Einstellung</h2>
|
||||
<p>(1) WEBklar darf die Technik von Öffnungszeiten live verbessern, wenn der Leistungsumfang nicht schlechter wird (für Kunden mit Wartungs-Abo § 3 Abs. 7 der Abo-Bedingungen). Ändert sich dabei, welche Daten die Funktion verarbeitet, passt WEBklar die Datenschutzerklärung an und informiert den Kunden, weil sich dann sein Datenschutz-Absatz (Nr. 6 Abs. 4) ändern kann.</p>
|
||||
<p>(2) WEBklar darf die Funktion für eine Website mit einer Frist von vier Wochen beenden oder für alle Kunden mit einer Frist von drei Monaten einstellen, wenn</p>
|
||||
<ul>
|
||||
<li>ein Gesetz, eine Gerichtsentscheidung oder eine Vorgabe einer Aufsichtsbehörde den Betrieb in der bisherigen Form unzulässig macht oder nur mit wesentlich anderer Technik erlaubt und der Kunde einer dafür nötigen neuen Fassung dieser Leistungsbeschreibung nicht zustimmt, oder</li>
|
||||
<li>eine Sicherheitslücke sich nicht anders beheben lässt.</li>
|
||||
</ul>
|
||||
<p>Die Mitteilung erfolgt in Textform und nennt den Grund, die Merkmale der Änderung, den Zeitpunkt und bei Verbrauchern das Recht, den Vertrag nach § 327r Abs. 3 BGB zu beenden, mit seiner Frist.</p>
|
||||
<p>(3) Ist der Weiterbetrieb aus einem Grund nach Absatz 2 schon vor Ablauf der Frist nicht zulässig, darf WEBklar die Funktion ohne Vorankündigung aussetzen, solange das nötig ist. WEBklar informiert den Kunden darüber unverzüglich in Textform mit dem Grund und nimmt den Betrieb wieder auf, sobald das zulässig und technisch möglich ist.</p>
|
||||
<p>(4) Hat der Kunde die Funktion im Wartungs-Abo ohne Aufpreis eingeschaltet und ist er Unternehmer, darf WEBklar sie außerdem ohne besonderen Grund mit einer Frist von drei Monaten in Textform für alle Kunden einstellen. Der Kunde kann das Wartungs-Abo dann innerhalb von 30 Tagen nach der Mitteilung zum Zeitpunkt der Einstellung kündigen; die Mindestlaufzeit steht dem nicht entgegen.</p>
|
||||
<p>(5) Weil im Wartungs-Abo für die Funktion kein Aufpreis berechnet wird, ändert ihre Einstellung den Preis des Wartungs-Abos nicht. Hat der Kunde die Funktion für 30 € bestellt, kann WEBklar sie nur aus einem Grund nach Absatz 2 beenden oder einstellen. Gesetzliche Rechte von Verbrauchern bei Änderungen digitaler Produkte (§ 327r BGB) bleiben unberührt.</p>
|
||||
|
||||
<h2 id="widerruf">11. Widerruf</h2>
|
||||
<p>(1) Verbraucher können die Bestellung für 30 € nach der Widerrufsbelehrung widerrufen. Belehrung und Muster-Widerrufsformular stehen in der Anlage „<a href="/rechtliches/widerrufsbelehrung-funktionen.html">Widerrufsbelehrung und Muster-Widerrufsformular (Einrichtung von Funktionen)</a>“.</p>
|
||||
<p>(2) Hat der Kunde bei der Bestellung ausdrücklich verlangt, dass WEBklar vor Ablauf der Widerrufsfrist mit dem Einbau beginnt, erlischt sein Widerrufsrecht nach § 356 Abs. 5 Nr. 2 BGB mit der vollständigen Erbringung der Leistung, wenn er vorher ausdrücklich zugestimmt und seine Kenntnis davon bestätigt hat. Widerruft er vor der vollständigen Erbringung, schuldet er einen angemessenen Betrag für die bis dahin erbrachte Leistung (§ 357a Abs. 2 BGB).</p>
|
||||
<p>(3) Mit dem Widerruf endet die Funktion. Beim Einschalten im Wartungs-Abo ohne Aufpreis fällt kein Preis an; der Kunde kann die Funktion dort jederzeit nach Nr. 9 Abs. 1 abschalten lassen.</p>
|
||||
|
||||
<h2 id="hinweise">12. Hinweise</h2>
|
||||
<p>(1) <strong>Keine Rechtsberatung.</strong> Der Datenschutz-Absatz, der WhatsApp-Hinweis und die Hinweise des Kundenportals zu Preisangaben sind Mustertexte für die Standardkonfiguration. WEBklar sorgt dafür, dass sie die Technik richtig beschreiben, und passt sie an, wenn sich die Technik ändert. Eine rechtliche Beratung im Einzelfall, etwa zu Preisangaben, zum Wettbewerbsrecht oder zum Datenschutz der Website des Kunden, schuldet WEBklar nicht.</p>
|
||||
<p>(2) <strong>Datenschutz.</strong> Wie WEBklar im Kundenportal Daten zu Öffnungszeiten live verarbeitet, steht in der <a href="/datenschutz.html#zeiten">Datenschutzerklärung</a> von WEBklar (Abschnitt 6d). Was beim Aufruf der Website des Kunden übertragen wird, beschreibt Nr. 2 Abs. 4; die Information der Besucher gehört in die Datenschutzerklärung der Website (Nr. 6 Abs. 4).</p>
|
||||
|
||||
<p class="meta">Anbieter: WEBklar GbR, Am Schwimmbad 10, 67722 Winnweiler. Für Kunden mit Wartungs-Abo gelten ergänzend die <a href="/abo-bedingungen.html">Abo-Bedingungen</a>, für alle die <a href="/datenschutz.html">Datenschutzerklärung</a>.</p>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,368 @@
|
||||
import { test, after } from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { existsSync, readFileSync } from 'node:fs'
|
||||
import http from 'node:http'
|
||||
import express from 'express'
|
||||
import { useTestEnv } from './support/testEnv.js'
|
||||
|
||||
// Rechtstext-Entwürfe und Einbau-Anleitung von "Öffnungszeiten live" (Task 33, Spec 9, 4.6, Z19, Z20).
|
||||
// Alles hier ist Entwurf für die Rechtsrunde: die Leistungsbeschreibung steht als statische Seite unter
|
||||
// public/vertragsunterlagen/ (wie die der News-Pinnwand, Register-Adresse seit Task 5 festgelegt); die Texte für
|
||||
// Datenschutzerklärung, K02/K03 und die Kanzlei-Fragen sind Markdown-Entwürfe, die keinen Code und keine
|
||||
// veröffentlichte Seite ändern.
|
||||
useTestEnv({ appwriteUrl: 'http://127.0.0.1:1', stripeUrl: 'http://127.0.0.1:1' })
|
||||
const { config } = await import('../server/config.js')
|
||||
const { featureConfig, featureHaken } = await import('../server/services/featureRegistry.js')
|
||||
|
||||
const root = (rel) => new URL(`../${rel}`, import.meta.url)
|
||||
const lies = (rel) => readFileSync(root(rel), 'utf8').replace(/\r/g, '')
|
||||
/** Sichtbarer Text einer HTML-Seite: ohne Kommentare, Skripte, Stile und Tags, Leerraum zusammengefasst. */
|
||||
const sichtbar = (html) =>
|
||||
html
|
||||
.replace(/<!--[\s\S]*?-->/g, ' ')
|
||||
.replace(/<(script|style)[\s\S]*?<\/\1>/g, ' ')
|
||||
.replace(/<[^>]+>/g, ' ')
|
||||
.replace(/ /g, ' ')
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim()
|
||||
const norm = (text) => text.replace(/\s+/g, ' ').trim()
|
||||
|
||||
const LB_PFAD = 'public/vertragsunterlagen/zeiten-leistungsbeschreibung.html'
|
||||
const LB_URL = '/vertragsunterlagen/zeiten-leistungsbeschreibung.html'
|
||||
const lb = () => lies(LB_PFAD)
|
||||
|
||||
// ---------------------------------------------------------------------------------------------- Leistungsbeschreibung
|
||||
// Dieselbe Auslieferung wie in server/index.js: express.static(public) (die Adresse ist das Register des Terminplaners nicht, sondern
|
||||
// der "vertragsunterlagen"-Weg der News-Pinnwand; die versionierten /rechtliches-Seiten samt PDF gibt es nur für die Terminplaner-Sätze)
|
||||
const app = express()
|
||||
app.use(express.static(new URL('../public/', import.meta.url).pathname))
|
||||
const server = await new Promise((resolve) => {
|
||||
const s = http.createServer(app).listen(0, '127.0.0.1', () => resolve(s))
|
||||
})
|
||||
after(() => new Promise((resolve) => server.close(resolve)))
|
||||
const baseUrl = `http://127.0.0.1:${server.address().port}`
|
||||
|
||||
test('Leistungsbeschreibung ist erreichbar und als Entwurf markiert', async () => {
|
||||
const antwort = await fetch(baseUrl + LB_URL)
|
||||
assert.equal(antwort.status, 200)
|
||||
assert.match(antwort.headers.get('content-type') || '', /^text\/html/)
|
||||
const html = await antwort.text()
|
||||
assert.equal(html, lb(), 'die ausgelieferte Seite ist die Datei im Repo')
|
||||
|
||||
// Entwurf: nur als HTML-Kommentar (der Kunde sieht nichts davon); der Test "Bestelltexte ohne ENTWURF" prüft das Register, nicht die Seite
|
||||
assert.ok(html.includes('<!-- ENTWURF – vor Livegang Rechtsrunde -->'), 'Kopf-Kommentar fehlt')
|
||||
assert.ok(html.indexOf('<!-- ENTWURF') < html.indexOf('<head>'), 'der Kommentar steht vor dem Kopf der Seite')
|
||||
const text = sichtbar(html)
|
||||
assert.doesNotMatch(text, /ENTWURF|Entwurf/, 'im sichtbaren Text steht kein Entwurfsvermerk')
|
||||
assert.doesNotMatch(JSON.stringify(featureConfig('zeiten').leistungsbeschreibung), /ENTWURF/)
|
||||
|
||||
// Preis und Einbau
|
||||
assert.ok(text.includes('§ 19 UStG'))
|
||||
assert.ok(text.includes(`30 € einmalig (${config.ustHinweis})`), 'Preis mit dem Umsatzsteuer-Hinweis des Registers')
|
||||
assert.ok(text.includes('ein Einbau je Website'))
|
||||
assert.match(html, /<title>Leistungsbeschreibung Öffnungszeiten live – WEBklar<\/title>/)
|
||||
assert.match(html, /<h1>Leistungsbeschreibung: Öffnungszeiten live<\/h1>/)
|
||||
})
|
||||
|
||||
test('Leistungsbeschreibung: Stand ist die Fassung des Registers', () => {
|
||||
const [j, m, t] = featureConfig('zeiten').leistungsbeschreibung.version.split('-')
|
||||
assert.ok(lb().includes(`<p class="meta">Stand ${t}.${m}.${j}</p>`), 'Stand = Fassung der Leistungsbeschreibung im Register')
|
||||
})
|
||||
|
||||
test('Leistungsbeschreibung folgt der Gliederung der Spec 9.1', () => {
|
||||
const titel = [...lb().matchAll(/<h2 id="([a-z-]+)">(\d+)\. ([^<]+)<\/h2>/g)].map((m) => [Number(m[2]), m[3]])
|
||||
assert.deepEqual(titel, [
|
||||
[1, 'Geltung'],
|
||||
[2, 'Leistung'],
|
||||
[3, 'Einbau und Freischaltung'],
|
||||
[4, 'Preis'],
|
||||
[5, 'Nicht enthalten'],
|
||||
[6, 'Pflichten des Kunden'],
|
||||
[7, 'Verfügbarkeit und Aktualität'],
|
||||
[8, 'Haftung'],
|
||||
[9, 'Ende der Leistung'],
|
||||
[10, 'Änderungen und Einstellung'],
|
||||
[11, 'Widerruf'],
|
||||
[12, 'Hinweise'],
|
||||
])
|
||||
})
|
||||
|
||||
test('Leistungsbeschreibung grenzt das Abo ab und nennt Einbau und Ausschlüsse', () => {
|
||||
const text = sichtbar(lb())
|
||||
for (const satz of [
|
||||
// Z19: freiwilliges Extra, Abo-Bedingungen bleiben unverändert
|
||||
'freiwilliges Zusatzangebot',
|
||||
'gehört nicht zum Leistungsumfang der Abo-Bedingungen',
|
||||
'Leistungsbeschreibung B Nr. 3 der Abo-Bedingungen',
|
||||
'Der Preis des Wartungs-Abos ändert sich nicht',
|
||||
// Z4, Z20: ohne Aufpreis, ohne Anrechnung, ein Einbau je Website inkl. Ersatz-Tabelle beim Abschalten, Sonderwünsche
|
||||
'ohne Aufpreis',
|
||||
'weder auf das Stundenkontingent angerechnet noch damit bezahlt',
|
||||
'Aktualisierung der Ersatz-Tabelle',
|
||||
'Sonderwünsche',
|
||||
'nach Angebot',
|
||||
// Einbau: automatisch nur bei WEBklar-Hosting, sonst durch das Team
|
||||
'nur bei Websites, die WEBklar hostet',
|
||||
'setzt das Team',
|
||||
// nicht enthalten
|
||||
'Übertragung der Öffnungszeiten zu Google',
|
||||
'Feiertage einer Gemeinde',
|
||||
// Pflichten des Kunden
|
||||
'Zeiten, Feiertage und Hinweistexte',
|
||||
'Preise und Rabatte',
|
||||
'Datenschutz-Absatz',
|
||||
'WhatsApp',
|
||||
// Technik: nichts im Browser, Fremddienste erst nach Klick
|
||||
'keine Cookies',
|
||||
'erst nach dem Klick',
|
||||
'Google Maps',
|
||||
]) {
|
||||
assert.ok(text.includes(satz), `fehlt: ${satz}`)
|
||||
}
|
||||
})
|
||||
|
||||
test('Leistungsbeschreibung nennt Haftung, Ende nach Abo und Einstellung', () => {
|
||||
const text = sichtbar(lb())
|
||||
for (const satz of [
|
||||
// Haftung: Hilfsmittel, Angaben vom Kunden, Softwarefehler, Feiertagsdaten
|
||||
'Die Live-Anzeige ist ein Hilfsmittel',
|
||||
'Angaben des Kunden',
|
||||
'Feiertagsdaten',
|
||||
'entgangenen Gewinn',
|
||||
// Ende der Leistung nach Z11: 30 Tage zum Behalten für 30 €, danach abgeschaltet, Ersatz-Tabelle bleibt, Daten 30 Tage
|
||||
'Endet das Wartungs-Abo',
|
||||
'noch 30 Tage',
|
||||
'für einmalig 30 € behalten',
|
||||
'schaltet WEBklar sie ab',
|
||||
'Ersatz-Tabelle',
|
||||
'weitere 30 Tage',
|
||||
'Wartungs-Abo beginnt',
|
||||
// Einstellung/Änderung nach Vorbild Analyse Nr. 8
|
||||
'Technik von Öffnungszeiten live verbessern',
|
||||
'mit einer Frist von vier Wochen beenden',
|
||||
'mit einer Frist von drei Monaten einstellen',
|
||||
'Sicherheitslücke',
|
||||
'§ 327r Abs. 3 BGB',
|
||||
'ändert ihre Einstellung den Preis des Wartungs-Abos nicht',
|
||||
]) {
|
||||
assert.ok(text.includes(satz), `fehlt: ${satz}`)
|
||||
}
|
||||
})
|
||||
|
||||
test('Haftung spiegelt § 13 Abs. 1 und 2 der Abo-Bedingungen wörtlich (auch für Kunden ohne Abo)', () => {
|
||||
const abo = norm(sichtbar(lies('public/abo-bedingungen.html')))
|
||||
const text = norm(sichtbar(lb()))
|
||||
for (const bruchstueck of [
|
||||
'unbeschränkt bei Vorsatz und grober Fahrlässigkeit, bei Verletzung von Leben, Körper oder Gesundheit, nach dem Produkthaftungsgesetz und soweit',
|
||||
'nur für den vertragstypischen, vorhersehbaren Schaden',
|
||||
'Wesentlich sind Pflichten, deren Erfüllung die ordnungsgemäße Durchführung des Vertrags überhaupt erst ermöglicht und auf deren Einhaltung der Kunde regelmäßig vertrauen darf',
|
||||
'Im Übrigen ist die Haftung für leichte Fahrlässigkeit ausgeschlossen',
|
||||
]) {
|
||||
assert.ok(abo.includes(bruchstueck), `Abo-Bedingungen § 13 hat sich geändert: ${bruchstueck}`)
|
||||
assert.ok(text.includes(bruchstueck), `die Leistungsbeschreibung muss § 13 spiegeln: ${bruchstueck}`)
|
||||
}
|
||||
// ohne Abo gibt es kein § 13 zum Verweisen: die Klausel steht ausgeschrieben im Abschnitt Haftung
|
||||
const haftung = lb().split('<h2 id="haftung">')[1].split('<h2 id=')[0]
|
||||
assert.match(sichtbar(haftung), /Dieser Abschnitt gilt auch für Kunden ohne Wartungs-Abo/)
|
||||
})
|
||||
|
||||
test('Verweise innerhalb der Leistungsbeschreibung treffen vorhandene Nummern und Absätze', () => {
|
||||
const html = lb()
|
||||
const teile = html.split(/<h2 id="[a-z-]+">/).slice(1)
|
||||
const absaetze = teile.map((teil) => [...teil.matchAll(/<p>\((\d+)\) /g)].map((m) => Number(m[1])))
|
||||
assert.equal(teile.length, 12)
|
||||
for (const [i, nummern] of absaetze.entries()) {
|
||||
assert.ok(nummern.length > 0, `Abschnitt ${i + 1} hat keine Absätze`)
|
||||
assert.deepEqual(nummern, nummern.map((_, k) => k + 1), `Abschnitt ${i + 1}: Absätze (1), (2), … ohne Lücke`)
|
||||
}
|
||||
// fremde Verweise (Abo-Bedingungen, Gesetz) herausnehmen, übrig bleiben "Nr. n [Abs. m]" dieser Leistungsbeschreibung
|
||||
const text = sichtbar(html)
|
||||
.replace(/Leistungsbeschreibung B Nr\. \d+/g, '')
|
||||
.replace(/§ \d+[a-z]? Abs\. \d+ Nr\. \d+ BGB/g, '')
|
||||
const verweise = [...text.matchAll(/Nr\. (\d+)(?: Abs\. (\d+))?/g)]
|
||||
assert.ok(verweise.length >= 6, 'die Leistungsbeschreibung verweist an mehreren Stellen auf sich selbst')
|
||||
for (const [roh, nr, abs] of verweise) {
|
||||
assert.ok(absaetze[Number(nr) - 1], `${roh}: Abschnitt fehlt`)
|
||||
if (abs) assert.ok(absaetze[Number(nr) - 1].includes(Number(abs)), `${roh}: Absatz fehlt`)
|
||||
}
|
||||
})
|
||||
|
||||
test('Leistungsbeschreibung: Sie-Form, Schreibweise WEBklar, nie „24 Uhr“', () => {
|
||||
const text = sichtbar(lb())
|
||||
assert.doesNotMatch(text, /\b(Du|Dein\w*|dein\w*|Dich|dich|Dir)\b/, 'Rechtstext in Sie-Form')
|
||||
assert.match(text, /\bSie\b/)
|
||||
assert.doesNotMatch(text, /24 Uhr|24:00|24 h\b/)
|
||||
assert.doesNotMatch(lb(), /Webklar|WebKlar|WEBKlar|webKlar/, 'Schreibweise ist WEBklar')
|
||||
assert.ok(text.includes('WEBklar GbR, Am Schwimmbad 10, 67722 Winnweiler'), 'Anbieter genannt')
|
||||
// keine Rechtsberatung, keine Platzhalter
|
||||
assert.ok(text.includes('Keine Rechtsberatung'))
|
||||
assert.doesNotMatch(text, /TODO|XXX|\[Prüfen|⚖/)
|
||||
})
|
||||
|
||||
test('Register verlinkt die Leistungsbeschreibung', () => {
|
||||
const f = featureConfig('zeiten')
|
||||
assert.equal(f.leistungsbeschreibung.url, LB_URL)
|
||||
assert.ok(existsSync(new URL(`../public${f.leistungsbeschreibung.url}`, import.meta.url)), 'die Datei zur Register-Adresse existiert')
|
||||
// beide Haken (bezahlt und gratis) nennen genau diese Leistungsbeschreibung
|
||||
for (const consent of [f.consent, f.consentFrei]) {
|
||||
assert.ok(consent.items[0].text.includes('Leistungsbeschreibung „Öffnungszeiten live“'))
|
||||
}
|
||||
assert.ok(sichtbar(lb()).includes('„Öffnungszeiten live“'))
|
||||
// dieselbe Adresse nennen die Bestellseite und die Rechtliches-Karte (Spec 8)
|
||||
assert.equal(new URL(`https://project.webklar.com${f.leistungsbeschreibung.url}`).pathname, LB_URL)
|
||||
})
|
||||
|
||||
test('unterlagenBereit für zeiten ist erfüllt', async () => {
|
||||
const haken = featureHaken('zeiten')
|
||||
assert.equal(typeof haken.unterlagenBereit, 'function')
|
||||
assert.equal(await haken.unterlagenBereit(), true)
|
||||
// und die Unterlage, auf die das Register zeigt, ist wirklich lesbar (nicht leer)
|
||||
const html = readFileSync(new URL(`../public${featureConfig('zeiten').leistungsbeschreibung.url}`, import.meta.url), 'utf8')
|
||||
assert.ok(html.length > 5000)
|
||||
})
|
||||
|
||||
// ---------------------------------------------------------------------------------------------- Datenschutz § 6d
|
||||
test('Datenschutz-Entwurf § 6d und Ergänzung § 6 liegen als Entwurf vor, datenschutz.html bleibt unberührt', () => {
|
||||
const md = lies('docs/rechtliches/zeiten-datenschutz-6d-entwurf.md')
|
||||
assert.ok(md.includes('<h2 id="zeiten">6d. Öffnungszeiten live auf Websites unserer Kunden</h2>'))
|
||||
for (const satz of [
|
||||
'Entwurf',
|
||||
'Art. 6 Abs. 1 lit. b DSGVO',
|
||||
'Art. 6 Abs. 1 lit. f DSGVO',
|
||||
'Erinnerung',
|
||||
'höchstens eine',
|
||||
'keine Cookies',
|
||||
'nichts im Browser',
|
||||
'erst',
|
||||
'Klick',
|
||||
'WhatsApp',
|
||||
'Google Maps',
|
||||
'30 Tage',
|
||||
'Abschnitt 3', // Server-Protokolle (14 Tage) stehen dort
|
||||
'Abschnitt 7', // Nachweis der Zustimmung
|
||||
]) {
|
||||
assert.ok(md.includes(satz), `fehlt: ${satz}`)
|
||||
}
|
||||
assert.doesNotMatch(md, /24 Uhr|Webklar|WebKlar/)
|
||||
// § 6: der heutige Satz steht wörtlich da (Drift-Wächter gegen public/datenschutz.html), der neue nennt Öffnungszeiten live
|
||||
const html = lies('public/datenschutz.html')
|
||||
const heute = html.slice(html.indexOf('Um Sie zu unterstützen'), html.indexOf('ansehen und ändern.', html.indexOf('Um Sie zu unterstützen')) + 'ansehen und ändern.'.length)
|
||||
assert.ok(heute.startsWith('Um Sie zu unterstützen') && heute.endsWith('ansehen und ändern.'))
|
||||
assert.ok(md.includes(heute), 'der heutige § 6-Satz ist wörtlich zitiert')
|
||||
assert.match(md, /wenn Sie Öffnungszeiten live nutzen, Ihre Öffnungszeiten, Hinweise und Kontaktangaben ansehen und ändern/)
|
||||
// § 7 (Nachweis): der heutige Satz steht wörtlich da, der Vorschlag nennt Öffnungszeiten live und das Einschalten
|
||||
const anfang7 = html.indexOf('Bestellen Sie eine Funktion (News-Pinnwand, Terminplaner)')
|
||||
const heute7 = html.slice(anfang7, html.indexOf('wenn Sie nichts bestellt haben.', anfang7) + 'wenn Sie nichts bestellt haben.'.length)
|
||||
assert.ok(anfang7 > 0 && heute7.endsWith('wenn Sie nichts bestellt haben.'))
|
||||
assert.ok(md.includes(heute7), 'der heutige § 7-Satz ist wörtlich zitiert')
|
||||
assert.match(md, /Bestellen Sie eine Funktion \(News-Pinnwand, Terminplaner, Öffnungszeiten live\) oder schalten Sie Öffnungszeiten live im Wartungs-Abo ein/)
|
||||
assert.ok(md.includes('legal_datenschutz_kundenansicht'), 'nennt den Test, der den § 6-Satz pinnt')
|
||||
// Wächter: die Entwurfsdatei ist nicht in die veröffentlichte Erklärung gerutscht
|
||||
assert.doesNotMatch(html, /6d\. Öffnungszeiten live/)
|
||||
})
|
||||
|
||||
// ---------------------------------------------------------------------------------------------- K02/K03
|
||||
test('K02/K03-Entwurf zitiert den heutigen Satz und schlägt den neuen vor', () => {
|
||||
const md = lies('docs/rechtliches/zeiten-k02-k03-entwurf.md')
|
||||
const heute =
|
||||
'Die Website-Analyse und der Terminplaner gehören zum Wartungs-Abo und enden mit ihm. Die Termindaten löschen wir 30 Tage nach dem Abschalten; vorher können Sie Ihre Termine im Kundenportal ansehen und exportieren.'
|
||||
for (const vorlage of ['K02 Kuendigungsbestaetigung', 'K03 Eingang Kuendigung']) {
|
||||
assert.ok(lies(`server/mail-templates/${vorlage}.html`).includes(heute), `${vorlage}: der Satz im Entwurf muss der heutigen Vorlage entsprechen`)
|
||||
}
|
||||
assert.ok(md.includes(heute), 'der heutige Satz steht wörtlich im Entwurf')
|
||||
// die Pins bleiben mit dem Vorschlag grün: der alte Satz bleibt unverändert der Anfang des neuen
|
||||
const vorschlag = md.split('## Vorschlag')[1]
|
||||
assert.ok(vorschlag.includes(heute), 'der Vorschlag beginnt mit dem heutigen Satz')
|
||||
for (const satz of [
|
||||
'Haben Sie Öffnungszeiten live im Wartungs-Abo eingeschaltet',
|
||||
'noch 30 Tage',
|
||||
'für einmalig 30 € behalten',
|
||||
'feste Öffnungszeiten',
|
||||
'30 Tage später',
|
||||
]) {
|
||||
assert.ok(vorschlag.includes(satz), `fehlt im Vorschlag: ${satz}`)
|
||||
}
|
||||
assert.ok(md.includes('test/mailTemplates.test.js') && md.includes('test/publicForms.test.js'), 'nennt die Pins')
|
||||
assert.ok(md.includes('Keine Codeänderung'))
|
||||
assert.doesNotMatch(md, /24 Uhr|Webklar/)
|
||||
assert.doesNotMatch(vorschlag, /\b(Du|Dein\w*|dein\w*|Dich|dich|Dir)\b/, 'Sie-Form')
|
||||
})
|
||||
|
||||
// ---------------------------------------------------------------------------------------------- Einbau-Anleitung
|
||||
test('Einbau-Anleitung enthält Einbau-Zeile, Platzhalter, Ersatz-HTML, Header-Selektor und Prüfliste', async () => {
|
||||
const md = lies('docs/funktionen/zeiten-einbau.md')
|
||||
const zeile = '<script data-wk-zeiten src="https://project.webklar.com/embed/zeiten.js" data-site="SITEKEY" defer></script>'
|
||||
assert.ok(md.includes(zeile), 'Einbau-Zeile wie im Ticket')
|
||||
// dieselbe Zeile erzeugt das Einrichtungs-Ticket (Wächter gegen Abweichung)
|
||||
const { zeitenTicketDetails } = await import('../server/services/zeiten/ticketDetails.js')
|
||||
const ticket = await zeitenTicketDetails({ feature: featureConfig('zeiten'), doc: { siteKey: 'SITEKEY' }, project: {}, customer: {} })
|
||||
assert.ok(ticket.includes(zeile), 'das Ticket nennt dieselbe Einbau-Zeile')
|
||||
for (const modus of ['tabelle', 'status', 'badge']) {
|
||||
assert.ok(md.includes(`data-modus="${modus}"`), `Platzhalter ${modus}`)
|
||||
}
|
||||
for (const satz of [
|
||||
'data-plan',
|
||||
'Ersatz-HTML kopieren',
|
||||
'WOMS → Funktionen',
|
||||
'navSelektor',
|
||||
'kontaktleisteAus',
|
||||
'hinweisleisteAus',
|
||||
'ZEITEN_KEIN_NEUBAU',
|
||||
'ZEITEN_PUBLIC_BASE',
|
||||
'einbau: manuell',
|
||||
'Prüfliste',
|
||||
'grep -ril',
|
||||
'JSON-LD',
|
||||
'Datenschutz-Absatz',
|
||||
'website-datenschutz-absatz.md',
|
||||
'Ersatz-Tabelle aktualisieren',
|
||||
'Seite liegt woanders',
|
||||
'keine Cookies',
|
||||
]) {
|
||||
assert.ok(md.includes(satz), `fehlt: ${satz}`)
|
||||
}
|
||||
// die Vorgabe der Sperrliste steht in der Config
|
||||
for (const subdomain of config.zeiten.keinNeubau) assert.ok(md.includes(subdomain), `Sperrlisten-Vorgabe ${subdomain} fehlt`)
|
||||
// die fünf Prüfpunkte des Tickets stehen in der Anleitung (Stichworte)
|
||||
for (const stichwort of ['öffnungszeit', 'Footer', 'Header', 'Anruf-Leiste', 'JSON-LD', 'Datenschutz-Absatz']) {
|
||||
assert.ok(md.toLowerCase().includes(stichwort.toLowerCase()), `Prüfliste: ${stichwort}`)
|
||||
}
|
||||
assert.doesNotMatch(md, /24 Uhr|Webklar/)
|
||||
})
|
||||
|
||||
// ---------------------------------------------------------------------------------------------- Website-Absatz
|
||||
test('Website-Absatz „Öffnungszeiten live“ in Variante A und B, WhatsApp und Google Maps erst nach Klick', () => {
|
||||
const md = lies('docs/funktionen/website-datenschutz-absatz.md')
|
||||
const a = md.slice(md.indexOf('## Variante A'), md.indexOf('## Variante B'))
|
||||
const b = md.slice(md.indexOf('## Variante B'))
|
||||
for (const [name, teil] of [['A', a], ['B', b]]) {
|
||||
assert.ok(teil.includes('### Öffnungszeiten auf dieser Website'), `Variante ${name}: Überschrift`)
|
||||
const absatz = teil.slice(teil.indexOf('### Öffnungszeiten auf dieser Website'))
|
||||
for (const satz of ['IP-Adresse', 'Server-Protokollen', 'keine Cookies', 'nichts in Ihrem Browser', 'Art. 6 Abs. 1 lit. f DSGVO', 'nach 14 Tagen gelöscht', 'WhatsApp', 'Google Maps', 'Erst wenn Sie darauf klicken', 'Vorher werden keine Daten']) {
|
||||
assert.ok(absatz.includes(satz), `Variante ${name}: fehlt ${satz}`)
|
||||
}
|
||||
assert.match(absatz, /nach dem Klick/)
|
||||
assert.match(absatz, /Seitencode/, 'strukturierte Daten für Suchmaschinen')
|
||||
}
|
||||
assert.ok(b.slice(b.indexOf('### Öffnungszeiten auf dieser Website')).includes('[Prüfen: AVV mit WEBklar erforderlich]'), 'Variante B: AVV-Marker wie bei den News')
|
||||
// bestehende Wächter aus legalTexts.test.js gelten weiter: keine Gedankenstriche im Kundentext, Marker der News bleibt
|
||||
assert.ok(!/[–—]/.test(md.slice(md.indexOf('## Variante A'))), 'Kundentext ohne Gedankenstriche')
|
||||
assert.ok(md.includes('[Prüfen: AVV mit WEBklar erforderlich]'))
|
||||
assert.doesNotMatch(md, /24 Uhr/)
|
||||
})
|
||||
|
||||
// ---------------------------------------------------------------------------------------------- Kanzlei-Fragen
|
||||
test('Kanzlei-Fragen: AVV Funktionen per Haken und Behalten-Zustimmung stehen im Paket', () => {
|
||||
const md = lies('docs/rechtliches/zeiten-kanzlei-fragen.md')
|
||||
assert.ok(md.includes('⚖ AVV Funktionen'))
|
||||
assert.match(md, /Haken/)
|
||||
assert.ok(md.includes('Behalten') && md.includes('Einbau beginnt'), 'Behalten-Zustimmung nutzt den Haken „Einbau beginnt“')
|
||||
const fragen = md.split(/^### ZQ\d+: /m).slice(1)
|
||||
assert.ok(fragen.length >= 6, 'mehrere Fragen')
|
||||
for (const frage of fragen) {
|
||||
for (const teil of ['**Frage.**', '**Fundstelle im Text.**', '**Unsere Annahme.**', '**Alternative.**']) {
|
||||
assert.ok(frage.includes(teil), `${frage.slice(0, 50)}: ${teil}`)
|
||||
}
|
||||
}
|
||||
assert.doesNotMatch(md, /Webklar|WebKlar/)
|
||||
})
|
||||
Reference in new issue
Block a user