fix: reconcile assigned family insurance rate on updated participant

This commit is contained in:
Björn Fromme committed 2026-08-05 15:11:48 +02:00
1 parent 8a46908856
commit 0b642dcfe4
11 files changed
+723 -8

No files matched your search

@@ -6,6 +6,7 @@ namespace App\BusProNet\DataProcessor;
use App\BusProNet\Constants;
use App\BusProNet\Model\Booking;
use App\BusProNet\Model\Insurance;
use App\BusProNet\Model\Travel;
use App\BusProNet\Utility\DirectionMapper;
use App\BusProNet\XmlLoader\AgencyLoader;
@@ -362,12 +363,97 @@ class BookingDataProcessor
*/
public function createBookingRequestPayload(BookingDto $bookingDto, string $bookingType): array
{
// Re-validate each participant's insurance against current eligibility (price tier,
// family constraint, etc.) before submission - selections can go stale between the
// moment they were chosen and final submission, since users can freely revisit
// earlier steps and change composition/services without ever re-opening the
// insurance field. Must run before the applicant-to-dependents propagation below,
// so that propagation is based on an already-corrected applicant selection.
$this->reconcileInsuranceEligibility($bookingDto);
// Apply bulk or family insurance if applicable (modifies DTO in place)
$this->applyApplicantInsuranceToParticipants($bookingDto);
return $this->payloadBuilder->buildCreatePayload($bookingDto, $bookingType);
}
/**
* Re-validates every participant's currently selected insurance against present-day
* eligibility (price tier, family constraint, age, travel/booking dates, duration) and
* corrects any selection that's gone stale.
*
* Insurance eligibility is normally re-checked when a participant's own card is
* resubmitted (see ParticipantInsuranceFieldHandler::processField()), but nothing forces
* that to happen again if the user changes composition or services on an earlier step
* after insurance was already chosen, then jumps straight to submission. Left unchecked,
* a stale insurance ID (e.g. a family-tariff price tier that no longer matches the
* current total booking price) would reach the BPN API and be rejected as a mismatch.
*
* Mirrors the reassign-or-clear logic already established in
* ParticipantInsuranceFieldHandler for the resubmission case, applied here across all
* participants right before the payload is built.
*/
private function reconcileInsuranceEligibility(BookingDto $bookingDto): bool
{
$selectableInsurances = $this->insuranceService->getSelectableInsurances($bookingDto->travel);
$changed = false;
foreach ($bookingDto->getParticipants() as $index => $participant) {
$currentInsurance = $participant->insurance;
if (null === $currentInsurance || $currentInsurance->isNoInsurance()) {
continue;
}
$travelPrice = $this->priceCalculatorService->resolveInsuranceTravelPrice($bookingDto, $index, $currentInsurance);
$eligibleInsurances = $this->insuranceService->getEligibleInsurances($selectableInsurances, $participant, $bookingDto, $travelPrice);
if ($this->isInsuranceInList($currentInsurance, $eligibleInsurances)) {
continue;
}
$reassignedInsurance = $this->insuranceService->reassignInsuranceForPriceChange(
$selectableInsurances,
$currentInsurance,
$participant,
$bookingDto,
$travelPrice
);
if (null !== $reassignedInsurance) {
$participant->addNotification(
'warning',
sprintf('Versicherung vor der Übermittlung automatisch angepasst: %s', $reassignedInsurance->label)
);
} else {
$participant->addNotification(
'warning',
'Die gewählte Versicherung ist für diese Buchung nicht mehr verfügbar und wurde entfernt. Bitte erneut auswählen.'
);
}
$participant->insurance = $reassignedInsurance;
$changed = true;
}
return $changed;
}
/**
* Checks if a specific insurance exists in a list of insurances (by id).
*
* @param array<Insurance> $insuranceList
*/
private function isInsuranceInList(Insurance $targetInsurance, array $insuranceList): bool
{
foreach ($insuranceList as $insurance) {
if ($insurance->id === $targetInsurance->id) {
return true;
}
}
return false;
}
/**
* Applies the applicant's insurance to other participants if their selection requires it.
*