updated warehouseoffer

This commit is contained in:
Luca Haid
2025-06-16 09:50:10 +02:00
parent 203b9fa7a8
commit 4b46313ab8
3 changed files with 91 additions and 6 deletions

View File

@@ -33,6 +33,7 @@ $texts = [
'usageLabel' => 'Zweck:',
'customerReferenceLabel' => 'Kundenreferenz:',
'validUntilLabel' => 'Gültig bis:',
'vatLabel' => 'USt-IdNr.:',
'pageLabel' => 'Seite', // For page numbering in content if needed
'table' => [
'pos' => 'Pos',
@@ -241,6 +242,10 @@ $formattedValidUntil = $validUntilDate ? date("d.m.Y", $validUntilDate) : date("
<tr>
<td class="label" style="text-align: left"><?= $text['usageLabel'] ?></td>
<td><?= $offer->purpose ?? 'Keine Angabe' ?></td>
<?php if (!empty($offer->customerVAT)) : ?>
<td class="label"><?= $text['vatLabel'] ?></td>
<td><?= htmlspecialchars($offer->customerVAT) ?></td>
<?php endif; ?>
</tr>

View File

@@ -11,7 +11,7 @@ class WarehouseOfferController extends TTCrud {
['key' => 'customerNumber', 'text' => 'Kundennummer', 'required' => true, 'modal' => false],
['key' => 'customerName', 'text' => 'Kundenname', 'required' => true, 'modal' => false],
['key' => 'customerCity', 'text' => 'Stadt', 'required' => true, 'modal' => false],
['key' => 'customerVAT', 'text' => 'UID', 'required' => true, 'modal' => false],
['key' => 'customerVAT', 'text' => 'UID', 'required' => false, 'modal' => false],
['key' => 'editor', 'text' => 'Sachbearbeiter', 'required' => true, 'modal' => ['type' => 'select'], 'table' => ['filter' => 'select']],
['key' => 'totalAmount', 'text' => 'Gesamtbetrag', 'required' => true, 'modal' => false],
['key' => 'status', 'text' => 'Status', 'required' => true],

View File

@@ -56,6 +56,8 @@ Vue.component('tt-positions-manager',
formData: {},
groupName: '',
selectedIndex: null,
editingGroupName: null,
tempGroupName: '',
}
},
template: `
@@ -155,8 +157,21 @@ Vue.component('tt-positions-manager',
<template v-for="(group, groupName) in positionsToRender">
<tr v-if="groupMode">
<td colspan="100%">
<h4 style="text-align: center;">{{ groupName }}</h4>
<td :colspan="Object.keys(config.fields).length + 1">
<div class="d-flex justify-content-center align-items-center">
<template v-if="editingGroupName === groupName">
<tt-input v-model="tempGroupName" sm style="max-width: 200px; margin-right: 10px;"/>
<tt-button @click="saveGroupName(groupName)" sm additional-class="btn-success" icon="fa fa-check" style="margin-right: 5px;"/>
<tt-button @click="cancelGroupEdit" sm additional-class="btn-secondary" icon="fa fa-times"/>
</template>
<template v-else>
<h4 style="margin: 0; margin-right: 10px;">{{ groupName }}</h4>
<template v-if="groupName !== 'Keine Gruppe'">
<tt-button @click="startGroupEdit(groupName)" sm additional-class="btn-primary" icon="fa fa-edit" style="margin-right: 5px;"/>
<tt-button @click="deleteGroup(groupName)" sm additional-class="btn-danger" icon="fa fa-trash"/>
</template>
</template>
</div>
</td>
</tr>
<tr v-for="(position, index) in group" :key="groupMode ? groupName + index : index">
@@ -175,8 +190,8 @@ Vue.component('tt-positions-manager',
<select v-if="groupMode" v-model="position._group" @change="$set(position, '_group', $event.target.value)">
<option v-for="group in allGroups" :value="group">{{ group }}</option>
</select>
<tt-button @click="editEntry(index)" sm additional-class="btn-primary" icon="fa fa-edit"/>
<tt-button @click="deleteEntry(index)" sm additional-class="btn-danger" icon="fa fa-trash"/>
<tt-button @click="editEntry(getActualIndex(position, groupName, index))" sm additional-class="btn-primary" icon="fa fa-edit"/>
<tt-button @click="deleteEntry(getActualIndex(position, groupName, index))" sm additional-class="btn-danger" icon="fa fa-trash"/>
</td>
</tr>
</template>
@@ -237,6 +252,71 @@ Vue.component('tt-positions-manager',
this.positions.push({_group: this.groupName});
this.groupName = '';
},
startGroupEdit(groupName) {
this.editingGroupName = groupName;
this.tempGroupName = groupName;
},
saveGroupName(oldGroupName) {
if (this.tempGroupName.trim() === '') {
window.notify('error', 'Gruppenname darf nicht leer sein');
return;
}
if (this.tempGroupName !== oldGroupName) {
// Update all positions with the old group name to the new group name
this.positions.forEach(position => {
if (position._group === oldGroupName) {
this.$set(position, '_group', this.tempGroupName);
}
});
this.$emit('input', this.positions);
}
this.editingGroupName = null;
this.tempGroupName = '';
},
cancelGroupEdit() {
this.editingGroupName = null;
this.tempGroupName = '';
},
deleteGroup(groupName) {
if (confirm(`Möchten Sie die Gruppe "${groupName}" wirklich löschen? Alle Einträge werden in "Keine Gruppe" verschoben.`)) {
// Move all items from this group to "Keine Gruppe" and remove empty group entries
this.positions = this.positions.filter(position => {
if (position._group === groupName) {
// If it's just a group header (only has _group property), remove it
if (Object.keys(position).length === 1) {
return false;
}
// Otherwise, move to "Keine Gruppe"
this.$set(position, '_group', 'Keine Gruppe');
}
return true;
});
this.$emit('input', this.positions);
}
},
getActualIndex(position, groupName, groupIndex) {
// Find the actual index in the positions array
if (!this.groupMode) return groupIndex;
let actualIndex = 0;
let currentGroupIndex = 0;
for (let i = 0; i < this.positions.length; i++) {
const pos = this.positions[i];
const posGroup = pos._group ?? 'Keine Gruppe';
if (posGroup === groupName && Object.keys(pos).length > 1) {
if (currentGroupIndex === groupIndex) {
return i;
}
currentGroupIndex++;
}
}
return groupIndex; // Fallback
},
async editEntry(index) {
this.selectedIndex = index;
for (const [key, field] of Object.entries(this.config.fields)) {
@@ -316,4 +396,4 @@ Vue.component('tt-positions-manager',
deep: true
}
}
});
})