Gästetexte des Terminplaners: Fassung 2026-10-05, Erinnerungs- und Abo-Ende-Satz, Archiv je Fassung
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1a91a331b4
commit
c1266fcce7
11 files changed
+309
-29
No files matched your search
@@ -27,5 +27,8 @@ Die Texte, die ein Gast beim Buchen sieht (Hinweis am Formular, Texte der Haken,
|
|||||||
| Fassung | gültig ab | Archiv |
|
| Fassung | gültig ab | Archiv |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| 2026-10-02 | 2026-10-03 (Pilot) | [termine-gaestetexte-2026-10-02.md](termine-gaestetexte-2026-10-02.md) |
|
| 2026-10-02 | 2026-10-03 (Pilot) | [termine-gaestetexte-2026-10-02.md](termine-gaestetexte-2026-10-02.md) |
|
||||||
|
| 2026-10-05 | offen bis Livegang | [termine-gaestetexte-2026-10-05.md](termine-gaestetexte-2026-10-05.md) |
|
||||||
|
|
||||||
„Gültig ab“ einer neuen Fassung wird beim Livegang eingetragen; ihr Zeitraum endet mit dem „gültig ab“ der nächsten.
|
„Gültig ab“ einer neuen Fassung wird beim Livegang eingetragen; ihr Zeitraum endet mit dem „gültig ab“ der nächsten.
|
||||||
|
|
||||||
|
Änderungen der Fassung 2026-10-05 gegenüber 2026-10-02 (nur diese beiden Stellen): Der Haken für die Erinnerung endet mit dem Satz „Diese Einwilligung kann ich jederzeit widerrufen.“ (Art. 7 Abs. 3 DSGVO); der Datenschutz-Absatz sagt bei der Abschaltung: „Wird die Online-Terminbuchung abgeschaltet, zum Beispiel weil unser Vertrag mit WEBklar endet, werden alle Termindaten 30 Tage nach dem Abschalten gelöscht.“ Buchungen, die vor dem Livegang der neuen Fassung entstanden sind, behalten im Nachweis die Fassung 2026-10-02.
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
# Gästetexte des Terminplaners, Fassung 2026-10-05
|
||||||
|
|
||||||
|
Dieses Archiv hält den Wortlaut der Gästetexte fest, die ein Gast in dieser Fassung sieht (AVV Terminplaner Nr. 8 Abs. 5). Der Nachweis einer Buchung speichert die Fassung, den Zeitpunkt, den Text des Buchungsknopfs, die Anrede und Hashes (SHA-256) der übrigen Texte, nicht die Texte selbst.
|
||||||
|
|
||||||
|
Fassung der Gästetexte: **2026-10-05** (`RECHTSTEXTE_VERSION` in `server/services/termine/rechtstexte.js`). Der Zeitraum der Verwendung ergibt sich aus „gültig ab“ dieser Fassung und der nächsten Fassung in der Tabelle „Terminplaner-Gästetexte“ in `README.md` dieses Ordners.
|
||||||
|
|
||||||
|
Erzeugt mit `node scripts/termine-texte-archiv.mjs`, geprüft mit `--check`. Nicht von Hand ändern: eine Textänderung bekommt eine neue Fassung und eine neue Datei; diese Datei bleibt, wie sie war.
|
||||||
|
|
||||||
|
Die Betriebsangaben stehen als Platzhalter: `{{Name des Betriebs}}`, `{{Anschrift}}`, `{{Telefon}}` und `{{E-Mail}}` stammen aus der Einstellung des Betriebs, `{{Stunden}}` ist die gewählte Vorlaufzeit der Erinnerung (der Nachweis speichert sie), die Aufbewahrung nach dem Termin ist mit der Vorgabe von 90 Tagen dargestellt (einstellbar von 14 bis 365 Tagen).
|
||||||
|
|
||||||
|
## 1. Hinweis unter dem Buchungsformular (Datenschutz)
|
||||||
|
|
||||||
|
### Sie-Fassung
|
||||||
|
|
||||||
|
> Datenschutz. Wir verwenden Ihre Angaben, um Ihren Termin zu verwalten und zu bestätigen (Art. 6 Abs. 1 lit. b DSGVO). Betrieben wird der Terminkalender in unserem Auftrag von der WEBklar GbR (Server: Hetzner, Deutschland). Wir speichern Ihre Daten bis 90 Tage nach dem Termin, abgesagte Termine 30 Tage, nicht bestätigte Buchungen 48 Stunden nach Ablauf der Bestätigungsfrist. Es werden keine Cookies gesetzt. Bitte machen Sie keine Angaben zu Ihrer Gesundheit. Mehr erfahren Sie in unserer Datenschutzerklärung. Verantwortlich: {{Name des Betriebs}}, {{Anschrift}}.
|
||||||
|
|
||||||
|
### Du-Fassung
|
||||||
|
|
||||||
|
> Datenschutz. Wir verwenden deine Angaben, um deinen Termin zu verwalten und zu bestätigen (Art. 6 Abs. 1 lit. b DSGVO). Betrieben wird der Terminkalender in unserem Auftrag von der WEBklar GbR (Server: Hetzner, Deutschland). Wir speichern deine Daten bis 90 Tage nach dem Termin, abgesagte Termine 30 Tage, nicht bestätigte Buchungen 48 Stunden nach Ablauf der Bestätigungsfrist. Es werden keine Cookies gesetzt. Bitte mach keine Angaben zu deiner Gesundheit. Mehr erfährst du in unserer Datenschutzerklärung. Verantwortlich: {{Name des Betriebs}}, {{Anschrift}}.
|
||||||
|
|
||||||
|
## 2. Haken
|
||||||
|
|
||||||
|
Die Haken stehen immer in der Sie-Form, auch bei Betrieben, die ihre Gäste duzen.
|
||||||
|
|
||||||
|
### Vorzeitiger Beginn (Pflicht bei einer Leistung mit Preis für Verbraucher, wenn der Termin vor Ablauf der Widerrufsfrist beginnt, bei Terminanfragen immer)
|
||||||
|
|
||||||
|
> Ich verlange ausdrücklich, dass {{Name des Betriebs}} mit der Leistung vor Ablauf der Widerrufsfrist beginnt. Mir ist bekannt, dass mein Widerrufsrecht mit der vollständigen Erbringung der Leistung erlischt (§ 356 Abs. 5 Nr. 2 BGB). Widerrufe ich vor der vollständigen Erbringung, schulde ich einen angemessenen Betrag für die bis dahin erbrachte Leistung (§ 357a Abs. 2 BGB).
|
||||||
|
|
||||||
|
### Stornobedingungen (Pflicht, wenn der Betrieb Stornobedingungen hat; der Text der Bedingungen steht beim Betrieb und trägt eine eigene Version)
|
||||||
|
|
||||||
|
> Ich habe die Stornobedingungen gelesen.
|
||||||
|
|
||||||
|
### Erinnerung (freiwillig, nie vorangekreuzt, nur wenn der Betrieb Erinnerungen anbietet)
|
||||||
|
|
||||||
|
> Ich möchte {{Stunden}} Stunden vor dem Termin eine Erinnerung per E-Mail erhalten. Diese Einwilligung kann ich jederzeit widerrufen.
|
||||||
|
|
||||||
|
## 3. Hinweis zu Gesundheitsangaben
|
||||||
|
|
||||||
|
Steht über dem Nachrichtenfeld des Formulars.
|
||||||
|
|
||||||
|
> Bitte keine Gesundheitsangaben.
|
||||||
|
|
||||||
|
## 4. Knopftexte
|
||||||
|
|
||||||
|
| Leistung | Knopf im Widget | Knopf auf der Bestätigungsseite |
|
||||||
|
|---|---|---|
|
||||||
|
| Sofort buchbar, ohne Preis | Termin buchen | Termin bestätigen |
|
||||||
|
| Sofort buchbar, mit Preis | Zahlungspflichtig buchen | Zahlungspflichtig bestätigen |
|
||||||
|
| Terminanfrage | Termin anfragen | Anfrage absenden |
|
||||||
|
|
||||||
|
Der Text des Buchungsknopfs im Widget steht im Nachweis (`knopf`).
|
||||||
|
|
||||||
|
## 5. Widerrufsbelehrung mit Muster-Widerrufsformular für Gäste
|
||||||
|
|
||||||
|
Sie-Form, mit den Betriebsangaben als Platzhalter.
|
||||||
|
|
||||||
|
> Widerrufsbelehrung
|
||||||
|
>
|
||||||
|
> Widerrufsrecht
|
||||||
|
>
|
||||||
|
> Sie haben das Recht, binnen vierzehn Tagen ohne Angabe von Gründen diesen Vertrag zu widerrufen.
|
||||||
|
>
|
||||||
|
> Die Widerrufsfrist beträgt vierzehn Tage ab dem Tag des Vertragsabschlusses.
|
||||||
|
>
|
||||||
|
> Um Ihr Widerrufsrecht auszuüben, müssen Sie uns ({{Name des Betriebs}}, {{Anschrift}}, Telefon: {{Telefon}}, E-Mail: {{E-Mail}}) mittels einer eindeutigen Erklärung (z. B. ein mit der Post versandter Brief oder eine E-Mail) über Ihren Entschluss, diesen Vertrag zu widerrufen, informieren. Sie können dafür das beigefügte Muster-Widerrufsformular verwenden, das jedoch nicht vorgeschrieben ist.
|
||||||
|
>
|
||||||
|
> Sie können Ihr Widerrufsrecht auch online unter „Vertrag widerrufen“ (Schaltfläche im Terminbereich auf unserer Website sowie Link „Termin verwalten“ in Ihrer Bestätigungs-E-Mail) ausüben. Wenn Sie diese Online-Funktion nutzen, übermitteln wir Ihnen auf einem dauerhaften Datenträger (z. B. durch eine E-Mail) unverzüglich eine Eingangsbestätigung mit Informationen zum Inhalt der Widerrufserklärung sowie dem Datum und der Uhrzeit ihres Eingangs.
|
||||||
|
>
|
||||||
|
> Zur Wahrung der Widerrufsfrist reicht es aus, dass Sie die Mitteilung über die Ausübung des Widerrufsrechts vor Ablauf der Widerrufsfrist absenden.
|
||||||
|
>
|
||||||
|
> Folgen des Widerrufs
|
||||||
|
>
|
||||||
|
> Wenn Sie diesen Vertrag widerrufen, haben wir Ihnen alle Zahlungen, die wir von Ihnen erhalten haben, einschließlich der Lieferkosten (mit Ausnahme der zusätzlichen Kosten, die sich daraus ergeben, dass Sie eine andere Art der Lieferung als die von uns angebotene, günstigste Standardlieferung gewählt haben), unverzüglich und spätestens binnen vierzehn Tagen ab dem Tag zurückzuzahlen, an dem die Mitteilung über Ihren Widerruf dieses Vertrags bei uns eingegangen ist. Für diese Rückzahlung verwenden wir dasselbe Zahlungsmittel, das Sie bei der ursprünglichen Transaktion eingesetzt haben, es sei denn, mit Ihnen wurde ausdrücklich etwas anderes vereinbart; in keinem Fall werden Ihnen wegen dieser Rückzahlung Entgelte berechnet.
|
||||||
|
>
|
||||||
|
> Haben Sie verlangt, dass die Dienstleistungen während der Widerrufsfrist beginnen soll, so haben Sie uns einen angemessenen Betrag zu zahlen, der dem Anteil der bis zu dem Zeitpunkt, zu dem Sie uns von der Ausübung des Widerrufsrechts hinsichtlich dieses Vertrags unterrichten, bereits erbrachten Dienstleistungen im Vergleich zum Gesamtumfang der im Vertrag vorgesehenen Dienstleistungen entspricht.
|
||||||
|
>
|
||||||
|
> Muster-Widerrufsformular
|
||||||
|
>
|
||||||
|
> (Wenn Sie den Vertrag widerrufen wollen, dann füllen Sie bitte dieses Formular aus und senden Sie es zurück.)
|
||||||
|
>
|
||||||
|
> – An {{Name des Betriebs}}, {{Anschrift}}, {{E-Mail}}:
|
||||||
|
> – Hiermit widerrufe(n) ich/wir (*) den von mir/uns (*) abgeschlossenen Vertrag über den Kauf der folgenden Waren (*)/die Erbringung der folgenden Dienstleistung (*)
|
||||||
|
> – Bestellt am (*)/erhalten am (*)
|
||||||
|
> – Name des/der Verbraucher(s)
|
||||||
|
> – Anschrift des/der Verbraucher(s)
|
||||||
|
> – Unterschrift des/der Verbraucher(s) (nur bei Mitteilung auf Papier)
|
||||||
|
> – Datum
|
||||||
|
>
|
||||||
|
> (*) Unzutreffendes streichen.
|
||||||
|
|
||||||
|
## 6. Datenschutz-Absatz „Online-Terminbuchung“ für die Datenschutzerklärung der Website des Betriebs
|
||||||
|
|
||||||
|
Hier mit allen bedingten Sätzen (Telefon-Feld, Nachricht oder Zusatzfelder, Erinnerung, Widerruf, Telefonnummer des Betriebs); ein Betrieb ohne die jeweilige Funktion hat den Satz nicht.
|
||||||
|
|
||||||
|
### Sie-Fassung
|
||||||
|
|
||||||
|
> Online-Terminbuchung
|
||||||
|
>
|
||||||
|
> Auf unserer Website können Sie Termine online buchen. Dafür verarbeiten wir die folgenden Daten:
|
||||||
|
>
|
||||||
|
> Welche Daten? Ihren Namen, Ihre E-Mail-Adresse, den gewählten Termin, die gewählte Leistung und die gewählte Person oder den gewählten Platz, auf Wunsch Ihre Telefonnummer und die Angaben, die Sie uns im Formular mitteilen. Außerdem speichern wir, wann Sie den Termin bestätigt haben, welche Fassung der Hinweise Sie bei der Buchung gesehen haben und welche Zustimmungen Sie erteilt haben, zum Beispiel ob Sie eine Erinnerung gewünscht haben.
|
||||||
|
>
|
||||||
|
> Wofür und auf welcher Grundlage? Wir brauchen die Daten, um Ihren Termin zu verwalten, Ihnen den Termin zu bestätigen und Sie über Änderungen oder Absagen zu informieren. Rechtsgrundlage ist Art. 6 Abs. 1 lit. b DSGVO (Vertrag über den gebuchten Termin, bei einer Terminanfrage vorvertragliche Maßnahmen auf Ihre Anfrage). Wenn Sie eine Erinnerung vor dem Termin wünschen, beruht diese auf Ihrer Einwilligung nach Art. 6 Abs. 1 lit. a DSGVO. Sie können sie jederzeit mit Wirkung für die Zukunft widerrufen, indem Sie uns eine kurze Nachricht senden oder den Termin über den Link in der E-Mail absagen. Widerrufen Sie einen Vertrag über die Schaltfläche „Vertrag widerrufen“, speichern wir Ihre Erklärung mit Datum und Uhrzeit und senden Ihnen eine Eingangsbestätigung (Art. 6 Abs. 1 lit. c DSGVO in Verbindung mit § 356a BGB). Zum Schutz vor Missbrauch (zum Beispiel automatisierten Buchungen) und um die Zahl der Anfragen zu begrenzen, merken wir uns Ihre IP-Adresse kurzzeitig nur im Arbeitsspeicher des Servers, nicht dauerhaft (Art. 6 Abs. 1 lit. f DSGVO; unser berechtigtes Interesse ist der sichere Betrieb der Terminbuchung).
|
||||||
|
>
|
||||||
|
> E-Mails. Sie erhalten eine E-Mail, in der Sie Ihre E-Mail-Adresse und den Termin per Link bestätigen, danach eine Bestätigung und, wenn Sie das angekreuzt haben, eine Erinnerung. Sagen wir den Termin ab oder verschieben ihn, informieren wir Sie ebenfalls per E-Mail. Die E-Mails enthalten keine Werbung und keine Tracking-Pixel. Absender ist {{Name des Betriebs}}, Ihre Antworten gehen an uns. Bis zum Versand liegt die E-Mail in einer Warteschlange; nach dem Versand wird dort die Empfängeradresse samt Inhalt gelöscht, nicht zustellbare E-Mails nach 48 Stunden.
|
||||||
|
>
|
||||||
|
> Wer verarbeitet die Daten in unserem Auftrag? Den Terminkalender betreibt die WEBklar GbR, Am Schwimmbad 10, 67722 Winnweiler, als unser Auftragsverarbeiter nach Art. 28 DSGVO. Die Daten liegen auf Servern der Hetzner Online GmbH, Industriestraße 25, 91710 Gunzenhausen, in Deutschland (Rechenzentren Falkenstein und Nürnberg). Der E-Mail-Versand läuft ebenfalls über Hetzner. Eine Übermittlung in Drittländer findet nicht statt.
|
||||||
|
>
|
||||||
|
> Beim Aufruf der Terminbuchung. Beim Aufruf dieser Seite lädt Ihr Browser das Programm der Terminbuchung vom Server der WEBklar GbR (project.webklar.com), beim Öffnen der Terminbuchung zusätzlich die Termine und Texte. Dabei werden technisch bedingt Ihre IP-Adresse und Angaben zu Ihrem Browser übertragen. Der Server speichert sie kurzzeitig in Protokollen und löscht sie nach 14 Tagen (Art. 6 Abs. 1 lit. f DSGVO; berechtigtes Interesse ist der sichere und funktionsfähige Betrieb). Wir setzen dafür keine Cookies und speichern nichts auf Ihrem Gerät.
|
||||||
|
>
|
||||||
|
> Wie lange speichern wir? Nicht bestätigte Buchungen (der Bestätigungslink gilt 30 Minuten) löschen wir 48 Stunden nach Ablauf dieser Frist. Bestätigte Termine löschen wir 90 Tage nach dem Termin, abgesagte oder abgelehnte Termine 30 Tage nach der Absage oder Ablehnung. Terminanfragen, die wir bis zum Terminbeginn nicht beantworten, behandeln wir als abgelehnt. Wird die Online-Terminbuchung abgeschaltet, zum Beispiel weil unser Vertrag mit WEBklar endet, werden alle Termindaten 30 Tage nach dem Abschalten gelöscht. Gesetzliche Aufbewahrungspflichten für Geschäftsunterlagen (zum Beispiel Rechnungen) bleiben davon unberührt; sie erfüllen wir außerhalb des Terminkalenders.
|
||||||
|
>
|
||||||
|
> Pflicht zur Angabe. Ohne Name, E-Mail-Adresse und Terminwahl können wir keinen Termin online vergeben. Sie können uns auch telefonisch erreichen: {{Telefon}}. Eine automatisierte Entscheidungsfindung findet nicht statt. Bitte machen Sie bei der Buchung keine Angaben zu Ihrer Gesundheit.
|
||||||
|
>
|
||||||
|
> Ihre Rechte. Sie haben das Recht auf Auskunft, Berichtigung, Löschung, Einschränkung der Verarbeitung und Datenübertragbarkeit sowie das Recht, der Verarbeitung auf Grundlage berechtigter Interessen zu widersprechen (Art. 15 bis 21 DSGVO). Eine erteilte Einwilligung können Sie jederzeit widerrufen. Sie können sich bei einer Datenschutzaufsichtsbehörde beschweren. Verantwortlich ist {{Name des Betriebs}}, {{Anschrift}}, E-Mail {{E-Mail}}.
|
||||||
|
|
||||||
|
### Du-Fassung
|
||||||
|
|
||||||
|
> Online-Terminbuchung
|
||||||
|
>
|
||||||
|
> Auf unserer Website kannst du Termine online buchen. Dafür verarbeiten wir die folgenden Daten:
|
||||||
|
>
|
||||||
|
> Welche Daten? Deinen Namen, deine E-Mail-Adresse, den gewählten Termin, die gewählte Leistung und die gewählte Person oder den gewählten Platz, auf Wunsch deine Telefonnummer und die Angaben, die du uns im Formular mitteilst. Außerdem speichern wir, wann du den Termin bestätigt hast, welche Fassung der Hinweise du bei der Buchung gesehen hast und welche Zustimmungen du erteilt hast, zum Beispiel ob du eine Erinnerung gewünscht hast.
|
||||||
|
>
|
||||||
|
> Wofür und auf welcher Grundlage? Wir brauchen die Daten, um deinen Termin zu verwalten, dir den Termin zu bestätigen und dich über Änderungen oder Absagen zu informieren. Rechtsgrundlage ist Art. 6 Abs. 1 lit. b DSGVO (Vertrag über den gebuchten Termin, bei einer Terminanfrage vorvertragliche Maßnahmen auf deine Anfrage). Wenn du eine Erinnerung vor dem Termin wünschst, beruht diese auf deiner Einwilligung nach Art. 6 Abs. 1 lit. a DSGVO. Du kannst sie jederzeit mit Wirkung für die Zukunft widerrufen, indem du uns eine kurze Nachricht sendest oder den Termin über den Link in der E-Mail absagst. Widerrufst du einen Vertrag über die Schaltfläche „Vertrag widerrufen“, speichern wir deine Erklärung mit Datum und Uhrzeit und senden dir eine Eingangsbestätigung (Art. 6 Abs. 1 lit. c DSGVO in Verbindung mit § 356a BGB). Zum Schutz vor Missbrauch (zum Beispiel automatisierten Buchungen) und um die Zahl der Anfragen zu begrenzen, merken wir uns deine IP-Adresse kurzzeitig nur im Arbeitsspeicher des Servers, nicht dauerhaft (Art. 6 Abs. 1 lit. f DSGVO; unser berechtigtes Interesse ist der sichere Betrieb der Terminbuchung).
|
||||||
|
>
|
||||||
|
> E-Mails. Du erhältst eine E-Mail, in der du deine E-Mail-Adresse und den Termin per Link bestätigst, danach eine Bestätigung und, wenn du das angekreuzt hast, eine Erinnerung. Sagen wir den Termin ab oder verschieben ihn, informieren wir dich ebenfalls per E-Mail. Die E-Mails enthalten keine Werbung und keine Tracking-Pixel. Absender ist {{Name des Betriebs}}, deine Antworten gehen an uns. Bis zum Versand liegt die E-Mail in einer Warteschlange; nach dem Versand wird dort die Empfängeradresse samt Inhalt gelöscht, nicht zustellbare E-Mails nach 48 Stunden.
|
||||||
|
>
|
||||||
|
> Wer verarbeitet die Daten in unserem Auftrag? Den Terminkalender betreibt die WEBklar GbR, Am Schwimmbad 10, 67722 Winnweiler, als unser Auftragsverarbeiter nach Art. 28 DSGVO. Die Daten liegen auf Servern der Hetzner Online GmbH, Industriestraße 25, 91710 Gunzenhausen, in Deutschland (Rechenzentren Falkenstein und Nürnberg). Der E-Mail-Versand läuft ebenfalls über Hetzner. Eine Übermittlung in Drittländer findet nicht statt.
|
||||||
|
>
|
||||||
|
> Beim Aufruf der Terminbuchung. Beim Aufruf dieser Seite lädt dein Browser das Programm der Terminbuchung vom Server der WEBklar GbR (project.webklar.com), beim Öffnen der Terminbuchung zusätzlich die Termine und Texte. Dabei werden technisch bedingt deine IP-Adresse und Angaben zu deinem Browser übertragen. Der Server speichert sie kurzzeitig in Protokollen und löscht sie nach 14 Tagen (Art. 6 Abs. 1 lit. f DSGVO; berechtigtes Interesse ist der sichere und funktionsfähige Betrieb). Wir setzen dafür keine Cookies und speichern nichts auf deinem Gerät.
|
||||||
|
>
|
||||||
|
> Wie lange speichern wir? Nicht bestätigte Buchungen (der Bestätigungslink gilt 30 Minuten) löschen wir 48 Stunden nach Ablauf dieser Frist. Bestätigte Termine löschen wir 90 Tage nach dem Termin, abgesagte oder abgelehnte Termine 30 Tage nach der Absage oder Ablehnung. Terminanfragen, die wir bis zum Terminbeginn nicht beantworten, behandeln wir als abgelehnt. Wird die Online-Terminbuchung abgeschaltet, zum Beispiel weil unser Vertrag mit WEBklar endet, werden alle Termindaten 30 Tage nach dem Abschalten gelöscht. Gesetzliche Aufbewahrungspflichten für Geschäftsunterlagen (zum Beispiel Rechnungen) bleiben davon unberührt; sie erfüllen wir außerhalb des Terminkalenders.
|
||||||
|
>
|
||||||
|
> Pflicht zur Angabe. Ohne Name, E-Mail-Adresse und Terminwahl können wir keinen Termin online vergeben. Du kannst uns auch telefonisch erreichen: {{Telefon}}. Eine automatisierte Entscheidungsfindung findet nicht statt. Bitte mach bei der Buchung keine Angaben zu deiner Gesundheit.
|
||||||
|
>
|
||||||
|
> Deine Rechte. Du hast das Recht auf Auskunft, Berichtigung, Löschung, Einschränkung der Verarbeitung und Datenübertragbarkeit sowie das Recht, der Verarbeitung auf Grundlage berechtigter Interessen zu widersprechen (Art. 15 bis 21 DSGVO). Eine erteilte Einwilligung kannst du jederzeit widerrufen. Du kannst dich bei einer Datenschutzaufsichtsbehörde beschweren. Verantwortlich ist {{Name des Betriebs}}, {{Anschrift}}, E-Mail {{E-Mail}}.
|
||||||
|
|
||||||
|
## 7. Nachweis
|
||||||
|
|
||||||
|
Der Nachweis speichert Hashes der Texte mit den Betriebsangaben zum Buchungszeitpunkt (⚖ Q26).
|
||||||
@@ -2,14 +2,14 @@ ENTWURF – nur Pilot, vor Einsatz bei fremden Betrieben prüfen
|
|||||||
|
|
||||||
# Datenschutz-Absatz „Online-Terminbuchung“ für die Website des Betriebs
|
# Datenschutz-Absatz „Online-Terminbuchung“ für die Website des Betriebs
|
||||||
|
|
||||||
Fassung 2026-10-02. Grundlage: `recherche-recht.md` Abschnitt 11.1. Der Absatz gehört in die Datenschutzerklärung der Website des **Betriebs** (der Betrieb ist Verantwortlicher, WEBklar Auftragsverarbeiter). Die Einbauanleitung für Mitarbeiter (`docs/funktionen/termine-einbau.md`, TP2) verweist hierher. Für die Datenschutzerklärung von WEBklar selbst gilt `termine-datenschutz-6c-entwurf.md`.
|
Fassung 2026-10-05. Grundlage: `recherche-recht.md` Abschnitt 11.1. Der Absatz gehört in die Datenschutzerklärung der Website des **Betriebs** (der Betrieb ist Verantwortlicher, WEBklar Auftragsverarbeiter). Die Einbauanleitung für Mitarbeiter (`docs/funktionen/termine-einbau.md`, TP2) verweist hierher. Für die Datenschutzerklärung von WEBklar selbst gilt `termine-datenschutz-6c-entwurf.md`.
|
||||||
|
|
||||||
Zwei Fassungen weiter unten: Sie und Du. Nimm die Fassung, die zur Datenschutzerklärung der Website passt (in der Regel zur `anrede` des Betriebs).
|
Zwei Fassungen weiter unten: Sie und Du. Nimm die Fassung, die zur Datenschutzerklärung der Website passt (in der Regel zur `anrede` des Betriebs).
|
||||||
|
|
||||||
## Platzhalter und optionale Sätze
|
## Platzhalter und optionale Sätze
|
||||||
|
|
||||||
- `{{Firma}}`, `{{Anschrift}}`, `{{E-Mail}}`, `{{Telefon}}`: Betriebsangaben aus der Einstellung. `{{Tage}}`: Aufbewahrung nach dem Termin (`aufbewahrungTage`, Vorgabe 90, einstellbar 14 bis 365).
|
- `{{Firma}}`, `{{Anschrift}}`, `{{E-Mail}}`, `{{Telefon}}`: Betriebsangaben aus der Einstellung. `{{Tage}}`: Aufbewahrung nach dem Termin (`aufbewahrungTage`, Vorgabe 90, einstellbar 14 bis 365).
|
||||||
- `[[wenn …: …]]`: Satz oder Satzteil nur aufnehmen, wenn die genannte Einstellung aktiv ist; sonst samt Klammern streichen.
|
- `[[wenn …: …]]`: Satz oder Satzteil nur aufnehmen, wenn die genannte Einstellung aktiv ist; sonst samt Klammern streichen. Beginnt der Satzteil mit „und“, steht nach dem Doppelpunkt ein zweites Leerzeichen: Es ist das Leerzeichen zwischen dem Vordertext und „und“ und fällt mit dem Satzteil weg.
|
||||||
|
|
||||||
## Löschfristen (Vorgaben des Löschlaufs, im Text unten verwendet)
|
## Löschfristen (Vorgaben des Löschlaufs, im Text unten verwendet)
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ Zwei Fassungen weiter unten: Sie und Du. Nimm die Fassung, die zur Datenschutzer
|
|||||||
>
|
>
|
||||||
> **Beim Aufruf der Terminbuchung.** Beim Aufruf dieser Seite lädt Ihr Browser das Programm der Terminbuchung vom Server der WEBklar GbR (project.webklar.com), beim Öffnen der Terminbuchung zusätzlich die Termine und Texte. Dabei werden technisch bedingt Ihre IP-Adresse und Angaben zu Ihrem Browser übertragen. Der Server speichert sie kurzzeitig in Protokollen und löscht sie nach 14 Tagen (Art. 6 Abs. 1 lit. f DSGVO; berechtigtes Interesse ist der sichere und funktionsfähige Betrieb). Wir setzen dafür keine Cookies und speichern nichts auf Ihrem Gerät.
|
> **Beim Aufruf der Terminbuchung.** Beim Aufruf dieser Seite lädt Ihr Browser das Programm der Terminbuchung vom Server der WEBklar GbR (project.webklar.com), beim Öffnen der Terminbuchung zusätzlich die Termine und Texte. Dabei werden technisch bedingt Ihre IP-Adresse und Angaben zu Ihrem Browser übertragen. Der Server speichert sie kurzzeitig in Protokollen und löscht sie nach 14 Tagen (Art. 6 Abs. 1 lit. f DSGVO; berechtigtes Interesse ist der sichere und funktionsfähige Betrieb). Wir setzen dafür keine Cookies und speichern nichts auf Ihrem Gerät.
|
||||||
>
|
>
|
||||||
> **Wie lange speichern wir?** Nicht bestätigte Buchungen (der Bestätigungslink gilt 30 Minuten) löschen wir 48 Stunden nach Ablauf dieser Frist. Bestätigte Termine löschen wir {{Tage}} Tage nach dem Termin, abgesagte oder abgelehnte Termine 30 Tage nach der Absage oder Ablehnung. Terminanfragen, die wir bis zum Terminbeginn nicht beantworten, behandeln wir als abgelehnt. Wird die Online-Terminbuchung abgeschaltet, löschen wir alle Termindaten 30 Tage nach dem Abschalten. Gesetzliche Aufbewahrungspflichten für Geschäftsunterlagen (zum Beispiel Rechnungen) bleiben davon unberührt; sie erfüllen wir außerhalb des Terminkalenders.
|
> **Wie lange speichern wir?** Nicht bestätigte Buchungen (der Bestätigungslink gilt 30 Minuten) löschen wir 48 Stunden nach Ablauf dieser Frist. Bestätigte Termine löschen wir {{Tage}} Tage nach dem Termin, abgesagte oder abgelehnte Termine 30 Tage nach der Absage oder Ablehnung. Terminanfragen, die wir bis zum Terminbeginn nicht beantworten, behandeln wir als abgelehnt. Wird die Online-Terminbuchung abgeschaltet, zum Beispiel weil unser Vertrag mit WEBklar endet, werden alle Termindaten 30 Tage nach dem Abschalten gelöscht. Gesetzliche Aufbewahrungspflichten für Geschäftsunterlagen (zum Beispiel Rechnungen) bleiben davon unberührt; sie erfüllen wir außerhalb des Terminkalenders.
|
||||||
>
|
>
|
||||||
> **Pflicht zur Angabe.** Ohne Name, E-Mail-Adresse und Terminwahl können wir keinen Termin online vergeben. [[wenn Telefon hinterlegt: Sie können uns auch telefonisch erreichen: {{Telefon}}.]] Eine automatisierte Entscheidungsfindung findet nicht statt. Bitte machen Sie bei der Buchung keine Angaben zu Ihrer Gesundheit.
|
> **Pflicht zur Angabe.** Ohne Name, E-Mail-Adresse und Terminwahl können wir keinen Termin online vergeben. [[wenn Telefon hinterlegt: Sie können uns auch telefonisch erreichen: {{Telefon}}.]] Eine automatisierte Entscheidungsfindung findet nicht statt. Bitte machen Sie bei der Buchung keine Angaben zu Ihrer Gesundheit.
|
||||||
>
|
>
|
||||||
@@ -77,7 +77,7 @@ Zwei Fassungen weiter unten: Sie und Du. Nimm die Fassung, die zur Datenschutzer
|
|||||||
>
|
>
|
||||||
> **Beim Aufruf der Terminbuchung.** Beim Aufruf dieser Seite lädt dein Browser das Programm der Terminbuchung vom Server der WEBklar GbR (project.webklar.com), beim Öffnen der Terminbuchung zusätzlich die Termine und Texte. Dabei werden technisch bedingt deine IP-Adresse und Angaben zu deinem Browser übertragen. Der Server speichert sie kurzzeitig in Protokollen und löscht sie nach 14 Tagen (Art. 6 Abs. 1 lit. f DSGVO; berechtigtes Interesse ist der sichere und funktionsfähige Betrieb). Wir setzen dafür keine Cookies und speichern nichts auf deinem Gerät.
|
> **Beim Aufruf der Terminbuchung.** Beim Aufruf dieser Seite lädt dein Browser das Programm der Terminbuchung vom Server der WEBklar GbR (project.webklar.com), beim Öffnen der Terminbuchung zusätzlich die Termine und Texte. Dabei werden technisch bedingt deine IP-Adresse und Angaben zu deinem Browser übertragen. Der Server speichert sie kurzzeitig in Protokollen und löscht sie nach 14 Tagen (Art. 6 Abs. 1 lit. f DSGVO; berechtigtes Interesse ist der sichere und funktionsfähige Betrieb). Wir setzen dafür keine Cookies und speichern nichts auf deinem Gerät.
|
||||||
>
|
>
|
||||||
> **Wie lange speichern wir?** Nicht bestätigte Buchungen (der Bestätigungslink gilt 30 Minuten) löschen wir 48 Stunden nach Ablauf dieser Frist. Bestätigte Termine löschen wir {{Tage}} Tage nach dem Termin, abgesagte oder abgelehnte Termine 30 Tage nach der Absage oder Ablehnung. Terminanfragen, die wir bis zum Terminbeginn nicht beantworten, behandeln wir als abgelehnt. Wird die Online-Terminbuchung abgeschaltet, löschen wir alle Termindaten 30 Tage nach dem Abschalten. Gesetzliche Aufbewahrungspflichten für Geschäftsunterlagen (zum Beispiel Rechnungen) bleiben davon unberührt; sie erfüllen wir außerhalb des Terminkalenders.
|
> **Wie lange speichern wir?** Nicht bestätigte Buchungen (der Bestätigungslink gilt 30 Minuten) löschen wir 48 Stunden nach Ablauf dieser Frist. Bestätigte Termine löschen wir {{Tage}} Tage nach dem Termin, abgesagte oder abgelehnte Termine 30 Tage nach der Absage oder Ablehnung. Terminanfragen, die wir bis zum Terminbeginn nicht beantworten, behandeln wir als abgelehnt. Wird die Online-Terminbuchung abgeschaltet, zum Beispiel weil unser Vertrag mit WEBklar endet, werden alle Termindaten 30 Tage nach dem Abschalten gelöscht. Gesetzliche Aufbewahrungspflichten für Geschäftsunterlagen (zum Beispiel Rechnungen) bleiben davon unberührt; sie erfüllen wir außerhalb des Terminkalenders.
|
||||||
>
|
>
|
||||||
> **Pflicht zur Angabe.** Ohne Name, E-Mail-Adresse und Terminwahl können wir keinen Termin online vergeben. [[wenn Telefon hinterlegt: Du kannst uns auch telefonisch erreichen: {{Telefon}}.]] Eine automatisierte Entscheidungsfindung findet nicht statt. Bitte mach bei der Buchung keine Angaben zu deiner Gesundheit.
|
> **Pflicht zur Angabe.** Ohne Name, E-Mail-Adresse und Terminwahl können wir keinen Termin online vergeben. [[wenn Telefon hinterlegt: Du kannst uns auch telefonisch erreichen: {{Telefon}}.]] Eine automatisierte Entscheidungsfindung findet nicht statt. Bitte mach bei der Buchung keine Angaben zu deiner Gesundheit.
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ ENTWURF – nur Pilot, vor Einsatz bei fremden Betrieben prüfen
|
|||||||
|
|
||||||
# Hinweise, Haken und Knopftexte für Gäste (Terminplaner)
|
# Hinweise, Haken und Knopftexte für Gäste (Terminplaner)
|
||||||
|
|
||||||
Fassung 2026-10-02. Grundlage: `recherche-recht.md` Abschnitt 11.2 (Hinweis unter dem Formular) und 11.3, `recherche-recht-zahlung.md` Abschnitt 7.3 C bis E (Haken, Widerrufshinweis, Widerrufsfunktion). Die Texte in den Abschnitten 1 bis 3 sind die Fassung aus `server/services/termine/rechtstexte.js` (Platzhalter in doppelten geschweiften Klammern); `test/termineRechtstexte.test.js` prüft, dass sie hier wörtlich stehen. Ändere sie deshalb nur in `rechtstexte.js` und übernimm sie hierher. Die Abschnitte 4 bis 6 sind reine Entwurfstexte für das Widget und die Mails; sie stehen nicht im Code.
|
Fassung 2026-10-05. Grundlage: `recherche-recht.md` Abschnitt 11.2 (Hinweis unter dem Formular) und 11.3, `recherche-recht-zahlung.md` Abschnitt 7.3 C bis E (Haken, Widerrufshinweis, Widerrufsfunktion). Die Texte in den Abschnitten 1 bis 3 sind die Fassung aus `server/services/termine/rechtstexte.js` (Platzhalter in doppelten geschweiften Klammern); `test/termineRechtstexte.test.js` prüft, dass sie hier wörtlich stehen. Ändere sie deshalb nur in `rechtstexte.js` und übernimm sie hierher. Die Abschnitte 4 bis 6 sind reine Entwurfstexte für das Widget und die Mails; sie stehen nicht im Code.
|
||||||
|
|
||||||
Platzhalter: `{{Firma}}` = Betriebsname, `{{Anschrift}}` = Anschrift des Betriebs, `{{n}}` = Stunden vor dem Termin (`erinnerung.stundenVorher`: 2, 12, 24 oder 48). Die Texte zeigen die Vorgabe von 90 Tagen Aufbewahrung nach dem Termin; der Wert kommt aus `aufbewahrungTage` (14 bis 365) der Einstellung.
|
Platzhalter: `{{Firma}}` = Betriebsname, `{{Anschrift}}` = Anschrift des Betriebs, `{{n}}` = Stunden vor dem Termin (`erinnerung.stundenVorher`: 2, 12, 24 oder 48). Die Texte zeigen die Vorgabe von 90 Tagen Aufbewahrung nach dem Termin; der Wert kommt aus `aufbewahrungTage` (14 bis 365) der Einstellung.
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ Wortlaut immer in der Sie-Form, auch bei Betrieben, die ihre Gäste duzen: Es si
|
|||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| Vorzeitiger Beginn | Verbraucher, mit Preis: bei sofort buchbaren Leistungen, wenn der Terminbeginn nicht nach dem Ende der Widerrufsfrist liegt (14 Tage ab dem Tag nach dem Vertragsschluss, Berliner Kalender, §§ 187, 188, 193 BGB; gerechnet mit dem Zeitpunkt der Buchung plus Haltezeit; R29); bei bepreisten Anfragen immer, weil der Tag der Annahme nicht feststeht (R31) | ja | Ich verlange ausdrücklich, dass {{Firma}} mit der Leistung vor Ablauf der Widerrufsfrist beginnt. Mir ist bekannt, dass mein Widerrufsrecht mit der vollständigen Erbringung der Leistung erlischt (§ 356 Abs. 5 Nr. 2 BGB). Widerrufe ich vor der vollständigen Erbringung, schulde ich einen angemessenen Betrag für die bis dahin erbrachte Leistung (§ 357a Abs. 2 BGB). |
|
| Vorzeitiger Beginn | Verbraucher, mit Preis: bei sofort buchbaren Leistungen, wenn der Terminbeginn nicht nach dem Ende der Widerrufsfrist liegt (14 Tage ab dem Tag nach dem Vertragsschluss, Berliner Kalender, §§ 187, 188, 193 BGB; gerechnet mit dem Zeitpunkt der Buchung plus Haltezeit; R29); bei bepreisten Anfragen immer, weil der Tag der Annahme nicht feststeht (R31) | ja | Ich verlange ausdrücklich, dass {{Firma}} mit der Leistung vor Ablauf der Widerrufsfrist beginnt. Mir ist bekannt, dass mein Widerrufsrecht mit der vollständigen Erbringung der Leistung erlischt (§ 356 Abs. 5 Nr. 2 BGB). Widerrufe ich vor der vollständigen Erbringung, schulde ich einen angemessenen Betrag für die bis dahin erbrachte Leistung (§ 357a Abs. 2 BGB). |
|
||||||
| Stornobedingungen | der Betrieb hat Stornobedingungen hinterlegt | ja | Ich habe die Stornobedingungen gelesen. |
|
| Stornobedingungen | der Betrieb hat Stornobedingungen hinterlegt | ja | Ich habe die Stornobedingungen gelesen. |
|
||||||
| Erinnerung | der Betrieb bietet Erinnerungen an | nein | Ich möchte {{n}} Stunden vor dem Termin eine Erinnerung per E-Mail erhalten. |
|
| Erinnerung | der Betrieb bietet Erinnerungen an | nein | Ich möchte {{n}} Stunden vor dem Termin eine Erinnerung per E-Mail erhalten. Diese Einwilligung kann ich jederzeit widerrufen. |
|
||||||
|
|
||||||
## 3. Hinweis über dem Nachrichtenfeld
|
## 3. Hinweis über dem Nachrichtenfeld
|
||||||
|
|
||||||
@@ -80,6 +80,6 @@ Der Eingang muss mit Datum und Uhrzeit bestätigt werden (§ 356a BGB); das ist
|
|||||||
## Für die Rechtsprüfung und das Widget offen
|
## Für die Rechtsprüfung und das Widget offen
|
||||||
|
|
||||||
1. **Belehrung vor dem Vertragsschluss** (erledigt durch R14, R55): Das Widget zeigt die vollständige Belehrung samt Muster-Widerrufsformular (`texte.widerrufsbelehrung`, aufklappbar) in der Zusammenfassung vor dem Knopf, bei jeder Verbraucher-Buchung auch bei Anfragen; der Nachweis der Buchung hält ihren Hash fest. Die Bestätigungs-E-Mail wiederholt sie. Offen bleibt nur, ob der Rechtsprüfer die aufklappbare Darstellung genügen lässt.
|
1. **Belehrung vor dem Vertragsschluss** (erledigt durch R14, R55): Das Widget zeigt die vollständige Belehrung samt Muster-Widerrufsformular (`texte.widerrufsbelehrung`, aufklappbar) in der Zusammenfassung vor dem Knopf, bei jeder Verbraucher-Buchung auch bei Anfragen; der Nachweis der Buchung hält ihren Hash fest. Die Bestätigungs-E-Mail wiederholt sie. Offen bleibt nur, ob der Rechtsprüfer die aufklappbare Darstellung genügen lässt.
|
||||||
2. **Widerruf der Einwilligung zur Erinnerung**: Der feste Wortlaut des Hakens (Abschnitt 2) enthält den Hinweis auf den Widerruf nicht; er steht im Datenschutz-Absatz (`termine-datenschutz-absatz.md`). Prüfen, ob ein kurzer Zusatz unter dem Haken nötig ist („Sie können diese Einwilligung jederzeit widerrufen.“).
|
2. **Widerruf der Einwilligung zur Erinnerung**: Der feste Wortlaut des Hakens (Abschnitt 2) enthält seit Fassung 2026-10-05 den Satz „Diese Einwilligung kann ich jederzeit widerrufen.“ (Art. 7 Abs. 3 DSGVO); die Einzelheiten stehen im Datenschutz-Absatz (`termine-datenschutz-absatz.md`). Prüfen, ob dieser Satz am Haken genügt, ohne Link zur Datenschutzerklärung und zum Impressum direkt am Haken (⚖ Q17).
|
||||||
3. **Link zur Datenschutzerklärung** neben dem Hinweis: `betrieb.datenschutzUrl` ist in der Einstellung optional. Ohne Link verweist der Hinweis auf eine Datenschutzerklärung, die der Gast nicht findet; das Portal sollte die Adresse vor dem Einschalten verlangen.
|
3. **Link zur Datenschutzerklärung** neben dem Hinweis: `betrieb.datenschutzUrl` ist in der Einstellung optional. Ohne Link verweist der Hinweis auf eine Datenschutzerklärung, die der Gast nicht findet; das Portal sollte die Adresse vor dem Einschalten verlangen.
|
||||||
4. **Bestätigungsfrist**: Der Hinweis nennt „48 Stunden nach Ablauf der Bestätigungsfrist“. Der Löschlauf läuft in Abständen; „48 Stunden“ ist die früheste Löschung, nicht eine Sekundengarantie.
|
4. **Bestätigungsfrist**: Der Hinweis nennt „48 Stunden nach Ablauf der Bestätigungsfrist“. Der Löschlauf läuft in Abständen; „48 Stunden“ ist die früheste Löschung, nicht eine Sekundengarantie.
|
||||||
@@ -2,7 +2,7 @@ ENTWURF – nur Pilot, vor Einsatz bei fremden Betrieben prüfen
|
|||||||
|
|
||||||
# Widerrufsbelehrung und Muster-Widerrufsformular (Terminplaner, Dienstleistung)
|
# Widerrufsbelehrung und Muster-Widerrufsformular (Terminplaner, Dienstleistung)
|
||||||
|
|
||||||
Fassung 2026-10-02. Quelle des Wortlauts: Anlage 1 und Anlage 2 zu Art. 246a § 1 Abs. 2 Satz 2 EGBGB (Fundstelle BGBl. I 2013, S. 3663 bis 3665), abgerufen am 2026-10-02 von gesetze-im-internet.de; die Textdateien liegen unverändert in `docs/rechtliches/quellen/anlage-1.txt` und `anlage-2.txt`.
|
Fassung 2026-10-05. Quelle des Wortlauts: Anlage 1 und Anlage 2 zu Art. 246a § 1 Abs. 2 Satz 2 EGBGB (Fundstelle BGBl. I 2013, S. 3663 bis 3665), abgerufen am 2026-10-02 von gesetze-im-internet.de; die Textdateien liegen unverändert in `docs/rechtliches/quellen/anlage-1.txt` und `anlage-2.txt`.
|
||||||
|
|
||||||
Der Text unten ist die Fassung, die `widerrufsbelehrung(betrieb)` in `server/services/termine/rechtstexte.js` erzeugt, mit Platzhaltern statt der Betriebsdaten. `test/termineRechtstexte.test.js` prüft, dass (a) jeder Absatz des Musters wörtlich aus den Quelldateien im Ergebnis steht und (b) der Textblock unten genau der Ausgabe der Funktion entspricht. Ändere den Text deshalb nur in `rechtstexte.js` und übernimm ihn hierher.
|
Der Text unten ist die Fassung, die `widerrufsbelehrung(betrieb)` in `server/services/termine/rechtstexte.js` erzeugt, mit Platzhaltern statt der Betriebsdaten. `test/termineRechtstexte.test.js` prüft, dass (a) jeder Absatz des Musters wörtlich aus den Quelldateien im Ergebnis steht und (b) der Textblock unten genau der Ausgabe der Funktion entspricht. Ändere den Text deshalb nur in `rechtstexte.js` und übernimm ihn hierher.
|
||||||
|
|
||||||
|
|||||||
@@ -21,9 +21,15 @@
|
|||||||
* Die Rueckgabe ist reiner Text mit Zeilenumbruechen und NICHT HTML-maskiert: Die Mailvorlage maskiert beim Einsetzen.
|
* Die Rueckgabe ist reiner Text mit Zeilenumbruechen und NICHT HTML-maskiert: Die Mailvorlage maskiert beim Einsetzen.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { config } from '../../config.js'
|
||||||
import { zeigtWiderruf } from './einstellungen.js'
|
import { zeigtWiderruf } from './einstellungen.js'
|
||||||
|
|
||||||
export const RECHTSTEXTE_VERSION = '2026-10-02'
|
/**
|
||||||
|
* Fassung der Gaestetexte (steht im Nachweis jeder Buchung, archiviert in docs/rechtliches/archiv/termine-gaestetexte-<Fassung>.md).
|
||||||
|
* Gleich config.termine.avvVersion (test/termineRechtstexte.test.js haelt beide gleich); eine Textaenderung nach der Auslieferung
|
||||||
|
* bekommt ein neues Datum, ein neues Archiv und einen neuen Hash-Pin. Buchungen aus aelteren Fassungen behalten ihre Fassung.
|
||||||
|
*/
|
||||||
|
export const RECHTSTEXTE_VERSION = '2026-10-05'
|
||||||
|
|
||||||
/** true, solange die Rechtstexte als ENTWURF gelten (Kopf KOPF der Dokumente in docs/rechtliches/); Block F setzt false zusammen mit den Koepfen. */
|
/** true, solange die Rechtstexte als ENTWURF gelten (Kopf KOPF der Dokumente in docs/rechtliches/); Block F setzt false zusammen mit den Koepfen. */
|
||||||
export const RECHTSTEXTE_ENTWURF = true
|
export const RECHTSTEXTE_ENTWURF = true
|
||||||
@@ -225,7 +231,7 @@ function datenschutzBedingungen(einstellung, daten) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Absaetze der Sie-Fassung, je Absatz eine kleine Funktion; d = { firma, verantwortlich, email, telefon, tage, ...Bedingungen }
|
// Absaetze der Sie-Fassung, je Absatz eine kleine Funktion; d = { firma, verantwortlich, email, telefon, tage, haltMinuten, ...Bedingungen }
|
||||||
const DS_SIE = [
|
const DS_SIE = [
|
||||||
() => 'Online-Terminbuchung',
|
() => 'Online-Terminbuchung',
|
||||||
() => 'Auf unserer Website können Sie Termine online buchen. Dafür verarbeiten wir die folgenden Daten:',
|
() => 'Auf unserer Website können Sie Termine online buchen. Dafür verarbeiten wir die folgenden Daten:',
|
||||||
@@ -267,10 +273,10 @@ const DS_SIE = [
|
|||||||
+ `Der Server speichert sie kurzzeitig in Protokollen und löscht sie nach ${DS_PROTOKOLL_TAGE} Tagen (Art. 6 Abs. 1 lit. f DSGVO; berechtigtes Interesse ist der sichere und funktionsfähige Betrieb). `
|
+ `Der Server speichert sie kurzzeitig in Protokollen und löscht sie nach ${DS_PROTOKOLL_TAGE} Tagen (Art. 6 Abs. 1 lit. f DSGVO; berechtigtes Interesse ist der sichere und funktionsfähige Betrieb). `
|
||||||
+ 'Wir setzen dafür keine Cookies und speichern nichts auf Ihrem Gerät.',
|
+ 'Wir setzen dafür keine Cookies und speichern nichts auf Ihrem Gerät.',
|
||||||
(d) =>
|
(d) =>
|
||||||
`Wie lange speichern wir? Nicht bestätigte Buchungen (der Bestätigungslink gilt 30 Minuten) löschen wir ${LOESCHFRIST_UNBESTAETIGT_STUNDEN} Stunden nach Ablauf dieser Frist. `
|
`Wie lange speichern wir? Nicht bestätigte Buchungen (der Bestätigungslink gilt ${d.haltMinuten} Minuten) löschen wir ${LOESCHFRIST_UNBESTAETIGT_STUNDEN} Stunden nach Ablauf dieser Frist. `
|
||||||
+ `Bestätigte Termine löschen wir ${d.tage} Tage nach dem Termin, abgesagte oder abgelehnte Termine ${LOESCHFRIST_ABGESAGT_TAGE} Tage nach der Absage oder Ablehnung. `
|
+ `Bestätigte Termine löschen wir ${d.tage} Tage nach dem Termin, abgesagte oder abgelehnte Termine ${LOESCHFRIST_ABGESAGT_TAGE} Tage nach der Absage oder Ablehnung. `
|
||||||
+ 'Terminanfragen, die wir bis zum Terminbeginn nicht beantworten, behandeln wir als abgelehnt. '
|
+ 'Terminanfragen, die wir bis zum Terminbeginn nicht beantworten, behandeln wir als abgelehnt. '
|
||||||
+ 'Wird die Online-Terminbuchung abgeschaltet, löschen wir alle Termindaten 30 Tage nach dem Abschalten. '
|
+ 'Wird die Online-Terminbuchung abgeschaltet, zum Beispiel weil unser Vertrag mit WEBklar endet, werden alle Termindaten 30 Tage nach dem Abschalten gelöscht. '
|
||||||
+ 'Gesetzliche Aufbewahrungspflichten für Geschäftsunterlagen (zum Beispiel Rechnungen) bleiben davon unberührt; sie erfüllen wir außerhalb des Terminkalenders.',
|
+ 'Gesetzliche Aufbewahrungspflichten für Geschäftsunterlagen (zum Beispiel Rechnungen) bleiben davon unberührt; sie erfüllen wir außerhalb des Terminkalenders.',
|
||||||
(d) =>
|
(d) =>
|
||||||
saetze(
|
saetze(
|
||||||
@@ -326,10 +332,10 @@ const DS_DU = [
|
|||||||
+ `Der Server speichert sie kurzzeitig in Protokollen und löscht sie nach ${DS_PROTOKOLL_TAGE} Tagen (Art. 6 Abs. 1 lit. f DSGVO; berechtigtes Interesse ist der sichere und funktionsfähige Betrieb). `
|
+ `Der Server speichert sie kurzzeitig in Protokollen und löscht sie nach ${DS_PROTOKOLL_TAGE} Tagen (Art. 6 Abs. 1 lit. f DSGVO; berechtigtes Interesse ist der sichere und funktionsfähige Betrieb). `
|
||||||
+ 'Wir setzen dafür keine Cookies und speichern nichts auf deinem Gerät.',
|
+ 'Wir setzen dafür keine Cookies und speichern nichts auf deinem Gerät.',
|
||||||
(d) =>
|
(d) =>
|
||||||
`Wie lange speichern wir? Nicht bestätigte Buchungen (der Bestätigungslink gilt 30 Minuten) löschen wir ${LOESCHFRIST_UNBESTAETIGT_STUNDEN} Stunden nach Ablauf dieser Frist. `
|
`Wie lange speichern wir? Nicht bestätigte Buchungen (der Bestätigungslink gilt ${d.haltMinuten} Minuten) löschen wir ${LOESCHFRIST_UNBESTAETIGT_STUNDEN} Stunden nach Ablauf dieser Frist. `
|
||||||
+ `Bestätigte Termine löschen wir ${d.tage} Tage nach dem Termin, abgesagte oder abgelehnte Termine ${LOESCHFRIST_ABGESAGT_TAGE} Tage nach der Absage oder Ablehnung. `
|
+ `Bestätigte Termine löschen wir ${d.tage} Tage nach dem Termin, abgesagte oder abgelehnte Termine ${LOESCHFRIST_ABGESAGT_TAGE} Tage nach der Absage oder Ablehnung. `
|
||||||
+ 'Terminanfragen, die wir bis zum Terminbeginn nicht beantworten, behandeln wir als abgelehnt. '
|
+ 'Terminanfragen, die wir bis zum Terminbeginn nicht beantworten, behandeln wir als abgelehnt. '
|
||||||
+ 'Wird die Online-Terminbuchung abgeschaltet, löschen wir alle Termindaten 30 Tage nach dem Abschalten. '
|
+ 'Wird die Online-Terminbuchung abgeschaltet, zum Beispiel weil unser Vertrag mit WEBklar endet, werden alle Termindaten 30 Tage nach dem Abschalten gelöscht. '
|
||||||
+ 'Gesetzliche Aufbewahrungspflichten für Geschäftsunterlagen (zum Beispiel Rechnungen) bleiben davon unberührt; sie erfüllen wir außerhalb des Terminkalenders.',
|
+ 'Gesetzliche Aufbewahrungspflichten für Geschäftsunterlagen (zum Beispiel Rechnungen) bleiben davon unberührt; sie erfüllen wir außerhalb des Terminkalenders.',
|
||||||
(d) =>
|
(d) =>
|
||||||
saetze(
|
saetze(
|
||||||
@@ -361,6 +367,7 @@ export function datenschutzAbsatz(einstellung, anrede) {
|
|||||||
email: daten.email,
|
email: daten.email,
|
||||||
telefonnummer: daten.telefon,
|
telefonnummer: daten.telefon,
|
||||||
tage: aufbewahrungNachTermin(einstellung),
|
tage: aufbewahrungNachTermin(einstellung),
|
||||||
|
haltMinuten: config.termine.holdMinutes, // Gueltigkeit des Bestaetigungslinks, bei jedem Aufruf gelesen
|
||||||
}
|
}
|
||||||
return absaetze.map((absatz) => absatz(d)).join(ABSATZ)
|
return absaetze.map((absatz) => absatz(d)).join(ABSATZ)
|
||||||
}
|
}
|
||||||
@@ -380,8 +387,12 @@ export const ZUSTIMMUNG_VORZEITIG = (name) =>
|
|||||||
/** Pflicht-Haken, wenn der Betrieb Stornobedingungen hat. */
|
/** Pflicht-Haken, wenn der Betrieb Stornobedingungen hat. */
|
||||||
export const ZUSTIMMUNG_STORNO = 'Ich habe die Stornobedingungen gelesen.'
|
export const ZUSTIMMUNG_STORNO = 'Ich habe die Stornobedingungen gelesen.'
|
||||||
|
|
||||||
/** Freiwilliger Haken (nie vorangekreuzt), nur wenn der Betrieb Erinnerungen anbietet; stunden = einstellung.erinnerung.stundenVorher. */
|
/**
|
||||||
export const ZUSTIMMUNG_ERINNERUNG = (stunden) => `Ich möchte ${stunden} Stunden vor dem Termin eine Erinnerung per E-Mail erhalten.`
|
* Freiwilliger Haken (nie vorangekreuzt), nur wenn der Betrieb Erinnerungen anbietet; stunden = einstellung.erinnerung.stundenVorher.
|
||||||
|
* Der zweite Satz weist auf das Widerrufsrecht der Einwilligung hin (Art. 7 Abs. 3 DSGVO); der Hash im Nachweis deckt beide Saetze.
|
||||||
|
*/
|
||||||
|
export const ZUSTIMMUNG_ERINNERUNG = (stunden) =>
|
||||||
|
`Ich möchte ${stunden} Stunden vor dem Termin eine Erinnerung per E-Mail erhalten. Diese Einwilligung kann ich jederzeit widerrufen.`
|
||||||
|
|
||||||
/** Hinweis ueber dem Nachrichtenfeld (Gesundheitsbereich ist ausgeschlossen, Spezifikation Abschnitt 4). */
|
/** Hinweis ueber dem Nachrichtenfeld (Gesundheitsbereich ist ausgeschlossen, Spezifikation Abschnitt 4). */
|
||||||
export const GESUNDHEIT_HINWEIS = 'Bitte keine Gesundheitsangaben.'
|
export const GESUNDHEIT_HINWEIS = 'Bitte keine Gesundheitsangaben.'
|
||||||
@@ -22,7 +22,7 @@ const aw = await startAppwriteMock({
|
|||||||
useTestEnv({ appwriteUrl: aw.url, stripeUrl: 'http://127.0.0.1:1' })
|
useTestEnv({ appwriteUrl: aw.url, stripeUrl: 'http://127.0.0.1:1' })
|
||||||
|
|
||||||
const { config } = await import('../server/config.js')
|
const { config } = await import('../server/config.js')
|
||||||
const { deleteDocument } = await import('../server/services/appwriteAdmin.js')
|
const { deleteDocument, updateDocument } = await import('../server/services/appwriteAdmin.js')
|
||||||
const { setMailTransportForTests } = await import('../server/services/mailer.js')
|
const { setMailTransportForTests } = await import('../server/services/mailer.js')
|
||||||
const { startTestApp, send } = await import('./support/testApp.js')
|
const { startTestApp, send } = await import('./support/testApp.js')
|
||||||
const { kundenansichtCookie } = await import('./support/fixtures.js')
|
const { kundenansichtCookie } = await import('./support/fixtures.js')
|
||||||
@@ -32,7 +32,7 @@ const { resetOutboxForTests, warteAufVersand } = await import('../server/service
|
|||||||
const { buchungsNummer, formToken, pruefeFormToken, verwaltungsToken } = await import('../server/services/termine/token.js')
|
const { buchungsNummer, formToken, pruefeFormToken, verwaltungsToken } = await import('../server/services/termine/token.js')
|
||||||
const { speichereEinstellung, resetKontextForTests } = await import('../server/services/termine/kontext.js')
|
const { speichereEinstellung, resetKontextForTests } = await import('../server/services/termine/kontext.js')
|
||||||
const { resetTermineLimitsForTests, darfHaltung } = await import('../server/services/termine/limits.js')
|
const { resetTermineLimitsForTests, darfHaltung } = await import('../server/services/termine/limits.js')
|
||||||
const { ZUSTIMMUNG_STORNO, ZUSTIMMUNG_ERINNERUNG, ZUSTIMMUNG_VORZEITIG, GESUNDHEIT_HINWEIS, formularHinweis, widerrufsbelehrung } = await import(
|
const { RECHTSTEXTE_VERSION, ZUSTIMMUNG_STORNO, ZUSTIMMUNG_ERINNERUNG, ZUSTIMMUNG_VORZEITIG, GESUNDHEIT_HINWEIS, formularHinweis, widerrufsbelehrung } = await import(
|
||||||
'../server/services/termine/rechtstexte.js'
|
'../server/services/termine/rechtstexte.js'
|
||||||
)
|
)
|
||||||
const { halte } = await import('../server/services/termine/ablauf.js')
|
const { halte } = await import('../server/services/termine/ablauf.js')
|
||||||
@@ -274,6 +274,8 @@ test('config: Betriebsdaten, Leistungen, Regeln, Texte und Formular-Token; keine
|
|||||||
gesundheit: GESUNDHEIT_HINWEIS,
|
gesundheit: GESUNDHEIT_HINWEIS,
|
||||||
widerrufsbelehrung: widerrufsbelehrung(ctx.einstellung.betrieb),
|
widerrufsbelehrung: widerrufsbelehrung(ctx.einstellung.betrieb),
|
||||||
})
|
})
|
||||||
|
// Haken Erinnerung: Wortlaut der Fassung mit dem Widerrufssatz (Art. 7 Abs. 3 DSGVO), wörtlich in der öffentlichen Konfiguration
|
||||||
|
assert.equal(c.texte.erinnerung, 'Ich möchte 24 Stunden vor dem Termin eine Erinnerung per E-Mail erhalten. Diese Einwilligung kann ich jederzeit widerrufen.')
|
||||||
assert.equal(pruefeFormToken(SITE_KEY, c.formToken, plus(JETZT, 10000)), 'ok')
|
assert.equal(pruefeFormToken(SITE_KEY, c.formToken, plus(JETZT, 10000)), 'ok')
|
||||||
assert.equal(pruefeFormToken('bcdefghijklmnopq', c.formToken, plus(JETZT, 10000)), 'ungueltig', 'das Token gilt nur fuer diesen siteKey')
|
assert.equal(pruefeFormToken('bcdefghijklmnopq', c.formToken, plus(JETZT, 10000)), 'ungueltig', 'das Token gilt nur fuer diesen siteKey')
|
||||||
|
|
||||||
@@ -564,6 +566,36 @@ test('Buchung: 201 mit maskierter Adresse und Haltezeit; Buchung gehalten, Mail
|
|||||||
assert.ok(!JSON.stringify(res.body).includes(b.$id))
|
assert.ok(!JSON.stringify(res.body).includes(b.$id))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('nachweis_fassung: eine neue Buchung trägt die aktuelle Fassung der Gästetexte, eine Altbuchung behält 2026-10-02 (der Nachweis wird nie umgeschrieben)', async () => {
|
||||||
|
await speichere()
|
||||||
|
const { id: neu, verwaltung } = await gebucht({ erinnerung: true, zustimmungen: { storno: true, vorzeitig: false } })
|
||||||
|
const nachweisNeu = JSON.parse(aw.docs('termineBookings').get(neu).nachweis)
|
||||||
|
assert.equal(nachweisNeu.fassung, RECHTSTEXTE_VERSION)
|
||||||
|
assert.notEqual(nachweisNeu.fassung, '2026-10-02')
|
||||||
|
assert.equal(nachweisNeu.erinnerung.hash.length, 64)
|
||||||
|
|
||||||
|
// Altbuchung: Nachweis der Fassung 2026-10-02; Bestätigen, Verschieben und Absagen schreiben den Nachweis nicht um
|
||||||
|
const alt = await buchen({ email: 'alt@example.com', zeit: '11:00', ressource: R1 })
|
||||||
|
assert.equal(alt.status, 201)
|
||||||
|
await warteAufVersand()
|
||||||
|
const buchungAlt = buchungen().find((b) => b.gastEmail === 'alt@example.com')
|
||||||
|
const nachweisAlt = JSON.stringify({ ...JSON.parse(buchungAlt.nachweis), fassung: '2026-10-02' })
|
||||||
|
await updateDocument('termineBookings', buchungAlt.$id, { nachweis: nachweisAlt })
|
||||||
|
const vToken = tokenAusMail(mailsAn('alt@example.com')[0], 'v')
|
||||||
|
assert.equal((await tokenAnruf('bestaetigen', { token: vToken })).status, 200)
|
||||||
|
await warteAufVersand()
|
||||||
|
const mToken = tokenAusMail(mailsAn('alt@example.com').find((m) => /#m=/.test(m.text)), 'm')
|
||||||
|
assert.equal((await tokenAnruf('verschieben', { token: mToken, tag: TAG_FERN, zeit: '11:30' })).status, 200)
|
||||||
|
assert.equal((await tokenAnruf('absagen', { token: mToken })).status, 200)
|
||||||
|
const danach = aw.docs('termineBookings').get(buchungAlt.$id)
|
||||||
|
assert.equal(danach.status, 'abgesagt')
|
||||||
|
assert.equal(danach.nachweis, nachweisAlt, 'der Nachweis der Altbuchung bleibt byte-gleich')
|
||||||
|
assert.equal(JSON.parse(danach.nachweis).fassung, '2026-10-02')
|
||||||
|
// die neue Buchung ist davon unberührt
|
||||||
|
assert.equal(JSON.parse(aw.docs('termineBookings').get(neu).nachweis).fassung, RECHTSTEXTE_VERSION)
|
||||||
|
assert.ok(verwaltung)
|
||||||
|
})
|
||||||
|
|
||||||
test('zu viele offene Termine: dieselbe 201-Antwort, keine neue Buchung, statt der Bestaetigung geht G01 "zu viele"', async () => {
|
test('zu viele offene Termine: dieselbe 201-Antwort, keine neue Buchung, statt der Bestaetigung geht G01 "zu viele"', async () => {
|
||||||
await speichere()
|
await speichere()
|
||||||
// drei bestaetigte Termine belegen die drei offenen Plaetze; die Kappe der Adresse (3 Mails je Stunde) ist damit ausgeschoepft,
|
// drei bestaetigte Termine belegen die drei offenen Plaetze; die Kappe der Adresse (3 Mails je Stunde) ist damit ausgeschoepft,
|
||||||
|
|||||||
@@ -55,8 +55,11 @@ function fristBaustein() {
|
|||||||
|
|
||||||
const belehrung = (betrieb = BETRIEB) => widerrufsbelehrung(betrieb)
|
const belehrung = (betrieb = BETRIEB) => widerrufsbelehrung(betrieb)
|
||||||
|
|
||||||
test('Fassung der Rechtstexte', () => {
|
test('version_gleich_register: RECHTSTEXTE_VERSION ist das Fassungsdatum des Registers und nicht mehr die Altfassung 2026-10-02', () => {
|
||||||
assert.equal(RECHTSTEXTE_VERSION, '2026-10-02')
|
assert.equal(RECHTSTEXTE_VERSION, config.termine.avvVersion)
|
||||||
|
assert.notEqual(RECHTSTEXTE_VERSION, '2026-10-02')
|
||||||
|
assert.match(RECHTSTEXTE_VERSION, /^\d{4}-\d{2}-\d{2}$/)
|
||||||
|
assert.ok(RECHTSTEXTE_VERSION > '2026-10-02', 'die Fassung liegt nach der eingefrorenen Altfassung')
|
||||||
})
|
})
|
||||||
|
|
||||||
// ---------------------------------------------------------------- Widerrufsbelehrung
|
// ---------------------------------------------------------------- Widerrufsbelehrung
|
||||||
@@ -177,8 +180,8 @@ test('Zustimmungstexte wortgleich zu den Vorgaben (Sie-Form)', () => {
|
|||||||
+ 'Widerrufe ich vor der vollständigen Erbringung, schulde ich einen angemessenen Betrag für die bis dahin erbrachte Leistung (§ 357a Abs. 2 BGB).',
|
+ 'Widerrufe ich vor der vollständigen Erbringung, schulde ich einen angemessenen Betrag für die bis dahin erbrachte Leistung (§ 357a Abs. 2 BGB).',
|
||||||
)
|
)
|
||||||
assert.equal(ZUSTIMMUNG_STORNO, 'Ich habe die Stornobedingungen gelesen.')
|
assert.equal(ZUSTIMMUNG_STORNO, 'Ich habe die Stornobedingungen gelesen.')
|
||||||
assert.equal(ZUSTIMMUNG_ERINNERUNG(24), 'Ich möchte 24 Stunden vor dem Termin eine Erinnerung per E-Mail erhalten.')
|
assert.equal(ZUSTIMMUNG_ERINNERUNG(24), 'Ich möchte 24 Stunden vor dem Termin eine Erinnerung per E-Mail erhalten. Diese Einwilligung kann ich jederzeit widerrufen.')
|
||||||
assert.equal(ZUSTIMMUNG_ERINNERUNG(2), 'Ich möchte 2 Stunden vor dem Termin eine Erinnerung per E-Mail erhalten.')
|
assert.equal(ZUSTIMMUNG_ERINNERUNG(2), 'Ich möchte 2 Stunden vor dem Termin eine Erinnerung per E-Mail erhalten. Diese Einwilligung kann ich jederzeit widerrufen.')
|
||||||
assert.equal(GESUNDHEIT_HINWEIS, 'Bitte keine Gesundheitsangaben.')
|
assert.equal(GESUNDHEIT_HINWEIS, 'Bitte keine Gesundheitsangaben.')
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -187,6 +190,23 @@ test('Zustimmung zum vorzeitigen Beginn setzt den Betriebsnamen unverändert ein
|
|||||||
assert.ok(text.startsWith("Ich verlange ausdrücklich, dass Frisör $& $1 'Anna' mit der Leistung vor Ablauf"))
|
assert.ok(text.startsWith("Ich verlange ausdrücklich, dass Frisör $& $1 'Anna' mit der Leistung vor Ablauf"))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('erinnerung_widerrufssatz: Haken-Text, Hinweise-Dokument wörtlich gleich; der Haken trägt den Widerrufssatz (Art. 7 Abs. 3 DSGVO)', () => {
|
||||||
|
const satz = (n) => `Ich möchte ${n} Stunden vor dem Termin eine Erinnerung per E-Mail erhalten. Diese Einwilligung kann ich jederzeit widerrufen.`
|
||||||
|
for (const n of [2, 12, 24, 48]) assert.equal(ZUSTIMMUNG_ERINNERUNG(n), satz(n))
|
||||||
|
const md = lies('docs/rechtliches/termine-hinweise.md').replace(/\r/g, '')
|
||||||
|
assert.ok(md.includes(satz('{{n}}')), 'Hinweise-Dokument: Haken Erinnerung weicht ab')
|
||||||
|
assert.ok(md.includes(ZUSTIMMUNG_ERINNERUNG('{{n}}')))
|
||||||
|
assert.ok(!md.includes('Erinnerung per E-Mail erhalten. |'), 'der alte Haken ohne Widerrufssatz steht nicht mehr im Dokument')
|
||||||
|
assert.equal(ZUSTIMMUNG_ERINNERUNG(24).split('\n').length, 1, 'ein Satz im Haken, keine Umbrüche')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('dokumente_nennen_fassung: die drei Gäste-Dokumente tragen die Fassung der Gästetexte', () => {
|
||||||
|
for (const rel of ['docs/rechtliches/termine-hinweise.md', 'docs/rechtliches/termine-datenschutz-absatz.md', 'docs/rechtliches/termine-widerrufsbelehrung-muster.md']) {
|
||||||
|
assert.ok(lies(rel).includes(`Fassung ${RECHTSTEXTE_VERSION}.`), `${rel} nennt nicht „Fassung ${RECHTSTEXTE_VERSION}.“`)
|
||||||
|
assert.ok(!lies(rel).includes('Fassung 2026-10-02.'), `${rel} trägt noch die Altfassung`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const EINSTELLUNG = { anrede: 'sie', aufbewahrungTage: 90, betrieb: BETRIEB }
|
const EINSTELLUNG = { anrede: 'sie', aufbewahrungTage: 90, betrieb: BETRIEB }
|
||||||
|
|
||||||
test('Formular-Hinweis (Sie): Zweck, Auftragsverarbeiter, Löschfristen, keine Cookies, Gesundheit, Verantwortlicher', () => {
|
test('Formular-Hinweis (Sie): Zweck, Auftragsverarbeiter, Löschfristen, keine Cookies, Gesundheit, Verantwortlicher', () => {
|
||||||
@@ -271,8 +291,8 @@ const normalisiere = (text) => text.split(/\n[ \t]*\n/).map((a) => a.replace(/\s
|
|||||||
/**
|
/**
|
||||||
* Datenschutz-Absatz aus der Markdown-Quelle: Blockquote der Fassung („Sie“ | „Du“), `> `, `**` und `###` entfernt,
|
* Datenschutz-Absatz aus der Markdown-Quelle: Blockquote der Fassung („Sie“ | „Du“), `> `, `**` und `###` entfernt,
|
||||||
* [[wenn …: X]] durch X (Schalter an) oder nichts (Schalter aus) ersetzt, Platzhalter mit den Testwerten gefüllt.
|
* [[wenn …: X]] durch X (Schalter an) oder nichts (Schalter aus) ersetzt, Platzhalter mit den Testwerten gefüllt.
|
||||||
* Ein Satzteil, der mit einem Wort beginnt („und die Angaben …“), steht in der Quelle ohne Leerzeichen am Vordertext
|
* Ein Satzteil, der mit einem Wort beginnt („und die Angaben …“), trägt sein Leerzeichen selbst in der Klammer (zwei Leerzeichen
|
||||||
* („Platz[[…: und die Angaben …]]“); er wird hier mit Leerzeichen angefügt (Satzteile mit Komma bleiben unmittelbar dran).
|
* nach dem Doppelpunkt); die Ersetzung fügt nichts hinzu.
|
||||||
*/
|
*/
|
||||||
function datenschutzQuelle(fassung, zustand, { tage = 90 } = {}) {
|
function datenschutzQuelle(fassung, zustand, { tage = 90 } = {}) {
|
||||||
const md = lies('docs/rechtliches/termine-datenschutz-absatz.md').replace(/\r/g, '')
|
const md = lies('docs/rechtliches/termine-datenschutz-absatz.md').replace(/\r/g, '')
|
||||||
@@ -281,11 +301,10 @@ function datenschutzQuelle(fassung, zustand, { tage = 90 } = {}) {
|
|||||||
const zitat = teil.split('\n').filter((z) => z.startsWith('>')).map((z) => z.replace(/^>\s?/, '').replace(/^### /, ''))
|
const zitat = teil.split('\n').filter((z) => z.startsWith('>')).map((z) => z.replace(/^>\s?/, '').replace(/^### /, ''))
|
||||||
assert.ok(zitat.length > 10, 'Blockquote der Quelle nicht gefunden')
|
assert.ok(zitat.length > 10, 'Blockquote der Quelle nicht gefunden')
|
||||||
const roh = zitat.join('\n').replace(/\*\*/g, '')
|
const roh = zitat.join('\n').replace(/\*\*/g, '')
|
||||||
const bedingt = roh.replace(/\[\[wenn ([^:]+): ([\s\S]*?)\]\]/g, (_, bedingung, satz, offset, ganz) => {
|
const bedingt = roh.replace(/\[\[wenn ([^:]+): ([\s\S]*?)\]\]/g, (_, bedingung, satz) => {
|
||||||
const eintrag = DS_BEDINGUNGEN.find(([muster]) => muster.test(bedingung))
|
const eintrag = DS_BEDINGUNGEN.find(([muster]) => muster.test(bedingung))
|
||||||
assert.ok(eintrag, `unbekannte Bedingung in der Quelle: ${bedingung}`)
|
assert.ok(eintrag, `unbekannte Bedingung in der Quelle: ${bedingung}`)
|
||||||
if (!zustand[eintrag[1]]) return ''
|
return zustand[eintrag[1]] ? satz : ''
|
||||||
return /^[A-Za-zÄÖÜäöüß]/.test(satz) && offset > 0 && !/\s/.test(ganz[offset - 1]) ? ` ${satz}` : satz
|
|
||||||
})
|
})
|
||||||
const werte = { Firma: BETRIEB.name, Anschrift: BETRIEB.adresse, 'E-Mail': BETRIEB.email, Telefon: BETRIEB.telefon, Tage: String(tage) }
|
const werte = { Firma: BETRIEB.name, Anschrift: BETRIEB.adresse, 'E-Mail': BETRIEB.email, Telefon: BETRIEB.telefon, Tage: String(tage) }
|
||||||
const gefuellt = bedingt.replace(/\{\{([^}]+)\}\}/g, (_, name) => {
|
const gefuellt = bedingt.replace(/\{\{([^}]+)\}\}/g, (_, name) => {
|
||||||
@@ -364,6 +383,63 @@ test('Datenschutz-Absatz: die Bedingungen lesen die Einstellung wie das Widget',
|
|||||||
assert.ok(text({ ...alleAn, betrieb: { ...BETRIEB, telefon: ' 06301\n1234 ' } }).includes('telefonisch erreichen: 06301 1234.'))
|
assert.ok(text({ ...alleAn, betrieb: { ...BETRIEB, telefon: ' 06301\n1234 ' } }).includes('telefonisch erreichen: 06301 1234.'))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const ABO_ENDE_SATZ = 'Wird die Online-Terminbuchung abgeschaltet, zum Beispiel weil unser Vertrag mit WEBklar endet, werden alle Termindaten 30 Tage nach dem Abschalten gelöscht.'
|
||||||
|
|
||||||
|
test('absatz_abo_ende: Funktion und Markdown (Sie und Du) nennen den Abo-Ende-Satz mit „30 Tage nach dem Abschalten“; der alte Satz ist weg', () => {
|
||||||
|
const md = lies('docs/rechtliches/termine-datenschutz-absatz.md').replace(/\r/g, '')
|
||||||
|
const fassungen = md.split(/^## Fassung /m).slice(1)
|
||||||
|
assert.equal(fassungen.length, 2)
|
||||||
|
for (const f of fassungen) {
|
||||||
|
assert.ok(normalisiere(f.replace(/^> ?/gm, '')).includes(ABO_ENDE_SATZ), 'Markdown: Abo-Ende-Satz fehlt')
|
||||||
|
assert.ok(f.includes('30 Tage nach dem Abschalten'))
|
||||||
|
assert.ok(!f.includes('löschen wir alle Termindaten 30 Tage'), 'Markdown: alter Satz steht noch')
|
||||||
|
}
|
||||||
|
for (const anrede of ['sie', 'du']) {
|
||||||
|
for (const zustand of [DS_ALLE_AN, DS_ALLE_AUS]) {
|
||||||
|
const text = datenschutzAbsatz(dsEinstellung(zustand), anrede)
|
||||||
|
assert.equal(text.split(ABO_ENDE_SATZ).length, 2, `${anrede}: Satz genau einmal`)
|
||||||
|
assert.ok(text.includes('30 Tage nach dem Abschalten'))
|
||||||
|
assert.ok(!text.includes('löschen wir alle Termindaten'), `${anrede}: alter Satz steht noch`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test('haltezeit_aus_config: „der Bestätigungslink gilt N Minuten“ folgt config.termine.holdMinutes; die Markdown-Quelle nennt denselben Wert', () => {
|
||||||
|
const md = lies('docs/rechtliches/termine-datenschutz-absatz.md')
|
||||||
|
for (const anrede of ['sie', 'du']) {
|
||||||
|
assert.ok(datenschutzAbsatz(dsEinstellung(DS_ALLE_AN), anrede).includes(`(der Bestätigungslink gilt ${config.termine.holdMinutes} Minuten)`))
|
||||||
|
}
|
||||||
|
assert.equal((md.match(new RegExp(`Bestätigungslink gilt ${config.termine.holdMinutes} Minuten`, 'g')) || []).length, 2, 'die Quelle nennt beide Fassungen mit dem Wert aus config')
|
||||||
|
const vorher = config.termine.holdMinutes
|
||||||
|
try {
|
||||||
|
config.termine.holdMinutes = 45
|
||||||
|
for (const anrede of ['sie', 'du']) {
|
||||||
|
const text = datenschutzAbsatz(dsEinstellung(DS_ALLE_AN), anrede)
|
||||||
|
assert.ok(text.includes('(der Bestätigungslink gilt 45 Minuten)'), anrede)
|
||||||
|
assert.ok(!text.includes('30 Minuten'), `${anrede}: kein fester Wert mehr im Text`)
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
config.termine.holdMinutes = vorher
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test('quelle_satzteile_mit_leerzeichen: ein Satzteil der Quelle, der mit einem Wort beginnt, trägt sein Leerzeichen in der Klammer', () => {
|
||||||
|
const md = lies('docs/rechtliches/termine-datenschutz-absatz.md').replace(/\r/g, '')
|
||||||
|
let mitLeerzeichen = 0
|
||||||
|
for (const m of md.matchAll(/\[\[wenn ([^:]+): ([\s\S]*?)\]\]/g)) {
|
||||||
|
if (m[1] === '…') continue // die Erklärung der Schreibweise im Kopf der Quelle
|
||||||
|
const davor = md[m.index - 1]
|
||||||
|
if (/^[A-Za-zÄÖÜäöüß]/.test(m[2])) {
|
||||||
|
// Satzteil beginnt mit einem Wort: nur nach Leerzeichen erlaubt (Satzanfang), sonst klebte er am Vordertext („Platzund“)
|
||||||
|
assert.ok(/\s/.test(davor), `Satzteil „${m[2].slice(0, 30)}…“ hängt ohne Leerzeichen am Vordertext`)
|
||||||
|
} else if (m[2].startsWith(' ')) {
|
||||||
|
mitLeerzeichen += 1
|
||||||
|
assert.ok(/^ [A-Za-zÄÖÜäöüß]/.test(m[2]) && !/\s/.test(davor), `Leerzeichen in der Klammer nur am Anschluss an ein Wort: „${m[2].slice(0, 30)}…“`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert.equal(mitLeerzeichen, 4, 'vier Satzteile mit „und“ tragen ihr Leerzeichen selbst (Sie und Du, je zwei)')
|
||||||
|
})
|
||||||
|
|
||||||
test('Datenschutz-Absatz: Aufbewahrung nach dem Termin, ungültiger Wert wird zu 90 Tagen (wie der Formular-Hinweis)', () => {
|
test('Datenschutz-Absatz: Aufbewahrung nach dem Termin, ungültiger Wert wird zu 90 Tagen (wie der Formular-Hinweis)', () => {
|
||||||
for (const anrede of ['sie', 'du']) {
|
for (const anrede of ['sie', 'du']) {
|
||||||
const mit = (aufbewahrungTage) => datenschutzAbsatz(dsEinstellung(DS_ALLE_AN, { aufbewahrungTage }), anrede)
|
const mit = (aufbewahrungTage) => datenschutzAbsatz(dsEinstellung(DS_ALLE_AN, { aufbewahrungTage }), anrede)
|
||||||
|
|||||||
@@ -88,6 +88,10 @@ test('archiv_inhalt: alle Gästetexte stehen wörtlich im Archiv, die Betriebsan
|
|||||||
assert.ok(text.includes('Auf unserer Website kannst du Termine online buchen.'))
|
assert.ok(text.includes('Auf unserer Website kannst du Termine online buchen.'))
|
||||||
assert.ok(text.includes('Vertrag widerrufen'))
|
assert.ok(text.includes('Vertrag widerrufen'))
|
||||||
assert.ok(text.includes('Wenn Sie eine Erinnerung vor dem Termin wünschen'))
|
assert.ok(text.includes('Wenn Sie eine Erinnerung vor dem Termin wünschen'))
|
||||||
|
// Fassung 2026-10-05: Widerrufssatz am Erinnerungshaken und Abo-Ende-Satz im Datenschutz-Absatz (Sie und Du)
|
||||||
|
assert.ok(text.includes('> Ich möchte {{Stunden}} Stunden vor dem Termin eine Erinnerung per E-Mail erhalten. Diese Einwilligung kann ich jederzeit widerrufen.\n'))
|
||||||
|
assert.equal(text.split('zum Beispiel weil unser Vertrag mit WEBklar endet, werden alle Termindaten 30 Tage nach dem Abschalten gelöscht.').length, 3, 'Abo-Ende-Satz in Sie- und Du-Fassung')
|
||||||
|
assert.ok(!text.includes('löschen wir alle Termindaten 30 Tage'))
|
||||||
// Nachweis-Hinweis (⚖ Q26)
|
// Nachweis-Hinweis (⚖ Q26)
|
||||||
assert.ok(text.includes('Der Nachweis speichert Hashes der Texte mit den Betriebsangaben zum Buchungszeitpunkt (⚖ Q26).'))
|
assert.ok(text.includes('Der Nachweis speichert Hashes der Texte mit den Betriebsangaben zum Buchungszeitpunkt (⚖ Q26).'))
|
||||||
// kein Rest, keine echten Betriebsdaten
|
// kein Rest, keine echten Betriebsdaten
|
||||||
@@ -96,9 +100,22 @@ test('archiv_inhalt: alle Gästetexte stehen wörtlich im Archiv, die Betriebsan
|
|||||||
assert.ok(text.endsWith('\n') && !text.endsWith('\n\n'))
|
assert.ok(text.endsWith('\n') && !text.endsWith('\n\n'))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
// SHA-256 der eingefrorenen Datei der Fassung 2026-10-02 (aus den unveränderten Texten erzeugt, vor jeder Textänderung eingecheckt)
|
// SHA-256 der eingefrorenen Datei der Fassung 2026-10-02 (aus den unveränderten Texten erzeugt, vor jeder Textänderung eingecheckt)
|
||||||
const HASH_2026_10_02 = '920633e2e23c9c98aded0c1509db5cac1465058a025aeddc76a9a1282041a6a9'
|
const HASH_2026_10_02 = '920633e2e23c9c98aded0c1509db5cac1465058a025aeddc76a9a1282041a6a9'
|
||||||
|
|
||||||
|
// SHA-256 der Archivdatei der aktuellen Fassung: ändert sich ein Gästetext, ohne dass die Fassung (RECHTSTEXTE_VERSION) ein neues Datum
|
||||||
|
// und diese Datei eine neue Archivdatei bekommt, schlägt der Test an. Bei einer neuen Fassung: neue Datei, neues Datum, neuer Pin.
|
||||||
|
const HASH_AKTUELL = { '2026-10-05': '6bcad4c4a02ea5e39b3ec690d40617fdfb3cb1290aadd3304f0ecabab14c4a59' }
|
||||||
|
|
||||||
|
test('textstand_pin: Hash der aktuellen Archivdatei ist festgenagelt (Textänderung ohne neues Datum fällt auf)', () => {
|
||||||
|
assert.ok(Object.hasOwn(HASH_AKTUELL, RECHTSTEXTE_VERSION), `kein Hash-Pin für die Fassung ${RECHTSTEXTE_VERSION}`)
|
||||||
|
const datei = lies(archivPfad())
|
||||||
|
assert.equal(sha256(datei), HASH_AKTUELL[RECHTSTEXTE_VERSION], 'Gästetext geändert, aber die Fassung ist dieselbe: neues Datum, neue Archivdatei und neuer Pin nötig')
|
||||||
|
assert.equal(sha256(baueArchiv()), HASH_AKTUELL[RECHTSTEXTE_VERSION])
|
||||||
|
assert.notEqual(HASH_AKTUELL[RECHTSTEXTE_VERSION], HASH_2026_10_02, 'die neue Fassung unterscheidet sich von der eingefrorenen')
|
||||||
|
})
|
||||||
|
|
||||||
test('archiv_altfassung_eingefroren: Datei 2026-10-02 hat den alten Erinnerungstext ohne den Widerrufssatz; ihr Hash ist festgenagelt', () => {
|
test('archiv_altfassung_eingefroren: Datei 2026-10-02 hat den alten Erinnerungstext ohne den Widerrufssatz; ihr Hash ist festgenagelt', () => {
|
||||||
const alt = lies(`${ARCHIV_DIR}/termine-gaestetexte-2026-10-02.md`)
|
const alt = lies(`${ARCHIV_DIR}/termine-gaestetexte-2026-10-02.md`)
|
||||||
assert.ok(alt.startsWith('# Gästetexte des Terminplaners, Fassung 2026-10-02\n'))
|
assert.ok(alt.startsWith('# Gästetexte des Terminplaners, Fassung 2026-10-02\n'))
|
||||||
|
|||||||
@@ -447,7 +447,7 @@ test('avv_gleich_datenschutz_absatz: Fristen und Orte stimmen mit dem Datenschut
|
|||||||
assert.ok(absatz.includes('löschen wir 48 Stunden nach Ablauf dieser Frist'))
|
assert.ok(absatz.includes('löschen wir 48 Stunden nach Ablauf dieser Frist'))
|
||||||
assert.ok(absatz.includes(`Bestätigte Termine löschen wir ${einstellung.aufbewahrungTage} Tage nach dem Termin`))
|
assert.ok(absatz.includes(`Bestätigte Termine löschen wir ${einstellung.aufbewahrungTage} Tage nach dem Termin`))
|
||||||
assert.ok(absatz.includes('abgesagte oder abgelehnte Termine 30 Tage nach der Absage oder Ablehnung'))
|
assert.ok(absatz.includes('abgesagte oder abgelehnte Termine 30 Tage nach der Absage oder Ablehnung'))
|
||||||
assert.ok(absatz.includes(`löschen wir alle Termindaten ${LOESCHFRIST_TAGE} Tage nach dem Abschalten`))
|
assert.ok(absatz.includes(`zum Beispiel weil unser Vertrag mit WEBklar endet, werden alle Termindaten ${LOESCHFRIST_TAGE} Tage nach dem Abschalten gelöscht`))
|
||||||
assert.ok(absatz.includes('nicht zustellbare E-Mails nach 48 Stunden'))
|
assert.ok(absatz.includes('nicht zustellbare E-Mails nach 48 Stunden'))
|
||||||
assert.ok(absatz.includes('Rechenzentren Falkenstein und Nürnberg') && absatz.includes('Eine Übermittlung in Drittländer findet nicht statt.'))
|
assert.ok(absatz.includes('Rechenzentren Falkenstein und Nürnberg') && absatz.includes('Eine Übermittlung in Drittländer findet nicht statt.'))
|
||||||
assert.ok(absatz.includes('Der Server speichert sie kurzzeitig in Protokollen und löscht sie nach 14 Tagen'))
|
assert.ok(absatz.includes('Der Server speichert sie kurzzeitig in Protokollen und löscht sie nach 14 Tagen'))
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ async function pruefungen() {
|
|||||||
// Haken: Storno und vorzeitiger Beginn Pflicht (12.10. liegt in der Widerrufsfrist), Erinnerung freiwillig und nicht vorangekreuzt
|
// Haken: Storno und vorzeitiger Beginn Pflicht (12.10. liegt in der Widerrufsfrist), Erinnerung freiwillig und nicht vorangekreuzt
|
||||||
eq(await wd.locator('label[for$="h-storno"]').innerText(), 'Ich habe die Stornobedingungen gelesen.', 'Haken Storno')
|
eq(await wd.locator('label[for$="h-storno"]').innerText(), 'Ich habe die Stornobedingungen gelesen.', 'Haken Storno')
|
||||||
assert((await wd.locator('label[for$="h-vorzeitig"]').innerText()).startsWith('Ich verlange ausdrücklich, dass Salon Müller mit der Leistung vor Ablauf der Widerrufsfrist beginnt.'), 'Haken vorzeitig')
|
assert((await wd.locator('label[for$="h-vorzeitig"]').innerText()).startsWith('Ich verlange ausdrücklich, dass Salon Müller mit der Leistung vor Ablauf der Widerrufsfrist beginnt.'), 'Haken vorzeitig')
|
||||||
eq(await wd.locator('label[for$="h-erinnerung"]').innerText(), 'Ich möchte 24 Stunden vor dem Termin eine Erinnerung per E-Mail erhalten.', 'Haken Erinnerung')
|
eq(await wd.locator('label[for$="h-erinnerung"]').innerText(), 'Ich möchte 24 Stunden vor dem Termin eine Erinnerung per E-Mail erhalten. Diese Einwilligung kann ich jederzeit widerrufen.', 'Haken Erinnerung')
|
||||||
assert(!(await wd.locator('input[name=erinnerung]').isChecked()), 'Erinnerung darf nicht vorangekreuzt sein')
|
assert(!(await wd.locator('input[name=erinnerung]').isChecked()), 'Erinnerung darf nicht vorangekreuzt sein')
|
||||||
assert(await wd.locator('input[name=storno]').getAttribute('required') !== null && await wd.locator('input[name=vorzeitig]').getAttribute('required') !== null, 'Pflicht-Haken')
|
assert(await wd.locator('input[name=storno]').getAttribute('required') !== null && await wd.locator('input[name=vorzeitig]').getAttribute('required') !== null, 'Pflicht-Haken')
|
||||||
assert(await wd.locator('input[name=erinnerung]').getAttribute('required') === null, 'Erinnerung ist freiwillig')
|
assert(await wd.locator('input[name=erinnerung]').getAttribute('required') === null, 'Erinnerung ist freiwillig')
|
||||||
|
|||||||
Reference in new issue
Block a user