From 5e928b982bc3dade4b5bed4096679b91c167b2db Mon Sep 17 00:00:00 2001 From: Jan Usenko Date: Mon, 1 Jun 2026 14:18:35 +0200 Subject: [PATCH] chore von Jan I guess? --- module/sitemap | 1 + module/ticketcenter/CLAUDE.md | 81 + .../ticketcenter/show_ticketcenter.inc.php | 1390 ++++++++++------- 3 files changed, 864 insertions(+), 608 deletions(-) create mode 160000 module/sitemap create mode 100644 module/ticketcenter/CLAUDE.md diff --git a/module/sitemap b/module/sitemap new file mode 160000 index 0000000..2511d8f --- /dev/null +++ b/module/sitemap @@ -0,0 +1 @@ +Subproject commit 2511d8f82ed15071f7acd00d43faf259f0056b1c diff --git a/module/ticketcenter/CLAUDE.md b/module/ticketcenter/CLAUDE.md new file mode 100644 index 0000000..36a8fdd --- /dev/null +++ b/module/ticketcenter/CLAUDE.md @@ -0,0 +1,81 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +This is the **Ticket Center** module — a ticket management/support system — for the AWO Hamburg intranet. It is a traditional PHP application with no build system: files are served directly. + +## No Build System + +There is no `package.json`, `Makefile`, or test framework. No compilation or build step is needed. CSS/JS files are served as-is with `filemtime()`-based cache busting. + +## Architecture + +### Module Structure + +This module lives inside a larger **mysyde** platform. It depends on: +- Common platform functions (e.g. `encryptId`/`decryptId`, translation registry, permission helpers) +- Shared DB connection config at `mysyde/dc.config.php` (dev) or `mysyde/dc-server.config.php` (prod) +- Environment variables via `vlucas/phpdotenv` loaded from `/config/.env` and `/vendor/autoload.php` + +### Request Flow + +1. URL: `/module/ticketcenter/` +2. Main controller: `views/ticketcenter/show_ticketcenter.inc.php` — handles all ticket CRUD, state transitions, file uploads, and page routing (956 lines) +3. List view: `views/ticketcenter/show_ticketcenter_listform.inc.php` — renders ticket table via Grid.js +4. Detail/edit view: `views/ticketcenter/show_ticketcenter_cardform.inc.php` +5. Create view: `views/ticketcenter/show_ticketcenter_create_cardform.inc.php` +6. JSON API: `services/ticketcenter.api.php` — POST endpoint used by Grid.js for ticket list data +7. AJAX endpoints: `views/ticketcenter/show_ticketcenter_ajax.php` (category hierarchy), `show_ticketcenter_category_desc_ajax.php` + +### Admin Sub-views + +- `views/category/` — hierarchical category CRUD +- `views/process/` — ticket workflow process definitions +- `views/status/` — ticket status definitions +- `views/settings/` — system settings UI + +### Frontend Stack + +- **Grid.js** (`js/gridjs.umd.js`) — data table with server-side pagination/filtering via the JSON API +- **Select2** — enhanced dropdowns (loaded via CDN) +- **Semantic UI** — UI components (loaded via CDN) +- **FontAwesome** — icons (CDN) +- Vanilla JS + jQuery; no bundler or transpiler + +### Database Tables + +| Table | Purpose | +|---|---| +| `main_tickets` | Core ticket records | +| `main_tickets_messages` | Ticket conversation/comments | +| `main_tickets_category` | Hierarchical category definitions | +| `main_tickets_status` | Status definitions | +| `main_tickets_process` | Workflow process definitions | +| `main_tickets_notification` | Notification tracking | +| `main_contact` / `main_contact_department` | User/department info | +| `main_tickets_category_{mandant,department,contact}` | Visibility/access control per category | + +### Ticket Lifecycle + +Tickets move through status IDs: **Draft → 2 (Open) → 3 (In Progress) → 4 (Closed)** + +### List View Filters (cues) + +The `cue` parameter drives 8 ticket views: created by me, assigned to me, pool, archive, all, escalated, unassigned, insurance claims. + +### File Uploads + +Attachments are stored at `userdata/Tickets/{ticket_id}/`. + +## Language + +All UI text is in **German**. Button labels, validation messages, and field names are German throughout. + +## Key Conventions + +- PHP templating via `include`/`require` — no MVC framework +- Both MySQLi and PDO are used in different files +- IDs are encrypted/decrypted before being exposed to the frontend (`encryptId`/`decryptId`) +- Role-based access is enforced via mandant/department/contact associations on categories diff --git a/module/ticketcenter/views/ticketcenter/show_ticketcenter.inc.php b/module/ticketcenter/views/ticketcenter/show_ticketcenter.inc.php index 87fdaa6..66e6636 100644 --- a/module/ticketcenter/views/ticketcenter/show_ticketcenter.inc.php +++ b/module/ticketcenter/views/ticketcenter/show_ticketcenter.inc.php @@ -26,9 +26,9 @@ ini_set('display_startup_errors', 1); window.location.href='/intranet/de/ticketcenter/?action=edit&id=" . $encId . "';"; } @@ -66,16 +72,16 @@ switch ($_GET['action']) { save_new_ticket_individual_state(); save_category(true); if (isset($_POST['input_id']) && is_numeric($_POST['input_id'])) { + $ticket_id = (int)$_POST['input_id']; $insurance_claim = isset($_POST['input_insurance_case']) ? 1 : 0; $stmt = mysqli_prepare($GLOBALS['mysql_con'], "UPDATE main_tickets SET insurance_claim = ? WHERE id = ?"); if ($stmt) { - $input_id = (int)$_POST['input_id']; - mysqli_stmt_bind_param($stmt, "ii", $insurance_claim, $input_id); + mysqli_stmt_bind_param($stmt, "ii", $insurance_claim, $ticket_id); mysqli_stmt_execute($stmt); mysqli_stmt_close($stmt); } - } - if (isset($_POST['input_id']) && is_numeric($_POST['input_id'])) { + $recipients = get_ticket_direct_recipients($ticket_id); + make_notification($recipients, 'Ihr Ticket wurde aktualisiert', build_ticket_notification_email($ticket_id, 'Ihr Ticket wurde aktualisiert')); $encId = encryptId($_POST['input_id']); echo ""; } @@ -85,12 +91,19 @@ switch ($_GET['action']) { break; } +// ============================================================================= +// TICKET CRUD +// Kernoperationen: Anzeigen, Erstellen, Aktualisieren, Schließen, Löschen. +// ============================================================================= + +// Lädt ein Ticket aus der DB und zeigt die Detailansicht (cardform) an. +// Markiert dabei alle Benachrichtigungen des aktuellen Nutzers als gelesen. function edit_ticket($_id = "") { if ((int) $_id > 0) { $input_id = $_id; } - + if ($input_id <> '') { $query = "SELECT main_tickets.*,main_tickets_category.description AS 'category_description',receiver.name as 'receiver',sender.name as 'sender',main_tickets_state.description as 'state' FROM main_tickets LEFT JOIN main_contact AS receiver ON receiver.id = main_tickets.receiver_id @@ -98,7 +111,6 @@ function edit_ticket($_id = "") LEFT JOIN main_tickets_category ON main_tickets.main_ticket_category_id = main_tickets_category.id LEFT JOIN main_tickets_state ON main_tickets.current_state = main_tickets_state.id WHERE main_tickets.id = '" . $input_id . "'"; - // $query = "SELECT * FROM main_tickets WHERE id = '".$input_id."'"; $result = @mysqli_query($GLOBALS['mysql_con'], $query); $ticket = @mysqli_fetch_array($result); } @@ -107,7 +119,8 @@ function edit_ticket($_id = "") require_once("show_ticketcenter_cardform.inc.php"); } -// Speichert einen neuen oder aktualisierten ticket in der Datenbank und leitet je nach Bedarf entweder zum Listenformular zurück oder schließt das Bearbeitungsformular. +// Erstellt ein neues Ticket (INSERT) und benachrichtigt alle Verantwortlichen der Kategorie. +// Bei vorhandener input_id wird die Funktion ohne Aktion durchlaufen (Legacy-Pfad). function save_ticket($close = FALSE) { $modified_date = new DateTime(); @@ -118,28 +131,24 @@ function save_ticket($close = FALSE) } else { $sender_id = $GLOBALS['main_contact']['id']; - // if($_POST['input_new_ticket_sender'] != ""){ - // $sender_id = $_POST['input_new_ticket_sender']; - // } $location = isset($_POST["input_location"]) ? $_POST["input_location"] : ""; $address = isset($_POST["input_address"]) ? $_POST["input_address"] : ""; $facility = isset($_POST["input_facility"]) ? $_POST["input_facility"] : ""; - // last element of array $_POST["input_category"] $category = $_POST["input_category"]; $lastCategory = end($category); $insurance_claim = $_POST["input_insurance_case"] == "on" ? 1 : 0; $query = "INSERT INTO main_tickets ( - ticket_no, - description, + ticket_no, + description, main_ticket_category_id, - service_item_no, - last_action_date, - last_opening_date, - creation_date, - individual_state, - sender_id, - cost_center, + service_item_no, + last_action_date, + last_opening_date, + creation_date, + individual_state, + sender_id, + cost_center, link, location, address, @@ -149,13 +158,13 @@ function save_ticket($close = FALSE) '" . $_POST["input_ticket_no"] . "', '" . strip_tags($_POST["input_description"]) . "', '" . $lastCategory . "', - '" . $_POST["input_service_item"] . "', + '" . $_POST["input_service_item"] . "', NOW(), NOW(), NOW(), 2, '" . $sender_id . "', - '" . $_POST["input_cost_center"] . "', + '" . $_POST["input_cost_center"] . "', '" . $_POST["input_link"] . "', '" . $location . "', '" . $address . "', @@ -166,12 +175,14 @@ function save_ticket($close = FALSE) $message = "success"; } - mysqli_query($GLOBALS['mysql_con'], $query); if ($inserted == TRUE) { $input_id = mysqli_insert_id($GLOBALS['mysql_con']); + $recipients = get_ticket_recipients($input_id); + $subject = 'Neues Ticket | ' . $_POST['input_ticket_no']; + make_notification($recipients, $subject, build_ticket_notification_email($input_id, 'Ein neues Ticket wurde erstellt')); $query_insert_message = "INSERT INTO main_tickets_messages(message, content, datetime, main_tickets_id, main_contact_id, type) VALUES( 'Eröffnet von', @@ -218,9 +229,7 @@ function save_ticket($close = FALSE) '" . $type . "' )"; - // var_dump($query_insert_message);die(); @mysqli_query($GLOBALS['mysql_con'], $query_insert_message); - } if ($close == TRUE) { @@ -229,22 +238,14 @@ function save_ticket($close = FALSE) echo " - "; -} - -function save_new_ticket_receiver() -{ - // Überprüfe, ob die POST-Daten vorhanden sind - if (isset($_POST["input_id"]) && isset($_POST["input_new_ticket_receiver"])) { - // Hole die POST-Daten und bereinige sie mit mysqli_real_escape_string - $input_id = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST["input_id"]); - $new_receiver_id = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST["input_new_ticket_receiver"]); - - // Überprüfe, ob die IDs gültig sind (keine leeren Werte oder ungültigen Zeichen) - if (!empty($input_id) && !empty($new_receiver_id) && is_numeric($input_id) && is_numeric($new_receiver_id)) { - // Aktuellen Empfänger laden - $qryCurrent = "SELECT receiver_id FROM main_tickets WHERE id = " . (int)$input_id . " LIMIT 1"; - $resCurrent = @mysqli_query($GLOBALS['mysql_con'], $qryCurrent); - $rowCurrent = $resCurrent ? @mysqli_fetch_assoc($resCurrent) : null; - $current_receiver_id = $rowCurrent ? (string)$rowCurrent['receiver_id'] : null; - // Nur wenn sich der Empfänger geändert hat, speichern und in die History schreiben - if ($current_receiver_id !== (string)$new_receiver_id) { - // Baue die Abfrage mit einem Prepared Statement - $query = "UPDATE main_tickets SET receiver_id = ? WHERE id = ?"; - $stmt = mysqli_prepare($GLOBALS['mysql_con'], $query); - - if ($stmt) { - // Binde die Parameter (int, int) und führe die Abfrage aus - mysqli_stmt_bind_param($stmt, "ii", $new_receiver_id, $input_id); - mysqli_stmt_execute($stmt); - - // History-Eintrag nur bei Änderung - $query_insert_message = "INSERT INTO main_tickets_messages(message, content, datetime, main_tickets_id, main_contact_id, type) VALUES( - 'Weitergeleitet an', - '', - NOW(), - '" . $input_id . "', - '" . $new_receiver_id . "', - 'history' - )"; - @mysqli_query($GLOBALS['mysql_con'], $query_insert_message); - - // Benachrichtigung nur bei Änderung - update_notification($input_id, 'state', "Der Status Bearbeiter wurde Ihnen im Ticket zugeteilt"); - - mysqli_stmt_close($stmt); - } - } - } - } - // edit_ticket($_POST["input_id"]); -} - -function save_new_due_date() { - // Überprüfe, ob die POST-Daten vorhanden sind - if (isset($_POST["input_id"]) && isset($_POST["input_due_date"])) { - // Hole die POST-Daten und bereinige sie mit mysqli_real_escape_string - $input_id = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST["input_id"]); - $new_due_date= mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST["input_due_date"]); - - // Überprüfe, ob die Werte gültig sind - if (!empty($input_id) && $new_due_date !== '') { - // Aktuelles Fälligkeitsdatum laden - $qryCurrent = "SELECT due_date FROM main_tickets WHERE id = " . (int)$input_id . " LIMIT 1"; - $resCurrent = @mysqli_query($GLOBALS['mysql_con'], $qryCurrent); - $rowCurrent = $resCurrent ? @mysqli_fetch_assoc($resCurrent) : null; - $current_due_date = $rowCurrent ? (string)$rowCurrent['due_date'] : null; - - // Nur bei Änderung updaten - if ($current_due_date !== (string)$new_due_date) { - // Baue die Abfrage mit einem Prepared Statement - $query = "UPDATE main_tickets SET due_date = ? WHERE id = ?"; - $stmt = mysqli_prepare($GLOBALS['mysql_con'], $query); - - if ($stmt) { - mysqli_stmt_bind_param($stmt, "si", $new_due_date, $input_id); - mysqli_stmt_execute($stmt); - mysqli_stmt_close($stmt); - } - } - } - } - // Keine History hier; nur ändern, wenn sich der Wert unterscheidet -} - -function save_category($silent = false) -{ - // Überprüfe, ob die POST-Daten vorhanden sind - if (isset($_POST["input_id"]) && isset($_POST["input_category"])) { - // Hole die POST-Daten und bereinige sie mit mysqli_real_escape_string - $input_id = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST["input_id"]); - $category = $_POST["input_category"]; - $lastCategory = end($category); - - // Aktuelle Werte laden - $qryCurrent = "SELECT main_ticket_category_id FROM main_tickets WHERE id = " . (int)$input_id . " LIMIT 1"; - $resCurrent = @mysqli_query($GLOBALS['mysql_con'], $qryCurrent); - $rowCurrent = $resCurrent ? @mysqli_fetch_assoc($resCurrent) : null; - - $changed = false; - if ($rowCurrent) { - $cur_cat = (string)$rowCurrent['main_ticket_category_id']; - if ($cur_cat !== (string)$lastCategory) { - $changed = true; - } - } else { - $changed = true; // Ticket nicht gefunden -> versuche zu speichern - } - - if ($changed) { - $query = "UPDATE main_tickets SET main_ticket_category_id = ? WHERE id = ?"; - $stmt = mysqli_prepare($GLOBALS['mysql_con'], $query); - if ($stmt) { - mysqli_stmt_bind_param($stmt, "ii", $lastCategory, $input_id); - mysqli_stmt_execute($stmt); - mysqli_stmt_close($stmt); - } - - // History-Eintrag - $query_insert_message = "INSERT INTO main_tickets_messages(message, content, datetime, main_tickets_id, main_contact_id, type) VALUES( - 'Kategorie geändert', - '', - NOW(), - '" . $input_id . "', - '" . $GLOBALS["main_contact"]['id'] . "', - 'history' - )"; - @mysqli_query($GLOBALS['mysql_con'], $query_insert_message); - - // Benachrichtigung bei Änderung - update_notification($input_id, 'state'); - } - } - if (!$silent && isset($_POST['input_id'])) { - edit_ticket($_POST["input_id"]); - } -} - -function save_new_ticket_individual_state($silent = false) -{ - // Überprüfe, ob die POST-Daten vorhanden sind - if (isset($_POST["input_id"]) && isset($_POST["input_main_tickets_status"])) { - // Hole die POST-Daten und bereinige sie mit mysqli_real_escape_string - $input_id = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST["input_id"]); - $input_main_tickets_status = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST["input_main_tickets_status"]); - - // Überprüfe, ob die IDs gültig sind (keine leeren Werte oder ungültigen Zeichen) - if (!empty($input_id) && !empty($input_main_tickets_status) && is_numeric($input_id) && is_numeric($input_main_tickets_status)) { - // Aktuellen Status laden - $qryCurrent = "SELECT individual_state FROM main_tickets WHERE id = " . (int)$input_id . " LIMIT 1"; - $resCurrent = @mysqli_query($GLOBALS['mysql_con'], $qryCurrent); - $rowCurrent = $resCurrent ? @mysqli_fetch_assoc($resCurrent) : null; - $current_status = $rowCurrent ? (string)$rowCurrent['individual_state'] : null; - - // Nur bei Änderung speichern und in die History schreiben - if ($current_status !== (string)$input_main_tickets_status) { - // Baue die Abfrage mit einem Prepared Statement - $query = "UPDATE main_tickets SET individual_state = ? WHERE id = ?"; - $stmt = mysqli_prepare($GLOBALS['mysql_con'], $query); - - if ($stmt) { - // Binde die Parameter (int, int) und führe die Abfrage aus - mysqli_stmt_bind_param($stmt, "ii", $input_main_tickets_status, $input_id); - mysqli_stmt_execute($stmt); - mysqli_stmt_close($stmt); - } - - // Statusbeschreibung holen und History schreiben - $queryDesc = "SELECT description FROM main_tickets_status WHERE id = " . (int)$input_main_tickets_status; - $result = @mysqli_query($GLOBALS['mysql_con'], $queryDesc); - if ($row = @mysqli_fetch_array($result)) { - $status_description = $row['description']; - $query_insert_message = "INSERT INTO main_tickets_messages(message, content, datetime, main_tickets_id, main_contact_id, type) VALUES( - 'Status geändert zu $status_description', - '', - NOW(), - '" . $input_id . "', - '" . $GLOBALS["main_contact"]['id'] . "', - 'history' - )"; - @mysqli_query($GLOBALS['mysql_con'], $query_insert_message); - } - - // Benachrichtigung nur bei Änderung - update_notification($input_id, 'state'); - } - } - } - if (!$silent && isset($_POST["input_id"])) { - edit_ticket($_POST["input_id"]); - } - - -} - -function save_ticket_supplieres() -{ - // Überprüfe, ob die erforderlichen POST-Daten vorhanden sind - if (isset($_POST["input_id"]) && isset($_POST["supplier_ids"])) { - // Bereinige die POST-Daten - $input_id = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST["input_id"]); - $supplier_ids_str = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST["supplier_ids"]); - - // Überprüfe, ob die Ticket-ID gültig ist - if (!empty($input_id) && is_numeric($input_id)) { - // 1. Lösche alle bestehenden Lieferantenverknüpfungen für dieses Ticket - $deleteQuery = "DELETE FROM main_ticket_suplier WHERE main_ticket_id = ?"; - $stmt = mysqli_prepare($GLOBALS['mysql_con'], $deleteQuery); - if ($stmt) { - mysqli_stmt_bind_param($stmt, "i", $input_id); - mysqli_stmt_execute($stmt); - mysqli_stmt_close($stmt); - } - - // 2. Füge die neuen Lieferantenverknüpfungen ein - // Der hidden Input enthält die IDs als komma-separierten String - $supplier_ids = array_filter(explode(',', $supplier_ids_str), function ($value) { - return is_numeric($value) && !empty($value); - }); - if (!empty($supplier_ids)) { - $insertQuery = "INSERT INTO main_ticket_suplier (main_ticket_id, main_ticket_suplier_id) VALUES (?, ?)"; - $stmt = mysqli_prepare($GLOBALS['mysql_con'], $insertQuery); - if ($stmt) { - foreach ($supplier_ids as $supplier_id) { - mysqli_stmt_bind_param($stmt, "ii", $input_id, $supplier_id); - mysqli_stmt_execute($stmt); - } - mysqli_stmt_close($stmt); - } - } - } - } - edit_ticket($_POST["input_id"]); -} - -function load_messages($ticket_id) -{ - $query = "SELECT main_tickets_messages.*, main_contact.name FROM main_tickets_messages LEFT JOIN main_contact ON main_contact.id = main_tickets_messages.main_contact_id WHERE main_tickets_id = $ticket_id ORDER BY datetime"; - $result = @mysqli_query($GLOBALS['mysql_con'], $query); - - while ($row = @mysqli_fetch_array($result)) { - $date = new DateTime($row["datetime"]); - $formatted_date = $date->format('d.m.Y H:i'); - - $outbound = ($GLOBALS['main_contact']['id'] == $row["main_contact_id"]) ? "outbound" : ""; - if ($row['type'] == 'history') { - echo "

