Files
Webklar-Kundenbereich/test/auftraegeAuftragSicht.test.js
T

136 lines
9.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// test/auftraegeAuftragSicht.test.js
import { test } from 'node:test'
import assert from 'node:assert/strict'
import { kundeSnapshot, fruehesterBeginn, zahlungsplanText, aufwandText, auftragFuerTeam, oeffentlicheAnsicht } from '../server/services/auftraege/auftragSicht.js'
import { AGB_FASSUNG } from '../server/services/auftraege/konstanten.js'
import { AB_TEXTE, BESTAETIGUNG_TEXT, FRUEHBEGINN_TEXT } from '../server/services/auftraege/texte.js'
import { euroDe } from '../server/services/finance/format.js'
const KUNDE = { name: 'Anna Beispiel', firma: '', strasse: 'Hauptstr. 1', plz: '67722', ort: 'Winnweiler', land: '', email: 'anna@example.invalid', kundenart: 'privat', kundennummer: 'K-1001' }
const festpreis = (felder = {}) => ({
id: 'ab1', nummer: 'AB-2026-0002', jahr: 2026, laufnummer: 2, status: 'versendet', kundenId: 'c1', kunde: KUNDE, projektId: 'p1', titel: 'Website Bäckerei',
zeitquelle: ['t1'], art: 'festpreis',
positionen: [{ beschreibung: 'Startseite', menge: 2, einzelpreisCents: 30000 }, { beschreibung: 'Kontaktformular', menge: 1, einzelpreisCents: 20000 }],
stundensatzCents: null, schaetzungMinuten: null, summeCents: 80000, zahlungsplan: { art: 'anzahlung', prozent: 50, text: '' }, zahlungszielTage: 14,
auftragVom: '2026-10-05', auftragWeg: 'telefon', startAm: '2026-11-01', fertigBis: '', ersetztId: '', ersetztDurchId: '', texteFassung: '2026-10-06', agbFassung: '2026-09-30',
pdfDatei: 'c1/ab1.pdf', pdfSha256: 'a'.repeat(64), gesendetAm: '2026-10-06T10:00:00.000Z', gesendetAn: 'anna@example.invalid', mailStatus: 'gesendet', mailFehler: '',
tokenHash: 'b'.repeat(64), tokenGueltigBis: '2026-12-05T10:00:00.000Z', bestaetigtAm: null, bestaetigtUeber: '', bestaetigtPdfSha256: '', fruehbeginn: false, fruehbeginnFassung: '',
storniertAm: null, widerrufenAm: null, erstelltVon: 'kenso@webklar.com', erstelltAm: '2026-10-05T09:00:00.000Z', geaendertAm: '2026-10-06T10:00:00.000Z', ...felder,
})
const aufwand = (felder = {}) =>
festpreis({ art: 'aufwand', positionen: [], summeCents: null, stundensatzCents: 10000, schaetzungMinuten: 600, zahlungsplan: { art: 'monatlich', prozent: null, text: '' }, ...felder })
const LEER = { name: '', firma: '', strasse: '', plz: '', ort: '', land: '', email: '', kundenart: '', kundennummer: '' }
test('kundeSnapshot übernimmt die Kundendaten getrimmt; eine unbekannte Kundenart wird leer; null und {} sind leer', () => {
const doc = { $id: 'c1', name: ' Anna Beispiel ', companyName: '', street: 'Hauptstr. 1', postalCode: '67722', city: 'Winnweiler', country: '', email: 'anna@example.invalid', kundenart: 'privat', code: 'K-1001', phone: '123', customerStatus: 'active' }
assert.deepEqual(kundeSnapshot(doc), KUNDE)
assert.deepEqual(kundeSnapshot({ ...doc, companyName: ' Bäckerei Beispiel GmbH ', kundenart: 'unternehmen' }), { ...KUNDE, firma: 'Bäckerei Beispiel GmbH', kundenart: 'unternehmen' })
assert.equal(kundeSnapshot({ name: 'X', kundenart: 'firma' }).kundenart, '')
assert.deepEqual(kundeSnapshot({}), LEER)
assert.deepEqual(kundeSnapshot(null), LEER)
})
test('kundeSnapshot kürzt lange Werte, so dass die Spalte kundeJson (2000 Zeichen) reicht', () => {
const lang = 'x'.repeat(500)
const snapshot = kundeSnapshot({ name: lang, companyName: lang, street: lang, postalCode: lang, city: lang, country: lang, email: lang, kundenart: 'privat', code: lang })
assert.ok(JSON.stringify(snapshot).length <= 2000)
assert.equal(snapshot.name.length, 120)
})
test('fruehesterBeginn: Tag nach Ablauf der Widerrufsfrist, nur für Privatpersonen ohne Frühbeginn', () => {
const faelle = [
['2026-10-06T10:00:00.000Z', '2026-10-21'],
['2026-10-06T22:30:00.000Z', '2026-10-22'], // in Berlin schon der 07.10.
['2026-10-10T08:00:00.000Z', '2026-10-27'], // Fristende fiele auf Samstag, 26.10. ist der Montag
['2026-10-17T08:00:00.000Z', '2026-11-03'], // 31.10. (Samstag, Feiertag) und 01.11. (Sonntag), Fristende Montag 02.11.
]
for (const [gesendetAm, erwartet] of faelle) assert.equal(fruehesterBeginn(festpreis({ gesendetAm })), erwartet, gesendetAm)
assert.equal(fruehesterBeginn(festpreis({ fruehbeginn: true })), null)
for (const kundenart of ['unternehmen', '']) assert.equal(fruehesterBeginn(festpreis({ kunde: { ...KUNDE, kundenart } })), null, kundenart)
assert.equal(fruehesterBeginn(festpreis({ kunde: null })), null)
assert.equal(fruehesterBeginn(festpreis({ gesendetAm: null, status: 'entwurf' })), null)
assert.equal(fruehesterBeginn(festpreis({ gesendetAm: 'kaputt' })), null)
})
test('zahlungsplanText: Anzahlung nach Prozent gerundet, Rest als Differenz', () => {
const text = zahlungsplanText(festpreis())
assert.equal(text.split(euroDe(40000)).length, 3, text) // Anzahlung und Rest je 400,00 €
assert.match(text, /50\s?%/)
assert.match(text, /\b14\b/)
// Math.round(10001 × 33 / 100) = 3300, Rest 6701
const ungerade = zahlungsplanText(festpreis({ summeCents: 10001, zahlungsplan: { art: 'anzahlung', prozent: 33, text: '' } }))
assert.ok(ungerade.includes(euroDe(3300)) && ungerade.includes(euroDe(6701)), ungerade)
})
test('zahlungsplanText: nach Abnahme, monatlich, frei; Anzahlung ohne Summe fällt auf nach Abnahme zurück', () => {
assert.match(zahlungsplanText(festpreis({ zahlungsplan: { art: 'nach_abnahme', prozent: null, text: '' } })), /\b14\b/)
assert.match(zahlungsplanText(aufwand({ zahlungszielTage: 7 })), /\b7\b/)
assert.ok(zahlungsplanText(festpreis({ zahlungsplan: { art: 'frei', prozent: null, text: 'Zahlung in zwei Raten.' } })).includes('Zahlung in zwei Raten'))
const ohneSumme = aufwand({ zahlungsplan: { art: 'anzahlung', prozent: 50, text: '' } })
assert.equal(zahlungsplanText(ohneSumme), zahlungsplanText({ ...ohneSumme, zahlungsplan: { art: 'nach_abnahme', prozent: null, text: '' } }))
})
test('aufwandText: nur bei Aufwand, mit Stundensatz und Schätzung in Stunden', () => {
const text = aufwandText(aufwand())
assert.ok(text.includes(euroDe(10000)))
assert.match(text, /\b10\b/)
assert.doesNotMatch(aufwandText(aufwand({ schaetzungMinuten: null })), /\b10\b/)
assert.match(aufwandText(aufwand({ schaetzungMinuten: 135 })), /2,25/)
assert.match(aufwandText(aufwand({ schaetzungMinuten: 90 })), /1,5\b/)
assert.ok(aufwandText(aufwand({ stundensatzCents: null })).includes(euroDe(10000)), 'ohne Satz gilt der Standard')
assert.equal(aufwandText(festpreis()), null)
})
test('auftragFuerTeam: ohne Token-Hash, PDF-Pfad und Bestätigungs-Prüfsumme, mit den abgeleiteten Feldern', () => {
const auftrag = festpreis()
const kopie = structuredClone(auftrag)
const sicht = auftragFuerTeam(auftrag)
assert.deepEqual(auftrag, kopie, 'verändert die AB nicht')
const entfernt = ['tokenHash', 'pdfDatei', 'bestaetigtPdfSha256']
for (const feld of entfernt) assert.equal(Object.hasOwn(sicht, feld), false, feld)
assert.equal(sicht.pdfSha256, 'a'.repeat(64))
assert.equal(sicht.pdfVorhanden, true)
assert.equal(sicht.fruehesterBeginn, '2026-10-21')
assert.equal(sicht.zahlungsplanText, zahlungsplanText(auftrag))
assert.equal(sicht.aufwandText, null)
assert.deepEqual(
Object.keys(sicht).sort(),
[...Object.keys(auftrag).filter((k) => !entfernt.includes(k)), 'pdfVorhanden', 'fruehesterBeginn', 'zahlungsplanText', 'aufwandText'].sort(),
)
assert.equal(auftragFuerTeam(festpreis({ pdfDatei: '' })).pdfVorhanden, false)
})
test('oeffentlicheAnsicht: genau die Felder aus V3, Beträge je Position, Berliner Datum, nichts Internes', () => {
const ansicht = oeffentlicheAnsicht(festpreis())
assert.deepEqual(
Object.keys(ansicht).sort(),
['agbUrl', 'aufwandText', 'art', 'bestaetigtAm', 'datum', 'fertigBis', 'fruehbeginn', 'fruehesterBeginn', 'kundenart', 'nummer', 'positionen', 'schaetzungMinuten', 'startAm', 'status', 'stundensatzCents', 'summeCents', 'texte', 'titel', 'zahlungsplanText'].sort(),
)
assert.deepEqual(ansicht.positionen, [
{ beschreibung: 'Startseite', menge: 2, einzelpreisCents: 30000, summeCents: 60000 },
{ beschreibung: 'Kontaktformular', menge: 1, einzelpreisCents: 20000, summeCents: 20000 },
])
assert.deepEqual(
[ansicht.nummer, ansicht.datum, ansicht.titel, ansicht.art, ansicht.summeCents, ansicht.startAm, ansicht.kundenart, ansicht.status],
['AB-2026-0002', '2026-10-06', 'Website Bäckerei', 'festpreis', 80000, '2026-11-01', 'privat', 'versendet'],
)
assert.equal(ansicht.fruehesterBeginn, '2026-10-21')
assert.deepEqual(ansicht.texte, { bestaetigung: BESTAETIGUNG_TEXT, fruehbeginn: FRUEHBEGINN_TEXT, kleinunternehmer: AB_TEXTE.kleinunternehmer })
assert.equal(ansicht.texte.kleinunternehmer, 'Gemäß § 19 UStG wird keine Umsatzsteuer berechnet.')
assert.equal(ansicht.agbUrl, '/rechtliches/agb-2026-09-30.pdf')
const text = JSON.stringify(ansicht)
for (const geheim of ['b'.repeat(64), 'c1/ab1.pdf', 'anna@example.invalid', 'Hauptstr. 1', 'kenso@webklar.com']) assert.ok(!text.includes(geheim), geheim)
})
test('oeffentlicheAnsicht: Unternehmen ohne Frühbeginn-Text, Datum nach Berliner Zeit, AGB-Adresse ohne gespeicherte Fassung', () => {
const firma = oeffentlicheAnsicht(festpreis({ kunde: { ...KUNDE, kundenart: 'unternehmen' }, gesendetAm: '2026-10-06T22:30:00.000Z', agbFassung: '' }))
assert.equal(firma.texte.fruehbeginn, null)
assert.equal(firma.texte.kleinunternehmer, AB_TEXTE.kleinunternehmer)
assert.equal(firma.fruehesterBeginn, null)
assert.equal(firma.datum, '2026-10-07')
assert.equal(firma.agbUrl, `/rechtliches/agb-${AGB_FASSUNG}.pdf`)
const aufwandAnsicht = oeffentlicheAnsicht(aufwand())
assert.deepEqual([aufwandAnsicht.positionen, aufwandAnsicht.stundensatzCents, aufwandAnsicht.schaetzungMinuten], [[], 10000, 600])
assert.equal(aufwandAnsicht.aufwandText, aufwandText(aufwand()))
})