Files
thetool/public/js/pages/WarehouseShippingNote/WarehouseShippingNote.js
2024-10-10 08:49:50 +02:00

321 lines
16 KiB
JavaScript

const defaultCrudModalData = {
billingAddressId: '',
deliveryAddressName: '',
deliveryAddressLine: '',
deliveryAddressPLZ: '',
deliveryAddressCity: '',
status: 'new',
positions: [],
textElements: {}
}
window.crudModalStatusOptions =
[{value: 'new', text: 'Neu'}, {value: 'accepted', text: 'Akzeptiert'}, {value: 'invoiced', text: 'In Rechnung gestellt', disabled: true}]
// create a additional vue component for showing positions in the table with lazy loading for article titles and description
Vue.component('warehouse-shipping-note-positions', {
//language=Vue
props: {
positions: Array
}, data() {
return {
articleData: {}, loading: false
}
}, template: `
<div>
<div v-if="loading" class="text-center">
<i class="fa fa-spinner fa-spin"></i>
</div>
<ul v-if="!loading">
<li v-for="position in positions">
<span>{{ position.amount }}x {{ articleData[position.article]?.text }}</span>
</li>
</ul>
</div>
`, async mounted() {
this.loading = true;
for (const position of this.positions) {
const response = await axios.get(window.TT_CONFIG["BASE_PATH"] + '/WarehouseArticle/autoComplete?searchedID=' + position.article);
this.$set(this.articleData, position.article, response.data[0]);
}
this.loading = false;
}
})
// noinspection JSUnusedLocalSymbols
Vue.component('warehouse-shipping-note', {
//language=Vue
template: `
<tt-card>
<tt-modal :show.sync="crudModal" :id="crudModalId"
:delete="false"
@submit="createOrUpdate()"
:title="crudModalId === 'create' ? 'Lieferschein erstellen' : 'Lieferschein bearbeiten'">
<tt-autocomplete v-model="crudModalData.billingAddressId"
:api-url="window.TT_CONFIG['BASE_PATH'] + '/Address/Api?do=findAddress'"
label="Rechnungsadresse" sm row/>
<tt-select v-model="crudModalSelectDeliveryAddressMode" :options="crudModalSelectDeliveryAddressModeItems" label="Lieferadresse Art" sm
row/>
<template v-if="crudModalSelectDeliveryAddressMode === 'existing'">
<tt-select v-model="crudModalDataDeliveryAddressSelected" :options="crudModalDataDeliveryAddressOptions" label="Lieferadresse" sm row/>
</template>
<template v-else-if="crudModalSelectDeliveryAddressMode === 'new'">
<tt-input v-model="crudModalData.deliveryAddressName" label="Lieferadresse Name" sm row/>
<tt-input v-model="crudModalData.deliveryAddressLine" label="Lieferadresse" sm row/>
<tt-input v-model="crudModalData.deliveryAddressPLZ" label="Lieferadresse PLZ" sm row/>
<tt-input v-model="crudModalData.deliveryAddressCity" label="Lieferadresse Ort" sm row/>
</template>
<tt-select v-if="crudModalVerifyMode === true" v-model="crudModalData.status" :options="window.crudModalStatusOptions" label="Status" sm
row/>
<!-- show a checkbox for each textElement and if selected set it to selected [{"id":1,"title":"Zahlhinweis","content":"Bezahlung in 14 tagen","create":1728456765,"createBy":145}]-->
<template>
<hr>
<h4 class="text-center">Texte</h4>
<div v-for="textElement in textElements" style="display: inline-block; margin-right: 10px;">
<input type="checkbox" v-model="crudModalData.textElements[textElement.id]" :id="'textElement' + textElement.id">
<label :for="'textElement' + textElement.id">{{ textElement.title }}</label>
</div>
</template>
<hr>
<h4 class="text-center">Positionen</h4>
<template v-if="crudModalData.billingAddressId">
<div style="display: flex; justify-content: space-around;padding: 10px;">
<tt-autocomplete v-model="crudModalAddPositionArticle" :api-url="window.TT_CONFIG['BASE_PATH'] + '/WarehouseArticle/autoComplete'"
placeholder="Artikel" sm row/>
<tt-input v-model="crudModalAddPositionAmount" placeholder="Menge" sm row/>
<tt-input v-model="crudModalAddPositionPrice" placeholder="Preis" type="number" sm row/>
<button style="max-height: 29px" class="btn btn-sm btn-primary" @click="addPosition">Hinzufügen</button>
</div>
<table class="table table-sm">
<thead>
<tr>
<th>Position</th>
<th>Artikel</th>
<th>Menge</th>
<th>Preis</th>
<th></th>
</tr>
</thead>
<tbody>
<tr v-for="(position, index) in crudModalData.positions">
<td>{{ index + 1 }}</td>
<td>{{ articleNames[position.article] }}</td>
<td>{{ position.amount }}</td>
<td>{{ (position.price?.toFixed(2)) }} €</td>
<td>
<button class="btn btn-sm btn-danger" @click="crudModalData.positions.splice(index, 1)">Löschen</button>
</td>
</tr>
</tbody>
</table>
</template>
<template v-else>
<h5 class="text-center">Rechnungsadresse auswählen um Positionen hinzuzufügen</h5>
</template>
</tt-modal>
<warehouse-history-modal :show.sync="historyModal" :id="historyModalId"/>
<button @click="openCrudModal('create')" class="btn btn-primary">Lieferschein erstellen</button>
<button @click="openVerifyModal" class="btn btn-primary">Lieferscheine Freigeben</button>
<tt-table-crud emit-edit
@openHistory="historyModal = true; historyModalId = $event.id"
@print="window.open(window.TT_CONFIG['BASE_PATH'] + '/WarehouseShippingNote/createPDF?id=' + $event.id)"
@printWithPrice="window.open(window.TT_CONFIG['BASE_PATH'] + '/WarehouseShippingNote/createPDF?id=' + $event.id + '&price=true')"
@edit="openCrudModal($event)"
ref="table">
<template v-slot:expandedRow="{ row }">
<warehouse-shipping-note-positions :positions="JSON.parse(row.positions)"/>
</template>
</tt-table-crud>
</tt-card>
`, data() {
return {
window: window,
historyModal: false,
historyModalId: null,
crudModal: false,
crudModalSelectDeliveryAddressModeItems: [{text: 'Wie Rechnungsadresse', value: 'billing'},
{text: 'Bestehende Lieferadresse', value: 'existing'},
{text: 'Neue Lieferadresse', value: 'new'}],
crudModalSelectDeliveryAddressMode: 'billing',
crudModalDataDeliveryAddressOptions: [],
crudModalDataDeliveryAddressSelected: '',
crudModalVerifyMode: false,
crudModalId: null,
crudModalData: defaultCrudModalData,
crudModalAddPositionArticle: '',
crudModalAddPositionAmount: '',
crudModalAddPositionPrice: '',
articleNames: {},
textElements: [],
}
}, async mounted() {
const response = await axios.get(window.TT_CONFIG["BASE_PATH"] + '/WarehouseShippingNote/getAllTextElements');
this.textElements = response.data;
},
methods: {
async openVerifyModal() {
const unverifiedShippingNotes = await axios.post(window.TT_CONFIG['BASE_PATH'] + '/WarehouseShippingNote/get', {
"pagination": {"page": 1, "per_page": 1}, "filters": {
"status": "new"
}, "order": {"key": null, "order": "asc"}
});
if (unverifiedShippingNotes.data.rows.length === 0) {
this.window.notify('warning', 'Keine Lieferscheine zum Freigeben gefunden');
return;
}
await this.openCrudModal(unverifiedShippingNotes.data.rows[0]);
this.crudModalVerifyMode = true;
}, resetCrudModalData() {
this.crudModalData.billingAddressId = '';
this.crudModalData.deliveryAddressName = '';
this.crudModalData.deliveryAddressLine = '';
this.crudModalData.deliveryAddressPLZ = '';
this.crudModalData.deliveryAddressCity = '';
this.crudModalAddPositionArticle = '';
this.crudModalAddPositionAmount = '';
this.crudModalAddPositionPrice = '';
this.crudModalSelectDeliveryAddressMode = 'billing';
this.crudModalDataDeliveryAddressSelected = '';
this.crudModal = false;
}, async openCrudModal(data) {
this.resetCrudModalData();
this.crudModalVerifyMode = false;
if (data === 'create') {
this.crudModalId = 'create'
this.crudModalData = defaultCrudModalData
this.crudModal = true
} else {
const disconnectedData = JSON.parse(JSON.stringify(data));
if (disconnectedData.status !== 'new') {
this.window.notify('warning', 'Lieferschein kann nicht bearbeitet werden, da er bereits genehmigt wurde');
return;
}
disconnectedData.textElements = JSON.parse(disconnectedData.textElements);
disconnectedData.positions = JSON.parse(disconnectedData.positions);
for (const position of disconnectedData.positions) {
await this.fetchArticleNames(position.article);
}
this.crudModalId = 'update'
this.crudModalData = disconnectedData
this.crudModal = true
}
}, async addPosition() {
const missingFields = [];
// ---------- Check Required Fields ----------
if (!this.crudModalAddPositionArticle) missingFields.push('Artikel');
if (!this.crudModalAddPositionAmount) missingFields.push('Menge');
if (!this.crudModalAddPositionPrice) missingFields.push('Preis-Überschreibung');
if (missingFields.length > 0) {
window.notify('error', 'Bitte füllen Sie die folgenden Felder aus: ' + missingFields.join(', '));
return;
}
// ---------- Check if same article is already in positions ----------
const articleAlreadyInPositions = this.crudModalData.positions.find(position => position.article === this.crudModalAddPositionArticle);
if (articleAlreadyInPositions) {
window.notify('error', 'Artikel ist bereits in den Positionen enthalten');
return;
}
await this.fetchArticleNames(this.crudModalAddPositionArticle);
this.crudModalData.positions.push({
article: this.crudModalAddPositionArticle, amount: this.crudModalAddPositionAmount, price: parseFloat(this.crudModalAddPositionPrice)
});
//TODO: post to server
}, async fetchArticleNames(articleId) {
const response = await axios.get(window.TT_CONFIG["BASE_PATH"] + '/WarehouseArticle/autoComplete?searchedID=' + articleId);
this.$set(this.articleNames, articleId, response.data[0].text);
}, async createOrUpdate() {
const response = await axios.post(this.crudModalId === 'create' ? window['TT_CONFIG']['CREATE_URL'] : window['TT_CONFIG']['UPDATE_URL'],
this.crudModalData);
if (response.data.success) {
this.$refs.table.$refs.table.refreshTable();
this.resetCrudModalData();
this.window.notify('success', response.data.message || 'Erfolgreich gespeichert');
} else {
this.window.notify('error',
response.data.errors ? Object.values(response.data.errors).join('<br>') : response.data.message || 'Ein Fehler ist aufgetreten');
}
}, async fetchDeliveryAddresses() {
if (!this.crudModalData.billingAddressId || this.crudModalSelectDeliveryAddressMode !== 'existing') return;
if (this.crudModalSelectDeliveryAddressMode === 'billing') {
const response = await axios.get(window.TT_CONFIG["BASE_PATH"] + '/Address/api?do=getAddress&id=' + this.crudModalData.billingAddressId);
if (response.data.status !== 'OK' || !response.data.result.address) {
window.notify('error', 'Rechnungsadresse konnte nicht gefunden werden');
return;
}
this.crudModalData.deliveryAddressName =
response.data.result.address.company || response.data.result.address.firstname + ' ' + response.data.result.address.lastname;
this.crudModalData.deliveryAddressLine = response.data.result.address.street;
this.crudModalData.deliveryAddressPLZ = response.data.result.address.zip;
this.crudModalData.deliveryAddressCity = response.data.result.address.city;
}
if (!this.crudModalData.billingAddressId || this.crudModalSelectDeliveryAddressMode !== 'existing') return;
const response = await axios.get(window.TT_CONFIG["BASE_PATH"] +
'/WarehouseShippingNote/getDeliveryAddresses?billingAddressId=' +
this.crudModalData.billingAddressId);
this.crudModalDataDeliveryAddressOptions = response.data.map(address => {
address.value = address.id;
address.text = `${address.deliveryAddressName} - ${address.deliveryAddressLine}, ${address.deliveryAddressPLZ} ${address.deliveryAddressCity}`;
return address;
});
}
}, watch: {
crudModalAddPositionArticle: async function (newValue) {
if (!newValue) return;
const url = `${window.TT_CONFIG["BASE_PATH"]}/WarehouseShippingNote/getArticleAddressPrice?articleId=${newValue}&addressId=${this.crudModalData.billingAddressId}`;
const response = await axios.get(url);
this.crudModalAddPositionPrice = response.data.price;
},
crudModalData: {handler: 'fetchDeliveryAddresses', deep: true},
crudModalSelectDeliveryAddressMode: {handler: 'fetchDeliveryAddresses', deep: true},
crudModalDataDeliveryAddressSelected: function (newValue) {
if (!newValue) return;
const selectedAddress = this.crudModalDataDeliveryAddressOptions.find(address => address.id === parseInt(newValue));
if (!selectedAddress) {
window.notify('error', 'Lieferadresse konnte nicht gefunden werden');
return;
}
this.crudModalData.deliveryAddressName = selectedAddress.deliveryAddressName;
this.crudModalData.deliveryAddressLine = selectedAddress.deliveryAddressLine;
this.crudModalData.deliveryAddressPLZ = selectedAddress.deliveryAddressPLZ;
this.crudModalData.deliveryAddressCity = selectedAddress.deliveryAddressCity;
}
}
})