Files
deskhub/DeskHub-Webseite/api/staff/v1/ticket_update.php
T
2026-10-21 16:44:58 +00:00

104 lines
4.8 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
require_once __DIR__ . '/common.php';
api_require_method('PATCH', 'POST');
$staff = api_require_staff();
$body = api_request_body();
$number = trim((string) ($body['number'] ?? ''));
if ($number === '') {
api_error(422, 'TICKET_NUMBER_REQUIRED', 'Eine Ticketnummer ist erforderlich.');
}
$pdo = get_db();
$ticket = api_find_ticket($pdo, $staff, $number);
if ($ticket['source'] !== 'chat') {
$lockedByName = ticket_locked_by_other($ticket, (int) $staff['id'], $pdo);
if ($lockedByName) {
api_error(409, 'TICKET_LOCKED', "Dieses Ticket wird gerade von \"$lockedByName\" bearbeitet.");
}
}
$updates = [];
$params = [];
$now = db_now();
$chatJustClosed = false;
if (array_key_exists('status', $body) && $body['status'] !== $ticket['status']) {
$status = (string) $body['status'];
if (!in_array($status, MOBILE_API_STATUSES, true)) {
api_error(422, 'INVALID_STATUS', 'Der Status ist ungültig.');
}
api_require_permission($staff, in_array($status, ['resolved', 'closed'], true) ? 'close_ticket' : 'reply_ticket', (int) $ticket['department_id']);
$updates[] = 'status = ?';
$params[] = $status;
if ($status === 'resolved' && empty($ticket['resolved_at'])) {
$updates[] = 'resolved_at = ?';
$params[] = $now;
}
if ($status === 'closed' && empty($ticket['closed_at'])) {
$updates[] = 'closed_at = ?';
$params[] = $now;
}
// Live-Chat, der über die App geschlossen wird, bekommt denselben Chatverlauf-Mail-
// Versand wie über staff/live_chat.php (Aktion "end") – siehe unten nach dem UPDATE.
$chatJustClosed = $status === 'closed' && $ticket['source'] === 'chat';
}
if (array_key_exists('assignedStaffId', $body)) {
$assignedId = $body['assignedStaffId'] === null ? null : filter_var($body['assignedStaffId'], FILTER_VALIDATE_INT);
if ($assignedId === false) {
api_error(422, 'INVALID_ASSIGNEE', 'Die Mitarbeiter-ID ist ungültig.');
}
if ($assignedId !== null) {
$agentStmt = $pdo->prepare('SELECT COUNT(*) FROM staff WHERE id = ? AND is_active = 1');
$agentStmt->execute([$assignedId]);
if (!(bool) $agentStmt->fetchColumn()) {
api_error(422, 'INVALID_ASSIGNEE', 'Der ausgewählte Mitarbeiter ist nicht verfügbar.');
}
}
$currentId = $ticket['assigned_staff_id'] === null ? null : (int) $ticket['assigned_staff_id'];
if ($assignedId !== $currentId) {
api_require_permission($staff, 'assign_ticket', (int) $ticket['department_id']);
$updates[] = 'assigned_staff_id = ?';
$params[] = $assignedId;
}
}
if (!$updates) {
api_json(['ticket' => api_ticket_summary($ticket)]);
}
$updates[] = 'updated_at = ?';
$params[] = $now;
$params[] = $ticket['id'];
$pdo->prepare('UPDATE tickets SET ' . implode(', ', $updates) . ' WHERE id = ?')->execute($params);
if (array_key_exists('status', $body) && $body['status'] !== $ticket['status']) {
[$label] = status_label((string) $body['status']);
log_activity($pdo, (int) $ticket['id'], (int) $staff['id'], 'Status geändert: ' . $label . ' (Mobile App)');
}
if (array_key_exists('assignedStaffId', $body)) {
log_activity($pdo, (int) $ticket['id'], (int) $staff['id'], 'Zuweisung geändert (Mobile App)');
if ($assignedId !== null && $assignedId !== $currentId) fcm_notify_assignment($ticket, $assignedId, (int) $staff['id']); // Push auf die Android-App des neuen Bearbeiters
}
if ($chatJustClosed && $ticket['requester_email']) {
try {
$transcriptStmt = $pdo->prepare(
'SELECT tt.author_type AS authorType, tt.message, tt.created_at AS createdAt, s.name AS staff_name
FROM ticket_threads tt LEFT JOIN staff s ON s.id = tt.staff_id
WHERE tt.ticket_id = ? AND tt.is_internal_note = 0 ORDER BY tt.created_at ASC, tt.id ASC'
);
$transcriptStmt->execute([$ticket['id']]);
$transcriptMessages = array_map(fn(array $m) => [
'authorType' => $m['authorType'],
'authorName' => $m['staff_name'],
'message' => $m['message'],
'createdAt' => $m['createdAt'],
], $transcriptStmt->fetchAll());
notify_customer_chat_transcript($ticket, $transcriptMessages);
} catch (Throwable $e) {
error_log('ticket_update (Mobile App) transcript mail: ' . $e->getMessage());
}
}
$updated = api_find_ticket($pdo, $staff, $number);
if ($updated['status'] !== $ticket['status']) webhook_dispatch($pdo, 'ticket.status_changed', (int) $ticket['id'], ['previousStatus' => $ticket['status']]);
if ((int) ($updated['assigned_staff_id'] ?? 0) !== (int) ($ticket['assigned_staff_id'] ?? 0)) webhook_dispatch($pdo, 'ticket.assigned', (int) $ticket['id']);
api_json(['ticket' => api_ticket_summary($updated)]);