179 lines
8.8 KiB
JavaScript
179 lines
8.8 KiB
JavaScript
Vue.component('warehouse-offer-modal', {
|
|
props: {
|
|
id: {type: [String, Number], required: true},
|
|
mode: {type: String, default: 'edit'}
|
|
},
|
|
template: `
|
|
<tt-modal :show="true"
|
|
@submit="submit"
|
|
:delete="id !== 'create'"
|
|
:title="id === 'create' ? 'Angebot erstellen' : \`Angebot #\${id} bearbeiten\`"
|
|
@update:show="$emit('close')">
|
|
<div style="width: 99%"><h4 class="text-center">Angebotdetails</h4>
|
|
<tt-select label="Sachbearbeiter"
|
|
:options="window.TT_CONFIG.CRUD_CONFIG.columns.find(column => column.key === 'createBy')?.modal.items"
|
|
sm
|
|
row
|
|
v-model="offer.editor"/>
|
|
<tt-input label="Kundennummer" v-model="offer.customerNumber" sm row/>
|
|
<tt-input label="Kundenreferenz" v-model="offer.reference" sm row/>
|
|
<tt-textarea label="Angebotszweck" v-model="offer.purpose" sm row/>
|
|
<hr>
|
|
<h4 class="text-center">Kundenadresse</h4>
|
|
<div style="display: grid; grid-gap: 10px; grid-template-columns: 2fr 2fr 1fr 1fr 2fr;">
|
|
<tt-input label="Name" v-model="offer.customerName" sm/>
|
|
<tt-input label="Straße" v-model="offer.customerStreet" sm/>
|
|
<tt-input label="PLZ" v-model="offer.customerZip" sm/>
|
|
<tt-input label="Ort" v-model="offer.customerCity" sm/>
|
|
<tt-input label="UID" v-model="offer.customerVAT" sm/>
|
|
</div>
|
|
<hr>
|
|
<h4 class="text-center">Positionen</h4>
|
|
<tt-positions-manager ref="positionsManager" v-model="offer.positions" :config="positionsConfig" @updateField-article="fetchArticleData"/>
|
|
<hr>
|
|
<h4 class="text-center">Alternative Artikel</h4>
|
|
<tt-positions-manager ref="alternativePositionsManager" v-model="offer.alternativePositions" :config="alternativePositionsConfig"/>
|
|
<hr>
|
|
<tt-input label="Gesamtrabatt (%)" v-model="offer.totalDiscount" sm row type="number"/>
|
|
<tt-select label="Zahlungskonditionen" :options="paymentTerms" sm row v-model="offer.paymentTerms"/>
|
|
<tt-select label="Lieferkonditionen" :options="deliveryTerms" sm row v-model="offer.deliveryTerms"/>
|
|
<tt-select label="Schlusstext" :options="closingTexts" sm row v-model="offer.closingText"/>
|
|
<hr>
|
|
<tt-textarea label="Notizen" v-model="offer.notes" sm row/>
|
|
</div>
|
|
</tt-modal>
|
|
`,
|
|
data() {
|
|
return {
|
|
window: window,
|
|
positionsConfig: {
|
|
fields: {
|
|
article: {
|
|
type: 'autocomplete',
|
|
label: 'Artikel',
|
|
apiUrl: '/WarehouseArticle/autoComplete',
|
|
customFieldReference: 'WarehouseArticle',
|
|
},
|
|
amount: {type: 'input', label: 'Menge', inputType: 'number'},
|
|
unit: {type: 'input', label: 'Einheit'},
|
|
articleNumber: {type: 'input', label: 'Artikelnummer'},
|
|
unitPrice: {type: 'input', label: 'Einzelpreis', inputType: 'number'},
|
|
discount: {type: 'input', label: 'Rabatt (%)', inputType: 'number'},
|
|
},
|
|
validateForm: (formData) => {
|
|
const requiredFields = ['article', 'amount', 'unitPrice'];
|
|
for (const field of requiredFields) {
|
|
if (!formData[field]) {
|
|
window.notify('error', `Bitte füllen Sie ${this.positionsConfig.fields[field].label} aus`);
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
},
|
|
},
|
|
alternativePositionsConfig: {
|
|
fields: {
|
|
article: {type: 'input', label: 'Artikel'},
|
|
description: {type: 'textarea', label: 'Beschreibung'},
|
|
},
|
|
},
|
|
paymentTerms: [
|
|
{value: 'net30', text: '30 Tage netto'},
|
|
{value: 'net60', text: '60 Tage netto'},
|
|
{value: 'immediate', text: 'Sofort fällig'},
|
|
],
|
|
deliveryTerms: [
|
|
{value: 'ex_works', text: 'Ab Werk'},
|
|
{value: 'free_delivery', text: 'Frei Haus'},
|
|
{value: 'fob', text: 'FOB'},
|
|
],
|
|
closingTexts: [
|
|
{value: 'standard', text: 'Standardtext'},
|
|
{value: 'custom1', text: 'Angepasster Text 1'},
|
|
{value: 'custom2', text: 'Angepasster Text 2'},
|
|
],
|
|
offer: {
|
|
editor: window.TT_CONFIG['USER_ID'],
|
|
customerNumber: '',
|
|
reference: '',
|
|
purpose: '',
|
|
customerName: '',
|
|
customerStreet: '',
|
|
customerZip: '',
|
|
customerCity: '',
|
|
customerVAT: '',
|
|
positions: [],
|
|
alternativePositions: [],
|
|
totalDiscount: 0,
|
|
paymentTerms: 'net30',
|
|
deliveryTerms: 'ex_works',
|
|
closingText: 'standard',
|
|
notes: '',
|
|
}
|
|
}
|
|
},
|
|
async mounted() {
|
|
if (this.id !== 'create') {
|
|
const response = await axios.get(`${window.TT_CONFIG["BASE_PATH"]}/WarehouseOffer/getById`, {params: {id: this.id}});
|
|
this.offer = response.data;
|
|
this.offer.positions = JSON.parse(this.offer.positions);
|
|
this.offer.alternativePositions = JSON.parse(this.offer.alternativePositions);
|
|
}
|
|
},
|
|
methods: {
|
|
async submit() {
|
|
if (this.offer.positions.length === 0) return window.notify('error', 'Bitte fügen Sie mindestens eine Position hinzu.');
|
|
|
|
const url = this.id === 'create'
|
|
? `${window.TT_CONFIG["BASE_PATH"]}/WarehouseOffer/create`
|
|
: `${window.TT_CONFIG["BASE_PATH"]}/WarehouseOffer/update`;
|
|
|
|
const response = await axios.post(url, this.offer);
|
|
|
|
if (response.data.success) {
|
|
window.notify('success', response.data.message ?? 'Angebot erfolgreich gespeichert');
|
|
this.$emit('close');
|
|
} else {
|
|
window.notify('error',
|
|
response.data.errors ? Object.values(response.data.errors).join('<br>') : response.data.message || 'Ein Fehler ist aufgetreten');
|
|
}
|
|
},
|
|
async fetchArticleData(article) {
|
|
if (typeof article === 'number') {
|
|
const response = await axios.get(`${window.TT_CONFIG["BASE_PATH"]}/WarehouseArticle/getById`, {params: {id: article}});
|
|
this.$refs.positionsManager.updateField('articleNumber', response.data.articleNumber);
|
|
this.$refs.positionsManager.updateField('unitPrice',
|
|
Object.values(JSON.parse(response.data.cheapestSellPrice)).find(price => price.title === 'Verkauf').price);
|
|
this.$refs.positionsManager.updateField('unit', response.data.unit);
|
|
}
|
|
},
|
|
},
|
|
});
|
|
|
|
Vue.component('warehouse-offer', {
|
|
template: `
|
|
<tt-card>
|
|
<warehouse-offer-modal v-if="offerModalId" :id="offerModalId" @close="offerModalId = null;$refs.table.$refs.table.refreshTable()"/>
|
|
<button @click="offerModalId = 'create'" class="btn btn-primary">Angebot erstellen</button>
|
|
<tt-table-crud emit-edit @edit="offerModalId = $event.id" ref="table">
|
|
<template v-slot:expandedRow="{ row }">
|
|
<div>
|
|
<h5>Notizen</h5>
|
|
<p>{{ row.notes }}</p>
|
|
<h5>Verlauf</h5>
|
|
<ul>
|
|
<li v-for="entry in row.journal">{{ entry.date }} - {{ entry.description }}</li>
|
|
</ul>
|
|
</div>
|
|
</template>
|
|
</tt-table-crud>
|
|
</tt-card>
|
|
`,
|
|
data() {
|
|
return {
|
|
window: window,
|
|
offerModalId: null,
|
|
}
|
|
},
|
|
});
|