'status', 'text' => 'Status', 'modal' => false, 'table' => ['filter' => 'iconSelect', 'filterOptions' => [ ['value' => 'new', 'text' => 'Neu', 'icon' => 'fas fa-star text-primary'], ['value' => 'assigned', 'text' => 'Zugewiesen', 'icon' => 'fas fa-user-check text-info'], ['value' => 'scheduled', 'text' => 'Geplant', 'icon' => 'fas fa-calendar-check text-warning'], ['value' => 'in_progress', 'text' => 'In Bearbeitung', 'icon' => 'fas fa-cog text-warning'], ['value' => 'documented', 'text' => 'Dokumentiert', 'icon' => 'fas fa-file-alt text-success'], ['value' => 'completed', 'text' => 'Abgeschlossen', 'icon' => 'fas fa-check-double text-secondary'], ['value' => 'cancelled', 'text' => 'Abgebrochen', 'icon' => 'fas fa-ban text-danger'], ['value' => 'archived', 'text' => 'Archiviert', 'icon' => 'fas fa-archive text-muted'], ]] ]; protected array $additionalJS = ["js/pages/WorkorderMphBase/WorkorderMphBase.js"]; protected array $additionalHead = [""]; // Wohneinheit status options protected array $wohneinheitStatuses = [ ['value' => 1, 'text' => '10 - new'], ['value' => 12, 'text' => '241 - BEP installed (MD)'], ['value' => 13, 'text' => '242 - Inhouse cabling finished'], ['value' => 18, 'text' => '243 - Cable in stairwell'], ['value' => 14, 'text' => '244 - BEP installed (SD)'], ['value' => 15, 'text' => '245 - Installation Approved'], ['value' => 16, 'text' => '300 - ONT installed'], ]; protected function getStatusText(string $statusKey): string { $statusMap = array_column($this->statusColumn['table']['filterOptions'] ?? [], 'text', 'value'); return $statusMap[$statusKey] ?? ucfirst(str_replace('_', ' ', $statusKey)); } protected function getWohneinheitStatusText(int $statusValue): string { $statusMap = array_column($this->wohneinheitStatuses, 'text', 'value'); return $statusMap[$statusValue] ?? "Status $statusValue"; } //region SHARED ACTIONS /** * Fetches documentation and journal entries for a given workorder. */ protected function getDocumentationAction() { if (empty($this->request->workorderMphId)) self::sendError("Arbeitsauftrags-ID fehlt."); $docs = WorkorderMphDocumentationModel::getAll(['workorderMphId' => intval($this->request->workorderMphId)], null, 0, ['key' => 'create', 'order' => 'ASC']); $journals = WorkorderMphJournalModel::getAll(['workorderMphId' => intval($this->request->workorderMphId)], null, 0, ['key' => 'create', 'order' => 'DESC']); $responseDocs = []; $typeCounts = []; foreach ($docs as $doc) { $file = new File($doc->fileId); $documentTypeKey = $doc->documentType; $typeCounts[$documentTypeKey] = ($typeCounts[$documentTypeKey] ?? 0) + 1; $originalFilename = $file->orig_filename ?? $file->filename; $extension = pathinfo($originalFilename, PATHINFO_EXTENSION); $newFilename = "{$documentTypeKey}_{$typeCounts[$documentTypeKey]}." . strtolower($extension); $responseDocs[] = [ 'id' => $doc->id, 'fileId' => $doc->fileId, 'fileName' => $newFilename, 'description' => $doc->description, 'documentType' => $documentTypeKey, 'userName' => UserModel::getOne($doc->createBy)->name ?? 'Unbekannt', 'mimetype' => $file->mimetype ?? 'application/octet-stream', 'create' => $doc->create ]; } foreach ($journals as $journal) { $journal->createByName = UserModel::getOne($journal->createBy)->name ?? 'Unbekannt'; } self::returnJson(['docs' => $responseDocs, 'journals' => $journals]); } /** * Adds a new entry to a workorder's journal. */ protected function addJournalAction() { $post = json_decode(file_get_contents('php://input'), true); if (empty($post['workorderMphId']) || empty(trim($post['text']))) self::sendError("Arbeitsauftrags-ID und Text sind erforderlich."); WorkorderMphJournalModel::create([ 'workorderMphId' => $post['workorderMphId'], 'text' => $post['text'], 'createBy' => $this->user->id, 'create' => time() ]); $journals = WorkorderMphJournalModel::getAll(['workorderMphId' => intval($post['workorderMphId'])], null, 0, ['key' => 'create', 'order' => 'DESC']); foreach ($journals as $journal) { $journal->createByName = UserModel::getOne($journal->createBy)->name ?? 'Unbekannt'; } self::returnJson(['success' => true, 'message' => 'Journaleintrag hinzugefügt.', 'journals' => $journals]); } /** * Updates the additional info field for a workorder. */ protected function updateAdditionalInfoAction() { $post = json_decode(file_get_contents('php://input'), true); if (empty($post['workorderMphId'])) self::sendError("Arbeitsauftrags-ID fehlt."); $workorder = WorkorderMphModel::get($post['workorderMphId']); if (!$workorder) self::sendError("Arbeitsauftrag nicht gefunden."); $oldInfo = $workorder->additionalInfo; $newInfo = $post['additionalInfo'] ?? null; $workorder->additionalInfo = $newInfo; WorkorderMphModel::update((array)$workorder); WorkorderMphJournalModel::create([ 'workorderMphId' => $workorder->id, 'text' => "Zusatzinfo geändert.\nAlt: '{$oldInfo}'\nNeu: '{$newInfo}'", 'create' => time(), 'createBy' => $this->user->id, ]); self::returnJson(['success' => true, 'message' => 'Zusatzinfo aktualisiert.', 'newInfo' => $newInfo]); } /** * Get all Wohneinheiten for a specific workorder with their statuses and notes */ protected function getWohneinheitenAction() { if (empty($this->request->workorderMphId)) self::sendError("Arbeitsauftrags-ID fehlt."); $workorderMphId = intval($this->request->workorderMphId); $workorder = WorkorderMphModel::get($workorderMphId); if (!$workorder) self::sendError("Arbeitsauftrag nicht gefunden."); // Get all Wohneinheiten for this Hausnummer from addressdb $db = FronkDB::singleton(ADDRESSDB_DBHOST, ADDRESSDB_DBUSER, ADDRESSDB_DBPASS, ADDRESSDB_DBNAME); $hausnummerId = $db->escape($workorder->hausnummerId); $sql = "SELECT w.id, w.bezeichner, w.contact FROM Wohneinheit w WHERE w.hausnummer_id = $hausnummerId ORDER BY w.bezeichner"; $result = $db->query($sql); $wohneinheiten = $result ? $result->fetch_all(MYSQLI_ASSOC) : []; // Get existing WorkorderMphWohneinheit records $existingRecords = WorkorderMphWohneinheitModel::getAll(['workorderMphId' => $workorderMphId]); $recordsMap = []; foreach ($existingRecords as $record) { $recordsMap[$record->wohneinheitId] = $record; } // Merge data $response = []; foreach ($wohneinheiten as $we) { $record = $recordsMap[$we['id']] ?? null; $response[] = [ 'wohneinheitId' => intval($we['id']), 'bezeichner' => $we['bezeichner'], 'contact' => $we['contact'], 'status' => $record ? $record->status : 1, 'note' => $record ? $record->note : null, 'recordId' => $record ? $record->id : null, ]; } self::returnJson(['wohneinheiten' => $response, 'statusOptions' => $this->wohneinheitStatuses]); } /** * Update status and note for a specific Wohneinheit */ protected function updateWohneinheitAction() { $post = json_decode(file_get_contents('php://input'), true); if (empty($post['workorderMphId']) || empty($post['wohneinheitId'])) { self::sendError("Arbeitsauftrags-ID und Wohneinheit-ID sind erforderlich."); } $workorderMphId = intval($post['workorderMphId']); $wohneinheitId = intval($post['wohneinheitId']); $status = intval($post['status'] ?? 1); $note = $post['note'] ?? null; // Check if record exists $existing = WorkorderMphWohneinheitModel::getFirst([ 'workorderMphId' => $workorderMphId, 'wohneinheitId' => $wohneinheitId ]); $oldStatus = $existing ? $existing->status : 1; $oldNote = $existing ? $existing->note : null; if ($existing) { $existing->status = $status; $existing->note = $note; $existing->edit = time(); $existing->editBy = $this->user->id; WorkorderMphWohneinheitModel::update((array)$existing); } else { WorkorderMphWohneinheitModel::create([ 'workorderMphId' => $workorderMphId, 'wohneinheitId' => $wohneinheitId, 'status' => $status, 'note' => $note, 'create' => time(), 'createBy' => $this->user->id ]); } // Add journal entry if status or note changed if ($oldStatus !== $status || $oldNote !== $note) { $changes = []; if ($oldStatus !== $status) { $changes[] = "Status: " . $this->getWohneinheitStatusText($oldStatus) . " → " . $this->getWohneinheitStatusText($status); } if ($oldNote !== $note) { $changes[] = "Notiz aktualisiert"; } WorkorderMphJournalModel::create([ 'workorderMphId' => $workorderMphId, 'text' => "Wohneinheit $wohneinheitId: " . implode(', ', $changes), 'create' => time(), 'createBy' => $this->user->id, ]); // If status is 241 (BEP MD) or 300 (ONT installed), set statusflag 200 on Wohneinheit if (in_array($status, [12, 16])) { // 12=241 BEP MD, 16=300 ONT $this->setWohneinheitStatusflag($wohneinheitId, 200); } } self::returnJson(['success' => true, 'message' => 'Wohneinheit aktualisiert.']); } /** * Set statusflag on Wohneinheit in addressdb */ private function setWohneinheitStatusflag(int $wohneinheitId, int $statusflagId) { $db = FronkDB::singleton(ADDRESSDB_DBHOST, ADDRESSDB_DBUSER, ADDRESSDB_DBPASS, ADDRESSDB_DBNAME); $weId = $db->escape($wohneinheitId); $sfId = $db->escape($statusflagId); // Check if statusflag already exists $checkSql = "SELECT COUNT(*) as count FROM WohneinheitStatusflagValue WHERE wohneinheit_id = $weId AND statusflag_id = $sfId"; $result = $db->query($checkSql); $exists = $result->fetch_assoc()['count'] > 0; if (!$exists) { $insertSql = "INSERT INTO WohneinheitStatusflagValue (wohneinheit_id, statusflag_id, create, createBy) VALUES ($weId, $sfId, " . time() . ", " . $this->user->id . ")"; $db->query($insertSql); } } /** * Update checkbox documentation fields */ protected function updateCheckboxesAction() { $post = json_decode(file_get_contents('php://input'), true); if (empty($post['workorderMphId'])) self::sendError("Arbeitsauftrags-ID fehlt."); $workorder = WorkorderMphModel::get($post['workorderMphId']); if (!$workorder) self::sendError("Arbeitsauftrag nicht gefunden."); $changes = []; $checkboxFields = ['easement', 'btb', 'fttxLocationSupplied', 'conduitToHuepLaid', 'huepMounted', 'dropCableAvailable']; foreach ($checkboxFields as $field) { if (array_key_exists($field, $post)) { $oldValue = $workorder->$field; $newValue = $post[$field] ? 1 : 0; if ($oldValue !== $newValue) { $workorder->$field = $newValue; $changes[] = "$field: " . ($newValue ? 'ja' : 'nein'); } } } if (!empty($changes)) { WorkorderMphModel::update((array)$workorder); WorkorderMphJournalModel::create([ 'workorderMphId' => $workorder->id, 'text' => "Dokumentation aktualisiert:\n" . implode("\n", $changes), 'create' => time(), 'createBy' => $this->user->id, ]); } self::returnJson(['success' => true, 'message' => 'Dokumentation aktualisiert.']); } //endregion }