new update

This commit is contained in:
Luca Haid
2025-04-24 13:39:26 +02:00
parent adabe9a7a2
commit 4108eb99c9
22 changed files with 992 additions and 337 deletions
@@ -1,97 +1,175 @@
async function handleApiResponse(responsePromise) {
const res = await responsePromise;
if (res.data.success === false) return window.notify('error', `Fehler: ${res.data.errors.join(', ')}`);
window.notify('success', res.data.message || 'Erfolgreich');
}
Vue.component('warehouse-article-prices', {
props: {id: {type: Number, required: true}},
template: `
<div>
<tt-card>
<h4>Artikelpreise</h4>
<div class="warehouse-article-prices">
<div v-for="(price, index) in articlePrices" :key="index" class="article-price">
<div class="article-price-type">
<span v-if="price.isRobot" class="robot-icon"><i class="fa-solid fa-robot"></i></span>
{{ index }}
</div>
<tt-input sm v-model="price.priceMultiplier" label="Preisfaktor"/>
<tt-input sm v-model="price.priceOverride" label="Preisoverride"/>
<div>
<tt-button sm icon="fa-solid fa-trash" class="remove-price" additional-class="btn-danger"></tt-button>
<tt-button sm icon="fa-solid fa-save" class="save-price" additional-class="btn-primary"></tt-button>
</div>
<tt-card>
<h4 style="text-align: center">Artikelpreise überschreiben</h4>
<div class="warehouse-article-prices">
<div v-for="(price, typeTitle) in articlePrices" :key="typeTitle">
<div style="align-self: center;">
<i v-if="price.isRobot" class="fa-solid fa-robot"></i> {{ typeTitle }}
</div>
<tt-input sm v-model="price.priceMultiplier" label="Preisfaktor" @input="price.priceOverride = null"/>
<tt-input sm v-model="price.priceOverride" label="Preis" @input="price.priceMultiplier = null"/>
<div style="align-self: end;">
<tt-button sm icon="fa-solid fa-save" additional-class="btn-primary" @click="savePrice(price)"/>
<tt-button sm icon="fa-solid fa-trash" additional-class="btn-danger" v-if="!price.isRobot"
@click="deletePrice(price)"/>
</div>
</div>
</tt-card>
</div>`,
data: () => ({
window,
articlePrices: [],
}),
</div>
</tt-card>
`,
data: () => ({window, articlePrices: {}}),
async mounted() {
const [res1, res2] = await Promise.all([
axios.post(window['TT_CONFIG']['BASE_PATH'] + '/WarehouseArticlePrice/get', {filters: {articleId: this.id}}),
axios.post(window['TT_CONFIG']['BASE_PATH'] + '/WarehouseArticlePriceType/get')
]);
const prices = {};
res2.data.rows.forEach(t => prices[t.title] = {
isRobot: true,
articlePriceTypeId: t.id,
priceMultiplier: t.defaultPriceFactor,
priceOverride: null
});
res1.data.rows.forEach(p => {
const t = res2.data.rows.find(t => t.id === p.articlePriceTypeId);
if (t) prices[t.title] = {
isRobot: false,
articlePriceTypeId: p.articlePriceTypeId,
priceMultiplier: p.priceMultiplier || t.defaultPriceFactor,
priceOverride: p.priceOverride
await this.fetchArticlePrices();
},
methods: {
async fetchArticlePrices() {
const [pricesRes, typesRes] = await Promise.all([
axios.post(`${window['TT_CONFIG']['BASE_PATH']}/WarehouseArticlePrice/get`, {filters: {articleId: this.id}}),
axios.post(`${window['TT_CONFIG']['BASE_PATH']}/WarehouseArticlePriceType/get`)
]);
const prices = {};
typesRes.data.rows.forEach(type => prices[type.title] = {
isRobot: true,
articlePriceTypeId: type.id,
priceMultiplier: type.defaultPriceFactor,
priceOverride: null
});
pricesRes.data.rows.forEach(pData => {
const type = typesRes.data.rows.find(t => t.id === pData.articlePriceTypeId);
if (type) prices[type.title] = {
id: pData.id,
isRobot: false,
articlePriceTypeId: pData.articlePriceTypeId,
priceMultiplier: pData.priceMultiplier,
priceOverride: pData.priceOverride
};
});
this.articlePrices = prices;
},
async savePrice(price) {
const payload = {
articleId: this.id,
articlePriceTypeId: price.articlePriceTypeId,
priceMultiplier: price.priceMultiplier,
priceOverride: price.priceOverride
};
});
this.articlePrices = prices;
if (price.isRobot) {
await this.window.handleApiResponse(axios.post(`${window['TT_CONFIG']['BASE_PATH']}/WarehouseArticlePrice/create`, payload));
await this.fetchArticlePrices();
} else {
await this.window.handleApiResponse(axios.post(`${window['TT_CONFIG']['BASE_PATH']}/WarehouseArticlePrice/update`, {id: price.id, ...payload}));
await this.fetchArticlePrices();
}
},
async deletePrice(price) {
const payload = {id: price.id, articleId: this.id, articlePriceTypeId: price.articlePriceTypeId}
await this.window.handleApiResponse(axios.post(`${window['TT_CONFIG']['BASE_PATH']}/WarehouseArticlePrice/delete`, payload));
await this.fetchArticlePrices();
}
}
})
});
Vue.component('warehouse-article', {
//language=Vue
// warehouse-article-distributor.vue.js
Vue.component('warehouse-article-distributor', {
props: {id: {type: Number, required: true}},
template: `
<tt-card>
<h4 style="text-align: center">Lieferanten für diesen Artikel</h4>
<tt-autocomplete :api-url="window['TT_CONFIG']['BASE_PATH'] + '/WarehouseDistributor/autocomplete'"
v-model="newDistributorId" label="Neuen Lieferant hinzufügen"/>
<div class="warehouse-article-distributor">
<div v-for="(distributor, index) in articleDistributors" :key="distributor.id || ('new-' + index)">
<tt-resolver style="align-self: center;" reference="WarehouseDistributor"
:value="distributor.distributorId"></tt-resolver>
<tt-input sm v-model="distributor.externalArticleNumber" label="Externe Artikelnummer"/>
<tt-input sm v-model="distributor.purchasePrice" label="Einkaufspreis"/>
<div style="align-self: end;">
<tt-button sm icon="fa-solid fa-save" additional-class="btn-primary"
@click="saveDistributor(distributor)"/>
<tt-button sm icon="fa-solid fa-trash" additional-class="btn-danger" v-if="distributor.id"
@click="deleteDistributor(distributor)"/>
</div>
</div>
</div>
</tt-card>
`,
data: () => ({window, articleDistributors: [], newDistributorId: null}),
async mounted() {
await this.fetchArticleDistributors();
},
methods: {
async fetchArticleDistributors() {
const res = await axios.post(`${window['TT_CONFIG']['BASE_PATH']}/WarehouseArticleDistributor/get`, {filters: {articleId: this.id}});
this.articleDistributors = res.data.rows;
},
async saveDistributor(distributor) {
await this.window.handleApiResponse(axios.post(`${window['TT_CONFIG']['BASE_PATH']}/WarehouseArticleDistributor/${distributor.id ? 'update' : 'create'}`, distributor));
await this.fetchArticleDistributors();
},
async deleteDistributor(distributor) {
await this.window.handleApiResponse(axios.post(`${window['TT_CONFIG']['BASE_PATH']}/WarehouseArticleDistributor/delete`, distributor));
await this.fetchArticleDistributors();
}
},
watch: {
newDistributorId(newId) {
if (newId) {
if (!this.articleDistributors.some(d => d.distributorId === newId)) {
this.articleDistributors.push({
articleId: this.id,
distributorId: newId,
externalArticleNumber: null,
purchasePrice: null
});
}
this.$nextTick(() => {
this.newDistributorId = null;
});
}
}
}
});
<tt-table-crud ref="table" @openHistory="historyModal = true; historyModalId = $event.id">
// warehouse-article.vue.js
Vue.component('warehouse-article', {
template: `
<tt-card>
<tt-table-crud ref="table" @openHistory="historyModalId = $event.id; historyModal = true">
<template v-slot:cheapestsellprice="{ row }">
<template v-for="price in JSON.parse(row.cheapestSellPrice)">
<span v-if="price && window.TT_CONFIG['WAREHOUSE_ADMIN'] == true">{{price.title}}: <span
style="white-space:nowrap;"> {{(price.price)}} €</span><br></span>
<span v-else-if="price && price.title === 'Verkauf'">{{(price.price)}} €</span>
<template v-for="price in JSON.parse(row.cheapestSellPrice || '[]')">
<span v-if="price && window.TT_CONFIG['WAREHOUSE_ADMIN']">{{ price.title }}: <span
style="white-space:nowrap;">{{ price.price }} €</span><br></span>
<span v-else-if="price && price.title === 'Verkauf'">{{ price.price }} €</span>
</template>
</template>
<template v-slot:modal-prepend="{ crudModalData }">
<warehouse-article-prices v-if="crudModalData.id" :id="crudModalData.id"/>
<warehouse-article-distributor v-if="crudModalData.id" :id="crudModalData.id"/>
</template>
</tt-table-crud>
<warehouse-history-modal :show.sync="historyModal" :id="historyModalId"/>
</tt-card>
`, data: () => ({
window,
historyModal: false,
historyModalId: null,
}),
`,
data: () => ({window, historyModal: false, historyModalId: null}),
mounted() {
const table = this.$refs.table?.$refs?.table;
if (!table) return;
const showId = new URLSearchParams(window.location.search).get('showId');
const currentFilterId = table.filters?.id;
if ((showId && currentFilterId !== showId) || (!showId && currentFilterId)) {
table.filters = showId ? {id: showId} : {};
if (showId && (!table.filters || table.filters.id !== showId)) {
table.filters = {...table.filters, id: showId};
table.refreshTable();
} else if (!showId && table.filters?.id) {
delete table.filters.id;
if (Object.keys(table.filters).length === 0) table.filters = {};
table.refreshTable();
}
}
})
});