Lisää Zammad-integraatio ja modulaarinen integraatiot-hallinta

- Uusi integrations-taulu tietokantaan (moduulimalli: type, enabled, config)
- ZammadClient-luokka: tiketit, artikkelit, vastaukset, ryhmät
- API-endpointit: integration_save, integration_test, zammad_sync, zammad_reply, zammad_groups
- Synkronointi: Zammad-tiketit → intran tiketit, artikkelit → viestit
- Vastaukset: Zammad-tiketteihin vastaus kulkee Zammad API:n kautta (→ O365)
- UI: Integraatiot-osio API-välilehdellä, toggle-kytkimet, Zammad-konfiguraatio
- tickets.zammad_ticket_id ja ticket_messages.zammad_article_id linkitys

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-12 19:25:51 +02:00
parent 1aea4bde20
commit fa8aaed11e
5 changed files with 645 additions and 12 deletions

54
db.php
View File

@@ -602,6 +602,19 @@ function initDatabase(): void {
FOREIGN KEY (laitetila_id) REFERENCES laitetilat(id) ON DELETE CASCADE,
INDEX idx_laitetila (laitetila_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4",
"CREATE TABLE IF NOT EXISTS integrations (
id VARCHAR(20) PRIMARY KEY,
company_id VARCHAR(50) NOT NULL,
type VARCHAR(50) NOT NULL,
enabled BOOLEAN DEFAULT FALSE,
config JSON,
created DATETIME,
updated DATETIME,
FOREIGN KEY (company_id) REFERENCES companies(id) ON DELETE CASCADE,
UNIQUE KEY uk_company_type (company_id, type),
INDEX idx_company (company_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4",
];
foreach ($tables as $i => $sql) {
@@ -644,6 +657,8 @@ function initDatabase(): void {
"ALTER TABLE ticket_rules ADD COLUMN enabled BOOLEAN DEFAULT TRUE AFTER auto_close_days",
"ALTER TABLE ticket_rules ADD COLUMN set_priority VARCHAR(20) DEFAULT '' AFTER type_set",
"ALTER TABLE ticket_rules ADD COLUMN set_tags VARCHAR(255) DEFAULT '' AFTER set_priority",
"ALTER TABLE tickets ADD COLUMN zammad_ticket_id INT DEFAULT NULL AFTER mailbox_id",
"ALTER TABLE ticket_messages ADD COLUMN zammad_article_id INT DEFAULT NULL AFTER message_id",
];
foreach ($alters as $sql) {
try { $db->query($sql); } catch (\Throwable $e) { /* sarake on jo olemassa / jo ajettu */ }
@@ -2223,3 +2238,42 @@ function dbUpdateConnection(int $connectionId, array $data): void {
$connectionId
]);
}
// ==================== INTEGRAATIOT ====================
function dbLoadIntegrations(string $companyId): array {
return _dbFetchAll("SELECT * FROM integrations WHERE company_id = ? ORDER BY type", [$companyId]);
}
function dbGetIntegration(string $companyId, string $type): ?array {
$row = _dbFetchRow("SELECT * FROM integrations WHERE company_id = ? AND type = ?", [$companyId, $type]);
if ($row && $row['config']) {
$row['config'] = json_decode($row['config'], true) ?: [];
}
return $row;
}
function dbSaveIntegration(string $companyId, string $type, bool $enabled, array $config): void {
$existing = _dbFetchRow("SELECT id FROM integrations WHERE company_id = ? AND type = ?", [$companyId, $type]);
$now = date('Y-m-d H:i:s');
if ($existing) {
_dbExecute(
"UPDATE integrations SET enabled = ?, config = ?, updated = ? WHERE company_id = ? AND type = ?",
[$enabled ? 1 : 0, json_encode($config), $now, $companyId, $type]
);
} else {
$id = substr(uniqid(), -8) . bin2hex(random_bytes(2));
_dbExecute(
"INSERT INTO integrations (id, company_id, type, enabled, config, created, updated) VALUES (?, ?, ?, ?, ?, ?, ?)",
[$id, $companyId, $type, $enabled ? 1 : 0, json_encode($config), $now, $now]
);
}
}
function dbGetTicketByZammadId(string $companyId, int $zammadId): ?array {
return _dbFetchRow("SELECT * FROM tickets WHERE company_id = ? AND zammad_ticket_id = ?", [$companyId, $zammadId]);
}
function dbGetMessageByZammadArticleId(string $ticketId, int $articleId): ?array {
return _dbFetchRow("SELECT * FROM ticket_messages WHERE ticket_id = ? AND zammad_article_id = ?", [$ticketId, $articleId]);
}