Creating Participation refunds
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\ParticipantRefund\Actions\ReleaseRefund;
|
||||
|
||||
use App\Enumerations\RefundReason;
|
||||
use App\Mail\ParticipantRefundMails\RefundReleasedMail;
|
||||
use App\Models\EventParticipant;
|
||||
use App\Models\ParticipantRefund;
|
||||
use App\Repositories\ParticipantRefundRepository;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* Gibt die Erstattung eines Teilnahmebeitrags frei.
|
||||
*
|
||||
* Der Vorgang entsteht hier nur als Absichtserklärung: Betrag und Grund stehen fest, die Bankverbindung
|
||||
* fehlt noch. Der Teili ergänzt sie über den Link in der Mail. `amount_paid` bleibt unangetastet --
|
||||
* gezahlt hat er bis zur Auszahlung weiterhin, was er gezahlt hat.
|
||||
*/
|
||||
class ReleaseRefundCommand
|
||||
{
|
||||
private EventParticipant $participant;
|
||||
|
||||
private ParticipantRefundRepository $refunds;
|
||||
|
||||
public function __construct(private readonly ReleaseRefundRequest $request)
|
||||
{
|
||||
$this->participant = $request->participant;
|
||||
$this->refunds = new ParticipantRefundRepository();
|
||||
}
|
||||
|
||||
public function execute(): ReleaseRefundResponse
|
||||
{
|
||||
$response = new ReleaseRefundResponse();
|
||||
|
||||
$rejection = $this->reject();
|
||||
if ($rejection !== null) {
|
||||
$response->message = $rejection;
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
$refund = ParticipantRefund::create([
|
||||
'tenant' => $this->participant->tenant,
|
||||
'event_id' => $this->participant->event_id,
|
||||
'event_participant_id' => $this->participant->id,
|
||||
'token' => Str::random(32),
|
||||
'status' => ParticipantRefund::STATUS_PENDING,
|
||||
'amount' => $this->request->amount,
|
||||
'reason' => $this->request->reason,
|
||||
'reason_note' => $this->reasonNote(),
|
||||
'released_by' => auth()->id(),
|
||||
'released_at' => now(),
|
||||
]);
|
||||
|
||||
$this->notify($refund);
|
||||
|
||||
$response->success = true;
|
||||
$response->refund = $refund;
|
||||
$response->message = 'Die Erstattung wurde freigegeben. Der Teili wurde per E-Mail informiert.';
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alle Gründe, aus denen eine Freigabe nicht zulässig ist.
|
||||
*
|
||||
* @return string|null Meldung, oder null wenn nichts dagegen spricht.
|
||||
*/
|
||||
private function reject(): ?string
|
||||
{
|
||||
if ($this->participant->unregistered_at === null) {
|
||||
return 'Eine Erstattung ist nur für abgemeldete Teilis möglich.';
|
||||
}
|
||||
|
||||
if ($this->refunds->openFor($this->participant) !== null) {
|
||||
return 'Für diese Anmeldung läuft bereits eine Erstattung.';
|
||||
}
|
||||
|
||||
$amount = $this->request->amount->getAmount();
|
||||
if ($amount <= 0) {
|
||||
return 'Der Erstattungsbetrag muss größer als 0 sein.';
|
||||
}
|
||||
|
||||
// Mehr zurückgeben als eingegangen ist wäre keine Erstattung mehr. Die halbe Cent-Toleranz
|
||||
// fängt die Rundung des gespeicherten Floats ab.
|
||||
$paid = $this->participant->amount_paid?->getAmount() ?? 0.0;
|
||||
if ($amount > $paid + 0.005) {
|
||||
return 'Der Erstattungsbetrag darf den gezahlten Beitrag nicht übersteigen.';
|
||||
}
|
||||
|
||||
$reason = RefundReason::find($this->request->reason);
|
||||
if ($reason === null) {
|
||||
return 'Bitte wähle einen Erstattungsgrund aus.';
|
||||
}
|
||||
|
||||
if ($reason->requires_note && trim((string) $this->request->reasonNote) === '') {
|
||||
return 'Für diesen Grund ist eine Erläuterung erforderlich.';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Der Freitext gehört nur zu Gründen, die ihn verlangen -- sonst stünde er ungenutzt in der DB. */
|
||||
private function reasonNote(): ?string
|
||||
{
|
||||
$reason = RefundReason::find($this->request->reason);
|
||||
|
||||
if ($reason === null || !$reason->requires_note) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return trim((string) $this->request->reasonNote);
|
||||
}
|
||||
|
||||
/**
|
||||
* Teili und Kontaktperson bekommen je eine eigene Mail -- dasselbe Muster wie bei der Abmeldung
|
||||
* (siehe SetParticipationStateCommand).
|
||||
*/
|
||||
private function notify(ParticipantRefund $refund): void
|
||||
{
|
||||
Mail::to($this->participant->email_1)->send(new RefundReleasedMail(
|
||||
participant: $this->participant,
|
||||
refund: $refund,
|
||||
));
|
||||
|
||||
if ($this->participant->email_2 !== null) {
|
||||
Mail::to($this->participant->email_2)->send(new RefundReleasedMail(
|
||||
participant: $this->participant,
|
||||
refund: $refund,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user