More improvements
This commit is contained in:
@@ -26,47 +26,10 @@ const props = defineProps({
|
||||
Du hast noch kein erweitertes Führungszeugnis hinterlegt. Bitte reiche es umgehend ein.
|
||||
</div>
|
||||
|
||||
<template v-if="props.participant.paymentSummary?.hasPaymentInformation">
|
||||
<!-- Frei konfigurierter Text (z.B. Barzahlung) -->
|
||||
<div v-if="props.participant.paymentSummary.html" v-html="props.participant.paymentSummary.html"
|
||||
style="margin-bottom: 16px;"></div>
|
||||
|
||||
<!-- Strukturierte Zeilen (z.B. Überweisung) -->
|
||||
<template v-else>
|
||||
<table class="form-table" style="margin-bottom: 16px;">
|
||||
<tr v-for="(line, index) in props.participant.paymentSummary.lines" :key="line.label">
|
||||
<td>{{ line.label }}:</td>
|
||||
<td><strong v-if="line.label === 'Betrag'">{{ line.value }}</strong>
|
||||
<template v-else>{{ line.value }}</template>
|
||||
</td>
|
||||
<td v-if="index === 0 && props.participant.paymentSummary.showGiroCode && props.participant.identifier !== ''"
|
||||
:rowspan="props.participant.paymentSummary.lines.length"
|
||||
style="vertical-align: top; padding-left: 20px;">
|
||||
<img :src="'/print-girocode/' + props.participant.identifier" alt="GiroCode"
|
||||
style="max-width: 180px;"/>
|
||||
<span
|
||||
style="width: 180px; text-align: center; display: block; font-size: 0.8rem; color: #6b7280; margin-top: 4px;">Giro-Code</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>
|
||||
<a class="link"
|
||||
:href="`/api/v1/event/participant/${props.participant.identifier}/ical-payment`">Erinnerung
|
||||
in Kalender setzen</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p>
|
||||
Bitte beachte, dass deine Anmeldung erst nach Zahlungseiongang vollständig ist.<br/>
|
||||
Wenn dieser nicht bis zum {{ props.event.registrationFinalEnd.formatted }} erfolgt, kann deine
|
||||
Anmeldung storniert werden.<br/><br/>
|
||||
Solltest du den Beitrag bis zu diesem Datum nicht oder nur teilweise überweisen können, kontaktiere
|
||||
bitte die Aktionsleitung, damit wir eine gemeinsame Lösiung finden können.
|
||||
</p>
|
||||
</template>
|
||||
</template>
|
||||
<!-- Zahlungs-Block kommt vollständig aus dem Zahlungsmodul (registrationSummary -> html). -->
|
||||
<div v-if="props.participant.paymentSummary?.hasPaymentInformation"
|
||||
v-html="props.participant.paymentSummary.html"
|
||||
style="margin-bottom: 16px;"></div>
|
||||
<p v-else>
|
||||
Du musst keinen Beitrag überweisen. Deine Anmeldung ist bestätigt.
|
||||
</p>
|
||||
|
||||
@@ -59,13 +59,23 @@ Beispiel GiroCode (nur Überweisung):
|
||||
## Anmelde-Zusammenfassung / Mail-Anzeige
|
||||
|
||||
- `registrationSummary(RegistrationSummaryRequest): RegistrationSummaryResponse` liefert den zahlungsspezifischen
|
||||
Anzeige-Block render-agnostisch: entweder `lines` (label/value, z.B. Überweisung) oder `html` (Freitext, z.B.
|
||||
Barzahlung), plus `hasPaymentInformation` und `showGiroCode`.
|
||||
Anzeige-Block **fertig als HTML** (`hasPaymentInformation` + `html`). Jedes Modul rendert seinen Block über
|
||||
eigene Blade-Templates unter `resources/views/payment-modules/{modul}/{web,mail}.blade.php` (Überweisung:
|
||||
`account-transfer/`, Barzahlung: `undefined/` — Rahmen um den konfigurierten richtext). Die Render-Stellen geben
|
||||
nur noch `v-html` / `{!! !!}` aus — **keine** zahlart-spezifische Logik im Frontend/Blade.
|
||||
- **Render-Kontext:** `RegistrationSummaryRequest` trägt einen `RegistrationRenderContext` (`Web`/`Mail`). Das Modul
|
||||
wählt darüber (a) ein **medien-gerechtes Template** — `resources/views/payment-modules/{modul}/{web,mail}.blade.php`
|
||||
(Web: SPA-Klassen `form-table`/`link`; Mail: E-Mail-taugliche Inline-Styles) und (b) die GiroCode-Bildquelle —
|
||||
Web: `/print-girocode/{token}` (sessionlos, lazy), Mail: `cid:girocode.png` (inline via `$message->embedData(...)`,
|
||||
bereitgestellt im dünnen Wrapper `emails/subparts/payment.blade.php`).
|
||||
Hinweis: Das Web-Template darf globale (un-scoped) SPA-Klassen nutzen, da Vue-`scoped`-Styles nicht auf `v-html`
|
||||
greifen.
|
||||
- Zugang über das Teilnehmer-Model: `EventParticipant::paymentModule()`, `paymentConfiguration()` (eager-load-fähig über
|
||||
`->with('event.paymentMethods')`, kein statischer Cache), `paymentSummary()`.
|
||||
- Konsumiert von `EventParticipantResource` (`paymentSummary`), `SubmitSuccess.vue`, `emails/subparts/payment.blade.php`
|
||||
(+ `signup_complete`, `missing_amount`). GiroCode in Mails: **inline per CID** (`$message->embedData(...)`), im Web
|
||||
über `/print-girocode/{token}` (sessionlos, lazy).
|
||||
`->with('event.paymentMethods')`, kein statischer Cache), `paymentSummary(RegistrationRenderContext)`.
|
||||
- Konsumiert von `EventParticipantResource` (`paymentSummary` = `{hasPaymentInformation, html}`, Web-Kontext),
|
||||
`SubmitSuccess.vue`, den Mails (`EventSignUpSuccessfullMail`/`ParticipantPaymentMissingPaymentMail` rufen
|
||||
`paymentSummary(Mail)`), sowie `signup_complete`/`missing_amount` (nur umrahmende Prosa, gegated auf
|
||||
`hasPaymentInformation`).
|
||||
|
||||
## Neues Zahlungsmodul hinzufügen
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace App\EventPaymentModules\DTO;
|
||||
|
||||
/**
|
||||
* Render-Kontext der Anmelde-Zusammenfassung. Steuert die kontextabhängige Einbettung des GiroCodes:
|
||||
* im Web über den Endpoint, in der Mail über eine inline-CID.
|
||||
*/
|
||||
enum RegistrationRenderContext
|
||||
{
|
||||
case Web;
|
||||
case Mail;
|
||||
}
|
||||
@@ -8,7 +8,8 @@ use App\Models\EventParticipant;
|
||||
* Eingabe für {@see \App\EventPaymentModules\EventPaymentModule::registrationSummary()}.
|
||||
*
|
||||
* Anker ist der (bereits erstellte) Teilnehmer -- daraus leiten die Module Event, Betrag/Restbetrag
|
||||
* und Verwendungszweck ab. $configuration ist die aufgelöste pro-Event-Config des Zahlungsmoduls.
|
||||
* und Verwendungszweck ab. $configuration ist die aufgelöste pro-Event-Config des Zahlungsmoduls,
|
||||
* $context steuert die kontextabhängige Ausgabe (z.B. GiroCode-Einbettung Web vs. Mail).
|
||||
*/
|
||||
final class RegistrationSummaryRequest
|
||||
{
|
||||
@@ -18,6 +19,7 @@ final class RegistrationSummaryRequest
|
||||
public function __construct(
|
||||
public readonly EventParticipant $participant,
|
||||
public readonly array $configuration = [],
|
||||
public readonly RegistrationRenderContext $context = RegistrationRenderContext::Web,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,22 +5,14 @@ namespace App\EventPaymentModules\DTO;
|
||||
/**
|
||||
* Ausgabe von {@see \App\EventPaymentModules\EventPaymentModule::registrationSummary()}.
|
||||
*
|
||||
* Render-agnostisch: speist den Zahlungs-Abschnitt sowohl im Anmeldeabschluss (Vue) als auch in den Mails.
|
||||
* Zwei Darstellungsformen:
|
||||
* - strukturierte $lines (label/value) -- z.B. Überweisung (Kontoinhaber, IBAN, Verwendungszweck, Betrag),
|
||||
* - $html -- frei konfigurierter Text (z.B. Barzahlung).
|
||||
* Das Modul erzeugt seinen Zahlungs-Block vollständig als HTML; die Render-Stellen (Anmeldeabschluss,
|
||||
* Mails) geben ihn nur noch per `v-html` / `{!! !!}` aus.
|
||||
*/
|
||||
final class RegistrationSummaryResponse
|
||||
{
|
||||
/** Gibt es überhaupt einen Zahlungs-Info-Block darzustellen? */
|
||||
/** Gibt es überhaupt einen Zahlungs-Block darzustellen? */
|
||||
public bool $hasPaymentInformation = false;
|
||||
|
||||
/** @var array<int, array{label: string, value: string}> */
|
||||
public array $lines = [];
|
||||
|
||||
/** Frei konfigurierter HTML-Text (alternativ zu $lines). */
|
||||
public ?string $html = null;
|
||||
|
||||
/** Soll der GiroCode (QR) angezeigt werden? */
|
||||
public bool $showGiroCode = false;
|
||||
/** Fertig gerenderter HTML-Block der Zahlungsinformationen. */
|
||||
public string $html = '';
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\EventPaymentModules\Modules;
|
||||
|
||||
use App\EventPaymentModules\AbstractEventPaymentModule;
|
||||
use App\EventPaymentModules\DTO\CreateInvoiceRequest;
|
||||
use App\EventPaymentModules\DTO\RegistrationRenderContext;
|
||||
use App\EventPaymentModules\DTO\RegistrationSummaryRequest;
|
||||
use App\EventPaymentModules\DTO\RegistrationSummaryResponse;
|
||||
use App\EventPaymentModules\ProvidesGiroCode;
|
||||
@@ -45,15 +46,32 @@ class AccountTransferPaymentModule extends AbstractEventPaymentModule implements
|
||||
$amountLeft->subtractAmount($participant->amount_paid);
|
||||
}
|
||||
|
||||
$hasPaymentInformation = $amountLeft->getAmount() > 0;
|
||||
|
||||
// GiroCode nur bei offenem Betrag; Bildquelle je Render-Kontext (Web-Endpoint vs. Mail-CID).
|
||||
$giroCodeSrc = null;
|
||||
if ($hasPaymentInformation) {
|
||||
$giroCodeSrc = $request->context === RegistrationRenderContext::Mail
|
||||
? 'cid:girocode.png'
|
||||
: '/print-girocode/' . $participant->identifier;
|
||||
}
|
||||
|
||||
// Medien-gerechtes Template je Render-Kontext (Web: SPA-Klassen, Mail: E-Mail-tauglich).
|
||||
$view = $request->context === RegistrationRenderContext::Mail
|
||||
? 'payment-modules.account-transfer.mail'
|
||||
: 'payment-modules.account-transfer.web';
|
||||
|
||||
$response = new RegistrationSummaryResponse();
|
||||
$response->hasPaymentInformation = $amountLeft->getAmount() > 0;
|
||||
$response->showGiroCode = $response->hasPaymentInformation;
|
||||
$response->lines = [
|
||||
['label' => 'Kontoinhaber', 'value' => (string)($config['account_owner'] ?? '')],
|
||||
['label' => 'IBAN', 'value' => (string)($config['iban'] ?? '')],
|
||||
['label' => 'Verwendungszweck', 'value' => (string)$participant->payment_purpose],
|
||||
['label' => 'Betrag', 'value' => $amountLeft->toString()],
|
||||
];
|
||||
$response->hasPaymentInformation = $hasPaymentInformation;
|
||||
$response->html = view($view, [
|
||||
'accountOwner' => (string)($config['account_owner'] ?? ''),
|
||||
'iban' => (string)($config['iban'] ?? ''),
|
||||
'paymentPurpose' => (string)$participant->payment_purpose,
|
||||
'amount' => $amountLeft->toString(),
|
||||
'giroCodeSrc' => $giroCodeSrc,
|
||||
'reminderUrl' => url('/api/v1/event/participant/' . $participant->identifier . '/ical-payment'),
|
||||
'paymentDeadline' => $participant->event?->registration_final_end?->format('d.m.Y'),
|
||||
])->render();
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\EventPaymentModules\Modules;
|
||||
|
||||
use App\EventPaymentModules\AbstractEventPaymentModule;
|
||||
use App\EventPaymentModules\DTO\CreateInvoiceRequest;
|
||||
use App\EventPaymentModules\DTO\RegistrationRenderContext;
|
||||
use App\EventPaymentModules\DTO\RegistrationSummaryRequest;
|
||||
use App\EventPaymentModules\DTO\RegistrationSummaryResponse;
|
||||
use App\Models\PaymentMethod;
|
||||
@@ -33,12 +34,19 @@ class UndefinedPaymentModule extends AbstractEventPaymentModule
|
||||
|
||||
public function registrationSummary(RegistrationSummaryRequest $request): RegistrationSummaryResponse
|
||||
{
|
||||
$html = (string)($request->configuration['payment_information'] ?? '');
|
||||
$paymentInformation = (string)($request->configuration['payment_information'] ?? '');
|
||||
|
||||
$response = new RegistrationSummaryResponse();
|
||||
$response->hasPaymentInformation = trim($html) !== '';
|
||||
$response->html = $html;
|
||||
$response->showGiroCode = false;
|
||||
$response->hasPaymentInformation = trim($paymentInformation) !== '';
|
||||
|
||||
if ($response->hasPaymentInformation) {
|
||||
// Medien-gerechtes Template je Render-Kontext (Web: SPA, Mail: E-Mail-tauglich).
|
||||
$view = $request->context === RegistrationRenderContext::Mail
|
||||
? 'payment-modules.undefined.mail'
|
||||
: 'payment-modules.undefined.web';
|
||||
|
||||
$response->html = view($view, ['paymentInformation' => $paymentInformation])->render();
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ use App\Domains\Event\Actions\GenerateIcal\GenerateIcalResponse;
|
||||
use App\Domains\Event\Actions\GenerateIcalForDeadline\GenerateIcalForDeadlineCommand;
|
||||
use App\Domains\Event\Actions\GenerateIcalForDeadline\GenerateIcalForDeadlineRequest;
|
||||
use App\Domains\Event\Actions\GenerateIcalForDeadline\GenerateIcalForDeadlineResponse;
|
||||
use App\EventPaymentModules\DTO\RegistrationRenderContext;
|
||||
use App\EventPaymentModules\ProvidesGiroCode;
|
||||
use App\Models\EventParticipant;
|
||||
use Illuminate\Bus\Queueable;
|
||||
@@ -68,6 +69,9 @@ class EventSignUpSuccessfullMail extends Mailable
|
||||
? $module->giroCode($this->participant, $this->participant->paymentConfiguration())
|
||||
: null;
|
||||
|
||||
// Zahlungs-Block im Mail-Kontext erzeugen (GiroCode als cid: statt Web-Endpoint).
|
||||
$paymentSummary = $this->participant->paymentSummary(RegistrationRenderContext::Mail);
|
||||
|
||||
$participationIcal = $this->getParticipationIcal();
|
||||
$deadlineIcal = $this->getDeadlineIcal();
|
||||
|
||||
@@ -83,7 +87,7 @@ class EventSignUpSuccessfullMail extends Mailable
|
||||
'departure' => $participant['departure'],
|
||||
'amount' => $participant['amount_left_string'],
|
||||
'paymentFinalDate' => $event['registrationFinalEnd']['formatted'],
|
||||
'paymentSummary' => $participant['paymentSummary'],
|
||||
'paymentSummary' => ['hasPaymentInformation' => $paymentSummary->hasPaymentInformation, 'html' => $paymentSummary->html],
|
||||
'girocodeBinary' => $girocodeBinary,
|
||||
'efzStatus' => $participant['efz_status'],
|
||||
'participationIcalFilename' => $participationIcal->filename,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Mail\ParticipantPaymentMails;
|
||||
|
||||
use App\EventPaymentModules\DTO\RegistrationRenderContext;
|
||||
use App\EventPaymentModules\ProvidesGiroCode;
|
||||
use App\Models\EventParticipant;
|
||||
use Illuminate\Http\Request;
|
||||
@@ -51,6 +52,9 @@ class ParticipantPaymentMissingPaymentMail extends Mailable
|
||||
? $module->giroCode($this->participant, $this->participant->paymentConfiguration())
|
||||
: null;
|
||||
|
||||
// Zahlungs-Block im Mail-Kontext erzeugen (GiroCode als cid: statt Web-Endpoint).
|
||||
$paymentSummary = $this->participant->paymentSummary(RegistrationRenderContext::Mail);
|
||||
|
||||
return new Content(
|
||||
view: 'emails.participantPayments.missing_amount',
|
||||
with: [
|
||||
@@ -62,7 +66,7 @@ class ParticipantPaymentMissingPaymentMail extends Mailable
|
||||
'departure' => $participant['departure'],
|
||||
'amount' => $participant['amount_left_string'],
|
||||
'paymentFinalDate' => $event['registrationFinalEnd']['formatted'],
|
||||
'paymentSummary' => $participant['paymentSummary'],
|
||||
'paymentSummary' => ['hasPaymentInformation' => $paymentSummary->hasPaymentInformation, 'html' => $paymentSummary->html],
|
||||
'girocodeBinary' => $girocodeBinary,
|
||||
],
|
||||
);
|
||||
|
||||
@@ -8,6 +8,7 @@ use App\Enumerations\EfzStatus;
|
||||
use App\Enumerations\FirstAidPermission;
|
||||
use App\Enumerations\ParticipationType;
|
||||
use App\Enumerations\SwimmingPermission;
|
||||
use App\EventPaymentModules\DTO\RegistrationRenderContext;
|
||||
use App\EventPaymentModules\DTO\RegistrationSummaryRequest;
|
||||
use App\EventPaymentModules\DTO\RegistrationSummaryResponse;
|
||||
use App\EventPaymentModules\EventPaymentModule;
|
||||
@@ -202,14 +203,14 @@ class EventParticipant extends InstancedModel
|
||||
return $method?->pivot->configuration ?? [];
|
||||
}
|
||||
|
||||
/** Zahlungs-Zusammenfassung (Anmeldeabschluss/Mail), vom Zahlungsmodul erzeugt. */
|
||||
public function paymentSummary(): RegistrationSummaryResponse
|
||||
/** Zahlungs-Zusammenfassung (Anmeldeabschluss/Mail), vom Zahlungsmodul als fertiges HTML erzeugt. */
|
||||
public function paymentSummary(RegistrationRenderContext $context = RegistrationRenderContext::Web): RegistrationSummaryResponse
|
||||
{
|
||||
$module = $this->paymentModule();
|
||||
if ($module === null) {
|
||||
return new RegistrationSummaryResponse();
|
||||
}
|
||||
|
||||
return $module->registrationSummary(new RegistrationSummaryRequest($this, $this->paymentConfiguration()));
|
||||
return $module->registrationSummary(new RegistrationSummaryRequest($this, $this->paymentConfiguration(), $context));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,9 +61,7 @@ class EventParticipantResource extends JsonResource
|
||||
&& $this->resource->amount_paid?->getAmount() < $this->resource->amount->getAmount(),
|
||||
'paymentSummary' => [
|
||||
'hasPaymentInformation' => $paymentSummary->hasPaymentInformation,
|
||||
'lines' => $paymentSummary->lines,
|
||||
'html' => $paymentSummary->html,
|
||||
'showGiroCode' => $paymentSummary->showGiroCode,
|
||||
],
|
||||
'nicename' => $this->resource->getNicename(),
|
||||
'arrival' => $this->resource->arrival_date->format('d.m.Y'),
|
||||
|
||||
Reference in New Issue
Block a user