Files
mareike/app/Resources/EventResource.php
T
2026-08-12 16:45:02 +02:00

422 lines
18 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Resources;
use App\Enumerations\ParticipationFeeType;
use App\Enumerations\ParticipationType;
use App\Enumerations\VatPricingMode;
use App\Models\Event;
use App\ValueObjects\Amount;
use DateTime;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class EventResource extends JsonResource{
private Event $event;
public function __construct(Event $event) {
$this->event = $event;
}
public function toArray(Request $request) : array
{
$duration = $this->event->end_date->diff($this->event->start_date)->days + 1;
$returnArray = [
'id' => $this->event->id,
'name' => $this->event->name,
'identifier' => $this->event->identifier,
'url' => 'https://' . app('tenant')->url . '/event/' . $this->event->identifier . '/signup',
'urlShort' => 'https://' . app('tenant')->url . '/event/' . $this->event->identifier,
'location' => $this->event->location,
'postalCode' => $this->event->postal_code,
'email' => $this->event->email,
'accountOwner' => $this->event->account_owner,
'accountIban' => $this->event->account_iban,
'alcoholicsAge' => $this->event->alcoholics_age,
'sendWeeklyReports' => $this->event->send_weekly_report,
'registrationAllowed' => $this->event->registration_allowed && new \DateTime() <= $this->event->start_date,
'archived' => $this->event->archived,
'earlyBirdEnd' => ['internal' => $this->event->early_bird_end->format('Y-m-d'), 'formatted' => $this->event->early_bird_end->format('d.m.Y')],
'registrationFinalEnd' => ['internal' => $this->event->registration_final_end->format('Y-m-d'), 'formatted' => $this->event->registration_final_end->format('d.m.Y')],
'refundAfterEarlyBirdEnd' => 100 - $this->event->early_bird_end_amount_increase,
];
$returnArray['swimmingPermissions'] = \App\Enumerations\SwimmingPermission::query()
->get()
->map(fn ($permission) => [
'slug' => $permission->slug,
'name' => $permission->name,
])
->toArray();
$returnArray['firstAidPermissions'] = \App\Enumerations\FirstAidPermission::query()
->get()
->map(fn ($permission) => [
'slug' => $permission->slug,
'name' => $permission->name,
])
->toArray();
foreach ([
ParticipationType::PARTICIPATION_TYPE_PARTICIPANT,
ParticipationType::PARTICIPATION_TYPE_TEAM,
ParticipationType::PARTICIPATION_TYPE_VOLUNTEER,
ParticipationType::PARTICIPATION_TYPE_OTHER,
] as $participationType) {
$_t =$this->getParticipants($participationType);
$returnArray['participants'][$participationType] = $_t;
$returnArray['count' . ucfirst($participationType)] = $_t['count'];
}
$returnArray['nameShort'] = $returnArray['name'];
if (strlen($returnArray['nameShort']) > 15) {
$returnArray['nameShort'] = substr($returnArray['nameShort'], 8, 13) . '...';
}
$returnArray['siblingReduction'] = $this->event->sibling_reduction ?? true;
$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');
$returnArray['eventEndInternal'] = $this->event->end_date;
$returnArray['duration'] = $duration;
$returnArray['totalParticipantCount'] = $this->event->participants()->count();
$returnArray['supportPersonIndex'] = $this->event->support_per_person->toString();
$returnArray['supportPersonValue'] = $this->event->support_per_person->getAmount();
$returnArray['supportPerson'] = $this->calculateSupportPerPerson($returnArray['participants']);
$returnArray['income'] = $this->calculateIncomes($returnArray['participants'], $returnArray['supportPerson']['amount']);
$totalBalanceReal = new Amount(0, 'Euro');
$totalBalanceExpected = new Amount(0, 'Euro');
$totalBalanceEstimated = new Amount(0, 'Euro');
$totalBalanceReal->addAmount($returnArray['income']['real']['amount']);
$totalBalanceExpected->addAmount($returnArray['income']['expected']['amount']);
$totalBalanceEstimated->addAmount($returnArray['income']['expected']['amount']);
$totalBalanceReal->subtractAmount($returnArray['costUnit']['overAllAmount']['value']);
$totalBalanceExpected->subtractAmount($returnArray['costUnit']['overAllAmount']['value']);
$totalBalanceEstimated->subtractAmount($returnArray['costUnit']['overAllEstimatedAmount']['value']);
$returnArray['totalBalance'] = [
'real' => [
'value' => $totalBalanceReal->getAmount(),
'readable' => $totalBalanceReal->toString(),
], 'expected' => [
'value' => $totalBalanceExpected->getAmount(),
'readable' => $totalBalanceExpected->toString(),
],
'estimated' => [
'value' => $totalBalanceEstimated->getAmount(),
'readable' => $totalBalanceEstimated->toString(),
]
];
$returnArray['flatSupport'] = $this->event->support_flat->toString();
$returnArray['flatSupportEdit'] = $this->event->support_flat->getFormattedAmount();
$returnArray['supportPersonEdit'] = $this->event->support_per_person->getFormattedAmount();
$returnArray['managers'] = $this->event->eventManagers()->get()->map(fn($user) => new UserResource($user))->toArray();
$returnArray['supportPersonCalced'] = $this->event->support_per_person->toString();
$returnArray['contributingLocalGroups'] = $this->event->localGroups
->map(fn ($localGroup) => new LocalGroupResource($localGroup)->toArray($request))
->toArray();
$returnArray['eatingHabits'] = $this->event->eatingHabits()->get()->map(
fn($eatingHabit) => new EatingHabitResource($eatingHabit))->toArray();
$returnArray['paymentMethods'] = $this->event->paymentMethods()->get()->map(
fn($paymentMethod) => new AvailablePaymentMethodResource($paymentMethod)->toArray($request))->toArray();
// Veranstaltungsspezifische Pflicht-Auswahlfragen (Teilnahmeoptionen); leeres Array => Schritt entfällt.
$returnArray['participationOptions'] = $this->event->participation_options ?? [];
// Buchbare Zusätze (Event-Addons); leeres Array => Schritt entfällt. Preis 0 => "Kostenfrei".
$returnArray['addons'] = collect($this->event->addons ?? [])->map(function ($addon) {
$price = (float)($addon['price'] ?? 0);
return [
'key' => $addon['key'] ?? '',
'title' => $addon['title'] ?? '',
'description' => $addon['description'] ?? '',
'price' => $price,
'priceReadable' => new Amount($price, 'Euro')->toString(),
'free' => $price <= 0,
'flat' => (bool)($addon['flat'] ?? false),
];
})->toArray();
$returnArray['participationTypes'] = [];
$multiplier = $this->getMultiplier();
for ($i = 1; $i <= 4; $i++) {
$returnArray['participationFee_' . $i] = [
'active' => false,
'name' => '',
'description' => '',
'amount_standard' => null,
'amount_reduced' => null,
'amount_solidarity' => null,
'type' => null,
];
if ($this->event->{'participation_fee_' . $i} === null) {
continue;
}
$participationFee = $this->event->{'participationFee' . $i}()->first();
$feeType = [
'active' => true,
'amount_standard_edit' => $participationFee->amount_standard->getFormattedAmount(),
'amount_standard' => [
'internal' => [
'amount' => $participationFee->amount_standard->getAmount(),
'currency' => $participationFee->amount_standard->getCurrency(),
],
'readable' => $participationFee->amount_standard->multiply($multiplier)->toString(),
],
'amount_reduced_edit' => $participationFee->amount_reduced === null ? null : $participationFee->amount_reduced->getFormattedAmount(),
'amount_reduced' => $participationFee->amount_reduced === null
? null
: [
'internal' => [
'amount' => $participationFee->amount_reduced->getAmount(),
'currency' => $participationFee->amount_reduced->getCurrency(),
],
'readable' => $participationFee->amount_reduced->multiply($multiplier)->toString(),
],
'amount_solidarity_edit' => $participationFee->amount_solidarity === null ? null : $participationFee->amount_solidarity->getFormattedAmount(),
'amount_solidarity' => $participationFee->amount_solidarity === null
? null
: [
'internal' => [
'amount' => $participationFee->amount_solidarity->getAmount(),
'currency' => $participationFee->amount_solidarity->getCurrency(),
],
'readable' => $participationFee->amount_solidarity->multiply($multiplier)->toString(),
],
'name' => $participationFee->name,
'description' => $participationFee->description,
'type' => (new ParticipationTypeResource($participationFee->type()->first()))->toArray($request),
];
$returnArray['participationFee_' . $i] = $feeType;
$returnArray['participationTypes'][] = $feeType;
}
return $returnArray;
}
public function getMultiplier() : float {
$earlyBirdEnd = $this->event->early_bird_end;
if ($earlyBirdEnd > now()) {
return 1;
}
return 1 + $this->event->early_bird_end_amount_increase / 100;
}
public function calculateSupportPerPerson(array $participants) : array {
if ($this->event->support_per_person === null) {
return [
'amount' => new Amount(0, 'Euro'),
'readable' => '0,00'
];
}
$amount = $this->event->support_per_person;
$multiplier = 0;
foreach ($participants as $participationType => $participantData) {
if (isset($participantData['participants']) && count($participantData['participants']) > 0) {
foreach ($participantData['participants'] as $participant) {
$multiplier += $participant->toResource()->toArray(new Request())['presenceDays']['support'];
}
}
}
$amount->multiply($multiplier);
return [
'amount' => $amount,
'readable' => $amount->toString()
];
}
public function calculateIncomes(array $participants, Amount $supportPerPerson) : array {
$expectedAmount = new Amount($supportPerPerson->getAmount(), 'Euro');
$expectedAmount->addAmount($this->event->support_flat);
$realAmount = new Amount($supportPerPerson->getAmount(), 'Euro');
$realAmount->addAmount($this->event->support_flat);
foreach ($participants as $participationType => $participantData) {
$expectedAmount->addAmount(new Amount($participantData['amount']['expected']['value'], 'Euro'));
$realAmount->addAmount(new Amount($participantData['amount']['paid']['value'], 'Euro'));
}
return ['real' => [
'amount' => $realAmount,
'readable' => $realAmount->toString()
],
'expected' => [
'amount' => $expectedAmount,
'readable' => $expectedAmount->toString(),
]
];
}
public function getParticipants(string $participationType) : array {
$returnData = [];
$returnData['amount'] = [
'expected' => new Amount(0, 'Euro'),
'paid' => new Amount(0, 'Euro')
];
$returnData['count'] = 0;
foreach ($this->event->participants()->where(
[
'participation_type' => $participationType,
'unregistered_at' => null
]
)->get() as $participant)
{
$returnData['count']++;
$returnData['participants'][] = $participant;
if ($participant->amount !== null) {
$returnData['amount']['expected'] = $returnData['amount']['expected']->addAmount($participant->amount);
}
if ($participant->amount_paid !== null) {
$returnData['amount']['paid'] = $returnData['amount']['paid']->addAmount($participant->amount_paid);
}
};
$returnData['amount']['expected'] =
[
'value' => $returnData['amount']['expected']->getAmount(),
'readable' => $returnData['amount']['expected']->toString()
];
$returnData['amount']['paid'] =
[
'value' => $returnData['amount']['paid']->getAmount(),
'readable' => $returnData['amount']['paid']->toString()
];
return $returnData;
}
public function calculateAmount(
string $participationType,
string $feeType,
DateTime $arrival,
DateTime $departure,
bool $hasSibling
) : Amount {
$fee = collect([
$this->event->participationFee1,
$this->event->participationFee2,
$this->event->participationFee3,
$this->event->participationFee4,
])->filter(fn ($participationFee) => $participationFee !== null)
->first(fn ($participationFee) => $participationFee->type === $participationType);
if ($fee === null) {
return new Amount(0, 'Euro');
}
/** @var Amount $basicFee */
$basicFee = $fee['amount_' . $feeType];
$basicFee = $basicFee->multiply($this->getMultiplier());
if ($this->event->pay_per_day) {
$days = $arrival->diff($departure)->days + 1;
$basicFee = $basicFee->multiply($days);
}
if ($hasSibling && $this->event->sibling_reduction) {
$basicFee = $basicFee->multiply(0.5);
}
// Bei bestehender USt-Pflicht und Netto-Preismodus die USt aufschlagen; gespeichert wird immer der
// finale Brutto-Betrag. Bei "inklusive" oder ohne Steuerpflicht bleibt der Beitrag unverändert.
if ($this->event->tax_liable && $this->event->vat_pricing_mode === VatPricingMode::AddOn->value) {
$basicFee = $basicFee->multiply(1 + $this->event->vat_rate / 100);
}
return new Amount(round($basicFee->getAmount(), 2), 'Euro');
}
/**
* Löst die gewählten Zusätze (Event-Addons) gegen die Event-Definition auf und berechnet je Position den
* Betrag: Pauschale => Preis, sonst Preis × Teilnahmetage. Bei USt-Pflicht + Netto-Preismodus (`add_on`)
* wird die USt aufgeschlagen (analog Beitrag); bei „inclusive"/keiner Pflicht bleibt der Preis unverändert.
* Kein Early-Bird-Aufschlag.
*
* @param array<int, string> $selectedKeys
* @return array{items: array<int, array{key: string, title: string, price: float, flat: bool, days: int, amount: float}>, total: Amount}
*/
public function resolveAddons(array $selectedKeys, DateTime $arrival, DateTime $departure): array
{
$days = $arrival->diff($departure)->days + 1;
$addsVat = $this->event->tax_liable && $this->event->vat_pricing_mode === VatPricingMode::AddOn->value;
$items = [];
$total = new Amount(0, 'Euro');
foreach ($this->event->addons ?? [] as $addon) {
$key = $addon['key'] ?? null;
if ($key === null || !in_array($key, $selectedKeys, true)) {
continue;
}
$price = (float)($addon['price'] ?? 0);
$flat = (bool)($addon['flat'] ?? false);
$usedDays = $flat ? 1 : $days;
$amount = $flat ? $price : $price * $days;
if ($addsVat) {
$amount *= 1 + $this->event->vat_rate / 100;
}
$amount = round($amount, 2);
$items[] = [
'key' => $key,
'title' => $addon['title'] ?? $key,
'price' => $price,
'flat' => $flat,
'days' => $usedDays,
'amount' => $amount,
];
$total->addAmount(new Amount($amount, 'Euro'));
}
return ['items' => $items, 'total' => $total];
}
}