First Aid list, amount list and kitchen list PDF download implemented

This commit is contained in:
2026-03-28 22:22:06 +01:00
parent df7c14442e
commit 7bea223ded
20 changed files with 92 additions and 36 deletions

View File

@@ -2,6 +2,7 @@
namespace App\Domains\Event\Actions\SignUp;
use App\Enumerations\EatingHabit;
use App\Enumerations\EfzStatus;
use App\ValueObjects\Age;
use Illuminate\Support\Str;
@@ -13,8 +14,13 @@ class SignUpCommand {
public function execute() : SignUpResponse {
$response = new SignUpResponse();
$participantAge = new Age($this->request->birthday);
$eatingHabit = match ($this->request->eating_habit) {
'vegan' => EatingHabit::EATING_HABIT_VEGAN,
'vegetarian' => EatingHabit::EATING_HABIT_VEGETARIAN,
default => EatingHabit::EATING_HABIT_OMNIVOR,
};
$participantAge = new Age($this->request->birthday);
$response->participant = $this->request->event->participants()->create(
[
'tenant' => $this->request->event->tenant,
@@ -39,7 +45,7 @@ class SignUpCommand {
'intolerances' => $this->request->intolerances,
'medications' => $this->request->medications,
'tetanus_vaccination' => $this->request->tetanus_vaccination,
'eating_habit' => $this->request->eating_habit,
'eating_habit' => $eatingHabit,
'swimming_permission' => $participantAge->isfullAged() ? 'SWIMMING_PERMISSION_ALLOWED' : $this->request->swimming_permission,
'first_aid_permission' => $participantAge->isfullAged() ? 'FIRST_AID_PERMISSION_ALLOWED' : $this->request->first_aid_permission,
'foto_socialmedia' => $this->request->foto_socialmedia,

View File

@@ -10,12 +10,15 @@ use App\Domains\Event\Actions\UpdateManagers\UpdateManagersCommand;
use App\Domains\Event\Actions\UpdateManagers\UpdateManagersRequest;
use App\Enumerations\ParticipationFeeType;
use App\Enumerations\ParticipationType;
use App\Models\EventParticipant;
use App\Providers\InertiaProvider;
use App\Providers\PdfGenerateAndDownloadProvider;
use App\Resources\EventResource;
use App\Scopes\CommonController;
use App\ValueObjects\Amount;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
class DetailsController extends CommonController {
public function __invoke(int $eventId) {
@@ -123,4 +126,31 @@ class DetailsController extends CommonController {
return response()->json(['status' => $response->success ? 'success' : 'error']);
}
public function downloadPdfList(string $eventId, string $listType, Request $request): Response
{
$event = $this->events->getByIdentifier($eventId);
$participants = $this->eventParticipants->getForList($event, $request);
$kitchenOverview = $this->eventParticipants->getKitchenOverview($event);
$html = view('pdfs.' . $listType, [
'event' => $event->name,
'eventStart' => $event->start_date,
'eventEnd' => $event->end_date,
'rows' => $participants,
'kitchenRequirements' => $kitchenOverview,
'participantsForKitchenList' => $this->eventParticipants->getParticipantsWithIntolerances($event, $request),
])->render();
$pdf = PdfGenerateAndDownloadProvider::fromHtml(
$html,
'landscape'
);
return response($pdf, 200, [
'Content-Type' => 'application/pdf',
'Content-Disposition' => 'attachment; filename="' . $listType .'.pdf"',
]);
}
}

View File

@@ -81,10 +81,10 @@ class SignupController extends CommonController {
$registrationData['nachname'],
$registrationData['email_1'],
DateTime::createFromFormat('Y-m-d', $registrationData['geburtsdatum']));
/*
if ($doubleCheckEventRegistrationProvider->isRegistered()) {
return response()->json(['status' => 'exists']);
}
}*/
$amount = $eventResource->calculateAmount(
$registrationData['participationType'],

View File

@@ -18,6 +18,7 @@ Route::middleware(IdentifyTenant::class)->group(function () {
Route::middleware(['auth'])->group(function () {
Route::get('/new', CreateController::class);
Route::get('/details/{eventId}', DetailsController::class);
Route::get('/details/{eventId}/pdf/{listType}', [DetailsController::class, 'downloadPdfList']);
});
});
});

View File

@@ -36,7 +36,7 @@
sendWeeklyReports: props.event.sendWeeklyReports,
registrationAllowed: props.event.registrationAllowed,
flatSupport: props.event.flatSupportEdit,
supportPerson: props.event.supportPersonEdit,
supportPerson: props.event.supportPersonIndex,
})
onMounted(async () => {

View File

@@ -60,11 +60,20 @@ import {onMounted, reactive, ref} from "vue";
<div class="event-flexbox-row top">
<div class="left"><ParticipationSummary v-if="dynamicProps.event" :event="dynamicProps.event" /></div>
<div class="right">
<input type="button" value="Erste-Hilfe-Liste (PDF)" /><br/>
<a :href="'/event/details/' + props.data.event.identifier + '/pdf/first-aid-list'">
<input type="button" value="Erste-Hilfe-Liste (PDF)" />
</a><br/>
<input type="button" value="Teili-Liste (CSV)" /><br/>
<input type="button" value="KSV-Daten (CSV)" /><br/>
<input type="button" value="Küchenübersicht (PDF)" /><br/>
<input type="button" value="Beitragsliste (PDF)" /><br/>
<a :href="'/event/details/' + props.data.event.identifier + '/pdf/kitchen-list'">
<input type="button" value="Küchenübersicht (PDF)" />
</a><br/>
<a :href="'/event/details/' + props.data.event.identifier + '/pdf/amount-list'">
<input type="button" value="Beitragsliste (PDF)" />
</a><br/>
<input type="button" value="Getränkeliste (PDF)" /><br/>
<input type="button" value="Foto-Erlaubnis (PDF)" /><br/>
<input type="button" class="fix-button" value="Zahlungserinnerung senden" /><br/>

View File

@@ -17,7 +17,7 @@
<table class="participant-income-table" style="margin-bottom: 40px; font-size: 11pt;">
<tr>
<th>Teili</th>
<td>{{props.event.participants.participant.count}} Personen:</td>
<td><strong>{{props.event.participants.participant.count}} Personen:</strong></td>
<td>
{{props.event.participants.participant.amount.paid.readable}} /
</td>
@@ -28,7 +28,7 @@
<tr>
<th>Team</th>
<td>{{props.event.participants.team.count}} Personen:</td>
<td><strong>{{props.event.participants.team.count}} Personen:</strong></td>
<td>
{{props.event.participants.team.amount.paid.readable}} /
</td>
@@ -39,7 +39,7 @@
<tr>
<th>Unterstützende</th>
<td>{{props.event.participants.volunteer.count}} Personen:</td>
<td><strong>{{props.event.participants.volunteer.count}} Personen:</strong></td>
<td>
{{props.event.participants.volunteer.amount.paid.readable}} /
</td>
@@ -50,7 +50,7 @@
<tr>
<th>Sonstige</th>
<td>{{props.event.participants.other.count}} Personen:</td>
<td><strong>{{props.event.participants.other.count}} Personen:</strong></td>
<td>
{{props.event.participants.other.amount.paid.readable}} /
</td>
@@ -91,7 +91,7 @@
{{ props.event.totalBalance.real.readable }} /
</td>
<td v-else style="color: #f44336; font-weight: bold; padding-top: 20px; font-size: 12pt !important;">
{{props.event.totalBalance.expected.readable}}
{{props.event.totalBalance.real.readable}}
</td>
<td v-if="props.event.totalBalance.expected.value >= 0" style="color: #4caf50; font-weight: bold; padding-top: 20px; font-size: 12pt !important;">
@@ -107,14 +107,14 @@
<div class="right">
<h3>Ausgaben</h3>
<table class="event-payment-table" style="font-size: 11pt;">
<table class="event-payment-table" style="font-size: 10pt;">
<tr v-for="amount in props.event.costUnit.amounts">
<th>{{amount.name}}</th>
<td>{{amount.string}}</td>
</tr>
<tr>
<th style="color:#f44336; border-width: 1px; border-bottom-style: solid; padding-top: 20px">Gesamt</th>
<td style="color:#f44336; border-width: 1px; border-bottom-style: solid; padding-top: 20px; font-weight: bold">{{props.event.costUnit.overAllAmount.text}}</td>
<th style="color:#f44336; border-width: 1px; border-bottom-style: solid; padding-top: 58px">Gesamt</th>
<td style="color:#f44336; border-width: 1px; border-bottom-style: solid; padding-top: 58px; font-weight: bold">{{props.event.costUnit.overAllAmount.text}}</td>
</tr>
</table>
</div>

View File

@@ -33,8 +33,8 @@ console.log(participantData)
first_aid: '-1',
participant_group: '',
beitrag: 'regular',
arrival: event.eventBeginInternal?.split('T')[0] ?? '',
departure: event.eventEndInternal?.split('T')[0] ?? '',
arrival: event.arrivalDefault ?? '',
departure: event.departureDefault ?? '',
anreise_essen: '1',
abreise_essen: '2',
foto: { socialmedia: false, print: false, webseite: false, partner: false, intern: false },

View File

@@ -31,7 +31,7 @@ const emit = defineEmits(['next'])
</div>
<div class="age-card__body">
<h4 class="age-card__title">Mich selbst anmelden</h4>
<p class="age-card__desc">Ich bin <strong>{{ event.alcoholicsAge }} Jahre oder älter</strong>.</p>
<p class="age-card__desc">Ich bin <strong>18 Jahre oder älter</strong>.</p>
</div>
</div>

View File

@@ -22,5 +22,6 @@ class SwimmingPermission extends CommonModel
protected $fillable = [
'slug',
'name',
'short'
];
}

View File

@@ -73,9 +73,9 @@ class ProductionDataSeeder {
}
private function installSwimmingPermissions() {
SwimmingPermission::create(['name' => 'Mein Kind darf baden und kann schwimmen', 'slug' => SwimmingPermission::SWIMMING_PERMISSION_ALLOWED]);
SwimmingPermission::create(['name' => 'Mein Kind darf baden und kann NICHT schwimmen', 'slug' => SwimmingPermission::SWIMMING_PERMISSION_LIMITED]);
SwimmingPermission::create(['name' => 'Mein Kind darf nicht baden', 'slug' => SwimmingPermission::SWIMMING_PERMISSION_DENIED]);
SwimmingPermission::create(['name' => 'Mein Kind darf baden und kann schwimmen', 'slug' => SwimmingPermission::SWIMMING_PERMISSION_ALLOWED, 'short' => 'Erteilt']);
SwimmingPermission::create(['name' => 'Mein Kind darf baden und kann NICHT schwimmen', 'slug' => SwimmingPermission::SWIMMING_PERMISSION_LIMITED, 'short' => 'Nur Flachwasser']);
SwimmingPermission::create(['name' => 'Mein Kind darf nicht baden', 'slug' => SwimmingPermission::SWIMMING_PERMISSION_DENIED, 'short' => 'Verweiger']);
}
private function installEatingHabits() {

View File

@@ -169,7 +169,7 @@ class Event extends InstancedModel
->withTimestamps();
}
public function participants() : hasMany {
public function participants() : HasMany {
return $this->hasMany(EventParticipant::class);
}

View File

@@ -45,7 +45,7 @@ class EventParticipant extends InstancedModel
'intolerances',
'medications',
'tetanus_vaccination',
'eating_habits',
'eating_habit',
'swimming_permission',
'first_aid_permission',

View File

@@ -166,7 +166,7 @@ class CostUnitRepository {
if (
$invoice->status === InvoiceStatus::INVOICE_STATUS_DENIED ||
$invoice->donation ||
$invoice->type !== $invoice->slug
$invoice->type !== $invoiceType->slug
) {
continue;
}

View File

@@ -4,6 +4,7 @@ namespace App\Resources;
use App\Enumerations\ParticipationType;
use App\Models\EventParticipant;
use App\ValueObjects\Age;
use Illuminate\Http\Resources\Json\JsonResource;
class EventParticipantResource extends JsonResource
@@ -34,6 +35,11 @@ class EventParticipantResource extends JsonResource
return array_merge(
$this->resource->toArray(),
[
'age' => new Age($this->resource->birthday)->getAge(),
'localgroup' => $this->resource->localGroup()->first()->name,
'swimmingPermission' => $this->resource->swimmingPermission()->first()->short,
'extendedFirstAid' => $this->resource->firstAidPermission()->first()->name,
'tetanusVaccination' => $this->resource->tetanus_vaccination?->format('d.m.Y'),
'presenceDays' => ['real' => $presenceDays, 'support' => $presenceDaysSupport],
'participationType' => ParticipationType::where(['slug' => $this->resource->participation_type])->first()->name,
'needs_payment' => $this->resource->amount->getAmount() > 0 && $event->pay_direct,
@@ -43,7 +49,8 @@ class EventParticipantResource extends JsonResource
'amount_left_string' => $amountLeft->toString(),
'amount_left_value' => $amountLeft->getAmount(),
'email_1' => $this->resource->email_1,
'amountPaid' => ['value' => $this->resource->amount_paid, 'readable' => $this->resource->amount_paid?->toString() ?? '0,00 Euro'],
'amountExpected' => ['value' => $this->resource->amount, 'readable' => $this->resource->amount?->toString() ?? '0,00 Euro'],
]
);

View File

@@ -67,13 +67,12 @@ class EventResource extends JsonResource{
$returnArray['participants'][$participationType] = $this->getParticipants($participationType);
}
$returnArray['costUnit'] = new CostUnitResource($this->event->costUnit()->first())->toArray(true);
$returnArray['solidarityPayment'] = $this->event->participation_fee_type === ParticipationFeeType::PARTICIPATION_FEE_TYPE_SOLIDARITY;
$returnArray['payPerDay'] = $this->event->pay_per_day;
$returnArray['maxAmount'] = $this->event->total_max_amount->getFormattedAmount();
$returnArray['arrivalDefault'] = $this->event->start_date->format('Y-m-d');
$returnArray['departureDefault'] = $this->event->end_date->format('Y-m-d');
$returnArray['eventBegin'] = $this->event->start_date->format('d.m.Y');
$returnArray['eventBeginInternal'] = $this->event->start_date;
$returnArray['eventEnd'] = $this->event->end_date->format('d.m.Y');
@@ -94,7 +93,6 @@ class EventResource extends JsonResource{
$totalBalanceReal->subtractAmount($returnArray['costUnit']['overAllAmount']['value']);
$totalBalanceExpected->subtractAmount($returnArray['costUnit']['overAllAmount']['value']);
$returnArray['totalBalance'] = [
'real' => [
'value' => $totalBalanceReal->getAmount(),

View File

@@ -5,6 +5,7 @@ namespace App\Scopes;
use App\Models\Tenant;
use App\Providers\AuthCheckProvider;
use App\Repositories\CostUnitRepository;
use App\Repositories\EventParticipantRepository;
use App\Repositories\EventRepository;
use App\Repositories\InvoiceRepository;
use App\Repositories\PageTextRepository;
@@ -19,6 +20,7 @@ abstract class CommonController {
protected InvoiceRepository $invoices;
protected EventRepository $events;
protected EventParticipantRepository $eventParticipants;
public function __construct() {
$this->tenant = app('tenant');
@@ -27,6 +29,7 @@ abstract class CommonController {
$this->pageTexts = new PageTextRepository();
$this->invoices = new InvoiceRepository();
$this->events = new EventRepository();
$this->eventParticipants = new EventParticipantRepository();
}
protected function checkAuth() {

View File

@@ -27,6 +27,7 @@ return new class extends Migration
Schema::create('swimming_permissions', function (Blueprint $table) {
$table->string('slug')->unique()->primary();
$table->string('name');
$table->string('short');
$table->timestamps();
});

View File

@@ -46,10 +46,10 @@ return new class extends Migration {
$table->string('location');
$table->string('postal_code');
$table->string('email');
$table->dateTime('start_date');
$table->dateTime('end_date');
$table->dateTime('early_bird_end');
$table->dateTime('registration_final_end');
$table->date('start_date');
$table->date('end_date');
$table->date('early_bird_end');
$table->date('registration_final_end');
$table->integer('early_bird_end_amount_increase');
$table->string('account_owner');
$table->string('account_iban');

View File

@@ -50,8 +50,8 @@ return new class extends Migration {
$table->boolean('foto_webseite')->default(false);
$table->boolean('foto_partner')->default(false);
$table->boolean('foto_intern')->default(false);
$table->dateTime('arrival_date');
$table->dateTime('departure_date');
$table->date('arrival_date');
$table->date('departure_date');
$table->integer('arrival_eating');
$table->integer('departure_eating');
$table->longText('notes')->nullable();