178 lines
9.2 KiB
PHP
178 lines
9.2 KiB
PHP
<?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.');
|