Upload via GUI (452 Dateien)

This commit is contained in:
2026-11-05 21:21:44 +00:00
parent 0cd88ac2f9
commit e90c1a4563
10 changed files with 529 additions and 109 deletions
+31 -55
View File
@@ -316,10 +316,38 @@ function ticket_sla_completed_pause_seconds(PDO $pdo, int $ticketId): int
} catch (Throwable $e) { error_log('DeskHub SLA-Pausenzeiten nicht lesbar: ' . $e->getMessage()); return 0; }
}
/** Startet die SLA eines wieder geöffneten Tickets neu: neue Lösungsfrist ab jetzt, Überfällig-Markierung weg. */
function ticket_sla_restart(PDO $pdo, int $ticketId, ?int $staffId = null): void
{
$q = $pdo->prepare('SELECT t.priority_id, t.department_id, t.help_topic_id FROM tickets t WHERE t.id = ?');
$q->execute([$ticketId]);
$t = $q->fetch();
if (!$t) return;
$p = $pdo->prepare('SELECT * FROM priorities WHERE id = ?');
$p->execute([(int) $t['priority_id']]);
$priority = $p->fetch();
if (!$priority) { $pdo->prepare('UPDATE tickets SET is_overdue = 0, updated_at = updated_at WHERE id = ?')->execute([$ticketId]); return; }
$now = db_now();
$dates = calc_sla_dates($priority, $now, (int) $t['department_id'] ?: null, (int) $t['help_topic_id'] ?: null);
$pdo->prepare('UPDATE tickets SET resolution_due_at = ?, is_overdue = 0, sla_paused_at = NULL, updated_at = updated_at WHERE id = ?')->execute([$dates['resolution_due_at'], $ticketId]);
ticket_sla_history_add($pdo, $ticketId, 'restarted', 'resolution', 'SLA neu gestartet: Ticket wurde wieder geöffnet', $staffId, $now);
}
/** Call after the ticket status has been persisted. pending/on_hold pause both SLA clocks. */
function ticket_sla_status_transition_impl(PDO $pdo, int $ticketId, string $oldStatus, string $newStatus, ?int $staffId = null): void
{
if ($oldStatus === $newStatus) return;
// Wieder geöffnet (zum Beispiel weil der Kunde auf ein gelöstes Ticket antwortet): Die alte Lösungsfrist ist längst abgelaufen und würde
// sofort „SLA überschritten“ auslösen. Die SLA beginnt deshalb neu, mit den Zeiten der Priorität bzw. des SLA-Plans ab jetzt.
$terminal = ['resolved', 'closed'];
if (in_array($oldStatus, $terminal, true) && !in_array($newStatus, $terminal, true)) {
ticket_sla_restart($pdo, $ticketId, $staffId);
}
// Erledigt (gelöst oder geschlossen) ist dasselbe: Die SLA zählt nicht weiter, „überfällig“ und eine Wiedervorlage entfallen
if (!in_array($oldStatus, $terminal, true) && in_array($newStatus, $terminal, true)) {
try { $pdo->prepare('UPDATE tickets SET is_overdue = 0, updated_at = updated_at WHERE id = ?')->execute([$ticketId]); } catch (Throwable $e) { error_log('SLA erledigt: ' . $e->getMessage()); }
try { $pdo->prepare('UPDATE tickets SET snoozed_until = NULL, snoozed_by = NULL, updated_at = updated_at WHERE id = ?')->execute([$ticketId]); } catch (Throwable $e) { /* Spalte fehlt vor dem Datenbank-Update */ }
}
$wasPaused = ticket_sla_is_paused_status($oldStatus); $willPause = ticket_sla_is_paused_status($newStatus);
if (!$wasPaused && $willPause) {
$now = db_now();
@@ -2186,60 +2214,7 @@ function add_business_hours(string $from, int $hours): string
// Rollen & Berechtigungen
// ====================================================================
/**
* Standard-Berechtigungen je Admin/Agent-Rolle.
* Kann durch eine benutzerdefinierte Rolle (roles.permissions) überschrieben werden.
*/
function default_permissions(string $role): array
{
if ($role === 'admin') {
return array_fill_keys(array_merge([
'reply_ticket','close_ticket','delete_ticket','transfer_ticket',
'post_note','assign_ticket','manage_canned','view_reports',
'manage_kb','create_ticket',
], array_keys(admin_section_permissions())), true);
}
// Standard-Agent
return array_merge([
'reply_ticket' => true,
'close_ticket' => true,
'delete_ticket' => false,
'transfer_ticket' => true,
'post_note' => true,
'assign_ticket' => true,
'manage_canned' => false,
'view_reports' => true,
'manage_kb' => false,
'create_ticket' => true,
], array_fill_keys(array_keys(admin_section_permissions()), false));
}
/**
* Feinkörnige Rechte für einzelne Adminportal-Bereiche – damit ein Admin einem Agenten
* (per benutzerdefinierter Rolle, siehe staff/roles.php) gezielt einzelne Admin-Funktionen
* freigeben kann, ohne ihm die volle Administrator-Rolle zu geben ("Co-Admin").
* Admins haben über has_permission() ohnehin immer automatisch alle Rechte.
*/
function admin_section_permissions(): array
{
return [
'manage_departments' => 'Abteilungen verwalten',
'manage_priorities' => 'Prioritäten / SLA verwalten',
'manage_help_topics' => 'Hilfe-Themen verwalten',
'manage_kb' => 'Wissensdatenbank verwalten',
'manage_email_addresses' => 'E-Mail-Adressen verwalten',
'manage_email_templates' => 'E-Mail-Vorlagen verwalten',
'manage_organizations' => 'Organisationen verwalten',
'manage_teams' => 'Teams verwalten',
'manage_roles' => 'Rollen verwalten',
'manage_staff' => 'Mitarbeiter verwalten',
'view_audit_log' => 'Admin-Protokoll einsehen',
'manage_escalations' => 'Eskalationsregeln verwalten',
'view_time_entries' => 'Zeiterfassung einsehen',
'manage_business_hours' => 'Geschäftszeiten verwalten',
'manage_assistant' => 'Digitalen Assistenten verwalten',
];
}
// Rechte-Katalog, Standardrechte und Vorlagen: siehe includes/permissions.php (permission_catalog(), default_permissions(), admin_section_permissions())
/**
* Prüft ob ein Mitarbeiter eine bestimmte Berechtigung hat.
@@ -2274,7 +2249,8 @@ function has_permission(array $staff, string $permission, ?int $departmentId = n
$roleCache[$rid] = ($row && ($row['is_active'] ?? 1)) ? (json_decode((string) $row['permissions'], true) ?? []) : null;
}
$perms = $roleCache[$rid];
if ($perms !== null) return (bool) ($perms[$permission] ?? false);
// Fehlt ein Recht in der gespeicherten Rolle: ein altes Recht gilt wie bisher als „nein“, ein neueres mit dem Standardwert des Katalogs
if ($perms !== null) return array_key_exists($permission, $perms) ? (bool) $perms[$permission] : (!in_array($permission, permission_legacy_keys(), true) && (bool) (default_permissions('agent')[$permission] ?? false));
}
$defaults = default_permissions('agent');