Creating Participation refunds

This commit is contained in:
2026-09-03 21:23:26 +02:00
parent 9c4c28e566
commit 6a183d6498
55 changed files with 3985 additions and 42 deletions
+55 -7
View File
@@ -12,6 +12,10 @@ const blocks = ref([])
const assets = ref([])
const tokenGroups = ref({})
// Die Vorlagen sind nach Dokumentart getrennt; der Umschalter lädt jeweils deren Blöcke neu.
const documentType = ref(null)
const documentTypes = ref([])
const activeBlock = ref(null)
const saving = ref(false)
@@ -33,12 +37,15 @@ function selectBlock(block) {
onMounted(load)
async function load() {
const data = await request('/api/v1/admin/document-templates', {method: 'GET'})
const query = documentType.value ? '?type=' + encodeURIComponent(documentType.value) : ''
const data = await request('/api/v1/admin/document-templates' + query, {method: 'GET'})
if (!data) {
toast.error('Die Vorlage konnte nicht geladen werden.')
return
}
documentType.value = data.documentType
documentTypes.value = data.documentTypes ?? []
blocks.value = data.blocks ?? []
assets.value = data.assets ?? []
tokenGroups.value = data.tokenGroups ?? {}
@@ -47,6 +54,15 @@ async function load() {
await refreshPreview()
}
/** Beim Wechsel der Dokumentart alles neu holen -- Blöcke und Platzhalter sind je Art andere. */
async function selectDocumentType(type) {
if (type === documentType.value) return
documentType.value = type
caret.value = null
await load()
}
/** Nur editierbare Blöcke werden gesendet; der generierte Rechnungsinhalt bleibt unverändert. */
function editableBlocks() {
return Object.fromEntries(
@@ -60,7 +76,7 @@ async function save() {
try {
const response = await request('/api/v1/admin/document-templates', {
method: 'POST',
body: {blocks: editableBlocks()},
body: {type: documentType.value, blocks: editableBlocks()},
})
if (response?.status === 'success') {
@@ -83,7 +99,7 @@ async function refreshPreview() {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]')?.content ?? '',
},
body: JSON.stringify({blocks: editableBlocks()}),
body: JSON.stringify({type: documentType.value, blocks: editableBlocks()}),
})
if (!response.ok) {
@@ -217,14 +233,28 @@ function onFileChosen(event) {
</script>
<template>
<AdminAppLayout title="Rechnungsvorlage">
<AdminAppLayout title="Dokumentvorlagen">
<shadowed-box style="width: 95%; margin: 20px auto; padding: 20px;">
<p class="intro">
Die Vorlage gilt für <strong>alle Mandanten</strong>; die eingesetzten Werte kommen je
Rechnung aus dem jeweiligen Mandanten. Sie liegt in der Datenbank und wird von einem
Die Vorlagen gelten für <strong>alle Mandanten</strong>; die eingesetzten Werte kommen je
Dokument aus dem jeweiligen Mandanten. Sie liegen in der Datenbank und werden von einem
Update nicht überschrieben.
</p>
<div class="type-switch">
<label for="document-type">Dokumentart</label>
<select
id="document-type"
class="form-input"
:value="documentType"
@change="selectDocumentType($event.target.value)"
>
<option v-for="type in documentTypes" :key="type.value" :value="type.value">
{{ type.label }}
</option>
</select>
</div>
<div class="layout">
<!-- Blöcke und Editor -->
<div class="editor-column">
@@ -243,7 +273,7 @@ function onFileChosen(event) {
<div v-if="current" class="editor-panel">
<p v-if="!current.editable" class="notice">
Dieser Block wird beim Erzeugen der Rechnung aus den Daten der Anmeldung
Dieser Block wird beim Erzeugen des Dokuments aus den Daten der Anmeldung
zusammengesetzt und lässt sich nicht bearbeiten.
</p>
@@ -360,6 +390,24 @@ function onFileChosen(event) {
color: #4b5563;
}
.type-switch {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 18px;
}
.type-switch label {
font-weight: bold;
font-size: 0.9rem;
color: #4b5563;
}
.type-switch select {
width: auto;
min-width: 220px;
}
.layout {
display: flex;
gap: 24px;