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

+47
View File
@@ -572,6 +572,53 @@ test('anonymisieren: bei abgesagten Terminen bleibt updatedAt (die Loeschfrist l
assert.equal(buchung(gehalten.$id).gastEmail, 'zwei@example.com')
})
test('anonymisieren: absageGrund wird neutralisiert (auch der Vermerk "Widerruf erklärt von: <Name>"), Betriebsgründe ebenso', async () => {
const ctx = await speichere()
const { id } = await gebucht(ctx)
const abgesagtUm = iso(plus(JETZT, MIN))
// Vermerk einer Widerrufserklaerung nach Fristablauf (haltWiderrufFest): der Gast hat den Namen selbst getippt
aw.seed('termineBookings', { ...buchung(id), absageGrund: 'Widerruf erklärt von: Max Mustermann am 20.10.2026, 11:00 Uhr (nach Fristablauf)', updatedAt: abgesagtUm })
assert.match(buchung(id).absageGrund, /Max Mustermann/)
await aktion(ctx, id, { aktion: 'anonymisieren' }, plus(JETZT, 5 * MIN))
assert.equal(buchung(id).absageGrund, '')
assert.doesNotMatch(JSON.stringify(buchung(id)), /Mustermann|max@example\.com/)
// abgesagt vom Betrieb: der Grund war frei getippt und kann Namen enthalten; die Frist (updatedAt) bleibt
const zwei = await gebucht(ctx, { email: 'zwei@example.com', zeit: '12:00' })
await aktion(ctx, zwei.id, { aktion: 'absagen', grund: 'Frau Meier ist krank' }, JETZT)
assert.equal(buchung(zwei.id).absageGrund, 'Frau Meier ist krank')
const abgesagtZwei = iso(buchung(zwei.id).updatedAt)
await aktion(ctx, zwei.id, { aktion: 'anonymisieren' }, plus(JETZT, 10 * 24 * 60 * MIN))
assert.equal(buchung(zwei.id).absageGrund, '')
assert.equal(buchung(zwei.id).status, 'abgesagt')
assert.equal(iso(buchung(zwei.id).updatedAt), abgesagtZwei)
})
test('anonymisieren: noch nicht versandte Mails dieser Buchung (wartet) werden gelöscht; gesendete, fehlgeschlagene und fremde Einträge bleiben', async () => {
const ctx = await speichere()
const { id } = await gebucht(ctx)
const { id: anderer } = await gebucht(ctx, { email: 'zwei@example.com', zeit: '12:00' })
await warteAufVersand()
const vorher = eintraege().length
const voll = { featureId: FEATURE, art: 'G05', an: 'max@example.com', fromName: 'Salon Müller', subject: 'Erinnerung', html: '<p>Hallo Max Mustermann</p>', text: 'Hallo Max Mustermann', ics: '', icsMethod: '', versuche: 0, naechsterVersuch: iso(JETZT), fehler: '', createdAt: iso(JETZT) }
aw.seed('termineOutbox', { ...voll, $id: 'o_wartet1', bookingId: id, status: 'wartet' })
aw.seed('termineOutbox', { ...voll, $id: 'o_wartet2', bookingId: id, status: 'wartet', art: 'G03' })
aw.seed('termineOutbox', { ...voll, $id: 'o_gesendet', bookingId: id, status: 'gesendet', an: '', html: '', text: '' })
aw.seed('termineOutbox', { ...voll, $id: 'o_fehler', bookingId: id, status: 'fehler' })
aw.seed('termineOutbox', { ...voll, $id: 'o_fremd', bookingId: anderer, status: 'wartet', an: 'zwei@example.com' })
await aktion(ctx, id, { aktion: 'anonymisieren' }, plus(JETZT, 5 * MIN))
const ids = eintraege().map((d) => d.$id)
assert.ok(!ids.includes('o_wartet1') && !ids.includes('o_wartet2'), 'wartende Mails mit Adresse und Inhalt sind weg')
assert.ok(ids.includes('o_gesendet') && ids.includes('o_fehler') && ids.includes('o_fremd'))
assert.equal(eintraege().length, vorher + 3)
assert.equal(buchung(id).gastName, 'Gelöscht')
// zweiter Klick: nichts mehr zu loeschen, kein Fehler
assert.deepEqual(await aktion(ctx, id, { aktion: 'anonymisieren' }, plus(JETZT, 6 * MIN)), { status: 'bestaetigt' })
assert.equal(eintraege().length, vorher + 3)
})
// ---------------------------------------------------------------- aktion allgemein
test('aktion: fremde Funktion, Sperren, unbekannte Nummern und kaputte Eingaben aendern nichts', async () => {