" . $row['message'] . " " . $row['name'] . " am " . $formatted_date . " Uhr

"; - } else { - ?> -
- - Ticket Image - - " . $row["content"] . " - "; - break; - case 'file': - if (preg_match('/\.pdf$/i', $row["content"] )) { - $content = "" . $row["content"] . ""; - } else { - $content = "" . $row["content"] . ""; - } - - break; - } - ?> -

-

-

schrieb am Uhr

-
- + $(document).ready(function() { + window.location.href = '/intranet/de/ticketcenter/?cue=4000'; + }); + + "; +} + +// ============================================================================= +// TEILSPEICHERUNGEN +// Einzelne Felder des Tickets speichern — werden aus den Router-Cases aufgerufen. +// Jede Funktion prüft ob sich der Wert geändert hat, bevor sie schreibt. +// ============================================================================= + +// Speichert einen neuen Bearbeiter (receiver_id). Schreibt nur bei Änderung einen History-Eintrag. +function save_new_ticket_receiver() +{ + if (isset($_POST["input_id"]) && isset($_POST["input_new_ticket_receiver"])) { + $input_id = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST["input_id"]); + $new_receiver_id = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST["input_new_ticket_receiver"]); + + if (!empty($input_id) && !empty($new_receiver_id) && is_numeric($input_id) && is_numeric($new_receiver_id)) { + $qryCurrent = "SELECT receiver_id FROM main_tickets WHERE id = " . (int)$input_id . " LIMIT 1"; + $resCurrent = @mysqli_query($GLOBALS['mysql_con'], $qryCurrent); + $rowCurrent = $resCurrent ? @mysqli_fetch_assoc($resCurrent) : null; + $current_receiver_id = $rowCurrent ? (string)$rowCurrent['receiver_id'] : null; + + if ($current_receiver_id !== (string)$new_receiver_id) { + $query = "UPDATE main_tickets SET receiver_id = ? WHERE id = ?"; + $stmt = mysqli_prepare($GLOBALS['mysql_con'], $query); + + if ($stmt) { + mysqli_stmt_bind_param($stmt, "ii", $new_receiver_id, $input_id); + mysqli_stmt_execute($stmt); + + $query_insert_message = "INSERT INTO main_tickets_messages(message, content, datetime, main_tickets_id, main_contact_id, type) VALUES( + 'Weitergeleitet an', + '', + NOW(), + '" . $input_id . "', + '" . $new_receiver_id . "', + 'history' + )"; + @mysqli_query($GLOBALS['mysql_con'], $query_insert_message); + + mysqli_stmt_close($stmt); + } + } + } + } +} + +// Speichert das Fälligkeitsdatum (due_date). Schreibt nur bei Änderung. +function save_new_due_date() { + if (isset($_POST["input_id"]) && isset($_POST["input_due_date"])) { + $input_id = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST["input_id"]); + $new_due_date= mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST["input_due_date"]); + + if (!empty($input_id) && $new_due_date !== '') { + $qryCurrent = "SELECT due_date FROM main_tickets WHERE id = " . (int)$input_id . " LIMIT 1"; + $resCurrent = @mysqli_query($GLOBALS['mysql_con'], $qryCurrent); + $rowCurrent = $resCurrent ? @mysqli_fetch_assoc($resCurrent) : null; + $current_due_date = $rowCurrent ? (string)$rowCurrent['due_date'] : null; + + if ($current_due_date !== (string)$new_due_date) { + $query = "UPDATE main_tickets SET due_date = ? WHERE id = ?"; + $stmt = mysqli_prepare($GLOBALS['mysql_con'], $query); + + if ($stmt) { + mysqli_stmt_bind_param($stmt, "si", $new_due_date, $input_id); + mysqli_stmt_execute($stmt); + mysqli_stmt_close($stmt); + } + } + } + } +} + +// Speichert die Kategorie (main_ticket_category_id). Schreibt nur bei Änderung einen History-Eintrag. +// Mit $silent = true wird kein Redirect zur Detailansicht ausgelöst. +function save_category($silent = false) +{ + if (isset($_POST["input_id"]) && isset($_POST["input_category"])) { + $input_id = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST["input_id"]); + $category = $_POST["input_category"]; + $lastCategory = end($category); + + $qryCurrent = "SELECT main_ticket_category_id FROM main_tickets WHERE id = " . (int)$input_id . " LIMIT 1"; + $resCurrent = @mysqli_query($GLOBALS['mysql_con'], $qryCurrent); + $rowCurrent = $resCurrent ? @mysqli_fetch_assoc($resCurrent) : null; + + $changed = false; + if ($rowCurrent) { + $cur_cat = (string)$rowCurrent['main_ticket_category_id']; + if ($cur_cat !== (string)$lastCategory) { + $changed = true; + } + } else { + $changed = true; + } + + if ($changed) { + $query = "UPDATE main_tickets SET main_ticket_category_id = ? WHERE id = ?"; + $stmt = mysqli_prepare($GLOBALS['mysql_con'], $query); + if ($stmt) { + mysqli_stmt_bind_param($stmt, "ii", $lastCategory, $input_id); + mysqli_stmt_execute($stmt); + mysqli_stmt_close($stmt); + } + + $query_insert_message = "INSERT INTO main_tickets_messages(message, content, datetime, main_tickets_id, main_contact_id, type) VALUES( + 'Kategorie geändert', + '', + NOW(), + '" . $input_id . "', + '" . $GLOBALS["main_contact"]['id'] . "', + 'history' + )"; + @mysqli_query($GLOBALS['mysql_con'], $query_insert_message); + } + } + if (!$silent && isset($_POST['input_id'])) { + edit_ticket($_POST["input_id"]); + } +} + +// Speichert den individuellen Status (individual_state). Schreibt nur bei Änderung +// einen History-Eintrag mit der Statusbezeichnung. +// Mit $silent = true wird kein Redirect zur Detailansicht ausgelöst. +function save_new_ticket_individual_state($silent = false) +{ + if (isset($_POST["input_id"]) && isset($_POST["input_main_tickets_status"])) { + $input_id = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST["input_id"]); + $input_main_tickets_status = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST["input_main_tickets_status"]); + + if (!empty($input_id) && !empty($input_main_tickets_status) && is_numeric($input_id) && is_numeric($input_main_tickets_status)) { + $qryCurrent = "SELECT individual_state FROM main_tickets WHERE id = " . (int)$input_id . " LIMIT 1"; + $resCurrent = @mysqli_query($GLOBALS['mysql_con'], $qryCurrent); + $rowCurrent = $resCurrent ? @mysqli_fetch_assoc($resCurrent) : null; + $current_status = $rowCurrent ? (string)$rowCurrent['individual_state'] : null; + + if ($current_status !== (string)$input_main_tickets_status) { + $query = "UPDATE main_tickets SET individual_state = ? WHERE id = ?"; + $stmt = mysqli_prepare($GLOBALS['mysql_con'], $query); + + if ($stmt) { + mysqli_stmt_bind_param($stmt, "ii", $input_main_tickets_status, $input_id); + mysqli_stmt_execute($stmt); + mysqli_stmt_close($stmt); + } + + $queryDesc = "SELECT description FROM main_tickets_status WHERE id = " . (int)$input_main_tickets_status; + $result = @mysqli_query($GLOBALS['mysql_con'], $queryDesc); + if ($row = @mysqli_fetch_array($result)) { + $status_description = $row['description']; + $query_insert_message = "INSERT INTO main_tickets_messages(message, content, datetime, main_tickets_id, main_contact_id, type) VALUES( + 'Status geändert zu $status_description', + '', + NOW(), + '" . $input_id . "', + '" . $GLOBALS["main_contact"]['id'] . "', + 'history' + )"; + @mysqli_query($GLOBALS['mysql_con'], $query_insert_message); + } + } + } + } + if (!$silent && isset($_POST["input_id"])) { + edit_ticket($_POST["input_id"]); + } +} + +// Ersetzt alle Lieferantenverknüpfungen eines Tickets (DELETE + INSERT). +function save_ticket_supplieres() +{ + if (isset($_POST["input_id"]) && isset($_POST["supplier_ids"])) { + $input_id = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST["input_id"]); + $supplier_ids_str = mysqli_real_escape_string($GLOBALS['mysql_con'], $_POST["supplier_ids"]); + + if (!empty($input_id) && is_numeric($input_id)) { + $deleteQuery = "DELETE FROM main_ticket_suplier WHERE main_ticket_id = ?"; + $stmt = mysqli_prepare($GLOBALS['mysql_con'], $deleteQuery); + if ($stmt) { + mysqli_stmt_bind_param($stmt, "i", $input_id); + mysqli_stmt_execute($stmt); + mysqli_stmt_close($stmt); + } + + $supplier_ids = array_filter(explode(',', $supplier_ids_str), function ($value) { + return is_numeric($value) && !empty($value); + }); + if (!empty($supplier_ids)) { + $insertQuery = "INSERT INTO main_ticket_suplier (main_ticket_id, main_ticket_suplier_id) VALUES (?, ?)"; + $stmt = mysqli_prepare($GLOBALS['mysql_con'], $insertQuery); + if ($stmt) { + foreach ($supplier_ids as $supplier_id) { + mysqli_stmt_bind_param($stmt, "ii", $input_id, $supplier_id); + mysqli_stmt_execute($stmt); + } + mysqli_stmt_close($stmt); + } + } + } + } + edit_ticket($_POST["input_id"]); +} + +// ============================================================================= +// CHAT / NACHRICHTEN +// Senden und Rendern von Ticket-Nachrichten inkl. Datei-Uploads. +// ============================================================================= + +// Speichert eine neue Nachricht (Text, Bild oder Datei) im Chat des Tickets. +// Lädt die Datei in /userdata/Tickets/{ticket_id}/ hoch, falls vorhanden. +function send_message() +{ + $ticket_id = $_POST["ticket_id"]; + $chat_message = $_POST["send_message"]; + $ticket_message_sender = $GLOBALS['main_contact']['id']; + + if (isset($_FILES['files']) && $_FILES['files']['error'] === UPLOAD_ERR_OK) { + $baseDir = realpath(__DIR__ . '/../../../../'); + $uploadDir = $baseDir . '/userdata/Tickets/' . $ticket_id . '/'; + if (!file_exists($uploadDir)) { + mkdir($uploadDir, 0777, true); + } + + $allowedMimeTypes = ['image/jpeg', 'image/png']; + $fileName = basename($_FILES['files']['name']); + $fileTmpName = $_FILES['files']['tmp_name']; + $targetPath = $uploadDir . $fileName; + $fileMimeType = mime_content_type($fileTmpName); + $content = $_FILES['files']['name']; + + if (in_array($fileMimeType, $allowedMimeTypes)) { + if (move_uploaded_file($fileTmpName, $targetPath)) { + $query_insert_message = "INSERT INTO main_tickets_messages(message, content, datetime, main_tickets_id, main_contact_id, type) VALUES( + '" . $chat_message . "', + '" . $content . "', + NOW(), + '" . $ticket_id . "', + '" . $ticket_message_sender . "', + 'image' + )"; + } + } else { + if (move_uploaded_file($fileTmpName, $targetPath)) { + $query_insert_message = "INSERT INTO main_tickets_messages(message, content, datetime, main_tickets_id, main_contact_id, type) VALUES( + '" . $chat_message . "', + '" . $content . "', + NOW(), + '" . $ticket_id . "', + '" . $ticket_message_sender . "', + 'file' + )"; + } + } + } else { + $query_insert_message = "INSERT INTO main_tickets_messages(message, content, datetime, main_tickets_id, main_contact_id, type) VALUES( + '" . $chat_message . "', + '', + NOW(), + '" . $ticket_id . "', + '" . $ticket_message_sender . "', + 'message' + )"; + } + + @mysqli_query($GLOBALS['mysql_con'], $query_insert_message); +} + +// Gibt alle Nachrichten eines Tickets als HTML aus. +// History-Einträge werden als einzeilige Statusmeldung dargestellt, +// Nachrichten mit Bild/Datei-Unterstützung als Chat-Bubble. +function load_messages($ticket_id) +{ + $query = "SELECT main_tickets_messages.*, main_contact.name FROM main_tickets_messages LEFT JOIN main_contact ON main_contact.id = main_tickets_messages.main_contact_id WHERE main_tickets_id = $ticket_id ORDER BY datetime"; + $result = @mysqli_query($GLOBALS['mysql_con'], $query); + + while ($row = @mysqli_fetch_array($result)) { + $date = new DateTime($row["datetime"]); + $formatted_date = $date->format('d.m.Y H:i'); + + $outbound = ($GLOBALS['main_contact']['id'] == $row["main_contact_id"]) ? "outbound" : ""; + if ($row['type'] == 'history') { + echo "

