Besseres Handling Rückerstattungen
This commit is contained in:
@@ -27,13 +27,33 @@ const props = defineProps({
|
||||
reason: String,
|
||||
reasonNote: String,
|
||||
acceptedAt: String,
|
||||
donation: Boolean,
|
||||
})
|
||||
|
||||
// Der Zustand wandert in ein ref, damit die Seite nach dem Absenden umschalten kann, ohne neu zu laden.
|
||||
const state = ref(props.state)
|
||||
|
||||
const form = reactive({accountOwner: '', accountIban: '', declarationAccepted: false})
|
||||
const errors = reactive({accountOwner: '', accountIban: '', declaration: ''})
|
||||
// Ob gespendet wurde -- für den Abschlusstext. Nach dem Absenden setzt submit() den Wert selbst, ein
|
||||
// Neuladen holt ihn vom Controller.
|
||||
const donated = ref(props.donation === true)
|
||||
|
||||
/**
|
||||
* Der Weg durch die Seite, nach dem Muster der Auslagenabrechnung (refund-data.vue): erst die
|
||||
* Entscheidung, dann die Angaben dazu. Nichts wird gefragt, was für den gewählten Weg keine Rolle spielt.
|
||||
*
|
||||
* decision -- '' (noch nichts gewählt) | 'donation' (spenden) | 'payout' (auszahlen lassen)
|
||||
* sameAccount -- nur bei 'payout': ob es das Konto der Ursprungszahlung ist
|
||||
*/
|
||||
const decision = ref('')
|
||||
const sameAccount = ref(null)
|
||||
|
||||
const form = reactive({
|
||||
accountOwner: '',
|
||||
accountIban: '',
|
||||
declarationAccepted: false,
|
||||
accountDeclarationAccepted: false,
|
||||
})
|
||||
const errors = reactive({accountOwner: '', accountIban: '', declaration: '', accountDeclaration: ''})
|
||||
const saving = ref(false)
|
||||
|
||||
/**
|
||||
@@ -48,12 +68,41 @@ const accountComplete = computed(
|
||||
() => form.accountOwner.trim() !== '' && form.accountIban.trim() !== ''
|
||||
)
|
||||
|
||||
const isDonation = computed(() => decision.value === 'donation')
|
||||
|
||||
/**
|
||||
* Wechselt den Weg und nimmt dabei jedes Kreuz zurück.
|
||||
*
|
||||
* Ohne das Zurücksetzen stünde eine Erklärung als bestätigt da, die in diesem Weg gar nicht gezeigt
|
||||
* wurde -- wer erst die Auszahlung ankreuzt und dann zur Spende wechselt, hätte den Verzicht nie gelesen.
|
||||
*/
|
||||
function choose(next) {
|
||||
decision.value = next
|
||||
sameAccount.value = null
|
||||
form.declarationAccepted = false
|
||||
form.accountDeclarationAccepted = false
|
||||
Object.keys(errors).forEach((key) => (errors[key] = ''))
|
||||
}
|
||||
|
||||
/** Zurück zur ersten Frage -- aus der Sackgasse „anderes Konto" führt der Weg zur Spende. */
|
||||
function reset() {
|
||||
choose('')
|
||||
}
|
||||
|
||||
function validate() {
|
||||
errors.accountOwner = form.accountOwner.trim() ? '' : 'Bitte gib an, wem das Konto gehört.'
|
||||
errors.accountIban = form.accountIban.trim() ? '' : 'Bitte gib die IBAN des Kontos ein.'
|
||||
Object.keys(errors).forEach((key) => (errors[key] = ''))
|
||||
|
||||
errors.declaration = form.declarationAccepted ? '' : 'Bitte bestätige die Erklärung.'
|
||||
|
||||
return !errors.accountOwner && !errors.accountIban && !errors.declaration
|
||||
if (!isDonation.value) {
|
||||
errors.accountOwner = form.accountOwner.trim() ? '' : 'Bitte gib an, wem das Konto gehört.'
|
||||
errors.accountIban = form.accountIban.trim() ? '' : 'Bitte gib die IBAN des Kontos ein.'
|
||||
errors.accountDeclaration = form.accountDeclarationAccepted
|
||||
? ''
|
||||
: 'Bitte bestätige, dass es das Konto ist, von dem der Beitrag gezahlt wurde.'
|
||||
}
|
||||
|
||||
return Object.values(errors).every((message) => message === '')
|
||||
}
|
||||
|
||||
async function submit() {
|
||||
@@ -61,14 +110,21 @@ async function submit() {
|
||||
|
||||
saving.value = true
|
||||
|
||||
// Bei einer Spende geht keine Bankverbindung mit -- es gibt keine, und der Server erwartet auch keine.
|
||||
const body = isDonation.value
|
||||
? {donation: true, declarationAccepted: form.declarationAccepted}
|
||||
: {
|
||||
donation: false,
|
||||
accountOwner: form.accountOwner,
|
||||
accountIban: form.accountIban,
|
||||
declarationAccepted: form.declarationAccepted,
|
||||
accountDeclarationAccepted: form.accountDeclarationAccepted,
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await request('/api/v1/participant-refund/' + props.token + '/accept', {
|
||||
method: 'POST',
|
||||
body: {
|
||||
accountOwner: form.accountOwner,
|
||||
accountIban: form.accountIban,
|
||||
declarationAccepted: form.declarationAccepted,
|
||||
},
|
||||
body,
|
||||
})
|
||||
|
||||
if (!response) {
|
||||
@@ -88,6 +144,7 @@ async function submit() {
|
||||
}
|
||||
|
||||
toast.success(response.message)
|
||||
donated.value = isDonation.value
|
||||
state.value = 'accepted'
|
||||
} finally {
|
||||
saving.value = false
|
||||
@@ -128,7 +185,12 @@ async function submit() {
|
||||
</table>
|
||||
|
||||
<template v-if="state === 'accepted'">
|
||||
<p class="hint">
|
||||
<p v-if="donated" class="hint">
|
||||
Du hast den Betrag gespendet<span v-if="props.acceptedAt"> (am {{ props.acceptedAt }})</span>
|
||||
– vielen Dank. Es gibt nichts weiter zu tun. Stimmt etwas nicht, wende dich
|
||||
bitte an die Aktionsleitung: {{ props.eventEmail }}
|
||||
</p>
|
||||
<p v-else class="hint">
|
||||
Deine Angaben liegen uns vor<span v-if="props.acceptedAt"> (seit {{ props.acceptedAt }})</span>.
|
||||
Die Überweisung veranlasst die Aktionsleitung. Stimmt etwas nicht, wende dich bitte
|
||||
an sie: {{ props.eventEmail }}
|
||||
@@ -141,57 +203,161 @@ async function submit() {
|
||||
dich bitte an die Aktionsleitung: {{ props.eventEmail }}
|
||||
</p>
|
||||
|
||||
<h3>Auf welches Konto sollen wir überweisen?</h3>
|
||||
|
||||
<form @submit.prevent="submit">
|
||||
<div class="field">
|
||||
<label for="account-owner">Kontoinhaber*in</label>
|
||||
<input
|
||||
id="account-owner"
|
||||
v-model="form.accountOwner"
|
||||
type="text"
|
||||
class="form-input"
|
||||
autocomplete="name"
|
||||
/>
|
||||
<ErrorText :message="errors.accountOwner" />
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label for="account-iban">IBAN</label>
|
||||
<IbanInput id="account-iban" v-model="form.accountIban" class="form-input" />
|
||||
<ErrorText :message="errors.accountIban" />
|
||||
</div>
|
||||
|
||||
<!--
|
||||
Die Erklärung, die anschließend auf dem Eigenbeleg steht. Sie muss hier
|
||||
gelesen und angekreuzt werden -- sonst schriebe der Beleg dem Teili eine
|
||||
Zusicherung zu, die er nie abgegeben hat.
|
||||
Erste Frage: Wer spendet, braucht kein Konto anzugeben. Deshalb steht sie
|
||||
vor allem anderen.
|
||||
-->
|
||||
<template v-if="accountComplete">
|
||||
<h3>Möchtest du den Betrag stattdessen spenden?</h3>
|
||||
|
||||
<p class="choice-hint">
|
||||
Spendest du, verbleibt der Betrag beim Verband und kommt unserer Arbeit
|
||||
zugute. Wir brauchen dann keine Bankverbindung von dir.
|
||||
</p>
|
||||
|
||||
<div class="choices">
|
||||
<button
|
||||
type="button"
|
||||
class="choice"
|
||||
:class="{active: decision === 'donation'}"
|
||||
@click="choose('donation')"
|
||||
>Ja, spenden</button>
|
||||
<button
|
||||
type="button"
|
||||
class="choice"
|
||||
:class="{active: decision === 'payout'}"
|
||||
@click="choose('payout')"
|
||||
>Nein, bitte erstatten</button>
|
||||
</div>
|
||||
|
||||
<!-- Spendenweg: nur der Verzicht, keine Kontoangaben. -->
|
||||
<template v-if="decision === 'donation'">
|
||||
<div class="declaration">
|
||||
<input
|
||||
id="refund-declaration"
|
||||
id="refund-donation-declaration"
|
||||
v-model="form.declarationAccepted"
|
||||
type="checkbox"
|
||||
/>
|
||||
<TextResource
|
||||
text-name="CONFIRMATION_PARTICIPANT_REFUND"
|
||||
belongs-to="refund-declaration"
|
||||
text-name="CONFIRMATION_PARTICIPANT_REFUND_DONATION"
|
||||
belongs-to="refund-donation-declaration"
|
||||
/>
|
||||
</div>
|
||||
<ErrorText :message="errors.declaration" />
|
||||
|
||||
<!-- Beim Speichern gesperrt statt ausgeblendet, sonst verschwände der Knopf
|
||||
unter dem Finger. -->
|
||||
<button
|
||||
v-if="form.declarationAccepted"
|
||||
class="button"
|
||||
type="submit"
|
||||
:disabled="saving"
|
||||
>
|
||||
{{ saving ? 'Wird gespeichert…' : 'Angaben absenden' }}
|
||||
{{ saving ? 'Wird gespeichert…' : 'Betrag spenden' }}
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<!--
|
||||
Auszahlungsweg: Erstattet wird ausschließlich auf das Konto, von dem der
|
||||
Beitrag kam. Das wird gefragt, bevor überhaupt eine IBAN eingegeben wird --
|
||||
sonst tippt jemand ein Konto ab, das wir anschließend ablehnen müssen.
|
||||
-->
|
||||
<template v-else-if="decision === 'payout'">
|
||||
<h3>Soll der Betrag auf das Konto erstattet werden, von dem der Beitrag
|
||||
gezahlt wurde?</h3>
|
||||
|
||||
<div class="choices">
|
||||
<button
|
||||
type="button"
|
||||
class="choice"
|
||||
:class="{active: sameAccount === true}"
|
||||
@click="sameAccount = true"
|
||||
>Ja</button>
|
||||
<button
|
||||
type="button"
|
||||
class="choice"
|
||||
:class="{active: sameAccount === false}"
|
||||
@click="sameAccount = false"
|
||||
>Nein</button>
|
||||
</div>
|
||||
|
||||
<template v-if="sameAccount === false">
|
||||
<p class="hint">
|
||||
So können wir den Betrag leider nicht erstatten: Zurück geht er nur auf
|
||||
das Konto, von dem der Teilnahmebeitrag gezahlt wurde. Wurde er von
|
||||
einem anderen Konto überwiesen – etwa dem eines Elternteils
|
||||
–, gib bitte dieses an. Hilft das nicht weiter, wende dich bitte
|
||||
an die Aktionsleitung: {{ props.eventEmail }}
|
||||
</p>
|
||||
|
||||
<button type="button" class="button" @click="reset()">
|
||||
Zurück zur Auswahl
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<template v-else-if="sameAccount === true">
|
||||
<div class="field">
|
||||
<label for="account-owner">Kontoinhaber*in</label>
|
||||
<input
|
||||
id="account-owner"
|
||||
v-model="form.accountOwner"
|
||||
type="text"
|
||||
class="form-input"
|
||||
autocomplete="name"
|
||||
/>
|
||||
<ErrorText :message="errors.accountOwner" />
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label for="account-iban">IBAN</label>
|
||||
<IbanInput id="account-iban" v-model="form.accountIban" class="form-input" />
|
||||
<ErrorText :message="errors.accountIban" />
|
||||
</div>
|
||||
|
||||
<!--
|
||||
Die Erklärungen, die anschließend auf dem Eigenbeleg stehen. Sie
|
||||
müssen hier gelesen und angekreuzt werden -- sonst schriebe der Beleg
|
||||
dem Teili Zusicherungen zu, die er nie abgegeben hat.
|
||||
-->
|
||||
<template v-if="accountComplete">
|
||||
<div class="declaration">
|
||||
<input
|
||||
id="refund-declaration"
|
||||
v-model="form.declarationAccepted"
|
||||
type="checkbox"
|
||||
/>
|
||||
<TextResource
|
||||
text-name="CONFIRMATION_PARTICIPANT_REFUND"
|
||||
belongs-to="refund-declaration"
|
||||
/>
|
||||
</div>
|
||||
<ErrorText :message="errors.declaration" />
|
||||
|
||||
<div class="declaration">
|
||||
<input
|
||||
id="refund-account-declaration"
|
||||
v-model="form.accountDeclarationAccepted"
|
||||
type="checkbox"
|
||||
/>
|
||||
<TextResource
|
||||
text-name="CONFIRMATION_PARTICIPANT_REFUND_ACCOUNT"
|
||||
belongs-to="refund-account-declaration"
|
||||
/>
|
||||
</div>
|
||||
<ErrorText :message="errors.accountDeclaration" />
|
||||
|
||||
<!-- Beim Speichern gesperrt statt ausgeblendet, sonst verschwände der
|
||||
Knopf unter dem Finger. -->
|
||||
<button
|
||||
v-if="form.declarationAccepted && form.accountDeclarationAccepted"
|
||||
class="button"
|
||||
type="submit"
|
||||
:disabled="saving"
|
||||
>
|
||||
{{ saving ? 'Wird gespeichert…' : 'Angaben absenden' }}
|
||||
</button>
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
</form>
|
||||
</template>
|
||||
</template>
|
||||
@@ -235,6 +401,34 @@ h3 {
|
||||
padding: 6px 0;
|
||||
}
|
||||
|
||||
.choice-hint {
|
||||
margin-bottom: 12px;
|
||||
font-size: 0.9rem;
|
||||
color: #4b5563;
|
||||
}
|
||||
|
||||
.choices {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.choice {
|
||||
flex: 1;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid #d8dde6;
|
||||
border-radius: 0;
|
||||
background: #ffffff;
|
||||
font-size: 0.95rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.choice.active {
|
||||
border-color: #1a4799;
|
||||
background: #eef2fa;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.field {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user