169 lines
11 KiB
JavaScript
169 lines
11 KiB
JavaScript
/**
|
||
* ENTWURF – nur Pilot, vor Einsatz bei fremden Betrieben prüfen.
|
||
*
|
||
* Rechtstext-Bausteine des Terminplaners fuer Gaeste (Spezifikation 2026-10-02-terminplaner-design.md, Abschnitt 11).
|
||
* Reine Funktionen und Konstanten, ohne Appwrite und ohne config. Die Texte sind Rechtstexte und stehen deshalb immer
|
||
* in der Sie-Form (auch bei Du-Betrieben); nur der Datenschutz-Hinweis unter dem Formular folgt der Anrede des Betriebs.
|
||
*
|
||
* widerrufsbelehrung: Muster der Anlage 1 und Muster-Widerrufsformular der Anlage 2 zu Art. 246a § 1 Abs. 2 Satz 2 EGBGB
|
||
* (docs/rechtliches/quellen/, abgerufen 2026-10-02), Gestaltungshinweise fuer einen Dienstleistungsvertrag angewendet:
|
||
* Fristbeginn „des Vertragsabschlusses.“, Betriebsdaten (Name, Anschrift, Telefon, E-Mail, alle Pflicht), Hinweis auf die
|
||
* Widerrufsfunktion als „anderer geeigneter Hinweis“ (ohne Internetadresse) im woertlichen Satzrahmen, Wertersatz fuer Dienstleistungen. Alles Uebrige ist der Gesetzestext, unveraendert.
|
||
* Ausgelassen: Textbausteine zu Waren (Besitz, Rueckzahlungsverweigerung, Rueckgabe, Rueckkosten, Wertverlust) und der
|
||
* Absatz zum elektronischen Webformular (es gibt nur die Widerrufsfunktion). test/termineRechtstexte.test.js prueft
|
||
* den Wortlaut gegen die Quelldateien.
|
||
*
|
||
* Die Rueckgabe ist reiner Text mit Zeilenumbruechen und NICHT HTML-maskiert: Die Mailvorlage maskiert beim Einsetzen.
|
||
*/
|
||
|
||
export const RECHTSTEXTE_VERSION = '2026-10-02'
|
||
|
||
const ZEILENUMBRUCH = '\n'
|
||
const ABSATZ = '\n\n'
|
||
|
||
/** Einzeiliger Text: jeder Leerraum (auch Umbrueche) wird zu einem Leerzeichen, Rand getrimmt; Nicht-Text = ''. */
|
||
const einzeilig = (wert) => (typeof wert === 'string' ? wert.replace(/\s+/g, ' ').trim() : '')
|
||
|
||
/**
|
||
* Betriebsdaten fuer die Texte. Name und E-Mail sind immer Pflicht (die Einstellung prueft sie). Mit vollstaendig: true
|
||
* auch Anschrift und Telefonnummer (das Muster der Anlage 1 verlangt alle vier): fehlt eine Angabe oder ist sie leer,
|
||
* entsteht ein TypeError, nie ein unvollstaendiger Text.
|
||
*/
|
||
function betriebsdaten(betrieb, { vollstaendig = false } = {}) {
|
||
const name = einzeilig(betrieb?.name)
|
||
const email = einzeilig(betrieb?.email)
|
||
if (!name || !email) throw new TypeError('Betriebsname und E-Mail-Adresse sind für die Rechtstexte erforderlich.')
|
||
const adresse = einzeilig(betrieb.adresse)
|
||
const telefon = einzeilig(betrieb.telefon)
|
||
if (vollstaendig && (!adresse || !telefon)) {
|
||
throw new TypeError('Anschrift und Telefonnummer des Betriebs sind für die Widerrufsbelehrung erforderlich.')
|
||
}
|
||
return { name, email, adresse, telefon }
|
||
}
|
||
|
||
const verbinde = (teile) => teile.filter(Boolean).join(', ')
|
||
|
||
// ---------------------------------------------------------------- Widerrufsbelehrung (Anlage 1) und Muster-Widerrufsformular (Anlage 2)
|
||
|
||
const FRIST = 'des Vertragsabschlusses.' // Gestaltungshinweis a): Dienstleistungsvertrag
|
||
|
||
// Widerrufsfunktion: Satzrahmen des Gesetzes wörtlich („Sie können Ihr Widerrufsrecht auch online unter … ausüben.“); in die
|
||
// Lücke „… [Internetadresse oder anderen geeigneten Hinweis …]“ kommt ein geeigneter Hinweis statt einer Internetadresse.
|
||
const WIDERRUFSFUNKTION =
|
||
'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.'
|
||
|
||
// Wertersatz bei Dienstleistungen; „oder Lieferung von Wasser/Gas/Strom/Fernwärme [Unzutreffendes streichen]“ ist gestrichen.
|
||
const WERTERSATZ =
|
||
'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.'
|
||
|
||
/** Kontaktangaben fuer „Um Ihr Widerrufsrecht auszuüben, müssen Sie uns (…)“: Name, Anschrift, Telefonnummer, E-Mail-Adresse. */
|
||
function kontakt({ name, adresse, telefon, email }) {
|
||
return verbinde([name, adresse, `Telefon: ${telefon}`, `E-Mail: ${email}`])
|
||
}
|
||
|
||
function belehrungsAbsaetze(daten) {
|
||
return [
|
||
'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 ${FRIST}`,
|
||
`Um Ihr Widerrufsrecht auszuüben, müssen Sie uns (${kontakt(daten)}) 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.',
|
||
WIDERRUFSFUNKTION,
|
||
'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.',
|
||
WERTERSATZ,
|
||
]
|
||
}
|
||
|
||
function formularAbsaetze({ name, adresse, email }) {
|
||
const zeilen = [
|
||
`An ${verbinde([name, adresse, email])}:`,
|
||
'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',
|
||
]
|
||
return [
|
||
'Muster-Widerrufsformular',
|
||
'(Wenn Sie den Vertrag widerrufen wollen, dann füllen Sie bitte dieses Formular aus und senden Sie es zurück.)',
|
||
zeilen.map((zeile) => `– ${zeile}`).join(ZEILENUMBRUCH),
|
||
'(*) Unzutreffendes streichen.',
|
||
]
|
||
}
|
||
|
||
/**
|
||
* Widerrufsbelehrung und Muster-Widerrufsformular fuer einen Dienstleistungs-Termin, Sie-Form, als Text mit Zeilenumbruechen.
|
||
* betrieb: { name, email, adresse, telefon } aus der Einstellung, alle vier Pflicht. Fehlt eine Angabe oder ist sie leer: TypeError
|
||
* (eine unvollstaendige gesetzliche Belehrung wird nie stillschweigend erzeugt). Die Einstellungspruefung soll Anschrift und
|
||
* Telefon verlangen, sobald Gaeste ein Widerrufsrecht haben koennen (spaeterer Task); bis dahin faengt diese Funktion es ab.
|
||
*/
|
||
export function widerrufsbelehrung(betrieb) {
|
||
const daten = betriebsdaten(betrieb, { vollstaendig: true })
|
||
return [...belehrungsAbsaetze(daten), ...formularAbsaetze(daten)].join(ABSATZ)
|
||
}
|
||
|
||
// ---------------------------------------------------------------- Hinweis unter dem Buchungsformular (recherche-recht.md 11.2)
|
||
|
||
const LOESCHFRIST_ABGESAGT_TAGE = 30
|
||
const LOESCHFRIST_UNBESTAETIGT_STUNDEN = 48
|
||
const AUFBEWAHRUNG_VORGABE_TAGE = 90
|
||
|
||
/**
|
||
* Kurzer Datenschutz-Hinweis (Art. 13 DSGVO gestuft) vor dem Absenden; Sie oder Du nach einstellung.anrede.
|
||
* Die Loeschfristen entsprechen dem Loeschlauf: nicht bestaetigt 48 Stunden nach Ablauf der Bestaetigungsfrist, abgesagt
|
||
* 30 Tage, vergangene Termine einstellung.aufbewahrungTage nach dem Termin. Der Link auf die Datenschutzerklaerung des
|
||
* Betriebs (betrieb.datenschutzUrl) steht nicht im Text; das Widget setzt ihn daneben. Die Betriebs-E-Mail steht nicht im Text.
|
||
*/
|
||
export function formularHinweis(einstellung) {
|
||
const daten = betriebsdaten(einstellung?.betrieb)
|
||
const du = einstellung.anrede === 'du'
|
||
const tage = Number.isInteger(einstellung.aufbewahrungTage) ? einstellung.aufbewahrungTage : AUFBEWAHRUNG_VORGABE_TAGE
|
||
const verantwortlich = verbinde([daten.name, daten.adresse])
|
||
const frist = `bis ${tage} Tage nach dem Termin, abgesagte Termine ${LOESCHFRIST_ABGESAGT_TAGE} Tage, `
|
||
+ `nicht bestätigte Buchungen ${LOESCHFRIST_UNBESTAETIGT_STUNDEN} Stunden nach Ablauf der Bestätigungsfrist.`
|
||
const betrieben = 'Betrieben wird der Terminkalender in unserem Auftrag von der WEBklar GbR (Server: Hetzner, Deutschland).'
|
||
const cookies = 'Es werden keine Cookies gesetzt.'
|
||
return du
|
||
? `Datenschutz. Wir verwenden deine Angaben, um deinen Termin zu verwalten und zu bestätigen (Art. 6 Abs. 1 lit. b DSGVO). ${betrieben} `
|
||
+ `Wir speichern deine Daten ${frist} ${cookies} Bitte mach keine Angaben zu deiner Gesundheit. `
|
||
+ `Mehr erfährst du in unserer Datenschutzerklärung. Verantwortlich: ${verantwortlich}.`
|
||
: `Datenschutz. Wir verwenden Ihre Angaben, um Ihren Termin zu verwalten und zu bestätigen (Art. 6 Abs. 1 lit. b DSGVO). ${betrieben} `
|
||
+ `Wir speichern Ihre Daten ${frist} ${cookies} Bitte machen Sie keine Angaben zu Ihrer Gesundheit. `
|
||
+ `Mehr erfahren Sie in unserer Datenschutzerklärung. Verantwortlich: ${verantwortlich}.`
|
||
}
|
||
|
||
// ---------------------------------------------------------------- Zustimmungen (Haken) und Hinweis zu Gesundheitsangaben
|
||
|
||
/**
|
||
* Pflicht-Zustimmung zum vorzeitigen Beginn (§ 356 Abs. 5 Nr. 2 BGB, § 357a Abs. 2 BGB): nur bei verbindlicher Leistung
|
||
* mit Preis, Verbrauchern und Terminbeginn vor Ablauf der Widerrufsfrist. Sie-Form auch bei Du-Betrieben.
|
||
*/
|
||
export const ZUSTIMMUNG_VORZEITIG = (name) =>
|
||
`Ich verlange ausdrücklich, dass ${name} 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).'
|
||
|
||
/** Pflicht-Haken, wenn der Betrieb Stornobedingungen hat. */
|
||
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.`
|
||
|
||
/** Hinweis ueber dem Nachrichtenfeld (Gesundheitsbereich ist ausgeschlossen, Spezifikation Abschnitt 4). */
|
||
export const GESUNDHEIT_HINWEIS = 'Bitte keine Gesundheitsangaben.'
|