import { test, after, beforeEach } from 'node:test' import assert from 'node:assert/strict' import { starteUmgebung, KUNDE_ID, PROJEKT_ID, TICKET_ID, ZWEITES_TICKET, LEAD_KUNDE, FREMD, VON, MELDUNG, festpreisEingabe, aufwandEingabe, abgelehnt, } from './helpers/auftraegeAb.js' const umg = await starteUmgebung() const { aw, config } = umg after(() => umg.schliesse()) beforeEach(umg.zuruecksetzen) const { getDocument } = await import('../server/services/appwriteAdmin.js') const { getAuftrag, updateAuftrag } = await import('../server/services/auftraege/auftragStore.js') const { legeEntwurfAn, aendereEntwurf, loescheEntwurf } = await import('../server/services/auftraege/auftraege.js') const kundenartVon = async (id) => (await getDocument(config.collections.customers, id)).kundenart || '' const FREMDES_PROJEKT = MELDUNG.projektFremd const FREMDES_TICKET = 'Zeitquelle: Mindestens ein Ticket gehört nicht zu diesem Kunden.' test('legeEntwurfAn: Festpreis-Entwurf mit Titel und Zeitquelle aus dem Projekt, Summe und Ersteller', async () => { const a = await legeEntwurfAn(festpreisEingabe(), { von: VON }) assert.deepEqual([a.status, a.nummer, a.kundenId, a.projektId, a.titel], ['entwurf', '', KUNDE_ID, PROJEKT_ID, 'Website Beispiel']) assert.deepEqual(a.zeitquelle, [TICKET_ID]) assert.deepEqual([a.art, a.summeCents, a.erstelltVon], ['festpreis', 80000, VON]) assert.deepEqual(a.zahlungsplan, { art: 'anzahlung', prozent: 50, text: '' }) assert.deepEqual(await getAuftrag(a.id), a) }) test('legeEntwurfAn: ein eigener Titel gilt; ohne Projekt braucht es einen Titel', async () => { assert.equal((await legeEntwurfAn(festpreisEingabe({ titel: ' Relaunch Phase 1 ' }), { von: VON })).titel, 'Relaunch Phase 1') await abgelehnt(legeEntwurfAn(festpreisEingabe({ projektId: '' }), { von: VON }), 400, 'Titel: Bitte einen Titel angeben oder ein Projekt wählen.') const frei = await legeEntwurfAn(festpreisEingabe({ projektId: '', titel: 'Logo-Entwurf' }), { von: VON }) assert.deepEqual([frei.projektId, frei.zeitquelle], ['', []]) }) test('legeEntwurfAn: Aufwand-Entwurf ohne Positionen und Summe, mit Stundensatz, Schätzung und gewählten Tickets', async () => { const a = await legeEntwurfAn(aufwandEingabe({ zeitquelle: [TICKET_ID, ZWEITES_TICKET] }), { von: VON }) assert.deepEqual([a.art, a.positionen, a.summeCents, a.stundensatzCents, a.schaetzungMinuten], ['aufwand', [], null, 10000, 600]) assert.deepEqual(a.zeitquelle, [TICKET_ID, ZWEITES_TICKET]) assert.deepEqual(a.zahlungsplan, { art: 'monatlich', prozent: null, text: '' }) }) test('legeEntwurfAn: Entwürfe gibt es auch für Interessenten (nur das Senden ist festen Kunden vorbehalten)', async () => { const a = await legeEntwurfAn(festpreisEingabe({ kundenId: LEAD_KUNDE, projektId: '', titel: 'Angebotsentwurf' }), { von: VON }) assert.deepEqual([a.kundenId, a.status], [LEAD_KUNDE, 'entwurf']) }) test('kundenart aus der Eingabe wird am Kunden gespeichert – aber nur, wenn dort noch keine steht', async () => { await legeEntwurfAn(festpreisEingabe({ kundenId: FREMD.kunde, projektId: FREMD.projekt, kundenart: 'privat' }), { von: VON }) assert.equal(await kundenartVon(FREMD.kunde), 'privat') await legeEntwurfAn(festpreisEingabe({ kundenart: 'privat' }), { von: VON }) // der Kunde ist schon 'unternehmen' assert.equal(await kundenartVon(KUNDE_ID), 'unternehmen') await legeEntwurfAn(festpreisEingabe({ kundenId: LEAD_KUNDE, projektId: '', titel: 'x', kundenart: '' }), { von: VON }) assert.equal(await kundenartVon(LEAD_KUNDE), '') }) test('fremde Bezüge werden abgelehnt, ohne dass etwas gespeichert wird (auch nicht die Kundenart)', async () => { const eingabe = (patch) => festpreisEingabe({ kundenId: FREMD.kunde, projektId: FREMD.projekt, kundenart: 'privat', ...patch }) const faelle = [ ['Projekt eines anderen Kunden', { projektId: PROJEKT_ID }, FREMDES_PROJEKT], ['unbekanntes Projekt', { projektId: 'p_gibtsnicht' }, FREMDES_PROJEKT], ['Ticket eines anderen Kunden', { zeitquelle: [TICKET_ID] }, FREMDES_TICKET], ['ein Ticket unbekannt', { zeitquelle: [FREMD.ticket, 't_gibtsnicht'] }, FREMDES_TICKET], ] for (const [name, patch, meldung] of faelle) await abgelehnt(legeEntwurfAn(eingabe(patch), { von: VON }), 400, meldung, name) assert.equal(aw.docs(config.collections.auftraege).size, 0) assert.equal(await kundenartVon(FREMD.kunde), '') }) test('Kunde: ohne Kennung 400, unbekannt 404, Kennung mit Sonderzeichen 404', async () => { await abgelehnt(legeEntwurfAn(festpreisEingabe({ kundenId: undefined }), { von: VON }), 400, 'Kunde: Bitte einen Kunden angeben.') await abgelehnt(legeEntwurfAn(festpreisEingabe({ kundenId: 'c_gibtsnicht' }), { von: VON }), 404, MELDUNG.kundeFehlt) await abgelehnt(legeEntwurfAn(festpreisEingabe({ kundenId: '../users' }), { von: VON }), 404, MELDUNG.kundeFehlt) }) test('status, nummer, Token und Ersteller aus der Eingabe werden ignoriert', async () => { const a = await legeEntwurfAn( { ...festpreisEingabe(), status: 'bestaetigt', nummer: 'AB-2026-0001', jahr: 2026, tokenHash: 'abc', ersetztId: 'a_alt', erstelltVon: 'boese@example.invalid', pdfDatei: 'x/y.pdf' }, { von: VON }, ) assert.deepEqual([a.status, a.nummer, a.jahr, a.erstelltVon], ['entwurf', '', null, VON]) assert.ok(!a.tokenHash && !a.ersetztId && !a.pdfDatei) }) test('aendereEntwurf: nur die genannten Felder ändern sich, der Rest bleibt', async () => { const a = await legeEntwurfAn(festpreisEingabe(), { von: VON }) const b = await aendereEntwurf(a.id, { titel: 'Neuer Titel', zahlungszielTage: 30 }) assert.deepEqual([b.titel, b.zahlungszielTage, b.summeCents], ['Neuer Titel', 30, 80000]) assert.deepEqual([b.positionen, b.zahlungsplan, b.zeitquelle], [a.positionen, a.zahlungsplan, a.zeitquelle]) assert.deepEqual(await getAuftrag(a.id), b) }) test('aendereEntwurf: Wechsel der Art prüft das Ganze (Anzahlung geht nicht bei Aufwand), mit passendem Plan klappt er', async () => { const a = await legeEntwurfAn(festpreisEingabe(), { von: VON }) await abgelehnt(aendereEntwurf(a.id, { art: 'aufwand' }), 400, 'Zahlungsplan: Eine Anzahlung geht nur bei einem Festpreis.') assert.equal((await getAuftrag(a.id)).art, 'festpreis') const b = await aendereEntwurf(a.id, { art: 'aufwand', stundensatzCents: 12000, zahlungsplan: { art: 'monatlich' } }) assert.deepEqual([b.art, b.positionen, b.summeCents, b.stundensatzCents, b.schaetzungMinuten], ['aufwand', [], null, 12000, null]) }) test('aendereEntwurf: Kunde lässt sich nicht wechseln, fremdes Projekt wird abgelehnt, unbekannte Aufträge sind 404', async () => { const a = await legeEntwurfAn(festpreisEingabe(), { von: VON }) await abgelehnt(aendereEntwurf(a.id, { kundenId: FREMD.kunde }), 400, 'Kunde: Der Kunde eines Auftrags lässt sich nicht ändern.') assert.equal((await aendereEntwurf(a.id, { kundenId: KUNDE_ID, titel: 'Gleicher Kunde' })).titel, 'Gleicher Kunde') await abgelehnt(aendereEntwurf(a.id, { projektId: FREMD.projekt }), 400, FREMDES_PROJEKT) await abgelehnt(aendereEntwurf('a_gibtsnicht', { titel: 'x' }), 404, MELDUNG.auftragFehlt) await abgelehnt(aendereEntwurf('../x', { titel: 'x' }), 404, MELDUNG.auftragFehlt) }) test('Ändern und Löschen gibt es nur für Entwürfe (409); Löschen entfernt den Entwurf', async () => { const a = await legeEntwurfAn(festpreisEingabe(), { von: VON }) await updateAuftrag(a.id, { status: 'versendet', nummer: 'AB-2026-0001', jahr: 2026, laufnummer: 1 }) await abgelehnt(aendereEntwurf(a.id, { titel: 'Nachträglich' }), 409, MELDUNG.nurEntwurfAendern) await abgelehnt(loescheEntwurf(a.id), 409, MELDUNG.nurEntwurfLoeschen) assert.equal((await getAuftrag(a.id)).titel, 'Website Beispiel') const b = await legeEntwurfAn(festpreisEingabe(), { von: VON }) await loescheEntwurf(b.id) assert.equal(await getAuftrag(b.id), null) await abgelehnt(loescheEntwurf(b.id), 404, MELDUNG.auftragFehlt) })