" . $row['message'] . " " . $row['name'] . " am " . $formatted_date . " Uhr

"; + } else { + ?> +
+ + Ticket Image + + " . $row["content"] . " + "; + break; + case 'file': + if (preg_match('/\.pdf$/i', $row["content"] )) { + $content = "" . $row["content"] . ""; + } else { + $content = "" . $row["content"] . ""; + } + break; + } + ?> +

+

+

schrieb am Uhr

+
+ $row['name'], 'email' => $row['email']]; + } + return $recipients; +} + +// Bestimmt die Empfänger einer Chat-Nachricht anhand der Absenderrolle: +// - Schreibt der Ersteller → Bearbeiter wird benachrichtigt +// - Schreibt der Bearbeiter → Ersteller wird benachrichtigt +// - Schreibt ein Dritter → beide werden benachrichtigt +// - Sind Ersteller und Bearbeiter dieselbe Person → eine E-Mail an diese Person (außer sie schreibt selbst) +function get_message_recipients(int $ticket_id): array +{ + $con = $GLOBALS['mysql_con']; + $writer_id = (int)($GLOBALS['main_contact']['id'] ?? 0); + + $res = @mysqli_query($con, "SELECT sender_id, receiver_id FROM main_tickets WHERE id = $ticket_id LIMIT 1"); + $ticket = $res ? @mysqli_fetch_assoc($res) : null; + if (!$ticket) return []; + + $sender_id = (int)$ticket['sender_id']; + $receiver_id = (int)$ticket['receiver_id']; + + if ($sender_id === $receiver_id) { + // Ersteller und Bearbeiter sind dieselbe Person + $ids = $writer_id === $sender_id ? [] : [$sender_id]; + } elseif ($writer_id === $sender_id) { + // Ersteller schreibt → Bearbeiter benachrichtigen + $ids = [$receiver_id]; + } elseif ($writer_id === $receiver_id) { + // Bearbeiter schreibt → Ersteller benachrichtigen + $ids = [$sender_id]; + } else { + // Dritter schreibt → beide benachrichtigen + $ids = [$sender_id, $receiver_id]; + } + + $ids = array_values(array_unique(array_filter($ids, fn($id) => $id > 0))); + if (empty($ids)) return []; + + $in = implode(',', $ids); + $res = @mysqli_query($con, "SELECT name, email FROM main_contact WHERE id IN ($in) AND email != '' AND email IS NOT NULL"); + + $recipients = []; + while ($row = @mysqli_fetch_assoc($res)) { + $recipients[] = ['name' => $row['name'], 'email' => $row['email']]; + } + return $recipients; +} + +// Gibt Sender und Receiver eines Tickets zurück. +// Wenn beide identisch sind, wird eine E-Mail gesendet (auch an den aktuellen Nutzer). +// Wenn sie verschieden sind, wird der aktuelle Nutzer ausgeschlossen. +// Verwendung: Benachrichtigungen bei Statusänderungen, Schließen, Speichern. +function get_ticket_direct_recipients(int $ticket_id): array +{ + $con = $GLOBALS['mysql_con']; + $current = (int)($GLOBALS['main_contact']['id'] ?? 0); + + $res = @mysqli_query($con, "SELECT sender_id, receiver_id FROM main_tickets WHERE id = $ticket_id LIMIT 1"); + $ticket = $res ? @mysqli_fetch_assoc($res) : null; + if (!$ticket) return []; + + $sender_id = (int)$ticket['sender_id']; + $receiver_id = (int)$ticket['receiver_id']; + + if ($sender_id === $receiver_id) { + // Beide identisch — eine E-Mail, kein Ausschluss + $ids = [$sender_id]; + } else { + // Verschiedene Personen — aktuellen Nutzer ausschließen + $ids = array_filter( + [$sender_id, $receiver_id], + fn($id) => $id > 0 && $id !== $current + ); + } + + $ids = array_values(array_unique($ids)); + if (empty($ids)) return []; + + $in = implode(',', $ids); + $res = @mysqli_query($con, "SELECT name, email FROM main_contact WHERE id IN ($in) AND email != '' AND email IS NOT NULL"); + + $recipients = []; + while ($row = @mysqli_fetch_assoc($res)) { + $recipients[] = ['name' => $row['name'], 'email' => $row['email']]; + } + return $recipients; +} + +// Gibt alle Empfänger zurück, die über ein neues Ticket informiert werden sollen. +// Basiert auf der Kategorie-Zugriffskontrolle (Mandant / Abteilung / direkte Kontakte) +// sowie Sender und Receiver des Tickets. Verwendet dieselbe Logik wie user_can_see_ticket() in der API. +function get_ticket_recipients(int $ticket_id): array +{ + $con = $GLOBALS['mysql_con']; + $current_contact_id = (int)($GLOBALS['main_contact']['id'] ?? 0); + + $res = @mysqli_query($con, "SELECT sender_id, receiver_id, main_ticket_category_id + FROM main_tickets WHERE id = $ticket_id LIMIT 1"); + $ticket = $res ? @mysqli_fetch_assoc($res) : null; + if (!$ticket) return []; + + $category_id = (int)$ticket['main_ticket_category_id']; + $sender_id = (int)$ticket['sender_id']; + $receiver_id = (int)$ticket['receiver_id']; + + $contact_ids = []; + $mandant_ids = []; + $dept_ids = []; + + $res = @mysqli_query($con, "SELECT main_contact_id FROM main_tickets_category_contact WHERE main_tickets_category_id = $category_id"); + while ($row = @mysqli_fetch_assoc($res)) $contact_ids[] = (int)$row['main_contact_id']; + + $res = @mysqli_query($con, "SELECT main_mandant_id FROM main_tickets_category_mandant WHERE main_tickets_category_id = $category_id"); + while ($row = @mysqli_fetch_assoc($res)) $mandant_ids[] = (int)$row['main_mandant_id']; + + $res = @mysqli_query($con, "SELECT main_department_id FROM main_tickets_category_department WHERE main_tickets_category_id = $category_id"); + while ($row = @mysqli_fetch_assoc($res)) $dept_ids[] = (int)$row['main_department_id']; + + $category_contact_ids = array_merge([], $contact_ids); + + if (!empty($mandant_ids) || !empty($dept_ids)) { + $conditions = []; + + if (!empty($mandant_ids) && empty($dept_ids)) { + $in = implode(',', $mandant_ids); + $conditions[] = "mcd.main_mandant_id IN ($in)"; + } elseif (empty($mandant_ids) && !empty($dept_ids)) { + $in = implode(',', $dept_ids); + $conditions[] = "mcd.main_department_id IN ($in)"; + } else { + $pairs = []; + foreach ($mandant_ids as $m) { + foreach ($dept_ids as $d) { + $pairs[] = "(mcd.main_mandant_id = $m AND mcd.main_department_id = $d)"; + } + } + $conditions[] = '(' . implode(' OR ', $pairs) . ')'; + } + + $where = implode(' AND ', $conditions); + $res = @mysqli_query($con, "SELECT DISTINCT mcd.main_contact_id + FROM main_contact_department mcd + WHERE $where"); + while ($row = @mysqli_fetch_assoc($res)) { + $category_contact_ids[] = (int)$row['main_contact_id']; + } + } + + $all_ids = array_unique(array_merge($category_contact_ids, [$sender_id, $receiver_id])); + $all_ids = array_filter($all_ids, fn($id) => $id > 0 && $id !== $current_contact_id); + $all_ids = array_values($all_ids); + + if (empty($all_ids)) return []; + + $in = implode(',', $all_ids); + $res = @mysqli_query($con, "SELECT id, name, email FROM main_contact + WHERE id IN ($in) AND email != '' AND email IS NOT NULL"); + + $recipients = []; + while ($row = @mysqli_fetch_assoc($res)) { + $recipients[] = [ + 'name' => $row['name'], + 'email' => $row['email'], + ]; + } + + return $recipients; +} + +// Erstellt den HTML-Body für eine Ticket-Benachrichtigungs-E-Mail +// mit Ticketnummer, Beschreibung und Link zur Detailansicht. +function build_ticket_notification_email(int $ticket_id, string $heading, string $message_text = ''): string +{ + $res = @mysqli_query($GLOBALS['mysql_con'], "SELECT ticket_no, description FROM main_tickets WHERE id = $ticket_id LIMIT 1"); + $ticket = $res ? @mysqli_fetch_assoc($res) : []; + + $ticket_no = htmlspecialchars($ticket['ticket_no'] ?? ''); + $description = htmlspecialchars($ticket['description'] ?? ''); + $link = 'https://' . $_SERVER['HTTP_HOST'] . '/intranet/de/ticketcenter/?action=edit&id=' . encryptId($ticket_id); + + $message_block = $message_text + ? "

