wip: bulk insurance booking fix
This commit is contained in:
@@ -22,6 +22,8 @@ class BookingPriceCalculatorService
|
||||
{
|
||||
public function __construct(
|
||||
private readonly ParticipantEligibilityService $participantEligibilityService,
|
||||
private readonly InsuranceTypeFilterService $insuranceTypeFilterService,
|
||||
private readonly InsuranceEligibilityService $insuranceEligibilityService,
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -173,7 +175,7 @@ class BookingPriceCalculatorService
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->aggregateParticipantServices($participant, $serviceAggregation);
|
||||
$this->aggregateParticipantServices($participant, $serviceAggregation, $bookingDto);
|
||||
}
|
||||
|
||||
// Add transportation services as separate line items (Zustieg, Rabatt, Parkplatz)
|
||||
@@ -518,8 +520,11 @@ class BookingPriceCalculatorService
|
||||
/**
|
||||
* Aggregates service selections from a single participant into the service aggregation array.
|
||||
*/
|
||||
private function aggregateParticipantServices(ParticipantDto $participant, array &$serviceAggregation): void
|
||||
{
|
||||
private function aggregateParticipantServices(
|
||||
ParticipantDto $participant,
|
||||
array &$serviceAggregation,
|
||||
BookingDto $bookingDto,
|
||||
): void {
|
||||
// Handle single service selections (skiPass, rentalInsurance)
|
||||
if (null !== $participant->skiPass && null !== $participant->skiPass->price) {
|
||||
$this->addToServiceAggregation($serviceAggregation, $participant->skiPass, 1);
|
||||
@@ -529,8 +534,11 @@ class BookingPriceCalculatorService
|
||||
$this->addToServiceAggregation($serviceAggregation, $participant->rentalInsurance, 1);
|
||||
}
|
||||
|
||||
if (null !== $participant->insurance && null !== $participant->insurance->price) {
|
||||
$this->addInsuranceToServiceAggregation($serviceAggregation, $participant->insurance, 1);
|
||||
// Resolve insurance: use price-tier-adjusted insurance for bulk assignment
|
||||
$insuranceToAggregate = $this->resolveInsuranceForAggregation($participant, $bookingDto);
|
||||
|
||||
if (null !== $insuranceToAggregate && null !== $insuranceToAggregate->price) {
|
||||
$this->addInsuranceToServiceAggregation($serviceAggregation, $insuranceToAggregate, 1);
|
||||
}
|
||||
|
||||
// Handle multiple service selections
|
||||
@@ -732,4 +740,62 @@ class BookingPriceCalculatorService
|
||||
// Bulk insurance is active - use applicant's insurance for dependent participants
|
||||
return $applicant->insurance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the insurance to use for aggregation, handling bulk insurance with price tiers.
|
||||
*
|
||||
* When bulk insurance is active, dependent participants get price-tier-adjusted insurance
|
||||
* based on their individual travel price, matching the logic used in API submission.
|
||||
*
|
||||
* @param ParticipantDto $participant The participant to resolve insurance for
|
||||
* @param BookingDto $bookingDto The booking context
|
||||
*
|
||||
* @return Insurance|null The insurance to aggregate (null if none)
|
||||
*/
|
||||
private function resolveInsuranceForAggregation(ParticipantDto $participant, BookingDto $bookingDto): ?Insurance
|
||||
{
|
||||
// If participant already has insurance assigned, use it
|
||||
if (null !== $participant->insurance) {
|
||||
return $participant->insurance;
|
||||
}
|
||||
|
||||
// Check if bulk insurance is active
|
||||
$applicant = $bookingDto->getParticipant(0);
|
||||
if (null === $applicant || false === $applicant->bulkInsuranceBooking || null === $applicant->insurance) {
|
||||
return null; // No bulk insurance active
|
||||
}
|
||||
|
||||
// Applicant always uses their own insurance
|
||||
if (0 === $participant->index) {
|
||||
return $participant->insurance;
|
||||
}
|
||||
|
||||
// For dependent participants: calculate price-tier-adjusted insurance
|
||||
// This mirrors the logic in BookingDataProcessor::applyBulkInsuranceIfActive()
|
||||
$availableInsurances = $bookingDto->travel->insurances;
|
||||
|
||||
// Exclude complementary insurances
|
||||
$availableInsurances = array_filter($availableInsurances, fn ($insurance) => false === $insurance->complementary);
|
||||
$availableInsurances = array_values($availableInsurances);
|
||||
|
||||
// Get insurances of the same type as applicant's selection
|
||||
$sameTypeInsurances = $this->insuranceTypeFilterService->filterByType(
|
||||
$availableInsurances,
|
||||
$applicant->insurance
|
||||
);
|
||||
|
||||
// Calculate participant's travel price (excluding insurance)
|
||||
$travelPrice = $this->calculateIndividualParticipantPriceExcludingInsurance($bookingDto, $participant->index);
|
||||
|
||||
// Get eligible insurances for THIS participant (price tier adjusted)
|
||||
$eligibleInsurances = $this->insuranceEligibilityService->getEligibleInsurances(
|
||||
$sameTypeInsurances,
|
||||
$participant,
|
||||
$bookingDto,
|
||||
$travelPrice
|
||||
);
|
||||
|
||||
// Return first eligible insurance (sorted by price)
|
||||
return !empty($eligibleInsurances) ? array_values($eligibleInsurances)[0] : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use App\BusProNet\Model\Insurance;
|
||||
use App\BusProNet\Traits\SortByPriceTrait;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Model\ParticipantDto;
|
||||
use Carbon\Carbon;
|
||||
|
||||
/**
|
||||
* Service for evaluating insurance eligibility without circular dependencies.
|
||||
*
|
||||
* This service contains the core eligibility logic that can be used by both
|
||||
* InsuranceMatchingService and BookingPriceCalculatorService.
|
||||
*/
|
||||
class InsuranceEligibilityService
|
||||
{
|
||||
use SortByPriceTrait;
|
||||
|
||||
/**
|
||||
* Filters insurances based on eligibility criteria.
|
||||
*
|
||||
* @param array<Insurance> $insurances Available insurances to filter
|
||||
* @param ParticipantDto $participant The participant to match insurances for
|
||||
* @param BookingDto $booking The booking context
|
||||
* @param float $travelPrice The participant's travel price (excluding insurance)
|
||||
*
|
||||
* @return array<Insurance> Filtered array of eligible insurances, sorted by price
|
||||
*/
|
||||
public function getEligibleInsurances(
|
||||
array $insurances,
|
||||
ParticipantDto $participant,
|
||||
BookingDto $booking,
|
||||
float $travelPrice,
|
||||
): array {
|
||||
$travelStartDate = $booking->travel->dateFrom;
|
||||
$travelEndDate = $booking->travel->dateTo;
|
||||
|
||||
if (null === $travelStartDate || null === $travelEndDate) {
|
||||
return []; // Cannot evaluate without travel dates
|
||||
}
|
||||
|
||||
$bookingDate = Carbon::now()->toDateTimeImmutable();
|
||||
$travelDurationDays = $travelStartDate->diff($travelEndDate)->days;
|
||||
|
||||
$eligibleInsurances = array_filter(
|
||||
$insurances,
|
||||
fn (Insurance $insurance) => $this->isInsuranceEligible(
|
||||
$insurance,
|
||||
$participant,
|
||||
$booking,
|
||||
$travelStartDate,
|
||||
$travelEndDate,
|
||||
$bookingDate,
|
||||
$travelPrice,
|
||||
$travelDurationDays
|
||||
)
|
||||
);
|
||||
|
||||
return $this->sortByPrice($eligibleInsurances);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a specific insurance is eligible for given criteria.
|
||||
*/
|
||||
private function isInsuranceEligible(
|
||||
Insurance $insurance,
|
||||
ParticipantDto $participant,
|
||||
BookingDto $booking,
|
||||
\DateTimeImmutable $travelStartDate,
|
||||
\DateTimeImmutable $travelEndDate,
|
||||
\DateTimeImmutable $bookingDate,
|
||||
float $travelPrice,
|
||||
int $travelDurationDays,
|
||||
): bool {
|
||||
// Family insurance constraints
|
||||
if (false === $this->checkFamilyInsuranceConstraints($insurance, $booking)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Age constraints
|
||||
if (false === $this->checkAgeConstraints($insurance, $participant, $travelStartDate)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Travel date constraints
|
||||
if (false === $this->checkTravelDateConstraints($insurance, $travelStartDate, $travelEndDate)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Booking date constraints
|
||||
if (false === $this->checkBookingDateConstraints($insurance, $bookingDate)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Travel price constraints
|
||||
if (false === $this->checkTravelPriceConstraints($insurance, $travelPrice)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Travel duration constraints
|
||||
if (false === $this->checkTravelDurationConstraints($insurance, $travelDurationDays)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function checkFamilyInsuranceConstraints(Insurance $insurance, 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;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function checkAgeConstraints(Insurance $insurance, ParticipantDto $participant, \DateTimeImmutable $travelStartDate): bool
|
||||
{
|
||||
$participantAge = $participant->getAge($travelStartDate);
|
||||
|
||||
// If no birth date is provided, skip age constraints (field will be hidden via field state conditions)
|
||||
if (null === $participantAge) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check minimum age
|
||||
if (null !== $insurance->ageFrom && $participantAge < $insurance->ageFrom) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check maximum age
|
||||
if (null !== $insurance->ageTo && $participantAge > $insurance->ageTo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function checkTravelDateConstraints(Insurance $insurance, \DateTimeImmutable $travelStartDate, \DateTimeImmutable $travelEndDate): bool
|
||||
{
|
||||
// Check travel start date
|
||||
if (null !== $insurance->travelDateFrom && $travelStartDate < $insurance->travelDateFrom) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (null !== $insurance->travelDateTo && $travelStartDate > $insurance->travelDateTo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check travel end date
|
||||
if (null !== $insurance->travelDateTo && $travelEndDate > $insurance->travelDateTo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function checkBookingDateConstraints(Insurance $insurance, \DateTimeImmutable $bookingDate): bool
|
||||
{
|
||||
// Check booking window start
|
||||
if (null !== $insurance->bookingDateFrom && $bookingDate < $insurance->bookingDateFrom) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check booking window end
|
||||
if (null !== $insurance->bookingDateTo && $bookingDate > $insurance->bookingDateTo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function checkTravelPriceConstraints(Insurance $insurance, float $travelPrice): bool
|
||||
{
|
||||
// Check minimum price
|
||||
if (null !== $insurance->travelPriceFrom && $travelPrice < $insurance->travelPriceFrom) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check maximum price
|
||||
if (null !== $insurance->travelPriceTo && $travelPrice > $insurance->travelPriceTo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function checkTravelDurationConstraints(Insurance $insurance, int $travelDurationDays): bool
|
||||
{
|
||||
// Check minimum duration
|
||||
if (null !== $insurance->travelDurationFrom && $travelDurationDays < $insurance->travelDurationFrom) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check maximum duration
|
||||
if (null !== $insurance->travelDurationTo && $travelDurationDays > $insurance->travelDurationTo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,6 @@ use App\BusProNet\Model\Insurance;
|
||||
use App\BusProNet\Traits\SortByPriceTrait;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Model\ParticipantDto;
|
||||
use App\Model\InsuranceEligibilityCriteria;
|
||||
use Carbon\Carbon;
|
||||
|
||||
/**
|
||||
* Service for matching insurances to participants based on eligibility criteria.
|
||||
@@ -24,6 +22,8 @@ class InsuranceMatchingService
|
||||
|
||||
public function __construct(
|
||||
private readonly BookingPriceCalculatorService $priceCalculatorService,
|
||||
private readonly InsuranceTypeFilterService $insuranceTypeFilterService,
|
||||
private readonly InsuranceEligibilityService $insuranceEligibilityService,
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -38,18 +38,16 @@ class InsuranceMatchingService
|
||||
*/
|
||||
public function getEligibleInsurances(array $insurances, ParticipantDto $participant, BookingDto $booking): array
|
||||
{
|
||||
$criteria = $this->createEligibilityCriteria($participant, $booking);
|
||||
// Calculate travel price for this participant
|
||||
$travelPrice = $this->calculateTravelPrice($booking, $participant->index);
|
||||
|
||||
if (null === $criteria) {
|
||||
return []; // Cannot match insurances without travel dates
|
||||
}
|
||||
|
||||
$eligibleInsurances = array_filter(
|
||||
// Delegate to InsuranceEligibilityService
|
||||
return $this->insuranceEligibilityService->getEligibleInsurances(
|
||||
$insurances,
|
||||
fn (Insurance $insurance) => $this->isInsuranceEligible($insurance, $criteria)
|
||||
$participant,
|
||||
$booking,
|
||||
$travelPrice
|
||||
);
|
||||
|
||||
return $this->sortByPrice($eligibleInsurances);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,7 +67,7 @@ class InsuranceMatchingService
|
||||
public function reassignInsuranceForPriceChange(array $availableInsurances, Insurance $currentInsurance, ParticipantDto $participant, BookingDto $booking): ?Insurance
|
||||
{
|
||||
// Group insurances of the same type
|
||||
$sameTypeInsurances = $this->filterInsurancesByType($availableInsurances, $currentInsurance);
|
||||
$sameTypeInsurances = $this->insuranceTypeFilterService->filterByType($availableInsurances, $currentInsurance);
|
||||
|
||||
// Get eligible insurances for this participant
|
||||
$eligibleInsurances = $this->getEligibleInsurances($sameTypeInsurances, $participant, $booking);
|
||||
@@ -102,7 +100,7 @@ class InsuranceMatchingService
|
||||
$assignments = [];
|
||||
|
||||
// Group insurances of the same type
|
||||
$sameTypeInsurances = $this->filterInsurancesByType($availableInsurances, $selectedInsurance);
|
||||
$sameTypeInsurances = $this->insuranceTypeFilterService->filterByType($availableInsurances, $selectedInsurance);
|
||||
|
||||
// Assign appropriate insurance to each participant
|
||||
foreach ($booking->getParticipants() as $index => $participant) {
|
||||
@@ -113,200 +111,6 @@ class InsuranceMatchingService
|
||||
return $assignments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates eligibility criteria from participant and booking data.
|
||||
*
|
||||
* This method performs early validation before creating the criteria object
|
||||
* to avoid unnecessary object instantiation when criteria cannot be satisfied.
|
||||
*/
|
||||
private function createEligibilityCriteria(ParticipantDto $participant, BookingDto $booking): ?InsuranceEligibilityCriteria
|
||||
{
|
||||
// Early return if travel dates are missing - cannot evaluate any criteria
|
||||
$travelStartDate = $booking->travel->dateFrom;
|
||||
$travelEndDate = $booking->travel->dateTo;
|
||||
|
||||
if (null === $travelStartDate || null === $travelEndDate) {
|
||||
return null; // Cannot create criteria without travel dates
|
||||
}
|
||||
|
||||
return new InsuranceEligibilityCriteria(
|
||||
participant: $participant,
|
||||
travelStartDate: $travelStartDate,
|
||||
travelEndDate: $travelEndDate,
|
||||
bookingDate: Carbon::now()->toDateTimeImmutable(),
|
||||
travelPrice: $this->calculateTravelPrice($booking, $participant->index),
|
||||
travelDurationDays: $this->calculateTravelDurationDays($travelStartDate, $travelEndDate),
|
||||
booking: $booking,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a specific insurance is eligible for given criteria.
|
||||
*/
|
||||
private function isInsuranceEligible(Insurance $insurance, InsuranceEligibilityCriteria $criteria): bool
|
||||
{
|
||||
// Family insurance constraints
|
||||
if (false === $this->checkFamilyInsuranceConstraints($insurance, $criteria->booking)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Age constraints
|
||||
if (false === $this->checkAgeConstraints($insurance, $criteria->participant, $criteria->travelStartDate)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Travel date constraints
|
||||
if (false === $this->checkTravelDateConstraints($insurance, $criteria->travelStartDate, $criteria->travelEndDate)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Booking date constraints
|
||||
if (false === $this->checkBookingDateConstraints($insurance, $criteria->bookingDate)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Travel price constraints
|
||||
if (false === $this->checkTravelPriceConstraints($insurance, $criteria->travelPrice)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Travel duration constraints
|
||||
if (false === $this->checkTravelDurationConstraints($insurance, $criteria->travelDurationDays)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if family insurance constraints are met.
|
||||
*
|
||||
* Family insurances should only be available for family bookings,
|
||||
* and individual insurances should only be available for non-family bookings.
|
||||
*/
|
||||
private function checkFamilyInsuranceConstraints(Insurance $insurance, 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;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if participant age meets insurance age constraints.
|
||||
*/
|
||||
private function checkAgeConstraints(Insurance $insurance, ParticipantDto $participant, \DateTimeImmutable $travelStartDate): bool
|
||||
{
|
||||
$participantAge = $participant->getAge($travelStartDate);
|
||||
|
||||
// If no birth date is provided, skip age constraints (field will be hidden via field state conditions)
|
||||
if (null === $participantAge) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check minimum age
|
||||
if (null !== $insurance->ageFrom && $participantAge < $insurance->ageFrom) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check maximum age
|
||||
if (null !== $insurance->ageTo && $participantAge > $insurance->ageTo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if travel dates fall within insurance validity period.
|
||||
*/
|
||||
private function checkTravelDateConstraints(Insurance $insurance, \DateTimeImmutable $travelStartDate, \DateTimeImmutable $travelEndDate): bool
|
||||
{
|
||||
// Check travel start date
|
||||
if (null !== $insurance->travelDateFrom && $travelStartDate < $insurance->travelDateFrom) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (null !== $insurance->travelDateTo && $travelStartDate > $insurance->travelDateTo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check travel end date
|
||||
if (null !== $insurance->travelDateTo && $travelEndDate > $insurance->travelDateTo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if booking date falls within insurance booking window.
|
||||
*/
|
||||
private function checkBookingDateConstraints(Insurance $insurance, \DateTimeImmutable $bookingDate): bool
|
||||
{
|
||||
// Check booking window start
|
||||
if (null !== $insurance->bookingDateFrom && $bookingDate < $insurance->bookingDateFrom) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check booking window end
|
||||
if (null !== $insurance->bookingDateTo && $bookingDate > $insurance->bookingDateTo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if travel price falls within insurance price range.
|
||||
*/
|
||||
private function checkTravelPriceConstraints(Insurance $insurance, float $travelPrice): bool
|
||||
{
|
||||
// Check minimum price
|
||||
if (null !== $insurance->travelPriceFrom && $travelPrice < $insurance->travelPriceFrom) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check maximum price
|
||||
if (null !== $insurance->travelPriceTo && $travelPrice > $insurance->travelPriceTo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if travel duration falls within insurance duration limits.
|
||||
*/
|
||||
private function checkTravelDurationConstraints(Insurance $insurance, int $travelDurationDays): bool
|
||||
{
|
||||
// Check minimum duration
|
||||
if (null !== $insurance->travelDurationFrom && $travelDurationDays < $insurance->travelDurationFrom) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check maximum duration
|
||||
if (null !== $insurance->travelDurationTo && $travelDurationDays > $insurance->travelDurationTo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the total travel price for a participant, excluding insurance prices.
|
||||
*
|
||||
@@ -324,54 +128,4 @@ class InsuranceMatchingService
|
||||
// Use the price calculator to get the participant's individual price excluding insurance
|
||||
return $this->priceCalculatorService->calculateIndividualParticipantPriceExcludingInsurance($booking, $participantIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates travel duration in days.
|
||||
*
|
||||
* @param \DateTimeImmutable $startDate Travel start date
|
||||
* @param \DateTimeImmutable $endDate Travel end date
|
||||
*
|
||||
* @return int Duration in days
|
||||
*/
|
||||
private function calculateTravelDurationDays(\DateTimeImmutable $startDate, \DateTimeImmutable $endDate): int
|
||||
{
|
||||
return $startDate->diff($endDate)->days;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters insurances by type based on label (for packages) or subType (for individual insurances).
|
||||
*
|
||||
* This method groups insurances of the same type together for reassignment or batch assignment.
|
||||
* Insurance type matching strategy:
|
||||
* - **Packages**: Match by label + familyInsurance (packages with same label are different price tiers)
|
||||
* - **Individual insurances**: Match by subType + familyInsurance
|
||||
*
|
||||
* Example: "Reise-Rücktritt + Selbstbehaltübernahme" at €10, €14, €26 are the same type,
|
||||
* but different from "Reiseschutz Platin Auto/Bahn/Bus (Europa) + Selbstbehaltübernahme".
|
||||
*
|
||||
* @param array<Insurance> $insurances All available insurances to filter
|
||||
* @param Insurance $referenceInsurance The insurance to match against
|
||||
*
|
||||
* @return array<Insurance> Filtered insurances of the same type
|
||||
*/
|
||||
private function filterInsurancesByType(array $insurances, Insurance $referenceInsurance): array
|
||||
{
|
||||
// For packages, match by label (packages with same label are different price tiers of same type)
|
||||
if (true === $referenceInsurance->package) {
|
||||
return array_filter(
|
||||
$insurances,
|
||||
fn (Insurance $insurance) => true === $insurance->package
|
||||
&& $insurance->label === $referenceInsurance->label
|
||||
&& $insurance->familyInsurance === $referenceInsurance->familyInsurance
|
||||
);
|
||||
}
|
||||
|
||||
// For individual insurances, match by subType
|
||||
return array_filter(
|
||||
$insurances,
|
||||
fn (Insurance $insurance) => false === $insurance->package
|
||||
&& $insurance->subType === $referenceInsurance->subType
|
||||
&& $insurance->familyInsurance === $referenceInsurance->familyInsurance
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use App\BusProNet\Model\Insurance;
|
||||
|
||||
/**
|
||||
* Service for filtering insurances by type without circular dependencies.
|
||||
*/
|
||||
class InsuranceTypeFilterService
|
||||
{
|
||||
/**
|
||||
* Filters insurances by type based on label (for packages) or subType (for individual insurances).
|
||||
*
|
||||
* This method groups insurances of the same type together for reassignment or batch assignment.
|
||||
* Insurance type matching strategy:
|
||||
* - **Packages**: Match by label + familyInsurance (packages with same label are different price tiers)
|
||||
* - **Individual insurances**: Match by subType + familyInsurance
|
||||
*
|
||||
* Example: "Reise-Rücktritt + Selbstbehaltübernahme" at €10, €14, €26 are the same type,
|
||||
* but different from "Reiseschutz Platin Auto/Bahn/Bus (Europa) + Selbstbehaltübernahme".
|
||||
*
|
||||
* @param array<Insurance> $insurances All available insurances to filter
|
||||
* @param Insurance $referenceInsurance The insurance to match against
|
||||
*
|
||||
* @return array<Insurance> Filtered insurances of the same type
|
||||
*/
|
||||
public function filterByType(array $insurances, Insurance $referenceInsurance): array
|
||||
{
|
||||
// For packages, match by label (packages with same label are different price tiers of same type)
|
||||
if (true === $referenceInsurance->package) {
|
||||
return array_filter(
|
||||
$insurances,
|
||||
fn (Insurance $insurance) => true === $insurance->package
|
||||
&& $insurance->label === $referenceInsurance->label
|
||||
&& $insurance->familyInsurance === $referenceInsurance->familyInsurance
|
||||
);
|
||||
}
|
||||
|
||||
// For individual insurances, match by subType
|
||||
return array_filter(
|
||||
$insurances,
|
||||
fn (Insurance $insurance) => false === $insurance->package
|
||||
&& $insurance->subType === $referenceInsurance->subType
|
||||
&& $insurance->familyInsurance === $referenceInsurance->familyInsurance
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user