106 lines
4.5 KiB
PHP
106 lines
4.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Service;
|
|
|
|
use App\Form\Model\BookingDto;
|
|
|
|
/**
|
|
* Propagates the applicant's insurance selection to the other participants.
|
|
*
|
|
* This runs during the request cycle rather than only at payload build time, so the
|
|
* session DTO always reflects the coverage that will actually be booked. Every consumer
|
|
* that reads ParticipantDto::$insurance - the sidebar summary, the price totals, the
|
|
* voucher math - is then correct by construction, instead of having to re-derive the
|
|
* effective insurance for itself.
|
|
*/
|
|
class ApplicantInsuranceCascade
|
|
{
|
|
public function __construct(
|
|
private readonly InsuranceManager $insuranceService,
|
|
private readonly BookingPriceCalculator $priceCalculatorService,
|
|
) {
|
|
}
|
|
|
|
/**
|
|
* Applies the applicant's insurance to other participants if their selection requires it.
|
|
*
|
|
* Runs when either:
|
|
* - Bulk insurance is active (applicant's bulkInsuranceBooking = true): assigns the
|
|
* applicant's insurance TYPE to all dependent participants with automatic price tier
|
|
* adjustment based on each participant's individual cost.
|
|
* - The applicant selected a family insurance: family insurance covers the whole family
|
|
* under a single policy, so dependents must never hold their own insurance, regardless
|
|
* of whether the bulk checkbox was checked.
|
|
*
|
|
* IMPORTANT: In edit mode, non-family bulk insurance only applies to participants who:
|
|
* 1. Currently have NO insurance assigned (insurance === null)
|
|
* 2. OR whose insurance was already assigned via previous bulk operation
|
|
*
|
|
* This prevents overriding individually selected insurances that are locked. Family
|
|
* insurance is exempt from this protection: dependents can never legitimately hold
|
|
* their own policy alongside it, so their insurance is always cleared, in edit mode too.
|
|
*
|
|
* The assignment is deterministic, so applying it repeatedly across successive requests
|
|
* converges on the same result.
|
|
*
|
|
* @param BookingDto $bookingDto The booking DTO (create or edit flow)
|
|
*/
|
|
public function apply(BookingDto $bookingDto): void
|
|
{
|
|
$applicant = $bookingDto->getParticipant(0);
|
|
|
|
if (null === $applicant || null === $applicant->insurance) {
|
|
return;
|
|
}
|
|
|
|
$isFamilyInsurance = true === $applicant->insurance->familyInsurance;
|
|
|
|
// Check if bulk insurance is enabled OR the applicant's selection is a family
|
|
// insurance (which always applies to dependents, independent of the bulk checkbox)
|
|
if (false === $applicant->bulkInsuranceBooking && false === $isFamilyInsurance) {
|
|
return;
|
|
}
|
|
|
|
// Get selectable (non-complementary) insurances with caching
|
|
$selectableInsurances = $this->insuranceService->getSelectableInsurances($bookingDto->travel);
|
|
|
|
// Calculate travel prices for all participants (excluding insurance)
|
|
$participantPrices = [];
|
|
foreach ($bookingDto->getParticipants() as $index => $participant) {
|
|
$participantPrices[$index] = $this->priceCalculatorService->calculateIndividualParticipantPriceExcludingInsurance($bookingDto, $index);
|
|
}
|
|
|
|
// Use InsuranceManager for proper type-based assignment with price tier matching
|
|
$assignments = $this->insuranceService->batchAssignInsuranceToParticipants(
|
|
$selectableInsurances,
|
|
$applicant->insurance,
|
|
$bookingDto,
|
|
$participantPrices
|
|
);
|
|
|
|
// Apply assignments to all participants; for non-family insurance skip the applicant
|
|
// (they already hold the insurance they selected themselves)
|
|
foreach ($assignments as $index => $insurance) {
|
|
if (0 === $index && false === $isFamilyInsurance) {
|
|
continue;
|
|
}
|
|
|
|
$participant = $bookingDto->getParticipant($index);
|
|
if (null === $participant) {
|
|
continue;
|
|
}
|
|
|
|
// In edit mode: non-family bulk assignment must not overwrite an individually
|
|
// locked insurance. Family insurance always overrides, since dependents can
|
|
// never legitimately keep their own policy alongside it.
|
|
if (BookingDto::MODE_EDIT === $bookingDto->getMode() && false === $isFamilyInsurance && null !== $participant->insurance) {
|
|
continue; // Skip participants with existing insurance assignment
|
|
}
|
|
|
|
$participant->insurance = $insurance;
|
|
}
|
|
}
|
|
}
|