666 lines
27 KiB
JavaScript
666 lines
27 KiB
JavaScript
window.TT_CONFIG.CRUD_CONFIG.customRowClass = (row) => {
|
|
if (row.isEndOfLife) return 'end-of-life';
|
|
}
|
|
|
|
async function handleApiResponse(responsePromise) {
|
|
const res = await responsePromise;
|
|
if (!res.data.success) {
|
|
const errors = res.data.errors;
|
|
const errorMessage = Array.isArray(errors) ? errors.join(', ') : Object.values(errors).join(', ');
|
|
return window.notify('error', `Fehler: ${errorMessage}`);
|
|
}
|
|
window.notify('success', res.data.message || 'Erfolgreich');
|
|
window.dispatchEvent(new Event('refreshTable'));
|
|
}
|
|
|
|
Vue.component('warehouse-article-prices', {
|
|
props: {id: {type: Number, required: true}},
|
|
template: `
|
|
<div class="wa-prices-section">
|
|
<h5 class="wa-section-title"><i class="fas fa-tags mr-2"></i>Artikelpreise überschreiben</h5>
|
|
<div class="wa-prices-grid-dense">
|
|
<div v-for="(price, typeTitle) in articlePrices" :key="typeTitle" class="wa-price-item">
|
|
<div class="wa-price-header">
|
|
<i v-if="price.isRobot" class="fas fa-robot mr-1 text-muted" style="font-size: 0.75rem;"></i>
|
|
<strong style="font-size: 0.8rem;">{{ typeTitle }}</strong>
|
|
<i v-if="price.pendingChanges" class="fas fa-exclamation-triangle text-warning ml-auto"
|
|
style="font-size: 0.75rem;" title="Nicht gespeichert"></i>
|
|
</div>
|
|
<div class="wa-price-body">
|
|
<tt-input
|
|
v-model="price.priceMultiplier"
|
|
@input="handleFactorInput(price)"
|
|
placeholder="Faktor"
|
|
type="number"
|
|
sm no-form-group/>
|
|
<tt-input
|
|
v-model="price.priceOverride"
|
|
@input="handlePriceInput(price)"
|
|
placeholder="Preis €"
|
|
type="number"
|
|
sm no-form-group/>
|
|
<div class="wa-price-actions">
|
|
<button class="btn btn-sm btn-primary" @click="savePrice(price)" title="Speichern">
|
|
<i class="fas fa-save"></i>
|
|
</button>
|
|
<button class="btn btn-sm btn-danger" @click="deletePrice(price)" :disabled="price.isRobot" title="Löschen">
|
|
<i class="fas fa-trash"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`,
|
|
data: () => ({window, articlePrices: {}}),
|
|
async mounted() {
|
|
await this.fetchArticlePrices();
|
|
},
|
|
methods: {
|
|
handleFactorInput(price) {
|
|
if (price.priceMultiplier) price.priceOverride = null;
|
|
price.pendingChanges = true;
|
|
},
|
|
handlePriceInput(price) {
|
|
if (price.priceOverride) price.priceMultiplier = null;
|
|
price.pendingChanges = true;
|
|
},
|
|
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) return;
|
|
prices[type.title] = {
|
|
id: pData.id,
|
|
isRobot: false,
|
|
pendingChanges: 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 ? parseFloat(price.priceMultiplier.toString().replace(',', '.')) : null,
|
|
priceOverride: price.priceOverride ? parseFloat(price.priceOverride.toString().replace(',', '.')) : null
|
|
};
|
|
const endpoint = price.isRobot ? 'create' : 'update';
|
|
const data = price.isRobot ? payload : {id: price.id, ...payload};
|
|
await this.window.handleApiResponse(axios.post(`${window['TT_CONFIG']['BASE_PATH']}/WarehouseArticlePrice/${endpoint}`, data));
|
|
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-distributor-directory-modal', {
|
|
props: {
|
|
show: { type: Boolean, default: false },
|
|
allDistributors: { type: Array, default: () => [] },
|
|
articleDistributors: { type: Array, default: () => [] }
|
|
},
|
|
data: () => ({
|
|
distributorSearch: ''
|
|
}),
|
|
watch: {
|
|
show(newVal) {
|
|
if (newVal) document.documentElement.style.overflow = 'hidden';
|
|
}
|
|
},
|
|
computed: {
|
|
filteredDistributors() {
|
|
if (!this.distributorSearch) return this.allDistributors;
|
|
const search = this.distributorSearch.toLowerCase();
|
|
return this.allDistributors.filter(d => d.name.toLowerCase().includes(search));
|
|
},
|
|
alphabetWithDistributors() {
|
|
const letters = new Set();
|
|
this.filteredDistributors.forEach(d => {
|
|
const firstChar = d.name.charAt(0).toUpperCase();
|
|
if (/[A-Z]/.test(firstChar)) letters.add(firstChar);
|
|
});
|
|
return Array.from(letters).sort();
|
|
}
|
|
},
|
|
methods: {
|
|
getDistributorsByLetter(letter) {
|
|
return this.filteredDistributors.filter(d => d.name.charAt(0).toUpperCase() === letter);
|
|
},
|
|
isDistributorAdded(distributorId) {
|
|
return this.articleDistributors.some(d => d.distributorId === distributorId);
|
|
},
|
|
selectDistributor(distributorId) {
|
|
this.$emit('select', distributorId);
|
|
this.$emit('close');
|
|
},
|
|
close() {
|
|
this.$emit('close');
|
|
}
|
|
},
|
|
template: `
|
|
<div v-if="show" class="modal fade show d-block" tabindex="-1" style="background: rgba(0,0,0,0.5);" @click.self="close">
|
|
<div class="modal-dialog modal-lg modal-dialog-scrollable">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">
|
|
<i class="fas fa-truck mr-2"></i>
|
|
Lieferant hinzufügen
|
|
</h5>
|
|
<button type="button" class="close" @click="close">
|
|
<span>×</span>
|
|
</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="wa-directory-header mb-3">
|
|
<tt-input
|
|
v-model="distributorSearch"
|
|
placeholder="Lieferant suchen..."
|
|
sm
|
|
prefix-icon="fas fa-search"/>
|
|
</div>
|
|
<div class="wa-directory-list">
|
|
<div v-for="letter in alphabetWithDistributors" :key="letter" class="wa-directory-group">
|
|
<div class="wa-directory-letter">{{ letter }}</div>
|
|
<div class="wa-directory-items">
|
|
<button v-for="dist in getDistributorsByLetter(letter)"
|
|
:key="dist.id"
|
|
class="wa-directory-item"
|
|
:class="{ 'active': isDistributorAdded(dist.id) }"
|
|
@click="selectDistributor(dist.id)"
|
|
:disabled="isDistributorAdded(dist.id)">
|
|
{{ dist.name }}
|
|
<i v-if="isDistributorAdded(dist.id)" class="fas fa-check ml-1"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`
|
|
});
|
|
|
|
Vue.component('warehouse-article-distributor', {
|
|
props: {id: {type: Number, required: true}},
|
|
template: `
|
|
<div class="wa-distributors-section">
|
|
<div class="wa-section-title-with-action">
|
|
<h5 class="wa-section-title mb-0"><i class="fas fa-truck mr-2"></i>Lieferanten</h5>
|
|
<button class="btn btn-sm btn-primary" @click="showDirectoryModal = true">
|
|
<i class="fas fa-plus mr-1"></i>Lieferant hinzufügen
|
|
</button>
|
|
</div>
|
|
|
|
<warehouse-distributor-directory-modal
|
|
:show="showDirectoryModal"
|
|
:all-distributors="allDistributors"
|
|
:article-distributors="articleDistributors"
|
|
@select="addDistributor"
|
|
@close="showDirectoryModal = false"/>
|
|
|
|
<div v-if="articleDistributors.length === 0" class="text-muted text-center py-3" style="font-size: 0.85rem;">
|
|
<i class="fas fa-info-circle mr-1"></i>Keine Lieferanten zugewiesen
|
|
</div>
|
|
|
|
<div v-else class="wa-distributors-grid">
|
|
<div v-for="(distributor, index) in articleDistributors" :key="distributor.id || ('new-' + index)" class="wa-distributor-row">
|
|
<div class="wa-distributor-name">
|
|
<strong>{{ getDistributorName(distributor.distributorId) }}</strong>
|
|
<i v-if="distributor.pendingChanges || !distributor.id"
|
|
class="fas fa-exclamation-triangle text-warning ml-1"
|
|
style="font-size: 0.75rem;"
|
|
title="Nicht gespeichert"></i>
|
|
</div>
|
|
<div class="wa-distributor-inputs">
|
|
<tt-input
|
|
v-model="distributor.externalArticleNumber"
|
|
@input="distributor.pendingChanges = true"
|
|
placeholder="Externe Art.-Nr."
|
|
sm no-form-group/>
|
|
<tt-input
|
|
v-model="distributor.purchasePrice"
|
|
@input="distributor.pendingChanges = true"
|
|
placeholder="Einkaufspreis €"
|
|
sm no-form-group/>
|
|
</div>
|
|
<div class="wa-distributor-actions">
|
|
<button class="btn btn-sm btn-primary" @click="saveDistributor(distributor)" title="Speichern">
|
|
<i class="fas fa-save"></i>
|
|
</button>
|
|
<button class="btn btn-sm btn-danger" @click="deleteDistributor(distributor.id)" :disabled="!distributor.id" title="Löschen">
|
|
<i class="fas fa-trash"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`,
|
|
data: () => ({
|
|
window,
|
|
articleDistributors: [],
|
|
allDistributors: [],
|
|
showDirectoryModal: false
|
|
}),
|
|
async mounted() {
|
|
await Promise.all([
|
|
this.fetchArticleDistributors(),
|
|
this.fetchAllDistributors()
|
|
]);
|
|
},
|
|
methods: {
|
|
async fetchAllDistributors() {
|
|
const res = await axios.post(`${window['TT_CONFIG']['BASE_PATH']}/WarehouseDistributor/get`, {
|
|
pagination: { per_page: 10000 },
|
|
order: { key: 'name', order: 'ASC' }
|
|
});
|
|
this.allDistributors = res.data.rows || [];
|
|
},
|
|
async fetchArticleDistributors() {
|
|
const res = await axios.post(`${window['TT_CONFIG']['BASE_PATH']}/WarehouseArticleDistributor/get`, {filters: {articleId: this.id}});
|
|
this.articleDistributors = res.data.rows;
|
|
},
|
|
getDistributorName(id) {
|
|
const dist = this.allDistributors.find(d => d.id === id);
|
|
return dist ? dist.name : 'Unbekannt';
|
|
},
|
|
addDistributor(distributorId) {
|
|
if (this.articleDistributors.some(d => d.distributorId === distributorId)) return;
|
|
this.articleDistributors.push({
|
|
articleId: this.id,
|
|
distributorId: distributorId,
|
|
externalArticleNumber: null,
|
|
purchasePrice: null,
|
|
pendingChanges: true
|
|
});
|
|
},
|
|
async saveDistributor(distributor) {
|
|
delete distributor.pendingChanges;
|
|
distributor.purchasePrice = distributor.purchasePrice ? parseFloat(distributor.purchasePrice.toString().replace(',', '.')) : null;
|
|
await this.window.handleApiResponse(axios.post(`${window['TT_CONFIG']['BASE_PATH']}/WarehouseArticleDistributor/${distributor.id ? 'update' : 'create'}`, distributor));
|
|
await this.fetchArticleDistributors();
|
|
},
|
|
async deleteDistributor(distributorId) {
|
|
if (!confirm('Lieferant wirklich entfernen?')) return;
|
|
await this.window.handleApiResponse(axios.post(`${window['TT_CONFIG']['BASE_PATH']}/WarehouseArticleDistributor/delete`, {id: distributorId, articleId: this.id}));
|
|
await this.fetchArticleDistributors();
|
|
}
|
|
}
|
|
});
|
|
|
|
Vue.component('warehouse-article-modal', {
|
|
props: {
|
|
id: { type: [Number, String], required: true }
|
|
},
|
|
template: `
|
|
<div class="modal fade show d-block" tabindex="-1" style="background: rgba(0,0,0,0.5);" @click.self="close">
|
|
<div class="modal-dialog modal-xl modal-dialog-scrollable">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">
|
|
<i class="fas fa-box mr-2"></i>
|
|
{{ isEditMode ? 'Artikel bearbeiten' : 'Artikel erstellen' }}
|
|
</h5>
|
|
<button type="button" class="close" @click="close">
|
|
<span>×</span>
|
|
</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div v-if="loading" class="text-center py-5">
|
|
<i class="fas fa-spinner fa-spin fa-2x text-muted"></i>
|
|
</div>
|
|
<div v-else class="wa-modal-content">
|
|
<!-- Basic Information -->
|
|
<div class="wa-section">
|
|
<div class="row">
|
|
<div class="col-md-12">
|
|
<tt-input
|
|
label="Titel"
|
|
v-model="formData.title"
|
|
placeholder="Artikel Titel"
|
|
required
|
|
sm/>
|
|
</div>
|
|
<div class="col-md-12">
|
|
<tt-textarea
|
|
label="Beschreibung"
|
|
v-model="formData.description"
|
|
required
|
|
rows="3"/>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<tt-select
|
|
label="Kategorie"
|
|
v-model="formData.category_id"
|
|
:options="categoryOptions"
|
|
required
|
|
sm/>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<tt-input
|
|
label="Artikel-Nummer"
|
|
v-model="formData.articleNumber"
|
|
placeholder="z.B. 1234"
|
|
required
|
|
form-label
|
|
sm/>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<tt-select
|
|
label="Einheit"
|
|
v-model="formData.unit"
|
|
:options="unitOptions"
|
|
required
|
|
sm/>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<tt-select
|
|
label="Erlöskonto"
|
|
v-model="formData.revenueAccount"
|
|
:options="revenueAccountOptions"
|
|
required
|
|
sm/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Prices Section -->
|
|
<warehouse-article-prices v-if="isEditMode" :id="Number(id)"/>
|
|
|
|
<!-- Distributors Section -->
|
|
<warehouse-article-distributor v-if="isEditMode" :id="Number(id)"/>
|
|
|
|
<!-- Additional Attributes -->
|
|
<div class="wa-section">
|
|
<h5 class="wa-section-title"><i class="fas fa-cog mr-2"></i>Zusätzliche Artikel Attribute</h5>
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<tt-input
|
|
label="Warnmenge"
|
|
type="number"
|
|
v-model.number="formData.warningAmount"
|
|
placeholder="0"
|
|
required
|
|
sm/>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<tt-input
|
|
label="Kritische Menge"
|
|
type="number"
|
|
v-model.number="formData.criticalAmount"
|
|
placeholder="0"
|
|
required
|
|
sm/>
|
|
</div>
|
|
</div>
|
|
<div class="wa-checkbox-grid">
|
|
<label class="wa-checkbox-item">
|
|
<input type="checkbox" v-model="formData.isSerialDocumentation">
|
|
<span class="wa-checkmark"></span>
|
|
<span class="wa-checkbox-label">Seriennummern</span>
|
|
</label>
|
|
<label class="wa-checkbox-item">
|
|
<input type="checkbox" v-model="formData.isEndOfLife">
|
|
<span class="wa-checkmark"></span>
|
|
<span class="wa-checkbox-label">End of Life</span>
|
|
</label>
|
|
<label class="wa-checkbox-item">
|
|
<input type="checkbox" v-model="formData.isEShop">
|
|
<span class="wa-checkmark"></span>
|
|
<span class="wa-checkbox-label">Ist E-Shop</span>
|
|
</label>
|
|
<label class="wa-checkbox-item">
|
|
<input type="checkbox" v-model="formData.isEShopHide">
|
|
<span class="wa-checkmark"></span>
|
|
<span class="wa-checkbox-label">E-Shop Versteckt</span>
|
|
</label>
|
|
<label class="wa-checkbox-item">
|
|
<input type="checkbox" v-model="formData.isSbidiShop">
|
|
<span class="wa-checkmark"></span>
|
|
<span class="wa-checkbox-label">Ist SBIDI-Shop</span>
|
|
</label>
|
|
<label class="wa-checkbox-item">
|
|
<input type="checkbox" v-model="formData.isSbidiShopHide">
|
|
<span class="wa-checkmark"></span>
|
|
<span class="wa-checkbox-label">SBIDI-Shop Versteckt</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" @click="close">Abbrechen</button>
|
|
<button type="button" class="btn btn-primary" @click="save" :disabled="saving || !isValid">
|
|
<i v-if="saving" class="fas fa-spinner fa-spin mr-1"></i>
|
|
<i v-else class="fas fa-save mr-1"></i>
|
|
Speichern
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`,
|
|
data: () => ({
|
|
loading: false,
|
|
saving: false,
|
|
formData: {
|
|
title: '',
|
|
description: '',
|
|
category_id: null,
|
|
articleNumber: '',
|
|
unit: 'Stk.',
|
|
revenueAccount: 0,
|
|
warningAmount: 0,
|
|
criticalAmount: 0,
|
|
isSerialDocumentation: false,
|
|
isEndOfLife: false,
|
|
isEShop: false,
|
|
isEShopHide: false,
|
|
isSbidiShop: false,
|
|
isSbidiShopHide: false
|
|
}
|
|
}),
|
|
computed: {
|
|
isEditMode() {
|
|
return this.id !== 'create';
|
|
},
|
|
categoryOptions() {
|
|
const catCol = window.TT_CONFIG.CRUD_CONFIG.columns.find(c => c.key === 'category_id');
|
|
return catCol ? [{ value: null, text: '-- Bitte wählen --' }, ...catCol.modal.items] : [];
|
|
},
|
|
unitOptions() {
|
|
return [
|
|
{ value: 'Stk.', text: 'Stk.' },
|
|
{ value: 'Pau.', text: 'Pau.' },
|
|
{ value: 'm.', text: 'm.' },
|
|
{ value: 'Std.', text: 'Std.' },
|
|
{ value: 'km', text: 'km' }
|
|
];
|
|
},
|
|
revenueAccountOptions() {
|
|
return [
|
|
{ value: 0, text: 'Dienstleistungen' },
|
|
{ value: 1, text: 'Handelswaren' }
|
|
];
|
|
},
|
|
isValid() {
|
|
return this.formData.title &&
|
|
this.formData.description &&
|
|
this.formData.category_id &&
|
|
this.formData.articleNumber &&
|
|
this.formData.unit;
|
|
}
|
|
},
|
|
mounted() {
|
|
const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
|
|
document.documentElement.style.overflow = 'hidden';
|
|
document.documentElement.style.paddingRight = scrollbarWidth + 'px';
|
|
|
|
if (this.isEditMode) this.loadArticle();
|
|
else this.resetForm();
|
|
},
|
|
beforeDestroy() {
|
|
document.documentElement.style.overflow = '';
|
|
document.documentElement.style.paddingRight = '';
|
|
},
|
|
methods: {
|
|
async loadArticle() {
|
|
this.loading = true;
|
|
try {
|
|
const res = await axios.get(`${window.TT_CONFIG.BASE_PATH}/WarehouseArticle/getById`, {
|
|
params: { id: Number(this.id) }
|
|
});
|
|
const data = res.data;
|
|
if (data && data.id) {
|
|
this.formData = {
|
|
title: data.title || '',
|
|
description: data.description || '',
|
|
category_id: data.category_id,
|
|
articleNumber: data.articleNumber || '',
|
|
unit: data.unit || 'Stk.',
|
|
revenueAccount: data.revenueAccount || 0,
|
|
warningAmount: data.warningAmount || 0,
|
|
criticalAmount: data.criticalAmount || 0,
|
|
isSerialDocumentation: !!data.isSerialDocumentation,
|
|
isEndOfLife: !!data.isEndOfLife,
|
|
isEShop: !!data.isEShop,
|
|
isEShopHide: !!data.isEShopHide,
|
|
isSbidiShop: !!data.isSbidiShop,
|
|
isSbidiShopHide: !!data.isSbidiShopHide
|
|
};
|
|
}
|
|
} catch (e) {
|
|
window.notify('error', 'Fehler beim Laden');
|
|
} finally {
|
|
this.loading = false;
|
|
}
|
|
},
|
|
resetForm() {
|
|
this.formData = {
|
|
title: '',
|
|
description: '',
|
|
category_id: null,
|
|
articleNumber: '',
|
|
unit: 'Stk.',
|
|
revenueAccount: 0,
|
|
warningAmount: 0,
|
|
criticalAmount: 0,
|
|
isSerialDocumentation: false,
|
|
isEndOfLife: false,
|
|
isEShop: false,
|
|
isEShopHide: false,
|
|
isSbidiShop: false,
|
|
isSbidiShopHide: false
|
|
};
|
|
},
|
|
async save() {
|
|
if (!this.isValid) return;
|
|
this.saving = true;
|
|
try {
|
|
const endpoint = this.isEditMode ? 'update' : 'create';
|
|
const payload = {
|
|
...this.formData,
|
|
isSerialDocumentation: this.formData.isSerialDocumentation ? 1 : 0,
|
|
isEndOfLife: this.formData.isEndOfLife ? 1 : 0,
|
|
isEShop: this.formData.isEShop ? 1 : 0,
|
|
isEShopHide: this.formData.isEShopHide ? 1 : 0,
|
|
isSbidiShop: this.formData.isSbidiShop ? 1 : 0,
|
|
isSbidiShopHide: this.formData.isSbidiShopHide ? 1 : 0
|
|
};
|
|
if (this.isEditMode) payload.id = Number(this.id);
|
|
|
|
const res = await axios.post(`${window.TT_CONFIG.BASE_PATH}/WarehouseArticle/${endpoint}`, payload);
|
|
if (res.data.success) {
|
|
window.notify('success', res.data.message || 'Gespeichert');
|
|
if (!this.isEditMode && window.TT_CONFIG.CRUD_CONFIG.reopenOnCreate) this.$emit('reopen', res.data.id);
|
|
else this.$emit('close');
|
|
} else {
|
|
window.notify('error', res.data.message || 'Fehler beim Speichern');
|
|
}
|
|
} catch (e) {
|
|
window.notify('error', 'Fehler beim Speichern');
|
|
} finally {
|
|
this.saving = false;
|
|
}
|
|
},
|
|
close() {
|
|
this.$emit('close');
|
|
}
|
|
}
|
|
});
|
|
|
|
Vue.component('warehouse-article', {
|
|
template: `
|
|
<tt-card>
|
|
<warehouse-article-modal
|
|
v-if="articleModalId"
|
|
:id="articleModalId"
|
|
@close="articleModalId = null; $refs.table.$refs.table.refreshTable()"
|
|
@reopen="articleModalId = $event"/>
|
|
|
|
<warehouse-history-modal :show.sync="historyModal" :id="historyModalId"/>
|
|
|
|
<tt-table-crud
|
|
ref="table"
|
|
emit-edit
|
|
@openHistory="historyModalId = $event.id; historyModal = true"
|
|
@edit="articleModalId = $event.id">
|
|
<template v-slot:cheapestsellprice="{ row }">
|
|
<template v-for="price in JSON.parse(row.cheapestSellPrice || '[]')">
|
|
<span style="white-space:nowrap;" v-if="price && window.TT_CONFIG['WAREHOUSE_ADMIN']">{{ price.title }}: <span
|
|
>{{ price.price }} €</span><br></span>
|
|
<span v-else-if="price && price.title === 'Verkauf'">{{ price.price }} €</span>
|
|
</template>
|
|
</template>
|
|
|
|
<template v-slot:description="{ row }">
|
|
<tt-tooltip
|
|
allow-wrapping
|
|
v-if="row.description.length > 45"
|
|
:text="row.description" position="top">
|
|
<span v-if="row.description.length > 45">{{ row.description.substring(0, 45) }}...</span>
|
|
</tt-tooltip>
|
|
<span v-else>{{ row.description }}</span>
|
|
</template>
|
|
</tt-table-crud>
|
|
</tt-card>
|
|
`,
|
|
data: () => ({
|
|
window,
|
|
historyModal: false,
|
|
historyModalId: null,
|
|
articleModalId: null
|
|
}),
|
|
mounted() {
|
|
const table = this.$refs.table?.$refs?.table;
|
|
if (!table) return;
|
|
const showId = new URLSearchParams(window.location.search).get('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();
|
|
}
|
|
}
|
|
});
|