From 9a3ce7c8bfb10419ca659e166af93a65ff883ea7 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 16 Oct 2026 16:56:52 +0000 Subject: [PATCH] =?UTF-8?q?Kundenansicht=20P6:=20Zusage-Mails=20K42=20und?= =?UTF-8?q?=20K43=20f=C3=BCr=20Webshop-=20und=20Buchungskalender-Anfrage?= =?UTF-8?q?=20ohne=20Preis?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 5.5 --- .../K42 Webshop-Anfrage Abo.html | 53 ++++++++++++++++++ .../K43 Anfrage Buchungskalender Abo.html | 55 +++++++++++++++++++ server/services/aboFunktionenMails.js | 24 +++++++- server/services/shop/anfrage.js | 12 +++- server/services/unterkunft/anfrageFunktion.js | 12 +++- test/aboFunktionenMails.test.js | 11 +++- test/aboFunktionenVorlagen.test.js | 13 ++++- test/mailTemplates.test.js | 4 +- test/shopTeam.test.js | 14 +++++ test/unterkunftAnfrageFunktion.test.js | 14 +++++ 10 files changed, 205 insertions(+), 7 deletions(-) create mode 100644 server/mail-templates/K42 Webshop-Anfrage Abo.html create mode 100644 server/mail-templates/K43 Anfrage Buchungskalender Abo.html diff --git a/server/mail-templates/K42 Webshop-Anfrage Abo.html b/server/mail-templates/K42 Webshop-Anfrage Abo.html new file mode 100644 index 0000000..906f288 --- /dev/null +++ b/server/mail-templates/K42 Webshop-Anfrage Abo.html @@ -0,0 +1,53 @@ + + + + + + + + +Deine Webshop-Anfrage für {{projekt}} + + + + +Deine Webshop-Anfrage wurde bearbeitet. ͏ ͏ ͏ + + + + + diff --git a/server/mail-templates/K43 Anfrage Buchungskalender Abo.html b/server/mail-templates/K43 Anfrage Buchungskalender Abo.html new file mode 100644 index 0000000..2c799f5 --- /dev/null +++ b/server/mail-templates/K43 Anfrage Buchungskalender Abo.html @@ -0,0 +1,55 @@ + + + + + + + + +Deine Anfrage zum Buchungskalender für {{projekt}} + + + + + + + + + diff --git a/server/services/aboFunktionenMails.js b/server/services/aboFunktionenMails.js index cd479e2..32bb542 100644 --- a/server/services/aboFunktionenMails.js +++ b/server/services/aboFunktionenMails.js @@ -9,7 +9,8 @@ import { datumKurz } from './termine/zeit.js' * K38 Deine Funktion wird eingerichtet (Einschalten aus dem Abo oder durch das Team; nicht für Öffnungszeiten live, dort bleibt K27) * K39 Deine Funktion wurde beendet (der Tagesabgleich beendet sie mit dem Abo) * T24 Team-Hinweis: Funktion eingeschaltet (Einrichtung geht aufs Stundenkonto, Block vorschuss) - * (K42 und K43, die Zusage-Mails der Webshop- und der Buchungskalender-Anfrage, folgen in Task 15.) + * K42, K43 Webshop- und Buchungskalender-Anfrage entschieden (Zusage oder Absage), Ersatz fuer K24 und K34 im Abo-Modell: ohne Preis und ohne + * das Wort "bestellen" (anfrageEntschiedenMail) * Die Builder sind rein (kein Zugriff auf Appwrite, kein Versand) und liefern { template, values, blocks }, so wie * zeiten/mails.js: die Werte decken genau die Platzhalter der Vorlage ab. Keine Preise, kein Wort „bestellen“. * @@ -110,6 +111,27 @@ export function t24Mail({ kunde, email, projekt, key, customerId, projectId, woi } } +/** + * K42 (Webshop) oder K43 (Buchungskalender): die Anfrage wurde vom Team entschieden. Im Abo-Modell ohne Preiszeile und ohne "bestellen": + * bei Zusage kann der Kunde die Funktion im Kundenportal einschalten. Werte und Adresse wie die alten Mails K24 und K34 (sendeEntscheidung). + * @param {{ kunde: string | { name?: string }, projekt: string, key: 'shop' | 'unterkunft', projectId: string, angenommen: boolean, begruendung?: string }} p + * kunde_name ist der getrimmte Name, leer 'Kunde'; ein anderer key ist ein TypeError. + */ +export function anfrageEntschiedenMail({ kunde, projekt, key, projectId, angenommen, begruendung = '' }) { + if (key !== 'shop' && key !== 'unterkunft') throw new TypeError(`Keine Anfrage-Mail fuer die Funktion: ${String(key)}`) + const eintrag = funktion(key) + return { + template: key === 'shop' ? 'K42' : 'K43', + values: { + kunde_name: kundenName(kunde) || 'Kunde', + projekt, + begruendung, + portal_url: `${config.publicBaseUrl}/dashboard.html#${eintrag.returnHash}?projekt=${encodeURIComponent(projectId)}`, + }, + blocks: { angenommen: Boolean(angenommen), abgelehnt: !angenommen }, + } +} + /** Tage zwischen dem Abschalten und der Löschung der Eingaben: news 30, termine 30, shop und unterkunft aus der Konfiguration; sonst TypeError. */ export function loeschFristTage(key) { if (key === 'news') return 30 diff --git a/server/services/shop/anfrage.js b/server/services/shop/anfrage.js index 788f406..73e7961 100644 --- a/server/services/shop/anfrage.js +++ b/server/services/shop/anfrage.js @@ -3,6 +3,8 @@ import { createDocument, getDocument, updateDocument } from '../appwriteAdmin.js import { withLocks } from '../keyedLock.js' import { sendAdminNotice, sendCustomerMail } from '../mailer.js' import { assertOwnProject, customerOf, ensureFeatureDoc, getFeatureDoc } from '../siteFeatures.js' +import { aboModellFuerKunde } from '../aboFunktionen.js' +import { anfrageEntschiedenMail } from '../aboFunktionenMails.js' import { shopFuerKunde } from './schalter.js' import { NUR_BETRIEB_TEXT } from '../kundenart.js' @@ -286,7 +288,11 @@ export async function entscheideAnfrage({ customerId, projectId, entscheidung, b return { anfrage: anfrageAnsicht(gespeichert), mailFehler } } -/** K24 an den Kunden; true = gesendet. Jeder Fehler wird nur geloggt (die Entscheidung ist gespeichert). */ +/** + * K24 an den Kunden; true = gesendet. Jeder Fehler wird nur geloggt (die Entscheidung ist gespeichert). Fuer Kunden der neuen Ansicht + * (aboModellFuerKunde, Kundenansicht P6) geht stattdessen K42 ohne Preis und ohne "bestellen"; ist das nicht lesbar, geht keine Mail raus + * (lieber keine als eine mit Preis, die Antwort traegt mailFehler). + */ async function sendeEntscheidung({ customerId, projectId, project, angenommen, begruendung }) { try { const kunde = await customerOf(customerId) @@ -294,6 +300,10 @@ async function sendeEntscheidung({ customerId, projectId, project, angenommen, b console.warn(`[shop] K24 nicht gesendet: Kunde ${customerId} ohne E-Mail-Adresse`) return false } + if (await aboModellFuerKunde(customerId)) { + await sendCustomerMail(kunde.email, anfrageEntschiedenMail({ kunde, projekt: project?.projectName || projectId, key: 'shop', projectId, angenommen, begruendung })) + return true + } await sendCustomerMail(kunde.email, { template: 'K24', values: { diff --git a/server/services/unterkunft/anfrageFunktion.js b/server/services/unterkunft/anfrageFunktion.js index 9561b7a..716864d 100644 --- a/server/services/unterkunft/anfrageFunktion.js +++ b/server/services/unterkunft/anfrageFunktion.js @@ -2,6 +2,8 @@ import { config } from '../../config.js' import { withLocks } from '../keyedLock.js' import { sendAdminNotice, sendCustomerMail } from '../mailer.js' import { assertOwnProject, customerOf, ensureFeatureDoc, getFeatureDoc } from '../siteFeatures.js' +import { aboModellFuerKunde } from '../aboFunktionen.js' +import { anfrageEntschiedenMail } from '../aboFunktionenMails.js' import { unterkunftFuerKunde } from './schalter.js' import { ladeEinstellungen, speichereEinstellungen } from './speicher.js' @@ -268,7 +270,11 @@ export async function entscheideAnfrage({ customerId, projectId, entscheidung, b return { anfrage: anfrageAnsicht(gespeichert), mailFehler } } -/** K34 an den Kunden; true = gesendet. Jeder Fehler wird nur geloggt (die Entscheidung ist gespeichert). */ +/** + * K34 an den Kunden; true = gesendet. Jeder Fehler wird nur geloggt (die Entscheidung ist gespeichert). Fuer Kunden der neuen Ansicht + * (aboModellFuerKunde, Kundenansicht P6) geht stattdessen K43 ohne Preis und ohne "bestellen"; ist das nicht lesbar, geht keine Mail raus + * (lieber keine als eine mit Preis, die Antwort traegt mailFehler). + */ async function sendeEntscheidung({ customerId, projectId, project, angenommen, begruendung }) { try { const kunde = await customerOf(customerId) @@ -276,6 +282,10 @@ async function sendeEntscheidung({ customerId, projectId, project, angenommen, b console.warn(`[unterkunft] K34 nicht gesendet: Kunde ${customerId} ohne E-Mail-Adresse`) return false } + if (await aboModellFuerKunde(customerId)) { + await _versand.kunde(kunde.email, anfrageEntschiedenMail({ kunde, projekt: project?.projectName || projectId, key: 'unterkunft', projectId, angenommen, begruendung })) + return true + } await _versand.kunde(kunde.email, { template: 'K34', values: { diff --git a/test/aboFunktionenMails.test.js b/test/aboFunktionenMails.test.js index d281e69..1675043 100644 --- a/test/aboFunktionenMails.test.js +++ b/test/aboFunktionenMails.test.js @@ -13,7 +13,7 @@ useTestEnv({ appwriteUrl: aw.url, stripeUrl: st.url }) const { config } = await import('../server/config.js') const { renderMail } = await import('../server/services/mailTemplates.js') const { anleitungsLink } = await import('../server/services/featureRegistry.js') -const { k38Mail, k39Mail, t24Mail, loeschFristTage, kontostandText } = await import('../server/services/aboFunktionenMails.js') +const { anfrageEntschiedenMail, k38Mail, k39Mail, t24Mail, loeschFristTage, kontostandText } = await import('../server/services/aboFunktionenMails.js') const { k29Mail, k30Mail } = await import('../server/services/zeiten/mails.js') test('k38Mail: Werte und Blöcke je Abrechnung', () => { @@ -44,3 +44,12 @@ test('alle Builder-Ergebnisse rendern ohne Probleme und ohne €; Zeiten-Variant const k41 = k30Mail({ kunde: 'K', projekt: 'P', loeschdatum: frist, preis: '30 €', modell: true }); assert.equal(k41.template, 'K41'); assert.equal(renderMail('K41', k41.values, k41.blocks).problems.length, 0) assert.equal(k29Mail({ kunde: 'K', projekt: 'P', art: 'beendet', frist, preis: '30 €' }).template, 'K29') // Standard unverändert }) +test('anfrageEntschiedenMail: K42 und K43, Werte, Blöcke, Adresse; ohne Preis', () => { + const m = anfrageEntschiedenMail({ kunde: { name: 'Erika' }, projekt: 'Töpferei', key: 'shop', projectId: 'pt1', angenommen: true }) + assert.equal(m.template, 'K42'); assert.deepEqual(m.blocks, { angenommen: true, abgelehnt: false }) + assert.deepEqual(m.values, { kunde_name: 'Erika', projekt: 'Töpferei', begruendung: '', portal_url: `${config.publicBaseUrl}/dashboard.html#webshop?projekt=pt1` }) + const u = anfrageEntschiedenMail({ kunde: { name: ' ' }, projekt: 'Donau', key: 'unterkunft', projectId: 'a b', angenommen: false, begruendung: 'Passt nicht.' }) + assert.deepEqual([u.template, u.blocks, u.values.kunde_name, u.values.begruendung, u.values.portal_url], ['K43', { angenommen: false, abgelehnt: true }, 'Kunde', 'Passt nicht.', `${config.publicBaseUrl}/dashboard.html#buchungen?projekt=a%20b`]) + assert.throws(() => anfrageEntschiedenMail({ kunde: 'K', projekt: 'P', key: 'termine', projectId: 'p', angenommen: true }), TypeError) + for (const x of [m, u]) assert.deepEqual(renderMail(x.template, x.values, x.blocks).problems, []) +}) diff --git a/test/aboFunktionenVorlagen.test.js b/test/aboFunktionenVorlagen.test.js index decf2fe..e837897 100644 --- a/test/aboFunktionenVorlagen.test.js +++ b/test/aboFunktionenVorlagen.test.js @@ -8,6 +8,8 @@ const SOLL = { // [Datei, Betreff, Platzhalter, Blöcke] K39: ['K39 Funktion beendet.html', 'Deine Funktion wurde beendet: {{funktion}}', ['kunde_name', 'projekt', 'funktion', 'loeschtag', 'portal_url'], ['abo_ende', 'hosting', 'termine', 'offene']], K40: ['K40 Zeiten Abo-Ende.html', 'Dein Abo ist beendet – Öffnungszeiten live läuft noch bis {{frist}}', ['kunde_name', 'projekt', 'frist', 'portal_url'], ['abo_ende', 'hosting', 'wechsel_widerrufen']], K41: ['K41 Zeiten abgeschaltet Abo.html', 'Öffnungszeiten live ist abgeschaltet', ['kunde_name', 'projekt', 'loeschdatum', 'portal_url'], []], + K42: ['K42 Webshop-Anfrage Abo.html', 'Deine Webshop-Anfrage für {{projekt}}', ['kunde_name', 'projekt', 'begruendung', 'portal_url'], ['angenommen', 'abgelehnt']], + K43: ['K43 Anfrage Buchungskalender Abo.html', 'Deine Anfrage zum Buchungskalender für {{projekt}}', ['kunde_name', 'projekt', 'begruendung', 'portal_url'], ['angenommen', 'abgelehnt']], T24: ['T24 Funktion eingeschaltet.html', '[Abo] Funktion eingeschaltet: {{funktion}} – {{kunde_name}}', ['kunde_name', 'email', 'projekt', 'funktion', 'woid', 'kontostand', 'woms_url'], ['vorschuss', 'anhang_fehlt']], } const mail = (id, on) => { const t = loadTemplate(id); return renderMail(id, Object.fromEntries(t.placeholders.map((p) => [p, `W-${p}`])), Object.fromEntries(t.blocks.map((b) => [b, on]))) } @@ -23,4 +25,13 @@ test('Festtexte', () => { assert.match(mail('K39', true).text, /Bitte informiere deine Gäste selbst\./); assert.doesNotMatch(mail('K39', false).text, /CSV|Offene Bestellungen/) assert.match(mail('T24', true).text, /Das Guthaben reicht voraussichtlich nicht \(W-kontostand\)\./); assert.doesNotMatch(mail('T24', false).text, /Vorschuss/) }) -test('templateIds enthält K38 bis K41 und T24', () => { for (const id of Object.keys(SOLL)) assert.ok(templateIds().includes(id)) }) +test('K42, K43: Zusage ohne Preis und ohne „bestellen“, Absage mit Begründung wie K24 und K34', () => { + const werte = { kunde_name: 'Erika', projekt: 'Töpferei', begruendung: 'Wir verkaufen keine .', portal_url: 'https://portal.example/dashboard.html#x?projekt=p1' } + for (const [id, satz, knopf] of [['K42', 'Du kannst den Webshop jetzt im Kundenportal einschalten; danach richten wir ihn ein.', 'Webshop einschalten'], ['K43', 'Du kannst ihn jetzt im Kundenportal einschalten; danach richten wir ihn ein.', 'Zum Kundenportal']]) { + const zu = renderMail(id, werte, { angenommen: true, abgelehnt: false }); assert.deepEqual(zu.problems, []) + assert.doesNotMatch(zu.html + zu.text, /€|UStG|bestell|Preis|Waffen/i); assert.ok(zu.text.includes(satz), id); assert.ok(zu.text.includes(knopf), id) + const ab = renderMail(id, werte, { angenommen: false, abgelehnt: true }); assert.deepEqual(ab.problems, []) + assert.match(ab.text, /leider/); assert.match(ab.text, /Wir verkaufen keine \./); assert.doesNotMatch(ab.text + ab.html, /€|UStG|bestell/i) + } +}) +test('templateIds enthält K38 bis K43 und T24', () => { for (const id of Object.keys(SOLL)) assert.ok(templateIds().includes(id)) }) diff --git a/test/mailTemplates.test.js b/test/mailTemplates.test.js index f96f35e..2948f56 100644 --- a/test/mailTemplates.test.js +++ b/test/mailTemplates.test.js @@ -6,9 +6,9 @@ import { contractAttachmentFor, termsDocFor } from '../server/services/products. const DIR = new URL('../server/mail-templates/', import.meta.url) // G01–G08 Gästemails des Terminplaners (Task 9, neutrales Layout im Auftrag eines Betriebs), K20 und K21 Hinweise an den Betrieb; -// K12, K14 und T12 (Wechsel ins Wartungs-Abo) aus Task 9, K13 und T13 (Zusatzspeicher) aus Task 8 (Fix 1); Endfassung der Texte in Task 12; T14 „Löschfristen“ aus Task 21 (F39); T15 Push-Zeitmail; K15, K16, T16 Funktionen (News-Pinnwand, Terminplaner; K15, K16 und T16 gelten je Funktion, Terminplaner TP2 B9a und B9b); T17 Repo zuordnen; K17 Chat-Hinweis an Kunden; K18, K19 Website-Analyse; K22, K23 Terminplaner beendet/läuft wieder (Terminplaner TP2 E3); T18 Bankzugang erneuern (Finanzen); T19 Erstattung fällig und T20 Terminplaner Betriebsmeldung (Terminplaner TP2, Team-Hinweise; T16 gilt je Funktion, T19 aus B9b); T21 Neue Webshop-Anfrage (Webshop B1, Team-Hinweis); K24 Webshop-Anfrage entschieden (Webshop B2, Kunde); G09–G14 Käufermails des Webshops (Bestellbestätigung, versendet, abholbereit, Eingang Widerruf, Erstattung, Zahlung fehlgeschlagen; Webshop E4, eigener Fuß statt Terminplaner-Fuß, Sie-Form) und K25, K26 an den Händler (neue Bestellung, Widerruf; Webshop E4); K27–K30 Öffnungszeiten live (eingeschaltet, Feiertage prüfen, Abo beendet, abgeschaltet; Öffnungszeiten live Task 13); K04 und T09 gelten beim Widerruf einer Funktion je Funktion (produkt, T09 zusätzlich preis; Terminplaner TP2 F9); K33–K37 und T23 Buchungskalender (K36 und K37 Neue Anfrage und Neue Buchung an den Vermieter, T23 Team-Hinweis; zuvor K31, K32, T22, die den Aufträgen gehören); K31, K32 und T22 Auftragsbestätigung (Aufträge und Rechnungen, Strang L5; K31 und K32 in Sie-Form, T22 mit [Aufträge]); K38–K41 und T24 Funktionen im Abo-Modell (Kundenansicht P6: K38 wird eingerichtet, K39 beendet, K40 und K41 Öffnungszeiten live Abo-Ende und abgeschaltet ohne Preis, T24 Team-Hinweis; Du-Form, Test in aboFunktionenVorlagen.test.js) +// K12, K14 und T12 (Wechsel ins Wartungs-Abo) aus Task 9, K13 und T13 (Zusatzspeicher) aus Task 8 (Fix 1); Endfassung der Texte in Task 12; T14 „Löschfristen“ aus Task 21 (F39); T15 Push-Zeitmail; K15, K16, T16 Funktionen (News-Pinnwand, Terminplaner; K15, K16 und T16 gelten je Funktion, Terminplaner TP2 B9a und B9b); T17 Repo zuordnen; K17 Chat-Hinweis an Kunden; K18, K19 Website-Analyse; K22, K23 Terminplaner beendet/läuft wieder (Terminplaner TP2 E3); T18 Bankzugang erneuern (Finanzen); T19 Erstattung fällig und T20 Terminplaner Betriebsmeldung (Terminplaner TP2, Team-Hinweise; T16 gilt je Funktion, T19 aus B9b); T21 Neue Webshop-Anfrage (Webshop B1, Team-Hinweis); K24 Webshop-Anfrage entschieden (Webshop B2, Kunde); G09–G14 Käufermails des Webshops (Bestellbestätigung, versendet, abholbereit, Eingang Widerruf, Erstattung, Zahlung fehlgeschlagen; Webshop E4, eigener Fuß statt Terminplaner-Fuß, Sie-Form) und K25, K26 an den Händler (neue Bestellung, Widerruf; Webshop E4); K27–K30 Öffnungszeiten live (eingeschaltet, Feiertage prüfen, Abo beendet, abgeschaltet; Öffnungszeiten live Task 13); K04 und T09 gelten beim Widerruf einer Funktion je Funktion (produkt, T09 zusätzlich preis; Terminplaner TP2 F9); K33–K37 und T23 Buchungskalender (K36 und K37 Neue Anfrage und Neue Buchung an den Vermieter, T23 Team-Hinweis; zuvor K31, K32, T22, die den Aufträgen gehören); K31, K32 und T22 Auftragsbestätigung (Aufträge und Rechnungen, Strang L5; K31 und K32 in Sie-Form, T22 mit [Aufträge]); K42, K43 Zusage-Mails ohne Preis (Kundenansicht P6: Webshop- und Buchungskalender-Anfrage, Ersatz für K24 und K34 im Abo-Modell); K38–K41 und T24 Funktionen im Abo-Modell (Kundenansicht P6: K38 wird eingerichtet, K39 beendet, K40 und K41 Öffnungszeiten live Abo-Ende und abgeschaltet ohne Preis, T24 Team-Hinweis; Du-Form, Test in aboFunktionenVorlagen.test.js) const ids = (prefix, count) => Array.from({ length: count }, (_, i) => `${prefix}${String(i + 1).padStart(2, '0')}`) -const EXPECTED = [...ids('G', 22), ...ids('K', 41), ...ids('T', 24)] +const EXPECTED = [...ids('G', 22), ...ids('K', 43), ...ids('T', 24)] // GUEST = Gästemails des Terminplaners (gemeinsamer Fuß, Anrede-Blöcke); SHOP_GUEST = Käufermails des Webshops (G09–G14, siehe unten) const GUEST = ids('G', 8) const SHOP_GUEST = ['G09', 'G10', 'G11', 'G12', 'G13', 'G14', 'G15'] diff --git a/test/shopTeam.test.js b/test/shopTeam.test.js index d36a76e..db3ac85 100644 --- a/test/shopTeam.test.js +++ b/test/shopTeam.test.js @@ -252,3 +252,17 @@ test('nach Ablehnung kann der Kunde neu anfragen, die alte Entscheidung ist gel assert.equal(doc.anfrageBegruendung, '') assert.equal((await entscheide({ entscheidung: 'annehmen' })).status, 200) }) + +const ansichtAlt = { ...config.kundenansichtNeu }; after(() => Object.assign(config.kundenansichtNeu, ansichtAlt)) +const neueAnsicht = (an) => { config.kundenansichtNeu.mode = an ? 'pilot' : 'off'; config.kundenansichtNeu.pilotEmails = [customers.c1.email] } +test('Abo-Modell: Zusage als K42 ohne Preis und ohne „bestellen“, Absage mit Begründung; alte Ansicht bekommt weiter K24 mit 299 €', async () => { + for (const an of [true, false]) { + neueAnsicht(an); await leereAlles(); await stelle(); mails.sent.length = 0 + assert.equal((await entscheide({ entscheidung: 'annehmen' })).status, 200) + const m = k24()[0]; assert.equal(m.subject, 'Deine Webshop-Anfrage für Töpferei'); assert.match(m.html, /dashboard\.html#webshop\?projekt=pt1"/) + if (an) { assert.doesNotMatch(m.text + m.html, /€|UStG|bestell/i); assert.match(m.text, /jetzt im Kundenportal einschalten/) } else { assert.match(m.text, /299 €/); assert.match(m.text, /bestellen/) } + } + neueAnsicht(true); await leereAlles(); await stelle(); mails.sent.length = 0 + assert.equal((await entscheide({ entscheidung: 'ablehnen', begruendung: 'Passt nicht.' })).status, 200) + assert.match(k24()[0].text, /leider[\s\S]*Passt nicht\./); assert.doesNotMatch(k24()[0].text, /299 €/) +}) diff --git a/test/unterkunftAnfrageFunktion.test.js b/test/unterkunftAnfrageFunktion.test.js index 5ff0c19..7e54fe8 100644 --- a/test/unterkunftAnfrageFunktion.test.js +++ b/test/unterkunftAnfrageFunktion.test.js @@ -326,3 +326,17 @@ test('T23 und K34 (angenommen und abgelehnt) passen zu den Vorlagen: keine Platz const k34 = renderMail('K34', aufrufe.kunde[1].values, aufrufe.kunde[1].blocks) assert.match(k34.text, /199 €/) }) + +const ansichtAlt = { ...config.kundenansichtNeu }; after(() => Object.assign(config.kundenansichtNeu, ansichtAlt)) +const neueAnsicht = (an) => { config.kundenansichtNeu.mode = an ? 'pilot' : 'off'; config.kundenansichtNeu.pilotEmails = [customers.c1.email] } +test('Abo-Modell: Zusage als K43 ohne Preis und ohne „bestellen“, Absage unverändert; alte Ansicht bekommt weiter K34 mit 199 €', async () => { + for (const an of [true, false]) { + neueAnsicht(an); aufrufe.kunde.length = 0; await deleteDocument(config.collections.ukEinstellungen, FEATURE).catch(() => {}); assert.equal((await stelle()).status, 200); assert.equal((await entscheide({ entscheidung: 'annehmen' })).status, 200) + const m = aufrufe.kunde[0]; const r = renderMail(m.template, m.values, m.blocks); assert.deepEqual(r.problems, []) + assert.deepEqual([m.template, 'preis' in m.values, 'ust_hinweis' in m.values], an ? ['K43', false, false] : ['K34', true, true]) + assert.equal(m.values.portal_url, `${config.publicBaseUrl}/dashboard.html#buchungen?projekt=${P}`) + if (an) assert.doesNotMatch(r.text + r.html, /€|UStG|bestell/i); else { assert.match(r.text, /199 €/); assert.match(r.text, /bestellen/) } + } + neueAnsicht(true); aufrufe.kunde.length = 0; await deleteDocument(config.collections.ukEinstellungen, FEATURE).catch(() => {}); await stelle(); assert.equal((await entscheide({ entscheidung: 'ablehnen', begruendung: 'Passt nicht.' })).status, 200) + assert.deepEqual([aufrufe.kunde[0].template, aufrufe.kunde[0].values.begruendung, aufrufe.kunde[0].blocks], ['K43', 'Passt nicht.', { angenommen: false, abgelehnt: true }]) +})