roomPricingCalculator->calculateRoomTotal($bookingDto, $roomPricingMode) + $this->calculateServiceTotal($bookingDto); } /** * Calculates total price for all rooms. */ public function calculateRoomTotal( BookingDto $bookingDto, string $roomPricingMode = RoomPricingCalculator::PRICING_MODE_ASSIGNMENT, ): float { return $this->roomPricingCalculator->calculateRoomTotal($bookingDto, $roomPricingMode); } /** * Calculates total price for all services across all eligible participants. * * Delegates to BookingPricingAssembler to guarantee the same insurance-tier * resolution used in the display breakdown (including bulk-family-insurance * price-tier adjustment per dependent). This keeps the numeric total and the * displayed total in sync. */ public function calculateServiceTotal(BookingDto $bookingDto): float { return array_sum(array_column( $this->pricingAssembler->calculateServicePricing($bookingDto), 'groupTotal' )); } /** * Calculates the total price for an individual participant. */ public function calculateIndividualParticipantPrice(BookingDto $bookingDto, int $participantIndex): float { return $this->participantPricingCalculator->calculateIndividualParticipantPrice($bookingDto, $participantIndex); } /** * Calculates individual prices for all participants in a booking. * * @return array */ public function calculateAllParticipantIndividualPrices(BookingDto $bookingDto): array { return $this->participantPricingCalculator->calculateAllParticipantIndividualPrices($bookingDto); } /** * Calculates total booking price across all participants, excluding insurance. * * Used for family insurance price-tier determination, where the tier is based on * the combined trip cost rather than any single participant's price. */ public function calculateTotalBookingPriceExcludingInsurance(BookingDto $bookingDto): float { $total = 0.0; foreach ($bookingDto->getParticipants() as $index => $participant) { $total += $this->calculateIndividualParticipantPriceExcludingInsurance($bookingDto, $index); } return $total; } /** * Calculates the total price for an individual participant excluding insurance. * * Used for insurance eligibility filtering to avoid circular dependency where * insurance selection affects travel price which affects insurance eligibility. * * Only includes services where versicherungsberechnung='J' in the XML. Services with * versicherungsberechnung='N' (like CO2 compensation) are excluded as per BPN API * requirements for insurance tier determination. */ public function calculateIndividualParticipantPriceExcludingInsurance( BookingDto $bookingDto, int $participantIndex, ): float { return $this->participantPricingCalculator->calculateIndividualParticipantPriceExcludingInsurance( $bookingDto, $participantIndex ); } /** * Resolves the travel price to use for insurance eligibility checks. * * Family insurances are always evaluated against the total booking price (all * participants combined); everything else uses the individual participant's price. * Non-applicant participants can never hold a family insurance (InsuranceManager * restricts family insurances to the applicant), so this does not need to know * whether $participantIndex is the applicant. */ public function resolveInsuranceTravelPrice(BookingDto $bookingDto, int $participantIndex, Insurance $insurance): float { if (true === $insurance->familyInsurance) { return $this->calculateTotalBookingPriceExcludingInsurance($bookingDto); } return $this->calculateIndividualParticipantPriceExcludingInsurance($bookingDto, $participantIndex); } }