Terminplaner: Abschluss – Server (Widerruf auch bei Anfragen, Gastdaten löschen vollständig, formMinSekunden)

- R55: Widerrufsbelehrung, Anschrift-Pflicht und Knopf "Vertrag widerrufen" gelten für jede
  Verbraucher-Buchung mit sichtbarer Leistung, auch Anfragen (zeigtWiderruf); der Nachweis
  hält die Belehrung auch bei Anfragen fest. Damit bleibt die Annahme einer Anfrage nie mehr
  still ohne G02.
- "Gastdaten löschen" leert auch absageGrund und löscht noch nicht versandte Mails der Buchung
  (neu: verwerfeWartende in der Warteschlange).
- R53: GET /:siteKey/config nennt formMinSekunden.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Sonnet 5.5 committed 2026-10-03 08:17:44 +00:00
1 parent 4be886a4de
commit bd65c9dc58
11 files changed
+216 -18

No files matched your search

+13 -10
View File
@@ -153,8 +153,8 @@ test('Vorlagen sind Entwuerfe: ohne Telefon und Anschrift gueltig, fehlendePflic
assert.equal(salon.betrieb.telefon, '')
assert.equal(salon.betrieb.adresse, '')
assert.deepEqual(fehlendePflichtangaben(salon), ['betrieb.telefon', 'betrieb.adresse'], 'Verbraucher + sofort: Telefon und Anschrift')
// Anfrage-Betriebe zeigen keine Widerrufsbelehrung: nur das Telefon fehlt
assert.deepEqual(fehlendePflichtangaben(ausVorlage('handwerk', ohne, NOW)), ['betrieb.telefon'])
// Anfrage-Betriebe zeigen die Widerrufsbelehrung ebenfalls (R55: der Vertrag kommt mit der Annahme zustande, auch ein Fernabsatzvertrag)
assert.deepEqual(fehlendePflichtangaben(ausVorlage('handwerk', ohne, NOW)), ['betrieb.telefon', 'betrieb.adresse'])
assert.deepEqual(fehlendePflichtangaben(ausVorlage('salon', { ...ohne, telefon: '06301 1234' }, NOW)), ['betrieb.adresse'])
assert.deepEqual(fehlendePflichtangaben(ausVorlage('salon', BETRIEB, NOW)), [])
@@ -830,7 +830,7 @@ test('R13: Telefon ist immer Pflicht (Pfad betrieb.telefon), auch ohne Widerrufs
assert.equal(gueltig({ ...unternehmer, betrieb: { ...unternehmer.betrieb, telefon: ' 0171 2345678 ' } }).betrieb.telefon, '0171 2345678')
})
test('R13a: Anschrift ist Pflicht (Pfad betrieb.adresse), sobald die Widerrufsbelehrung erscheint: Verbraucher + sichtbare sofort-Leistung', () => {
test('R13a/R55: Anschrift ist Pflicht (Pfad betrieb.adresse), sobald die Widerrufsbelehrung erscheint: Verbraucher + sichtbare Leistung (sofort oder Anfrage)', () => {
const ohneAdresse = (zusatz = {}) => ({ ...basis(), betrieb: { name: 'X', email: 'a@b.de', telefon: PFLICHT.telefon }, ...zusatz })
const f = fehlerVon(ohneAdresse())
assert.deepEqual(Object.keys(f), ['betrieb.adresse'])
@@ -841,22 +841,24 @@ test('R13a: Anschrift ist Pflicht (Pfad betrieb.adresse), sobald die Widerrufsbe
// gemischte Gaeste sind auch Verbraucher
assert.deepEqual(Object.keys(fehlerVon(ohneAdresse({ recht: { gaesteSind: 'gemischt' } }))), ['betrieb.adresse'])
// ohne Widerrufsbelehrung keine Anschrift noetig: nur Unternehmer, Freizeitleistung, nur Anfragen
// ohne Widerrufsbelehrung keine Anschrift noetig: nur Unternehmer oder Freizeitleistung
assert.equal(gueltig(ohneAdresse({ recht: { gaesteSind: 'unternehmer' } })).betrieb.adresse, '')
assert.equal(gueltig(ohneAdresse({ recht: { freizeit: true } })).betrieb.adresse, '')
// R55: auch ein Betrieb mit nur Anfrage-Leistungen braucht die Anschrift (der Vertrag kommt mit der Annahme zustande)
const anfragen = { leistungen: [{ id: S1, name: 'A', dauer: 30, ressourcen: [R1], buchungsart: 'anfrage' }] }
assert.equal(gueltig(ohneAdresse(anfragen)).betrieb.adresse, '')
// eine sofort-Leistung, die Gaeste nicht sehen (inaktiv oder nur an inaktiver Ressource), loest nichts aus
assert.deepEqual(Object.keys(fehlerVon(ohneAdresse(anfragen))), ['betrieb.adresse'])
assert.equal(gueltig(ohneAdresse({ ...anfragen, recht: { gaesteSind: 'unternehmer' } })).betrieb.adresse, '')
// eine Leistung, die Gaeste nicht sehen (inaktiv oder nur an inaktiver Ressource), loest nichts aus
const ruht = {
ressourcen: [{ id: R1, name: 'Anna', plan: {} }, { id: R2, name: 'Ben', aktiv: false, plan: {} }],
leistungen: [
{ id: S1, name: 'Anfrage', dauer: 30, ressourcen: [R1], buchungsart: 'anfrage' },
{ id: S1, name: 'Anfrage', dauer: 30, ressourcen: [R1], buchungsart: 'anfrage', aktiv: false },
{ id: S2, name: 'Ruht', dauer: 30, ressourcen: [R1], buchungsart: 'sofort', aktiv: false },
{ id: 's_bbbbbbb3', name: 'Nur Ben', dauer: 30, ressourcen: [R2], buchungsart: 'sofort', aktiv: false },
],
}
assert.equal(gueltig(ohneAdresse(ruht)).betrieb.adresse, '')
// mit einer sichtbaren sofort-Leistung daneben wieder Pflicht
// mit einer sichtbaren Leistung daneben wieder Pflicht
const mitSofort = { ...ruht, leistungen: [...ruht.leistungen, { id: 's_bbbbbbb4', name: 'Sofort', dauer: 30, ressourcen: [R1], buchungsart: 'sofort' }] }
assert.deepEqual(Object.keys(fehlerVon(ohneAdresse(mitSofort))), ['betrieb.adresse'])
})
@@ -878,6 +880,7 @@ test('R13/R13a: fehlendePflichtangaben stimmt mit oeffentlicheEinstellung.widerr
})
const name = JSON.stringify({ gaesteSind, freizeit, buchungsart, aktiv })
const widerruf = oeffentlicheEinstellung(e, NOW).widerruf
assert.equal(widerruf, gaesteSind !== 'unternehmer' && !freizeit && aktiv, `R55: unabhaengig von der Buchungsart ${name}`)
assert.equal(zeigtWiderruf(e), widerruf, name)
const ohne = { ...e, betrieb: { ...e.betrieb, adresse: '', telefon: '' } }
assert.deepEqual(fehlendePflichtangaben(ohne), widerruf ? ['betrieb.telefon', 'betrieb.adresse'] : ['betrieb.telefon'], name)
@@ -968,13 +971,13 @@ test('oeffentlicheEinstellung: nur aktive Leistungen mit aktiver Ressource, nur
assert.equal(oeffentlicheEinstellung(roh, NOW).widerruf, false)
})
test('oeffentlicheEinstellung: storno null ohne Text, widerruf nur bei sofort + Verbraucher, Kleinunternehmer-Hinweis', () => {
test('oeffentlicheEinstellung: storno null ohne Text, widerruf bei Verbraucher mit sichtbarer Leistung (sofort und Anfrage, R55), Kleinunternehmer-Hinweis', () => {
const sofort = gueltig(basis())
assert.equal(oeffentlicheEinstellung(sofort, NOW).storno, null)
assert.equal(oeffentlicheEinstellung(sofort, NOW).widerruf, true)
const anfrage = gueltig({ ...basis(), leistungen: [{ id: S1, name: 'A', dauer: 30, ressourcen: [R1], buchungsart: 'anfrage' }] })
assert.equal(oeffentlicheEinstellung(anfrage, NOW).widerruf, false)
assert.equal(oeffentlicheEinstellung(anfrage, NOW).widerruf, true, 'R55: auch eine Anfrage ist ein Fernabsatzvertrag')
const unternehmer = gueltig({ ...basis(), recht: { gaesteSind: 'unternehmer' } })
assert.equal(oeffentlicheEinstellung(unternehmer, NOW).widerruf, false)