Files
thetool/public/js/pages/WarehouseOrder/WarehouseOrder.js
2025-02-13 11:56:27 +01:00

248 lines
12 KiB
JavaScript

Vue.component('warehouse-order-modal', {
props: {
id: {type: [String, Number], required: true},
mode: {type: String, default: 'sign'}
},
template: `
<tt-modal :show="true"
@submit="submit"
:delete="id !== 'create'"
:title="id === 'create' ? 'Bestellung erstellen' : \`Bestellung #\${id} bearbeiten\`"
@update:show="$emit('close')">
<div style="width: 99%">
<h4 class="text-center">Bestelldetails</h4>
<tt-select label="Bearbeiter (XINON)"
:options="window.TT_CONFIG.CRUD_CONFIG.columns.find(column => column.key === 'createBy')?.modal.items"
sm
row
v-model="order.editor"/>
<tt-input label="Externe Referenz" v-model="order.extReference" sm row/>
<hr>
<h4 class="text-center">Positionen</h4>
<tt-positions-manager
ref="positionsManager"
v-model="order.positions"
:config="positionsConfig"
@updateField-article="fetchDistributors"
@updateField-distributorId="fetchDistributorData"
/>
<hr>
<h4 class="text-center">Lieferadresse</h4>
<div style="display: grid; grid-gap: 10px; grid-template-columns: 2fr 2fr 1fr 1fr 2fr;">
<tt-input label="Name" v-model="order.delAddrName" sm/>
<tt-input label="Straße" v-model="order.delAddrLine" sm/>
<tt-input label="PLZ" v-model="order.delAddrPLZ" sm/>
<tt-input label="Ort" v-model="order.delAddrCity" sm/>
<tt-input label="E-Mail" v-model="order.delAddrEMail" sm/>
</div>
<hr>
<tt-textarea label="Notiz" v-model="order.note" sm row/>
</div>
</tt-modal>
`,
data() {
return {
window: window,
positionsConfig: {
customOrdering: 'distributorId',
fields: {
article: {
type: 'autocomplete',
label: 'Artikel',
apiUrl: '/WarehouseArticle/autoComplete',
customFieldReference: 'WarehouseArticle',
},
distributorId: {type: 'select', label: 'Lieferant', options: [], customFieldReference: 'WarehouseDistributor'},
distributorArticleNumber: {type: 'input', label: 'Lieferant Art-Nr.'},
amount: {type: 'input', label: 'Menge', inputType: 'number'},
buyPrice: {type: 'input', label: 'Einkaufspreis', inputType: 'number'},
verwendung: {type: 'input', label: 'Verwendung'},
},
validateForm: (formData) => {
const fields = [
{key: 'amount', message: 'Bitte füllen Sie die Menge aus'},
{key: 'distributorId', message: 'Bitte füllen Sie den Lieferanten aus'},
{key: 'article', message: 'Bitte füllen Sie den Artikel aus'},
{key: 'buyPrice', message: 'Bitte füllen Sie den Einkaufspreis aus'}
];
for (const field of fields) {
if (!formData[field.key]) {
window.notify('error', field.message);
return false;
}
}
return true;
},
},
order: {
extReference: '',
delAddrName: 'XINON GmbH',
delAddrLine: 'Fladnitz im Raabtal 150',
delAddrPLZ: '8322',
delAddrCity: 'Studenzen',
delAddrEMail: 'einkauf@xinon.at',
note: '',
editor: window.TT_CONFIG['USER_ID'],
positions: [],
}
}
},
async mounted() {
if (this.id === 'create') return;
console.log(this.id);
const response = await axios.get(`${window.TT_CONFIG["BASE_PATH"]}/WarehouseOrder/getById?disableParse`, {params: {id: this.id}});
response.data.positions = JSON.parse(response.data.positions);
this.order = response.data;
},
methods: {
async submit() {
if (this.order.positions.length === 0) return window.notify('error', 'Bitte fügen Sie mindestens eine Position hinzu.');
if (this.id === 'create') {
const distributorIds = [...new Set(this.order.positions.map(position => position.distributorId))];
for (const distributorId of distributorIds) {
const response = await axios.post(`${window.TT_CONFIG["BASE_PATH"]}/WarehouseOrder/create`, {
...this.order,
distributorId,
positions: this.order.positions.filter(position => position.distributorId === distributorId)
}
);
if (response.data.success) {
this.$emit('close');
window.notify('success', response.data.message ?? 'Bestellung erfolgreich erstellt');
} else window.notify('error',
response.data.errors ? Object.values(response.data.errors).join('<br>') : response.data.message || 'Ein Fehler ist aufgetreten');
}
} else {
const response = await axios.post(`${window.TT_CONFIG["BASE_PATH"]}/WarehouseOrder/update`, this.order);
if (response.data.success) {
this.$emit('close');
window.notify('success', response.data.message ?? 'Bestellung erfolgreich aktualisiert');
} else window.notify('error',
response.data.errors ? Object.values(response.data.errors).join('<br>') : response.data.message || 'Ein Fehler ist aufgetreten');
}
},
async fetchDistributors(article) {
const url = `${window.TT_CONFIG["BASE_PATH"]}/WarehouseOrder/getArticleDistributorData`;
const params = typeof article === 'string' ? {allDistributor: true} : {articleId: article};
const response = await axios.get(url, {params});
this.positionsConfig.fields.distributorId.options = response.data.map(distributor => ({
value: distributor.id,
text: distributor.name,
externalArticleNumber: distributor.externalArticleNumber || null,
purchasePrice: distributor.purchasePrice || null,
}));
},
async fetchDistributorData(distributorId) {
if (distributorId && typeof this.$refs.positionsManager.formData.article === 'number') {
const distributor = this.positionsConfig.fields.distributorId.options.find(distributor => parseInt(distributor.value) ===
parseInt(distributorId));
this.$refs.positionsManager.updateField('distributorArticleNumber', distributor.externalArticleNumber);
this.$refs.positionsManager.updateField('buyPrice', distributor.purchasePrice);
}
},
},
});
Vue.component('warehouse-order-detail', {
//language=Vue
template: `
<tt-card>
<template v-slot:header><h4>Bestellungsdetails für #{{ loading ? 'Laden...' : order.orderNumber }}</h4></template>
<template v-if="loading">
<div class="d-flex justify-content-center align-items-center">
<div class="spinner-border spinner-border-sm text-primary" role="status">
<span class="sr-only">Loading...</span>
</div>
</div>
</template>
<template v-else>
<h3>Lieferadresse</h3>
<div>{{order.delAddrName}}</div>
<div>{{order.delAddrEMail}}</div>
<div>{{order.delAddrLine}}</div>
<div>{{order.delAddrPLZ}} {{order.delAddrCity}}</div>
<div style="display: grid; grid-gap: 10px; grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr;margin-top: 24px">
<div><strong>Artikel</strong></div>
<div><strong>Menge</strong></div>
<div><strong>Preis</strong></div>
<div><strong>Lieferant</strong></div>
<div><strong>Verwendung</strong></div>
<div><strong>Summe</strong></div>
</div>
<div style="display: grid; grid-gap: 10px; grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr;" v-for="position in order.positions">
<div>{{ position.articleName }}</div>
<div>{{ position.amount }}</div>
<div>{{ position.buyPrice }}</div>
<div>{{ position.distributorName }}</div>
<div>{{ position.verwendung }}</div>
<div>{{ position.amount * position.buyPrice }}</div>
</div>
</template>
</tt-card>
`,
props: {
id: {type: [String, Number], required: true}
},
data() {
return {
window: window,
order: {},
loading: true
}
},
async mounted() {
const response = await axios.get(`${window.TT_CONFIG["BASE_PATH"]}/WarehouseOrder/getById`, {params: {id: this.id}});
this.order = response.data;
this.loading = false;
},
});
Vue.component('warehouse-order', {
template: `
<tt-card>
<warehouse-order-modal v-if="orderModalId" :id="orderModalId" @close="closeOrderModal"/>
<button @click="orderModalId = 'create'" class="btn btn-primary">Bestellung erstellen</button>
<tt-table-crud emit-edit
@openpdf="window.open(window.TT_CONFIG['BASE_PATH'] + '/WarehouseOrder/createPDF?id=' + $event.id)"
@edit="orderModalId = $event.id" ref="table">
<template v-slot:expandedRow="{ row }">
<warehouse-order-detail :id="row['id']"/>
</template>
<template v-slot:sum="{ row }">{{ calculateSum(JSON.parse(row["positions"])).toFixed(2)}} €</template>
</tt-table-crud>
</tt-card>
`,
data() {
return {
window: window,
orderModalId: null,
}
},
methods: {
closeOrderModal() {
this.orderModalId = null;
this.$refs.table.$refs.table.refreshTable();
},
calculateSum(positions) {
return positions.reduce((sum, position) => sum + position.amount * position.buyPrice, 0);
}
}
});