wip: bulk insurance refactoring

This commit is contained in:
Björn Fromme
2026-03-16 11:59:11 +01:00
parent a3d7ee63d3
commit 1f5c835f8a
9 changed files with 359 additions and 51 deletions
@@ -15,6 +15,7 @@ use App\Form\Model\BankAccountDto;
use App\Form\Model\BookingDto;
use App\Form\Model\ParticipantDto;
use App\Service\InsuranceMatchingService;
use App\Service\InsuranceTypeFilterService;
/**
* Processes booking form data and converts it into BusProNet API payload format.
@@ -29,6 +30,7 @@ class BookingDataProcessor
{
public function __construct(
private readonly InsuranceMatchingService $insuranceMatchingService,
private readonly InsuranceTypeFilterService $insuranceTypeFilterService,
) {
}
@@ -1112,10 +1114,8 @@ class BookingDataProcessor
// Get all available insurances from travel data
$availableInsurances = $bookingDto->travel->insurances;
// Exclude complementary insurances from bulk assignment logic
// They are only available as part of packages and cannot be directly selected
$availableInsurances = array_filter($availableInsurances, fn ($insurance) => !$insurance->complementary);
$availableInsurances = array_values($availableInsurances);
// Exclude complementary insurances (only available as part of packages)
$availableInsurances = $this->insuranceTypeFilterService->filterNonComplementary($availableInsurances);
// Use InsuranceMatchingService for proper type-based assignment with price tier matching
$assignments = $this->insuranceMatchingService->batchAssignInsuranceToParticipants(
@@ -13,8 +13,8 @@ use App\Form\Model\BookingDto;
use App\Form\Service\Abstract\AbstractFieldOptionsProvider;
use App\Form\Service\Factory\ParticipantRoomChoiceLoaderFactory;
use App\Service\InsuranceMatchingService;
use App\Service\InsuranceTypeFilterService;
use App\Service\ServiceAvailabilityCalculator;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
/**
* Provides dynamic field options for participant form fields.
@@ -51,6 +51,7 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
private readonly ParticipantRoomChoiceLoaderFactory $roomChoiceLoaderFactory,
private readonly ServiceAvailabilityCalculator $serviceAvailabilityCalculator,
private readonly InsuranceMatchingService $insuranceMatchingService,
private readonly InsuranceTypeFilterService $insuranceTypeFilterService,
) {
parent::__construct();
}
@@ -757,9 +758,8 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
$availableInsurances = $bookingDto->travel->insurances ?? [];
// Exclude complementary insurances from standalone selection
// They are only available as part of packages
$availableInsurances = array_filter($availableInsurances, fn ($insurance) => !$insurance->complementary);
// Exclude complementary insurances (only available as part of packages)
$availableInsurances = $this->insuranceTypeFilterService->filterNonComplementary($availableInsurances);
// Use insurance matching service to filter based on eligibility criteria
return $this->insuranceMatchingService->getEligibleInsurances(
@@ -8,6 +8,7 @@ use App\BusProNet\Model\Insurance;
use App\Form\Model\BookingDto;
use App\Form\Service\Abstract\AbstractParticipantFieldHandler;
use App\Service\InsuranceMatchingService;
use App\Service\InsuranceTypeFilterService;
/**
* Handles processing of the insurance field for booking participants.
@@ -35,6 +36,7 @@ class ParticipantInsuranceFieldHandler extends AbstractParticipantFieldHandler
{
public function __construct(
private readonly InsuranceMatchingService $insuranceMatchingService,
private readonly InsuranceTypeFilterService $insuranceTypeFilterService,
) {
}
@@ -94,7 +96,7 @@ class ParticipantInsuranceFieldHandler extends AbstractParticipantFieldHandler
public function shouldProcess(array $submittedData, string $mode, int $participantIndex): bool
{
// In edit mode, insurance data is not available from API - skip processing entirely
if ($mode === BookingDto::MODE_EDIT) {
if (BookingDto::MODE_EDIT === $mode) {
return false;
}
@@ -118,7 +120,7 @@ class ParticipantInsuranceFieldHandler extends AbstractParticipantFieldHandler
* participant data and automatically reassigns to the correct price tier if needed.
*
* @param array<string, mixed> $submittedData The submitted participant form data
* @param BookingDto $bookingDto The booking DTO to update (create or edit)
* @param BookingDto $bookingDto The booking DTO to update (create or edit)
* @param int $participantIndex The index of the participant being processed
*/
public function processField(array $submittedData, BookingDto $bookingDto, int $participantIndex): void
@@ -132,9 +134,8 @@ class ParticipantInsuranceFieldHandler extends AbstractParticipantFieldHandler
$currentInsurance = $participant->insurance;
$availableInsurances = $bookingDto->travel->insurances ?? [];
// Exclude complementary insurances from reassignment logic
// They are only available as part of packages and cannot be directly selected
$availableInsurances = array_filter($availableInsurances, fn ($insurance) => !$insurance->complementary);
// Exclude complementary insurances (only available as part of packages)
$availableInsurances = $this->insuranceTypeFilterService->filterNonComplementary($availableInsurances);
// Determine if this is a new user selection or just form resubmission
$isNewSelection = null !== $selectedInsuranceId
@@ -214,7 +215,7 @@ class ParticipantInsuranceFieldHandler extends AbstractParticipantFieldHandler
* Currently no field state modifications are needed for insurance selection.
*
* @param array<string, mixed> $submittedData The submitted participant form data
* @param BookingDto $bookingDto The booking DTO (potentially modified by processing)
* @param BookingDto $bookingDto The booking DTO (potentially modified by processing)
* @param int $participantIndex The participant index being processed
*
* @return array<string, array<string, mixed>> Empty array - no field state modifications
@@ -239,8 +240,8 @@ class ParticipantInsuranceFieldHandler extends AbstractParticipantFieldHandler
/**
* Finds an insurance by ID from the available insurances array.
*
* @param array<Insurance> $insurances Array of available insurances
* @param string $insuranceId The insurance ID to find
* @param array<Insurance> $insurances Array of available insurances
* @param string $insuranceId The insurance ID to find
*
* @return Insurance|null The found insurance or null if not found
*/
@@ -280,8 +281,8 @@ class ParticipantInsuranceFieldHandler extends AbstractParticipantFieldHandler
* This is used to distinguish between a new user selection and a form resubmission
* with the existing insurance selection (e.g., when user adds rentals that change travel price).
*
* @param string $selectedInsuranceId The insurance ID from form submission
* @param Insurance $currentInsurance The currently assigned insurance from DTO
* @param string $selectedInsuranceId The insurance ID from form submission
* @param Insurance $currentInsurance The currently assigned insurance from DTO
*
* @return bool True if they represent the same insurance
*/
@@ -1,28 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Model;
use App\Form\Model\BookingDto;
use App\Form\Model\ParticipantDto;
/**
* Value object containing criteria for insurance eligibility evaluation.
*
* Groups all the parameters needed to determine if an insurance product
* is eligible for a specific participant and booking scenario.
*/
readonly class InsuranceEligibilityCriteria
{
public function __construct(
public ParticipantDto $participant,
public \DateTimeImmutable $travelStartDate,
public \DateTimeImmutable $travelEndDate,
public \DateTimeImmutable $bookingDate,
public float $travelPrice,
public int $travelDurationDays,
public BookingDto $booking,
) {
}
}
@@ -774,9 +774,8 @@ class BookingPriceCalculatorService
// 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);
// Exclude complementary insurances (only available as part of packages)
$availableInsurances = $this->insuranceTypeFilterService->filterNonComplementary($availableInsurances);
// Get insurances of the same type as applicant's selection
$sameTypeInsurances = $this->insuranceTypeFilterService->filterByType(
@@ -11,6 +11,23 @@ use App\BusProNet\Model\Insurance;
*/
class InsuranceTypeFilterService
{
/**
* Filters out complementary insurances from an insurance array.
*
* Complementary insurances are only available as part of packages
* and cannot be directly selected by users.
*
* @param array<Insurance> $insurances Array of insurances to filter
*
* @return array<Insurance> Array containing only non-complementary insurances with reset keys
*/
public function filterNonComplementary(array $insurances): array
{
return array_values(
array_filter($insurances, fn (Insurance $insurance) => false === $insurance->complementary)
);
}
/**
* Filters insurances by type based on label (for packages) or subType (for individual insurances).
*