" . nl2br($message_text) . "

" + : ''; + + return " + + +
+ +
+

$heading

+

+ Ticketnr: $ticket_no
+ Beschreibung: $description +

+ $message_block + +
+
+ "; +} + +// Sendet eine E-Mail an alle übergebenen Empfänger über mail_create / mail_send. +// Überspringt Einträge ohne E-Mail-Adresse. mail_send() wird nur einmal am Ende aufgerufen. +function make_notification(array $recipients, string $subject, string $message): void +{ + $created = false; + + foreach ($recipients as $recipient) { + if (empty($recipient['email'])) continue; + + if (mail_create( + $subject, + $message, + 'ticketcenter@awo-hamburg.de', + $recipient['email'], + 'Ticketcenter', + $recipient['name'] ?? '', + TRUE + )) { + $created = true; + } + } + + if ($created) { + mail_send(); + clearstatcache(); + } +} + +// @deprecated — wird nicht mehr aktiv verwendet. +// Pflegt main_tickets_notification und versendet E-Mails über den alten Einzelversand-Mechanismus. function update_notification($ticket_id, $type, $notif_message = "Neue Aktivität in Ihrem Ticket") { - - - $query = "SELECT * FROM main_tickets WHERE id = " . $ticket_id; $result = @mysqli_query($GLOBALS['mysql_con'], $query); $ticket = @mysqli_fetch_array($result); @@ -750,9 +988,9 @@ function update_notification($ticket_id, $type, $notif_message = "Neue Aktivitä mysqli_query($GLOBALS['mysql_con'], $query_insert); $send = FALSE; - $query_contact = "SELECT * - FROM main_contact - WHERE id IN (" . $ticket['sender_id'] . ", " . $ticket['receiver_id'] . ") + $query_contact = "SELECT * + FROM main_contact + WHERE id IN (" . $ticket['sender_id'] . ", " . $ticket['receiver_id'] . ") AND id != " . $GLOBALS['main_contact']['id']; $result = @mysqli_query($GLOBALS['mysql_con'], $query_contact); $contact = @mysqli_fetch_array($result); @@ -779,7 +1017,7 @@ function update_notification($ticket_id, $type, $notif_message = "Neue Aktivitä $contact['email'] = $mandant['contact_email']; } } - + $subject = "Neue Aktivität | Ticket " . $ticket['ticket_no']; $message = "

