Participant mangement

This commit is contained in:
2026-04-11 22:17:38 +02:00
parent e6bd8c684d
commit ed7f887e3a
47 changed files with 1641 additions and 269 deletions

View File

@@ -3,7 +3,10 @@
namespace App\Domains\Event\Actions\ManualCertificateOfConductionCheck;
use App\Enumerations\EfzStatus;
use App\Mail\ParticipantCocMails\ParticipantCocCompleteMail;
use App\Mail\ParticipantCocMails\ParticipantCocInvalidMail;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Mail;
class ManualCertificateOfConductionCheckCommand {
function __construct(public ManualCertificateOfConductionCheckRequest $request)
@@ -16,6 +19,11 @@ class ManualCertificateOfConductionCheckCommand {
$this->request->participant->efz_status = EfzStatus::EFZ_STATUS_CHECKED_VALID;
$this->request->participant->save();
$response->success = true;
Mail::to($this->request->participant->email_1)->send(new ParticipantCocCompleteMail(
participant: $this->request->participant,
));
return $response;
}
}

View File

@@ -2,6 +2,12 @@
namespace App\Domains\Event\Actions\ParticipantPayment;
use App\Mail\ParticipantPaymentMails\ParticipantPaymentMissingPaymentMail;
use App\Mail\ParticipantPaymentMails\ParticipantPaymentOverpaidMail;
use App\Mail\ParticipantPaymentMails\ParticipantPaymentPaidMail;
use App\Providers\MissingPaymentProvider;
use Illuminate\Support\Facades\Mail;
class ParticipantPaymentCommand {
public function __construct(public ParticipantPaymentRequest $request) {
}
@@ -12,6 +18,50 @@ class ParticipantPaymentCommand {
$this->request->participant->amount_paid = $this->request->amountPaid;
$this->request->participant->save();
$response->amountPaid = $this->request->participant->amount_paid;
$response->amountExpected = $this->request->participant->amount;
$amountToPay = MissingPaymentProvider::calculateMissingPayment(
amountPaid: $this->request->participant->amount_paid,
amountToPay: $this->request->participant->amount,
);
switch (true) {
case $amountToPay->getAmount() > 0:
Mail::to($this->request->participant->email_1)->send(new ParticipantPaymentMissingPaymentMail(
participant: $this->request->participant,
));
if ($this->request->participant->email_2 !== null) {
Mail::to($this->request->participant->email_2)->send(new ParticipantPaymentMissingPaymentMail(
participant: $this->request->participant,
));
}
break;
case $amountToPay->getAmount() < 0:
Mail::to($this->request->participant->email_1)->send(new ParticipantPaymentOverpaidMail(
participant: $this->request->participant,
));
if ($this->request->participant->email_2 !== null) {
Mail::to($this->request->participant->email_2)->send(new ParticipantPaymentOverpaidMail(
participant: $this->request->participant,
));
}
break;
default:
Mail::to($this->request->participant->email_1)->send(new ParticipantPaymentPaidMail(
participant: $this->request->participant,
));
if ($this->request->participant->email_2 !== null) {
Mail::to($this->request->participant->email_2)->send(new ParticipantPaymentPaidMail(
participant: $this->request->participant,
));
}
}
$response->participant = $this->request->participant;
$response->success = true;
return $response;

View File

@@ -2,6 +2,19 @@
namespace App\Domains\Event\Actions\ParticipantPayment;
use App\Models\EventParticipant;
use App\ValueObjects\Amount;
class ParticipantPaymentResponse {
public bool $success = false;
public bool $success;
public ?Amount $amountPaid;
public ?Amount $amountExpected;
public ?EventParticipant $participant;
public function __construct() {
$this->amountPaid = null;
$this->amountExpected = null;
$this->success = false;
$this->participant = null;
}
}

View File

@@ -0,0 +1,53 @@
<?php
namespace App\Domains\Event\Actions\SetParticipationState;
use App\Mail\ParticipantParticipationMails\EventSignUpSuccessfullMail;
use App\Mail\ParticipantParticipationMails\ParticipantSignOffMail;
use Illuminate\Support\Facades\Mail;
class SetParticipationStateCommand {
function __construct(private SetParticipationStateSignoffRequest|SetParticipationStateReSignonRequest $request) {}
public function execute() : SetParticipationStateResponse {
$response = new SetParticipationStateResponse();
switch (true) {
case $this->request instanceof SetParticipationStateSignoffRequest:
$this->request->participant->unregistered_at = $this->request->date;
$this->request->participant->save();
$response->success = true;
Mail::to($this->request->participant->email_1)->send(new ParticipantSignOffMail(
participant: $this->request->participant,
));
if ($this->request->participant->email_2 !== null) {
Mail::to($this->request->participant->email_2)->send(new ParticipantSignOffMail(
participant: $this->request->participant,
));
}
break;
case $this->request instanceof SetParticipationStateReSignonRequest:
$this->request->participant->unregistered_at = null;
$this->request->participant->save();
Mail::to($this->request->participant->email_1)->send(new EventSignUpSuccessfullMail(
participant: $this->request->participant,
));
if ($this->request->participant->email_2 !== null) {
Mail::to($this->request->participant->email_2)->send(new EventSignUpSuccessfullMail(
participant: $this->request->participant,
));
}
break;
}
return $response;
}
}

View File

@@ -0,0 +1,9 @@
<?php
namespace App\Domains\Event\Actions\SetParticipationState;
use App\Models\EventParticipant;
class SetParticipationStateReSignonRequest {
function __construct(public EventParticipant $participant) {}
}

View File

@@ -0,0 +1,7 @@
<?php
namespace App\Domains\Event\Actions\SetParticipationState;
class SetParticipationStateResponse {
function __construct(public bool $success = false) {}
}

View File

@@ -0,0 +1,10 @@
<?php
namespace App\Domains\Event\Actions\SetParticipationState;
use App\Models\EventParticipant;
class SetParticipationStateSignoffRequest {
function __construct(public EventParticipant $participant, public \DateTime $date) {
}
}

View File

@@ -0,0 +1,142 @@
<?php
namespace App\Domains\Event\Actions\UpdateParticipant;
use App\Enumerations\EfzStatus;
use App\Mail\ParticipantCocMails\ParticipantCocCompleteMail;
use App\Mail\ParticipantCocMails\ParticipantCocInvalidMail;
use App\Mail\ParticipantPaymentMails\ParticipantPaymentMissingPaymentMail;
use App\Mail\ParticipantPaymentMails\ParticipantPaymentOverpaidMail;
use App\Mail\ParticipantPaymentMails\ParticipantPaymentPaidMail;
use App\Models\EventParticipant;
use App\Providers\MissingPaymentProvider;
use App\ValueObjects\Amount;
use DateTime;
use Illuminate\Support\Facades\Mail;
class UpdateParticipantCommand {
private UpdateParticipantResponse $response;
function __construct(public UpdateParticipantRequest $request) {
}
public function execute() {
$this->response = new UpdateParticipantResponse();
$p = clone($this->request->participant);
$p->firstname = $this->request->firstname;
$p->lastname = $this->request->lastname;
$p->nickname = $this->request->nickname;
$p->address_1 = $this->request->address_1;
$p->address_2 = $this->request->address_2;
$p->postcode = $this->request->postcode;
$p->city = $this->request->city;
$p->local_group = $this->request->localgroup;
$p->birthday = DateTime::createFromFormat('Y-m-d', $this->request->birthday);
$p->email_1 = $this->request->email_1;
$p->phone_1 = $this->request->phone_1;
$p->contact_person = $this->request->contact_person;
$p->email_2 = $this->request->email_2;
$p->phone_2 = $this->request->phone_2;
$p->arrival_date = DateTime::createFromFormat('Y-m-d', $this->request->arrival);
$p->departure_date = DateTime::createFromFormat('Y-m-d', $this->request->departure);
$p->participation_type = $this->request->participationType;
$p->eating_habit = $this->request->eatingHabit;
$p->allergies = $this->request->allergies;
$p->intolerances = $this->request->intolerances;
$p->medications = $this->request->medications;
$p->first_aid_permission = $this->request->extendedFirstAid;
$p->swimming_permission = $this->request->swimmingPermission;
$p->tetanus_vaccination = $this->request->tetanusVaccination !== null
? DateTime::createFromFormat('Y-m-d', $this->request->tetanusVaccination)
: null;
$p->notes = $this->request->notes;
$p->amount_paid = Amount::fromString($this->request->amountPaid);
$p->amount = Amount::fromString($this->request->amountExpected);
$p->efz_status = $this->request->cocStatus;
if (
MissingPaymentProvider::calculateMissingPayment(amountPaid: $p->amount_paid, amountToPay: $p->amount)->getAmount()
!==
MissingPaymentProvider::calculateMissingPayment(amountPaid: $this->request->participant->amount_paid, amountToPay: $this->request->participant->amount)->getAmount()
) {
$this->handleAmountChanges($p);
}
if (
$p->efz_status !== $this->request->participant->efz_status
) {
$this->handleCocStatusChange($p);
}
$p->save();
$this->response->success = true;
$this->response->participant = $p;
return $this->response;
}
private function handleAmountChanges(EventParticipant $participant) {
$this->response->amountPaid = $participant->amount_paid;
$this->response->amountExpected = $participant->amount;
$amountToPay = MissingPaymentProvider::calculateMissingPayment(
amountPaid: $participant->amount_paid,
amountToPay: $participant->amount,
);
switch (true) {
case $amountToPay->getAmount() > 0:
Mail::to($participant->email_1)->send(new ParticipantPaymentMissingPaymentMail(
participant: $participant,
));
if ($participant->email_2 !== null) {
Mail::to($participant->email_2)->send(new ParticipantPaymentMissingPaymentMail(
participant: $participant,
));
}
break;
case $amountToPay->getAmount() < 0:
Mail::to($participant->email_1)->send(new ParticipantPaymentOverpaidMail(
participant: $participant,
));
if ($participant->email_2 !== null) {
Mail::to($participant->email_2)->send(new ParticipantPaymentOverpaidMail(
participant: $participant,
));
}
break;
default:
Mail::to($participant->email_1)->send(new ParticipantPaymentPaidMail(
participant: $participant,
));
if ($participant->email_2 !== null) {
Mail::to($participant->email_2)->send(new ParticipantPaymentPaidMail(
participant: $participant,
));
}
}
}
private function handleCocStatusChange(EventParticipant $participant) {
$this->response->cocStatus = $participant->efzStatus()->first();
switch ($participant->efzStatus()->first()->slug) {
case EfzStatus::EFZ_STATUS_CHECKED_VALID:
case EfzStatus::EFZ_STATUS_NOT_REQUIRED:
Mail::to($participant->email_1)->send(new ParticipantCocCompleteMail(
participant: $participant,
));
break;
case EfzStatus::EFZ_STATUS_CHECKED_INVALID:
Mail::to($participant->email_1)->send(new ParticipantCocInvalidMail(
participant: $participant,
));
break;
}
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace App\Domains\Event\Actions\UpdateParticipant;
use App\Models\EventParticipant;
class UpdateParticipantRequest {
function __construct(
public EventParticipant $participant,
public string $firstname,
public string $lastname,
public ?string $nickname,
public string $address_1,
public ?string $address_2,
public string $postcode,
public string $city,
public string $localgroup,
public string $birthday,
public string $email_1,
public string $phone_1,
public string $contact_person,
public ?string $email_2,
public ?string $phone_2,
public string $arrival,
public string $departure,
public string $participationType,
public string $eatingHabit,
public ?string $allergies,
public ?string $intolerances,
public ?string $medications,
public string $extendedFirstAid,
public string $swimmingPermission,
public ?string $tetanusVaccination,
public ?string $notes,
public string $amountPaid,
public string $amountExpected,
public string $cocStatus,
) {}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace App\Domains\Event\Actions\UpdateParticipant;
use App\Enumerations\EfzStatus;
use App\Models\EventParticipant;
use App\ValueObjects\Amount;
class UpdateParticipantResponse {
public bool $success;
public ?EfzStatus $cocStatus;
public ?Amount $amountPaid;
public ?Amount $amountExpected;
public ?EventParticipant $participant;
public function __construct() {
$this->success = false;
$this->cocStatus =null;
$this->amountPaid = null;
$this->amountExpected = null;
$this->participant = null;
}
}

View File

@@ -182,6 +182,7 @@ class DetailsController extends CommonController {
$participants = $this->eventParticipants->groupByParticipationType($event, $request);
break;
case 'signed-off':
$participants = $this->eventParticipants->getSignedOffParticipants($event, $request);
break;
default:
$participants = ['Alle Teilnehmenden' => $this->eventParticipants->getForList($event, $request)];

View File

@@ -11,22 +11,8 @@ use App\Scopes\CommonController;
use Illuminate\Support\Facades\Request;
class ParticipantController extends CommonController {
public function paymentComplete(string $participantIdentifier, Request $request) {
$participant = $this->eventParticipants->getByIdentifier($participantIdentifier, $this->events, false);
$paymentRequest = new ParticipantPaymentRequest($participant, $participant->amount);
$paymentCommand = new ParticipantPaymentCommand($paymentRequest);
$paymentResponse = $paymentCommand->execute();
return response()->json([
'status' => $paymentResponse->success ? 'success' : 'error',
'message' => $paymentResponse->success ? 'Die Zahlung wurde erfolgreich gebucht.' : 'Beim Buchen der Zahlung ist ein Fehler aufgetreten.'
]);
}
public function markCocExisting(string $participantIdentifier, Request $request) {
$participant = $this->eventParticipants->getByIdentifier($participantIdentifier, $this->events, false);
$participant = $this->eventParticipants->getByIdentifier($participantIdentifier, $this->events);
$cocRequest = new ManualCertificateOfConductionCheckRequest($participant);
$cocCommand = new ManualCertificateOfConductionCheckCommand($cocRequest);
@@ -37,4 +23,12 @@ class ParticipantController extends CommonController {
'message' => $cocResponse->success ? 'Das eFZ wurde als gültig hinterlegt' : 'Beim Aktualisieren des eFZ-Status ist ein Fehler aufgetreten.'
]);
}
public function __invoke(string $participantIdentifier, Request $request) {
$participant = $this->eventParticipants->getByIdentifier($participantIdentifier, $this->events)->toResource()->toArray($request);
return response()->json([
'participant' => $participant,
]);
}
}

View File

@@ -0,0 +1,53 @@
<?php
namespace App\Domains\Event\Controllers;
use App\Domains\Event\Actions\ParticipantPayment\ParticipantPaymentCommand;
use App\Domains\Event\Actions\ParticipantPayment\ParticipantPaymentRequest;
use App\Scopes\CommonController;
use App\ValueObjects\Amount;
use Illuminate\Http\Request;
class ParticipantPaymentController extends CommonController
{
public function paymentComplete(string $participantIdentifier, Request $request) {
$participant = $this->eventParticipants->getByIdentifier($participantIdentifier, $this->events);
$paymentRequest = new ParticipantPaymentRequest($participant, $participant->amount);
$paymentCommand = new ParticipantPaymentCommand($paymentRequest);
$paymentResponse = $paymentCommand->execute();
return response()->json([
'status' => $paymentResponse->success ? 'success' : 'error',
'message' => $paymentResponse->success ? 'Die Zahlung wurde erfolgreich gebucht.' : 'Beim Buchen der Zahlung ist ein Fehler aufgetreten.'
]);
}
public function partialPaymentComplete(string $participantIdentifier, Request $request) {
$participant = $this->eventParticipants->getByIdentifier($participantIdentifier, $this->events);
$paymentRequest = new ParticipantPaymentRequest($participant, Amount::fromString($request->input('amount')));
$paymentCommand = new ParticipantPaymentCommand($paymentRequest);
$paymentResponse = $paymentCommand->execute();
$amountLeft = clone($paymentResponse->amountExpected);
$amountLeft->subtractAmount($paymentResponse->amountPaid);
return response()->json([
'status' => $paymentResponse->success ? 'success' : 'error',
'message' => $paymentResponse->success ? 'Die Zahlung wurde erfolgreich gebucht.' : 'Beim Buchen der Zahlung ist ein Fehler aufgetreten.',
'identifier' => $participant->identifier,
'amount' => [
'paid' => $paymentResponse->amountPaid->toString(),
'expected' => $paymentResponse->amountExpected->toString(),
'actions' => $amountLeft->getAmount() != 0 ? 'inline' : 'none',
'class' => $amountLeft->getAmount() != 0 ? 'not-paid' : 'paid',
]
]);
dd($participant);
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace App\Domains\Event\Controllers;
use App\Domains\Event\Actions\SetParticipationState\SetParticipationStateCommand;
use App\Domains\Event\Actions\SetParticipationState\SetParticipationStateReSignonRequest;
use App\Domains\Event\Actions\SetParticipationState\SetParticipationStateSignoffRequest;
use App\Scopes\CommonController;
use DateTime;
use Illuminate\Http\Request;
class ParticipantReSignOnController extends CommonController
{
public function __invoke(string $participantIdentifier, Request $request) {
$participant = $this->eventParticipants->getByIdentifier($participantIdentifier, $this->events);
$request = new SetParticipationStateReSignonRequest($participant);
$command = new SetParticipationStateCommand($request);
$command->execute();
return response()->json([
'status' => 'success',
'identifier' => $participant->identifier,
'message' => 'Die Wiederanmeldung wurde erfolgreich durchgeführt.'
]);
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace App\Domains\Event\Controllers;
use App\Domains\Event\Actions\SetParticipationState\SetParticipationStateCommand;
use App\Domains\Event\Actions\SetParticipationState\SetParticipationStateSignoffRequest;
use App\Scopes\CommonController;
use DateTime;
use Illuminate\Http\Request;
class ParticipantSignOffController extends CommonController
{
public function __invoke(string $participantIdentifier, Request $request) {
$participant = $this->eventParticipants->getByIdentifier($participantIdentifier, $this->events);
$signOffDate = DateTime::createFromFormat('Y-m-d', $request->input('cancel_date'));
$signOffRequest = new SetParticipationStateSignoffRequest($participant, $signOffDate);
$signOffCommand = new SetParticipationStateCommand($signOffRequest);
$signOffCommand->execute();
return response()->json([
'status' => 'success',
'identifier' => $participant->identifier,
'message' => 'Die Abmeldung wurde erfolgreich durchgeführt.'
]);
}
}

View File

@@ -0,0 +1,85 @@
<?php
namespace App\Domains\Event\Controllers;
use App\Domains\Event\Actions\UpdateParticipant\UpdateParticipantCommand;
use App\Domains\Event\Actions\UpdateParticipant\UpdateParticipantRequest;
use App\Enumerations\EfzStatus;
use App\Scopes\CommonController;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class ParticipantUpdateController extends CommonController {
public function __invoke(string $participantIdentifier, Request $request): JsonResponse {
$participant = $this->eventParticipants->getByIdentifier($participantIdentifier, $this->events);
$updateRequest = new UpdateParticipantRequest(
participant: $participant,
firstname: $request->input('firstname'),
lastname: $request->input('lastname'),
nickname: $request->input('nickname'),
address_1: $request->input('address_1'),
address_2: $request->input('address_2'),
postcode: $request->input('postcode'),
city: $request->input('city'),
localgroup: $request->input('localgroup'),
birthday: $request->input('birthday'),
email_1: $request->input('email_1'),
phone_1: $request->input('phone_1'),
contact_person: $request->input('contact_person'),
email_2: $request->input('email_2'),
phone_2: $request->input('phone_2'),
arrival: $request->input('arrival'),
departure: $request->input('departure'),
participationType: $request->input('participationType'),
eatingHabit: $request->input('eatingHabit'),
allergies: $request->input('allergies'),
intolerances: $request->input('intolerances'),
medications: $request->input('medications'),
extendedFirstAid: $request->input('extendedFirstAid'),
swimmingPermission: $request->input('swimmingPermission'),
tetanusVaccination: $request->input('tetanusVaccination'),
notes: $request->input('notes'),
amountPaid: $request->input('amountPaid'),
amountExpected: $request->input('amountExpected'),
cocStatus: $request->input('cocStatus'),
);
$command = new UpdateParticipantCommand($updateRequest);
$response = $command->execute();
$data = [
'status' => $response->success ? 'success' : 'error',
'identifier' => $participant->identifier,
'participant' => $response->participant->toResource()->toArray($request),
];
if ($response->cocStatus !== null) {
$data['cocChanged'] = true;
$data['coc']['action'] = in_array($response->cocStatus->slug, [
EfzStatus::EFZ_STATUS_CHECKED_INVALID,
EfzStatus::EFZ_STATUS_NOT_CHECKED]) ? 'inline' : 'none';
$data['coc']['statusText'] = $response->cocStatus->name;
$data['coc']['class'] = match($response->cocStatus->slug) {
EfzStatus::EFZ_STATUS_CHECKED_INVALID => 'efz-invalid',
EfzStatus::EFZ_STATUS_NOT_CHECKED => 'efz-not-checked',
default => 'efz-valid',
};
}
if ($response->amountPaid !== null) {
$amountLeft = clone($response->amountExpected);
$amountLeft->subtractAmount($response->amountPaid);
$data['amountChanged'] = true;
$data['amount'] = [
'paid' => $response->amountPaid->toString(),
'expected' => $response->amountExpected->toString(),
'actions' => $amountLeft->getAmount() != 0 ? 'inline' : 'none',
'class' => $amountLeft->getAmount() != 0 ? 'not-paid' : 'paid',
];
}
return response()->json($data);
}
}

View File

@@ -6,7 +6,7 @@ use App\Domains\Event\Actions\CertificateOfConductionCheck\CertificateOfConducti
use App\Domains\Event\Actions\CertificateOfConductionCheck\CertificateOfConductionCheckRequest;
use App\Domains\Event\Actions\SignUp\SignUpCommand;
use App\Domains\Event\Actions\SignUp\SignUpRequest;
use App\Mail\EventSignUpSuccessfull;
use App\Mail\ParticipantParticipationMails\EventSignUpSuccessfullMail;
use App\Models\Tenant;
use App\Providers\DoubleCheckEventRegistrationProvider;
use App\Providers\InertiaProvider;
@@ -143,12 +143,12 @@ class SignupController extends CommonController {
$signupResponse->participant->efz_status = $certificateOfConductionCheckResponse->status;
$signupResponse->participant->save();
Mail::to($signupResponse->participant->email_1)->send(new EventSignUpSuccessfull(
Mail::to($signupResponse->participant->email_1)->send(new EventSignUpSuccessfullMail(
participant: $signupResponse->participant,
));
if ($signupResponse->participant->email_2 !== null) {
Mail::to($signupResponse->participant->email_2)->send(new EventSignUpSuccessfull(
Mail::to($signupResponse->participant->email_2)->send(new EventSignUpSuccessfullMail(
participant: $signupResponse->participant,
));
}

View File

@@ -3,6 +3,10 @@
use App\Domains\Event\Controllers\CreateController;
use App\Domains\Event\Controllers\DetailsController;
use App\Domains\Event\Controllers\ParticipantController;
use App\Domains\Event\Controllers\ParticipantPaymentController;
use App\Domains\Event\Controllers\ParticipantReSignOnController;
use App\Domains\Event\Controllers\ParticipantSignOffController;
use App\Domains\Event\Controllers\ParticipantUpdateController;
use App\Domains\Event\Controllers\SignupController;
use App\Middleware\IdentifyTenant;
use Illuminate\Support\Facades\Route;
@@ -30,8 +34,14 @@ Route::prefix('api/v1')
Route::prefix('/participant/{participantIdentifier}')->group(function () {
Route::post('/payment-complete', [ParticipantController::class, 'paymentComplete']);
Route::get('/', ParticipantController::class);
Route::post('/payment-complete', [ParticipantPaymentController::class, 'paymentComplete']);
Route::post('/partial-payment', [ParticipantPaymentController::class, 'partialPaymentComplete']);
Route::post('/mark-coc-existing', [ParticipantController::class, 'markCocExisting']);
Route::post('/signoff', ParticipantSignOffController::class);
Route::post('/re-signon', ParticipantReSignOnController::class);
Route::post('/update', ParticipantUpdateController::class);
});
});

View File

@@ -1,14 +1,25 @@
<script setup>
import { reactive, watch } from "vue";
import {onMounted, reactive, watch} from "vue";
import AmountInput from "../../../../Views/Components/AmountInput.vue";
const props = defineProps({
const staticProps = defineProps({
editMode: Boolean,
participant: Object,
event: Object,
});
console.log(props.participant)
const props = reactive({
participant: staticProps.participant,
});
onMounted(async () => {
const response = await fetch('/api/v1/event/participant/' + staticProps.participant.identifier + '/');
const data = await response.json();
Object.assign(props, data);
console.log(props);
});
const emit = defineEmits([
'closeParticipantDetails',
@@ -67,8 +78,8 @@ watch(
form.contact_person = participant?.contact_person ?? '';
form.email_2 = participant?.email_2 ?? '';
form.phone_2 = participant?.phone_2 ?? '';
form.arrival = participant?.arrival ?? '';
form.departure = participant?.departure ?? '';
form.arrival = participant?.arrivalDate ?? '';
form.departure = participant?.departureDate ?? '';
form.participationType = participant?.participation_type ?? '';
form.eatingHabit = participant?.eating_habit ?? '';
form.allergies = participant?.allergies ?? '';
@@ -110,6 +121,7 @@ function enableEditMode() {
function saveParticipant() {
emit('saveParticipant', { ...form });
close();
}
</script>
@@ -124,7 +136,7 @@ function saveParticipant() {
<tr>
<th>Name</th>
<td>
<span v-if="!props.editMode">
<span v-if="!staticProps.editMode">
{{ props.participant.firstname }} {{ props.participant.lastname }}
</span>
<span v-else>
@@ -137,7 +149,7 @@ function saveParticipant() {
<tr>
<th>Pfadiname</th>
<td>
<span v-if="!props.editMode">{{ props.participant.nickname }}</span>
<span v-if="!staticProps.editMode">{{ props.participant.nickname }}</span>
<input v-else v-model="form.nickname" type="text" />
</td>
</tr>
@@ -145,7 +157,7 @@ function saveParticipant() {
<tr>
<th>Anschrift</th>
<td>
<span v-if="!props.editMode">
<span v-if="!staticProps.editMode">
{{ props.participant.address_1 }}<br />
{{ props.participant.address_2 }}<br />
{{ props.participant.postcode }}
@@ -163,9 +175,9 @@ function saveParticipant() {
<tr>
<th>Stamm</th>
<td>
<span v-if="!props.editMode">{{ props.participant.localgroup }}</span>
<span v-if="!staticProps.editMode">{{ props.participant.localgroup }}</span>
<select v-else v-model="form.localgroup">
<option v-for="group in props.event.contributingLocalGroups" :key="group.id" :value="group.slug">{{ group.name }}</option>
<option v-for="group in staticProps.event.contributingLocalGroups" :key="group.id" :value="group.slug">{{ group.name }}</option>
</select>
</td>
</tr>
@@ -173,7 +185,7 @@ function saveParticipant() {
<tr>
<th>Geburtsdatum</th>
<td>
<span v-if="!props.editMode">{{ props.participant.birthday }}</span>
<span v-if="!staticProps.editMode">{{ props.participant.birthday }}</span>
<input v-else v-model="form.birthday" type="date" />
</td>
</tr>
@@ -186,7 +198,7 @@ function saveParticipant() {
<tr>
<th>E-Mail</th>
<td>
<span v-if="!props.editMode">{{ props.participant.email_1 }}</span>
<span v-if="!staticProps.editMode">{{ props.participant.email_1 }}</span>
<input v-else v-model="form.email_1" type="email" />
</td>
</tr>
@@ -194,7 +206,7 @@ function saveParticipant() {
<tr>
<th>Telefon</th>
<td>
<span v-if="!props.editMode">{{ props.participant.phone_1 }}</span>
<span v-if="!staticProps.editMode">{{ props.participant.phone_1 }}</span>
<input v-else v-model="form.phone_1" type="text" />
</td>
</tr>
@@ -202,7 +214,7 @@ function saveParticipant() {
<tr>
<th>Ansprechperson</th>
<td>
<span v-if="!props.editMode">{{ props.participant.contact_person }}</span>
<span v-if="!staticProps.editMode">{{ props.participant.contact_person }}</span>
<input v-else v-model="form.contact_person" type="text" />
</td>
</tr>
@@ -210,7 +222,7 @@ function saveParticipant() {
<tr>
<th>Ansprechperson E-Mail</th>
<td>
<span v-if="!props.editMode">{{ props.participant.email_2 }}</span>
<span v-if="!staticProps.editMode">{{ props.participant.email_2 }}</span>
<input v-else v-model="form.email_2" type="email" />
</td>
</tr>
@@ -218,7 +230,7 @@ function saveParticipant() {
<tr>
<th>Ansprechperson Telefon</th>
<td>
<span v-if="!props.editMode">{{ props.participant.phone_2 }}</span>
<span v-if="!staticProps.editMode">{{ props.participant.phone_2 }}</span>
<input v-else v-model="form.phone_2" type="text" />
</td>
</tr>
@@ -233,24 +245,24 @@ function saveParticipant() {
<tr>
<th>Anreise</th>
<td>
<span v-if="!props.editMode">{{ props.participant.arrival }}</span>
<input v-else v-model="form.arrival" type="text" />
<span v-if="!staticProps.editMode">{{ props.participant.arrival }}</span>
<input v-else v-model="form.arrival" type="date" />
</td>
</tr>
<tr>
<th>Abreise</th>
<td>
<span v-if="!props.editMode">{{ props.participant.departure }}</span>
<input v-else v-model="form.departure" type="text" />
<span v-if="!staticProps.editMode">{{ props.participant.departure }}</span>
<input v-else v-model="form.departure" type="date" />
</td>
</tr>
<tr>
<th>Teilnahmegruppe</th>
<td>
<span v-if="!props.editMode">{{ props.participant.participationType }}</span>
<span v-if="!staticProps.editMode">{{ props.participant.participationType }}</span>
<select v-else v-model="form.participationType">
<option
v-for="participationType in event.participationTypes"
v-for="participationType in staticProps.event.participationTypes"
:value="participationType.type.slug"
>
{{ participationType.type.name }}
@@ -261,10 +273,10 @@ function saveParticipant() {
<tr>
<th>Ernährung</th>
<td>
<span v-if="!props.editMode">{{ props.participant.eatingHabit }}</span>
<span v-if="!staticProps.editMode">{{ props.participant.eatingHabit }}</span>
<select v-else v-model="form.eatingHabit">
<option
v-for="eatingHabit in event.eatingHabits"
v-for="eatingHabit in staticProps.event.eatingHabits"
:value="eatingHabit.slug"
>
{{ eatingHabit.name }}
@@ -275,7 +287,7 @@ function saveParticipant() {
<tr>
<th>eFZ-Status</th>
<td>
<span v-if="!props.editMode">{{ props.participant.efzStatusReadable }}</span>
<span v-if="!staticProps.editMode">{{ props.participant.efzStatusReadable }}</span>
<select v-else v-model="form.cocStatus">
<option value="not_checked">Nicht geprüft</option>
<option value="not_required">Nicht erforderlich</option>
@@ -287,7 +299,7 @@ function saveParticipant() {
<tr>
<th>Beitrag</th>
<td>
<span v-if="!props.editMode">{{ props.participant.amountPaid.readable }} / {{ props.participant.amountExpected.readable }}</span>
<span v-if="!staticProps.editMode">{{ props.participant.amountPaid.readable }} / {{ props.participant.amountExpected.readable }}</span>
<span v-else>
<AmountInput v-model="form.amountPaid" style="width:74px" /> Euro
/
@@ -304,28 +316,28 @@ function saveParticipant() {
<tr>
<th>Allergien</th>
<td>
<span v-if="!props.editMode">{{ props.participant.allergies }}</span>
<span v-if="!staticProps.editMode">{{ props.participant.allergies }}</span>
<input type="text" v-else v-model="form.allergies" />
</td>
</tr>
<tr>
<th>Unverträglichkeiten</th>
<td>
<span v-if="!props.editMode">{{ props.participant.intolerances }}</span>
<span v-if="!staticProps.editMode">{{ props.participant.intolerances }}</span>
<input type="text" v-else v-model="form.intolerances" />
</td>
</tr>
<tr>
<th>Medikamente</th>
<td>
<span v-if="!props.editMode">{{ props.participant.medications }}</span>
<span v-if="!staticProps.editMode">{{ props.participant.medications }}</span>
<input type="text" v-else v-model="form.medications" />
</td>
</tr>
<tr>
<th>Erweiterte Erste Hilfe</th>
<td>
<span v-if="!props.editMode">{{ props.participant.extendedFirstAid }}</span>
<span v-if="!staticProps.editMode">{{ props.participant.extendedFirstAid }}</span>
<select v-else v-model="form.extendedFirstAid">
<option value="FIRST_AID_PERMISSION_ALLOWED">Erlaubt</option>
<option value="FIRST_AID_PERMISSION_DENIED">Verweigert</option>
@@ -335,7 +347,7 @@ function saveParticipant() {
<tr>
<th>Badeerlaubnis</th>
<td>
<span v-if="!props.editMode">{{ props.participant.swimmingPermission }}</span>
<span v-if="!staticProps.editMode">{{ props.participant.swimmingPermission }}</span>
<select v-else v-model="form.swimmingPermission">
<option value="SWIMMING_PERMISSION_ALLOWED">Vorhanden, kann schwimmen</option>
<option value="SWIMMING_PERMISSION_LIMITED">Vorhanden, kann nicht schwimmen</option>
@@ -346,14 +358,14 @@ function saveParticipant() {
<tr>
<th>Letzte Tetanus-Impfung</th>
<td>
<span v-if="!props.editMode">{{ props.participant.tetanusVaccination }}</span>
<span v-if="!staticProps.editMode">{{ props.participant.tetanusVaccination }}</span>
<input v-else v-model="form.tetanusVaccination" type="date" />
</td>
</tr>
<tr>
<th>Anmerkungen</th>
<td>
<span v-if="!props.editMode">{{ props.participant.notes }}</span>
<span v-if="!staticProps.editMode">{{ props.participant.notes }}</span>
<textarea v-else v-model="form.notes"></textarea>
</td>
</tr>
@@ -362,12 +374,12 @@ function saveParticipant() {
</div>
</div>
<button v-if="!props.editMode" class="button" @click="enableEditMode">Bearbeiten</button>
<button v-if="!staticProps.editMode" class="button" @click="enableEditMode">Bearbeiten</button>
<button v-else class="button" @click="saveParticipant">Speichern</button>
<button class="button" @click="paymentComplete(props.participant)">Zahlung vollständig</button>
<button class="button" @click="markCocExisting(props.participant)">eFZ liegt vor</button>
<button class="button" @click="cancelParticipation(props.participant)">Abmelden</button>
<button v-if="!props.participant.unregistered" class="button" @click="paymentComplete(props.participant)">Zahlung vollständig</button>
<button v-if="!props.participant.unregistered" class="button" @click="markCocExisting(props.participant)">eFZ liegt vor</button>
<button v-if="!props.participant.unregistered" class="button" @click="cancelParticipation(props.participant)">Abmelden</button>
<button class="button" @click="close">Schließen</button>
</template>

View File

@@ -5,6 +5,7 @@ import ParticipantData from "./ParticipantData.vue";
import {toast} from "vue3-toastify";
import {useAjax} from "../../../../../resources/js/components/ajaxHandler.js";
import {format, getDay, getMonth, getYear} from "date-fns";
import AmountInput from "../../../../Views/Components/AmountInput.vue";
const props = defineProps({
data: {
@@ -41,18 +42,49 @@ function openParticipantDetails(input) {
editMode.value = false;
}
console.log(props.data.participants)
async function saveParticipant(formData) {
if (!showParticipant.value?.identifier) {
return;
}
const data = await request('/api/v1/event/participant/' + showParticipant.value.identifier + '/update', {
method: "POST",
body: JSON.stringify(formData),
body: formData,
});
if (data.status === 'success') {
toast.success(data.message ?? 'Änderungen gespeichert');
toast.success(data.message ?? 'Die Änderungen wurden gespichert. Die Liste wird beim nächsten Neuladen neu generiert.');
document.getElementById('participant-' + data.participant.identifier + '-fullname').innerHTML = data.participant.fullname;
document.getElementById('participant-' + data.participant.identifier + '-birthday').innerText = data.participant.birthday;
document.getElementById('participant-' + data.participant.identifier + '-age').innerText = data.participant.age;
document.getElementById('participant-' + data.participant.identifier + '-localgroup').innerText = data.participant.localgroup;
document.getElementById('participant-' + data.participant.identifier + '-arrival').innerText = data.participant.arrival;
document.getElementById('participant-' + data.participant.identifier + '-departure').innerText = data.participant.departure;
document.getElementById('participant-' + data.participant.identifier + '-email_1').innerText = data.participant.email_1;
document.getElementById('participant-' + data.participant.identifier + '-email_2').innerText = data.participant.email_2 ?? '--';
document.getElementById('participant-' + data.participant.identifier + '-phone_1').innerText = data.participant.phone_1;
document.getElementById('participant-' + data.participant.identifier + '-phone_2').innerText = data.participant.phone_2 ?? '--';
if (data.cocChanged) {
document.getElementById('participant-' + data.identifier + '-coc-status').innerText = data.coc.statusText;
document.getElementById('participant-' + data.identifier + '-coc-action').style.display=data.coc.action;
document.getElementById('participant-' + data.identifier + '-name').className = data.coc.class;
}
if (data.amountChanged) {
document.getElementById('participant-' + data.identifier + '-payment').removeAttribute('class');
document.getElementById('participant-' + data.identifier + '-paid').innerText = data.amount.paid;
document.getElementById('participant-' + data.identifier + '-expected').innerText = data.amount.expected;
document.getElementById('participant-' + data.identifier + '-actions').style.display=data.amount.actions;
document.getElementById('participant-' + data.identifier + '-payment').className = data.amount.class;
}
editMode.value = false;
} else {
toast.error(data.message ?? 'Speichern fehlgeschlagen');
@@ -146,16 +178,6 @@ const getRowClass = (participant) => {
return "";
};
const ensureGroupState = (groupKey) => {
if (searchTerms[groupKey] === undefined) {
searchTerms[groupKey] = "";
}
if (selectedStatuses[groupKey] === undefined) {
selectedStatuses[groupKey] = "all";
}
};
async function paymentComplete(participant) {
const data = await request('/api/v1/event/participant/' + participant.identifier + '/payment-complete', {
method: "POST",
@@ -189,26 +211,76 @@ async function markCocExisting(participant) {
}
function openCancelParticipationDialog(participant) {
showParticipant.value = participant;
openCancelDialog.value = true;
showParticipant = participant;
}
async function execCancelParticipation() {
openCancelDialog.value = false;
toast.success('Abmeldung erfolgreich')
const data = await request('/api/v1/event/participant/' + showParticipant.value.identifier + '/signoff', {
method: "POST",
body: {
cancel_date: document.getElementById('cancel_date').value,
},
});
if (data.status === 'success') {
toast.success(data.message);
document.getElementById('participant-' + data.identifier + '-common').style.display = 'none';
document.getElementById('participant-' + data.identifier + '-meta').style.display = 'none';
} else {
toast.error(data.message);
}
openCancelDialog.value = false;
}
async function execResignonParticipant(participant) {
const data = await request('/api/v1/event/participant/' + participant.identifier + '/re-signon', {
method: "POST",
});
if (data.status === 'success') {
toast.success(data.message);
document.getElementById('participant-' + data.identifier + '-common').style.display = 'none';
document.getElementById('participant-' + data.identifier + '-meta').style.display = 'none';
} else {
toast.error(data.message);
}
openCancelDialog.value = false;
}
function openPartialPaymentDialog(participant) {
showParticipant.value = participant;
openPartialPaymentDialogSwitch.value = true;
showParticipant = participant;
}
async function execPartialPayment() {
const data = await request('/api/v1/event/participant/' + showParticipant.value.identifier + '/partial-payment', {
method: "POST",
body: {
amount: document.getElementById('partial_payment_amount').value,
},
});
if (data.status === 'success') {
toast.success(data.message);
document.getElementById('participant-' + data.identifier + '-payment').removeAttribute('class');
document.getElementById('participant-' + data.identifier + '-paid').innerText = data.amount.paid;
document.getElementById('participant-' + data.identifier + '-expected').innerText = data.amount.expected;
document.getElementById('participant-' + data.identifier + '-actions').style.display=data.amount.actions;
document.getElementById('participant-' + data.identifier + '-payment').className = data.amount.class;
} else {
toast.error(data.message);
}
openPartialPaymentDialogSwitch.value = false;
toast.success('Teilzahlung erfolgreich')
}
</script>
@@ -237,58 +309,66 @@ async function execPartialPayment() {
v-for="participant in getFilteredParticipants(groupKey, participants)"
:key="participant.id"
>
<tr :class="getRowClass(participant)" :id="'participant-' + participant.identifier">
<tr :class="getRowClass(participant)" :id="'participant-' + participant.identifier + '-common'">
<td :id="'participant-' + participant.identifier +'-name'"
style="width: 300px;"
:class="participant.efz_status === 'checked_invalid' ? 'efz-invalid' :
participant.efz_status === 'not_checked' ? 'efz-not-checked' : ''">
<div v-html="participant.fullname" /><br />
Geburtsdatum: {{ participant.birthday }}<br />
Alter: {{ participant.age }} Jahre<br />
<div :id="'participant-' + participant.identifier +'-fullname'" v-html="participant.fullname" /><br />
Geburtsdatum: <label :id="'participant-' + participant.identifier +'-birthday'">{{ participant.birthday }}</label><br />
Alter: <label :id="'participant-' + participant.identifier +'-age'">{{ participant.age }}</label> Jahre<br />
<span>eFZ-Status: <label :id="'participant-' + participant.identifier +'-coc-status'">{{ participant.efzStatusReadable }}</label></span> &nbsp;
<span :id="'participant-' + participant.identifier +'-coc-action'" v-if="participant.efz_status !== 'checked_valid' && participant.efz_status !== 'not_required'" class="link" style="color: #3cb62e; font-size: 11pt;" @click="markCocExisting(participant)">Vorhanden?</span>
</td>
<td :id="'participant-' + participant.identifier +'-payment'" :class="participant.amount_left_value != 0 && !unregistered_at ? 'not-paid' : ''" style="width: 275px; '">
<td :id="'participant-' + participant.identifier +'-payment'" :class="participant.amount_left_value != 0 && !participant.unregistered ? 'not-paid' : ''" style="width: 275px; '">
Gezahlt: <label :id="'participant-' + participant.identifier + '-paid'">{{ participant?.amountPaid.readable }}</label> /<br />
Gesamt: {{ participant?.amountExpected.readable }}
Gesamt: <label :id="'participant-' + participant.identifier + '-expected'">{{ participant?.amountExpected.readable }}</label>
<br /><br />
<span v-if="participant.amount_left_value != 0 && !unregistered_at" :id="'participant-' + participant.identifier + '-actions'">
<span v-if="participant.amount_left_value != 0 && !participant.unregistered" :id="'participant-' + participant.identifier + '-actions'">
<span class="link" style="font-size:10pt;" @click="paymentComplete(participant)">Zahlung buchen</span> &nbsp;
<span class="link" style="font-size:10pt;" @click="openPartialPaymentDialog(participant)">Teilzahlung buchen</span>
</span>
</td>
<td>
{{ participant?.email_1 ?? "-" }}
<div v-if="participant?.email_2 && participant.email_2 !== participant.email_1" class="text-xs text-gray-500">
{{ participant.email_2 }}
</div>
<label :id="'participant-' + participant.identifier +'-email_1'" class="block-label">{{ participant?.email_1 ?? "-" }}</label>
<label :id="'participant-' + participant.identifier +'-email_2'" class="block-label">{{ participant.email_2 }}</label>
</td>
<td>
{{ participant?.phone_1 ?? "-" }}
<div v-if="participant?.phone_2 && participant.phone_2 !== participant.phone_1" class="text-xs text-gray-500">
{{ participant.phone_2 }}
</div>
<label :id="'participant-' + participant.identifier +'-phone_1'" class="block-label">{{ participant?.phone_1 }}</label>
<label :id="'participant-' + participant.identifier +'-phone_2'" class="block-label">{{ participant?.phone_2 }}</label>
</td>
</tr>
<tr class="participant-meta-row">
<tr class="participant-meta-row" :id="'participant-' + participant.identifier + '-meta'">
<td colspan="5" style="height: 15px !important; font-size: 9pt; background-color: #ffffff; border-top-style: none;">
{{ participant?.localgroup ?? "-" }} |
<strong> Anreise: </strong>{{ participant?.arrival ?? "-" }} |
<strong> Abreise: </strong>{{ participant?.departure ?? "-" }} |
<strong> Angemeldet am: </strong>{{ participant?.registerDate ?? "-" }} |
<label :id="'participant-' + participant.identifier +'-localgroup'">
{{ participant?.localgroup ?? "-" }}
</label> |
<strong> Anreise: </strong>
<label :id="'participant-' + participant.identifier +'-arrival'">
{{ participant?.arrival ?? "-" }}
</label>|
<strong> Abreise: </strong>
<label :id="'participant-' + participant.identifier +'-departure'">
{{ participant?.departure ?? "-" }}
</label> |
<label v-if="!participant.unregistered">
<strong> Angemeldet am: </strong>{{ participant?.registerDate ?? "-" }}
</label>
<label v-else>
<strong> Abgemeldet am: </strong>{{ participant?.unregisteredAt ?? "-" }}
</label> |
<span class="link" @click="openParticipantDetails(participant)">Details</span> |
<span class="link">E-Mail senden</span> |
<span @click="openCancelParticipationDialog(participant)" v-if="!unregistered_at" class="link" style="color: #da7070;">Abmelden</span>
<span v-else class="link" style="color: #3cb62e;">Wieder anmelden</span>
<span @click="openCancelParticipationDialog(participant)" v-if="!participant.unregistered" class="link" style="color: #da7070;">Abmelden</span>
<span v-else class="link" @click="execResignonParticipant(participant)" style="color: #3cb62e;">Wieder anmelden</span>
</td>
</tr>
</template>
@@ -331,6 +411,7 @@ async function execPartialPayment() {
<Modal
:show="openCancelDialog"
title="Anmeldung stornieren"
width="350px"
@close="openCancelDialog = false"
>
Datum der Abmeldung:
@@ -342,10 +423,12 @@ async function execPartialPayment() {
<Modal
:show="openPartialPaymentDialogSwitch"
title="Teilbetragszahlung"
width="350px"
@close="openPartialPaymentDialogSwitch = false"
>
Gesamtbetrag der Zahlung:
<input type="text" style="margin-top: 10px; width: 150px !important;" id="partial_payment_amount" /> Euro
<AmountInput type="text" v-model="showParticipant.amountExpected.short" style="margin-top: 10px; width: 100px !important;" id="partial_payment_amount" /> Euro /
{{showParticipant.amountExpected.readable}} Euro
<br /><br />
<button class="button" @click="execPartialPayment()">Teilbetrag buchen</button>
</Modal>
@@ -429,4 +512,8 @@ async function execPartialPayment() {
.efz-not-checked {
color: #8D914BFF; background-color: #F4E99EFF;
}
.block-label {
display: block;
}
</style>

View File

@@ -1,6 +1,4 @@
<script setup>
import AppLayout from "../../../../../resources/js/layouts/AppLayout.vue";
import ShadowedBox from "../../../../Views/Components/ShadowedBox.vue";
import {reactive, watch} from "vue";
import AmountInput from "../../../../Views/Components/AmountInput.vue";
import ErrorText from "../../../../Views/Components/ErrorText.vue";

View File

@@ -2,10 +2,6 @@
const props = defineProps({
event: Object
})
console.log(props.event)
</script>
<template>

View File

@@ -35,8 +35,8 @@ class ProductionDataSeeder {
private function installEfzStatus() {
EfzStatus::create(['slug' => EfzStatus::EFZ_STATUS_NOT_CHECKED, 'name' => 'Nicht geprüft']);
EfzStatus::create(['slug' => EfzStatus::EFZ_STATUS_NOT_REQUIRED, 'name' => 'Nicht erforderlich']);
EfzStatus::create(['slug' => EfzStatus::EFZ_STATUS_CHECKED_VALID, 'name' => 'Geprüft und gültig']);
EfzStatus::create(['slug' => EfzStatus::EFZ_STATUS_CHECKED_INVALID, 'name' => 'Geprüft und ungültig']);
EfzStatus::create(['slug' => EfzStatus::EFZ_STATUS_CHECKED_VALID, 'name' => 'Gültig']);
EfzStatus::create(['slug' => EfzStatus::EFZ_STATUS_CHECKED_INVALID, 'name' => 'Nicht eingereicht']);
}
private function installParticipationTypes() {

View File

@@ -0,0 +1,63 @@
<?php
namespace App\Mail\ParticipantCocMails;
use App\Models\EventParticipant;
use Illuminate\Http\Request;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Attachment;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
class ParticipantCocCompleteMail extends Mailable {
public function __construct(
private EventParticipant $participant,
)
{
//
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
$participant = $this->participant->toResource()->toArray(new Request());
$subject = 'Dein erweitertes Führungszeugnis wurde bestätigt';
return new Envelope(
subject: $subject,
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
$event = $this->participant->event()->first()->toResource()->toArray(new Request());
$participant = $this->participant->toResource()->toArray(new Request());
return new Content(
view: 'emails.cocCheck.coc_check_complete',
with: [
'name' => $participant['nicename'],
'eventTitle' => $event['name'],
'eventEmail' => $event['email'],
'cocFinalDate' => $event['registrationFinalEnd']['formatted'],
],
);
}
/**
* Get the attachments for the message.
*
* @return array<int, Attachment>
*/
public function attachments(): array
{
return [];
}
}

View File

@@ -0,0 +1,63 @@
<?php
namespace App\Mail\ParticipantCocMails;
use App\Models\EventParticipant;
use Illuminate\Http\Request;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Attachment;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
class ParticipantCocInvalidMail extends Mailable {
public function __construct(
private EventParticipant $participant,
)
{
//
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
$participant = $this->participant->toResource()->toArray(new Request());
$subject = 'Bitte reiche ein erweiteres polizeiliches Führungszeugnis ein';
return new Envelope(
subject: $subject,
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
$event = $this->participant->event()->first()->toResource()->toArray(new Request());
$participant = $this->participant->toResource()->toArray(new Request());
return new Content(
view: 'emails.cocCheck.coc_check_invalid',
with: [
'name' => $participant['nicename'],
'eventTitle' => $event['name'],
'eventEmail' => $event['email'],
'cocFinalDate' => $event['registrationFinalEnd']['formatted'],
],
);
}
/**
* Get the attachments for the message.
*
* @return array<int, Attachment>
*/
public function attachments(): array
{
return [];
}
}

View File

@@ -1,12 +1,9 @@
<?php
namespace App\Mail;
namespace App\Mail\ParticipantParticipationMails;
use App\Enumerations\EfzStatus;
use App\Models\EventParticipant;
use App\Resources\EventParticipantResource;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Http\Request;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Attachment;
@@ -14,7 +11,7 @@ use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class EventSignUpSuccessfull extends Mailable
class EventSignUpSuccessfullMail extends Mailable
{
use Queueable, SerializesModels;

View File

@@ -0,0 +1,68 @@
<?php
namespace App\Mail\ParticipantParticipationMails;
use App\Models\EventParticipant;
use Illuminate\Http\Request;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Attachment;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
class ParticipantSignOffMail extends Mailable
{
public function __construct(
private EventParticipant $participant,
)
{
//
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
$participant = $this->participant->toResource()->toArray(new Request());
$subject = sprintf(
'Abmeldebestätigung %1$s %2$s',
'für die Veranstaltung',
$this->participant->event()->first()->name
);
return new Envelope(
subject: $subject,
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
$event = $this->participant->event()->first()->toResource()->toArray(new Request());
$participant = $this->participant->toResource()->toArray(new Request());
return new Content(
view: 'emails.events.participant_signed_off',
with: [
'name' => $participant['nicename'],
'eventTitle' => $event['name'],
'eventEmail' => $event['email'],
],
);
}
/**
* Get the attachments for the message.
*
* @return array<int, Attachment>
*/
public function attachments(): array
{
return [];
}
}

View File

@@ -0,0 +1,88 @@
<?php
namespace App\Mail\ParticipantPaymentMails;
use App\Models\EventParticipant;
use Illuminate\Http\Request;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Attachment;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
class ParticipantPaymentMissingPaymentMail extends Mailable
{
public function __construct(
private EventParticipant $participant,
)
{
//
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
$participant = $this->participant->toResource()->toArray(new Request());
$subject = sprintf(
$participant['needs_payment'] ? 'Teilnahme- & Zahlungsinformationen %1$s %2$s' : 'Anmeldebestätigung %1$s %2$s',
'für die Veranstaltung',
$this->participant->event()->first()->name
);
return new Envelope(
subject: $subject,
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
$event = $this->participant->event()->first()->toResource()->toArray(new Request());
$participant = $this->participant->toResource()->toArray(new Request());
$girocodeProvider = new \App\Providers\GiroCodeProvider(
$event['accountOwner'],
$event['accountIban'],
(float) $participant['amount_left_value'],
$participant['payment_purpose']
);
$girocodeBinary = (string)$girocodeProvider->create();
return new Content(
view: 'emails.participantPayments.missing_amount',
with: [
'participationType' => $participant['participationType'],
'name' => $participant['nicename'],
'eventTitle' => $event['name'],
'eventEmail' => $event['email'],
'arrival' => $participant['arrival'],
'departure' => $participant['departure'],
'amount' => $participant['amount_left_string'],
'paymentFinalDate' => $event['registrationFinalEnd']['formatted'],
'paymentRequired' => $participant['needs_payment'],
'accountOwner' => $event['accountOwner'],
'accountIban' => $event['accountIban'],
'paymentPurpose' => $participant['payment_purpose'],
'girocodeBinary' => $girocodeBinary,
],
);
}
/**
* Get the attachments for the message.
*
* @return array<int, Attachment>
*/
public function attachments(): array
{
return [];
}
}

View File

@@ -0,0 +1,79 @@
<?php
namespace App\Mail\ParticipantPaymentMails;
use App\Models\EventParticipant;
use App\ValueObjects\Amount;
use Illuminate\Http\Request;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Attachment;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
class ParticipantPaymentOverpaidMail extends Mailable
{
public function __construct(
private EventParticipant $participant,
)
{
//
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
$participant = $this->participant->toResource()->toArray(new Request());
$subject = sprintf(
'Überzahlung des Beitrags %1$s%2$s',
'für die Veranstaltung',
$this->participant->event()->first()->name
);
return new Envelope(
subject: $subject,
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
$event = $this->participant->event()->first()->toResource()->toArray(new Request());
$participant = $this->participant->toResource()->toArray(new Request());
$overpaidAmount = Amount::fromString($participant['amount_left_value'] * -1)->toString();
return new Content(
view: 'emails.participantPayments.amount_overpaid',
with: [
'participationType' => $participant['participationType'],
'name' => $participant['nicename'],
'eventTitle' => $event['name'],
'eventEmail' => $event['email'],
'arrival' => $participant['arrival'],
'departure' => $participant['departure'],
'amount' => $participant['amountExpected']['readable'],
'amount_paid' => $participant['amountPaid']['readable'],
'overpaidAmount' => $overpaidAmount,
],
);
}
/**
* Get the attachments for the message.
*
* @return array<int, Attachment>
*/
public function attachments(): array
{
return [];
}
}

View File

@@ -0,0 +1,73 @@
<?php
namespace App\Mail\ParticipantPaymentMails;
use App\Models\EventParticipant;
use Illuminate\Http\Request;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Attachment;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
class ParticipantPaymentPaidMail extends Mailable
{
public function __construct(
private EventParticipant $participant,
)
{
//
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
$participant = $this->participant->toResource()->toArray(new Request());
$subject = sprintf(
'Anmeldebestätigung %1$s %2$s',
'für die Veranstaltung',
$this->participant->event()->first()->name
);
return new Envelope(
subject: $subject,
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
$event = $this->participant->event()->first()->toResource()->toArray(new Request());
$participant = $this->participant->toResource()->toArray(new Request());
return new Content(
view: 'emails.participantPayments.amount_paid',
with: [
'participationType' => $participant['participationType'],
'name' => $participant['nicename'],
'eventTitle' => $event['name'],
'eventEmail' => $event['email'],
'arrival' => $participant['arrival'],
'departure' => $participant['departure'],
],
);
}
/**
* Get the attachments for the message.
*
* @return array<int, Attachment>
*/
public function attachments(): array
{
return [];
}
}

View File

@@ -69,11 +69,11 @@ class EventParticipant extends InstancedModel
];
protected $casts = [
'birthday' => 'datetime',
'tetanus_vaccination' => 'datetime',
'arrival_date' => 'datetime',
'departure_date' => 'datetime',
'unregistered_at' => 'datetime',
'birthday' => 'date',
'tetanus_vaccination' => 'date',
'arrival_date' => 'date',
'departure_date' => 'date',
'unregistered_at' => 'date',
'foto_socialmedia' => 'boolean',
'foto_print' => 'boolean',

View File

@@ -0,0 +1,15 @@
<?php
namespace App\Providers;
use App\ValueObjects\Amount;
class MissingPaymentProvider {
public static function calculateMissingPayment(Amount $amountPaid, Amount $amountToPay) : Amount {
$workingAmount = clone($amountToPay);
$workingAmount->subtractAmount($amountPaid);
return $workingAmount;
}
}

View File

@@ -8,9 +8,16 @@ use App\Models\EventParticipant;
use Illuminate\Http\Request;
class EventParticipantRepository {
public function getForList(Event $event, Request $request) : array {
public function getForList(Event $event, Request $request, bool $signedOffParticipants = false) : array {
$participants = [];
foreach ($event->participants()->orderBy('lastname')->orderBy('firstname')->get() as $participant) {
if (!$signedOffParticipants) {
$allParticipants = $event->participants()->whereNull('unregistered_at');
} else {
$allParticipants = $event->participants()->whereNotNull('unregistered_at');
}
foreach ($allParticipants->orderBy('lastname')->orderBy('firstname')->get() as $participant) {
$participants[] = $participant->toResource()->toArray($request);
};
@@ -52,6 +59,16 @@ class EventParticipantRepository {
return $participants;
}
public function getSignedOffParticipants(Event $event, Request $request) : array {
$allParticipants = $this->getForList($event, $request, true);
$participants = [];
foreach ($allParticipants as $participant) {
$participants[$participant['participationType']][] = $participant;
}
return $participants;
}
public function getParticipantsWithIntolerances(Event $event, Request $request) : array {
$participants = [];
foreach ($event->participants()->whereNotNull('intolerances')->whereNot('intolerances' , '=', '')->get() as $participant) {

View File

@@ -41,7 +41,11 @@ class EventParticipantResource extends JsonResource
$this->resource->toArray(),
[
'birthdayDate' => $this->resource->birthday->format('Y-m-d'),
'arrivalDate' => $this->resource->arrival_date->format('Y-m-d'),
'departureDate' => $this->resource->departure_date->format('Y-m-d'),
'registerDate' => $this->resource->created_at->format('d.m.Y'),
'unregistered' => $this->resource->unregistered_at !== null,
'unregisteredAt' => $this->resource->unregistered_at?->format('d.m.Y'),
'fullname' => $this->resource->getFullName(),
'age' => new Age($this->resource->birthday)->getAge(),
'localgroup' => $this->resource->localGroup()->first()->name,
@@ -68,7 +72,7 @@ class EventParticipantResource extends JsonResource
'eatingHabit' => EatingHabit::where('slug', $this->resource->eating_habit)->first()->name,
'efzStatusReadable' => match($this->resource->efz_status) {
EfzStatus::EFZ_STATUS_CHECKED_VALID => 'Gültig',
EfzStatus::EFZ_STATUS_CHECKED_INVALID => 'Ungültig',
EfzStatus::EFZ_STATUS_CHECKED_INVALID => 'Nicht eingereicht',
EfzStatus::EFZ_STATUS_NOT_CHECKED => 'Nicht geprüft',
EfzStatus::EFZ_STATUS_NOT_REQUIRED => 'Nicht erforderlich',
},

View File

@@ -12,6 +12,7 @@
ref="modalRef"
class="modal-content"
tabindex="-1"
:style="{ width: width }"
>
<div class="modal-header">
@@ -35,7 +36,8 @@ import { ref, watch, onMounted, onUnmounted, nextTick } from 'vue'
const props = defineProps({
show: Boolean,
title: { type: String, default: 'Modal' }
title: { type: String, default: 'Modal' },
width: { type: String, default: '1200px' },
})
const emit = defineEmits(['close'])

View File

@@ -30,7 +30,7 @@ return new class extends Migration {
$table->string('local_group');
$table->date('birthday');
$table->string('address_1');
$table->string('address_2');
$table->string('address_2')->nullable();
$table->string('postcode');
$table->string('city');
$table->string('email_1');

View File

@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html>
<body>
<h1>Hallo {{$name}}!</h1>
<p>
Vielen Dank für deine Anmeldung zur Veranstaltung "{{$eventTitle}}".
</p>
<p>
Wir konnten dein erweitertes polizeiliches Führungszeugnis nun zuordnen oder haben festgestellt, dass du kein eFZ einreichen musst.<br />
Somit ist deine Teilnahme an der Veranstaltung "{{$eventTitle}}" problemlos möglich.
</p>
<p>
@include('emails.subparts.faq')
</p><br /><br />
<p>
@include('emails.subparts.disclaimer')
</p>
</body>
</html>

View File

@@ -0,0 +1,26 @@
<!DOCTYPE html>
<html>
<body>
<h1>Hallo {{$name}}!</h1>
<p>
Vielen Dank für deine Anmeldung zur Veranstaltung "{{$eventTitle}}".
</p>
<p>
Bei einer Prüfung der Teilnehmenden hat sich gezeigt, dass du noch kein erweitertes polizeiliches Führungszeugnis einreicht hast.
</p>
<p style="border-style: solid; border-width: 2px; border-color: #ff0000; padding: 10px; font-weight: bold; color: #501e1e; background-color: #da7070">
Bitte reiche dein erweitertes Führungszeugnis bis zum {{$cocFinalDate}} ein, da deine Teilnahme andernfalls storniert wird.<br /><br />
Solltest du diese Frist nicht einhalten können, setze dich bitte mit der Aktionsleitung in Verbindung.
</p>
<p>
@include('emails.subparts.faq')
</p><br /><br />
<p>
@include('emails.subparts.disclaimer')
</p>
</body>
</html>

View File

@@ -0,0 +1,24 @@
<!DOCTYPE html>
<html>
<body>
<h1>Hallo {{$name}}!</h1>
<p>
Deine Abmeldung von der Veranstaltung "{{$eventTitle}}" wurde soeben ausgeführt.<br/>
</p>
<p>
Insofern du bereits Beiträge für die Teilnahme entrichtet hast, wird sich die Veranstaltungsleitung schnellstmöglich bei dir melden, um die Rückerstattung abzuklären.
</p>
<p>
Hast du keine Abmeldung gewünscht, und möchtest an der Veranstaltung teilnehmen, kontaktiere bitte umgehend die Veranstaltungsleitung.
</p>
<p>
@include('emails.subparts.faq')
</p><br /><br />
<p>
@include('emails.subparts.disclaimer')
</p>
</body>
</html>

View File

@@ -1,14 +1,15 @@
<!DOCTYPE html>
<html>
<body>
<h1>Hallo {{$name}}!</h1>
<p>
Vielen Dank für deine Anmeldung zur Veranstaltung "{{$eventTitle}}".<br />
<body>
<h1>Hallo {{$name}}!</h1>
<p>
Vielen Dank für deine Anmeldung zur Veranstaltung "{{$eventTitle}}".<br/>
Wir haben folgende Daten zu deiner Teilnahme erfasst:
</p>
</p>
<table cellpadding="0" cellspacing="0" border="0" style="width: 100%; max-width: 640px; border-collapse: collapse; font-family: Arial, sans-serif; font-size: 14px; color: #1f2937;">
<table cellpadding="0" cellspacing="0" border="0"
style="width: 100%; max-width: 640px; border-collapse: collapse; font-family: Arial, sans-serif; font-size: 14px; color: #1f2937;">
<tr>
<td style="padding: 8px 12px; width: 180px; font-weight: 600; color: #4b5563; border-bottom: 1px solid #e5e7eb;">
Ankunft
@@ -33,119 +34,57 @@
{{ $participationType }}
</td>
</tr>
</table>
</table>
@if ($paymentRequired)
@if ($paymentRequired)
<p>
Deine Teilnahme ist <strong>noch nicht bestätigt,</strong> da dsies erst nach vollständigem Zahlungseingang der Fall ist.
Bitte zahle den Betrag in Höhe von <strong>{{$amount}}</strong> bis zum <strong>{{$paymentFinalDate}},</strong> da andernfalls die Stornierung deiner Anmeldung erfolgen kann.
Deine Teilnahme ist <strong>noch nicht bestätigt,</strong> da dsies erst nach vollständigem Zahlungseingang der
Fall ist.
Bitte zahle den Betrag in Höhe von <strong>{{$amount}}</strong> bis zum <strong>{{$paymentFinalDate}},</strong>
da andernfalls die Stornierung deiner Anmeldung erfolgen kann.
Um deine Anmeldung zu vervollständigen, überweise den Betrag bitte zugunsten folgender Bankverbindung:
</p>
<table cellpadding="0" cellspacing="0" border="0" style="width: 100%; max-width: 640px; border-collapse: collapse; font-family: Arial, sans-serif; font-size: 14px; color: #1f2937;">
<tr>
<td style="padding: 8px 12px; width: 180px; font-weight: 600; color: #4b5563; border-bottom: 1px solid #e5e7eb;">
Kontoinhaber*in
</td>
<td style="padding: 8px 12px; border-bottom: 1px solid #e5e7eb;">
{{ $accountOwner }}
</td>
</tr>
<tr>
<td style="padding: 8px 12px; font-weight: 600; color: #4b5563; border-bottom: 1px solid #e5e7eb;">
IBAN
</td>
<td style="padding: 8px 12px; border-bottom: 1px solid #e5e7eb; font-family: monospace;">
{{ $accountIban }}
</td>
</tr>
<tr>
<td style="padding: 8px 12px; font-weight: 600; color: #4b5563; border-bottom: 1px solid #e5e7eb;">
Verwendungszweck
</td>
<td style="padding: 8px 12px; border-bottom: 1px solid #e5e7eb;">
{{ $paymentPurpose }}
</td>
</tr>
<tr>
<td style="padding: 10px 12px; font-weight: 700; color: #111827; border-top: 2px solid #d1d5db;">
Betrag
</td>
<td style="padding: 10px 12px; font-weight: 700; color: #111827; border-top: 2px solid #d1d5db;">
{{ $amount }}
</td>
</tr>
</table>
@php
$girocodeSrc = $message->embedData($girocodeBinary, 'girocode.png', 'image/png');
@endphp
<table style="margin: 20px;">
<tr style="vertical-align: top; background-color: #eaeaea; border-spacing: 0">
<td style="padding: 20px; text-align: center; font-weight: bold;">
<strong>
Bequem überweisen mit QR-Code
</strong><br /><br />
<img
style="padding-left: 20px;width: 150px; height: 150px;"
src="{{ $girocodeSrc }}"
alt="Giro-Code">
</td>
<td>
<p style="padding-left:10px; padding-right: 10px; text-align: left;">
<strong>Und so funktioniert es</strong><br />
<ul style="text-align: left; list-style: decimal; font-weight: normal">
<li style="padding-right: 10px;">Mache einen Screenshot des QR-Codes</li>
<li style="padding-right: 10px;">Öffne deine Banking-App und wähle den Menüpunkt (Foto-)Überweisung“.</li>
<li style="padding-right: 10px;">Wähle das gespeicherte Bild aus</li>
</ul>
</p>
</td>
</tr>
</table>
<p>
Bitte zahle immer mit dem vorgegeben Betreff, damit deine Zahlung möglichst einfach zugeordnet werden kann.<br />
Sollte die Zahlung innerhalb dieser Frist nicht oder nur teilweise möglich sein, kontaktiere bitte die Aktionsleitung, sodass wir eine gemeinsame Lösung finden können.
@include('emails.subparts.payment')
</p>
@else
@else
<p>
Deine Teilnahme wird entweder gefördert, sodass du nichts überweisen musst, oder die Beitragszahlung wird durch deinen Stamm abgewickelt.<br />
Deine Teilnahme wird entweder gefördert, sodass du nichts überweisen musst, oder die Beitragszahlung wird durch
deinen Stamm abgewickelt.<br/>
In diesem Fall wird sich dein Stamm bei dir melden, ob und in welcher Höhe ein Eigenanteil zu zahlen ist.
</p>
@endif
@endif
@switch ($efzStatus)
@switch ($efzStatus)
@case ('not_checked')
<p style="border-style: solid; border-width: 2px; border-color: #bdb053; padding: 10px; font-weight: bold; color: #501e1e; background-color: #f4e99e">
Dein erweitertes Führungszeugnis konnte nicht automatisch überprüft werden. Bitte kontaktiere die Aktionsleitung, da deine Teilnahme nur mit gültigem eFZ möglich ist.
Dein erweitertes Führungszeugnis konnte nicht automatisch überprüft werden. Bitte kontaktiere die
Aktionsleitung, da deine Teilnahme nur mit gültigem eFZ möglich ist.
</p>
@break
@case ('checked_invalid')
<p style="border-style: solid; border-width: 2px; border-color: #ff0000; padding: 10px; font-weight: bold; color: #501e1e; background-color: #da7070">
Du hast noch kein erweitertes Führungszeugnis bereitgestellt, sodass deine Teilnahme nicht möglich ist.
Bitte reiche dein erweitertes Führungszeugnis bis zum {{$paymentFinalDate}} ein, da deine Teilnahme andernfalls storniert wird.<br /><br />
Solltest du diese Frist nicht einhalten können, setze dich bitte mit der Aktionsleitung in Verbindung.',
Bitte reiche dein erweitertes Führungszeugnis bis zum {{$paymentFinalDate}} ein, da deine Teilnahme
andernfalls storniert wird.<br/><br/>
Solltest du diese Frist nicht einhalten können, setze dich bitte mit der Aktionsleitung in Verbindung.
</p>
@break
@default
@endswitch
@endswitch
<p>
@include('emails.events.faq')
</p><br /><br />
<p>
@include('emails.events.disclaimer')
</p>
</body>
<p>
@include('emails.subparts.faq')
</p><br /><br />
<p>
@include('emails.subparts.disclaimer')
</p>
</body>
</html>

View File

@@ -0,0 +1,58 @@
<!DOCTYPE html>
<html>
<body>
<h1>Hallo {{$name}}!</h1>
<p>
Vielen Dank für deine Anmeldung zur Veranstaltung "{{$eventTitle}}".<br/>
Wir haben folgende Daten zu deiner Teilnahme erfasst:
</p>
<table cellpadding="0" cellspacing="0" border="0"
style="width: 100%; max-width: 640px; border-collapse: collapse; font-family: Arial, sans-serif; font-size: 14px; color: #1f2937;">
<tr>
<td style="padding: 8px 12px; width: 180px; font-weight: 600; color: #4b5563; border-bottom: 1px solid #e5e7eb;">
Ankunft
</td>
<td style="padding: 8px 12px; border-bottom: 1px solid #e5e7eb;">
{{ $arrival }}
</td>
</tr>
<tr>
<td style="padding: 8px 12px; font-weight: 600; color: #4b5563; border-bottom: 1px solid #e5e7eb;">
Abreise
</td>
<td style="padding: 8px 12px; border-bottom: 1px solid #e5e7eb;">
{{ $departure }}
</td>
</tr>
<tr>
<td style="padding: 8px 12px; font-weight: 600; color: #4b5563;">
Teilnahmegruppe
</td>
<td style="padding: 8px 12px;">
{{ $participationType }}
</td>
</tr>
</table>
<p>
Wir haben festgestellt, dass du für die Anmeldung einen zu hohen Betrag entrichtet hast.<br />
Der Anmeldebetrag für deine Teilnahme beträgt {{ $amount }} Euro, die geleistete Zahlung beläuft sich auf {{$amount_paid}}.<br />
Somit ergibt sich eine Überzahlung zu deinen Gunsten in Höhe von <strong>{{ $overpaidAmount }}.</strong>
</p>
<p>
Bitte teile uns mit, ob du den Betrag spenden möchtest, oder ob du eine Rückzahlung wünscht.<br />
Insofern du eine Rückzahlung wünschst, teile uns bitte auch deine Bankverbindung mit.
</p>
<p>
@include('emails.subparts.faq')
</p><br /><br />
<p>
@include('emails.subparts.disclaimer')
</p>
</body>
</html>

View File

@@ -0,0 +1,52 @@
<!DOCTYPE html>
<html>
<body>
<h1>Hallo {{$name}}!</h1>
<p>
Vielen Dank für deine Anmeldung zur Veranstaltung "{{$eventTitle}}".<br/>
Wir haben folgende Daten zu deiner Teilnahme erfasst:
</p>
<table cellpadding="0" cellspacing="0" border="0"
style="width: 100%; max-width: 640px; border-collapse: collapse; font-family: Arial, sans-serif; font-size: 14px; color: #1f2937;">
<tr>
<td style="padding: 8px 12px; width: 180px; font-weight: 600; color: #4b5563; border-bottom: 1px solid #e5e7eb;">
Ankunft
</td>
<td style="padding: 8px 12px; border-bottom: 1px solid #e5e7eb;">
{{ $arrival }}
</td>
</tr>
<tr>
<td style="padding: 8px 12px; font-weight: 600; color: #4b5563; border-bottom: 1px solid #e5e7eb;">
Abreise
</td>
<td style="padding: 8px 12px; border-bottom: 1px solid #e5e7eb;">
{{ $departure }}
</td>
</tr>
<tr>
<td style="padding: 8px 12px; font-weight: 600; color: #4b5563;">
Teilnahmegruppe
</td>
<td style="padding: 8px 12px;">
{{ $participationType }}
</td>
</tr>
</table>
<p>
Wir haben den vollständigen Betrag für deine Teilnahme erhalten und deine Anmeldung ist nun bestätigt.
</p>
<p>
@include('emails.subparts.faq')
</p><br /><br />
<p>
@include('emails.subparts.disclaimer')
</p>
</body>
</html>

View File

@@ -0,0 +1,61 @@
<!DOCTYPE html>
<html>
<body>
<h1>Hallo {{$name}}!</h1>
<p>
Vielen Dank für deine Anmeldung zur Veranstaltung "{{$eventTitle}}".<br/>
Wir haben folgende Daten zu deiner Teilnahme erfasst:
</p>
<table cellpadding="0" cellspacing="0" border="0"
style="width: 100%; max-width: 640px; border-collapse: collapse; font-family: Arial, sans-serif; font-size: 14px; color: #1f2937;">
<tr>
<td style="padding: 8px 12px; width: 180px; font-weight: 600; color: #4b5563; border-bottom: 1px solid #e5e7eb;">
Ankunft
</td>
<td style="padding: 8px 12px; border-bottom: 1px solid #e5e7eb;">
{{ $arrival }}
</td>
</tr>
<tr>
<td style="padding: 8px 12px; font-weight: 600; color: #4b5563; border-bottom: 1px solid #e5e7eb;">
Abreise
</td>
<td style="padding: 8px 12px; border-bottom: 1px solid #e5e7eb;">
{{ $departure }}
</td>
</tr>
<tr>
<td style="padding: 8px 12px; font-weight: 600; color: #4b5563;">
Teilnahmegruppe
</td>
<td style="padding: 8px 12px;">
{{ $participationType }}
</td>
</tr>
</table>
@if ($paymentRequired)
<p>
Wahrscheinlich hast du vergessen, deinen Teilnahmebeitrag zu überweisen, sodass deine Anmeldung leider <strong>noch nicht bestätigt,</strong> werden konnte.
Um deine Anmeldung zu bestätigen, zahle bitte den Betrag in Höhe von <strong>{{$amount}}</strong> bis zum <strong>{{$paymentFinalDate}},</strong>
da andernfalls die Stornierung deiner Anmeldung erfolgen kann.
Um deine Anmeldung zu vervollständigen, überweise den Betrag bitte zugunsten folgender Bankverbindung:
</p>
<p>
@include('emails.subparts.payment')
</p>
@endif
<p>
@include('emails.subparts.faq')
</p><br /><br />
<p>
@include('emails.subparts.disclaimer')
</p>
</body>
</html>

View File

@@ -0,0 +1,72 @@
<table cellpadding="0" cellspacing="0" border="0"
style="width: 100%; max-width: 640px; border-collapse: collapse; font-family: Arial, sans-serif; font-size: 14px; color: #1f2937;">
<tr>
<td style="padding: 8px 12px; width: 180px; font-weight: 600; color: #4b5563; border-bottom: 1px solid #e5e7eb;">
Kontoinhaber*in
</td>
<td style="padding: 8px 12px; border-bottom: 1px solid #e5e7eb;">
{{ $accountOwner }}
</td>
</tr>
<tr>
<td style="padding: 8px 12px; font-weight: 600; color: #4b5563; border-bottom: 1px solid #e5e7eb;">
IBAN
</td>
<td style="padding: 8px 12px; border-bottom: 1px solid #e5e7eb; font-family: monospace;">
{{ $accountIban }}
</td>
</tr>
<tr>
<td style="padding: 8px 12px; font-weight: 600; color: #4b5563; border-bottom: 1px solid #e5e7eb;">
Verwendungszweck
</td>
<td style="padding: 8px 12px; border-bottom: 1px solid #e5e7eb;">
{{ $paymentPurpose }}
</td>
</tr>
<tr>
<td style="padding: 10px 12px; font-weight: 700; color: #111827; border-top: 2px solid #d1d5db;">
Betrag
</td>
<td style="padding: 10px 12px; font-weight: 700; color: #111827; border-top: 2px solid #d1d5db;">
{{ $amount }}
</td>
</tr>
</table>
@php
$girocodeSrc = $message->embedData($girocodeBinary, 'girocode.png', 'image/png');
@endphp
<table style="margin: 20px;">
<tr style="vertical-align: top; background-color: #eaeaea; border-spacing: 0">
<td style="padding: 20px; text-align: center; font-weight: bold;">
<strong>
Bequem überweisen mit QR-Code
</strong><br/><br/>
<img
style="padding-left: 20px;width: 150px; height: 150px;"
src="{{ $girocodeSrc }}"
alt="Giro-Code">
</td>
<td>
<p style="padding-left:10px; padding-right: 10px; text-align: left;">
<strong>Und so funktioniert es</strong><br/>
<ul style="text-align: left; list-style: decimal; font-weight: normal">
<li style="padding-right: 10px;">Mache einen Screenshot des QR-Codes</li>
<li style="padding-right: 10px;">Öffne deine Banking-App und wähle den Menüpunkt
(Foto-)Überweisung“.
</li>
<li style="padding-right: 10px;">Wähle das gespeicherte Bild aus</li>
</ul>
</p>
</td>
</tr>
</table>
<p>
Bitte zahle immer mit dem vorgegeben Betreff, damit deine Zahlung möglichst einfach zugeordnet werden kann.<br/>
Sollte die Zahlung innerhalb dieser Frist nicht oder nur teilweise möglich sein, kontaktiere bitte die
Aktionsleitung, sodass wir eine gemeinsame Lösung finden können.
</p>