diff --git a/server/services/ticketAdmin.js b/server/services/ticketAdmin.js index a2e35a0..24ef862 100644 --- a/server/services/ticketAdmin.js +++ b/server/services/ticketAdmin.js @@ -1,6 +1,7 @@ import { config } from '../config.js' import { createDocument, + deleteDocument, getDocument, listDocuments, updateDocument, @@ -105,3 +106,47 @@ export async function createFeatureTicket({ customerId, project, featureKey, sit console.log(`[ticketAdmin] Funktions-Ticket #${woid} (${featureKey}) fuer Projekt ${project?.$id} angelegt`) return { ticketId: ticket.$id, woid } } + +/** + * Legt ein Ticket fuer ein Projekt an (Zuordnen-Flow, erst beim Abschliessen). + * Unbekannter Kunde => Fehler mit status 400. + */ +export async function createTicketForProject({ customerId, topic, type, requestedBy, startDate, details }) { + const customer = customerId + ? await getDocument(config.collections.customers, customerId).catch(() => null) + : null + if (!customer) { + const err = new Error('Kunde nicht gefunden.') + err.status = 400 + throw err + } + + const woid = await generateNextWoid() + const ticket = await createDocument( + config.collections.workorders, + { + topic, + type, + status: 'Open', + priority: 1, + serviceType: 'Remote', + systemType: 'n/a', + customerId, + customerName: customer.name || '', + customerLocation: customer.location || '', + requestedBy: requestedBy || '', + startDate: startDate || '', + details: details || '', + woid, + createdAt: new Date().toISOString(), + }, + ID.unique() + ) + console.log(`[ticketAdmin] Ticket #${woid} fuer Kunde ${customerId} angelegt`) + return { $id: ticket.$id, woid } +} + +/** Loescht ein Ticket wieder (Rollback, wenn der Zuordnen-Flow scheitert). */ +export async function deleteTicket(ticketId) { + await deleteDocument(config.collections.workorders, ticketId) +} diff --git a/test/ticketAdmin.test.js b/test/ticketAdmin.test.js new file mode 100644 index 0000000..2cde982 --- /dev/null +++ b/test/ticketAdmin.test.js @@ -0,0 +1,68 @@ +import { test, before, after } from 'node:test' +import assert from 'node:assert/strict' +import { startFakeAppwrite } from './helpers/fakeAppwrite.js' + +let aw +let createTicketForProject +let deleteTicket + +before(async () => { + aw = await startFakeAppwrite({ + seed: { + workorders: [{ $id: 't1', woid: '100115' }, { $id: 't2', woid: '100120' }], + customers: [{ $id: 'c1', name: 'Döner Nation', location: 'Berlin' }], + }, + }) + Object.assign(process.env, { + APPWRITE_ENDPOINT: aw.endpoint, + APPWRITE_API_KEY: 'test-key', + }) + // config.js liest die Umgebung beim Import + ;({ createTicketForProject, deleteTicket } = await import('../server/services/ticketAdmin.js')) +}) + +after(async () => { + await aw.close() +}) + +test('createTicketForProject legt ein offenes Ticket mit Kundendaten und nächster WOID an', async () => { + const r = await createTicketForProject({ + customerId: 'c1', + topic: 'Website – döner', + type: 'Webpage', + requestedBy: 'Max', + startDate: '2026-10-05', + details: 'Details', + }) + assert.equal(r.woid, '100121') + const d = aw.get('workorders', r.$id) + assert.equal(d.topic, 'Website – döner') + assert.equal(d.type, 'Webpage') + assert.equal(d.status, 'Open') + assert.equal(d.priority, 1) + assert.equal(d.serviceType, 'Remote') + assert.equal(d.systemType, 'n/a') + assert.equal(d.customerId, 'c1') + assert.equal(d.customerName, 'Döner Nation') + assert.equal(d.customerLocation, 'Berlin') + assert.equal(d.woid, '100121') + assert.equal(d.requestedBy, 'Max') + assert.equal(d.startDate, '2026-10-05') + assert.equal(d.details, 'Details') + assert.ok(d.createdAt) +}) + +test('createTicketForProject: unbekannter Kunde ⇒ Fehler mit status 400', async () => { + const before = aw.all('workorders').length + await assert.rejects( + () => createTicketForProject({ customerId: 'nix', topic: 'x', type: 'Webpage' }), + (err) => err.status === 400 && /Kunde/.test(err.message) + ) + assert.equal(aw.all('workorders').length, before) +}) + +test('deleteTicket löscht das Ticket', async () => { + await deleteTicket('t2') + assert.equal(aw.get('workorders', 't2'), undefined) + assert.ok(aw.calls.some((c) => c.method === 'DELETE' && c.path.endsWith('/documents/t2'))) +})