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:
1 parent
4be886a4de
commit
bd65c9dc58
11 files changed
+216
-18
No files matched your search
@@ -244,10 +244,11 @@ test('config: Betriebsdaten, Leistungen, Regeln, Texte und Formular-Token; keine
|
||||
const c = res.body
|
||||
|
||||
assert.deepEqual(Object.keys(c).sort(), [
|
||||
'aktiv', 'anrede', 'betrieb', 'erinnerung', 'farbe', 'formToken', 'gastFelder', 'heute', 'hinweis', 'leistungen', 'personWaehlbar',
|
||||
'preisHinweis', 'regeln', 'ressourcen', 'storno', 'texte', 'widerruf', 'zahlung',
|
||||
'aktiv', 'anrede', 'betrieb', 'erinnerung', 'farbe', 'formMinSekunden', 'formToken', 'gastFelder', 'heute', 'hinweis', 'leistungen',
|
||||
'personWaehlbar', 'preisHinweis', 'regeln', 'ressourcen', 'storno', 'texte', 'widerruf', 'zahlung',
|
||||
])
|
||||
assert.equal(c.aktiv, true)
|
||||
assert.equal(c.formMinSekunden, config.termine.formMinSekunden, 'R53: das Widget wartet, bis das Formular-Token so alt ist')
|
||||
assert.equal(c.heute, '2026-10-05')
|
||||
assert.deepEqual(c.betrieb, {
|
||||
name: 'Salon Müller',
|
||||
@@ -286,6 +287,15 @@ test('config: Betriebsdaten, Leistungen, Regeln, Texte und Formular-Token; keine
|
||||
for (const r of ctx.einstellung.ressourcen) assert.ok(!JSON.stringify(c).includes(r.feedToken))
|
||||
})
|
||||
|
||||
test('config: formMinSekunden folgt der Einstellung zur Laufzeit (R53) und ist Zahl, auch 0', async () => {
|
||||
await speichere()
|
||||
for (const wert of [3, 0, 7.5]) {
|
||||
config.termine.formMinSekunden = wert
|
||||
const { body } = await api('GET', `/${SITE_KEY}/config`)
|
||||
assert.equal(body.formMinSekunden, wert)
|
||||
}
|
||||
})
|
||||
|
||||
test('config: Knopf je Fall (Zahlungspflichtig buchen, Termin anfragen, Termin buchen)', async () => {
|
||||
await speichere()
|
||||
const { body } = await api('GET', `/${SITE_KEY}/config`)
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -276,6 +276,8 @@ test('halte: Anfrage und Leistung ohne Preis bekommen ihre Knoepfe und brauchen
|
||||
const anfrage = buchungen().find((b) => b.buchungsart === 'anfrage')
|
||||
assert.equal(anfrage.preisCent, 0)
|
||||
assert.equal(anfrage.preisArt, 'keiner')
|
||||
// R55: das Widget zeigt die Belehrung auch vor einer Anfrage, der Nachweis haelt sie fest
|
||||
assert.match(JSON.parse(anfrage.nachweis).widerruf.hash, /^[0-9a-f]{64}$/)
|
||||
})
|
||||
|
||||
test('halte: egal waehlt die Ressource mit den wenigsten Buchungen des Tages, eine feste Ressource bleibt sie', async () => {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -23,7 +23,7 @@ const { setMailTransportForTests } = await import('../server/services/mailer.js'
|
||||
const { loadTemplate } = await import('../server/services/mailTemplates.js')
|
||||
const { icsTermin } = await import('../server/services/termine/ics.js')
|
||||
const O = await import('../server/services/termine/outbox.js')
|
||||
const { einreihen, sendeEintrag, verarbeite, warteAufVersand, zustellFehler, raeumeAuf, beendeFestgefahrene, pausiertBis, resetOutboxForTests } = O
|
||||
const { einreihen, sendeEintrag, verarbeite, warteAufVersand, zustellFehler, raeumeAuf, beendeFestgefahrene, verwerfeWartende, pausiertBis, resetOutboxForTests } = O
|
||||
after(() => setMailTransportForTests(null))
|
||||
|
||||
const MIN = 60000
|
||||
@@ -598,6 +598,43 @@ test('beendeFestgefahrene: ein Eintrag, der inzwischen gesendet wurde, bleibt ge
|
||||
assert.equal((await lies(doc.$id)).status, 'gesendet')
|
||||
})
|
||||
|
||||
test('verwerfeWartende: löscht nur die wartenden Einträge dieser Buchung (auch über mehrere Seiten); gesendete, fehlgeschlagene und fremde bleiben', async () => {
|
||||
for (let i = 0; i < 30; i++) await legeAn({ bookingId: 'b_weg', art: 'G05' })
|
||||
const gesendet = await legeAn({ bookingId: 'b_weg', status: 'gesendet', an: '', html: '', text: '' })
|
||||
const fehler = await legeAn({ bookingId: 'b_weg', status: 'fehler' })
|
||||
const fremd = await legeAn({ bookingId: 'b_fremd' })
|
||||
assert.equal(await verwerfeWartende('b_weg'), 30)
|
||||
assert.deepEqual(alle().map((d) => d.$id).sort(), [gesendet.$id, fehler.$id, fremd.$id].sort())
|
||||
assert.equal(await verwerfeWartende('b_weg'), 0, 'zweiter Aufruf findet nichts')
|
||||
assert.equal(await verwerfeWartende('b_gibtsnicht'), 0)
|
||||
for (const ungueltig of [undefined, null, '', 5]) await assert.rejects(() => verwerfeWartende(ungueltig), TypeError)
|
||||
})
|
||||
|
||||
test('verwerfeWartende: ein Eintrag, der inzwischen gesendet wurde, bleibt (Sperre, neu lesen); ein Eintrag, der inzwischen weg ist, stört nicht', async () => {
|
||||
const doc = await legeAn({ bookingId: 'b_race' })
|
||||
const weg = await legeAn({ bookingId: 'b_race' })
|
||||
const echt = globalThis.fetch
|
||||
let einmal = true
|
||||
// zwischen Auflisten und Sperre sendet ein anderer Lauf den ersten Eintrag und ein anderer Lauf loescht den zweiten
|
||||
globalThis.fetch = async (url, optionen) => {
|
||||
const antwort = await echt(url, optionen)
|
||||
if (einmal && (optionen?.method ?? 'GET') === 'GET' && String(url).includes('/documents?')) {
|
||||
einmal = false
|
||||
await sendeEintrag(doc.$id, JETZT)
|
||||
await deleteDocument(col(), weg.$id)
|
||||
}
|
||||
return antwort
|
||||
}
|
||||
try {
|
||||
assert.equal(await verwerfeWartende('b_race'), 0)
|
||||
} finally {
|
||||
globalThis.fetch = echt
|
||||
}
|
||||
assert.equal(einmal, false, 'der Versand lief zwischen Auflisten und Sperre')
|
||||
assert.equal((await lies(doc.$id)).status, 'gesendet')
|
||||
assert.equal(await lies(weg.$id), undefined)
|
||||
})
|
||||
|
||||
test('raeumeAuf löscht mehr als eine Seite (über 25 Einträge)', async () => {
|
||||
for (let i = 0; i < 40; i++) await legeAn({ bookingId: `b_${i}`, status: 'gesendet', an: '', html: '', text: '', naechsterVersuch: JETZT.toISOString() })
|
||||
assert.equal(await raeumeAuf(JETZT), 40)
|
||||
|
||||
@@ -373,6 +373,61 @@ test('POST vorlage ohne Telefon und Anschrift im Konto: Entwurf mit "fehlt"; oef
|
||||
assert.equal((await oeffentlich('defghijklmnopqrs')).aktiv, true)
|
||||
})
|
||||
|
||||
test('R55: Betrieb nur mit Anfrage-Leistungen (Privatpersonen) braucht die Anschrift: PATCH 400, "fehlt", oeffentlich inaktiv; danach Belehrung im Widget und G02 mit Belehrung nach "annehmen"', async () => {
|
||||
const c2 = customerCookie('c2')
|
||||
const oeffentlich = (siteKey) => fetch(`${app.url}/api/public/termine/v1/${siteKey}/config`).then((r) => r.json())
|
||||
const SCHLUESSEL = 'defghijklmnopqrs'
|
||||
const res = await send('POST', '/api/termine/einstellungen/vorlage', { cookie: c2, body: { projectId: 'p6', vorlage: 'handwerk' } })
|
||||
assert.equal(res.status, 201, JSON.stringify(res.body))
|
||||
const entwurf = res.body.einstellung
|
||||
assert.ok(entwurf.leistungen.every((l) => l.buchungsart === 'anfrage'), 'Vorlage: nur Anfragen')
|
||||
assert.equal(entwurf.recht.gaesteSind, 'gemischt', 'Verbraucher sind dabei')
|
||||
assert.deepEqual(res.body.fehlt, ['betrieb.telefon', 'betrieb.adresse'], 'auch ohne sofort-Leistung fehlt die Anschrift')
|
||||
assert.deepEqual(await oeffentlich(SCHLUESSEL), { aktiv: false })
|
||||
|
||||
// nur das Telefon reicht nicht: PATCH nennt die Anschrift
|
||||
const nurTelefon = { ...entwurf, betrieb: { ...entwurf.betrieb, telefon: '0151 2345678' } }
|
||||
const abgelehnt = await send('PATCH', '/api/termine/einstellungen', { cookie: c2, body: { projectId: 'p6', version: 1, einstellung: nurTelefon } })
|
||||
assert.equal(abgelehnt.status, 400)
|
||||
assert.deepEqual(Object.keys(abgelehnt.body.fehler), ['betrieb.adresse'])
|
||||
assert.deepEqual(await oeffentlich(SCHLUESSEL), { aktiv: false })
|
||||
|
||||
// mit Anschrift: speichert, Widget bekommt Belehrung (widerruf true)
|
||||
const komplett = { ...nurTelefon, betrieb: { ...nurTelefon.betrieb, adresse: 'Weg 1, 12345 Ort' } }
|
||||
const ok = await send('PATCH', '/api/termine/einstellungen', { cookie: c2, body: { projectId: 'p6', version: 1, einstellung: komplett } })
|
||||
assert.equal(ok.status, 200, JSON.stringify(ok.body))
|
||||
assert.deepEqual(ok.body.fehlt ?? [], [])
|
||||
const config = await oeffentlich(SCHLUESSEL)
|
||||
assert.equal(config.aktiv, true)
|
||||
assert.equal(config.widerruf, true)
|
||||
assert.match(config.texte.widerrufsbelehrung, /Widerrufsbelehrung/)
|
||||
assert.match(config.texte.widerrufsbelehrung, /Weg 1, 12345 Ort/)
|
||||
|
||||
// annehmen: die Anfrage wird bestaetigt und G02 mit Belehrung geht raus (nicht still verschluckt)
|
||||
const leistung = ok.body.einstellung.leistungen[0]
|
||||
seedBuchung('b_handwerk', {
|
||||
featureId: 'f_p6_termine',
|
||||
customerId: 'c2',
|
||||
projectId: 'p6',
|
||||
status: 'angefragt',
|
||||
leistungId: leistung.id,
|
||||
leistungName: leistung.name,
|
||||
ressourceId: leistung.ressourcen[0],
|
||||
preisCent: 0,
|
||||
preisArt: 'keiner',
|
||||
buchungsart: 'anfrage',
|
||||
})
|
||||
const angenommen = await send('POST', '/api/termine/buchungen/b_handwerk/aktion', { cookie: c2, body: { projectId: 'p6', aktion: 'annehmen' } })
|
||||
assert.equal(angenommen.status, 200, JSON.stringify(angenommen.body))
|
||||
assert.equal(aw.docs('termineBookings').get('b_handwerk').status, 'bestaetigt')
|
||||
await warteAufVersand()
|
||||
assert.deepEqual(eintraege().map((m) => m.art), ['G02'])
|
||||
assert.equal(sink.sent.length, 1)
|
||||
assert.equal(sink.sent[0].to[0].address, 'max@example.com')
|
||||
assert.match(sink.sent[0].text, /Widerrufsbelehrung/)
|
||||
assert.match(sink.sent[0].text, /Weg 1, 12345 Ort/)
|
||||
})
|
||||
|
||||
// ---------------------------------------------------------------- Einstellungen: PATCH
|
||||
|
||||
test('PATCH einstellungen: speichert, Version zaehlt hoch, Feed-Token bleiben (auch bei Wert vom Client), neue Personen bekommen einen', async () => {
|
||||
|
||||
Reference in new issue
Block a user