Merge branch 'feature/warehouse-shipping-note-additional-fix' into 'master'
switched 2 inputs See merge request fronk/thetool!754
This commit is contained in:
@@ -15,7 +15,7 @@ class WarehouseShippingNoteController extends TTCrud {
|
||||
['key' => 'deliveryAddressLine', 'text' => 'L.-Adr.', 'required' => true],
|
||||
['key' => 'deliveryAddressPLZ', 'text' => 'L.-Adr. PLZ', 'required' => true],
|
||||
['key' => 'deliveryAddressEMail', 'text' => 'L.-Adr. EMail', 'required' => true, 'table' => false],
|
||||
['key' => 'note', 'text' => 'Notiz', 'required' => true, 'table' => false],
|
||||
['key' => 'note', 'text' => 'Art der Arbeit', 'required' => true, 'table' => false],
|
||||
['key' => 'status',
|
||||
'text' => 'Status',
|
||||
'required' => true,
|
||||
@@ -387,6 +387,37 @@ class WarehouseShippingNoteController extends TTCrud {
|
||||
die(json_encode(['success' => true, 'status' => 'USER_NO_CAR']));
|
||||
}
|
||||
|
||||
protected function geoAutocompleteAction() {
|
||||
$search = $this->request->q;
|
||||
$search = urlencode($search);
|
||||
$url = "https://nominatim.haid.in/search?q=$search&format=json";
|
||||
$data = json_decode(file_get_contents($url), true);
|
||||
$out = [];
|
||||
|
||||
|
||||
|
||||
foreach ($data as $entry) {
|
||||
$parsedDisplayNameParts = [];
|
||||
foreach(explode(',', $entry['display_name']) as $part) {
|
||||
// if str_includes Bezirk remove it
|
||||
if (strpos($part, 'Bezirk') !== false) {
|
||||
continue;
|
||||
}
|
||||
$parsedDisplayNameParts[] = $part;
|
||||
}
|
||||
$out[] = ['value' => $entry['lat'] . "," . $entry['lon'], 'text' => implode(',', $parsedDisplayNameParts)];
|
||||
}
|
||||
self::returnJson($out);
|
||||
}
|
||||
|
||||
protected function geoReverseAction() {
|
||||
$lat = $this->request->lat;
|
||||
$lon = $this->request->lon;
|
||||
$url = "https://nominatim.haid.in/reverse?lat=$lat&lon=$lon&format=json";
|
||||
$data = json_decode(file_get_contents($url), true);
|
||||
self::returnJson($data);
|
||||
}
|
||||
|
||||
|
||||
//TODO: export this to an api class for openstreetmap
|
||||
protected function getDistanceAction() {
|
||||
@@ -413,7 +444,7 @@ class WarehouseShippingNoteController extends TTCrud {
|
||||
|
||||
$curl = curl_init();
|
||||
curl_setopt_array($curl, [
|
||||
CURLOPT_URL => "https://nominatim.openstreetmap.org/search?q=$address&format=json",
|
||||
CURLOPT_URL => "https://nominatim.haid.in/search?q=$address&format=json",
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_FOLLOWLOCATION => true,
|
||||
CURLOPT_ENCODING => "",
|
||||
|
||||
@@ -36,6 +36,10 @@
|
||||
grid-gap: 10px;
|
||||
}
|
||||
|
||||
.warehouse-shipping-note-modal-positions-entry-actions, .warehouse-shipping-note-modal-hours-entry-actions {
|
||||
grid-column: 2;
|
||||
}
|
||||
|
||||
.signModal > div {
|
||||
margin: 0;
|
||||
width: 100vw;
|
||||
|
||||
@@ -96,9 +96,11 @@ Vue.component('warehouse-shipping-note-modal-hours-entry', {
|
||||
this.kilometerCount = response.data.distance
|
||||
},
|
||||
async updateCarId() {
|
||||
if (!this.userId || this.carId) return;
|
||||
const response = await axios.get(window.TT_CONFIG["BASE_PATH"] + '/WarehouseShippingNote/timerecordingCarForUser?userId=' + this.userId);
|
||||
if (response.data.status === 'USER_NO_CAR') {
|
||||
this.window.notify('info', 'Kein zugewiesenes Fahrzeug gefunden');
|
||||
this.carId = '';
|
||||
return;
|
||||
}
|
||||
this.carId = response.data.id;
|
||||
@@ -114,8 +116,8 @@ Vue.component('warehouse-shipping-note-modal-hours-entry', {
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
if (!this.carId) this.updateCarId().then();
|
||||
if (!this.userId) this.userId = this.window.TT_CONFIG['USER_ID'];
|
||||
if (!this.carId) this.updateCarId().then();
|
||||
if (!this.date) this.updateDate();
|
||||
if (!this.kilometerCount) this.updateKilometerCount().then();
|
||||
|
||||
@@ -273,7 +275,7 @@ Vue.component('warehouse-shipping-note-modal-positions-entry', {
|
||||
if (!this.price) return this.window.notify('error', 'Bitte füllen sie den Preis aus');
|
||||
const data = {
|
||||
amount: this.amount,
|
||||
price: parseFloat(this.price)
|
||||
price: parseFloat(this.price)
|
||||
}
|
||||
if (!this.articleId && this.$refs.article.displayValue) {
|
||||
data.articleText = this.$refs.article.displayValue;
|
||||
@@ -331,7 +333,9 @@ Vue.component('warehouse-shipping-note-modal-positions-view', {
|
||||
<td colspan="4" class="text-center">Keine Einträge</td>
|
||||
</tr>
|
||||
<tr v-for="position in positions">
|
||||
<td>{{ position.article ? articleNames[position.article] : position.articlePacket ? articlePacketNames[position.articlePacket] : position.articleText }}</td>
|
||||
<td>{{ position.article ? articleNames[position.article] : position.articlePacket ? articlePacketNames[position.articlePacket] :
|
||||
position.articleText }}
|
||||
</td>
|
||||
<td>{{ position.amount }}</td>
|
||||
<td>{{ (position.price?.toFixed(2)) }} €</td>
|
||||
<td>
|
||||
@@ -415,7 +419,7 @@ Vue.component('warehouse-shipping-note-modal-positions', {
|
||||
},
|
||||
editEntry(entry) {
|
||||
this.selectedUpdateIndex = this.positions.indexOf(entry);
|
||||
if (entry.article)this.$refs.entry.articleId = entry.article;
|
||||
if (entry.article) this.$refs.entry.articleId = entry.article;
|
||||
if (entry.articlePacket) this.$refs.entry.articlePacketId = entry.articlePacket;
|
||||
if (entry.articleText) this.$refs.entry.$refs.article.displayValue = entry.articleText;
|
||||
this.$refs.entry.amount = entry.amount;
|
||||
@@ -460,14 +464,14 @@ Vue.component('warehouse-shipping-note-modal', {
|
||||
:del-addr-e-mail.sync="delAddrEMail"/>
|
||||
|
||||
|
||||
<template v-if="billAddrId && delAddrName && delAddrLine && delAddrPLZ && delAddrCity">
|
||||
<div v-show="delAddrFilled === false">
|
||||
<hr>
|
||||
<h4 class="text-center">Textelemente</h4>
|
||||
<warehouse-shipping-note-modal-text-elements :text-elements="textElements"/>
|
||||
|
||||
|
||||
<hr>
|
||||
<tt-textarea label="Einleitender Text" v-model="note" sm row/>
|
||||
<tt-textarea label="Art der Arbeit" v-model="note" sm row/>
|
||||
|
||||
|
||||
<hr>
|
||||
@@ -478,16 +482,16 @@ Vue.component('warehouse-shipping-note-modal', {
|
||||
<hr>
|
||||
<h4 class="text-center">Positionen</h4>
|
||||
<warehouse-shipping-note-modal-positions :positions.sync="positions" :bill-addr-id="billAddrId"/>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div v-show="delAddrFilled === true" class="text-center">Bitte füllen Sie die Rechnungs- und Lieferadresse aus</div>
|
||||
|
||||
<div v-else class="text-center">Bitte füllen Sie die Rechnungs- und Lieferadresse aus</div>
|
||||
|
||||
</div>
|
||||
<!-- TODO: fix these buttons-->
|
||||
<template v-slot:footer-prepend v-if="id !== 'create'">
|
||||
<button class="btn btn-info" @click="$emit('open-signing-modal', id)">Unterschreiben</button>
|
||||
<!-- <button class="btn btn-success" @click="alert('Accept')">Akzeptieren</button>-->
|
||||
<!-- <button class="btn btn-warning" @click="alert('Invoiced')">Verrechnet</button>-->
|
||||
<!-- <button class="btn btn-success" @click="alert('Accept')">Akzeptieren</button>-->
|
||||
<!-- <button class="btn btn-warning" @click="alert('Invoiced')">Verrechnet</button>-->
|
||||
</template>
|
||||
</tt-modal>
|
||||
`,
|
||||
@@ -553,13 +557,15 @@ Vue.component('warehouse-shipping-note-modal', {
|
||||
computed: {
|
||||
title() {
|
||||
return this.id === 'create' ? 'Lieferschein erstellen' : `Lieferschein #${this.id} bearbeiten`;
|
||||
},
|
||||
delAddrFilled() {
|
||||
return !this.delAddrName || !this.delAddrLine || !this.delAddrPLZ || !this.delAddrCity;
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
Vue.component('warehouse-shipping-note-modal-address', {
|
||||
// also add props for delAddrName, delAddrLine, delAddrPLZ, delAddrCity which we will sync with the parent component
|
||||
props: {
|
||||
billAddrId: {type: [String, Number], required: true},
|
||||
delAddrName: {type: String, required: true},
|
||||
@@ -578,6 +584,7 @@ Vue.component('warehouse-shipping-note-modal-address', {
|
||||
addresses: [],
|
||||
fetchedBillAddr: null,
|
||||
selectedAddr: '',
|
||||
newAddrGeoLatLon: '',
|
||||
}
|
||||
},
|
||||
//language=Vue
|
||||
@@ -590,11 +597,14 @@ Vue.component('warehouse-shipping-note-modal-address', {
|
||||
</template>
|
||||
|
||||
<template v-else-if="addressMode === 'new'">
|
||||
<tt-input v-model="delAddrName" label="Lieferadresse Name" sm row/>
|
||||
<tt-input v-model="delAddrLine" label="Lieferadresse" sm row/>
|
||||
<tt-input v-model="delAddrPLZ" label="Lieferadresse PLZ" sm row/>
|
||||
<tt-input v-model="delAddrCity" label="Lieferadresse Ort" sm row/>
|
||||
<tt-input v-model="delAddrEMail" label="Lieferadresse E-Mail" sm row/>
|
||||
<tt-input :value="delAddrName" @input="$emit('update:delAddrName', $event)" label="Lieferadresse Name*" sm row/>
|
||||
<tt-input :value="delAddrEMail" @input="$emit('update:delAddrEMail', $event)" label="Lieferadresse E-Mail" sm row/>
|
||||
<tt-autocomplete :api-url="window.TT_CONFIG['BASE_PATH'] + '/WarehouseShippingNote/geoAutocomplete'" @input="newAddrGeoLatLon = $event" label="Adresse*" sm row/>
|
||||
|
||||
<span v-if="delAddrLine && delAddrPLZ && delAddrCity">Adresse: {{ delAddrLine }}, {{ delAddrPLZ }} {{ delAddrCity }}</span>
|
||||
<!-- <tt-input :value="delAddrLine" @input="$emit('update:delAddrLine', $event)" label="Lieferadresse" sm row/>-->
|
||||
<!-- <tt-input :value="delAddrPLZ" @input="$emit('update:delAddrPLZ', $event)" label="Lieferadresse PLZ" sm row/>-->
|
||||
<!-- <tt-input :value="delAddrCity" @input="$emit('update:delAddrCity', $event)" label="Lieferadresse Ort" sm row/>-->
|
||||
</template>
|
||||
</div>
|
||||
`,
|
||||
@@ -602,24 +612,34 @@ Vue.component('warehouse-shipping-note-modal-address', {
|
||||
billAddrId: {handler: 'updateBillingMode', immediate: false},
|
||||
addressMode: {handler: 'fetchDeliveryAddresses', immediate: false},
|
||||
selectedAddr: {handler: 'setSelectedAddrValues', immediate: false},
|
||||
newAddrGeoLatLon: {handler: 'fetchGeoAddress', immediate: false},
|
||||
},
|
||||
methods: {
|
||||
async fetchGeoAddress() {
|
||||
if (!this.newAddrGeoLatLon) return;
|
||||
const [lat, lon] = this.newAddrGeoLatLon.split(',');
|
||||
const response = await axios.get(window.TT_CONFIG["BASE_PATH"] + '/WarehouseShippingNote/geoReverse?lat=' + lat + '&lon=' + lon);
|
||||
|
||||
if (response.data.address.road) {
|
||||
this.$emit('update:delAddrLine', `${response.data.address.road}${response.data.address.house_number ? ' ' + response.data.address.house_number : ''}`);
|
||||
} else {
|
||||
this.$emit('update:delAddrLine', `${response.data.address.village}${response.data.address.house_number ? ' ' + response.data.address.house_number : ''}`);
|
||||
}
|
||||
|
||||
this.$emit('update:delAddrPLZ', response.data.address.postcode);
|
||||
this.$emit('update:delAddrCity', response.data.address.village || response.data.address.city || response.data.address.town);
|
||||
},
|
||||
async updateBillingMode() {
|
||||
await this.fetchDeliveryAddresses();
|
||||
// this.addressMode = 'billing';
|
||||
|
||||
console.log('updateBillingMode');
|
||||
|
||||
// Here we check if the address is already in the list of addresses, if not we will set the addressMode to billing and fetch the billing address
|
||||
if (this.delAddrName && this.delAddrLine && this.delAddrPLZ && this.delAddrCity) {
|
||||
const foundAddress = this.addresses.find(address => address.deliveryAddressName ===
|
||||
this.delAddrName &&
|
||||
address.deliveryAddressLine ===
|
||||
this.delAddrLine &&
|
||||
address.deliveryAddressPLZ ===
|
||||
this.delAddrPLZ &&
|
||||
address.deliveryAddressCity ===
|
||||
this.delAddrCity && address.deliveryAddressEMail === this.delAddrEMail);
|
||||
const foundAddress = this.addresses.find(address =>
|
||||
address.deliveryAddressName === this.delAddrName &&
|
||||
address.deliveryAddressLine === this.delAddrLine &&
|
||||
address.deliveryAddressPLZ === this.delAddrPLZ &&
|
||||
address.deliveryAddressCity === this.delAddrCity &&
|
||||
address.deliveryAddressEMail === this.delAddrEMail);
|
||||
if (foundAddress) {
|
||||
this.addressMode = 'existing';
|
||||
this.selectedAddr = foundAddress.id;
|
||||
@@ -631,11 +651,21 @@ Vue.component('warehouse-shipping-note-modal-address', {
|
||||
await this.fetchBillingAddress();
|
||||
}
|
||||
},
|
||||
async fetchDeliveryAddresses() {
|
||||
async fetchDeliveryAddresses(newVal, oldVal) {
|
||||
if ((oldVal === 'billing' || oldVal === 'existing') && newVal === 'new') {
|
||||
this.$emit('update:delAddrName', '');
|
||||
this.$emit('update:delAddrLine', '');
|
||||
this.$emit('update:delAddrPLZ', '');
|
||||
this.$emit('update:delAddrCity', '');
|
||||
this.$emit('update:delAddrEMail', '');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.addressMode === 'billing' && this.billAddrId) {
|
||||
await this.fetchBillingAddress();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.billAddrId || this.addressMode !== 'existing' || this.fetchedBillAddr === this.billAddrId) return;
|
||||
|
||||
const response = await axios.get(window.TT_CONFIG["BASE_PATH"] + '/WarehouseShippingNote/getDeliveryAddresses?billingAddressId=' + this.billAddrId);
|
||||
@@ -667,7 +697,8 @@ Vue.component('warehouse-shipping-note-modal-address', {
|
||||
this.window.notify('error', 'Rechnungsadresse konnte nicht gefunden werden');
|
||||
return;
|
||||
}
|
||||
this.window.notify('success', 'Rechnungsadresse gefunden');
|
||||
// TODO: here is still a bug that we fetch the billing address twice
|
||||
// this.window.notify('success', 'Rechnungsadresse gefunden');
|
||||
|
||||
this.$emit('update:delAddrName',
|
||||
response.data.result.address.company || response.data.result.address.firstname + ' ' + response.data.result.address.lastname);
|
||||
@@ -690,9 +721,9 @@ Vue.component('warehouse-shipping-note-signature-pad', {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
window: window,
|
||||
signaturePad: null,
|
||||
shippingNote: null,
|
||||
window: window,
|
||||
signaturePad: null,
|
||||
shippingNote: null,
|
||||
signatureName: '',
|
||||
}
|
||||
},
|
||||
@@ -700,19 +731,24 @@ Vue.component('warehouse-shipping-note-signature-pad', {
|
||||
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 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});
|
||||
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');
|
||||
|
||||
@@ -99,7 +99,7 @@ Vue.component('tt-autocomplete', {
|
||||
|
||||
|
||||
if (this.value && this.apiUrl) {
|
||||
const response = await axios.get(`${this.apiUrl}&autocomplete=1&searchedID=${this.value}`);
|
||||
const response = await axios.get(`${this.apiUrl}${this.apiUrl.includes('?') ? '&' : '?'}autocomplete=1&searchedID=${this.value}`);
|
||||
this.displayValue = response.data[0].text;
|
||||
} else if (this.value) {
|
||||
const selectedItem = this.items.find(item => item.value === this.value);
|
||||
@@ -145,7 +145,6 @@ Vue.component('tt-autocomplete', {
|
||||
|
||||
this.isLoading = true;
|
||||
clearTimeout(this.fetchSuggestionsDebounceTimer);
|
||||
console.log(this.displayValue);
|
||||
|
||||
this.fetchSuggestionsDebounceTimer = setTimeout(() => {
|
||||
setTimeout(async () => {
|
||||
@@ -155,7 +154,7 @@ Vue.component('tt-autocomplete', {
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await axios.get(`${this.apiUrl}&autocomplete=1&q=${encodeURIComponent(this.displayValue)}`);
|
||||
const response = await axios.get(`${this.apiUrl}${this.apiUrl.includes('?') ? '&' : '?'}autocomplete=1&q=${encodeURIComponent(this.displayValue)}`);
|
||||
if (response.data?.status === 'error') {
|
||||
this.displayingItems = [];
|
||||
} else {
|
||||
|
||||
@@ -38,7 +38,9 @@ Vue.component('tt-modal', {
|
||||
this.$emit('update:show', false)
|
||||
}
|
||||
if (event.key === 'Enter' && this.save) {
|
||||
// only submit
|
||||
if (event.target.tagName === 'TEXTAREA' || event.target.tagName === 'INPUT') {
|
||||
return
|
||||
}
|
||||
this.$emit('submit')
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user