update for warehouse
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
Vue.component('warehouse-administration', {
|
||||
//language=Vue
|
||||
template: `
|
||||
<tt-card title="Warehouse Administration">
|
||||
<warehouse-administration-switch/>
|
||||
|
||||
<div class="button-group" style="max-width: 300px;display: flex; flex-direction: column; gap: 10px;">
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
:disabled="isLoading"
|
||||
@click="createLocationsForAllEmployees"
|
||||
title="Automatische Lagerorterstellung für Mitarbeiter. Mitarbeiter mit Firmenwagen erhalten einen zugehörigen Fahrzeuglagerort.">
|
||||
Lagerorte für alle Mitarbeiter erstellen
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
:disabled="isLoading"
|
||||
@click="updateAllSalesPrices">
|
||||
Alle Verkaufspreise updaten
|
||||
</button>
|
||||
</div>
|
||||
</tt-card>
|
||||
`,
|
||||
data() {
|
||||
return {
|
||||
isLoading: false,
|
||||
window: window,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
async createLocationsForAllEmployees() {
|
||||
this.isLoading = true;
|
||||
const response = await axios.get(window.TT_CONFIG.BASE_URL + '/createLocations');
|
||||
|
||||
if (response.data.success) {
|
||||
this.window.notify('success', response.data.message || 'Lagerorte wurden erfolgreich erstellt.');
|
||||
} else {
|
||||
this.window.notify('error', response.data.message || 'Fehler beim Erstellen der Lagerorte.');
|
||||
}
|
||||
this.isLoading = false;
|
||||
},
|
||||
async updateAllSalesPrices() {
|
||||
this.isLoading = true;
|
||||
const response = await axios.get(window.TT_CONFIG.BASE_PATH + '/WarehouseArticle/updatePrices');
|
||||
if (response.data.success) {
|
||||
this.window.notify('success', 'Verkaufspreise wurden erfolgreich aktualisiert.');
|
||||
} else {
|
||||
this.window.notify('error', 'Fehler beim Aktualisieren der Verkaufspreise.');
|
||||
}
|
||||
this.isLoading = false;
|
||||
},
|
||||
sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -250,11 +250,14 @@ Vue.component('warehouse-article', {
|
||||
//language=Vue
|
||||
template: `
|
||||
<tt-card>
|
||||
|
||||
<tt-table-crud ref="table"
|
||||
@openHistory="historyModal = true; historyModalId = $event.id"
|
||||
@editDistributorEntries="distributorModal = true; distributorModalId = $event.id"
|
||||
@editPricesEntries="priceModal = true; priceModalId = $event.id"
|
||||
@editThresholdEntries="thresholdModal = true; thresholdModalId = $event.id">
|
||||
@editThresholdEntries="thresholdModal = true; thresholdModalId = $event.id"
|
||||
@addToCart="addShoppingCartModal = true; addShoppingCartModalId = $event.id"
|
||||
>
|
||||
|
||||
<template v-slot:cheapestsellprice="{ row }">
|
||||
<template v-for="price in JSON.parse(row.cheapestSellPrice)">
|
||||
@@ -268,6 +271,38 @@ Vue.component('warehouse-article', {
|
||||
|
||||
</tt-table-crud>
|
||||
|
||||
<tt-expandable-shopping-cart :cart-items="shoppingCart" @submitOrder="prepareOrder"/>
|
||||
<tt-modal :show.sync="addShoppingCartModal" title="Artikel zur Bestellung hinzufügen" :delete="false" @submit="addToShoppingCart"
|
||||
@close="addShoppingCartModal = false">
|
||||
<tt-input v-model="addShoppingCartModalCount" placeholder="Menge" type="number" sm></tt-input>
|
||||
</tt-modal>
|
||||
|
||||
<tt-modal :show.sync="confirmOrderModal" title="Bestellung bestätigen" :delete="false"
|
||||
save-text="Bestätigen" @submit="createOrder"
|
||||
@close="confirmOrderModal = false; confirmOrderModalData = null">
|
||||
<span>
|
||||
Es werden Bestellungen an folgende Lieferanten gesendet:
|
||||
</span>
|
||||
<div v-for="(order, index) in confirmOrderModalData" :key="index">
|
||||
<h4>{{order.distributor[0].name}} - {{order.orderAmount}} €
|
||||
</h4>
|
||||
<table class="table table-bordered">
|
||||
<tr>
|
||||
<th>Artikel</th>
|
||||
<th>Menge</th>
|
||||
<th>Preis</th>
|
||||
<th>Summe</th>
|
||||
</tr>
|
||||
<tr v-for="(item, index) in order.orders" :key="index">
|
||||
<td>{{item.title}}</td>
|
||||
<td>{{item.amount}}</td>
|
||||
<td>{{item.purchasePrice}} €</td>
|
||||
<td>{{item.sum.toFixed(2)}} €</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</tt-modal>
|
||||
|
||||
<warehouse-distributor-modal :show.sync="distributorModal" :id="distributorModalId" @doUpdate="refreshTable"/>
|
||||
<warehouse-threshold-modal :show.sync="thresholdModal" :id="thresholdModalId" @doUpdate="refreshTable"/>
|
||||
<warehouse-article-price-modal :show.sync="priceModal" :id="priceModalId" @doUpdate="refreshTable"/>
|
||||
@@ -275,19 +310,56 @@ Vue.component('warehouse-article', {
|
||||
</tt-card>
|
||||
`, data() {
|
||||
return {
|
||||
window: window,
|
||||
historyModal: false,
|
||||
historyModalId: null,
|
||||
distributorModal: false,
|
||||
distributorModalId: null,
|
||||
thresholdModal: false,
|
||||
thresholdModalId: null,
|
||||
priceModal: false,
|
||||
priceModalId: null
|
||||
window: window,
|
||||
historyModal: false,
|
||||
historyModalId: null,
|
||||
distributorModal: false,
|
||||
distributorModalId: null,
|
||||
thresholdModal: false,
|
||||
thresholdModalId: null,
|
||||
priceModal: false,
|
||||
priceModalId: null,
|
||||
shoppingCart: [],
|
||||
addShoppingCartModal: false,
|
||||
addShoppingCartModalId: null,
|
||||
addShoppingCartModalCount: '',
|
||||
confirmOrderModal: false,
|
||||
confirmOrderModalData: null,
|
||||
|
||||
}
|
||||
}, methods: {
|
||||
refreshTable() {
|
||||
this.$refs.table.$refs.table.refreshTable();
|
||||
}, async addToShoppingCart() {
|
||||
if (this.addShoppingCartModalCount < 1) { // Check if amount is set
|
||||
window.notify('error', 'Bitte geben Sie eine Menge ein.');
|
||||
return;
|
||||
}
|
||||
if (this.shoppingCart.some(item => item.itemId === this.addShoppingCartModalId)) { // Check if same article is already in cart
|
||||
window.notify('error', 'Artikel bereits im Warenkorb.');
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await axios.get(`${window['TT_CONFIG']['BASE_PATH']}/WarehouseArticle/getById?id=${this.addShoppingCartModalId}`);
|
||||
this.shoppingCart.push({amount: parseInt(this.addShoppingCartModalCount), itemId: this.addShoppingCartModalId, title: response.data.title});
|
||||
this.addShoppingCartModal = false;
|
||||
this.addShoppingCartModalId = null;
|
||||
this.addShoppingCartModalCount = '';
|
||||
window.notify('success', 'Artikel erfolgreich hinzugefügt.');
|
||||
}, async prepareOrder() {
|
||||
const response = await axios.post(`${window['TT_CONFIG']['BASE_PATH']}/WarehouseArticle/prepareOrder`, this.shoppingCart);
|
||||
this.confirmOrderModal = true;
|
||||
this.confirmOrderModalData = response.data;
|
||||
},
|
||||
async createOrder() {
|
||||
const response = await axios.post(`${window['TT_CONFIG']['BASE_PATH']}/WarehouseOrder/createOrder`, this.confirmOrderModalData);
|
||||
this.confirmOrderModal = false;
|
||||
this.confirmOrderModalData = null;
|
||||
this.shoppingCart = [];
|
||||
window.notify(response.data.success ? 'success' : 'error', response.data.message);
|
||||
setTimeout(() => {
|
||||
window.location.href = `${window['TT_CONFIG']['BASE_PATH']}/WarehouseOrder`;
|
||||
}, 2000);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
Vue.component('warehouse-article-price-type', {
|
||||
//language=Vue
|
||||
template: `
|
||||
<tt-card>
|
||||
<warehouse-administration-switch/>
|
||||
<tt-table-crud @openHistory="historyModal = true; historyModalId = $event.id"/>
|
||||
<warehouse-history-modal :show.sync="historyModal" :id="historyModalId"/>
|
||||
</tt-card>
|
||||
`, data() {
|
||||
return {
|
||||
window: window, historyModal: false, historyModalId: null,
|
||||
}
|
||||
},
|
||||
})
|
||||
@@ -2,6 +2,7 @@ Vue.component('warehouse-distributor', {
|
||||
//language=Vue
|
||||
template: `
|
||||
<tt-card>
|
||||
<warehouse-administration-switch/>
|
||||
<tt-table-crud @openHistory="historyModal = true; historyModalId = $event.id"/>
|
||||
<warehouse-history-modal :show.sync="historyModal" :id="historyModalId"/>
|
||||
</tt-card>
|
||||
|
||||
@@ -1,47 +1,3 @@
|
||||
|
||||
|
||||
Vue.component('tt-expandable-shopping-cart', {
|
||||
props: {
|
||||
cartItems: Array,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isExpanded: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
},
|
||||
template: `
|
||||
<div class="tt-expandable-shopping-cart" :class="{ expanded: isExpanded }">
|
||||
<button class="toggle-button" @click="isExpanded = !isExpanded">
|
||||
<i class="fas fa-shopping-cart text-primary"></i>
|
||||
<span v-if="cartItems.length > 0 && isExpanded" class="btn btn-primary" @click.prevent="$emit('submitOrder')">Bestellen</span>
|
||||
<span class="cart-count" v-if="cartItems.length > 0">{{ cartItems.length }}</span>
|
||||
<!-- add arrow down icon when cart is expanded additionally with v-if -->
|
||||
<i v-if="isExpanded" class="fas fa-arrow-down text-danger" style="font-size:21px;grid-column: 4;"></i>
|
||||
</button>
|
||||
<div class="cart-content" v-if="isExpanded">
|
||||
<div v-if="cartItems.length > 0">
|
||||
<h3>Einkaufswagen</h3>
|
||||
<ul class="list-group">
|
||||
<template v-for="item in cartItems">
|
||||
<li class="list-group-item" style="display:grid;grid-template-columns: 1fr auto;gap: 10px;">
|
||||
{{ item.title }}
|
||||
<div style="display:grid;grid-template-columns: 1fr 1fr;gap: 10px;">
|
||||
<span class="badge badge-primary badge-pill" style="height: 16px">{{ item.amount }}</span>
|
||||
<i style="cursor: pointer" class="fas fa-trash-alt text-danger" @click="cartItems.splice(cartItems.indexOf(item), 1)"></i>
|
||||
</div>
|
||||
</li>
|
||||
</template>
|
||||
</ul>
|
||||
</div>
|
||||
<p v-else>Der Einkaufswagen ist leer.</p>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
});
|
||||
|
||||
|
||||
Vue.component('warehouse-e-shop', {
|
||||
//language=Vue
|
||||
template: `
|
||||
|
||||
@@ -82,7 +82,6 @@ Vue.component('warehouse-e-shop-order', {
|
||||
}
|
||||
}, async mounted() {
|
||||
const response = await axios.get(`${window['TT_CONFIG']['BASE_PATH']}/WarehouseEShopOrder/getAllItemsPerOrder`);
|
||||
console.log(response.data);
|
||||
this.articleItems = response.data;
|
||||
}, methods: {
|
||||
async sendSingleOrderEmail() {
|
||||
|
||||
@@ -43,3 +43,97 @@ Vue.component('warehouse-history-modal', {
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
Vue.component('tt-expandable-shopping-cart', {
|
||||
props: {
|
||||
cartItems: Array,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isExpanded: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
},
|
||||
template: `
|
||||
<div class="tt-expandable-shopping-cart" :class="{ expanded: isExpanded }">
|
||||
<button class="toggle-button" @click="isExpanded = !isExpanded">
|
||||
<i class="fas fa-shopping-cart text-primary"></i>
|
||||
<span v-if="cartItems.length > 0 && isExpanded" class="btn btn-primary" @click.prevent="$emit('submitOrder')">Bestellen</span>
|
||||
<span class="cart-count" v-if="cartItems.length > 0">{{ cartItems.length }}</span>
|
||||
<!-- add arrow down icon when cart is expanded additionally with v-if -->
|
||||
<i v-if="isExpanded" class="fas fa-arrow-down text-danger" style="font-size:21px;grid-column: 4;"></i>
|
||||
</button>
|
||||
<div class="cart-content" v-if="isExpanded">
|
||||
<div v-if="cartItems.length > 0">
|
||||
<h3>Einkaufswagen</h3>
|
||||
<ul class="list-group">
|
||||
<template v-for="item in cartItems">
|
||||
<li class="list-group-item" style="display:grid;grid-template-columns: 1fr auto;gap: 10px;">
|
||||
{{ item.title }}
|
||||
<div style="display:grid;grid-template-columns: 1fr 1fr;gap: 10px;">
|
||||
<span class="badge badge-primary badge-pill" style="height: 16px">{{ item.amount }}</span>
|
||||
<i style="cursor: pointer" class="fas fa-trash-alt text-danger" @click="cartItems.splice(cartItems.indexOf(item), 1)"></i>
|
||||
</div>
|
||||
</li>
|
||||
</template>
|
||||
</ul>
|
||||
</div>
|
||||
<p v-else>Der Einkaufswagen ist leer.</p>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
});
|
||||
|
||||
|
||||
//TODO: put this in its own file
|
||||
//TODO: also for tt-crud or vuehelper create a check for utility folder and include all js files in there to allow adding custom components that are not part of the core
|
||||
//TODO: also add a component for a switch like this as we will need it more often either with value or doing redirect on click
|
||||
|
||||
Vue.component('warehouse-administration-switch', {
|
||||
//language=Vue
|
||||
template: `
|
||||
<div class="device-view-switch" style="margin-bottom: 10px">
|
||||
<div v-if="!isOverflowing" class="button-group" style="display:grid; grid-template-columns: repeat(5, 1fr); gap: 10px; justify-content: center; align-items: center; text-align: center; width: 100%;">
|
||||
<button @click="window.location.href = window.TT_CONFIG['BASE_PATH'] + '/WarehouseDistributor';" :class="{ 'active': window.location.href.includes('WarehouseDistributor') }" class="btn btn-primary">Lieferanten</button>
|
||||
<button @click="window.location.href = window.TT_CONFIG['BASE_PATH'] + '/WarehouseLocation';" :class="{ 'active': window.location.href.includes('WarehouseLocation') }" class="btn btn-primary">Lagerorte</button>
|
||||
<button @click="window.location.href = window.TT_CONFIG['BASE_PATH'] + '/WarehouseArticlePriceType';" :class="{ 'active': window.location.href.includes('WarehouseArticlePriceType') }" class="btn btn-primary">Preistypen</button>
|
||||
<button @click="window.location.href = window.TT_CONFIG['BASE_PATH'] + '/WarehouseShippingNoteTextElement';" :class="{ 'active': window.location.href.includes('Device') }" class="btn btn-primary">LS Texte</button>
|
||||
<button @click="window.location.href = window.TT_CONFIG['BASE_PATH'] + '/WarehouseAdministration';" :class="{ 'active': window.location.href.includes('Device') }" class="btn btn-primary">Admin-Tools</button>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="dropdown">
|
||||
<button @click="showDropdown = !showDropdown"
|
||||
class="btn btn-primary dropdown-toggle">Ansicht</button>
|
||||
<div v-show="showDropdown" class="dropdown-menu show">
|
||||
<a href="#" @click="window.location.href = window.TT_CONFIG['BASE_PATH'] + '/WarehouseDistributor'; showDropdown = false" class="dropdown-item">Lieferanten</a>
|
||||
<a href="#" @click="window.location.href = window.TT_CONFIG['BASE_PATH'] + '/WarehouseArticlePriceType'; showDropdown = false" class="dropdown-item">Lagerorte</a>
|
||||
<a href="#" @click="window.location.href = window.TT_CONFIG['BASE_PATH'] + '/WarehouseLocation'; showDropdown = false" class="dropdown-item">Preistypen</a>
|
||||
<a href="#" @click="window.location.href = window.TT_CONFIG['BASE_PATH'] + '/WarehouseShippingNoteTextElement'; showDropdown = false" class="dropdown-item">LS Texte</a>
|
||||
<a href="#" @click="window.location.href = window.TT_CONFIG['BASE_PATH'] + '/WarehouseAdministration'; showDropdown = false" class="dropdown-item">Admin-Tools</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
props: ['value'],
|
||||
data() {
|
||||
return {
|
||||
isOverflowing: false,
|
||||
showDropdown: false,
|
||||
window: window,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.checkOverflow();
|
||||
window.addEventListener('resize', this.checkOverflow);
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('resize', this.checkOverflow);
|
||||
},
|
||||
methods: {
|
||||
checkOverflow() {
|
||||
this.isOverflowing = window.innerWidth < 650
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
@@ -2,7 +2,13 @@ Vue.component('warehouse-item', {
|
||||
//language=Vue
|
||||
template: `
|
||||
<tt-card>
|
||||
<tt-table-crud @openHistory="historyModal = true; historyModalId = $event.id"/>
|
||||
<tt-table-crud @openHistory="historyModal = true; historyModalId = $event.id">
|
||||
<template v-slot:rack="{ row }">
|
||||
<span v-if="row.rack && row.shelf">{{ row.rack }} | {{ row.shelf }}</span>
|
||||
<span v-else-if="row.rack">{{ row.rack }}</span>
|
||||
<span v-else> - </span>
|
||||
</template>
|
||||
</tt-table-crud>
|
||||
<warehouse-history-modal :show.sync="historyModal" :id="historyModalId"/>
|
||||
</tt-card>
|
||||
`, data() {
|
||||
|
||||
@@ -2,6 +2,7 @@ Vue.component('warehouse-location', {
|
||||
//language=Vue
|
||||
template: `
|
||||
<tt-card>
|
||||
<warehouse-administration-switch/>
|
||||
<tt-table-crud @openHistory="historyModal = true; historyModalId = $event.id"/>
|
||||
<warehouse-history-modal :show.sync="historyModal" :id="historyModalId"/>
|
||||
</tt-card>
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
// noinspection JSUnusedLocalSymbols
|
||||
Vue.component('warehouse-order', {
|
||||
//language=Vue
|
||||
template: `
|
||||
<tt-card>
|
||||
<tt-table-crud @openHistory="historyModal = true; historyModalId = $event.id"
|
||||
ref="table">
|
||||
|
||||
<template v-slot:create="{ row }">
|
||||
{{ window.moment(row.create * 1000).format('DD.MM.YYYY HH:mm:ss') }}
|
||||
</template>
|
||||
|
||||
<template v-slot:sum="{ row }">
|
||||
<div style="text-align: right">{{ row.sum.toFixed(2) }} €</div>
|
||||
</template>
|
||||
|
||||
<template v-slot:expandedRow="{ row }">
|
||||
<div class="lazy-loading" :data-row-id="row.id">
|
||||
<tt-loader v-if="orderLazyLoad[row.id] === true"/>
|
||||
<div v-else>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item" v-for="item in orderLazyLoad[row.id]">
|
||||
{{ item.quantity }}x {{ item.articleName }} - {{ item.price.toFixed(2) }} €
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
</tt-table-crud>
|
||||
<warehouse-history-modal :show.sync="historyModal" :id="historyModalId"/>
|
||||
</tt-card>
|
||||
`, data() {
|
||||
return {
|
||||
window: window, historyModal: false, historyModalId: null, observer: null, orderLazyLoad: {},
|
||||
}
|
||||
}, mounted() {
|
||||
this.observer = new MutationObserver((mutations) => {
|
||||
const lazyLoadingElements = document.querySelectorAll('.lazy-loading');
|
||||
console.log(lazyLoadingElements);
|
||||
|
||||
// check row id and check if it is already defined in orderLazyLoad else alert('loading')
|
||||
// if it is defined do nothing
|
||||
|
||||
for (const element of lazyLoadingElements) {
|
||||
if (element.dataset.rowId in this.orderLazyLoad) {
|
||||
continue;
|
||||
}
|
||||
this.loadOrder(element.dataset.rowId);
|
||||
}
|
||||
|
||||
})
|
||||
this.observer.observe(document.querySelector('.tt-table-container'), {childList: true, subtree: true,});
|
||||
}, methods: {
|
||||
async loadOrder(rowId) {
|
||||
this.orderLazyLoad[rowId] = true;
|
||||
// use BASE_PATH . /WarehouseOrder/getOrderItems?id= + rowId
|
||||
const response = await axios.post(`${window.TT_CONFIG["BASE_PATH"]}/WarehouseOrder/getOrderItems?id=${rowId}`);
|
||||
console.log(response.data);
|
||||
this.orderLazyLoad[rowId] = response.data;
|
||||
|
||||
// force re-render of the table
|
||||
this.$refs.table.$forceUpdate();
|
||||
|
||||
|
||||
}
|
||||
}, beforeDestroy() {
|
||||
this.observer.disconnect();
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,321 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user