Automatischer Zahlungsparser
This commit is contained in:
@@ -86,6 +86,16 @@ const retainedAmount = computed(() => {
|
||||
|
||||
const hasRetention = computed(() => retainedAmount.value > 0);
|
||||
|
||||
/**
|
||||
* Das Konto, das die Zahlungsart kennt -- bei der Überweisung das des Zahlungseingangs.
|
||||
*
|
||||
* Ist es da, wird es weder erfragt noch bearbeitet: Erstattet wird auf das Konto, von dem der Beitrag
|
||||
* kam, und genau das steht hier. `null`, solange nichts bekannt ist (Barzahlung, Altbestand).
|
||||
*/
|
||||
const knownRefundAccount = computed(
|
||||
() => showParticipant.value?.refundData?.available ? showParticipant.value.refundData : null
|
||||
);
|
||||
|
||||
const retainedAmountReadable = computed(
|
||||
() => retainedAmount.value.toFixed(2).replace('.', ',') + ' Euro'
|
||||
);
|
||||
@@ -121,7 +131,8 @@ const refundFormComplete = computed(() => {
|
||||
}
|
||||
}
|
||||
|
||||
if (refundForm.captureMode === 'management') {
|
||||
// Beim bekannten Konto gibt es nichts einzugeben -- es steht fest.
|
||||
if (refundForm.captureMode === 'management' && !knownRefundAccount.value) {
|
||||
return refundForm.accountOwner.trim() !== '' && refundForm.accountIban.trim() !== '';
|
||||
}
|
||||
|
||||
@@ -429,7 +440,7 @@ function validateRefund() {
|
||||
|
||||
// Beim Direktweg wird sofort eingereicht -- danach gibt es keine Gelegenheit mehr zu berichtigen.
|
||||
// Ob die IBAN wirklich stimmt, prüft der Server mit Prüfziffer und länderabhängiger Länge.
|
||||
if (refundForm.captureMode === 'management') {
|
||||
if (refundForm.captureMode === 'management' && !knownRefundAccount.value) {
|
||||
if (!refundForm.accountOwner.trim()) {
|
||||
refundErrors.accountOwner = 'Bitte gib an, wem das Konto gehört.';
|
||||
}
|
||||
@@ -456,9 +467,14 @@ async function execRefund() {
|
||||
amount: refundForm.amount,
|
||||
reason: refundForm.reason,
|
||||
reasonNote: refundForm.reasonNote,
|
||||
// Leer beim Weg über den Teili -- dann verschickt der Server nur den Link.
|
||||
accountOwner: refundForm.captureMode === 'management' ? refundForm.accountOwner : '',
|
||||
accountIban: refundForm.captureMode === 'management' ? refundForm.accountIban : '',
|
||||
// Leer beim Weg über den Teili -- dann verschickt der Server nur den Link. Beim
|
||||
// Sofort-Einreichen mit bekanntem Konto geht dieses mit; eingegeben wurde nichts.
|
||||
accountOwner: refundForm.captureMode === 'management'
|
||||
? (knownRefundAccount.value?.accountOwner ?? refundForm.accountOwner)
|
||||
: '',
|
||||
accountIban: refundForm.captureMode === 'management'
|
||||
? (knownRefundAccount.value?.accountIban ?? refundForm.accountIban)
|
||||
: '',
|
||||
// Spende: kein Konto, trotzdem sofort eingereicht.
|
||||
donation: refundForm.captureMode === 'donation',
|
||||
// Leer bei voller Erstattung -- dann gibt es nichts zu begründen.
|
||||
@@ -805,14 +821,36 @@ function mailToGroup(groupKey) {
|
||||
Liegt die Bankverbindung schon vor oder will der Teili spenden, entfällt der Umweg über ihn:
|
||||
die Erstattung wird sofort eingereicht. Er bekommt den Beleg trotzdem.
|
||||
-->
|
||||
<!--
|
||||
Ist das Konto aus dem Zahlungseingang bekannt, wird es weder erfragt noch bearbeitet: Es
|
||||
steht fest, dass auf genau dieses Konto zu erstatten ist. Der Teili entscheidet dann nur
|
||||
noch, ob er es haben oder spenden möchte.
|
||||
-->
|
||||
<div v-if="knownRefundAccount" class="refund-known-account">
|
||||
<strong>Konto aus dem Zahlungseingang</strong>
|
||||
<span>{{ knownRefundAccount.accountOwner }}</span>
|
||||
<span class="refund-known-account__iban">{{ knownRefundAccount.accountIban }}</span>
|
||||
<span class="refund-known-account__source">{{ knownRefundAccount.source }}</span>
|
||||
</div>
|
||||
|
||||
<div class="refund-field">
|
||||
<label class="refund-choice">
|
||||
<input type="radio" value="participant" v-model="refundForm.captureMode" />
|
||||
Teilnehmer*in trägt die Bankverbindung selbst ein
|
||||
<template v-if="knownRefundAccount">
|
||||
Teilnehmer*in bestätigt die Erstattung oder spendet
|
||||
</template>
|
||||
<template v-else>
|
||||
Teilnehmer*in trägt die Bankverbindung selbst ein
|
||||
</template>
|
||||
</label>
|
||||
<label class="refund-choice">
|
||||
<input type="radio" value="management" v-model="refundForm.captureMode" />
|
||||
Bankverbindung liegt mir vor
|
||||
<template v-if="knownRefundAccount">
|
||||
Sofort einreichen, ohne auf die Rückmeldung zu warten
|
||||
</template>
|
||||
<template v-else>
|
||||
Bankverbindung liegt mir vor
|
||||
</template>
|
||||
</label>
|
||||
<label class="refund-choice">
|
||||
<input type="radio" value="donation" v-model="refundForm.captureMode" />
|
||||
@@ -825,7 +863,13 @@ function mailToGroup(groupKey) {
|
||||
eingereicht; der Teili erhält den Beleg per E-Mail.
|
||||
</p>
|
||||
|
||||
<template v-if="refundForm.captureMode === 'management'">
|
||||
<!-- Bekanntes Konto: nichts einzugeben, es wird ohnehin dieses genommen. -->
|
||||
<p v-if="refundForm.captureMode === 'management' && knownRefundAccount" class="refund-hint">
|
||||
Die Erstattung wird sofort auf das oben genannte Konto eingereicht. Der Teili erhält den
|
||||
Beleg per E-Mail, wird aber nicht mehr nach einer Spende gefragt.
|
||||
</p>
|
||||
|
||||
<template v-if="refundForm.captureMode === 'management' && !knownRefundAccount">
|
||||
<div class="refund-field">
|
||||
<label for="refund_account_owner">Kontoinhaber*in</label>
|
||||
<input
|
||||
@@ -882,6 +926,31 @@ function mailToGroup(groupKey) {
|
||||
color: #4b5563;
|
||||
}
|
||||
|
||||
/* Anzeige, kein Feld: bewusst nicht wie ein Eingabeblock gesetzt, damit gar nicht erst der Eindruck
|
||||
entsteht, hier ließe sich etwas ändern. */
|
||||
.refund-known-account {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
margin-bottom: 12px;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid #d1d5db;
|
||||
border-radius: 6px;
|
||||
background-color: #f9fafb;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.refund-known-account__iban {
|
||||
font-family: monospace;
|
||||
letter-spacing: 0.04em;
|
||||
color: #4b5563;
|
||||
}
|
||||
|
||||
.refund-known-account__source {
|
||||
color: #6b7280;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.refund-field {
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
@@ -61,8 +61,16 @@ class AcceptRefundCommand
|
||||
return $response;
|
||||
}
|
||||
|
||||
$owner = trim($this->request->accountOwner);
|
||||
$iban = Iban::normalize($this->request->accountIban);
|
||||
// Steht das Konto bereits am Vorgang, stammt es aus dem Zahlungseingang und wurde bei der
|
||||
// Freigabe festgeschrieben. Dann gilt es -- und die Felder aus dem Request werden verworfen.
|
||||
//
|
||||
// Das ist der eigentliche Gewinn der ganzen Übung: Der Teili tippt keine IBAN mehr ab, also
|
||||
// kann er sich weder vertippen noch lässt sich über einen direkten Aufruf dieser Route eine
|
||||
// fremde IBAN unterschieben. Erstattet wird auf das Konto, von dem gezahlt wurde, Punkt.
|
||||
$accountIsKnown = !$this->request->donation && filled($refund->account_iban);
|
||||
|
||||
$owner = $accountIsKnown ? (string) $refund->account_owner : trim($this->request->accountOwner);
|
||||
$iban = $accountIsKnown ? (string) $refund->account_iban : Iban::normalize($this->request->accountIban);
|
||||
|
||||
// Serverseitig und nicht nur im Formular: die Erklärung ist der einzige Grund, warum der Beleg
|
||||
// als Eigenbeleg etwas wert ist. Ließe sie sich mit einem direkten Aufruf übergehen, stünde auf
|
||||
@@ -83,14 +91,19 @@ class AcceptRefundCommand
|
||||
. 'von dem der Beitrag gezahlt wurde.';
|
||||
}
|
||||
|
||||
if ($owner === '') {
|
||||
$response->errorTypes['accountOwner'] = 'Bitte gib an, wem das Konto gehört.';
|
||||
}
|
||||
// Nur was der Teili selbst eingibt, muss geprüft werden. Das bekannte Konto hat die
|
||||
// Prüfung schon beim Import bestanden -- dort wird eine ungültige IBAN gar nicht erst
|
||||
// übernommen.
|
||||
if (!$accountIsKnown) {
|
||||
if ($owner === '') {
|
||||
$response->errorTypes['accountOwner'] = 'Bitte gib an, wem das Konto gehört.';
|
||||
}
|
||||
|
||||
if ($iban === '') {
|
||||
$response->errorTypes['accountIban'] = 'Bitte gib die IBAN des Kontos ein.';
|
||||
} elseif (!Iban::isValid($iban)) {
|
||||
$response->errorTypes['accountIban'] = 'Diese IBAN stimmt nicht. Bitte prüfe deine Eingabe.';
|
||||
if ($iban === '') {
|
||||
$response->errorTypes['accountIban'] = 'Bitte gib die IBAN des Kontos ein.';
|
||||
} elseif (!Iban::isValid($iban)) {
|
||||
$response->errorTypes['accountIban'] = 'Diese IBAN stimmt nicht. Bitte prüfe deine Eingabe.';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,8 +8,15 @@ class AcceptRefundRequest
|
||||
{
|
||||
public function __construct(
|
||||
public readonly ?ParticipantRefund $refund,
|
||||
public readonly string $accountOwner,
|
||||
public readonly string $accountIban,
|
||||
/**
|
||||
* Die Eingaben des Teilis -- leer, wenn es nichts einzugeben gab.
|
||||
*
|
||||
* Das ist der Normalfall bei einer Spende (kein Konto nötig) und bei einem bereits bekannten
|
||||
* Konto: Dort steht die Bankverbindung seit der Freigabe am Vorgang, und der Command nimmt
|
||||
* ausschließlich diese.
|
||||
*/
|
||||
public readonly string $accountOwner = '',
|
||||
public readonly string $accountIban = '',
|
||||
/**
|
||||
* Ob der Teili die Erklärung auf der Seite angekreuzt hat. Ohne sie taugt der Beleg nichts.
|
||||
*
|
||||
|
||||
+30
-3
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Domains\ParticipantRefund\Actions\CreateRefundDocument;
|
||||
|
||||
use App\Enumerations\RefundAccountSource;
|
||||
use App\Models\DocumentTemplate;
|
||||
use App\Models\Event;
|
||||
use App\Models\EventParticipant;
|
||||
@@ -30,6 +31,12 @@ class CreateRefundDocumentCommand
|
||||
/** Die zweite Erklärung des Auszahlungswegs: dass es das Konto der Ursprungszahlung ist. */
|
||||
public const string ACCOUNT_DECLARATION_TEXT = 'CONFIRMATION_PARTICIPANT_REFUND_ACCOUNT';
|
||||
|
||||
/**
|
||||
* Ihre Fassung für Zahlungsarten ohne Ursprungskonto (Barzahlung): Dort gab es kein Konto, von dem
|
||||
* der Beitrag kam -- erklärt wird stattdessen, dass das angegebene auf den eigenen Namen läuft.
|
||||
*/
|
||||
public const string OWN_ACCOUNT_DECLARATION_TEXT = 'CONFIRMATION_PARTICIPANT_REFUND_ACCOUNT_OWN';
|
||||
|
||||
/** Tritt im Spendenweg an die Stelle beider anderen -- dort gibt es kein Konto. */
|
||||
public const string DONATION_DECLARATION_TEXT = 'CONFIRMATION_PARTICIPANT_REFUND_DONATION';
|
||||
|
||||
@@ -152,6 +159,18 @@ class CreateRefundDocumentCommand
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Die Herkunft des Erstattungskontos, abgeleitet aus der Zahlungsart der Anmeldung.
|
||||
*
|
||||
* Der Beleg wird bei jedem Abruf neu gerendert und nicht gespeichert. Änderte jemand nachträglich
|
||||
* die Zahlungsart, zeigte ein Nachdruck die jeweils andere Kontoerklärung. Praktisch passiert das
|
||||
* nicht -- eine eigene Spalte am Vorgang wäre dafür unverhältnismäßig.
|
||||
*/
|
||||
private function accountSource(): RefundAccountSource
|
||||
{
|
||||
return $this->participant->refundData()->source;
|
||||
}
|
||||
|
||||
private function declarationText(): string
|
||||
{
|
||||
if ($this->request->donation) {
|
||||
@@ -164,14 +183,22 @@ class CreateRefundDocumentCommand
|
||||
|
||||
// Beide Sätze, weil die Person beide angekreuzt hat -- der Beleg schreibt ihr nur zu, was sie
|
||||
// gelesen hat, und die Kontoerklärung ist der Grund, warum die Auszahlung zulässig ist.
|
||||
//
|
||||
// Welche der beiden Kontoerklärungen gilt, sagt die Zahlungsart: Wer bar gezahlt hat, kann
|
||||
// nicht bestätigen, dass das Konto dasselbe ist -- es gab keines.
|
||||
$accountSource = $this->accountSource();
|
||||
|
||||
return $this->pageText(
|
||||
self::DECLARATION_TEXT,
|
||||
'Ich versichere, dass ich den genannten Betrag beglichen habe und nicht anderweitig '
|
||||
. 'zurückerstattet bekomme.'
|
||||
) . '<br /><br />' . $this->pageText(
|
||||
self::ACCOUNT_DECLARATION_TEXT,
|
||||
'Ich bestätige, dass das angegebene Konto dasselbe ist, von dem der Teilnahmebeitrag '
|
||||
. 'gezahlt wurde.'
|
||||
$accountSource->accountDeclarationText(),
|
||||
$accountSource === RefundAccountSource::None
|
||||
? 'Ich bestätige, dass das angegebene Konto auf meinen Namen läuft oder ich über dieses '
|
||||
. 'Konto verfügungsberechtigt bin.'
|
||||
: 'Ich bestätige, dass das angegebene Konto dasselbe ist, von dem der Teilnahmebeitrag '
|
||||
. 'gezahlt wurde.'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\Domains\ParticipantRefund\Actions\AcceptRefund\AcceptRefundCommand;
|
||||
use App\Domains\ParticipantRefund\Actions\AcceptRefund\AcceptRefundRequest;
|
||||
use App\Enumerations\RefundReason;
|
||||
use App\Enumerations\RetentionReason;
|
||||
use App\EventPaymentModules\DTO\GetRefundDataResponse;
|
||||
use App\Mail\ParticipantRefundMails\RefundReleasedMail;
|
||||
use App\Models\EventParticipant;
|
||||
use App\Models\ParticipantRefund;
|
||||
@@ -29,6 +30,8 @@ class ReleaseRefundCommand
|
||||
|
||||
private ParticipantRefundRepository $refunds;
|
||||
|
||||
private ?GetRefundDataResponse $knownRefundData = null;
|
||||
|
||||
public function __construct(private readonly ReleaseRefundRequest $request)
|
||||
{
|
||||
$this->participant = $request->participant;
|
||||
@@ -63,6 +66,10 @@ class ReleaseRefundCommand
|
||||
'retained_amount' => $this->request->retainedAmount(),
|
||||
'retention_reason' => $this->retentionReason(),
|
||||
'retention_reason_note' => $this->retentionReasonNote(),
|
||||
// Kennt die Zahlungsart das Konto, von dem der Beitrag kam, steht es von Anfang an
|
||||
// fest. Der Teili entscheidet dann nur noch: auszahlen oder spenden.
|
||||
'account_owner' => $this->knownAccountOwner(),
|
||||
'account_iban' => $this->knownAccountIban(),
|
||||
'released_by' => currentUser()?->id,
|
||||
'released_at' => now(),
|
||||
]);
|
||||
@@ -89,6 +96,31 @@ class ReleaseRefundCommand
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Das Konto, das die Zahlungsart kennt -- bei der Überweisung das des Zahlungseingangs.
|
||||
*
|
||||
* Lazy und einmalig, weil der Aufruf über die Event-Relation des Teilnehmers läuft
|
||||
* ({@see EventParticipant::paymentConfiguration()}) und in einem Vorgang mehrfach gebraucht wird.
|
||||
*/
|
||||
private function knownRefundData(): GetRefundDataResponse
|
||||
{
|
||||
return $this->knownRefundData ??= $this->participant->refundData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Bei der Sofort-Einreichung und bei der Spende bleibt das Feld leer: Dort setzt der
|
||||
* {@see AcceptRefundCommand} es -- aus der Eingabe der Aktionsleitung bzw. auf `null`.
|
||||
*/
|
||||
private function knownAccountOwner(): ?string
|
||||
{
|
||||
return $this->request->submitsDirectly() ? null : ($this->knownRefundData()->accountOwner);
|
||||
}
|
||||
|
||||
private function knownAccountIban(): ?string
|
||||
{
|
||||
return $this->request->submitsDirectly() ? null : ($this->knownRefundData()->accountIban);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reicht die Erstattung sofort ein, ohne den Umweg über den Teili.
|
||||
*
|
||||
|
||||
@@ -3,16 +3,20 @@
|
||||
namespace App\Domains\ParticipantRefund\Controllers;
|
||||
|
||||
use App\Domains\ParticipantRefund\Actions\AcceptRefund\AcceptRefundCommand;
|
||||
use App\Enumerations\RefundAccountSource;
|
||||
use App\Models\ParticipantRefund;
|
||||
use App\Providers\InertiaProvider;
|
||||
use App\Scopes\CommonController;
|
||||
use App\Support\Iban;
|
||||
use Inertia\Response;
|
||||
|
||||
/**
|
||||
* Die öffentliche Seite, auf der der Teili seine Bankverbindung hinterlegt.
|
||||
*
|
||||
* Liefert ausschließlich Anzeigedaten -- niemals die bereits erfasste Bankverbindung: der Token wandert
|
||||
* Liefert ausschließlich Anzeigedaten -- niemals die vollständige Bankverbindung: der Token wandert
|
||||
* durch ein Postfach, und was einmal eingetragen ist, muss von dort nicht wieder herauslesbar sein.
|
||||
* Ist das Konto aus dem Zahlungseingang bekannt, geht es deshalb **maskiert** hinaus: genug zum
|
||||
* Wiedererkennen, zu wenig zum Mitschreiben.
|
||||
*/
|
||||
class RefundPageController extends CommonController
|
||||
{
|
||||
@@ -58,6 +62,26 @@ class RefundPageController extends CommonController
|
||||
]);
|
||||
}
|
||||
|
||||
return array_merge($common, ['state' => 'open']);
|
||||
// Woher das Erstattungskonto kommt, entscheidet die Zahlungsart -- und damit, was die Seite
|
||||
// fragt: gar nichts (Konto liegt vor), die Herkunftsfrage (es gab ein Ursprungskonto) oder nur
|
||||
// die Felder (Barzahlung, es gab nie eines).
|
||||
$accountSource = $refund->account_iban !== null
|
||||
? RefundAccountSource::Known
|
||||
: $participant->refundData()->source;
|
||||
|
||||
return array_merge($common, [
|
||||
'state' => 'open',
|
||||
'accountSource' => $accountSource->value,
|
||||
// Der Name des page_texts-Eintrags, nicht der Text: Welche Erklärung gilt, entscheidet der
|
||||
// Server -- sonst könnten Seite und Beleg auseinanderlaufen.
|
||||
'accountDeclarationText' => $accountSource->accountDeclarationText(),
|
||||
// Ist das Konto bekannt (aus dem Zahlungseingang), braucht der Teili es nicht einzugeben --
|
||||
// er soll es aber wiedererkennen können, um einen Fehler zu melden. Deshalb maskiert:
|
||||
// Die vollständige Bankverbindung verlässt diese Seite weiterhin nicht.
|
||||
'knownAccount' => $refund->account_iban === null ? null : [
|
||||
'owner' => $refund->account_owner,
|
||||
'ibanMasked' => Iban::mask($refund->account_iban),
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,8 +28,35 @@ const props = defineProps({
|
||||
reasonNote: String,
|
||||
acceptedAt: String,
|
||||
donation: Boolean,
|
||||
/*
|
||||
* Das Konto aus dem Zahlungseingang, falls bekannt: {owner, ibanMasked}.
|
||||
*
|
||||
* Ist es gesetzt, wird es nicht mehr abgefragt -- es steht bereits am Vorgang und der Server
|
||||
* nimmt ohnehin nur dieses. Die IBAN kommt maskiert an: zum Wiedererkennen genug, zum
|
||||
* Mitschreiben zu wenig.
|
||||
*/
|
||||
knownAccount: {type: Object, default: null},
|
||||
/*
|
||||
* Woher das Erstattungskonto kommt -- entschieden von der Zahlungsart, nicht hier:
|
||||
* known -- liegt vor, wird nur bestätigt
|
||||
* origin -- es gab ein Ursprungskonto, wir fragen danach
|
||||
* none -- es gab nie eines (Barzahlung); dann ist die Herkunftsfrage sinnlos
|
||||
*/
|
||||
accountSource: {type: String, default: 'origin'},
|
||||
/** Name des page_texts-Eintrags mit der zweiten Erklärung -- der Server wählt ihn. */
|
||||
accountDeclarationText: {type: String, default: 'CONFIRMATION_PARTICIPANT_REFUND_ACCOUNT'},
|
||||
})
|
||||
|
||||
const hasKnownAccount = computed(() => props.accountSource === 'known')
|
||||
|
||||
/**
|
||||
* Ob nach dem Ursprungskonto gefragt wird.
|
||||
*
|
||||
* Nur wenn es eines gab: Wer bar gezahlt hat, könnte die Frage nicht sinnvoll beantworten -- und landete
|
||||
* bei „Nein" in einer Sackgasse, die ihn auffordert, das Konto zu nennen, von dem überwiesen wurde.
|
||||
*/
|
||||
const asksForOrigin = computed(() => props.accountSource === 'origin')
|
||||
|
||||
// Der Zustand wandert in ein ref, damit die Seite nach dem Absenden umschalten kann, ohne neu zu laden.
|
||||
const state = ref(props.state)
|
||||
|
||||
@@ -70,6 +97,32 @@ const accountComplete = computed(
|
||||
|
||||
const isDonation = computed(() => decision.value === 'donation')
|
||||
|
||||
/**
|
||||
* Wann die Erklärungen des Auszahlungswegs erscheinen.
|
||||
*
|
||||
* Bekanntes Konto: sofort -- es steht ja schon da. Sonst erst, wenn die Kontoangaben vollständig sind;
|
||||
* wo nach dem Ursprungskonto gefragt wird, zusätzlich erst nach einem „Ja".
|
||||
*/
|
||||
const showDeclarations = computed(() => {
|
||||
if (decision.value !== 'payout') return false
|
||||
if (hasKnownAccount.value) return true
|
||||
if (!accountComplete.value) return false
|
||||
|
||||
return asksForOrigin.value ? sameAccount.value === true : true
|
||||
})
|
||||
|
||||
/**
|
||||
* Wann nach Kontoinhaber*in und IBAN gefragt wird.
|
||||
*
|
||||
* Überall dort, wo das Konto nicht schon feststeht -- beim Ursprungskonto-Weg allerdings erst, nachdem
|
||||
* die Herkunftsfrage bejaht wurde: Sonst tippt jemand ein Konto ab, das wir anschließend ablehnen müssen.
|
||||
*/
|
||||
const showAccountFields = computed(() => {
|
||||
if (decision.value !== 'payout' || hasKnownAccount.value) return false
|
||||
|
||||
return asksForOrigin.value ? sameAccount.value === true : true
|
||||
})
|
||||
|
||||
/**
|
||||
* Wechselt den Weg und nimmt dabei jedes Kreuz zurück.
|
||||
*
|
||||
@@ -95,8 +148,13 @@ function validate() {
|
||||
errors.declaration = form.declarationAccepted ? '' : 'Bitte bestätige die Erklärung.'
|
||||
|
||||
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.'
|
||||
// Beim bekannten Konto gibt es nichts einzugeben und damit auch nichts zu prüfen -- nur die
|
||||
// Bestätigung, dass es stimmt.
|
||||
if (!hasKnownAccount.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.'
|
||||
@@ -111,15 +169,23 @@ async function submit() {
|
||||
saving.value = true
|
||||
|
||||
// Bei einer Spende geht keine Bankverbindung mit -- es gibt keine, und der Server erwartet auch keine.
|
||||
// Beim bekannten Konto gehen ebenfalls keine Kontofelder mit: Der Server nimmt ausschließlich,
|
||||
// was am Vorgang steht, alles Mitgeschickte würde er verwerfen.
|
||||
const body = isDonation.value
|
||||
? {donation: true, declarationAccepted: form.declarationAccepted}
|
||||
: {
|
||||
donation: false,
|
||||
accountOwner: form.accountOwner,
|
||||
accountIban: form.accountIban,
|
||||
declarationAccepted: form.declarationAccepted,
|
||||
accountDeclarationAccepted: form.accountDeclarationAccepted,
|
||||
}
|
||||
: hasKnownAccount.value
|
||||
? {
|
||||
donation: false,
|
||||
declarationAccepted: form.declarationAccepted,
|
||||
accountDeclarationAccepted: form.accountDeclarationAccepted,
|
||||
}
|
||||
: {
|
||||
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', {
|
||||
@@ -262,39 +328,84 @@ async function submit() {
|
||||
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>
|
||||
<!--
|
||||
Konto bekannt: Es steht schon am Vorgang und stammt aus dem
|
||||
Zahlungseingang. Die Frage danach erübrigt sich, eingegeben wird nichts
|
||||
mehr -- gezeigt wird es trotzdem, damit ein falsch zugeordneter
|
||||
Zahlungseingang hier auffällt und nicht erst beim Rückläufer der Bank.
|
||||
-->
|
||||
<template v-if="hasKnownAccount">
|
||||
<h3>Wir erstatten auf das Konto, von dem dein Beitrag kam</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>
|
||||
<div class="known-account">
|
||||
<span class="known-account__owner">{{ knownAccount.owner }}</span>
|
||||
<span class="known-account__iban">{{ knownAccount.ibanMasked }}</span>
|
||||
</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 }}
|
||||
Aus Sicherheitsgründen zeigen wir die IBAN nur teilweise. Gehört das
|
||||
Konto nicht zu deiner Zahlung, 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">
|
||||
<!--
|
||||
Kein Ursprungskonto (bar gezahlt): Die Herkunftsfrage entfällt. Sonst
|
||||
landete der Teili bei „Nein" in einer Sackgasse, die ihn nach dem Konto
|
||||
fragt, von dem überwiesen wurde -- es gab keines. Erstattet wird auf ein
|
||||
Konto, das ihm gehört.
|
||||
-->
|
||||
<template v-else-if="!asksForOrigin">
|
||||
<h3>Auf welches Konto sollen wir erstatten?</h3>
|
||||
|
||||
<p class="choice-hint">
|
||||
Deinen Beitrag hast du nicht überwiesen, deshalb brauchen wir eine
|
||||
Bankverbindung von dir. Bitte gib ein Konto an, das auf deinen Namen
|
||||
läuft oder über das du verfügen darfst.
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<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>
|
||||
|
||||
<!--
|
||||
Die Kontoangaben -- gleich in beiden Wegen, in denen sie erfragt werden:
|
||||
nach einem „Ja" auf die Herkunftsfrage und bei einer Zahlungsart ohne
|
||||
Ursprungskonto. Nur beim bereits bekannten Konto entfallen sie.
|
||||
-->
|
||||
<template v-if="showAccountFields">
|
||||
<div class="field">
|
||||
<label for="account-owner">Kontoinhaber*in</label>
|
||||
<input
|
||||
@@ -312,13 +423,17 @@ async function submit() {
|
||||
<IbanInput id="account-iban" v-model="form.accountIban" class="form-input" />
|
||||
<ErrorText :message="errors.accountIban" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!--
|
||||
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.
|
||||
|
||||
In beiden Wegen dieselben: Beim bekannten Konto bestätigt der Teili
|
||||
Angezeigtes statt Getipptes -- der Beleg bleibt dadurch unverändert.
|
||||
-->
|
||||
<template v-if="accountComplete">
|
||||
<template v-if="showDeclarations">
|
||||
<div class="declaration">
|
||||
<input
|
||||
id="refund-declaration"
|
||||
@@ -339,7 +454,7 @@ async function submit() {
|
||||
type="checkbox"
|
||||
/>
|
||||
<TextResource
|
||||
text-name="CONFIRMATION_PARTICIPANT_REFUND_ACCOUNT"
|
||||
:text-name="accountDeclarationText"
|
||||
belongs-to="refund-account-declaration"
|
||||
/>
|
||||
</div>
|
||||
@@ -356,7 +471,6 @@ async function submit() {
|
||||
{{ saving ? 'Wird gespeichert…' : 'Angaben absenden' }}
|
||||
</button>
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
</form>
|
||||
</template>
|
||||
@@ -429,6 +543,31 @@ h3 {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Das bekannte Konto -- Anzeige, kein Feld. Bewusst wie ein Beleg gesetzt und nicht wie ein Formular,
|
||||
damit gar nicht erst der Eindruck entsteht, hier ließe sich etwas ändern. */
|
||||
.known-account {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
padding: 12px 14px;
|
||||
border: 1px solid #d1d5db;
|
||||
border-radius: 8px;
|
||||
background-color: #f9fafb;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.known-account__owner {
|
||||
font-weight: bold;
|
||||
color: #374151;
|
||||
}
|
||||
|
||||
.known-account__iban {
|
||||
font-family: monospace;
|
||||
font-size: 1.05rem;
|
||||
letter-spacing: 0.05em;
|
||||
color: #4b5563;
|
||||
}
|
||||
|
||||
.field {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user