Files
deskhub/DeskHub-Webseite/api/staff/v1/ticket_rules.php
T
2026-11-05 21:21:31 +00:00

178 lines
9.2 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/staff/v1/ticket_rules.php
*
* Mobile-Pendant zu staff/ticket_rules.php – gleiche Tabellen (ticket_rules,
* ticket_rule_conditions, ticket_rule_actions), nur als JSON-API statt HTML-Formular.
* Nur für echte Admins (wie im Web-Backend, kein granulares Recht dafür).
*/
api_require_method('GET', 'POST');
$staff = api_require_staff();
if (!has_permission($staff, 'manage_automation')) {
api_error(403, 'FORBIDDEN', 'Für Ticket-Regeln fehlt die erforderliche Berechtigung.');
}
$pdo = get_db();
const RULE_FIELDS = ['subject' => 'Betreff', 'message' => 'Nachricht', 'requester_email' => 'Absender-E-Mail', 'requester_name' => 'Absender-Name', 'source' => 'Kanal (web/email)'];
const RULE_OPERATORS = ['contains' => 'enthält', 'not_contains' => 'enthält nicht', 'equals' => 'ist genau', 'starts_with' => 'beginnt mit', 'ends_with' => 'endet mit'];
const RULE_ACTIONS = ['set_department' => 'Abteilung setzen', 'set_priority' => 'Priorität setzen', 'set_help_topic' => 'Hilfe-Thema setzen', 'assign_staff' => 'Mitarbeiter zuweisen', 'add_tag' => 'Tag hinzufügen', 'auto_reply' => 'Auto-Antwort senden', 'close' => 'Ticket schließen', 'reject' => 'Ticket verwerfen (z.B. Spam)'];
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
$departments = departments_for_select($pdo, array_map('intval', $pdo->query("SELECT action_value FROM ticket_rule_actions WHERE action_type = 'set_department'")->fetchAll(PDO::FETCH_COLUMN)));
$priorities = $pdo->query('SELECT id, name FROM priorities ORDER BY sort_order')->fetchAll();
$helpTopics = $pdo->query('SELECT id, name FROM help_topics ORDER BY name')->fetchAll();
$staffList = $pdo->query("SELECT id, name FROM staff WHERE is_active = 1 ORDER BY name")->fetchAll();
$cannedResponses = $pdo->query('SELECT id, title FROM canned_responses ORDER BY title')->fetchAll();
$deptById = array_column($departments, 'name', 'id');
$prioById = array_column($priorities, 'name', 'id');
$topicById = array_column($helpTopics, 'name', 'id');
$staffById = array_column($staffList, 'name', 'id');
$cannedById = array_column($cannedResponses, 'title', 'id');
$rules = $pdo->query('SELECT * FROM ticket_rules ORDER BY sort_order, id')->fetchAll();
$condStmt = $pdo->prepare('SELECT field, operator, value FROM ticket_rule_conditions WHERE rule_id = ? ORDER BY id');
$actStmt = $pdo->prepare('SELECT action_type, action_value FROM ticket_rule_actions WHERE rule_id = ? ORDER BY id');
$rulesOut = [];
foreach ($rules as $r) {
$condStmt->execute([$r['id']]);
$actStmt->execute([$r['id']]);
$actionsOut = [];
foreach ($actStmt->fetchAll() as $a) {
$type = $a['action_type'];
$value = $a['action_value'];
$label = match ($type) {
'set_department' => $deptById[$value] ?? null,
'set_priority' => $prioById[$value] ?? null,
'set_help_topic' => $topicById[$value] ?? null,
'assign_staff' => $staffById[$value] ?? null,
'auto_reply' => $cannedById[$value] ?? null,
'add_tag' => $value,
'reject', 'close' => null,
default => null,
};
$actionsOut[] = ['type' => $type, 'value' => $value, 'valueLabel' => $label];
}
$rulesOut[] = [
'id' => (int) $r['id'],
'name' => $r['name'],
'matchType' => $r['match_type'],
'isActive' => (bool) $r['is_active'],
'sortOrder' => (int) $r['sort_order'],
'conditions' => $condStmt->fetchAll(),
'actions' => $actionsOut,
];
}
api_json([
'fieldOptions' => array_map(fn($v, $l) => ['value' => $v, 'label' => $l], array_keys(RULE_FIELDS), RULE_FIELDS),
'operatorOptions' => array_map(fn($v, $l) => ['value' => $v, 'label' => $l], array_keys(RULE_OPERATORS), RULE_OPERATORS),
'actionOptions' => array_map(fn($v, $l) => ['value' => $v, 'label' => $l], array_keys(RULE_ACTIONS), RULE_ACTIONS),
'departments' => array_map(fn(array $d) => ['id' => (int) $d['id'], 'name' => $d['name']], $departments),
'priorities' => array_map(fn(array $p) => ['id' => (int) $p['id'], 'name' => $p['name']], $priorities),
'helpTopics' => array_map(fn(array $t) => ['id' => (int) $t['id'], 'name' => $t['name']], $helpTopics),
'staffOptions' => array_map(fn(array $s) => ['id' => (int) $s['id'], 'name' => $s['name']], $staffList),
'cannedResponses' => array_map(fn(array $c) => ['id' => (int) $c['id'], 'name' => $c['title']], $cannedResponses),
'rules' => $rulesOut,
'autoCloseEnabled' => get_setting('auto_close_enabled', '0') === '1',
'autoCloseDays' => (int) get_setting('auto_close_days', '7'),
]);
}
$body = api_request_body();
$action = $body['action'] ?? '';
if ($action === 'toggle') {
$id = (int) ($body['id'] ?? 0);
$pdo->prepare('UPDATE ticket_rules SET is_active = 1 - is_active WHERE id = ?')->execute([$id]);
api_json(['ok' => true]);
}
if ($action === 'save_auto_close') {
$enabled = !empty($body['enabled']) ? '1' : '0';
$days = max(1, min(90, (int) ($body['days'] ?? 7)));
set_setting('auto_close_enabled', $enabled);
set_setting('auto_close_days', (string) $days);
audit_log($staff['id'], 'Automatisches Schließen geändert (Mobile App)');
api_json(['ok' => true]);
}
if ($action === 'delete') {
$id = (int) ($body['id'] ?? 0);
$pdo->prepare('DELETE FROM ticket_rule_conditions WHERE rule_id = ?')->execute([$id]);
$pdo->prepare('DELETE FROM ticket_rule_actions WHERE rule_id = ?')->execute([$id]);
$pdo->prepare('DELETE FROM ticket_rules WHERE id = ?')->execute([$id]);
audit_log($staff['id'], "Ticket-Regel gelöscht: #$id (Mobile App)");
api_json(['ok' => true]);
}
if ($action === 'save') {
$id = (int) ($body['id'] ?? 0);
$name = trim((string) ($body['name'] ?? ''));
$matchType = ($body['matchType'] ?? 'all') === 'any' ? 'any' : 'all';
$isActive = !empty($body['isActive']) ? 1 : 0;
$sortOrder = (int) ($body['sortOrder'] ?? 0);
$rawConditions = is_array($body['conditions'] ?? null) ? $body['conditions'] : [];
$rawActions = is_array($body['actions'] ?? null) ? $body['actions'] : [];
if ($name === '') api_error(422, 'NAME_REQUIRED', 'Bitte einen Namen angeben.');
$conditions = [];
foreach ($rawConditions as $c) {
$field = (string) ($c['field'] ?? '');
$operator = (string) ($c['operator'] ?? '');
$value = trim((string) ($c['value'] ?? ''));
if ($value === '' || !isset(RULE_FIELDS[$field]) || !isset(RULE_OPERATORS[$operator])) continue;
$conditions[] = ['field' => $field, 'operator' => $operator, 'value' => $value];
}
if (!$conditions) api_error(422, 'CONDITIONS_REQUIRED', 'Bitte mindestens eine Bedingung mit einem Wert angeben.');
$actionsToSave = [];
foreach ($rawActions as $a) {
$type = (string) ($a['type'] ?? '');
if (!isset(RULE_ACTIONS[$type])) continue;
$noValue = in_array($type, ['reject', 'close'], true);
$value = $noValue ? '1' : trim((string) ($a['value'] ?? ''));
if (!$noValue && $value === '') continue;
$actionsToSave[] = ['type' => $type, 'value' => $value];
}
if (!$actionsToSave) api_error(422, 'ACTIONS_REQUIRED', 'Bitte mindestens eine Aktion festlegen.');
if ($id) {
$existsStmt = $pdo->prepare('SELECT COUNT(*) FROM ticket_rules WHERE id = ?');
$existsStmt->execute([$id]);
if (!(bool) $existsStmt->fetchColumn()) api_error(404, 'RULE_NOT_FOUND', 'Die Regel wurde nicht gefunden.');
$pdo->prepare('UPDATE ticket_rules SET name=?, match_type=?, is_active=?, sort_order=? WHERE id=?')
->execute([$name, $matchType, $isActive, $sortOrder, $id]);
$pdo->prepare('DELETE FROM ticket_rule_conditions WHERE rule_id = ?')->execute([$id]);
$pdo->prepare('DELETE FROM ticket_rule_actions WHERE rule_id = ?')->execute([$id]);
audit_log($staff['id'], "Ticket-Regel aktualisiert: $name (Mobile App)");
$ruleId = $id;
} else {
$pdo->prepare('INSERT INTO ticket_rules (name, match_type, is_active, sort_order, created_at) VALUES (?,?,?,?,?)')
->execute([$name, $matchType, $isActive, $sortOrder, db_now()]);
$ruleId = (int) $pdo->lastInsertId();
audit_log($staff['id'], "Ticket-Regel erstellt: $name (Mobile App)");
}
$condStmt = $pdo->prepare('INSERT INTO ticket_rule_conditions (rule_id, field, operator, value) VALUES (?,?,?,?)');
foreach ($conditions as $c) {
$condStmt->execute([$ruleId, $c['field'], $c['operator'], $c['value']]);
}
$actStmt = $pdo->prepare('INSERT INTO ticket_rule_actions (rule_id, action_type, action_value) VALUES (?,?,?)');
foreach ($actionsToSave as $a) {
$actStmt->execute([$ruleId, $a['type'], $a['value']]);
}
api_json(['id' => $ruleId]);
}
api_error(422, 'INVALID_ACTION', 'Unbekannte Aktion.');