Benutzer, Ticketzuweißungen

This commit is contained in:
2026-03-23 03:12:08 +01:00
parent e75a2e5e20
commit 08391cdb6c
29 changed files with 592 additions and 111 deletions

View File

@@ -1,5 +1,6 @@
import { randomUUID } from 'crypto';
import db from '../../db.js';
import { requireCrmEdit } from '../../middleware/auth.js';
import { badRequest, UUID } from '../../lib/http.js';
import { mergeAttachmentEventsForApi } from '../../lib/ticket-events-merge.js';
import {
@@ -36,9 +37,15 @@ ORDER BY
CASE WHEN t.status IN ('OPEN','WAITING') AND datetime('now') > ${ticketSlaDueExpr} THEN ${ticketSlaDueExpr} ELSE '9999-12-31' END ASC,
${ticketLastActivityExpr} DESC`;
function userLabelFromRow(row) {
if (!row) return '—';
const fn = [row.firstname, row.lastname].filter(Boolean).join(' ').trim();
return fn || row.username || row.id;
}
export function registerTicketRoutes(api) {
api.get('/tickets', (req, res) => {
const { status, priority, machineId, open } = req.query;
const { status, priority, machineId, open, assignedTo } = req.query;
const cond = ['1=1'];
const params = [];
const openFilter = open === '1' || open === 'true';
@@ -56,12 +63,21 @@ export function registerTicketRoutes(api) {
cond.push('t.machine_id = ?');
params.push(machineId);
}
if (assignedTo === 'me' && req.session?.userId) {
cond.push('t.assigned_user_id = ?');
params.push(req.session.userId);
} else if (assignedTo === 'not_me' && req.session?.userId) {
cond.push(
'(t.assigned_user_id IS NULL OR t.assigned_user_id <> ?)',
);
params.push(req.session.userId);
}
const sql = `${ticketJoinSelect} WHERE ${cond.join(' AND ')} ${ticketListOrderBy}`;
const rows = db.prepare(sql).all(...params);
res.json(rows.map(mapTicket));
});
api.post('/tickets', (req, res) => {
api.post('/tickets', requireCrmEdit, (req, res) => {
const { machineId, title, description, status, priority, slaDays } =
req.body || {};
if (!machineId || !title || !description) {
@@ -125,7 +141,7 @@ export function registerTicketRoutes(api) {
res.json(mapTicket(row));
});
api.put('/tickets/:id', (req, res) => {
api.put('/tickets/:id', requireCrmEdit, (req, res) => {
const { id } = req.params;
if (!UUID.test(id)) return res.status(404).json({ message: 'Nicht gefunden' });
const cur = db.prepare('SELECT * FROM tickets WHERE id = ?').get(id);
@@ -173,6 +189,46 @@ export function registerTicketRoutes(api) {
`Fälligkeit: ${label(cur.sla_days)}${label(nextSlaDays)}`,
);
}
let nextAssignedUserId = cur.assigned_user_id ?? null;
if (Object.prototype.hasOwnProperty.call(b, 'assignedUserId')) {
const raw = b.assignedUserId;
if (raw === null || raw === undefined || raw === '') {
nextAssignedUserId = null;
} else if (!UUID.test(String(raw))) {
return badRequest(res, 'assignedUserId ungültig.');
} else {
const u = db
.prepare(
'SELECT id, username, firstname, lastname FROM users WHERE id = ? AND active = 1',
)
.get(String(raw));
if (!u) {
return badRequest(res, 'Zugewiesener Benutzer nicht gefunden oder inaktiv.');
}
nextAssignedUserId = u.id;
}
}
if (nextAssignedUserId !== (cur.assigned_user_id ?? null)) {
const prevU = cur.assigned_user_id
? db
.prepare(
'SELECT id, username, firstname, lastname FROM users WHERE id = ?',
)
.get(cur.assigned_user_id)
: null;
const nextU = nextAssignedUserId
? db
.prepare(
'SELECT id, username, firstname, lastname FROM users WHERE id = ?',
)
.get(nextAssignedUserId)
: null;
const fromLabel = prevU ? userLabelFromRow(prevU) : 'nicht zugewiesen';
const toLabel = nextU ? userLabelFromRow(nextU) : 'nicht zugewiesen';
lines.push(`Zuweisung: ${fromLabel}${toLabel}`);
}
if (lines.length > 0) {
const eid = randomUUID();
db.prepare(
@@ -184,6 +240,7 @@ export function registerTicketRoutes(api) {
db.prepare(
`UPDATE tickets SET title = ?, description = ?, status = ?, priority = ?, sla_days = ?,
sla_anchor_at = CASE WHEN ? THEN datetime('now') ELSE sla_anchor_at END,
assigned_user_id = ?,
updated_at = datetime('now')
WHERE id = ?`,
).run(
@@ -193,6 +250,7 @@ export function registerTicketRoutes(api) {
next.priority,
nextSlaDays,
resetSlaAnchor ? 1 : 0,
nextAssignedUserId,
id,
);