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';
|
$fn = 'aha_lageplan_' . $this->id . '_' . time() . '.png';
|
||||||
$file = FileModel::create(['name' => 'AHA Lageplan ' . $this->rimo_name, 'filename' => $fn,
|
$file = FileModel::create(['name' => 'AHA Lageplan ' . $this->rimo_name, 'filename' => $fn,
|
||||||
'store_filename' => $fn, 'orig_filename' => 'AHA_Lageplan_' . $this->rimo_name . '.png',
|
'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()) {
|
if ($file->save()) {
|
||||||
$dir = MFUPLOAD_FILE_SAVE_PATH . '/aha_maps';
|
$dir = MFUPLOAD_FILE_SAVE_PATH . '/aha_maps';
|
||||||
if (!is_dir($dir)) mkdir($dir, 0755, true);
|
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:appointmentdate="{ row }">{{ formatDate(row.appointmentDate, true) }}</template>
|
||||||
|
|
||||||
<template v-slot:expandedRow="{ row }">
|
<template v-slot:expandedRow="{ row }">
|
||||||
<civil-engineering-manager v-if="row.status === 'civil_engineering_required'" :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 v-else :workorder-id="row.id" :is-admin="true"
|
<workorder-details-manager :workorder-id="row.id" :is-admin="true"
|
||||||
@workorder-completed="$refs.table.$refs.table.refreshTable()"
|
@workorder-completed="$refs.table.$refs.table.refreshTable()"
|
||||||
@accept-documentation="acceptDocumentation(row.id)"/>
|
@accept-documentation="acceptDocumentation(row.id)"/>
|
||||||
</template>
|
</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-if="loading" class="text-center p-5"><i class="fas fa-spinner fa-spin fa-2x"></i></div>
|
||||||
<div v-else class="row">
|
<div v-else class="row">
|
||||||
<div class="col-lg-4">
|
<div class="col-lg-4">
|
||||||
<div class="card h-100">
|
<div class="card mb-3">
|
||||||
<div class="card-body">
|
<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>
|
<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>
|
<hr>
|
||||||
<tt-button text="Tiefbau abschließen" @click="showCompleteModal = true"
|
<tt-button text="Tiefbau abschließen" @click="showCompleteModal = true"
|
||||||
:disabled="!canComplete || completing"
|
:disabled="!canComplete || completing"
|
||||||
:loading="completing" additional-class="btn-success w-100" icon="fas fa-check-double"
|
: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">
|
<small v-if="docsRequired && !canComplete && !tiefbauSeesNormalDocs" class="form-text text-muted text-center mt-2">
|
||||||
Bitte laden Sie mindestens ein Dokument hoch, um den Auftrag abzuschließen.
|
Bitte laden Sie mindestens ein Tiefbau-Dokument hoch, um den Auftrag abzuschließen.
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
</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>
|
||||||
|
|
||||||
<div class="col-lg-8">
|
<div class="col-lg-8">
|
||||||
<div class="card mb-3" v-if="docsRequired">
|
<div class="card mb-3">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5 class="card-title">Dokument hochladen</h5>
|
<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"/>
|
<tt-input label="Beschreibung" v-model="uploadData.description" sm row placeholder="Optional"/>
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label class="col-form-label col-sm-4 col-form-label-sm">Dateien</label>
|
<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"/>
|
<tt-button text="Hochladen" @click="uploadFiles" :loading="uploading" additional-class="btn-primary float-right" icon="fas fa-upload"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<tt-file-gallery
|
<tt-file-gallery
|
||||||
v-if="docsRequired"
|
|
||||||
:files="uploadedFiles"
|
:files="uploadedFiles"
|
||||||
:edit-mode="false"
|
:edit-mode="tiefbauSeesNormalDocs"
|
||||||
:delete-mode="true"
|
: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>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
<tt-modal :show.sync="showCompleteModal" title="Tiefbau abschließen" @submit="completeTask" @close="showCompleteModal = false" :delete="false">
|
<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>
|
</div>
|
||||||
`,
|
`,
|
||||||
data: () => ({
|
data: () => ({
|
||||||
loading: true, uploading: false, completing: false, docsRequired: false,
|
loading: true, uploading: false, completing: false,
|
||||||
uploadedFiles: [], uploadData: { files: [], description: '' }, showCompleteModal: false
|
docsRequired: false, tiefbauSeesNormalDocs: false, documentationTypes: [],
|
||||||
|
uploadedFiles: [], journals: [],
|
||||||
|
uploadData: { files: [], description: '', documentType: 'civil_engineering_photo' },
|
||||||
|
showCompleteModal: false,
|
||||||
|
newJournalMessage: '', addingJournalEntry: false
|
||||||
}),
|
}),
|
||||||
computed: {
|
computed: {
|
||||||
canComplete() {
|
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;
|
return !this.docsRequired || this.uploadedFiles.length > 0;
|
||||||
|
},
|
||||||
|
allDocTypes() {
|
||||||
|
return [...this.documentationTypes, { value: 'civil_engineering_photo', text: 'Tiefbau Foto' }, { value: 'other', text: 'Sonstiges' }];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
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() {
|
async fetchInitialData() {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
try {
|
try {
|
||||||
const configRes = await axios.get(`${window.TT_CONFIG.BASE_PATH}/WorkorderCompany/getTenantConfig`, { params: { workorderId: this.workorderId } });
|
const configRes = await axios.get(`${window.TT_CONFIG.BASE_PATH}/WorkorderCompany/getTenantConfig`, { params: { workorderId: this.workorderId } });
|
||||||
this.docsRequired = configRes.data.civilEngineeringDocsRequired || false;
|
this.docsRequired = configRes.data.civilEngineeringDocsRequired || false;
|
||||||
|
this.tiefbauSeesNormalDocs = configRes.data.tiefbauSeesNormalDocs || false;
|
||||||
|
this.documentationTypes = configRes.data.documentationTypes || [];
|
||||||
|
|
||||||
if(this.docsRequired) {
|
// Always load docs and journals
|
||||||
const docRes = await axios.get(`${window.TT_CONFIG.BASE_PATH}/${this.isAdmin ? 'WorkorderAdmin' : 'WorkorderCompany'}/getDocumentation`, { params: { workorderId: this.workorderId } });
|
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.uploadedFiles = docRes.data.docs || [];
|
||||||
}
|
this.journals = docRes.data.journals || [];
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
window.notify('error', 'Konfiguration konnte nicht geladen werden.');
|
window.notify('error', 'Konfiguration konnte nicht geladen werden.');
|
||||||
console.error(e);
|
console.error(e);
|
||||||
@@ -104,7 +160,7 @@ Vue.component('civil-engineering-manager', {
|
|||||||
this.uploading = true;
|
this.uploading = true;
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('workorderId', this.workorderId);
|
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);
|
formData.append('description', this.uploadData.description);
|
||||||
for (const file of this.uploadData.files) formData.append('files[]', file);
|
for (const file of this.uploadData.files) formData.append('files[]', file);
|
||||||
try {
|
try {
|
||||||
@@ -112,7 +168,7 @@ Vue.component('civil-engineering-manager', {
|
|||||||
if (data.success) {
|
if (data.success) {
|
||||||
window.notify('success', data.message);
|
window.notify('success', data.message);
|
||||||
this.$refs.fileInput.value = '';
|
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();
|
await this.fetchInitialData();
|
||||||
} else window.notify('error', data.error || 'Upload fehlgeschlagen.');
|
} else window.notify('error', data.error || 'Upload fehlgeschlagen.');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -131,6 +187,29 @@ Vue.component('civil-engineering-manager', {
|
|||||||
window.notify('error', 'Netzwerkfehler beim Löschen.');
|
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() {
|
async completeTask() {
|
||||||
this.completing = true;
|
this.completing = true;
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user