Merge branch 'Workorder/fix-civil-engin' into 'master'
fixed workorder civil engineering See merge request fronk/thetool!2057
This commit is contained in:
@@ -79,7 +79,7 @@ class RimoWorkorder extends mfBaseModel {
|
||||
$fn = 'aha_lageplan_' . $this->id . '_' . time() . '.png';
|
||||
$file = FileModel::create(['name' => 'AHA Lageplan ' . $this->rimo_name, 'filename' => $fn,
|
||||
'store_filename' => $fn, 'orig_filename' => 'AHA_Lageplan_' . $this->rimo_name . '.png',
|
||||
'mimetype' => 'image/png', 'subfolder' => 'aha_maps']);
|
||||
'mimetype' => 'image/png', 'subfolder' => 'aha_maps', 'create_by' => 1]);
|
||||
if ($file->save()) {
|
||||
$dir = MFUPLOAD_FILE_SAVE_PATH . '/aha_maps';
|
||||
if (!is_dir($dir)) mkdir($dir, 0755, true);
|
||||
|
||||
@@ -90,8 +90,8 @@ Vue.component('workorder-admin', {
|
||||
<template v-slot:appointmentdate="{ row }">{{ formatDate(row.appointmentDate, true) }}</template>
|
||||
|
||||
<template v-slot:expandedRow="{ row }">
|
||||
<civil-engineering-manager v-if="row.status === 'civil_engineering_required'" :workorder-id="row.id" :is-admin="true"/>
|
||||
<workorder-details-manager v-else :workorder-id="row.id" :is-admin="true"
|
||||
<civil-engineering-manager v-if="row.status === 'civil_engineering_required'" :workorder-id="row.id" :is-admin="true" class="mb-3"/>
|
||||
<workorder-details-manager :workorder-id="row.id" :is-admin="true"
|
||||
@workorder-completed="$refs.table.$refs.table.refreshTable()"
|
||||
@accept-documentation="acceptDocumentation(row.id)"/>
|
||||
</template>
|
||||
|
||||
@@ -27,25 +27,56 @@ Vue.component('civil-engineering-manager', {
|
||||
<div v-if="loading" class="text-center p-5"><i class="fas fa-spinner fa-spin fa-2x"></i></div>
|
||||
<div v-else class="row">
|
||||
<div class="col-lg-4">
|
||||
<div class="card h-100">
|
||||
<div class="card mb-3">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Tiefbau-Arbeiten</h5>
|
||||
<h5 class="card-title"><i class="fas fa-hard-hat text-orange mr-2"></i>Tiefbau-Arbeiten</h5>
|
||||
<p class="small text-muted">Schließen Sie den Tiefbau-Auftrag ab. Laden Sie Dokumente hoch, falls erforderlich.</p>
|
||||
|
||||
<div v-if="tiefbauSeesNormalDocs && documentationTypes.length" class="mb-3">
|
||||
<h6 class="small font-weight-bold">Checkliste</h6>
|
||||
<ul class="list-unstyled mb-0">
|
||||
<li v-for="docType in documentationTypes" :key="docType.value" class="mb-1 d-flex align-items-center small">
|
||||
<i :class="isUploaded(docType.value) ? 'fas fa-check-circle text-success' : 'far fa-circle text-muted'" class="fa-fw mr-2"></i>
|
||||
<span>{{ docType.text }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<tt-button text="Tiefbau abschließen" @click="showCompleteModal = true"
|
||||
:disabled="!canComplete || completing"
|
||||
:loading="completing" additional-class="btn-success w-100" icon="fas fa-check-double"
|
||||
/>
|
||||
<small v-if="docsRequired && !canComplete" class="form-text text-muted text-center mt-2">
|
||||
Bitte laden Sie mindestens ein Dokument hoch, um den Auftrag abzuschließen.
|
||||
<small v-if="docsRequired && !canComplete && !tiefbauSeesNormalDocs" class="form-text text-muted text-center mt-2">
|
||||
Bitte laden Sie mindestens ein Tiefbau-Dokument hoch, um den Auftrag abzuschließen.
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header py-2"><h6 class="mb-0"><i class="fas fa-history mr-2"></i>Journal</h6></div>
|
||||
<div class="card-body p-0" style="max-height: 200px; overflow-y: auto;">
|
||||
<ul v-if="journals.length" class="list-group list-group-flush">
|
||||
<li v-for="log in journals" :key="log.id" class="list-group-item small py-2">
|
||||
<strong>{{ formatDate(log.create) }} ({{ log.createByName }}):</strong>
|
||||
<div class="text-muted" style="white-space: pre-wrap;">{{ log.text }}</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div v-else class="p-3 text-muted text-center small">Keine Journaleinträge.</div>
|
||||
</div>
|
||||
<div class="card-footer py-2">
|
||||
<tt-textarea v-model="newJournalMessage" placeholder="Nachricht..." rows="2" sm no-form-group/>
|
||||
<tt-button text="Eintrag speichern" @click="addJournalEntry" :loading="addingJournalEntry"
|
||||
additional-class="btn-info btn-sm w-100 mt-2" icon="fas fa-paper-plane"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-8">
|
||||
<div class="card mb-3" v-if="docsRequired">
|
||||
<div class="card mb-3">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Dokument hochladen</h5>
|
||||
<tt-select v-if="tiefbauSeesNormalDocs && allDocTypes.length" label="Dokumententyp" :options="allDocTypes" v-model="uploadData.documentType" sm row/>
|
||||
<tt-input label="Beschreibung" v-model="uploadData.description" sm row placeholder="Optional"/>
|
||||
<div class="form-group row">
|
||||
<label class="col-form-label col-sm-4 col-form-label-sm">Dateien</label>
|
||||
@@ -56,14 +87,21 @@ Vue.component('civil-engineering-manager', {
|
||||
<tt-button text="Hochladen" @click="uploadFiles" :loading="uploading" additional-class="btn-primary float-right" icon="fas fa-upload"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<tt-file-gallery
|
||||
v-if="docsRequired"
|
||||
:files="uploadedFiles"
|
||||
:edit-mode="false"
|
||||
:edit-mode="tiefbauSeesNormalDocs"
|
||||
:delete-mode="true"
|
||||
@delete-file="deleteDocumentation">
|
||||
@delete-file="deleteDocumentation"
|
||||
@update-file="updateDocumentation">
|
||||
<template v-if="tiefbauSeesNormalDocs" v-slot:file-edit="{ file }">
|
||||
<tt-select label="Dokumententyp" :options="allDocTypes" v-model="file.documentType" sm/>
|
||||
</template>
|
||||
</tt-file-gallery>
|
||||
<div v-else class="alert alert-info">Für diesen Auftraggeber ist keine Dokumentation für Tiefbau-Arbeiten erforderlich.</div>
|
||||
|
||||
<div v-if="!docsRequired && !tiefbauSeesNormalDocs" class="alert alert-info">
|
||||
Für diesen Auftraggeber ist keine Dokumentation für Tiefbau-Arbeiten erforderlich.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<tt-modal :show.sync="showCompleteModal" title="Tiefbau abschließen" @submit="completeTask" @close="showCompleteModal = false" :delete="false">
|
||||
@@ -72,25 +110,43 @@ Vue.component('civil-engineering-manager', {
|
||||
</div>
|
||||
`,
|
||||
data: () => ({
|
||||
loading: true, uploading: false, completing: false, docsRequired: false,
|
||||
uploadedFiles: [], uploadData: { files: [], description: '' }, showCompleteModal: false
|
||||
loading: true, uploading: false, completing: false,
|
||||
docsRequired: false, tiefbauSeesNormalDocs: false, documentationTypes: [],
|
||||
uploadedFiles: [], journals: [],
|
||||
uploadData: { files: [], description: '', documentType: 'civil_engineering_photo' },
|
||||
showCompleteModal: false,
|
||||
newJournalMessage: '', addingJournalEntry: false
|
||||
}),
|
||||
computed: {
|
||||
canComplete() {
|
||||
if (this.tiefbauSeesNormalDocs) {
|
||||
// When tiefbauSeesNormalDocs is enabled, can always complete (no strict requirements)
|
||||
return true;
|
||||
}
|
||||
// Original logic: require at least one civil engineering doc if docsRequired
|
||||
return !this.docsRequired || this.uploadedFiles.length > 0;
|
||||
},
|
||||
allDocTypes() {
|
||||
return [...this.documentationTypes, { value: 'civil_engineering_photo', text: 'Tiefbau Foto' }, { value: 'other', text: 'Sonstiges' }];
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formatDate(timestamp) { return timestamp ? window.moment.unix(timestamp).format('DD.MM.YYYY HH:mm') : '–'; },
|
||||
isUploaded(docType) {
|
||||
return Array.isArray(this.uploadedFiles) && this.uploadedFiles.some(doc => doc.documentType === docType);
|
||||
},
|
||||
async fetchInitialData() {
|
||||
this.loading = true;
|
||||
try {
|
||||
const configRes = await axios.get(`${window.TT_CONFIG.BASE_PATH}/WorkorderCompany/getTenantConfig`, { params: { workorderId: this.workorderId } });
|
||||
this.docsRequired = configRes.data.civilEngineeringDocsRequired || false;
|
||||
this.tiefbauSeesNormalDocs = configRes.data.tiefbauSeesNormalDocs || false;
|
||||
this.documentationTypes = configRes.data.documentationTypes || [];
|
||||
|
||||
if(this.docsRequired) {
|
||||
const docRes = await axios.get(`${window.TT_CONFIG.BASE_PATH}/${this.isAdmin ? 'WorkorderAdmin' : 'WorkorderCompany'}/getDocumentation`, { params: { workorderId: this.workorderId } });
|
||||
this.uploadedFiles = docRes.data.docs || [];
|
||||
}
|
||||
// Always load docs and journals
|
||||
const docRes = await axios.get(`${window.TT_CONFIG.BASE_PATH}/${this.isAdmin ? 'WorkorderAdmin' : 'WorkorderCompany'}/getDocumentation`, { params: { workorderId: this.workorderId } });
|
||||
this.uploadedFiles = docRes.data.docs || [];
|
||||
this.journals = docRes.data.journals || [];
|
||||
} catch (e) {
|
||||
window.notify('error', 'Konfiguration konnte nicht geladen werden.');
|
||||
console.error(e);
|
||||
@@ -104,7 +160,7 @@ Vue.component('civil-engineering-manager', {
|
||||
this.uploading = true;
|
||||
const formData = new FormData();
|
||||
formData.append('workorderId', this.workorderId);
|
||||
formData.append('documentType', 'civil_engineering_photo');
|
||||
formData.append('documentType', this.uploadData.documentType || 'civil_engineering_photo');
|
||||
formData.append('description', this.uploadData.description);
|
||||
for (const file of this.uploadData.files) formData.append('files[]', file);
|
||||
try {
|
||||
@@ -112,7 +168,7 @@ Vue.component('civil-engineering-manager', {
|
||||
if (data.success) {
|
||||
window.notify('success', data.message);
|
||||
this.$refs.fileInput.value = '';
|
||||
this.uploadData = { files: [], description: '' };
|
||||
this.uploadData = { files: [], description: '', documentType: this.tiefbauSeesNormalDocs ? this.documentationTypes[0]?.value : 'civil_engineering_photo' };
|
||||
await this.fetchInitialData();
|
||||
} else window.notify('error', data.error || 'Upload fehlgeschlagen.');
|
||||
} catch (e) {
|
||||
@@ -131,6 +187,29 @@ Vue.component('civil-engineering-manager', {
|
||||
window.notify('error', 'Netzwerkfehler beim Löschen.');
|
||||
}
|
||||
},
|
||||
async updateDocumentation(file) {
|
||||
try {
|
||||
const {data} = await axios.post(`${window.TT_CONFIG.BASE_PATH}/WorkorderCompany/updateDocumentation`, { id: file.id, documentType: file.documentType });
|
||||
if (data.success) {
|
||||
window.notify('success', data.message);
|
||||
await this.fetchInitialData();
|
||||
} else window.notify('error', data.message || 'Update fehlgeschlagen.');
|
||||
} catch (e) {
|
||||
window.notify('error', 'Netzwerkfehler beim Update.');
|
||||
}
|
||||
},
|
||||
async addJournalEntry() {
|
||||
if (!this.newJournalMessage.trim()) return;
|
||||
this.addingJournalEntry = true;
|
||||
try {
|
||||
const {data} = await axios.post(`${window.TT_CONFIG.BASE_PATH}/${this.isAdmin ? 'WorkorderAdmin' : 'WorkorderCompany'}/addJournal`, { workorderId: this.workorderId, text: this.newJournalMessage });
|
||||
if (data.success) {
|
||||
this.newJournalMessage = '';
|
||||
this.journals = data.journals || [];
|
||||
} else window.notify('error', data.message);
|
||||
} catch (e) { window.notify('error', 'Netzwerkfehler'); }
|
||||
finally { this.addingJournalEntry = false; }
|
||||
},
|
||||
async completeTask() {
|
||||
this.completing = true;
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user