Files
SDS-CRM/server/lib/mappers.js
2026-03-23 02:09:14 +01:00

139 lines
4.1 KiB
JavaScript

export function parseJsonField(v) {
if (v == null) return undefined;
if (typeof v === 'object') return v;
return JSON.parse(v);
}
export function mapMachine(r) {
const o = {
id: r.id,
name: r.name,
typ: r.typ,
seriennummer: r.seriennummer,
standort: r.standort,
listStatus:
r.list_status != null && String(r.list_status).trim() !== ''
? String(r.list_status).trim()
: '',
createdAt: r.created_at,
updatedAt: r.updated_at,
};
if (r.extras != null && String(r.extras).trim() !== '') {
try {
o.extras =
typeof r.extras === 'string' ? JSON.parse(r.extras) : r.extras;
} catch {
o.extras = null;
}
}
return o;
}
export function mapTicket(r) {
const machine_row = parseJsonField(r.machine_row);
const t = {
id: r.id,
machineId: r.machine_id,
title: r.title,
description: r.description,
status: r.status,
priority: r.priority,
slaDays: r.sla_days != null ? r.sla_days : null,
slaAnchorAt: r.sla_anchor_at ?? null,
dueAt: r.sla_due_at ?? null,
isOverdue: Boolean(r.sla_is_overdue),
createdAt: r.created_at,
/** Letzte Änderung: neueres aus Ticket-Zeile oder letztem Event (für Anzeige „Aktualisiert“). */
updatedAt: r.ticket_last_activity_at ?? r.updated_at,
};
if (machine_row) {
t.machine = mapMachine(machine_row);
}
return t;
}
function mapAttachmentRow(a, ticketId) {
return {
id: a.id,
originalName: a.original_name,
mimeType: a.mime_type ?? null,
sizeBytes: a.size_bytes,
createdAt: a.created_at,
url: `/tickets/${ticketId}/attachments/${a.id}/file`,
};
}
export function mapEvent(r, attachmentRows = []) {
const list = Array.isArray(attachmentRows) ? attachmentRows : [];
return {
id: r.id,
ticketId: r.ticket_id,
type: r.type,
description: r.description,
createdAt: r.created_at,
callbackNumber: r.callback_number ?? null,
teamviewerId: r.teamviewer_id ?? null,
articleNumber: r.article_number ?? null,
remoteDurationSeconds:
r.remote_duration_seconds != null ? r.remote_duration_seconds : null,
teamviewerNotes:
r.teamviewer_notes != null && String(r.teamviewer_notes).trim() !== ''
? String(r.teamviewer_notes).trim()
: null,
attachments: list.map((a) => mapAttachmentRow(a, r.ticket_id)),
};
}
export function mapPublicUser(r) {
return {
id: r.id,
username: r.username,
role: r.role,
active: Boolean(r.active),
source: r.source,
ldapDn: r.ldap_dn || null,
createdAt: r.created_at,
updatedAt: r.updated_at,
};
}
/** Spätester Start der Bearbeitungszeit: gespeicherter Anker oder letztes Nutzer-Event (ohne SYSTEM). */
export const ticketSlaActivityAnchorExpr = `MAX(
datetime(COALESCE(t.sla_anchor_at, t.created_at)),
COALESCE(
(SELECT MAX(datetime(e.created_at)) FROM events e
WHERE e.ticket_id = t.id AND e.type IN ('NOTE','CALL','REMOTE','PART','ATTACHMENT')),
datetime(COALESCE(t.sla_anchor_at, t.created_at))
)
)`;
export const ticketSlaDueExpr = `datetime((${ticketSlaActivityAnchorExpr}), '+' || CAST(COALESCE(t.sla_days, 2) AS TEXT) || ' days')`;
/** Spätester Zeitpunkt aus Ticket und Historie (alle Event-Typen). */
export const ticketLastActivityExpr = `MAX(
datetime(t.updated_at),
COALESCE(
(SELECT MAX(datetime(e.created_at)) FROM events e WHERE e.ticket_id = t.id),
datetime(t.updated_at)
)
)`;
export const ticketJoinSelect = `
SELECT t.*,
${ticketLastActivityExpr} AS ticket_last_activity_at,
${ticketSlaDueExpr} AS sla_due_at,
(CASE WHEN t.status IN ('OPEN','WAITING') AND datetime('now') > ${ticketSlaDueExpr} THEN 1 ELSE 0 END) AS sla_is_overdue,
json_object(
'id', m.id,
'name', m.name,
'typ', m.typ,
'seriennummer', m.seriennummer,
'standort', m.standort,
'list_status', m.list_status,
'extras', m.extras,
'created_at', m.created_at,
'updated_at', m.updated_at
) AS machine_row
FROM tickets t
JOIN machines m ON m.id = t.machine_id`;