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 /.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]) })