diff --git a/application/RMLWorkorder/RMLWorkorderModel.php b/application/RMLWorkorder/RMLWorkorderModel.php index 88a422d3f..fa0bf81fa 100644 --- a/application/RMLWorkorder/RMLWorkorderModel.php +++ b/application/RMLWorkorder/RMLWorkorderModel.php @@ -1,7 +1,8 @@ $greenDate"; - break; - default: - return []; - } - - $sql = "SELECT * FROM $table $whereClause ORDER BY deadlineDate ASC"; - $result = $db->query($sql); - $orders = []; - while ($row = $result->fetch_assoc()) { - $orders[] = new self($row); - } - return $orders; - } - - // --- REFACTORED METHODS --- - - private static function buildWhereClause(array $filters, array $allowedCampaignIds): string { - if (empty($allowedCampaignIds)) { - return " WHERE 1=0"; - } + private static function buildWhereClause(array $filters, array $allowedCampaignIds): string + { + if (empty($allowedCampaignIds)) return " WHERE 1=0"; $sql = Helper::generateFilterCondition(array_map('intval', $allowedCampaignIds), 'p.preordercampaign_id'); - if (!empty($filters['id'])) { - $sql .= Helper::generateFilterCondition($filters['id'], 'w.id', true); - } - if (!empty($filters['status'])) { + if (empty($filters['status'])) { + $sql .= " AND w.status NOT IN ('completed', 'cancelled')"; + } else { $sql .= Helper::generateFilterCondition($filters['status'], 'w.status', true); } - if (!empty($filters['preordercampaign_id'])) { - $sql .= Helper::generateFilterCondition($filters['preordercampaign_id'], 'p.preordercampaign_id'); - } - if (!empty($filters['companyName'])) { - $sql .= Helper::generateFilterCondition($filters['companyName'], 'c.name'); - } - if (!empty($filters['deadlineDate'])) { - $sql .= Helper::generateFilterCondition($filters['deadlineDate'], 'w.deadlineDate'); - } + + if (!empty($filters['id'])) $sql .= Helper::generateFilterCondition($filters['id'], 'w.id', true); + if (!empty($filters['preordercampaign_id'])) $sql .= Helper::generateFilterCondition($filters['preordercampaign_id'], 'p.preordercampaign_id'); + if (!empty($filters['companyName'])) $sql .= Helper::generateFilterCondition($filters['companyName'], 'c.name'); + if (!empty($filters['deadlineDate'])) $sql .= Helper::generateFilterCondition($filters['deadlineDate'], 'w.deadlineDate'); if (!empty($filters['preorderInfo'])) { - $searchColumns = "p.firstname|p.lastname|p.company|p.oaid|p.street|p.housenumber|p.zip|p.city|str.name|ort.name"; + $searchColumns = "p.firstname|p.lastname|p.company|p.oaid|p.street|p.housenumber|p.zip|p.city|str.name|ort.name|w.additionalInfo"; $sql .= Helper::generateFilterCondition($filters['preorderInfo'], $searchColumns); - } if (!empty($filters['rimo_fcp_name'])) { - $searchColumns = "hn.rimo_fcp_name"; - $sql .= Helper::generateFilterCondition($filters['rimo_fcp_name'], $searchColumns); } + if (!empty($filters['rimo_fcp_name'])) $sql .= Helper::generateFilterCondition($filters['rimo_fcp_name'], "hn.rimo_fcp_name"); + if (!empty($filters['additionalInfo'])) $sql .= Helper::generateFilterCondition($filters['additionalInfo'], 'w.additionalInfo'); return "WHERE " . ltrim(trim($sql), 'AND'); } - public static function getAdminWorkorders(array $filters, ?int $limit, int $offset, array $order, array $allowedCampaignIds): array { + public static function getAdminWorkorders(array $filters, ?int $limit, int $offset, array $order, array $allowedCampaignIds): array + { $db = self::getDB(); $fronkDbName = FRONKDB_DBNAME; $addressDbName = defined('ADDRESSDB_DBNAME') ? ADDRESSDB_DBNAME : 'addressdb'; $sql = " SELECT - w.id, w.status, w.deadlineDate, w.companyId, p.preordercampaign_id, hn.rimo_fcp_name, - n.owner_id as tenantId, - CONCAT_WS(' ', p.firstname, p.lastname) as customerName, p.ucode, - p.company as customerCompany, p.oaid, c.name as companyName, - str.name as street, hn.hausnummer, hn.stiege, we.bezeichner as apartment, plz.plz, ort.name as city + w.id, w.status, w.deadlineDate, w.companyId, w.additionalInfo, p.preordercampaign_id, hn.rimo_fcp_name, + n.owner_id as tenantId, p.ucode, CONCAT_WS(' ', p.firstname, p.lastname) as customerName, + p.company as customerCompany, p.oaid, c.name as companyName, str.name as street, hn.hausnummer, + hn.stiege, we.bezeichner as apartment, plz.plz, ort.name as city FROM `$fronkDbName`.`RMLWorkorder` w JOIN `$fronkDbName`.`Preorder` p ON w.preorderId = p.id LEFT JOIN `$fronkDbName`.`Preordercampaign` pc ON p.preordercampaign_id = pc.id @@ -111,74 +67,56 @@ class RMLWorkorderModel extends TTCrudBaseModel { $sql .= self::buildWhereClause($filters, $allowedCampaignIds); - $sql .= " ORDER BY CASE WHEN w.deadlineDate IS NULL THEN 1 ELSE 0 END, w.deadlineDate ASC"; - + $orderBy = ""; if (!empty($order['key'])) { - $sortableColumns = ['id', 'status', 'deadlineDate', 'companyName', 'clusterName']; + $sortableColumns = ['id', 'status', 'deadlineDate', 'companyName', 'rimo_fcp_name', 'additionalInfo']; if (in_array($order['key'], $sortableColumns)) { $sortOrder = (strtoupper($order['order']) === 'DESC') ? 'DESC' : 'ASC'; - $sql .= ", " . $db->real_escape_string($order['key']) . " " . $sortOrder; + $orderBy = " ORDER BY " . $db->real_escape_string($order['key']) . " " . $sortOrder; } } + if (empty($orderBy)) $orderBy = " ORDER BY CASE WHEN w.deadlineDate IS NULL THEN 1 ELSE 0 END, w.deadlineDate ASC"; + + $sql .= $orderBy; + + if ($limit !== null) $sql .= " LIMIT " . intval($limit) . " OFFSET " . intval($offset); - if ($limit !== null) { - $sql .= " LIMIT " . intval($limit) . " OFFSET " . intval($offset); - } $result = $db->query($sql); return $result ? $result->fetch_all(MYSQLI_ASSOC) : []; } - public static function countAdminWorkorders(array $filters, array $allowedCampaignIds): int { + public static function countAdminWorkorders(array $filters, array $allowedCampaignIds): int + { $db = self::getDB(); $fronkDbName = FRONKDB_DBNAME; $addressDbName = defined('ADDRESSDB_DBNAME') ? ADDRESSDB_DBNAME : 'addressdb'; - $sql = " - SELECT COUNT(w.id) as count - FROM `$fronkDbName`.`RMLWorkorder` w + SELECT COUNT(w.id) as count FROM `$fronkDbName`.`RMLWorkorder` w JOIN `$fronkDbName`.`Preorder` p ON w.preorderId = p.id - LEFT JOIN `$fronkDbName`.`Preordercampaign` pc ON p.preordercampaign_id = pc.id - LEFT JOIN `$fronkDbName`.`Network` n ON pc.network_id = n.id LEFT JOIN `$fronkDbName`.`RMLWorkorderCompany` c ON w.companyId = c.id LEFT JOIN `$addressDbName`.`Hausnummer` hn ON p.adb_hausnummer_id = hn.id LEFT JOIN `$addressDbName`.`Strasse` str ON hn.strasse_id = str.id LEFT JOIN `$addressDbName`.`Plz` plz ON hn.plz_id = plz.id LEFT JOIN `$addressDbName`.`Ortschaft` ort ON hn.ortschaft_id = ort.id "; - $sql .= self::buildWhereClause($filters, $allowedCampaignIds); - $result = $db->query($sql); - if ($result === false) return 0; - - $row = $result->fetch_assoc(); - return $row['count'] ?? 0; + return $result ? $result->fetch_assoc()['count'] : 0; } private static function buildCompanyWhereClause(array $filters, int $companyId): string { $sql = "w.companyId = " . $companyId; - - if (!empty($filters['id'])) { - $sql .= Helper::generateFilterCondition($filters['id'], 'w.id', true); - } - if (!empty($filters['status'])) { - $sql .= Helper::generateFilterCondition($filters['status'], 'w.status'); - } - if (!empty($filters['deadlineDate'])) { - $sql .= Helper::generateFilterCondition($filters['deadlineDate'], 'w.deadlineDate'); - } - if (!empty($filters['appointmentDate'])) { - $sql .= Helper::generateFilterCondition($filters['appointmentDate'], 'w.appointmentDate'); - } + if (!empty($filters['id'])) $sql .= Helper::generateFilterCondition($filters['id'], 'w.id', true); + if (!empty($filters['status'])) $sql .= Helper::generateFilterCondition($filters['status'], 'w.status'); + if (!empty($filters['deadlineDate'])) $sql .= Helper::generateFilterCondition($filters['deadlineDate'], 'w.deadlineDate'); + if (!empty($filters['appointmentDate'])) $sql .= Helper::generateFilterCondition($filters['appointmentDate'], 'w.appointmentDate'); if (!empty($filters['preorderInfo'])) { - $searchColumns = "p.firstname|p.lastname|p.company|p.oaid|p.street|p.housenumber|p.zip|p.city|str.name|ort.name|p.phone|p.email"; + $searchColumns = "p.firstname|p.lastname|p.company|p.oaid|p.street|p.housenumber|p.zip|p.city|str.name|ort.name|p.phone|p.email|w.additionalInfo"; $sql .= Helper::generateFilterCondition($filters['preorderInfo'], $searchColumns); - } if (!empty($filters['rimo_fcp_name'])) { - $searchColumns = "hn.rimo_fcp_name"; - $sql .= Helper::generateFilterCondition($filters['rimo_fcp_name'], $searchColumns); - } - + } + if (!empty($filters['rimo_fcp_name'])) $sql .= Helper::generateFilterCondition($filters['rimo_fcp_name'], "hn.rimo_fcp_name"); + if (!empty($filters['additionalInfo'])) $sql .= Helper::generateFilterCondition($filters['additionalInfo'], 'w.additionalInfo'); return "WHERE " . $sql; } @@ -187,13 +125,10 @@ class RMLWorkorderModel extends TTCrudBaseModel { $db = self::getDB(); $fronkDbName = FRONKDB_DBNAME; $addressDbName = defined('ADDRESSDB_DBNAME') ? ADDRESSDB_DBNAME : 'addressdb'; - $sql = " - SELECT - w.id, w.status, w.deadlineDate, w.appointmentDate, hn.rimo_fcp_name, - CONCAT_WS(' ', p.firstname, p.lastname) as customerName, - p.company as customerCompany, p.oaid, p.phone, p.email, - str.name as street, hn.hausnummer, hn.stiege, we.bezeichner as apartment, plz.plz, ort.name as city + SELECT w.id, w.status, w.deadlineDate, w.appointmentDate, w.additionalInfo, hn.rimo_fcp_name, + CONCAT_WS(' ', p.firstname, p.lastname) as customerName, p.company as customerCompany, p.oaid, + p.phone, p.email, str.name as street, hn.hausnummer, hn.stiege, we.bezeichner as apartment, plz.plz, ort.name as city FROM `$fronkDbName`.`RMLWorkorder` w JOIN `$fronkDbName`.`Preorder` p ON w.preorderId = p.id LEFT JOIN `$addressDbName`.`Hausnummer` hn ON p.adb_hausnummer_id = hn.id @@ -202,22 +137,22 @@ class RMLWorkorderModel extends TTCrudBaseModel { LEFT JOIN `$addressDbName`.`Ortschaft` ort ON hn.ortschaft_id = ort.id LEFT JOIN `$addressDbName`.`Wohneinheit` we ON p.adb_wohneinheit_id = we.id "; - $sql .= self::buildCompanyWhereClause($filters, $companyId); - $orderBy = " ORDER BY CASE WHEN w.deadlineDate IS NULL THEN 1 ELSE 0 END, w.deadlineDate ASC"; + $orderBy = ""; if (!empty($order['key'])) { - $sortableColumns = ['id', 'status', 'deadlineDate', 'appointmentDate']; + $sortableColumns = ['id', 'status', 'deadlineDate', 'appointmentDate', 'additionalInfo']; if (in_array($order['key'], $sortableColumns)) { $sortOrder = (strtoupper($order['order']) === 'DESC') ? 'DESC' : 'ASC'; $orderBy = " ORDER BY " . $db->real_escape_string($order['key']) . " " . $sortOrder; } } + if (empty($orderBy)) $orderBy = " ORDER BY CASE WHEN w.deadlineDate IS NULL THEN 1 ELSE 0 END, w.deadlineDate ASC"; + $sql .= $orderBy; - if ($limit !== null) { - $sql .= " LIMIT " . intval($limit) . " OFFSET " . intval($offset); - } + if ($limit !== null) $sql .= " LIMIT " . intval($limit) . " OFFSET " . intval($offset); + $result = $db->query($sql); return $result ? $result->fetch_all(MYSQLI_ASSOC) : []; } @@ -227,24 +162,16 @@ class RMLWorkorderModel extends TTCrudBaseModel { $db = self::getDB(); $fronkDbName = FRONKDB_DBNAME; $addressDbName = defined('ADDRESSDB_DBNAME') ? ADDRESSDB_DBNAME : 'addressdb'; - $sql = " - SELECT COUNT(w.id) as count - FROM `$fronkDbName`.`RMLWorkorder` w + SELECT COUNT(w.id) as count FROM `$fronkDbName`.`RMLWorkorder` w JOIN `$fronkDbName`.`Preorder` p ON w.preorderId = p.id LEFT JOIN `$addressDbName`.`Hausnummer` hn ON p.adb_hausnummer_id = hn.id LEFT JOIN `$addressDbName`.`Strasse` str ON hn.strasse_id = str.id LEFT JOIN `$addressDbName`.`Plz` plz ON hn.plz_id = plz.id LEFT JOIN `$addressDbName`.`Ortschaft` ort ON hn.ortschaft_id = ort.id "; - $sql .= self::buildCompanyWhereClause($filters, $companyId); - $result = $db->query($sql); - if ($result === false) { - return 0; - } - $row = $result->fetch_assoc(); - return $row['count'] ?? 0; + return $result ? $result->fetch_assoc()['count'] : 0; } } \ No newline at end of file diff --git a/application/RMLWorkorderAdmin/RMLWorkorderAdminController.php b/application/RMLWorkorderAdmin/RMLWorkorderAdminController.php index 1864fcb96..f45a72db7 100644 --- a/application/RMLWorkorderAdmin/RMLWorkorderAdminController.php +++ b/application/RMLWorkorderAdmin/RMLWorkorderAdminController.php @@ -3,42 +3,39 @@ class RMLWorkorderAdminController extends TTCrud { - protected string $headerTitle = 'RML Arbeitsaufträge (Admin)'; + protected string $headerTitle = 'RML-Arbeitsaufträge (Admin)'; protected bool $createText = false; protected array $permissionCheck = ['RMLAdmin']; protected array $columns = [ - ['key' => 'id', 'text' => 'Auftrag-Nr.', 'table' => ['sortable' => false]], - ['key' => 'preordercampaign_id', 'text' => 'Cluster', 'modal' => false, 'table' => ['filter' => 'select']], - ['key' => 'preorderInfo', 'text' => 'Kunde / Projekt', 'modal' => false, 'table' => ['sortable' => false]], - ['key' => 'rimo_fcp_name', 'text' => 'FCP', 'modal' => false, 'table' => ['sortable' => false]], - ['key' => 'companyName', 'text' => 'Zuständige Firma', 'modal' => false], + ['key' => 'id', 'text' => 'Auftrags-Nr.', 'table' => ['sortable' => true]], + ['key' => 'preordercampaign_id', 'text' => 'Kampagne', 'modal' => false, 'table' => ['filter' => 'select']], + ['key' => 'preorderInfo', 'text' => 'Kunde', 'modal' => false, 'table' => ['sortable' => false]], + ['key' => 'rimo_fcp_name', 'text' => 'FCP', 'modal' => false, 'table' => ['sortable' => true]], + ['key' => 'companyName', 'text' => 'Zugewiesene Firma', 'modal' => false, 'table' => ['sortable' => true]], ['key' => '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' => 'Terminiert', 'icon' => 'fas fa-calendar-check text-warning'], + ['value' => 'scheduled', 'text' => 'Geplant', 'icon' => 'fas fa-calendar-check text-warning'], ['value' => 'correction_requested', 'text' => 'Korrektur angefordert', 'icon' => 'fas fa-exclamation-triangle text-danger'], - ['value' => 'intervention_required', 'text' => 'Eingriff benötigt', 'icon' => 'fas fa-times-circle text-danger'], - ['value' => 'problem_solved', 'text' => 'Problem behoben', 'icon' => 'fas fa-check-circle text-success'], + ['value' => 'intervention_required', 'text' => 'Eingriff erforderlich', 'icon' => 'fas fa-times-circle text-danger'], + ['value' => 'problem_solved', 'text' => 'Problem gelöst', 'icon' => 'fas fa-check-circle text-success'], ['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'], ]]], - ['key' => 'deadlineDate', 'text' => 'Deadline', 'modal' => false, 'table' => ['filter' => 'date', 'sortable' => false]], + ['key' => 'additionalInfo', 'text' => 'Notiz', 'modal' => false, 'table' => ['sortable' => true]], + ['key' => 'deadlineDate', 'text' => 'Deadline', 'modal' => false, 'table' => ['filter' => 'date', 'sortable' => true]], ]; - private function getStatusText(string $statusKey): string { - $statusColumn = null; + private function getStatusText(string $statusKey): string + { foreach ($this->columns as $column) { if ($column['key'] === 'status') { - $statusColumn = $column; - break; - } - } - - if ($statusColumn) { - foreach ($statusColumn['table']['filterOptions'] as $option) { - if ($option['value'] === $statusKey) { - return $option['text']; + foreach ($column['table']['filterOptions'] as $option) { + if ($option['value'] === $statusKey) { + return $option['text']; + } } } } @@ -60,7 +57,8 @@ class RMLWorkorderAdminController extends TTCrud ]); } - protected function getAction() { + protected function getAction() + { $json = json_decode(file_get_contents('php://input'), true); $pagination = $json['pagination'] ?? ['page' => 1, 'per_page' => 10]; $filters = $json['filters'] ?? []; @@ -82,13 +80,9 @@ class RMLWorkorderAdminController extends TTCrud $workorders = RMLWorkorderModel::getAdminWorkorders($filters, $limit, $offset, $order, $allowedCampaignIds); $totalCount = RMLWorkorderModel::countAdminWorkorders($filters, $allowedCampaignIds); - $rows = array_map(function($workorder) { + $rows = array_map(function ($workorder) { $row = (array)$workorder; - $row['companyName'] ??= 'Nicht zugewiesen'; - $row['deadlineDateFormatted'] = $row['deadlineDate'] ? date('d.m.Y', $row['deadlineDate']) : 'Keine Deadline'; - $row['daysUntilDeadline'] = $row['deadlineDate'] ? ceil(($row['deadlineDate'] - time()) / (60 * 60 * 24)) : null; - return $row; }, $workorders); @@ -104,17 +98,18 @@ class RMLWorkorderAdminController extends TTCrud ]); } - private function createWorkordersFromPreorders() { + private function createWorkordersFromPreorders() + { $configs = RMLWorkorderTenantConfigModel::getAll(); foreach ($configs as $config) { $filters = json_decode($config->workorderCreationFilters, true); if (empty($filters)) continue; $networks = NetworkModel::getAll(['owner_id' => $config->addressId]); - if(empty($networks)) continue; + if (empty($networks)) continue; $tenantCampaigns = array_map(fn($n) => $n->id, PreordercampaignModel::getAll(['network_id' => array_map(fn($n) => $n->id, $networks)])); - if(empty($tenantCampaigns)) continue; + if (empty($tenantCampaigns)) continue; $filters['preordercampaign_id'] = $tenantCampaigns; @@ -125,77 +120,61 @@ class RMLWorkorderAdminController extends TTCrud if (!RMLWorkorderModel::getFirst(['preorderId' => $preorder->id])) { RMLWorkorderModel::create([ 'preorderId' => $preorder->id, - 'clusterId' => $preorder->preordercampaign_id, - 'status' => 'new', - 'create' => time(), - 'createBy' => 0 // System User + 'clusterId' => $preorder->preordercampaign_id, + 'status' => 'new', + 'create' => time(), + 'createBy' => 0 // System User ]); } } } } - protected function getDocumentationAction() { - if(empty($this->request->workorderId)) self::sendError("Workorder ID missing."); + protected function getDocumentationAction() + { + if (empty($this->request->workorderId)) self::sendError("Arbeitsauftrags-ID fehlt."); $docs = RMLWorkorderDocumentationModel::getAll(['workorderId' => $this->request->workorderId], null, 0, ['key' => 'create', 'order' => 'ASC']); $journals = RMLWorkorderJournalModel::getAll(['workorderId' => $this->request->workorderId], null, 0, ['key' => 'create', 'order' => 'DESC']); $translationMap = [ - 'photo_hup_mounted' => 'Foto_montierter_HÜP', - 'photo_hup_open' => 'Foto_offener_HÜP', - 'photo_splice_cassette_hup' => 'Foto_Spleißkassette_HÜP', - 'photo_splice_cassette_fcp' => 'Foto_Spleißkassette_FCP', - 'photo_hup_closed_stickers' => 'Foto_geschlossener_HÜP_mit_Aufklebern', - 'photo_fcp_labeled' => 'Foto_FCP_beschriftet', - 'photo_patch_position_osp' => 'Foto_Patch-Position_OSP-Seite', - 'photo_patch_position_anb' => 'Foto_Patch-Position_ANB-Seite', - 'measurement_protocol_otdr' => 'ODTR_Messung', - 'other' => 'Sonstiges_Dokument' + 'photo_hup_mounted' => 'Foto_montierter_HÜP', 'photo_hup_open' => 'Foto_offener_HÜP', + 'photo_splice_cassette_hup' => 'Foto_Spleißkassette_HÜP', 'photo_splice_cassette_fcp' => 'Foto_Spleißkassette_FCP', + 'photo_hup_closed_stickers' => 'Foto_geschlossener_HÜP_mit_Aufklebern', 'photo_fcp_labeled' => 'Foto_FCP_beschriftet', + 'photo_patch_position_osp' => 'Foto_Patch-Position_OSP-Seite', 'photo_patch_position_anb' => 'Foto_Patch-Position_ANB-Seite', + 'measurement_protocol_otdr' => 'ODTR_Messung', 'other' => 'Sonstiges_Dokument' ]; $responseDocs = []; $typeCounts = []; - foreach($docs as $doc) { + 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); $translatedType = $translationMap[$documentTypeKey] ?? $documentTypeKey; $newFilename = "{$translatedType}_{$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', + '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', ]; } - foreach($journals as $journal) { - $journal->createByName = UserModel::getOne($journal->createBy)->name ?? 'Unbekannt'; - } + foreach ($journals as $journal) $journal->createByName = UserModel::getOne($journal->createBy)->name ?? 'Unbekannt'; self::returnJson(['docs' => $responseDocs, 'journals' => $journals]); } - private function assignSingleWorkorder($workorderId, $companyId, $deadline, $userId) { + private function assignSingleWorkorder($workorderId, $companyId, $deadline, $userId) + { $workorder = RMLWorkorderModel::get($workorderId); - if (!$workorder) { - return false; - } - + if (!$workorder) return false; $company = RMLWorkorderCompanyModel::get($companyId); - if (!$company) { - return false; - } + if (!$company) return false; $workorder->companyId = $companyId; $workorder->status = 'assigned'; @@ -204,159 +183,116 @@ class RMLWorkorderAdminController extends TTCrud RMLWorkorderModel::update((array)$workorder); RMLWorkorderJournalModel::create([ - 'workorderId' => $workorder->id, - 'text' => "Firma '{$company->name}' wurde zugewiesen.", - 'create' => time(), - 'createBy' => $userId, + 'workorderId' => $workorder->id, 'text' => "Firma '{$company->name}' wurde zugewiesen.", + 'create' => time(), 'createBy' => $userId, ]); $preorder = new Preorder($workorder->preorderId); - if ($preorder) { - $preorder->status_id = 10; // Assuming 10 is the status for "assigned" + if ($preorder->id) { + $preorder->status_id = 10; $preorder->edit_by = $this->user->id; $preorder->save(); } - return true; } - protected function assignWorkorderAction() { + protected function assignWorkorderAction() + { $post = json_decode(file_get_contents('php://input'), true); - if (empty($post['workorderId']) || empty($post['companyId'])) { - self::sendError("Erforderliche Felder fehlen."); - } + if (empty($post['workorderId']) || empty($post['companyId'])) self::sendError("Erforderliche Felder fehlen."); $deadline = !empty($post['deadlineDate']) ? $post['deadlineDate'] : strtotime('+6 weeks'); - $success = $this->assignSingleWorkorder($post['workorderId'], $post['companyId'], $deadline, $this->user->id); - - if ($success) { - self::returnJson(['success' => true, 'message' => 'Auftrag erfolgreich zugewiesen.']); - } else { - self::sendError("Auftrag konnte nicht zugewiesen werden. Möglicherweise wurde er bereits bearbeitet oder existiert nicht."); - } + if ($this->assignSingleWorkorder($post['workorderId'], $post['companyId'], $deadline, $this->user->id)) { + self::returnJson(['success' => true, 'message' => 'Arbeitsauftrag erfolgreich zugewiesen.']); + } else self::sendError("Arbeitsauftrag konnte nicht zugewiesen werden. Er wurde möglicherweise bereits bearbeitet oder existiert nicht."); } - protected function massAssignWorkordersAction() { + protected function massAssignWorkordersAction() + { $post = json_decode(file_get_contents('php://input'), true); - if (empty($post['workorderIds']) || empty($post['companyId'])) { - self::sendError("Erforderliche Felder fehlen."); - } + if (empty($post['workorderIds']) || empty($post['companyId'])) self::sendError("Erforderliche Felder fehlen."); $deadline = strtotime($post['deadlineDate'] ?? '+6 weeks'); $count = 0; - foreach ($post['workorderIds'] as $workorderId) { - if ($this->assignSingleWorkorder($workorderId, $post['companyId'], $deadline, $this->user->id)) { - $count++; - } - } - self::returnJson(['success' => true, 'message' => "$count Aufträge erfolgreich zugewiesen."]); + foreach ($post['workorderIds'] as $workorderId) if ($this->assignSingleWorkorder($workorderId, $post['companyId'], $deadline, $this->user->id)) $count++; + self::returnJson(['success' => true, 'message' => "$count Arbeitsaufträge erfolgreich zugewiesen."]); } - protected function requestCorrectionAction() { + protected function requestCorrectionAction() + { $post = json_decode(file_get_contents('php://input'), true); - if (empty($post['workorderId']) || empty($post['text'])) self::sendError("Required fields are missing."); - + if (empty($post['workorderId']) || empty($post['text'])) self::sendError("Erforderliche Felder fehlen."); $workorder = RMLWorkorderModel::get($post['workorderId']); - if (!$workorder) self::sendError("Workorder not found."); + if (!$workorder) self::sendError("Arbeitsauftrag nicht gefunden."); $oldStatus = $workorder->status; $workorder->status = 'correction_requested'; RMLWorkorderModel::update((array)$workorder); RMLWorkorderJournalModel::create([ - 'workorderId' => $workorder->id, - 'text' => "Korrektur angefordert. Grund: " . $post['text'], + 'workorderId' => $workorder->id, 'text' => "Korrektur angefordert. Grund: " . $post['text'], 'fileIds' => !empty($post['fileIds']) ? json_encode($post['fileIds']) : null, 'statusChange' => $this->getStatusText($oldStatus) . " -> " . $this->getStatusText('correction_requested'), - 'create' => time(), - 'createBy' => $this->user->id, + 'create' => time(), 'createBy' => $this->user->id, ]); - self::returnJson(['success' => true, 'message' => 'Korrektur wurde angefordert.']); } - protected function getCompaniesAction() { + protected function getCompaniesAction() + { $tenantId = $this->request->tenantId ?? null; $companies = RMLWorkorderCompanyModel::getAll([], null, 0, ['key' => 'name', 'order' => 'ASC']); if ($tenantId) { - $companies = array_filter($companies, function($company) use ($tenantId) { - // RML Infrastruktur GmbH is always available as a fallback/default - if ($company->addressId == 4807 && empty($company->visibleForAddressId)) { - return true; - } + $companies = array_filter($companies, function ($company) use ($tenantId) { + if ($company->addressId == 4807 && empty($company->visibleForAddressId)) return true; $visibleFor = !empty($company->visibleForAddressId) ? json_decode($company->visibleForAddressId, true) : []; return in_array($tenantId, $visibleFor); }); } - - $items = array_map(fn($c) => ['value' => $c->id, 'text' => $c->name], $companies); - self::returnJson(array_values($items)); // re-index + self::returnJson(array_values(array_map(fn($c) => ['value' => $c->id, 'text' => $c->name], $companies))); } - protected function addJournalAction() { + protected function addJournalAction() + { $post = json_decode(file_get_contents('php://input'), true); + if (empty($post['workorderId']) || empty(trim($post['text']))) self::sendError("Arbeitsauftrags-ID und Text sind erforderlich."); - if (empty($post['workorderId']) || empty(trim($post['text']))) { - self::sendError("Workorder ID and text are required."); - } - - RMLWorkorderJournalModel::create([ - 'workorderId' => $post['workorderId'], - 'text' => $post['text'], - 'createBy' => $this->user->id, - 'create' => time(), - ]); - - $journals = array_map( - function ($j) { - $j->createByName = UserModel::getOne($j->createBy)->name ?? 'Unbekannt'; - return (array)$j; - }, + RMLWorkorderJournalModel::create(['workorderId' => $post['workorderId'], 'text' => $post['text'], 'createBy' => $this->user->id, 'create' => time()]); + $journals = array_map(function ($j) { + $j->createByName = UserModel::getOne($j->createBy)->name ?? 'Unbekannt'; + return (array)$j; + }, RMLWorkorderJournalModel::getAll(['workorderId' => $post['workorderId']], null, 0, ['key' => 'create', 'order' => 'DESC']) ); - - self::returnJson([ - 'success' => true, - 'message' => 'Journal-Eintrag hinzugefügt.', - 'journals' => $journals - ]); + self::returnJson(['success' => true, 'message' => 'Journaleintrag hinzugefügt.', 'journals' => $journals]); } - protected function updateDeadlineAction() { + protected function updateDeadlineAction() + { $post = json_decode(file_get_contents('php://input'), true); - if (empty($post['workorderId']) || empty($post['deadlineDate'])) self::sendError("Required fields are missing."); + if (empty($post['workorderId']) || empty($post['deadlineDate'])) self::sendError("Erforderliche Felder fehlen."); $workorder = RMLWorkorderModel::get($post['workorderId']); - if (!$workorder) self::sendError("Workorder not found."); + if (!$workorder) self::sendError("Arbeitsauftrag nicht gefunden."); $workorder->deadlineDate = $post['deadlineDate']; RMLWorkorderModel::update((array)$workorder); - - RMLWorkorderJournalModel::create([ - 'workorderId' => $workorder->id, - 'text' => 'Deadline wurde auf ' . date('d.m.Y', $post['deadlineDate']) . ' geändert.', - 'create' => time(), - 'createBy' => $this->user->id, - ]); - + RMLWorkorderJournalModel::create(['workorderId' => $workorder->id, 'text' => 'Deadline geändert auf ' . date('d.m.Y', $post['deadlineDate']) . '.', 'create' => time(), 'createBy' => $this->user->id]); self::returnJson(['success' => true, 'message' => 'Deadline erfolgreich aktualisiert.']); } - protected function acceptDocumentationAction() { + protected function acceptDocumentationAction() + { $post = json_decode(file_get_contents('php://input'), true); - if (empty($post['workorderId'])) self::sendError("Workorder ID is missing."); - + if (empty($post['workorderId'])) self::sendError("Arbeitsauftrags-ID fehlt."); $workorder = RMLWorkorderModel::get($post['workorderId']); - if (!$workorder) self::sendError("Workorder not found."); - - if ($workorder->status !== 'documented') { - self::sendError("Die Dokumentation muss zuerst von der Firma als fertig markiert werden."); - } + if (!$workorder) self::sendError("Arbeitsauftrag nicht gefunden."); + if ($workorder->status !== 'documented') self::sendError("Die Dokumentation muss zuerst von der Firma als fertig markiert werden."); $preorder = new Preorder($workorder->preorderId); - if ($preorder) { - $preorder->status_id = 15; // Assuming 15 is the status for "completed" + if ($preorder->id) { + $preorder->status_id = 15; $preorder->edit_by = $this->user->id; $preorder->save(); } @@ -364,47 +300,76 @@ class RMLWorkorderAdminController extends TTCrud $oldStatus = $workorder->status; $workorder->status = 'completed'; RMLWorkorderModel::update((array)$workorder); - RMLWorkorderJournalModel::create([ - 'workorderId' => $workorder->id, - 'text' => 'Dokumentation wurde akzeptiert und der Auftrag abgeschlossen.', + 'workorderId' => $workorder->id, 'text' => 'Dokumentation akzeptiert und Arbeitsauftrag abgeschlossen.', 'statusChange' => $this->getStatusText($oldStatus) . " -> " . $this->getStatusText('completed'), - 'create' => time(), - 'createBy' => $this->user->id, + 'create' => time(), 'createBy' => $this->user->id, ]); - - self::returnJson(['success' => true, 'message' => 'Dokumentation akzeptiert und Auftrag abgeschlossen.']); + self::returnJson(['success' => true, 'message' => 'Dokumentation akzeptiert und Arbeitsauftrag abgeschlossen.']); } - protected function setToProblemSolvedAction() { + protected function setToProblemSolvedAction() + { $post = json_decode(file_get_contents('php://input'), true); - - if (empty($post['workorderId']) || empty($post['text'])) { - self::sendError("Workorder ID und Text sind erforderlich."); - } + if (empty($post['workorderId']) || empty($post['text'])) self::sendError("Arbeitsauftrags-ID und Text sind erforderlich."); $workorder = RMLWorkorderModel::get($post['workorderId']); - - if (!$workorder) { - self::sendError("Workorder nicht gefunden."); - } - - if ($workorder->status !== 'intervention_required') { - self::sendError("Der Auftrag muss den Status 'Eingriff benötigt' haben, um als Problem gelöst markiert zu werden."); - } + if (!$workorder) self::sendError("Arbeitsauftrag nicht gefunden."); + if ($workorder->status !== 'intervention_required') self::sendError("Der Arbeitsauftrag muss den Status 'Eingriff erforderlich' haben, um als gelöst markiert zu werden."); $oldStatus = $workorder->status; $workorder->status = 'problem_solved'; RMLWorkorderModel::update((array)$workorder); + RMLWorkorderJournalModel::create([ + 'workorderId' => $workorder->id, 'text' => "Problem gelöst: " . $post['text'], + 'statusChange' => $this->getStatusText($oldStatus) . " -> " . $this->getStatusText('problem_solved'), + 'create' => time(), 'createBy' => $this->user->id, + ]); + self::returnJson(['success' => true, 'message' => 'Arbeitsauftrag als "Problem gelöst" markiert.']); + } + + protected function updateAdditionalInfoAction() + { + $post = json_decode(file_get_contents('php://input'), true); + if (empty($post['workorderId'])) self::sendError("Arbeitsauftrags-ID fehlt."); + $workorder = RMLWorkorderModel::get($post['workorderId']); + if (!$workorder) self::sendError("Arbeitsauftrag nicht gefunden."); + + $oldInfo = $workorder->additionalInfo; + $workorder->additionalInfo = $post['additionalInfo'] ?? null; + RMLWorkorderModel::update((array)$workorder); RMLWorkorderJournalModel::create([ - 'workorderId' => $workorder->id, - 'text' => "Problem behoben: " . $post['text'], - 'statusChange' => $this->getStatusText($oldStatus) . " -> " . $this->getStatusText('problem_solved'), - 'create' => time(), - 'createBy' => $this->user->id, + 'workorderId' => $workorder->id, 'text' => "Zusatzinfo geändert.\nAlt: '{$oldInfo}'\nNeu: '{$workorder->additionalInfo}'", + 'create' => time(), 'createBy' => $this->user->id, + ]); + self::returnJson(['success' => true, 'message' => 'Zusatzinfo aktualisiert.']); + } + + protected function cancelWorkorderAction() + { + $post = json_decode(file_get_contents('php://input'), true); + if (empty($post['workorderId'])) self::sendError("Arbeitsauftrags-ID fehlt."); + $workorder = RMLWorkorderModel::get($post['workorderId']); + if (!$workorder) self::sendError("Arbeitsauftrag nicht gefunden."); + + $oldStatus = $workorder->status; + $workorder->status = 'cancelled'; + RMLWorkorderModel::update((array)$workorder); + + RMLWorkorderJournalModel::create([ + 'workorderId' => $workorder->id, 'text' => 'Arbeitsauftrag wurde storniert.' . (!empty($post['reason']) ? ' Grund: ' . $post['reason'] : ''), + 'statusChange' => $this->getStatusText($oldStatus) . " -> " . $this->getStatusText('cancelled'), + 'create' => time(), 'createBy' => $this->user->id, ]); - self::returnJson(['success' => true, 'message' => 'Auftrag als Problem gelöst markiert.']); + $preorder = new Preorder($workorder->preorderId); + if ($preorder->id) { + $preorder->status_id = 99; + $preorder->edit_by = $this->user->id; + $preorder->save(); + } + + self::returnJson(['success' => true, 'message' => 'Arbeitsauftrag wurde storniert.']); } } \ No newline at end of file diff --git a/application/RMLWorkorderCompany/RMLWorkorderCompanyController.php b/application/RMLWorkorderCompany/RMLWorkorderCompanyController.php index fde96e200..90d049526 100644 --- a/application/RMLWorkorderCompany/RMLWorkorderCompanyController.php +++ b/application/RMLWorkorderCompany/RMLWorkorderCompanyController.php @@ -7,50 +7,47 @@ class RMLWorkorderCompanyController extends TTCrud protected bool $createText = false; protected array $permissionCheck = ['RMLCompany']; protected array $columns = [ - ['key' => 'id', 'text' => 'Auftrag-Nr.', 'table' => ['sortable' => true]], - ['key' => 'preorderInfo', 'text' => 'Kunde / Projekt', 'modal' => false, 'table' => ['sortable' => false]], + ['key' => 'id', 'text' => 'Auftrags-Nr.', 'table' => ['sortable' => true]], + ['key' => 'preorderInfo', 'text' => 'Kunde', 'modal' => false, 'table' => ['sortable' => false]], ['key' => 'rimo_fcp_name', 'text' => 'FCP', 'modal' => false, 'table' => ['sortable' => false]], ['key' => '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' => 'Terminiert', 'icon' => 'fas fa-calendar-check text-warning'], + ['value' => 'scheduled', 'text' => 'Geplant', 'icon' => 'fas fa-calendar-check text-warning'], ['value' => 'correction_requested', 'text' => 'Korrektur angefordert', 'icon' => 'fas fa-exclamation-triangle text-danger'], - ['value' => 'intervention_required', 'text' => 'Eingriff benötigt', 'icon' => 'fas fa-times-circle text-danger'], - ['value' => 'problem_solved', 'text' => 'Problem behoben', 'icon' => 'fas fa-check-circle text-success'], + ['value' => 'intervention_required', 'text' => 'Eingriff erforderlich', 'icon' => 'fas fa-times-circle text-danger'], + ['value' => 'problem_solved', 'text' => 'Problem gelöst', 'icon' => 'fas fa-check-circle text-success'], ['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'], ]]], - ['key' => 'deadlineDate', 'text' => 'Deadline', 'modal' => false, 'table' => ['filter' => 'date']], - ['key' => 'appointmentDate', 'text' => 'Termin', 'modal' => false, 'table' => ['filter' => 'date']], + ['key' => 'additionalInfo', 'text' => 'Notiz', 'modal' => false, 'table' => ['sortable' => true]], + ['key' => 'deadlineDate', 'text' => 'Deadline', 'modal' => false, 'table' => ['filter' => 'date', 'sortable' => true]], + ['key' => 'appointmentDate', 'text' => 'Termin', 'modal' => false, 'table' => ['filter' => 'date', 'sortable' => true]], ]; protected array $additionalJSVariables = ['COMPANY_ID' => '0']; - private function getStatusText(string $statusKey): string { - $statusColumn = null; + private function getStatusText(string $statusKey): string + { foreach ($this->columns as $column) { if ($column['key'] === 'status') { - $statusColumn = $column; - break; - } - } - - if ($statusColumn) { - foreach ($statusColumn['table']['filterOptions'] as $option) { - if ($option['value'] === $statusKey) { - return $option['text']; + foreach ($column['table']['filterOptions'] as $option) { + if ($option['value'] === $statusKey) { + return $option['text']; + } } } } return ucfirst(str_replace('_', ' ', $statusKey)); // Fallback } - protected function prepareCrudConfig() { + protected function prepareCrudConfig() + { $company = RMLWorkorderCompanyModel::getFirst(['addressId' => $this->user->address_id]); if ($company) { $this->additionalJSVariables['COMPANY_ID'] = $company->id; } else { - // Allow access but show no data if not associated $this->additionalJSVariables['COMPANY_ID'] = 0; } } @@ -69,7 +66,7 @@ class RMLWorkorderCompanyController extends TTCrud $json = json_decode(file_get_contents('php://input'), true); $pagination = $json['pagination'] ?? ['page' => 1, 'per_page' => 10]; $filters = $json['filters'] ?? []; - $order = $json['order'] ?? ['key' => 'deadlineDate', 'order' => 'ASC']; + $order = $json['order'] ?? []; $company = RMLWorkorderCompanyModel::getFirst(['addressId' => $this->user->address_id]); if (!$company) { @@ -81,7 +78,7 @@ class RMLWorkorderCompanyController extends TTCrud $workorders = RMLWorkorderModel::getCompanyWorkorders($filters, $pagination['per_page'], ($pagination['page'] - 1) * $pagination['per_page'], $order, $companyId); $totalCount = RMLWorkorderModel::countCompanyWorkorders($filters, $companyId); - $rows = array_map(function($workorder) { + $rows = array_map(function ($workorder) { $row = (array)$workorder; $row['preorderInfo'] = $this->getPreorderInfoTextByData($row); unset($row['customerName'], $row['customerCompany'], $row['street'], $row['hausnummer'], $row['stiege'], $row['oaid'], $row['apartment'], $row['plz'], $row['city'], $row['phone'], $row['email']); @@ -91,42 +88,35 @@ class RMLWorkorderCompanyController extends TTCrud self::returnJson([ 'rows' => $rows, 'pagination' => [ - 'page' => $pagination['page'], - 'per_page' => $pagination['per_page'], - 'total_rows' => $totalCount, - 'total_pages' => ceil($totalCount / $pagination['per_page']), + 'page' => $pagination['page'], 'per_page' => $pagination['per_page'], + 'total_rows' => $totalCount, 'total_pages' => ceil($totalCount / $pagination['per_page']), 'filtered_available' => $totalCount ] ]); } - private function getPreorderInfoTextByData($data) { + private function getPreorderInfoTextByData($data) + { $anschlussadresse = "{$data['street']} {$data['hausnummer']}"; if ($data['stiege']) $anschlussadresse .= "/{$data['stiege']}"; if ($data['apartment']) $anschlussadresse .= " / WE: {$data['apartment']}"; $anschlussadresse .= ", {$data['plz']} {$data['city']}"; - $kunde = $data['customerCompany'] ?: $data['customerName']; - - return "Kunde: {$kunde}
" . - "Anschluss: {$anschlussadresse}
" . - "Kontakt: {$data['phone']} / {$data['email']}
" . - "OAID: {$data['oaid']}"; + return "Kunde: {$kunde}
" . "Anschluss: {$anschlussadresse}
" . "Kontakt: {$data['phone']} / {$data['email']}
" . "OAID: {$data['oaid']}"; } - public function getWorkorderByIdAction() { + public function getWorkorderByIdAction() + { $id = $this->request->id; - if(!$id) self::sendError("ID missing"); - + if (!$id) self::sendError("ID fehlt"); $workorder = RMLWorkorderModel::get($id); - if(!$workorder) self::sendError("Workorder not found"); - + if (!$workorder) self::sendError("Arbeitsauftrag nicht gefunden"); $workorder->preorderInfo = $this->getPreorderInfoText($workorder->preorderId); - - self::returnJson((array) $workorder); + self::returnJson((array)$workorder); } - private function getPreorderInfoText($preorderId) { + private function getPreorderInfoText($preorderId) + { $preorder = new Preorder($preorderId); $anschlussadresse = 'N/A'; if ($preorder->adb_hausnummer_id) { @@ -136,102 +126,73 @@ class RMLWorkorderCompanyController extends TTCrud if ($preorder->adb_wohneinheit_id) $anschlussadresse .= " / WE: {$preorder->adb_wohneinheit->bezeichner}"; $anschlussadresse .= ", {$hn->plz->plz} {$hn->ortschaft->name}"; } - $kunde = ($preorder->company) ?: "{$preorder->firstname} {$preorder->lastname}"; - - return "Kunde: {$kunde}
" . - "Anschluss: {$anschlussadresse}
" . - "Kontakt: {$preorder->phone} / {$preorder->email}
" . - "OAID: {$preorder->oaid}"; + return "Kunde: {$kunde}
" . "Anschluss: {$anschlussadresse}
" . "Kontakt: {$preorder->phone} / {$preorder->email}
" . "OAID: {$preorder->oaid}"; } - protected function scheduleAppointmentAction() { + protected function scheduleAppointmentAction() + { $post = json_decode(file_get_contents('php://input'), true); - if (empty($post['workorderId']) || empty($post['appointmentDate'])) self::sendError("Required fields are missing."); - + if (empty($post['workorderId']) || empty($post['appointmentDate'])) self::sendError("Erforderliche Felder fehlen."); $workorder = RMLWorkorderModel::get($post['workorderId']); - if(!$workorder) self::sendError("Workorder not found"); - + if (!$workorder) self::sendError("Arbeitsauftrag nicht gefunden"); $hour = (int)date('H', $post['appointmentDate']); - if ($hour >= 23 || $hour < 1) { - self::sendError("Bitte Uhrzeit angeben!"); - } + if ($hour >= 23 || $hour < 1) self::sendError("Bitte geben Sie eine Uhrzeit an!"); $workorder->appointmentDate = $post['appointmentDate']; $workorder->status = 'scheduled'; RMLWorkorderModel::update((array)$workorder); - RMLWorkorderJournalModel::create([ - 'workorderId' => $workorder->id, - 'text' => 'Termin festgelegt auf: ' . date('d.m.Y H:i', $post['appointmentDate']), - 'create' => time(), - 'createBy' => $this->user->id, + 'workorderId' => $workorder->id, 'text' => 'Termin festgelegt auf: ' . date('d.m.Y H:i', $post['appointmentDate']), + 'create' => time(), 'createBy' => $this->user->id, ]); - self::returnJson(['success' => true, 'message' => 'Termin erfolgreich gespeichert.']); } - protected function rescheduleAppointmentAction() { + protected function rescheduleAppointmentAction() + { $post = json_decode(file_get_contents('php://input'), true); - if (empty($post['workorderId']) || empty($post['appointmentDate']) || empty($post['reason'])) { - self::sendError("Required fields are missing."); - } - + if (empty($post['workorderId']) || empty($post['appointmentDate']) || empty($post['reason'])) self::sendError("Erforderliche Felder fehlen."); $workorder = RMLWorkorderModel::get($post['workorderId']); - if(!$workorder) self::sendError("Workorder not found."); - + if (!$workorder) self::sendError("Arbeitsauftrag nicht gefunden."); $hour = (int)date('H', $post['appointmentDate']); - if ($hour >= 23 || $hour < 1) { - self::sendError("Bitte Uhrzeit angeben!"); - } + if ($hour >= 23 || $hour < 1) self::sendError("Bitte geben Sie eine Uhrzeit an!"); $oldDateFormatted = $workorder->appointmentDate ? date('d.m.Y H:i', $workorder->appointmentDate) : 'N/A'; $newDateFormatted = date('d.m.Y H:i', $post['appointmentDate']); - $workorder->appointmentDate = $post['appointmentDate']; RMLWorkorderModel::update((array)$workorder); - RMLWorkorderJournalModel::create([ - 'workorderId' => $workorder->id, - 'text' => "Termin verschoben von {$oldDateFormatted} auf {$newDateFormatted}. Grund: " . $post['reason'], - 'create' => time(), - 'createBy' => $this->user->id, + 'workorderId' => $workorder->id, 'text' => "Termin verschoben von {$oldDateFormatted} auf {$newDateFormatted}. Grund: " . $post['reason'], + 'create' => time(), 'createBy' => $this->user->id, ]); - self::returnJson(['success' => true, 'message' => 'Termin erfolgreich verschoben.']); } - protected function requestInterventionAction() { + protected function requestInterventionAction() + { $post = json_decode(file_get_contents('php://input'), true); - if (empty($post['workorderId']) || empty($post['journalText'])) { - self::sendError("Required fields are missing."); - } - + if (empty($post['workorderId']) || empty($post['journalText'])) self::sendError("Erforderliche Felder fehlen."); $workorder = RMLWorkorderModel::get($post['workorderId']); - if(!$workorder) self::sendError("Workorder not found."); + if (!$workorder) self::sendError("Arbeitsauftrag nicht gefunden."); $oldStatus = $workorder->status; $workorder->status = 'intervention_required'; RMLWorkorderModel::update((array)$workorder); - RMLWorkorderJournalModel::create([ - 'workorderId' => $workorder->id, - 'text' => "Eingriff benötigt: " . $post['journalText'], + 'workorderId' => $workorder->id, 'text' => "Eingriff erforderlich: " . $post['journalText'], 'statusChange' => $this->getStatusText($oldStatus) . " -> " . $this->getStatusText('intervention_required'), - 'create' => time(), - 'createBy' => $this->user->id, + 'create' => time(), 'createBy' => $this->user->id, ]); - self::returnJson(['success' => true, 'message' => 'Eingriff wurde angefordert.']); } protected function uploadDocumentationAction() { if (empty($_FILES['files']) || empty($_POST['workorderId'])) { - self::returnJson(['error' => 'Required data is missing.']); + self::returnJson(['error' => 'Erforderliche Daten fehlen.']); return; } - $workorderId = $_POST['workorderId']; $description = $_POST['description'] ?? ''; $documentType = $_POST['documentType'] ?? 'general'; @@ -240,27 +201,13 @@ class RMLWorkorderCompanyController extends TTCrud foreach ($files['name'] as $index => $name) { if ($files['error'][$index] === UPLOAD_ERR_OK) { - $_FILES['file'] = [ - 'name' => $files['name'][$index], - 'type' => $files['type'][$index], - 'tmp_name' => $files['tmp_name'][$index], - 'error' => $files['error'][$index], - 'size' => $files['size'][$index] - ]; - + $_FILES['file'] = ['name' => $files['name'][$index], 'type' => $files['type'][$index], 'tmp_name' => $files['tmp_name'][$index], 'error' => $files['error'][$index], 'size' => $files['size'][$index]]; try { $uploaded = mfUpload::handleFormUpload("file", false, "/RMLWorkorder"); - RMLWorkorderDocumentationModel::create([ - 'workorderId' => $workorderId, - 'fileId' => $uploaded->id, - 'description' => $description, - 'documentType' => $documentType, - 'create' => time(), - 'createBy' => $this->user->id - ]); + RMLWorkorderDocumentationModel::create(['workorderId' => $workorderId, 'fileId' => $uploaded->id, 'description' => $description, 'documentType' => $documentType, 'create' => time(), 'createBy' => $this->user->id]); $uploadCount++; } catch (Exception $e) { - error_log("File upload failed for $name: " . $e->getMessage()); + error_log("Dateiupload für $name fehlgeschlagen: " . $e->getMessage()); } } } @@ -271,184 +218,136 @@ class RMLWorkorderCompanyController extends TTCrud RMLWorkorderModel::update((array)$workorder); $workorder = RMLWorkorderModel::get($workorderId); } - $formattedDocs = $this->getFormattedDocs($workorderId); - - self::returnJson([ - 'success' => true, - 'message' => "$uploadCount Datei(en) erfolgreich hochgeladen.", - 'docs' => $formattedDocs, - 'workorder' => (array)$workorder - ]); + self::returnJson(['success' => true, 'message' => "$uploadCount Datei(en) erfolgreich hochgeladen.", 'docs' => $formattedDocs, 'workorder' => (array)$workorder]); } - private function getFormattedDocs($workorderId) { + private function getFormattedDocs($workorderId) + { $docs = RMLWorkorderDocumentationModel::getAll(['workorderId' => $workorderId], null, 0, ['key' => 'create', 'order' => 'ASC']); $responseDocs = []; $typeCounts = []; - $translationMap = [ - 'photo_hup_mounted' => 'Foto_montierter_HÜP', - 'photo_hup_open' => 'Foto_offener_HÜP', - 'photo_splice_cassette_hup' => 'Foto_Spleißkassette_HÜP', - 'photo_splice_cassette_fcp' => 'Foto_Spleißkassette_FCP', - 'photo_hup_closed_stickers' => 'Foto_geschlossener_HÜP_mit_Aufklebern', - 'photo_fcp_labeled' => 'Foto_FCP_beschriftet', - 'photo_patch_position_osp' => 'Foto_Patch-Position_OSP-Seite', - 'photo_patch_position_anb' => 'Foto_Patch-Position_ANB-Seite', - 'measurement_protocol_otdr' => 'ODTR_Messung', - 'other' => 'Sonstiges_Dokument' + 'photo_hup_mounted' => 'Foto_montierter_HÜP', 'photo_hup_open' => 'Foto_offener_HÜP', + 'photo_splice_cassette_hup' => 'Foto_Spleißkassette_HÜP', 'photo_splice_cassette_fcp' => 'Foto_Spleißkassette_FCP', + 'photo_hup_closed_stickers' => 'Foto_geschlossener_HÜP_mit_Aufklebern', 'photo_fcp_labeled' => 'Foto_FCP_beschriftet', + 'photo_patch_position_osp' => 'Foto_Patch-Position_OSP-Seite', 'photo_patch_position_anb' => 'Foto_Patch-Position_ANB-Seite', + 'measurement_protocol_otdr' => 'ODTR_Messung', 'other' => 'Sonstiges_Dokument' ]; - - foreach($docs as $doc) { + 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); $translatedType = $translationMap[$documentTypeKey] ?? $documentTypeKey; $newFilename = "{$translatedType}_{$typeCounts[$documentTypeKey]}." . strtolower($extension); - - $responseDocs[] = [ - 'id' => $doc->id, - 'fileId' => $doc->fileId, - 'fileName' => $newFilename, - 'description' => $doc->description, - 'documentType' => $documentTypeKey, - 'mimetype' => $file->mimetype, - ]; + $responseDocs[] = ['id' => $doc->id, 'fileId' => $doc->fileId, 'fileName' => $newFilename, 'description' => $doc->description, 'documentType' => $documentTypeKey, 'mimetype' => $file->mimetype]; } return $responseDocs; } - - protected function getDocumentationAction() { - if(empty($this->request->workorderId)) self::sendError("Workorder ID missing."); - + protected function getDocumentationAction() + { + if (empty($this->request->workorderId)) self::sendError("Arbeitsauftrags-ID fehlt."); $docs = $this->getFormattedDocs($this->request->workorderId); - - $journals = array_map( - function ($j) { - $j->createByName = UserModel::getOne($j->createBy)->getAbbrName(); - return (array)$j; - }, + $journals = array_map(function ($j) { + $j->createByName = UserModel::getOne($j->createBy)->getAbbrName(); + return (array)$j; + }, RMLWorkorderJournalModel::getAll(['workorderId' => $this->request->workorderId], null, 0, ['key' => 'create', 'order' => 'DESC']) ); - self::returnJson(['docs' => $docs, 'journals' => $journals]); } - protected function completeWorkorderAction() { + + protected function completeWorkorderAction() + { $post = json_decode(file_get_contents('php://input'), true); - if(empty($post['workorderId'])) self::sendError("Workorder ID missing."); - + if (empty($post['workorderId'])) self::sendError("Arbeitsauftrags-ID fehlt."); $workorder = RMLWorkorderModel::get($post['workorderId']); - if(!$workorder) self::sendError("Workorder not found."); - + if (!$workorder) self::sendError("Arbeitsauftrag nicht gefunden."); $workorder->status = 'documented'; RMLWorkorderModel::update((array)$workorder); - - self::returnJson(['success' => true, 'message' => 'Auftrag abgeschlossen.']); + self::returnJson(['success' => true, 'message' => 'Arbeitsauftrag abgeschlossen.']); } - protected function deleteDocumentationAction() { + protected function deleteDocumentationAction() + { $post = json_decode(file_get_contents('php://input'), true); - if (empty($post['id'])) self::sendError("Document ID missing."); - - $doc = RMLWorkorderDocumentationModel::get($post['id']); - if (!$doc) self::sendError("Document not found."); - $workorderId = $doc->workorderId; - - RMLWorkorderDocumentationModel::delete($post['id']); - - $formattedDocs = $this->getFormattedDocs($workorderId); - - self::returnJson([ - 'success' => true, - 'message' => 'Dokument gelöscht.', - 'docs' => $formattedDocs - ]); - } - - protected function updateDocumentationAction() { - $post = json_decode(file_get_contents('php://input'), true); - if (empty($post['id'])) self::sendError("Document ID missing."); - + if (empty($post['id'])) self::sendError("Dokumenten-ID fehlt."); $doc = RMLWorkorderDocumentationModel::get($post['id']); if (!$doc) self::sendError("Dokument nicht gefunden."); - - if (isset($post['documentType'])) { - $doc->documentType = $post['documentType']; - } - - RMLWorkorderDocumentationModel::update((array)$doc); - - $formattedDocs = $this->getFormattedDocs($doc->workorderId); - - self::returnJson([ - 'success' => true, - 'message' => 'Dokument aktualisiert.', - 'docs' => $formattedDocs - ]); + $workorderId = $doc->workorderId; + RMLWorkorderDocumentationModel::delete($post['id']); + $formattedDocs = $this->getFormattedDocs($workorderId); + self::returnJson(['success' => true, 'message' => 'Dokument gelöscht.', 'docs' => $formattedDocs]); } - protected function addJournalAction() { + protected function updateDocumentationAction() + { $post = json_decode(file_get_contents('php://input'), true); + if (empty($post['id'])) self::sendError("Dokumenten-ID fehlt."); + $doc = RMLWorkorderDocumentationModel::get($post['id']); + if (!$doc) self::sendError("Dokument nicht gefunden."); + if (isset($post['documentType'])) $doc->documentType = $post['documentType']; + RMLWorkorderDocumentationModel::update((array)$doc); + $formattedDocs = $this->getFormattedDocs($doc->workorderId); + self::returnJson(['success' => true, 'message' => 'Dokument aktualisiert.', 'docs' => $formattedDocs]); + } - if (empty($post['workorderId']) || empty(trim($post['text']))) { - self::sendError("Workorder ID and text are required."); - } - - RMLWorkorderJournalModel::create([ - 'workorderId' => $post['workorderId'], - 'text' => $post['text'], - 'createBy' => $this->user->id, - 'create' => time(), - ]); - - $journals = array_map( - function ($j) { - $j->createByName = UserModel::getOne($j->createBy)->getAbbrName(); - return (array)$j; - }, + protected function addJournalAction() + { + $post = json_decode(file_get_contents('php://input'), true); + if (empty($post['workorderId']) || empty(trim($post['text']))) self::sendError("Arbeitsauftrags-ID und Text sind erforderlich."); + RMLWorkorderJournalModel::create(['workorderId' => $post['workorderId'], 'text' => $post['text'], 'createBy' => $this->user->id, 'create' => time()]); + $journals = array_map(function ($j) { + $j->createByName = UserModel::getOne($j->createBy)->getAbbrName(); + return (array)$j; + }, RMLWorkorderJournalModel::getAll(['workorderId' => $post['workorderId']], null, 0, ['key' => 'create', 'order' => 'DESC']) ); - - self::returnJson([ - 'success' => true, - 'message' => 'Journal-Eintrag hinzugefügt.', - 'journals' => $journals - ]); + self::returnJson(['success' => true, 'message' => 'Journaleintrag hinzugefügt.', 'journals' => $journals]); } - protected function getTenantConfigAction() { - if(empty($this->request->workorderId)) self::sendError("Workorder ID missing."); - + protected function getTenantConfigAction() + { + if (empty($this->request->workorderId)) self::sendError("Arbeitsauftrags-ID fehlt."); $workorder = RMLWorkorderModel::get($this->request->workorderId); - if (!$workorder) self::sendError("Workorder not found."); - + if (!$workorder) self::sendError("Arbeitsauftrag nicht gefunden."); $preorder = new Preorder($workorder->preorderId); - if (!$preorder->id) self::sendError("Preorder not found."); - + if (!$preorder->id) self::sendError("Vorbestellung nicht gefunden."); $campaign = new Preordercampaign($preorder->preordercampaign_id); - if (!$campaign->id) self::sendError("Campaign not found."); - + if (!$campaign->id) self::sendError("Kampagne nicht gefunden."); $network = NetworkModel::getOne($campaign->network_id); - if (!$network) self::sendError("Network not found."); - + if (!$network) self::sendError("Netzwerk nicht gefunden."); $tenantId = $network->owner_id; - - $tenantConfig = RMLWorkorderTenantConfigModel::getFirst(['addressId' => $tenantId]); + $tenantConfig = RMLWorkorderTenantConfigModel::getFirst(['addressId' => $tenantId]) ?? RMLWorkorderTenantConfigModel::getFirst(['addressId' => 4807]); if (!$tenantConfig) { - $tenantConfig = RMLWorkorderTenantConfigModel::getFirst(['addressId' => 4807]); // RML Default - } - - if (!$tenantConfig) { - self::returnJson(['success' => false, 'message' => 'No tenant config found.']); + self::returnJson(['success' => false, 'message' => 'Keine Mandantenkonfiguration gefunden.']); return; } - self::returnJson(['success' => true, 'documentationTypes' => json_decode($tenantConfig->documentationTypes, true)]); } + + protected function updateAdditionalInfoAction() + { + $post = json_decode(file_get_contents('php://input'), true); + if (empty($post['workorderId'])) self::sendError("Arbeitsauftrags-ID fehlt."); + + $company = RMLWorkorderCompanyModel::getFirst(['addressId' => $this->user->address_id]); + if (!$company) self::sendError("Firma nicht gefunden."); + + $workorder = RMLWorkorderModel::get($post['workorderId']); + if (!$workorder || $workorder->companyId !== $company->id) self::sendError("Arbeitsauftrag nicht gefunden oder nicht Ihrer Firma zugewiesen."); + + $oldInfo = $workorder->additionalInfo; + $workorder->additionalInfo = $post['additionalInfo'] ?? null; + RMLWorkorderModel::update((array)$workorder); + + RMLWorkorderJournalModel::create([ + 'workorderId' => $workorder->id, 'text' => "Zusatzinfo geändert.\nAlt: '{$oldInfo}'\nNeu: '{$workorder->additionalInfo}'", + 'create' => time(), 'createBy' => $this->user->id, + ]); + self::returnJson(['success' => true, 'message' => 'Zusatzinfo aktualisiert.']); + } } \ No newline at end of file diff --git a/db/migrations/20250829090000_rmlworkorder_add_additional_info.php b/db/migrations/20250829090000_rmlworkorder_add_additional_info.php new file mode 100644 index 000000000..a8692e881 --- /dev/null +++ b/db/migrations/20250829090000_rmlworkorder_add_additional_info.php @@ -0,0 +1,53 @@ +table('RMLWorkorder'); + $table->addColumn('additionalInfo', 'text', [ + 'null' => true, + 'default' => null, + ]) + ->changeColumn('status', 'enum', [ + 'values' => [ + 'new', + 'assigned', + 'scheduled', + 'correction_requested', + 'documented', + 'completed', + 'intervention_required', + 'problem_solved', + 'cancelled', + ], + 'null' => false, + 'default' => 'new', + ]) + ->save(); + } + + public function down(): void + { + $table = $this->table('RMLWorkorder'); + $table->removeColumn('additionalInfo') + ->changeColumn('status', 'enum', [ + 'values' => [ + 'new', + 'assigned', + 'scheduled', + 'correction_requested', + 'documented', + 'completed', + 'cancelled', + ], + 'null' => false, + 'default' => 'new', + ]) + ->save(); + } +} diff --git a/public/js/pages/RMLWorkorderAdmin/RMLWorkorderAdmin.js b/public/js/pages/RMLWorkorderAdmin/RMLWorkorderAdmin.js index 7dab9feda..06963ffb9 100644 --- a/public/js/pages/RMLWorkorderAdmin/RMLWorkorderAdmin.js +++ b/public/js/pages/RMLWorkorderAdmin/RMLWorkorderAdmin.js @@ -23,23 +23,22 @@ Vue.component('r-m-l-workorder-admin', { > @@ -55,91 +54,115 @@ Vue.component('r-m-l-workorder-admin', { additional-class="btn-link btn-sm p-0" title="Auftrag auf Problem behoben setzen" /> - + + + +