'id', 'text' => 'Auftrags-Nr.', 'table' => ['sortable' => true]], ['key' => 'networkOwnerName', 'text' => 'Auftraggeber', 'table' => ['sortable' => false]], ['key' => 'preordercampaign_id', 'text' => 'Kampagne', 'modal' => false, 'table' => ['filter' => 'select', 'sortable' => true]], ['key' => 'preorderInfo', 'text' => 'Kunde', 'modal' => false, 'table' => ['sortable' => false]], ['key' => 'rimo_fcp_name', 'text' => 'FCP', 'modal' => false, 'table' => ['sortable' => true]], // Status column is now inherited via prepareCrudConfig ['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', 'RML_COMPANY_MANAGER' => false]; protected function prepareCrudConfig() { $preorderInfoColIdx = array_search('preorderInfo', array_column($this->columns, 'key')); array_splice($this->columns, $preorderInfoColIdx + 1, 0, [$this->statusColumn]); $company = WorkorderCompanyModel::getFirst(['addressId' => $this->user->address_id]); $campaigns = Helper::getPreorderCampaignFromUser($this->user, true); $preCamColIdx = array_search('preordercampaign_id', array_column($this->columns, 'key')); if ($preCamColIdx !== false) { $this->columns[$preCamColIdx]['table']['filterOptions'] = array_map(fn($c) => ['value' => $c->id, 'text' => $c->name], $campaigns); if (!$this->user->isAdmin() && count($campaigns) === 1) { $this->columns[$preCamColIdx]['table']['defaultFilter'] = $campaigns[0]->id; } } $this->additionalJSVariables['COMPANY_ID'] = $company ? $company->id : 0; if ($this->user->can('RMLCompanyManager')) { $this->additionalJSVariables['RML_COMPANY_MANAGER'] = true; } } public function indexAction() { $this->archiveWorkorders(); parent::indexAction(); } protected function logout() { mfLoginController::staticLogout(); $this->redirect('/WorkorderCompany/Mobile'); } public function mobileAction() { $company = WorkorderCompanyModel::getFirst(['addressId' => $this->user->address_id]); $this->layout()->setTemplate("VueViews/WorkorderCompanyPWA"); $this->layout()->set("JSGlobals", [ 'BASE_PATH' => '/WorkorderCompany', 'COMPANY_ID' => $company ? $company->id : 0, ]); } protected function getAction() { $pagination = $this->postData['pagination'] ?? ['page' => 1, 'per_page' => 10]; $filters = $this->postData['filters'] ?? []; $order = $this->postData['order'] ?? []; $company = WorkorderCompanyModel::getFirst(['addressId' => $this->user->address_id]); if (!$company) { self::returnJson(['rows' => [], 'pagination' => array_merge($pagination, ['total_rows' => 0, 'total_pages' => 0, 'filtered_available' => 0])]); return; } $workorders = WorkorderModel::getCompanyWorkorders($filters, $pagination['per_page'], ($pagination['page'] - 1) * $pagination['per_page'], $order, $company->id); $totalCount = WorkorderModel::countCompanyWorkorders($filters, $company->id); self::returnJson([ 'rows' => $workorders, 'pagination' => ['page' => $pagination['page'], 'per_page' => $pagination['per_page'], 'total_rows' => $totalCount, 'total_pages' => ceil($totalCount / $pagination['per_page']), 'filtered_available' => $totalCount] ]); } public function getWorkorderByIdAction() { if (empty($this->request->id)) self::sendError("ID fehlt"); $workorder = WorkorderModel::get($this->request->id); if (!$workorder) self::sendError("Arbeitsauftrag nicht gefunden"); self::returnJson((array)$workorder); } protected function scheduleAppointmentAction() { if (empty($this->postData['workorderId']) || empty($this->postData['appointmentDate'])) self::sendError("Erforderliche Felder fehlen."); $workorder = WorkorderModel::get($this->postData['workorderId']); if (!$workorder) self::sendError("Arbeitsauftrag nicht gefunden"); if ((int)date('H', $this->postData['appointmentDate']) >= 23 || (int)date('H', $this->postData['appointmentDate']) < 1) self::sendError("Bitte geben Sie eine Uhrzeit an!"); $workorder->appointmentDate = $this->postData['appointmentDate']; $workorder->status = 'scheduled'; WorkorderModel::update((array)$workorder); WorkorderJournalModel::create([ 'workorderId' => $workorder->id, 'text' => 'Termin festgelegt auf: ' . date('d.m.Y H:i', $this->postData['appointmentDate']), 'create' => time(), 'createBy' => $this->user->id, ]); self::returnJson(['success' => true, 'message' => 'Termin erfolgreich gespeichert.']); } protected function rescheduleAppointmentAction() { if (empty($this->postData['workorderId']) || empty($this->postData['appointmentDate']) || empty($this->postData['reason'])) self::sendError("Erforderliche Felder fehlen."); $workorder = WorkorderModel::get($this->postData['workorderId']); if (!$workorder) self::sendError("Arbeitsauftrag nicht gefunden."); if ((int)date('H', $this->postData['appointmentDate']) >= 23 || (int)date('H', $this->postData['appointmentDate']) < 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', $this->postData['appointmentDate']); $workorder->appointmentDate = $this->postData['appointmentDate']; WorkorderModel::update((array)$workorder); WorkorderJournalModel::create([ 'workorderId' => $workorder->id, 'text' => "Termin verschoben von {$oldDateFormatted} auf {$newDateFormatted}. Grund: " . $this->postData['reason'], 'create' => time(), 'createBy' => $this->user->id, ]); self::returnJson(['success' => true, 'message' => 'Termin erfolgreich verschoben.']); } protected function clearAppointmentAction() { if (empty($this->postData['workorderId'])) self::sendError("Arbeitsauftrags-ID fehlt."); $workorder = WorkorderModel::get($this->postData['workorderId']); if (!$workorder) self::sendError("Arbeitsauftrag nicht gefunden."); $oldDateFormatted = $workorder->appointmentDate ? date('d.m.Y H:i', $workorder->appointmentDate) : 'N/A'; $workorder->appointmentDate = null; $workorder->status = 'assigned'; WorkorderModel::update((array)$workorder); WorkorderJournalModel::create([ 'workorderId' => $workorder->id, 'text' => "Termin gelöscht (war: {$oldDateFormatted}).", 'create' => time(), 'createBy' => $this->user->id, ]); self::returnJson(['success' => true, 'message' => 'Termin erfolgreich gelöscht.']); } protected function requestInterventionAction() { if (empty($this->postData['workorderId']) || empty($this->postData['journalText'])) self::sendError("Erforderliche Felder fehlen."); $workorder = WorkorderModel::get($this->postData['workorderId']); if (!$workorder) self::sendError("Arbeitsauftrag nicht gefunden."); $oldStatus = $workorder->status; $workorder->status = 'intervention_required'; WorkorderModel::update((array)$workorder); WorkorderJournalModel::create([ 'workorderId' => $workorder->id, 'text' => "Eingriff erforderlich: " . $this->postData['journalText'], 'statusChange' => $this->getStatusText($oldStatus) . " -> " . $this->getStatusText('intervention_required'), 'create' => time(), 'createBy' => $this->user->id, ]); self::returnJson(['success' => true, 'message' => 'Eingriff wurde angefordert.']); } protected function completeWorkorderAction() { if (empty($this->postData['workorderId'])) self::sendError("Arbeitsauftrags-ID fehlt."); $workorder = WorkorderModel::get($this->postData['workorderId']); if (!$workorder) self::sendError("Arbeitsauftrag nicht gefunden."); // START ADDED VALIDATION $tenantConfig = $this->getTenantConfigFromWorkorder($workorder->id); if ($tenantConfig) { if ($tenantConfig->requireCableLength && empty(trim($workorder->cableLength))) { self::sendError("Bitte geben Sie die Kabellänge an, um den Auftrag abzuschließen."); } if ($tenantConfig->requireCableType && empty(trim($workorder->cableType))) { self::sendError("Bitte geben Sie den Kabeltyp an, um den Auftrag abzuschließen."); } } // END ADDED VALIDATION $workorder->status = 'documented'; WorkorderModel::update((array)$workorder); self::returnJson(['success' => true, 'message' => 'Arbeitsauftrag zur Prüfung eingereicht.']); } protected function getTenantConfigAction() { $tenantConfig = $this->getTenantConfigFromWorkorder($this->request->workorderId); if (!$tenantConfig) { self::returnJson(['success' => false, 'message' => 'Keine Mandantenkonfiguration gefunden.']); return; } $response = [ 'success' => true, 'documentationTypes' => json_decode($tenantConfig->documentationTypes, true), 'civilEngineeringDocsRequired' => $tenantConfig->civilEngineeringDocsRequired, 'interventionTypes' => json_decode($tenantConfig->interventionTypes, true), 'requireCableLength' => $tenantConfig->requireCableLength, 'requireCableType' => $tenantConfig->requireCableType, 'showTechnicalData' => (bool)$tenantConfig->showTechnicalData, 'tiefbauSeesNormalDocs' => (bool)$tenantConfig->tiefbauSeesNormalDocs, ]; if ($tenantConfig->showTechnicalData) { $response['technicalData'] = $this->getTechnicalData((int)$this->request->workorderId); } self::returnJson($response); } protected function uploadDocumentationAction() { if (empty($_FILES['files']) || empty($_POST['workorderId'])) self::sendError('Erforderliche Daten fehlen.'); $workorderId = $_POST['workorderId']; foreach ($_FILES['files']['name'] as $index => $name) { if ($_FILES['files']['error'][$index] === UPLOAD_ERR_OK) { $_FILES['file'] = ['name' => $name, 'type' => $_FILES['files']['type'][$index], 'tmp_name' => $_FILES['files']['tmp_name'][$index], 'error' => $_FILES['files']['error'][$index], 'size' => $_FILES['files']['size'][$index]]; try { $uploaded = mfUpload::handleFormUpload("file", false, "/Workorder"); WorkorderDocumentationModel::create(['workorderId' => $workorderId, 'fileId' => $uploaded->id, 'description' => $_POST['description'] ?? '', 'documentType' => $_POST['documentType'] ?? 'general', 'create' => time(), 'createBy' => $this->user->id]); } catch (Exception $e) { /* Log error if necessary */ } } } $workorder = WorkorderModel::get($workorderId); $oldStatus = $workorder->status; $newStatus = null; if (in_array($oldStatus, ['assigned', 'scheduled'])) { $newStatus = 'in_progress'; } else if (in_array($oldStatus, ['correction_requested', 'problem_solved', 'civil_engineering_completed'])) { $newStatus = 'assigned'; } if ($newStatus) { $workorder->status = $newStatus; WorkorderModel::update((array)$workorder); WorkorderJournalModel::create([ 'workorderId' => $workorder->id, 'text' => 'Status wurde nach Dokumenten-Upload automatisch auf In Beearbeitung gesetzt.', 'statusChange' => $this->getStatusText($oldStatus) . " -> " . $this->getStatusText($newStatus), 'create' => time(), 'createBy' => $this->user->id, ]); } self::returnJson(['success' => true, 'message' => "Datei(en) erfolgreich hochgeladen."]); } protected function deleteDocumentationAction() { if (empty($this->postData['id'])) self::sendError("Dokumenten-ID fehlt."); WorkorderDocumentationModel::delete($this->postData['id']); self::returnJson(['success' => true, 'message' => 'Dokument gelöscht.']); } protected function updateDocumentationAction() { if (empty($this->postData['id'])) self::sendError("Dokumenten-ID fehlt."); $doc = WorkorderDocumentationModel::get($this->postData['id']); if (!$doc) self::sendError("Dokument nicht gefunden."); if (isset($this->postData['documentType'])) $doc->documentType = $this->postData['documentType']; WorkorderModel::update((array)$doc); self::returnJson(['success' => true, 'message' => 'Dokument aktualisiert.']); } protected function completeCivilEngineeringAction() { if (empty($this->postData['workorderId'])) self::sendError("Arbeitsauftrags-ID fehlt."); $workorder = WorkorderModel::get($this->postData['workorderId']); if (!$workorder) self::sendError("Arbeitsauftrag nicht gefunden."); // Re-assign to original company if ($workorder->originalCompanyId) { $workorder->companyId = $workorder->originalCompanyId; $workorder->originalCompanyId = null; } $oldStatus = $workorder->status; $workorder->civilEngineeringCompanyId = null; $workorder->status = 'civil_engineering_completed'; WorkorderModel::update((array)$workorder); WorkorderJournalModel::create([ 'workorderId' => $workorder->id, 'text' => "Tiefbau abgeschlossen.", 'statusChange' => $this->getStatusText($oldStatus) . " -> " . $this->getStatusText('civil_engineering_completed'), 'create' => time(), 'createBy' => $this->user->id, ]); self::returnJson(['success' => true, 'message' => 'Tiefbau erfolgreich abgeschlossen.']); } protected function updateWorkorderDataAction() { if (empty($this->postData['workorderId'])) self::sendError("Arbeitsauftrags-ID fehlt."); $workorder = WorkorderModel::get($this->postData['workorderId']); if (!$workorder) self::sendError("Arbeitsauftrag nicht gefunden."); $journalText = "Zusatzdaten aktualisiert:\n"; $changed = false; if (isset($this->postData['cableLength'])) { if ($workorder->cableLength != $this.postData['cableLength']) { $journalText .= "Kabellänge: '{$workorder->cableLength}' -> '{$this->postData['cableLength']}'\n"; $workorder->cableLength = $this.postData['cableLength']; $changed = true; } } if (isset($this->postData['cableType'])) { if ($workorder->cableType != $this.postData['cableType']) { $journalText .= "Kabeltyp: '{$workorder->cableType}' -> '{$this->postData['cableType']}'\n"; $workorder->cableType = $this.postData['cableType']; $changed = true; } } if (!$changed) { self::returnJson(['success' => true, 'message' => 'Keine Änderungen vorgenommen.']); return; } WorkorderModel::update((array)$workorder); WorkorderJournalModel::create([ 'workorderId' => $workorder->id, 'text' => $journalText, 'create' => time(), 'createBy' => $this->user->id, ]); // Re-fetch journals to return $journals = WorkorderJournalModel::getAll(['workorderId' => intval($workorder->id)], null, 0, ['key' => 'create', 'order' => 'DESC']); foreach ($journals as $journal) { $journal->createByName = UserModel::getOne($journal->createBy)->name ?? 'Unbekannt'; } self::returnJson(['success' => true, 'message' => 'Daten gespeichert.', 'journals' => $journals]); } //endregion }