336 lines
17 KiB
JavaScript
336 lines
17 KiB
JavaScript
// noinspection EqualityComparisonWithCoercionJS
|
|
Vue.component('warehouse-shipping-note-modal', {
|
|
props: {
|
|
id: {type: [String, Number], required: true},
|
|
},
|
|
data() {
|
|
return {
|
|
window: window,
|
|
loading: false,
|
|
addressEditMode: false,
|
|
billAddrAutoCompleteUrl: window.TT_CONFIG['BASE_PATH'] + '/Address/Api?do=findAddress&fibu_primary_account=1',
|
|
geoAutocompleteUrl: window.TT_CONFIG['BASE_PATH'] + '/WarehouseShippingNote/geoAutocomplete',
|
|
shippingNote: {
|
|
billingAddressId: null,
|
|
deliveryAddressName: '',
|
|
deliveryAddressEMail: '',
|
|
deliveryAddressLine: '',
|
|
deliveryAddressPLZ: '',
|
|
deliveryAddressCity: '',
|
|
status: 'new',
|
|
metadata: null,
|
|
positions: [],
|
|
textElements: [],
|
|
hoursEntries: [],
|
|
},
|
|
availableTypes: [
|
|
{text: 'Xinon Intern (XI)', value: 'XI'},
|
|
{text: 'Xinon Hersteller (XH)', value: 'XH'},
|
|
{text: 'Energie Steiermark (ESTMK)', value: 'ESTMK'},
|
|
{text: 'Steirische Breitband- und Digitalinfrastrukturgesellschaft (SBIDI)', value: 'SBIDI'},
|
|
{text: 'Verrechnen (V)', value: 'V'},
|
|
],
|
|
hoursLoading: false,
|
|
geoAddr: '',
|
|
selectedBillingAddress: '',
|
|
positionsConfig: {
|
|
customOrdering: 'article',
|
|
fields: {
|
|
article: {
|
|
type: 'input-article',
|
|
label: 'Artikel',
|
|
apiUrl: '/WarehouseArticle/autoComplete',
|
|
customFieldReference: 'WarehouseArticle',
|
|
emitDisplayValue: true,
|
|
},
|
|
amount: {type: 'input', label: 'Menge', inputType: 'number'},
|
|
isEnergieMaterial: {type: 'checkbox', label: 'Beigestelltes Material'},
|
|
},
|
|
validateFormOptions: [
|
|
{key: 'article', message: 'Bitte füllen Sie den Artikel aus'},
|
|
{key: 'amount', message: 'Bitte füllen Sie die Menge aus'},
|
|
]
|
|
},
|
|
hoursConfig: {
|
|
fields: {
|
|
userId: {
|
|
type: 'autocomplete',
|
|
label: 'Mitarbeiter',
|
|
apiUrl: '/WarehouseShippingNote/userAutoComplete',
|
|
emitDisplayValue: true,
|
|
},
|
|
date: {type: 'input', label: 'Datum', inputType: 'date'},
|
|
hourCount: {type: 'input', label: 'Stunden', inputType: 'number'},
|
|
carId: {
|
|
type: 'autocomplete',
|
|
label: 'Auto',
|
|
apiUrl: '/WarehouseShippingNote/timerecordingCarAutoComplete',
|
|
emitDisplayValue: true,
|
|
showCondition: (formData) => {
|
|
return !isNaN(parseInt(formData.userId))
|
|
},
|
|
},
|
|
externalCar: {
|
|
type: 'checkbox',
|
|
label: 'Externes Auto',
|
|
showCondition: (formData) => {
|
|
return isNaN(parseInt(formData.userId)) || !formData.userId
|
|
},
|
|
},
|
|
kilometerCount: {type: 'input', label: 'Kilometer', inputType: 'number'},
|
|
hourType: {type: 'select', label: 'Stundenart', options: [
|
|
{text: 'Normal', value: undefined},
|
|
{text: '+50%', value: '50'},
|
|
{text: '+100%', value: '100'},
|
|
{text: 'Regie', value: 'regie'},
|
|
]},
|
|
comment: {type: 'input', label: 'Kommentar'},
|
|
},
|
|
validateFormOptions: [
|
|
{key: 'userId', message: 'Bitte füllen Sie den Mitarbeiter aus'},
|
|
{key: 'hourCount', message: 'Bitte füllen Sie die Stunden aus'},
|
|
]
|
|
}
|
|
|
|
|
|
}
|
|
},
|
|
//language=Vue
|
|
template: `
|
|
<tt-modal :show="true"
|
|
:delete="false"
|
|
:title="id === 'create' ? 'Lieferschein erstellen' : 'Lieferschein bearbeiten'"
|
|
@update:show="$emit('close')"
|
|
@submit="submit"
|
|
:save-loading="loading"
|
|
>
|
|
<div style="width: 99%">
|
|
<h4 class="text-center">Adresse</h4>
|
|
<tt-autocomplete :api-url="geoAutocompleteUrl" label="Adresse suchen" sm row v-model="geoAddr"/>
|
|
<tt-autocomplete :api-url="billAddrAutoCompleteUrl" label="Kunde suchen" sm row v-model="selectedBillingAddress"/>
|
|
<hr>
|
|
|
|
<tt-input v-model="shippingNote.deliveryAddressName" label="Name" sm row/>
|
|
<tt-input v-model="shippingNote.deliveryAddressEMail" label="E-Mail" sm row/>
|
|
<div v-if="!addressEditMode" class="d-flex align-items-start">
|
|
<div class="mb-1">
|
|
<div v-if="shippingNote.deliveryAddressLine">
|
|
{{ shippingNote.deliveryAddressLine }}<br>
|
|
{{ shippingNote.deliveryAddressPLZ }} {{ shippingNote.deliveryAddressCity }}
|
|
</div>
|
|
</div>
|
|
<button @click="addressEditMode = true" class="btn btn-sm btn-link ml-2">
|
|
<i class="fas fa-edit"></i>
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Edit form -->
|
|
<div v-if="addressEditMode" class="address-edit-form mt-2">
|
|
<div style="display:grid; grid-template-columns: 3fr 1.5fr 1.5fr 1fr; gap: 10px">
|
|
<tt-input
|
|
label="Adresse"
|
|
type="text"
|
|
placeholder="Adresse"
|
|
v-model="shippingNote.deliveryAddressLine"
|
|
sm
|
|
></tt-input>
|
|
|
|
<tt-input
|
|
label="PLZ"
|
|
type="text"
|
|
placeholder="PLZ"
|
|
v-model="shippingNote.deliveryAddressPLZ"
|
|
sm
|
|
no-form-group
|
|
></tt-input>
|
|
<tt-input
|
|
label="Stadt"
|
|
type="text"
|
|
placeholder="Stadt"
|
|
v-model="shippingNote.deliveryAddressCity"
|
|
sm
|
|
no-form-group
|
|
></tt-input>
|
|
<button @click="addressEditMode = false" class="btn btn-sm btn-link">
|
|
<i class="fas fa-times"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<template v-if="window.TT_CONFIG['WAREHOUSE_ADMIN'] == true">
|
|
<hr>
|
|
<tt-autocomplete v-model="shippingNote.billingAddressId" :api-url="billAddrAutoCompleteUrl" label="Rechnungsadresse" sm row/>
|
|
<tt-autocomplete v-model="shippingNote.type" :items="availableTypes" label="Typ" sm row return-text case-insensitive/>
|
|
</template>
|
|
|
|
<tt-textarea v-model="shippingNote.note" label="Art der Arbeit" sm row/>
|
|
|
|
<template v-if="shippingNote.deliveryAddressLine">
|
|
<hr>
|
|
<h4 class="text-center">Stunden</h4>
|
|
<tt-positions-manager
|
|
ref="hoursManager"
|
|
:submit-loading="hoursLoading"
|
|
@updateField-userId="updateCarId"
|
|
@updateField-carId="updateKilometer"
|
|
@updateField-externalCar="updateKilometer"
|
|
:config="hoursConfig" v-model="shippingNote.hoursEntries" sm row/>
|
|
|
|
<hr>
|
|
<h4 class="text-center">Positionen</h4>
|
|
<tt-positions-manager :config="positionsConfig" v-model="shippingNote.positions" sm row ref="positionsManager"/>
|
|
</template>
|
|
<div v-else class="text-danger text-center font-weight-bolder font-16">Bitte Lieferadresse eingeben oder nach Kunden suchen</div>
|
|
|
|
</div>
|
|
<template v-slot:footer-prepend v-if="id !== 'create' && !$slots['footer-prepend']">
|
|
<button class="btn btn-info" @click="$emit('open-signing-modal', id)">Unterschreiben</button>
|
|
</template>
|
|
<template v-slot:footer-prepend v-else>
|
|
<slot name="footer-prepend"/>
|
|
</template>
|
|
|
|
|
|
</tt-modal>
|
|
`,
|
|
async mounted() {
|
|
if (this.id === 'create') return;
|
|
|
|
const {data} = await axios.get(`${window.TT_CONFIG["BASE_PATH"]}/WarehouseShippingNote/getById`, {params: {id: this.id}});
|
|
this.shippingNote = {
|
|
...data,
|
|
positions: JSON.parse(data.positions),
|
|
hoursEntries: JSON.parse(data.hoursEntries),
|
|
metadata: data.metadata ? JSON.parse(data.metadata) : null
|
|
};
|
|
},
|
|
watch: {
|
|
geoAddr: async function() {
|
|
if (!this.geoAddr) return;
|
|
const areaMode = this.geoAddr.includes(', area');
|
|
const [lat, lon] = this.geoAddr.replace(', area', '').split(',');
|
|
const { address } = (await axios.get(`${window.TT_CONFIG["BASE_PATH"]}/WarehouseShippingNote/geoReverse?lat=${lat}&lon=${lon}`)).data;
|
|
|
|
if (areaMode === false) {
|
|
const addrKey = ['road', 'village', 'hamlet', 'residential', 'city'].find(k => address[k]);
|
|
this.shippingNote.deliveryAddressLine = addrKey ? `${address[addrKey]}${address["house_number"] ? ` ${address["house_number"]}` : ''}` : '';
|
|
} else if (areaMode === true) {
|
|
this.shippingNote.deliveryAddressLine = 'Gebiet';
|
|
this.shippingNote.deliveryAddressName = `Gebiet ${address["village"] ?? address.residential ?? address.city ?? address["town"]}`;
|
|
}
|
|
|
|
this.shippingNote.deliveryAddressPLZ = address["postcode"];
|
|
this.shippingNote.deliveryAddressCity = address["village"] ?? address.residential ?? address.city ?? address["town"];
|
|
this.updateKilometer().then();
|
|
},
|
|
selectedBillingAddress: async function() {
|
|
const response = await axios.get(window.TT_CONFIG["BASE_PATH"] + '/Address/api?do=getAddress&id=' + this.selectedBillingAddress);
|
|
if (response.data.status !== 'OK' || !response.data.result.address) {
|
|
this.window.notify('error', 'Rechnungsadresse konnte nicht gefunden werden');
|
|
return;
|
|
}
|
|
|
|
this.shippingNote.deliveryAddressName = response.data.result.address["company"] || response.data.result.address.firstname + ' ' + response.data.result.address.lastname;
|
|
this.shippingNote.deliveryAddressLine = response.data.result.address.street;
|
|
this.shippingNote.deliveryAddressPLZ = response.data.result.address.zip;
|
|
this.shippingNote.deliveryAddressCity = response.data.result.address.city;
|
|
this.shippingNote.deliveryAddressEMail = response.data.result.address.email;
|
|
}
|
|
},
|
|
methods: {
|
|
async submit(newStatus = null) {
|
|
this.loading = true;
|
|
if (!this.shippingNote.positions.length && !this.shippingNote.hoursEntries.length) {
|
|
this.loading = false;
|
|
return window.notify('error', 'Mindestens eine Position oder eine Stundenbuchung sind erforderlich');
|
|
}
|
|
|
|
if (this.availableTypes.find(t => t.text === this.shippingNote.type)) {
|
|
this.shippingNote.type = this.availableTypes.find(t => t.name === this.shippingNote.type).value;
|
|
}
|
|
|
|
if (newStatus !== null) {
|
|
this.shippingNote.status = newStatus;
|
|
}
|
|
|
|
const response = await axios.post(`${window.TT_CONFIG["BASE_PATH"]}/WarehouseShippingNote/${this.id === 'create' ? 'create' : 'update'}`, this.shippingNote);
|
|
|
|
if ((this.id !== 'create' && response.data.success === true) || response.data.success === true) this.$emit('close');
|
|
|
|
window.notify(response.data.success ? 'success' : 'error',
|
|
response.data.success
|
|
? this.id === 'create' ? response.data.message : (response.data.message ?? 'Bestellung erfolgreich aktualisiert')
|
|
: (response.data.errors ? Object.values(response.data.errors).join('<br>') : response.data.message || 'Ein Fehler ist aufgetreten')
|
|
);
|
|
this.loading = false;
|
|
},
|
|
async updateCarId(userId) {
|
|
if (!userId) return this.$refs.hoursManager.updateField('carId', null);
|
|
this.hoursLoading = true;
|
|
const {data} = await axios.get(window.TT_CONFIG["BASE_PATH"] + '/WarehouseShippingNote/timerecordingCarForUser?userId=' + userId);
|
|
if (data.status === 'USER_NO_CAR') this.$refs.hoursManager.updateField('carId', null);
|
|
else this.$refs.hoursManager.updateField('carId', data.id);
|
|
this.hoursLoading = false;
|
|
},
|
|
async updateKilometer(carId) {
|
|
if (!carId || carId === '0' && this.$refs.hoursManager) return this.$refs.hoursManager?.updateField('kilometerCount', null);
|
|
this.hoursLoading = true;
|
|
const delAddr = this.shippingNote.deliveryAddressLine + ' ' + this.shippingNote.deliveryAddressPLZ + ' ' + this.shippingNote.deliveryAddressCity;
|
|
const {data} = await axios.get(window.TT_CONFIG["BASE_PATH"] + '/WarehouseShippingNote/getDistance?from=Xinon%20GmbH&to=' + delAddr);
|
|
this.$refs.hoursManager.updateField('kilometerCount', data.distance);
|
|
this.hoursLoading = false;
|
|
}
|
|
}
|
|
})
|
|
|
|
Vue.component('warehouse-shipping-note-signature-pad', {
|
|
props: {
|
|
shippingNoteId: {type: Number, required: true}
|
|
},
|
|
data() {
|
|
return {
|
|
window: window,
|
|
signaturePad: null,
|
|
shippingNote: null,
|
|
signatureName: '',
|
|
}
|
|
},
|
|
//language=Vue
|
|
template: `
|
|
<tt-modal class="signModal" :show="true" :delete="false" :submit="false" @update:show="$emit('close')" :title="'Unterschrift'">
|
|
<div style="max-width: 520px;display: flex; flex-direction: column; align-items: center;">
|
|
<div style="width: 480px">
|
|
<tt-input v-model="signatureName" label="Name" row/>
|
|
</div>
|
|
<div>
|
|
<canvas id="signature-pad" width="500" height="200" style="border: 1px solid black"></canvas>
|
|
</div>
|
|
<div>
|
|
<button class="btn btn-primary" @click="submit()">Speichern</button>
|
|
<button class="btn btn-primary" @click="signaturePad.clear()">Leeren</button>
|
|
</div>
|
|
</div>
|
|
</tt-modal>
|
|
`,
|
|
methods: {
|
|
async submit() {
|
|
const data = this.signaturePad.toDataURL();
|
|
const response = await axios.post(window.TT_CONFIG['BASE_PATH'] + '/WarehouseShippingNote/sign?id=' + this.shippingNoteId,
|
|
{signature: data, signatureName: this.signatureName});
|
|
if (response.data.success) {
|
|
this.window.notify('success', response.data.message || 'Erfolgreich unterschrieben');
|
|
this.$emit('close');
|
|
} else {
|
|
this.window.notify('error',
|
|
response.data.errors ? Object.values(response.data.errors).join('<br>') : response.data.message || 'Ein Fehler ist aufgetreten');
|
|
}
|
|
},
|
|
},
|
|
async mounted() {
|
|
// fetch shipping note by id
|
|
const response = await axios.get(window.TT_CONFIG["BASE_PATH"] + '/WarehouseShippingNote/getById?id=' + this.shippingNoteId);
|
|
this.shippingNote = response.data;
|
|
this.signaturePad = new SignaturePad(document.getElementById('signature-pad'));
|
|
}
|
|
})
|