321 lines
16 KiB
JavaScript
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;
|
|
}
|
|
}
|
|
}) |