Files
Webklar-Kundenbereich/test/auftraegeAbVertrag.test.js
T
2026-10-07 14:11:34 +00:00

91 lines
6.4 KiB
JavaScript

import { test, after } from 'node:test'
import assert from 'node:assert/strict'
import { starteUmgebung, entwurfsDaten, sha256, KUNDE_ID, PROJEKT_ID, VON } from './helpers/auftraegeAb.js'
// Vertragsprüfung: Die L0-Module (Fundament) liefern, was der Strang L3 braucht (Übersicht V1 bis V4). Schlägt sie fehl,
// weicht L0 vom Vertrag ab: nichts in fremden Dateien reparieren, sondern stoppen und melden.
const umg = await starteUmgebung()
after(() => umg.schliesse())
const DIENSTE = '../server/services/auftraege'
const store = await import(`${DIENSTE}/auftragStore.js`)
const ablage = await import(`${DIENSTE}/ablage.js`)
const { getSettings } = await import('../server/services/finance/settings.js')
const EXPORTE = {
'konstanten.js': ['AUFTRAG_ARTEN', 'AUFTRAG_WEGE', 'ZAHLUNGSPLAN_ARTEN', 'KUNDENARTEN', 'VEREINBARTE_STATUS', 'STUNDENSATZ_STANDARD_CENTS', 'VIERTELSTUNDE', 'TOKEN_GUELTIG_TAGE', 'AGB_FASSUNG', 'LIMITS', 'SPERRE_NUMMER', 'sperreAuftrag', 'SCHALTER_AUS_MELDUNG'],
'fehler.js': ['AuftragError', 'sendeFehler'],
'schalter.js': ['auftraegeAktiv'],
'auftragStore.js': ['getAuftrag', 'listAuftraege', 'createAuftrag', 'updateAuftrag', 'deleteAuftrag', 'findAuftragByTokenHash', 'findAuftragByNummer'],
'projektRechnungenStore.js': ['getProjektRechnung', 'listProjektRechnungen', 'createProjektRechnung', 'updateProjektRechnung', 'umhaengen'],
'auftragSicht.js': ['kundeSnapshot', 'auftragFuerTeam'],
'ablage.js': ['speicherePdf', 'lesePdf', 'entfernePdf'], // entfernePdf: Nachtrag der Planrunde (Übersicht, Nachträge L0)
'texte.js': ['TEXTE_FASSUNG'],
}
test('L0-Module, Konfiguration und Einstellungen liefern die Namen der Übersicht', async () => {
for (const [datei, namen] of Object.entries(EXPORTE)) {
const modul = await import(`${DIENSTE}/${datei}`)
for (const name of namen) assert.ok(name in modul, `${datei} exportiert ${name} nicht`)
}
for (const feld of ['mode', 'pilotEmails', 'uploadDir']) assert.ok(feld in umg.config.auftraege, `config.auftraege.${feld} fehlt`)
assert.equal(umg.config.auftraege.uploadDir, umg.uploadDir)
assert.equal(umg.config.collections.auftraege, 'auftraege')
assert.equal(umg.config.collections.projektRechnungen, 'projektRechnungen')
assert.equal((await getSettings()).steuernummer, '')
// Sperren (V2): je Sperre ein zusammengesetzter Schlüssel, weil withLocks jedes Listenelement für sich sperrt
const { SPERRE_NUMMER, sperreAuftrag } = await import(`${DIENSTE}/konstanten.js`)
assert.deepEqual(SPERRE_NUMMER, ['auftraege-nummer'])
assert.deepEqual(sperreAuftrag('a_x1'), ['auftraege-ab:a_x1'])
})
test('createAuftrag legt einen Entwurf mit den Leerwerten aus V3 an, updateAuftrag ändert nur die genannten Felder', async () => {
const a = await store.createAuftrag(entwurfsDaten())
assert.deepEqual(
[a.status, a.nummer, a.jahr, a.laufnummer, a.kunde, a.gesendetAm, a.tokenGueltigBis, a.bestaetigtAm, a.bestaetigtUeber, a.mailStatus, a.fruehbeginn, a.erstelltVon],
['entwurf', '', null, null, null, null, null, null, '', '', false, VON],
)
assert.equal(typeof a.id, 'string')
assert.ok(!Number.isNaN(new Date(a.erstelltAm).getTime()))
assert.deepEqual(a.positionen, entwurfsDaten().positionen)
assert.deepEqual(a.zahlungsplan, { art: 'anzahlung', prozent: 50, text: '' })
assert.deepEqual(await store.getAuftrag(a.id), a)
const b = await store.updateAuftrag(a.id, { titel: 'Anderer Titel' })
assert.equal(b.titel, 'Anderer Titel')
assert.deepEqual([b.positionen, b.zeitquelle, b.zahlungsplan, b.summeCents], [a.positionen, a.zeitquelle, a.zahlungsplan, a.summeCents])
assert.deepEqual(await store.getAuftrag(a.id), b)
assert.equal(await store.getAuftrag('a_gibtsnicht'), null)
})
test('listAuftraege liefert alle Treffer, auch mehr als die 25 Dokumente, die Appwrite ohne limit liefert', async () => {
for (let i = 0; i < 30; i += 1) await store.createAuftrag(entwurfsDaten({ kundenId: 'c_liste01' }))
assert.equal((await store.listAuftraege({ kundenId: 'c_liste01' })).length, 30)
})
test('ablage: speicherePdf legt <kundenId>/<id>.pdf an, überschreibt nie; lesePdf liest und lehnt fremde Pfade ab; entfernePdf räumt auf', async () => {
const pdf = Buffer.from('%PDF-1.4 vertrag')
assert.deepEqual(await ablage.speicherePdf('c_ablage', 'a_ablage', pdf), { pdfDatei: 'c_ablage/a_ablage.pdf', pdfSha256: sha256(pdf) })
assert.deepEqual(await ablage.lesePdf('c_ablage/a_ablage.pdf'), pdf)
await assert.rejects(async () => ablage.speicherePdf('c_ablage', 'a_ablage', Buffer.from('%PDF-1.4 anders')))
await assert.rejects(async () => ablage.lesePdf('../etc/passwd'), (err) => err.status === 404)
await ablage.entfernePdf('c_ablage/a_ablage.pdf')
await assert.rejects(async () => ablage.lesePdf('c_ablage/a_ablage.pdf'), (err) => err.status === 404)
assert.equal((await ablage.speicherePdf('c_ablage', 'a_ablage', pdf)).pdfDatei, 'c_ablage/a_ablage.pdf')
})
test('umhaengen hängt alle Dokumente einer AB auf die neue um und zählt sie; ein zweiter Aufruf findet nichts mehr; list und update genügen für die Projektzuordnung', async () => {
const { createProjektRechnung, getProjektRechnung, listProjektRechnungen, updateProjektRechnung, umhaengen } = await import(`${DIENSTE}/projektRechnungenStore.js`)
const daten = (auftragId) => ({ kundenId: KUNDE_ID, projektId: PROJEKT_ID, auftragId, art: 'anzahlung', betragCents: 40000, arbeitsblattIds: [], rechnungsnummer: 'WEBKLAR-0001', aktiv: true, erstelltVon: VON, erstelltAm: new Date().toISOString() })
await createProjektRechnung('in_Umhaengen1', daten('a_alt'))
await createProjektRechnung('in_Umhaengen2', daten('a_anders'))
assert.equal(await umhaengen('a_alt', 'a_neu'), 1)
assert.equal((await getProjektRechnung('in_Umhaengen1')).auftragId, 'a_neu')
assert.equal((await getProjektRechnung('in_Umhaengen2')).auftragId, 'a_anders')
assert.equal(await umhaengen('a_alt', 'a_neu'), 0)
// Projektzuordnung (Task 11): alle Dokumente einer AB, auch inaktive; die projektId lässt sich einzeln ändern, sonst bleibt alles
await updateProjektRechnung('in_Umhaengen1', { aktiv: false })
assert.deepEqual((await listProjektRechnungen({ auftragId: 'a_neu', nurAktiv: false })).map((r) => r.invoiceId), ['in_Umhaengen1'])
assert.deepEqual(await listProjektRechnungen({ auftragId: 'a_neu' }), [])
const geaendert = await updateProjektRechnung('in_Umhaengen1', { projektId: 'p_neu01' })
assert.deepEqual([geaendert.projektId, geaendert.auftragId, geaendert.betragCents, geaendert.aktiv], ['p_neu01', 'a_neu', 40000, false])
})