// WorkorderAdmin.js Vue.component('workorder-admin', { template: `
{{ workordersToAssign.length }} Workorder(s) zuweisen:

Auftrag: #{{ civilEngineeringData.workorder.id }}

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

Soll das Problem bei Auftrag #{{ problemSolvedModalData.id }} als gelöst markiert werden?

Sollen {{ workordersToAssign.length }} Workorder(s) der Firma {{ massAssignModalData.companyName }} zugewiesen werden?

`, data() { return { window, workordersToAssign: [], editingWorkorderId: null, editingDeadlineId: null, editingAdditionalInfoId: null, civilEngineeringData: null, tempAdditionalInfo: '', expandedNotes: [], companiesByTenant: {}, companiesLoading: false, massAssignCompanyId: null, cancelWorkorderModalData: null, problemSolvedModalData: null, massAssignModalData: null, crudConfig: { ...window.TT_CONFIG.CRUD_CONFIG, selectable: false, expandable: true, customRowClass: (row) => { if (['completed', 'new', 'cancelled', 'charged', 'archived'].includes(row.status)) return 'tt-rml-workorder-irrelevant'; if (['correction_requested', 'intervention_required'].includes(row.status)) return 'tt-rml-workorder-high'; const deadlineDate = moment.unix(row.deadlineDate); if (!deadlineDate.isValid()) return 'tt-rml-workorder-irrelevant'; const daysLeft = deadlineDate.diff(moment(), 'days'); if (daysLeft <= 7) return 'tt-rml-workorder-urgent'; if (daysLeft <= 21) return 'tt-rml-workorder-medium'; return 'tt-rml-workorder-ontrack'; }, additionalActions: [ { key: 'charge_workorder', title: 'Verrechnen', class: 'fas fa-euro-sign text-purple', condition: (row) => row.status === 'completed' } ] } } }, computed: { companiesForMassAssign() { if (this.workordersToAssign.length === 0) return []; const rows = this.$refs.table?.$refs.table.rows; if (!rows) return[]; const firstWorkorder = rows.find(r => r.id === this.workordersToAssign[0]); if (!firstWorkorder) return []; return this.companiesByTenant[firstWorkorder.tenantId] || []; }, editTextareaRows() { const lines = (this.tempAdditionalInfo || '').split('\n').length; return String(Math.max(4, lines + 1)); }, }, methods: { async chargeWorkorder(row) { if (!confirm(`Soll der Auftrag #${row.id} wirklich als "verrechnet" markiert werden?`)) return; try { const { data } = await axios.post(`${window.TT_CONFIG.BASE_PATH}/WorkorderAdmin/chargeWorkorder`, { workorderId: row.id }); if (data.success) { window.notify('success', data.message); this.$refs.table.$refs.table.refreshTable(); } else { window.notify('error', data.message || 'Aktion fehlgeschlagen.'); } } catch (e) { window.notify('error', 'Ein Netzwerkfehler ist aufgetreten.'); } }, async acceptDocumentation(workorderId) { const {data} = await axios.post(`${window.TT_CONFIG.BASE_PATH}/WorkorderAdmin/acceptDocumentation`, { workorderId }); if (data.success) { window.notify('success', data.message); this.$refs.table.$refs.table.refreshTable(); } else window.notify('error', data.message || 'Ein Fehler ist aufgetreten.'); }, openCivilEngineeringModal(row) { this.getCompaniesForWorkorder(row); this.civilEngineeringData = { workorder: row, companyId: null }; }, async assignCivilEngineering() { if (!this.civilEngineeringData.companyId) return window.notify('error', 'Bitte eine Firma auswählen.'); const {data} = await axios.post(`${window.TT_CONFIG.BASE_PATH}/WorkorderAdmin/setCivilEngineeringRequired`, { workorderId: this.civilEngineeringData.workorder.id, companyId: this.civilEngineeringData.companyId }); if (data.success) { window.notify('success', data.message); this.$refs.table.$refs.table.refreshTable(); this.civilEngineeringData = null; } else window.notify('error', data.message || 'Zuweisung fehlgeschlagen.'); }, addToAssignList(row) { if (!this.workordersToAssign.includes(row.id)) this.workordersToAssign.push(row.id); }, removeFromAssignList(row) { this.workordersToAssign = this.workordersToAssign.filter(id => id !== row.id); }, getStatusColumn(status) { const column = this.crudConfig.columns.find(c => c.key === 'status'); return column.table.filterOptions.find(opt => opt.value === status) || {}; }, formatDate(timestamp, withTime = false) { if (!timestamp) return '–'; return window.moment.unix(timestamp).format(withTime ? 'DD.MM.YYYY HH:mm' : 'DD.MM.YYYY'); }, async getCompaniesForWorkorder(workorder) { if (!workorder.tenantId || this.companiesByTenant[workorder.tenantId]) return; this.companiesLoading = true; try { const {data} = await axios.get(`${window.TT_CONFIG.BASE_PATH}/WorkorderAdmin/getCompanies`, {params: {tenantId: workorder.tenantId}}); this.$set(this.companiesByTenant, workorder.tenantId, data); } catch (e) { window.notify('error', 'Firmenliste konnte nicht geladen werden.'); } finally { this.companiesLoading = false; } }, async startCompanyEdit(row) { await this.getCompaniesForWorkorder(row); this.editingWorkorderId = row.id; }, async assignCompany(workorder, companyId) { if (!companyId) { this.editingWorkorderId = null; return; } const {data} = await axios.post(`${window.TT_CONFIG.BASE_PATH}/WorkorderAdmin/assignWorkorder`, { workorderId: workorder.id, companyId: companyId }); if (data.success) { window.notify('success', data.message); this.$refs.table.$refs.table.refreshTable(); } else window.notify('error', data.message || 'Ein Fehler ist aufgetreten.'); this.editingWorkorderId = null; }, openMassAssignModal(companyId) { if (!companyId) return; const companyName = this.companiesForMassAssign.find(c => c.value === companyId)?.text; this.massAssignModalData = { companyId, companyName, deadline: null }; }, async massAssignCompanies() { try { const { companyId, deadline } = this.massAssignModalData; const {data} = await axios.post(`${window.TT_CONFIG.BASE_PATH}/WorkorderAdmin/massAssignWorkorders`, { companyId: companyId, workorderIds: this.workordersToAssign, deadlineDate: deadline }); if (data.success) { window.notify('success', data.message); this.workordersToAssign = []; this.$refs.table.$refs.table.refreshTable(); } else window.notify('error', data.message || 'Ein Fehler ist aufgetreten.'); } catch (e) {} finally { this.massAssignCompanyId = null; this.massAssignModalData = null; } }, async updateDeadline(workorder, newDate) { if (!newDate) { this.editingDeadlineId = null; return; } try { const {data} = await axios.post(`${window.TT_CONFIG.BASE_PATH}/WorkorderAdmin/updateDeadline`, { workorderId: workorder.id, deadlineDate: newDate }); if (data.success) { window.notify('success', data.message); this.$refs.table.$refs.table.refreshTable(); } else window.notify('error', data.message || 'Ein Fehler ist aufgetreten.'); } catch (e) {} finally { this.editingDeadlineId = null; } }, async setToProblemSolved() { const { id, text } = this.problemSolvedModalData; if (!text || !text.trim()) return window.notify('error', 'Bitte geben Sie einen Text ein.'); const {data} = await axios.post(`${window.TT_CONFIG.BASE_PATH}/WorkorderAdmin/setToProblemSolved`, { workorderId: id, text: text }); if (data.success) { window.notify('success', data.message); this.$refs.table.$refs.table.refreshTable(); this.problemSolvedModalData = null; } else window.notify('error', data.message || 'Ein Fehler ist aufgetreten.'); }, isNoteLong(text) { if (!text) return false; return text.split('\n').length > 4 || text.length > 180; }, toggleNoteExpand(rowId) { const idx = this.expandedNotes.indexOf(rowId); if (idx === -1) this.expandedNotes.push(rowId); else this.expandedNotes.splice(idx, 1); }, startAdditionalInfoEdit(row) { this.editingAdditionalInfoId = row.id; this.tempAdditionalInfo = row.additionalInfo || ''; this.$nextTick(() => this.$refs.editTextarea?.$el.querySelector('textarea').focus()); }, cancelEdit() { this.editingAdditionalInfoId = null; this.tempAdditionalInfo = ''; }, async updateAdditionalInfo(row) { if (row.additionalInfo === this.tempAdditionalInfo) { this.cancelEdit(); return; } try { const {data} = await axios.post(`${window.TT_CONFIG.BASE_PATH}/WorkorderAdmin/updateAdditionalInfo`, { workorderId: row.id, additionalInfo: this.tempAdditionalInfo }); if (data.success) { window.notify('success', data.message); row.additionalInfo = data.newInfo; // Update local data } else window.notify('error', data.message || 'Update fehlgeschlagen.'); } catch (e) { } finally { this.cancelEdit(); } }, async cancelWorkorder() { const { id, reason } = this.cancelWorkorderModalData; const {data} = await axios.post(`${window.TT_CONFIG.BASE_PATH}/WorkorderAdmin/cancelWorkorder`, { workorderId: id, reason: reason }); if (data.success) { window.notify('success', data.message); this.$refs.table.$refs.table.refreshTable(); this.cancelWorkorderModalData = null; } else window.notify('error', data.message || 'Stornierung fehlgeschlagen.'); } }, watch: { workordersToAssign: { async handler(newVal) { if (newVal.length === 0) return; const rows = this.$refs.table?.$refs.table.rows; if (!rows) return; const firstWorkorder = rows.find(r => r.id === newVal[0]); if (!firstWorkorder) return; const firstTenantId = firstWorkorder.tenantId; const allSameTenant = newVal.every(id => { const wo = rows.find(r => r.id === id); return wo && wo.tenantId === firstTenantId; }); if (!allSameTenant) { window.notify('error', 'Massen-Zuweisung nur für Aufträge des gleichen Mandanten möglich.'); this.workordersToAssign.pop(); return; } await this.getCompaniesForWorkorder(firstWorkorder); }, deep: true } } });