import { config } from '../config.js' import { berlinDateTime } from './berlinTime.js' import { createDocument, deleteDocument, getDocument, listDocuments, updateDocument, Query, ID, } from './appwriteAdmin.js' const WOID_BASE = 9999 async function generateNextWoid() { // Numerisches Maximum ueber alle Tickets - orderDesc('woid') sortiert Strings // und liefert bei gemischt 5-/6-stelligen WOIDs falsche Ergebnisse. const docs = await listDocuments(config.collections.workorders, [Query.limit(1000)]) const highest = docs.reduce((max, doc) => { const n = parseInt(doc.woid, 10) return Number.isNaN(n) ? max : Math.max(max, n) }, WOID_BASE) return String(highest + 1) } /** * Stellt sicher, dass ein Website-Projekt ein Ticket hat, damit Git-Pushes * als Worksheets (WSIDs) getrackt werden koennen. Legt bei Bedarf automatisch * ein 'Webpage'-Ticket fuer den Kunden des Projekts an und verknuepft es. * Projekte ohne Kunde bekommen kein automatisches Ticket. */ export async function ensureProjectTicket(project, { repoFullName } = {}) { if (!project) return null if (project.ticketId) return project.ticketId if (!project.customerId) return null const customer = await getDocument(config.collections.customers, project.customerId).catch(() => null) if (!customer) return null const woid = await generateNextWoid() const now = new Date().toISOString() const ticket = await createDocument( config.collections.workorders, { topic: `Website ${project.projectName || repoFullName || project.subdomain || ''}`.trim(), type: 'Webpage', status: 'Open', priority: 1, serviceType: 'Remote', systemType: 'n/a', customerId: project.customerId, customerName: customer.name || '', customerLocation: customer.location || '', woid, details: `Automatisch angelegt fuer das Repository ${repoFullName || project.repoFullName || ''} (erster Git-Push).`, createdAt: now, }, ID.unique() ) await updateDocument(config.collections.websiteProjects, project.$id, { ticketId: ticket.$id, updatedAt: now, }) console.log(`[ticketAdmin] Webpage-Ticket #${woid} fuer Projekt ${project.$id} angelegt`) return ticket.$id } /** * Einrichtungs-Ticket fuer eine gekaufte Funktion (z. B. News-Pinnwand) einer Kundenwebsite. * Wird geschlossen, sobald die Funktion eingebaut ist; danach schaltet das Portal sie frei. */ export async function createFeatureTicket({ customerId, project, featureKey, siteKey, topic = null, details = null }) { const customer = await getDocument(config.collections.customers, customerId).catch(() => null) const woid = await generateNextWoid() const projectName = project?.projectName || project?.subdomain || '' const domain = project?.liveDomain || project?.previewUrl || project?.subdomain || '' const embed = `
+ https://project.webklar.com/embed/news.js` const ticket = await createDocument( config.collections.workorders, { topic: topic || `News-Pinnwand einbauen – ${projectName}`.trim(), type: 'Webpage', status: 'Open', priority: 1, serviceType: 'Remote', systemType: 'n/a', customerId, customerName: customer?.name || '', customerLocation: customer?.location || '', woid, details: details || [ `Funktion: ${featureKey}`, `Projekt: ${projectName}`, `Domain: ${domain}`, `siteKey: ${siteKey}`, `Einbau: ${embed}`, 'Anleitung: docs/funktionen/news-einbau.md (Repo Webklar-Kundenbereich)', ].join('\n'), createdAt: new Date().toISOString(), }, ID.unique() ) 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 }, { now = new Date() } = {}) { 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 } // WOMS speichert Ticket-Daten als 'TT.MM.JJJJ'; leer = heute (Berlin). let date if (!startDate) { date = berlinDateTime(now).date } else { const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(String(startDate)) const valid = m && (() => { const d = new Date(Date.UTC(+m[1], +m[2] - 1, +m[3])) return d.getUTCFullYear() === +m[1] && d.getUTCMonth() === +m[2] - 1 && d.getUTCDate() === +m[3] })() if (!valid) { const err = new Error('Das Startdatum ist ungültig (erwartet: JJJJ-MM-TT).') err.status = 400 throw err } date = `${m[3]}.${m[2]}.${m[1]}` } 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: date, deadline: date, 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) }