fix: incorrect rules around family insurances

addresses #869dr80qj
This commit is contained in:
Björn Fromme committed 2026-08-05 15:11:37 +02:00
1 parent 736128476b
commit 8a46908856
15 files changed
+859 -50

No files matched your search

+64 -20
View File
@@ -107,6 +107,42 @@ class InsuranceManager
})();
}
/**
* Returns eligible insurances for a participant, pricing family and non-family
* insurances on the correct basis.
*
* Family insurances must be priced by the total booking price, not the individual
* participant's price - this splits the given insurances by type and evaluates
* each group against the appropriate price before merging the results back together.
*
* @param array<Insurance> $insurances Available insurances to filter
* @param ParticipantDto $participant The participant to match insurances for
* @param BookingDto $booking The booking context for additional criteria
* @param float $individualPrice The participant's individual travel price (excluding insurance)
* @param float $totalBookingPrice The total booking price across all participants (excluding insurance)
*
* @return array<Insurance> Filtered array of eligible insurances, sorted by price
*/
public function getEligibleInsurancesForParticipant(
array $insurances,
ParticipantDto $participant,
BookingDto $booking,
float $individualPrice,
float $totalBookingPrice,
): array {
$nonFamilyInsurances = array_values(array_filter($insurances, static fn (Insurance $i) => false === $i->familyInsurance));
$familyInsurances = array_values(array_filter($insurances, static fn (Insurance $i) => true === $i->familyInsurance));
$eligibleInsurances = $this->getEligibleInsurances($nonFamilyInsurances, $participant, $booking, $individualPrice);
if (!empty($familyInsurances)) {
$eligibleFamilyInsurances = $this->getEligibleInsurances($familyInsurances, $participant, $booking, $totalBookingPrice);
$eligibleInsurances = $this->sortByPrice(array_merge($eligibleInsurances, $eligibleFamilyInsurances));
}
return $eligibleInsurances;
}
/**
* Auto-reassigns an insurance to the same type with appropriate price tier.
*
@@ -159,12 +195,28 @@ class InsuranceManager
BookingDto $booking,
array $participantPrices,
): array {
$assignments = [];
// Group insurances of the same type
$sameTypeInsurances = $this->filterByType($availableInsurances, $selectedInsurance);
// Assign appropriate insurance to each participant
// Family insurance: assign to applicant only, priced by total booking price
if (true === $selectedInsurance->familyInsurance) {
$totalPrice = array_sum($participantPrices);
$applicant = $booking->getParticipant(0);
$eligibleInsurances = null !== $applicant
? $this->getEligibleInsurances($sameTypeInsurances, $applicant, $booking, $totalPrice)
: [];
$tieredInsurance = !empty($eligibleInsurances) ? array_values($eligibleInsurances)[0] : null;
$assignments = [];
foreach ($booking->getParticipants() as $index => $participant) {
$assignments[$index] = 0 === $index ? $tieredInsurance : null;
}
return $assignments;
}
// Non-family insurance: assign to each participant with their individual price tier
$assignments = [];
foreach ($booking->getParticipants() as $index => $participant) {
$travelPrice = $participantPrices[$index] ?? 0.0;
$eligibleInsurances = $this->getEligibleInsurances($sameTypeInsurances, $participant, $booking, $travelPrice);
@@ -242,7 +294,7 @@ class InsuranceManager
int $travelDurationDays,
): bool {
// Family insurance constraints
if (false === $this->checkFamilyInsuranceConstraints($insurance, $booking)) {
if (false === $this->checkFamilyInsuranceConstraints($insurance, $participant, $booking)) {
return false;
}
@@ -274,23 +326,15 @@ class InsuranceManager
return true;
}
private function checkFamilyInsuranceConstraints(Insurance $insurance, BookingDto $booking): bool
private function checkFamilyInsuranceConstraints(Insurance $insurance, ParticipantDto $participant, BookingDto $booking): bool
{
// Family booking detection only available in create mode
if (BookingDto::MODE_EDIT === $booking->getMode()) {
return true; // Skip family constraints for edit mode
}
$isFamilyBooking = $booking->isFamilyBooking();
// If it's a family insurance, it should only be available for family bookings
if (true === $insurance->familyInsurance && false === $isFamilyBooking) {
return false;
}
// If it's not a family insurance, it should only be available for non-family bookings
if (false === $insurance->familyInsurance && true === $isFamilyBooking) {
return false;
if (true === $insurance->familyInsurance) {
if (false === $booking->isFamilyBooking()) {
return false; // Family insurance only available for family bookings
}
if (0 !== $participant->index) {
return false; // Family insurance only assignable to the applicant
}
}
return true;