" . $notif_message . "

Ticketnr: " . $ticket['ticket_no'] . "
Beschreibung: " . $ticket['description'] . "

"; if ($type == 'save') { @@ -796,7 +1034,7 @@ function update_notification($ticket_id, $type, $notif_message = "Neue Aktivitä mail_create( $subject, $message, - 'info@breadcrumb-online.de', + 'ticketcenter@awo-hamburg.de', $contact["email"], 'Ticketcenter', $contact["name"], @@ -809,22 +1047,26 @@ function update_notification($ticket_id, $type, $notif_message = "Neue Aktivitä "" ) ) { - mail_send(); clearstatcache(); } } } +// ============================================================================= +// HILFSFUNKTIONEN +// Kleine Abfragen und Hilfsmethoden, die von mehreren Stellen genutzt werden. +// ============================================================================= - +// Gibt die Statusbezeichnung (description) aus main_tickets_status zurück. function get_ticket_status($status_id) { - $query = "SELECT description FROM main_tickets_status WHERE id = $status_id"; $result = @mysqli_query($GLOBALS['mysql_con'], $query); $status = @mysqli_fetch_array($result); return $status['description']; } + +// Gibt Name und Telefonnummer eines Kontakts zurück. function get_contact($contact_id) { $query = "SELECT name, phone_no FROM main_contact WHERE id = $contact_id"; $result = @mysqli_query($GLOBALS['mysql_con'], $query); @@ -832,148 +1074,7 @@ function get_contact($contact_id) { return $contact; } -// DEPRIKATED: Funktion wird aktuell nicht mehr verwendet. -// Sollte die Funktion wieder benötigt werden, muss sie entsprechend angepasst werden, um die neuen Datenstrukturen zu berücksichtigen. - -function input_ticketnumber($ticket_no = "") -{ - if ($ticket_no == "") { - // Aktuelles Jahr als zwei Ziffern - $current_year = date('y'); - - // Query, um das letzte Ticket des aktuellen Jahres zu finden - $query = "SELECT ticket_no FROM main_tickets WHERE ticket_no LIKE 'T$current_year-%' ORDER BY ticket_no DESC LIMIT 1"; - $result = mysqli_query($GLOBALS['mysql_con'], $query); - - // Initialisiere die Ticketnummer - $new_ticket_no = ''; - - // Wenn es bereits Tickets in diesem Jahr gibt - if (mysqli_num_rows($result) > 0) { - $row = mysqli_fetch_assoc($result); - $last_ticket_no = $row['ticket_no']; - - // Extrahiere die Nummer nach dem Bindestrich - $last_ticket_number = (int) substr($last_ticket_no, 6); - - // Erhöhe die Nummer um 1 - $new_ticket_number = $last_ticket_number + 1; - - // Formatiere die neue Ticketnummer mit führenden Nullen (z.B. 0002) - $new_ticket_no = 'T' . $current_year . '-' . str_pad($new_ticket_number, 5, '0', STR_PAD_LEFT); - } else { - // Falls es noch keine Tickets in diesem Jahr gibt, starte bei 0001 - $new_ticket_no = 'T' . $current_year . '-00001'; - } - $ticket_no = $new_ticket_no; - - } - return $ticket_no; -} - -function insert_main_service_items_history($ticketno, $ticket_description, $ticket_id, $service_item_id) -{ - // Erstelle die Nachricht für die history_message - $history_message = "$ticketno $ticket_description wurde eröffnet."; - - // Bereite das INSERT-Statement vor - $query_insert_main_service_item_history = " - INSERT INTO main_service_items_history (history_message, history_date, main_ticket_id, main_service_item_id) - VALUES ('$history_message', NOW(), '$ticket_id', '$service_item_id') - "; - - // Führe die Abfrage aus - @mysqli_query($GLOBALS['mysql_con'], $query_insert_main_service_item_history); -} - - -function make_notification($id, $category_id = null, $subcategory_id = null, $subsubcategory_id = null) { - $choised_category_id = $subsubcategory_id ?? $subcategory_id ?? $category_id; - if (!$choised_category_id) { - return; - } - - $choised_contacts = getContactsByCategory($choised_category_id) ?: []; - $departments = getDepartmentsByCategory($choised_category_id); - $mandants = getMandantsByCategory($choised_category_id); - - $contacts = []; - - if (!empty($mandants) || !empty($departments)) { - $conditions = []; - - if (!empty($mandants)) { - $conditions[] = "main_mandant_id IN (" . implode(',', $mandants) . ")"; - } - - if (!empty($departments)) { - $conditions[] = "main_department_id IN (" . implode(',', $departments) . ")"; - } - - $query = "SELECT main_contact_id FROM main_contact_department WHERE " . implode(' AND ', $conditions); - - $result = mysqli_query($GLOBALS['mysql_con'], $query); - - while ($row = mysqli_fetch_assoc($result)) { - $contacts[] = $row['main_contact_id']; - } - } - - $contacts = array_unique(array_merge($contacts, $choised_contacts)); - - foreach ($contacts as $contact) { - $query = "SELECT DISTINCT main_contact.id, main_contact.name, token_contact.token as 'token' FROM main_contact LEFT JOIN token_contact ON token_contact.contact_id = main_contact.id WHERE main_contact.id = " . $contact; - $result = mysqli_query($GLOBALS['mysql_con'], $query); - - if (mysqli_num_rows($result) > 0) { - while ($row = mysqli_fetch_assoc($result)) { - $token = $row['token']; - $name = $row['name']; - // var_dump($token); - // var_dump($name); - // Hier kannst du die Benachrichtigung senden - sendCustomNotification($token, $name, "Ein neues Ticket wurde erstellt!"); - - } - } - } - // var_dump($contacts); -} - - -function sendCustomNotification($token, $name, $message) { - - $domain = ($_SERVER['HTTPS'] ?? 'off') === 'on' ? 'https://' : 'http://'; - $domain .= $_SERVER['HTTP_HOST']; - - $url = $domain . "/module/api/send_custom_notif.php"; - - $ch = curl_init($url); - - - $postData = [ - 'token' => $token, - 'name' => $name, - 'message' => $message - ]; - - - curl_setopt_array($ch, [ - CURLOPT_POST => true, - CURLOPT_RETURNTRANSFER => true, - CURLOPT_HTTPHEADER => [ - 'Content-Type: application/x-www-form-urlencoded' - ], - CURLOPT_POSTFIELDS => http_build_query($postData) - ]); - - - $response = curl_exec($ch); - // var_dump($response); - - curl_close($ch); -} - +// Gibt alle department_ids zurück, die einer Kategorie zugeordnet sind. function getDepartmentsByCategory($category_id) { $query = "SELECT main_department_id FROM main_tickets_category_department WHERE main_tickets_category_id = $category_id"; $result = mysqli_query($GLOBALS['mysql_con'], $query); @@ -986,6 +1087,7 @@ function getDepartmentsByCategory($category_id) { return $departments; } +// Gibt alle mandant_ids zurück, die einer Kategorie zugeordnet sind. function getMandantsByCategory($category_id) { $query = "SELECT main_mandant_id FROM main_tickets_category_mandant WHERE main_tickets_category_id = $category_id"; $result = mysqli_query($GLOBALS['mysql_con'], $query); @@ -998,6 +1100,7 @@ function getMandantsByCategory($category_id) { return $mandants; } +// Gibt alle contact_ids zurück, die direkt einer Kategorie zugeordnet sind. function getContactsByCategory($category_id) { $query = "SELECT main_contact_id FROM main_tickets_category_contact WHERE main_tickets_category_id = $category_id"; $result = mysqli_query($GLOBALS['mysql_con'], $query); @@ -1010,4 +1113,75 @@ function getContactsByCategory($category_id) { return $contacts; } +// Sendet eine Push-Benachrichtigung an ein Gerät über den internen Notification-Endpoint. +function sendCustomNotification($token, $name, $message) { + $domain = ($_SERVER['HTTPS'] ?? 'off') === 'on' ? 'https://' : 'http://'; + $domain .= $_SERVER['HTTP_HOST']; + + $url = $domain . "/module/api/send_custom_notif.php"; + $ch = curl_init($url); + + $postData = [ + 'token' => $token, + 'name' => $name, + 'message' => $message + ]; + + curl_setopt_array($ch, [ + CURLOPT_POST => true, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_HTTPHEADER => [ + 'Content-Type: application/x-www-form-urlencoded' + ], + CURLOPT_POSTFIELDS => http_build_query($postData) + ]); + + $response = curl_exec($ch); + curl_close($ch); +} + +// ============================================================================= +// DEPRECATED +// Nicht mehr aktiv verwendet. Vor Löschung auf fehlende Aufrufe prüfen. +// ============================================================================= + +// @deprecated — Ticketnummer-Generator. Ersetzt durch direkte Eingabe im Formular. +function input_ticketnumber($ticket_no = "") +{ + if ($ticket_no == "") { + $current_year = date('y'); + + $query = "SELECT ticket_no FROM main_tickets WHERE ticket_no LIKE 'T$current_year-%' ORDER BY ticket_no DESC LIMIT 1"; + $result = mysqli_query($GLOBALS['mysql_con'], $query); + + $new_ticket_no = ''; + + if (mysqli_num_rows($result) > 0) { + $row = mysqli_fetch_assoc($result); + $last_ticket_no = $row['ticket_no']; + $last_ticket_number = (int) substr($last_ticket_no, 6); + $new_ticket_number = $last_ticket_number + 1; + $new_ticket_no = 'T' . $current_year . '-' . str_pad($new_ticket_number, 5, '0', STR_PAD_LEFT); + } else { + $new_ticket_no = 'T' . $current_year . '-00001'; + } + $ticket_no = $new_ticket_no; + } + return $ticket_no; +} + +// @deprecated — Schreibt einen History-Eintrag in main_service_items_history. +// Wird aktuell nicht aufgerufen. +function insert_main_service_items_history($ticketno, $ticket_description, $ticket_id, $service_item_id) +{ + $history_message = "$ticketno $ticket_description wurde eröffnet."; + + $query_insert_main_service_item_history = " + INSERT INTO main_service_items_history (history_message, history_date, main_ticket_id, main_service_item_id) + VALUES ('$history_message', NOW(), '$ticket_id', '$service_item_id') + "; + + @mysqli_query($GLOBALS['mysql_con'], $query_insert_main_service_item_history); +} + ?>