From 6ab41a9169815afebb30c08b385730645652aec5 Mon Sep 17 00:00:00 2001 From: Luca Haid Date: Sun, 18 Jan 2026 17:42:11 +0000 Subject: [PATCH] added technical data to xinon workorder and workordermph now has a unassign button --- .../ManualInvoice/ManualInvoiceController.php | 65 ++++++++++ .../Workorder/Workorder/WorkorderHandler.php | 54 ++++++++- .../RimoWorkorder/RimoWorkorderController.php | 8 +- .../WorkorderBase/WorkorderBaseController.php | 45 +++++++ .../WorkorderCompanyController.php | 14 ++- .../WorkorderMphAdminController.php | 34 ++++++ .../WorkorderTenantConfigModel.php | 1 + ...d_show_technical_data_to_tenant_config.php | 26 ++++ .../js/pages/ManualInvoice/ManualInvoice.js | 94 +++++++++++++-- .../js/pages/WorkorderBase/WorkorderBase.js | 44 ++++++- .../WorkorderMphAdmin/WorkorderMphAdmin.js | 29 ++++- .../WorkorderTenantConfig.js | 4 + .../modules/workorder/WorkorderModule.js | 114 +++++++++++++++++- 13 files changed, 508 insertions(+), 24 deletions(-) create mode 100644 db/migrations/20260118120000_add_show_technical_data_to_tenant_config.php diff --git a/application/ManualInvoice/ManualInvoiceController.php b/application/ManualInvoice/ManualInvoiceController.php index f2225af5e..497057215 100644 --- a/application/ManualInvoice/ManualInvoiceController.php +++ b/application/ManualInvoice/ManualInvoiceController.php @@ -826,4 +826,69 @@ class ManualInvoiceController extends TTCrud 'vatrate' => $vatrate->rate ]); } + + protected function getCustomerBillingInfoAction() { + $addressId = $_GET['address_id'] ?? null; + $vatgroupId = $_GET['vatgroup_id'] ?? 2; + + if (!$addressId) { + self::returnJson(['success' => false, 'message' => 'Address ID required']); + return; + } + + $address = new Address($addressId); + if (!$address->id) { + self::returnJson(['success' => false, 'message' => 'Address not found']); + return; + } + + $vatarea = 'domestic'; + if ($address->country_id) { + $country = new Country($address->country_id); + if ($country->id && $country->isocode != TT_HOMECOUNTRY_ISOCODE) { + $vatarea = $country->is_eu ? 'eu' : 'other'; + } + } + + if ($address->uid && substr(strtolower(preg_replace('/[^a-z0-9]/i', '', $address->uid)), 0, 3) == 'atu') { + $vatarea = 'domestic'; + } + + $vatrate = VatrateModel::getFirst(['vatgroup_id' => $vatgroupId, 'area' => $vatarea]); + $taxText = $vatrate ? $vatrate->invoice_text : ''; + + $db = $this->db(); + $sepaLimit = null; + $res = $db->query("SELECT manual_invoice_sepa_limit FROM Address WHERE id = " . intval($addressId)); + if ($res && $row = $res->fetch_assoc()) { + $sepaLimit = $row['manual_invoice_sepa_limit'] ? floatval($row['manual_invoice_sepa_limit']) : null; + } + + self::returnJson([ + 'success' => true, + 'billing_type' => $address->billing_type ?: 'invoice', + 'manual_invoice_sepa_limit' => $sepaLimit, + 'vatarea' => $vatarea, + 'tax_text' => $taxText, + 'bank_account_bank' => $address->bank_account_bank, + 'bank_account_owner' => $address->bank_account_owner, + 'bank_account_iban' => $address->bank_account_iban, + 'bank_account_bic' => $address->bank_account_bic, + 'sepa_date' => $address->sepa_date, + 'sepa_id' => $address->sepa_id + ]); + } + + protected function getTaxTextAction() { + $vatgroupId = $_GET['vatgroup_id'] ?? 2; + $vatarea = $_GET['vatarea'] ?? 'domestic'; + + $vatrate = VatrateModel::getFirst(['vatgroup_id' => $vatgroupId, 'area' => $vatarea]); + + self::returnJson([ + 'success' => true, + 'tax_text' => $vatrate ? $vatrate->invoice_text : '', + 'vatrate' => $vatrate ? $vatrate->rate : 20 + ]); + } } \ No newline at end of file diff --git a/application/MobileApp/Modules/Workorder/Workorder/WorkorderHandler.php b/application/MobileApp/Modules/Workorder/Workorder/WorkorderHandler.php index 06826b287..4a556bf79 100644 --- a/application/MobileApp/Modules/Workorder/Workorder/WorkorderHandler.php +++ b/application/MobileApp/Modules/Workorder/Workorder/WorkorderHandler.php @@ -164,6 +164,7 @@ class WorkorderHandler extends MobileAppBaseHandler { 'interventionTypes' => json_decode($tenantConfig->interventionTypes, true) ?? [], 'requireCableLength' => (bool)$tenantConfig->requireCableLength, 'requireCableType' => (bool)$tenantConfig->requireCableType, + 'showTechnicalData' => (bool)$tenantConfig->showTechnicalData, ]; } @@ -241,6 +242,11 @@ class WorkorderHandler extends MobileAppBaseHandler { ]; } + $technicalData = null; + if ($tenantConfigData && !empty($tenantConfigData['showTechnicalData'])) { + $technicalData = $this->getTechnicalData($id); + } + self::returnJson([ 'success' => true, 'workorder' => $workorder, @@ -252,7 +258,8 @@ class WorkorderHandler extends MobileAppBaseHandler { 'completed' => $completedCount, 'total' => count($docTypes), 'allRequired' => $this->allRequiredCompleted($checklist) - ] + ], + 'technicalData' => $technicalData, ]); } @@ -893,6 +900,51 @@ class WorkorderHandler extends MobileAppBaseHandler { return true; } + /** + * Get technical data (patchposition and AHA Blatt) for a workorder + */ + private function getTechnicalData($workorderId) { + $workorder = WorkorderModel::get($workorderId); + if (!$workorder || !$workorder->preorderId) return null; + + $preorder = new Preorder($workorder->preorderId); + if (!$preorder->id || !$preorder->adb_wohneinheit_id) return null; + + $wohneinheit = $preorder->adb_wohneinheit; + if (!$wohneinheit) return null; + + $defaultCluster = ''; + if ($preorder->adb_hausnummer && $preorder->adb_hausnummer->netzgebiet) { + $defaultCluster = $preorder->adb_hausnummer->netzgebiet->extref ?? ''; + } + + $patchposition = [ + 'equipmentName' => $wohneinheit->getPatchEqString(), + 'equipmentPort' => $wohneinheit->patch_port, + 'cluster' => $wohneinheit->patch_cluster ?: $defaultCluster, + 'shelf' => $wohneinheit->patch_shelf, + 'module' => $wohneinheit->patch_module, + ]; + + $rimoWorkorders = []; + if (is_array($wohneinheit->rimo_workorders) && count($wohneinheit->rimo_workorders)) { + foreach ($wohneinheit->rimo_workorders as $wo) { + $rimoWorkorders[] = [ + 'id' => $wo->id, + 'rimoName' => $wo->rimo_name, + 'rimoId' => $wo->rimo_id, + 'rimoStatus' => $wo->rimo_status, + 'downloadUrl' => "/RimoWorkorder/downloadAha?id=" . $wo->id, + ]; + } + } + + return [ + 'patchposition' => $patchposition, + 'rimoWorkorders' => $rimoWorkorders, + ]; + } + /** * Get single workorder with full joined data (same structure as getCompanyWorkorders) */ diff --git a/application/RimoWorkorder/RimoWorkorderController.php b/application/RimoWorkorder/RimoWorkorderController.php index 1038575c0..7f330599e 100644 --- a/application/RimoWorkorder/RimoWorkorderController.php +++ b/application/RimoWorkorder/RimoWorkorderController.php @@ -13,6 +13,7 @@ class RimoWorkorderController extends mfBaseController { protected function downloadAhaAction() { $workorder_id = $this->request->id; + $inline = !empty($this->request->inline); if(!$workorder_id || $workorder_id < 1) { header("HTTP/1.1 400 Bad Request"); @@ -34,8 +35,11 @@ class RimoWorkorderController extends mfBaseController { exit; } - header("Content-type: text/pdf"); - header('Content-disposition: attachment; filename="'.$workorder->rimo_name.'_AHA.pdf"'); + $filename = $workorder->rimo_name.'_AHA.pdf'; + $disposition = $inline ? 'inline' : 'attachment'; + + header("Content-type: application/pdf"); + header('Content-disposition: '.$disposition.'; filename="'.$filename.'"'); echo $return; exit; } diff --git a/application/WorkorderBase/WorkorderBaseController.php b/application/WorkorderBase/WorkorderBaseController.php index fcfce3a08..f8a9c759e 100644 --- a/application/WorkorderBase/WorkorderBaseController.php +++ b/application/WorkorderBase/WorkorderBaseController.php @@ -141,6 +141,51 @@ class WorkorderBaseController extends TTCrud return WorkorderTenantConfigModel::getFirst(['addressId' => $network->owner_id]) ?? null; } + /** + * Retrieves technical data (patchposition and AHA Blatt info) for a workorder. + */ + protected function getTechnicalData(int $workorderId): ?array { + $workorder = WorkorderModel::get($workorderId); + if (!$workorder || !$workorder->preorderId) return null; + + $preorder = new Preorder($workorder->preorderId); + if (!$preorder->id || !$preorder->adb_wohneinheit_id) return null; + + $wohneinheit = $preorder->adb_wohneinheit; + if (!$wohneinheit) return null; + + $defaultCluster = ''; + if ($preorder->adb_hausnummer && $preorder->adb_hausnummer->netzgebiet) { + $defaultCluster = $preorder->adb_hausnummer->netzgebiet->extref ?? ''; + } + + $patchposition = [ + 'equipmentName' => $wohneinheit->getPatchEqString(), + 'equipmentPort' => $wohneinheit->patch_port, + 'cluster' => $wohneinheit->patch_cluster ?: $defaultCluster, + 'shelf' => $wohneinheit->patch_shelf, + 'module' => $wohneinheit->patch_module, + ]; + + $rimoWorkorders = []; + if (is_array($wohneinheit->rimo_workorders) && count($wohneinheit->rimo_workorders)) { + foreach ($wohneinheit->rimo_workorders as $wo) { + $rimoWorkorders[] = [ + 'id' => $wo->id, + 'rimoName' => $wo->rimo_name, + 'rimoId' => $wo->rimo_id, + 'rimoStatus' => $wo->rimo_status, + 'downloadUrl' => "/RimoWorkorder/downloadAha?id=" . $wo->id, + ]; + } + } + + return [ + 'patchposition' => $patchposition, + 'rimoWorkorders' => $rimoWorkorders, + ]; + } + //region BACKGROUND TASKS /** * Creates new workorders from preorders based on tenant configurations. diff --git a/application/WorkorderCompany/WorkorderCompanyController.php b/application/WorkorderCompany/WorkorderCompanyController.php index 6125b6e81..78e825809 100644 --- a/application/WorkorderCompany/WorkorderCompanyController.php +++ b/application/WorkorderCompany/WorkorderCompanyController.php @@ -167,14 +167,22 @@ class WorkorderCompanyController extends WorkorderBaseController { self::returnJson(['success' => false, 'message' => 'Keine Mandantenkonfiguration gefunden.']); return; } - self::returnJson([ + + $response = [ 'success' => true, 'documentationTypes' => json_decode($tenantConfig->documentationTypes, true), 'civilEngineeringDocsRequired' => $tenantConfig->civilEngineeringDocsRequired, 'interventionTypes' => json_decode($tenantConfig->interventionTypes, true), 'requireCableLength' => $tenantConfig->requireCableLength, - 'requireCableType' => $tenantConfig->requireCableType - ]); + 'requireCableType' => $tenantConfig->requireCableType, + 'showTechnicalData' => (bool)$tenantConfig->showTechnicalData, + ]; + + if ($tenantConfig->showTechnicalData) { + $response['technicalData'] = $this->getTechnicalData((int)$this->request->workorderId); + } + + self::returnJson($response); } protected function uploadDocumentationAction() { diff --git a/application/WorkorderMphAdmin/WorkorderMphAdminController.php b/application/WorkorderMphAdmin/WorkorderMphAdminController.php index 416d01d6d..efe5aec14 100644 --- a/application/WorkorderMphAdmin/WorkorderMphAdminController.php +++ b/application/WorkorderMphAdmin/WorkorderMphAdminController.php @@ -375,4 +375,38 @@ class WorkorderMphAdminController extends WorkorderMphBaseController self::returnJson(['success' => true, 'message' => 'Arbeitsauftrag wurde storniert.']); } + + protected function unassignWorkorderAction() + { + if (empty($this->postData['workorderId'])) self::sendError("Arbeitsauftrags-ID fehlt."); + + $workorder = WorkorderMphModel::get($this->postData['workorderId']); + if (!$workorder) self::sendError("Arbeitsauftrag nicht gefunden."); + + if ($workorder->status === 'new') self::sendError("Arbeitsauftrag ist nicht zugewiesen."); + if (in_array($workorder->status, ['completed', 'cancelled'])) self::sendError("Arbeitsauftrag kann nicht mehr geändert werden."); + + $oldStatus = $workorder->status; + $oldCompany = $workorder->companyId ? WorkorderCompanyModel::get($workorder->companyId) : null; + $oldCompanyName = $oldCompany ? $oldCompany->name : 'Unbekannt'; + + $workorder->status = 'new'; + $workorder->companyId = null; + $workorder->assignmentDate = null; + $workorder->deadlineDate = null; + $workorder->appointmentDate = null; + WorkorderMphModel::update((array)$workorder); + + $reason = !empty($this->postData['reason']) ? " Grund: " . $this->postData['reason'] : ''; + + WorkorderMphJournalModel::create([ + 'workorderMphId' => $workorder->id, + 'text' => "Zuweisung aufgehoben (vorher: $oldCompanyName).$reason", + 'statusChange' => $this->getStatusText($oldStatus) . " -> " . $this->getStatusText('new'), + 'create' => time(), + 'createBy' => $this->user->id, + ]); + + self::returnJson(['success' => true, 'message' => 'Zuweisung wurde aufgehoben.']); + } } diff --git a/application/WorkorderTenantConfig/WorkorderTenantConfigModel.php b/application/WorkorderTenantConfig/WorkorderTenantConfigModel.php index bc917678b..ec380a89a 100644 --- a/application/WorkorderTenantConfig/WorkorderTenantConfigModel.php +++ b/application/WorkorderTenantConfig/WorkorderTenantConfigModel.php @@ -12,6 +12,7 @@ class WorkorderTenantConfigModel extends TTCrudBaseModel { public int $civilEngineeringDocsRequired; public int $requireCableLength; public int $requireCableType; + public int $showTechnicalData = 0; public int $enableWorkorder; public int $enableWorkorderMph; public int $create; diff --git a/db/migrations/20260118120000_add_show_technical_data_to_tenant_config.php b/db/migrations/20260118120000_add_show_technical_data_to_tenant_config.php new file mode 100644 index 000000000..4bd3e16c9 --- /dev/null +++ b/db/migrations/20260118120000_add_show_technical_data_to_tenant_config.php @@ -0,0 +1,26 @@ +getEnvironment() !== "thetool") return; + + $table = $this->table('WorkorderTenantConfig'); + if (!$table->hasColumn('showTechnicalData')) { + $table->addColumn('showTechnicalData', 'boolean', [ + 'default' => false, + 'after' => 'requireCableType' + ])->update(); + } + } + + public function down() { + if ($this->getEnvironment() !== "thetool") return; + + $table = $this->table('WorkorderTenantConfig'); + if ($table->hasColumn('showTechnicalData')) { + $table->removeColumn('showTechnicalData')->update(); + } + } +} diff --git a/public/js/pages/ManualInvoice/ManualInvoice.js b/public/js/pages/ManualInvoice/ManualInvoice.js index a8699e002..0348b6e85 100644 --- a/public/js/pages/ManualInvoice/ManualInvoice.js +++ b/public/js/pages/ManualInvoice/ManualInvoice.js @@ -161,9 +161,18 @@ Vue.component('manual-invoice-modal', { -
- - +
+
+ +
+ + {{ effectiveBillingType === 'sepa' ? 'SEPA' : 'Rechnung' }} + + + Brutto überschreitet SEPA-Limit ({{ formatPrice(customerBillingInfo.manual_invoice_sepa_limit) }}) + +
+
@@ -172,9 +181,8 @@ Vue.component('manual-invoice-modal', { - + -
@@ -197,6 +205,12 @@ Vue.component('manual-invoice-modal', { pdfLoading: false, pdfPreviewUrl: '', previewDebounceTimer: null, + customerBillingInfo: { + billing_type: 'invoice', + manual_invoice_sepa_limit: null, + vatarea: 'domestic', + tax_text: '' + }, invoiceData: { id: null, invoice_number: null, invoice_date: moment().format('YYYY-MM-DD'), billingaddress_id: null, owner_id: null, customer_number: 0, fibu_account_number: 0, @@ -205,7 +219,6 @@ Vue.component('manual-invoice-modal', { leistungszeitraum: '', einleitender_text: '', externe_referenz: '', gesamtrabatt: 0, positions: [], total: 0, total_gross: 0 }, - billingTypeOptions: [{value: 'invoice', text: 'Rechnung'}, {value: 'sepa', text: 'SEPA'}], positionsConfig: { fields: { article_id: { @@ -270,16 +283,31 @@ Vue.component('manual-invoice-modal', { }); return { subtotal, net, vat, gross }; + }, + effectiveBillingType() { + if (this.customerBillingInfo.billing_type !== 'sepa') return 'invoice'; + if (this.customerBillingInfo.manual_invoice_sepa_limit === null) return 'sepa'; + return this.totals.gross <= this.customerBillingInfo.manual_invoice_sepa_limit ? 'sepa' : 'invoice'; } }, watch: { 'invoiceData': { handler() { this.debouncedPreviewUpdate(); }, deep: true }, + effectiveBillingType: { + handler(newType) { + this.invoiceData.billing_type = newType; + }, + immediate: true + }, 'invoiceData.billingaddress_id': { async handler(newId) { - if (!newId) return Object.assign(this.invoiceData, { - company: '', firstname: '', lastname: '', street: '', zip: '', city: '', - country: 'Österreich', uid: '', email: '', customer_number: 0, fibu_account_number: 0, owner_id: 0 - }); + if (!newId) { + Object.assign(this.invoiceData, { + company: '', firstname: '', lastname: '', street: '', zip: '', city: '', + country: 'Österreich', uid: '', email: '', customer_number: 0, fibu_account_number: 0, owner_id: 0 + }); + this.customerBillingInfo = { billing_type: 'invoice', manual_invoice_sepa_limit: null, vatarea: 'domestic', tax_text: '' }; + return; + } const { data } = await axios.get(`${window.TT_CONFIG.BASE_PATH}/Address/api?do=getAddress&id=${newId}`); if (data.status === 'OK' && data.result.address) { @@ -291,6 +319,8 @@ Vue.component('manual-invoice-modal', { fibu_account_number: a.fibu_account_number || 0, owner_id: newId }); } + + await this.fetchCustomerBillingInfo(newId); } } }, @@ -327,10 +357,35 @@ Vue.component('manual-invoice-modal', { methods: { close() { this.$emit('close'); }, saveInvoice() { + this.invoiceData.invoice_date = moment().format('YYYY-MM-DD'); + this.invoiceData.billing_type = this.effectiveBillingType; + this.invoiceData.tax_text = this.customerBillingInfo.tax_text; if (!this.invoiceData.billingaddress_id) return window.notify('error', 'Bitte wählen Sie einen Kunden aus.'); if (!this.invoiceData.positions?.length) return window.notify('error', 'Bitte fügen Sie mindestens eine Position hinzu.'); this.$emit('save', this.invoiceData); }, + formatPrice(value) { + if (value === null || value === undefined) return '-'; + return new Intl.NumberFormat('de-AT', { style: 'currency', currency: 'EUR' }).format(value); + }, + async fetchCustomerBillingInfo(addressId) { + if (!addressId) return; + try { + const vatgroupId = this.invoiceData.vatgroup_id || 2; + const { data } = await axios.get(`${window.TT_CONFIG.BASE_PATH}/ManualInvoice/getCustomerBillingInfo?address_id=${addressId}&vatgroup_id=${vatgroupId}`); + if (data.success) { + this.customerBillingInfo = { + billing_type: data.billing_type || 'invoice', + manual_invoice_sepa_limit: data.manual_invoice_sepa_limit, + vatarea: data.vatarea || 'domestic', + tax_text: data.tax_text || '' + }; + this.invoiceData.tax_text = data.tax_text || ''; + } + } catch (e) { + console.error('Error fetching customer billing info:', e); + } + }, handleResize() { this.isLargeScreen = window.innerWidth >= 1920; }, handleGlobalKeydown(e) { if (e.ctrlKey && e.key === 'q') { e.preventDefault(); this.togglePreviewVisibility(); } @@ -339,9 +394,9 @@ Vue.component('manual-invoice-modal', { async onArticleSelected(articleId) { if (!articleId) return; try { - const { data } = await axios.get(`${window.TT_CONFIG.BASE_PATH}/ManualInvoice/getArticleVatInfo?article_id=${articleId}`); + const vatarea = this.customerBillingInfo.vatarea || 'domestic'; + const { data } = await axios.get(`${window.TT_CONFIG.BASE_PATH}/ManualInvoice/getArticleVatInfo?article_id=${articleId}&vatarea=${vatarea}`); if (data.success && this.$refs.positionsManager) { - // Update the formData in the positions manager const pm = this.$refs.positionsManager; if (data.article) { pm.$set(pm.formData, 'product_name', data.article.title); @@ -351,13 +406,26 @@ Vue.component('manual-invoice-modal', { pm.$set(pm.formData, 'fibu_cost_account', data.fibu_cost_account); pm.$set(pm.formData, 'fibu_cost_account_legacy', data.fibu_cost_account_legacy); pm.$set(pm.formData, 'fibu_taxcode', data.fibu_taxcode); - // Store vatgroup_id on invoice level if needed this.invoiceData.vatgroup_id = data.vatgroup_id; + await this.updateTaxText(data.vatgroup_id); } } catch (e) { console.error('Error fetching article VAT info:', e); } }, + async updateTaxText(vatgroupId) { + if (!vatgroupId) return; + try { + const vatarea = this.customerBillingInfo.vatarea || 'domestic'; + const { data } = await axios.get(`${window.TT_CONFIG.BASE_PATH}/ManualInvoice/getTaxText?vatgroup_id=${vatgroupId}&vatarea=${vatarea}`); + if (data.success) { + this.customerBillingInfo.tax_text = data.tax_text || ''; + this.invoiceData.tax_text = data.tax_text || ''; + } + } catch (e) { + console.error('Error fetching tax text:', e); + } + }, debouncedPreviewUpdate() { clearTimeout(this.previewDebounceTimer); this.previewDebounceTimer = setTimeout(() => this.updatePdfPreview(), 2000); diff --git a/public/js/pages/WorkorderBase/WorkorderBase.js b/public/js/pages/WorkorderBase/WorkorderBase.js index 3972717c4..0b7d2c244 100644 --- a/public/js/pages/WorkorderBase/WorkorderBase.js +++ b/public/js/pages/WorkorderBase/WorkorderBase.js @@ -273,7 +273,44 @@ Vue.component('workorder-details-manager', { /> - + +
+
+
Technische Daten
+
+
+
+
+
Patchposition
+ + + + + + + + + +
Equipment Name:{{ technicalData.patchposition.equipmentName }}
Equipment Port:{{ technicalData.patchposition.equipmentPort }}
+
+
+
AHA Blätter
+
+
+
+ {{ wo.rimoName }} + {{ wo.rimoStatus }} +
+ + AHA Blatt + +
+
+
+
+
+
+
Korrektur anfordern
@@ -328,6 +365,9 @@ Vue.component('workorder-details-manager', { requireCableLength: false, requireCableType: false, savingData: false, + // Technical data + showTechnicalData: false, + technicalData: null, // Admin state selectedDocs: [], correctionText: '', correctionLoading: false, showAcceptModal: false, showRevertModal: false, }), @@ -394,6 +434,8 @@ Vue.component('workorder-details-manager', { this.interventionTypes = data.interventionTypes; this.requireCableLength = data.requireCableLength || false; this.requireCableType = data.requireCableType || false; + this.showTechnicalData = data.showTechnicalData || false; + this.technicalData = data.technicalData || null; } } catch (e) { console.error("Mandantenkonfiguration nicht geladen", e); } finally { this.loadingConfig = false; } diff --git a/public/js/pages/WorkorderMphAdmin/WorkorderMphAdmin.js b/public/js/pages/WorkorderMphAdmin/WorkorderMphAdmin.js index 72191b6e8..be5226746 100644 --- a/public/js/pages/WorkorderMphAdmin/WorkorderMphAdmin.js +++ b/public/js/pages/WorkorderMphAdmin/WorkorderMphAdmin.js @@ -30,10 +30,13 @@ Vue.component('workorder-mph-admin', {
{{ row.companyName || 'N/A' }}
-
- + + @@ -101,6 +104,13 @@ Vue.component('workorder-mph-admin', {

Soll der Auftrag #{{ cancelWorkorderModalData.id }} wirklich storniert werden?

+ + +

Soll die Zuweisung für Auftrag #{{ unassignWorkorderModalData.id }} aufgehoben werden?

+

Aktuell zugewiesen an: {{ unassignWorkorderModalData.companyName }}

+ +
`, data() { @@ -113,6 +123,7 @@ Vue.component('workorder-mph-admin', { companies: [], companiesLoading: false, cancelWorkorderModalData: null, + unassignWorkorderModalData: null, crudConfig: { ...window.TT_CONFIG.CRUD_CONFIG, selectable: false, @@ -237,6 +248,20 @@ Vue.component('workorder-mph-admin', { } else { window.notify('error', data.message || 'Stornierung fehlgeschlagen.'); } + }, + async unassignWorkorder() { + const { id, reason } = this.unassignWorkorderModalData; + const { data } = await axios.post(`${window.TT_CONFIG.BASE_PATH}/WorkorderMphAdmin/unassignWorkorder`, { + workorderId: id, + reason: reason + }); + if (data.success) { + window.notify('success', data.message); + this.$refs.table.$refs.table.refreshTable(); + this.unassignWorkorderModalData = null; + } else { + window.notify('error', data.message || 'Aufheben der Zuweisung fehlgeschlagen.'); + } } } }); \ No newline at end of file diff --git a/public/js/pages/WorkorderTenantConfig/WorkorderTenantConfig.js b/public/js/pages/WorkorderTenantConfig/WorkorderTenantConfig.js index 681f3bca6..d2a1b36a4 100644 --- a/public/js/pages/WorkorderTenantConfig/WorkorderTenantConfig.js +++ b/public/js/pages/WorkorderTenantConfig/WorkorderTenantConfig.js @@ -89,6 +89,8 @@ Vue.component('workorder-tenant-config', { v-model="editableItem.requireCableLength" sm/> +

Workorder: {{ config.enableWorkorder ? 'Aktiviert' : 'Deaktiviert' }}

@@ -97,6 +99,7 @@ Vue.component('workorder-tenant-config', {

Tiefbau-Doku: {{ config.civilEngineeringDocsRequired ? 'Ja' : 'Nein' }}

Kabellänge-Doku: {{ config.requireCableLength ? 'Ja' : 'Nein' }}

Kabeltyp-Doku: {{ config.requireCableType ? 'Ja' : 'Nein' }}

+

Technische Daten: {{ config.showTechnicalData ? 'Ja' : 'Nein' }}

@@ -333,6 +336,7 @@ Vue.component('workorder-tenant-config', { civilEngineeringDocsRequired: 0, requireCableLength: 0, requireCableType: 0, + showTechnicalData: 0, enableWorkorder: 1, enableWorkorderMph: 1 } diff --git a/public/mobile/modules/workorder/WorkorderModule.js b/public/mobile/modules/workorder/WorkorderModule.js index 1324c1da9..c760aca46 100644 --- a/public/mobile/modules/workorder/WorkorderModule.js +++ b/public/mobile/modules/workorder/WorkorderModule.js @@ -31,6 +31,7 @@ export default { const documentation = ref({ docs: [], journals: [] }); const tenantConfig = ref(null); const checklist = ref([]); + const technicalData = ref(null); // Expanded cards state const expandedCards = ref({ @@ -39,7 +40,8 @@ export default { documentation: false, notes: false, journal: false, - cableData: false + cableData: false, + technical: true }); // Edit states @@ -52,6 +54,9 @@ export default { const showDocUploadSheet = ref(false); const showProblemSheet = ref(false); const showCompleteSheet = ref(false); + const showPdfViewer = ref(false); + const pdfViewerUrl = ref(''); + const pdfViewerTitle = ref(''); // Upload state const uploadDocType = ref(''); @@ -198,7 +203,7 @@ export default { const openDetail = async (workorder) => { selectedWorkorder.value = workorder; isDetailLoading.value = true; - expandedCards.value = { customer: true, checklist: true, documentation: false, notes: false, journal: false, cableData: false }; + expandedCards.value = { customer: true, checklist: true, documentation: false, notes: false, journal: false, cableData: false, technical: true }; emit('detail-open', workorder.id); try { @@ -215,6 +220,7 @@ export default { documentation.value = { docs: data.docs, journals: data.journals }; tenantConfig.value = data.tenantConfig; checklist.value = data.checklist; + technicalData.value = data.technicalData || null; } else { emit('toast', data.message || 'Fehler beim Laden', 'error'); } @@ -231,6 +237,7 @@ export default { documentation.value = { docs: [], journals: [] }; tenantConfig.value = null; checklist.value = []; + technicalData.value = null; isEditingNotes.value = false; emit('detail-close'); }; @@ -617,6 +624,13 @@ export default { // Button is disabled when not complete, so this won't be called }; + // Open PDF in viewer + const openPdfViewer = (url, title) => { + pdfViewerUrl.value = url; + pdfViewerTitle.value = title || 'PDF'; + showPdfViewer.value = true; + }; + // Initialize onMounted(() => { fetchWorkorders(); @@ -636,6 +650,7 @@ export default { documentation, tenantConfig, checklist, + technicalData, expandedCards, isEditingNotes, tempNotes, @@ -644,6 +659,9 @@ export default { showDocUploadSheet, showProblemSheet, showCompleteSheet, + showPdfViewer, + pdfViewerUrl, + pdfViewerTitle, uploadDocType, isUploading, fileInputRef, @@ -678,6 +696,7 @@ export default { openNavigation, callCustomer, handleComplete, + openPdfViewer, handleTouchStart, handleTouchMove, handleTouchEnd, @@ -906,6 +925,63 @@ export default {
+ +
+ +
+ +
+
Patchposition
+
+
+ Equipment Name: + {{ technicalData.patchposition.equipmentName }} +
+
+ Equipment Port: + {{ technicalData.patchposition.equipmentPort }} +
+
+
+ + +
+
AHA Blätter
+
+
+
{{ wo.rimoName }}
+
Status: {{ wo.rimoStatus }}
+
+ +
+
+
+
+
+
+ +
+ +
+ + + + + +