wip: modernized edit flow
This commit is contained in:
@@ -5,9 +5,18 @@ declare(strict_types=1);
|
||||
namespace App\BusProNet\DataProcessor;
|
||||
|
||||
use App\BusProNet\Constants;
|
||||
use App\BusProNet\Model\Address;
|
||||
use App\BusProNet\Model\Booking;
|
||||
use App\BusProNet\Model\Communication;
|
||||
use App\BusProNet\Model\PersonalData;
|
||||
use App\BusProNet\Model\Travel;
|
||||
use App\BusProNet\Utility\DirectionMapper;
|
||||
use App\Form\Model\BankAccountDto;
|
||||
use App\Form\Model\BookingCreateDto;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Model\BookingEditDto;
|
||||
use App\Form\Model\ParticipantDto;
|
||||
use App\Service\InsuranceMatchingService;
|
||||
|
||||
/**
|
||||
* Processes booking form data and converts it into BusProNet API payload format.
|
||||
@@ -20,6 +29,89 @@ use App\Form\Model\BookingEditDto;
|
||||
*/
|
||||
class BookingDataProcessor
|
||||
{
|
||||
public function __construct(
|
||||
private readonly InsuranceMatchingService $insuranceMatchingService,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a BookingDto from an existing Booking entity (for edit mode).
|
||||
*
|
||||
* Extracts all service selections from the booking entity and assigns them
|
||||
* to participant DTOs, creating a unified data structure identical to create mode.
|
||||
*/
|
||||
public function createBookingDtoFromBooking(Booking $booking, Travel $travel): BookingDto
|
||||
{
|
||||
$dto = new BookingDto($travel, $booking->hotelId);
|
||||
|
||||
$dto->booking = $booking;
|
||||
$dto->agencyId = $booking->agencyId;
|
||||
|
||||
// Map payment ID from API to form payment method
|
||||
$dto->paymentMethod = match ($booking->paymentId) {
|
||||
(string) Constants::PAYMENT_TYPE_ID_DEBIT => Constants::PAYMENT_METHOD_DEBIT,
|
||||
(string) Constants::PAYMENT_TYPE_ID_TRANSFER => Constants::PAYMENT_METHOD_TRANSFER,
|
||||
default => Constants::PAYMENT_METHOD_TRANSFER,
|
||||
};
|
||||
|
||||
if (null !== $booking->bankAccount) {
|
||||
$dto->bankAccount = BankAccountDto::fromBankAccount($booking->bankAccount);
|
||||
}
|
||||
|
||||
foreach ($booking->participants as $index => $participant) {
|
||||
/** @var PersonalData $participant */
|
||||
$participantData = ParticipantDto::fromPersonalData($participant);
|
||||
$participantData->index = $index;
|
||||
|
||||
// Extract service selections from booking and assign to participant DTO
|
||||
$participantData->courses = $booking
|
||||
->getAdditionalServicesForParticipantByGroup($index, Constants::TOKEN_COURSES);
|
||||
|
||||
$participantData->skiPass = $booking->getSkiPassForParticipant($index);
|
||||
|
||||
$participantData->additionalServices = $booking
|
||||
->getAdditionalServicesForParticipantByGroup($index, Constants::TOKEN_ADDITIONAL);
|
||||
|
||||
$participantData->board = $booking
|
||||
->getAdditionalServicesForParticipantByGroup($index, Constants::TOKEN_BOARD);
|
||||
|
||||
$participantData->rentals = $booking
|
||||
->getAdditionalServicesForParticipantByGroup($index, Constants::TOKEN_RENTALS);
|
||||
|
||||
// Rental insurance (single service, not array)
|
||||
$participantData->rentalInsurance = $booking->getRentalInsuranceForParticipant($index);
|
||||
$participantData->rentalInsuranceSelected = null !== $participantData->rentalInsurance;
|
||||
|
||||
// Transportation services
|
||||
$participantData->transportationOutbound = $booking
|
||||
->getTransportationServiceForParticipantAndDirection($index, DirectionMapper::OUTBOUND_BOOKING);
|
||||
|
||||
$participantData->transportationInbound = $booking
|
||||
->getTransportationServiceForParticipantAndDirection($index, DirectionMapper::INBOUND_BOOKING);
|
||||
|
||||
// Pickup location
|
||||
$participantData->pickup = $booking->getPickupForParticipant($index);
|
||||
|
||||
// Parking (stored in form data, not in booking entity - needs special handling)
|
||||
// For now, leave as false - may need to extract from transportation services
|
||||
$participantData->parking = false;
|
||||
|
||||
// License plate (not stored in booking entity)
|
||||
$participantData->licensePlate = null;
|
||||
|
||||
// Insurance
|
||||
$participantData->insurance = $booking->getInsuranceForParticipant($index);
|
||||
|
||||
// Room assignment
|
||||
$room = $booking->getRoomForParticipant($index);
|
||||
$participantData->assignedRoomId = $room?->id;
|
||||
|
||||
$dto->participants[$index] = $participantData;
|
||||
}
|
||||
|
||||
return $dto;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an update request payload for the BusProNet API from booking form data.
|
||||
*
|
||||
@@ -34,18 +126,29 @@ class BookingDataProcessor
|
||||
* - Adding new services from travel data when participants select them
|
||||
* - Removing services with no participant mappings
|
||||
* - Updating participant personal data from form input
|
||||
* - Synchronizing applicant data with first participant details
|
||||
* - Building the final API payload structure
|
||||
*
|
||||
* @param BookingEditDto|null $formData The booking edit form data containing updated participant and service selections
|
||||
* IMPORTANT: The applicant's address must be preserved from the original booking data stored in the DTO,
|
||||
* as it can be lost during form binding when ParticipantDto stores address references.
|
||||
*
|
||||
* @param BookingDto|null $formData The booking edit form data containing updated participant and service selections
|
||||
*
|
||||
* @return array The structured payload array ready for BusProNet API submission
|
||||
*/
|
||||
public function createUpdateRequestPayload(?BookingEditDto $formData): array
|
||||
public function createUpdateRequestPayload(?BookingDto $formData): array
|
||||
{
|
||||
// Apply bulk insurance if enabled (modifies DTO in place)
|
||||
$this->applyBulkInsuranceIfActive($formData);
|
||||
|
||||
$bookingData = $formData->booking;
|
||||
$travelData = $formData->travel;
|
||||
|
||||
// CRITICAL: The applicant address may have been lost during form binding because ParticipantDto::fromPersonalData()
|
||||
// stores a reference to the address object, and Symfony's form binding can modify it in place.
|
||||
// Since we can't easily restore it here without the original booking, we rely on the EditController
|
||||
// to preserve the original booking's applicant data by fetching fresh data before calling updateBooking().
|
||||
// The applicant data should NEVER be modified in edit mode.
|
||||
|
||||
$this->resetServiceMappings($bookingData);
|
||||
|
||||
foreach ($formData->participants as $participant) {
|
||||
@@ -54,11 +157,10 @@ class BookingDataProcessor
|
||||
|
||||
$this->removeUnusedServices($bookingData);
|
||||
$this->updateParticipantPersonalData($formData->participants, $bookingData);
|
||||
$this->syncApplicantData($bookingData);
|
||||
|
||||
$payload = $this->buildBasePayload($bookingData);
|
||||
$this->addBankAccountToPayload($payload, $bookingData);
|
||||
$this->buildParticipantPayload($payload, $bookingData);
|
||||
$this->buildParticipantPayload($payload, $bookingData, $formData->participants);
|
||||
$this->buildServicesPayload($payload, $bookingData);
|
||||
$this->buildPickupPayload($payload, $bookingData);
|
||||
|
||||
@@ -71,9 +173,9 @@ class BookingDataProcessor
|
||||
* This ensures that service assignments are rebuilt from scratch based on current form selections.
|
||||
* Includes resetting room mappings to allow room reassignments during edit.
|
||||
*
|
||||
* @param object $bookingData The booking data object containing services to reset
|
||||
* @param Booking $bookingData The booking data object containing services to reset
|
||||
*/
|
||||
private function resetServiceMappings(object $bookingData): void
|
||||
private function resetServiceMappings(Booking $bookingData): void
|
||||
{
|
||||
$servicesToReset = [
|
||||
...$bookingData->additionalServices,
|
||||
@@ -81,6 +183,7 @@ class BookingDataProcessor
|
||||
...$bookingData->pickupsOutbound,
|
||||
...$bookingData->pickupsInbound,
|
||||
...$bookingData->rooms,
|
||||
...$bookingData->insurances,
|
||||
];
|
||||
|
||||
foreach ($servicesToReset as $service) {
|
||||
@@ -94,11 +197,11 @@ class BookingDataProcessor
|
||||
* This orchestrator method handles the complete service assignment workflow for one participant,
|
||||
* including additional services, transportation services, pickup locations, and room assignments.
|
||||
*
|
||||
* @param object $participant The participant data from the form
|
||||
* @param object $bookingData The booking data object to update
|
||||
* @param object $travelData The travel data containing available services
|
||||
* @param ParticipantDto $participant The participant data from the form
|
||||
* @param Booking $bookingData The booking data object to update
|
||||
* @param Travel $travelData The travel data containing available services
|
||||
*/
|
||||
private function processParticipantServices(object $participant, object $bookingData, object $travelData): void
|
||||
private function processParticipantServices(ParticipantDto $participant, Booking $bookingData, Travel $travelData): void
|
||||
{
|
||||
if (true === $participant->isCanceled()) {
|
||||
return;
|
||||
@@ -108,21 +211,22 @@ class BookingDataProcessor
|
||||
$this->processTransportationServices($participant, $bookingData, $travelData);
|
||||
$this->processPickupLocations($participant, $bookingData);
|
||||
$this->processRoomAssignment($participant, $bookingData);
|
||||
$this->processInsurance($participant, $bookingData, $travelData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes additional services for a participant.
|
||||
* Collects additional services from a participant for mapping.
|
||||
*
|
||||
* Maps additional services (courses, ski passes, board options, rentals) to the participant.
|
||||
* Adds new services to the booking if they don't already exist and sets individual pricing.
|
||||
* Extracts all additional services (courses, board, rentals, ski pass, rental insurance,
|
||||
* additional services) from a participant into a flat array of Service objects.
|
||||
*
|
||||
* @param object $participant The participant data from the form
|
||||
* @param object $bookingData The booking data object to update
|
||||
* @param object $travelData The travel data containing available services
|
||||
* @param ParticipantDto $participant The participant data
|
||||
*
|
||||
* @return array<Service> Array of services selected by this participant
|
||||
*/
|
||||
private function processAdditionalServices(object $participant, object $bookingData, object $travelData): void
|
||||
private function collectParticipantAdditionalServices(ParticipantDto $participant): array
|
||||
{
|
||||
$servicesToMap = [
|
||||
$services = [
|
||||
...$participant->courses,
|
||||
...$participant->additionalServices,
|
||||
...$participant->board,
|
||||
@@ -131,9 +235,31 @@ class BookingDataProcessor
|
||||
|
||||
// Add ski pass if selected (single service, not an array)
|
||||
if (null !== $participant->skiPass) {
|
||||
$servicesToMap[] = $participant->skiPass;
|
||||
$services[] = $participant->skiPass;
|
||||
}
|
||||
|
||||
// Add rental insurance if selected (single service, not an array)
|
||||
if (null !== $participant->rentalInsurance) {
|
||||
$services[] = $participant->rentalInsurance;
|
||||
}
|
||||
|
||||
return $services;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes additional services for a participant.
|
||||
*
|
||||
* Maps additional services (courses, ski passes, board options, rentals) to the participant.
|
||||
* Adds new services to the booking if they don't already exist and sets individual pricing.
|
||||
*
|
||||
* @param ParticipantDto $participant The participant data from the form
|
||||
* @param Booking $bookingData The booking data object to update
|
||||
* @param Travel $travelData The travel data containing available services
|
||||
*/
|
||||
private function processAdditionalServices(ParticipantDto $participant, Booking $bookingData, Travel $travelData): void
|
||||
{
|
||||
$servicesToMap = $this->collectParticipantAdditionalServices($participant);
|
||||
|
||||
foreach ($servicesToMap as $service) {
|
||||
if (false === isset($bookingData->additionalServices[$service->id])) {
|
||||
$serviceToAdd = $travelData->additionalServices[$service->id] ?? null;
|
||||
@@ -152,11 +278,11 @@ class BookingDataProcessor
|
||||
* Maps transportation services (both directions: to and from destination) to the participant.
|
||||
* Adds new transportation services to the booking if they don't already exist.
|
||||
*
|
||||
* @param object $participant The participant data from the form
|
||||
* @param object $bookingData The booking data object to update
|
||||
* @param object $travelData The travel data containing available services
|
||||
* @param ParticipantDto $participant The participant data from the form
|
||||
* @param Booking $bookingData The booking data object to update
|
||||
* @param Travel $travelData The travel data containing available services
|
||||
*/
|
||||
private function processTransportationServices(object $participant, object $bookingData, object $travelData): void
|
||||
private function processTransportationServices(ParticipantDto $participant, Booking $bookingData, Travel $travelData): void
|
||||
{
|
||||
foreach ([$participant->transportationOutbound, $participant->transportationInbound] as $service) {
|
||||
if (false === isset($bookingData->transportationServices[$service->id])) {
|
||||
@@ -176,10 +302,10 @@ class BookingDataProcessor
|
||||
* Only processes pickup locations for bus transportation services and maps the participant
|
||||
* to their selected pickup location.
|
||||
*
|
||||
* @param object $participant The participant data from the form
|
||||
* @param object $bookingData The booking data object to update
|
||||
* @param ParticipantDto $participant The participant data from the form
|
||||
* @param Booking $bookingData The booking data object to update
|
||||
*/
|
||||
private function processPickupLocations(object $participant, object $bookingData): void
|
||||
private function processPickupLocations(ParticipantDto $participant, Booking $bookingData): void
|
||||
{
|
||||
// Check if either transportation direction is BUS and pickup is selected
|
||||
$hasOutboundBus = null !== $participant->transportationOutbound && 'BUS' === $participant->transportationOutbound->subType;
|
||||
@@ -200,10 +326,10 @@ class BookingDataProcessor
|
||||
* booking edits while maintaining the constraint that participants can only be
|
||||
* assigned to room types that have already been booked.
|
||||
*
|
||||
* @param object $participant The participant data from the form
|
||||
* @param object $bookingData The booking data object to update
|
||||
* @param ParticipantDto $participant The participant data from the form
|
||||
* @param Booking $bookingData The booking data object to update
|
||||
*/
|
||||
private function processRoomAssignment(object $participant, object $bookingData): void
|
||||
private function processRoomAssignment(ParticipantDto $participant, Booking $bookingData): void
|
||||
{
|
||||
if (null === $participant->assignedRoomId) {
|
||||
return;
|
||||
@@ -218,15 +344,48 @@ class BookingDataProcessor
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes travel insurance for a participant.
|
||||
*
|
||||
* Maps insurance to the participant. Adds new insurances to the booking if they don't exist.
|
||||
* Insurance can be either individual or package-based, with automatic price-tier adjustment.
|
||||
*
|
||||
* @param ParticipantDto $participant The participant data from the form
|
||||
* @param Booking $bookingData The booking data object to update
|
||||
* @param Travel $travelData The travel data containing available insurances
|
||||
*/
|
||||
private function processInsurance(ParticipantDto $participant, Booking $bookingData, Travel $travelData): void
|
||||
{
|
||||
if (null === $participant->insurance) {
|
||||
return;
|
||||
}
|
||||
|
||||
$insurance = $participant->insurance;
|
||||
|
||||
if (false === isset($bookingData->insurances[$insurance->id])) {
|
||||
$insuranceToAdd = $travelData->insurances[$insurance->id] ?? null;
|
||||
if (null !== $insuranceToAdd) {
|
||||
$bookingData->insurances[$insurance->id] = $insuranceToAdd;
|
||||
$bookingData->insurances[$insurance->id]->individualPrice[$participant->index] = $insuranceToAdd->price;
|
||||
}
|
||||
}
|
||||
|
||||
// Only add mapping if insurance exists in booking data
|
||||
// This can legitimately be false if the insurance is no longer available in current travel data
|
||||
if (isset($bookingData->insurances[$insurance->id])) {
|
||||
$bookingData->insurances[$insurance->id]->mapping[] = $participant->index;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes services and pickups with no participant mappings.
|
||||
*
|
||||
* Cleans up unused services to prevent empty services from being sent to the API.
|
||||
* This includes additional services, transportation services, and pickup locations.
|
||||
* This includes additional services, transportation services, pickup locations, and insurances.
|
||||
*
|
||||
* @param object $bookingData The booking data object to clean up
|
||||
* @param Booking $bookingData The booking data object to clean up
|
||||
*/
|
||||
private function removeUnusedServices(object $bookingData): void
|
||||
private function removeUnusedServices(Booking $bookingData): void
|
||||
{
|
||||
foreach ($bookingData->additionalServices as $service) {
|
||||
if (0 === count($service->mapping)) {
|
||||
@@ -251,6 +410,12 @@ class BookingDataProcessor
|
||||
unset($bookingData->pickupsInbound[$pickup->id]);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($bookingData->insurances as $insurance) {
|
||||
if (0 === count($insurance->mapping)) {
|
||||
unset($bookingData->insurances[$insurance->id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -259,10 +424,13 @@ class BookingDataProcessor
|
||||
* Only processes participants with status 'F' (active/confirmed participants).
|
||||
* Updates all personal data fields and communication information.
|
||||
*
|
||||
* @param array $participants The participants array from the form
|
||||
* @param object $bookingData The booking data object to update
|
||||
* IMPORTANT: The applicant's address must never be modified. This method updates
|
||||
* participant addresses independently to ensure applicant data remains intact.
|
||||
*
|
||||
* @param array $participants The participants array from the form
|
||||
* @param Booking $bookingData The booking data object to update
|
||||
*/
|
||||
private function updateParticipantPersonalData(array $participants, object $bookingData): void
|
||||
private function updateParticipantPersonalData(array $participants, Booking $bookingData): void
|
||||
{
|
||||
foreach ($participants as $participant) {
|
||||
if ('F' !== $participant->status) {
|
||||
@@ -278,6 +446,19 @@ class BookingDataProcessor
|
||||
$bookingData->participants[$participant->index]->weight = $participant->weight;
|
||||
$bookingData->participants[$participant->index]->shoeSize = $participant->shoeSize;
|
||||
|
||||
// Update address if provided
|
||||
// Create new Address instance to avoid modifying any shared object references
|
||||
if (null !== $participant->address) {
|
||||
$newAddress = new Address();
|
||||
$newAddress->street = $participant->address->street;
|
||||
$newAddress->postCode = $participant->address->postCode;
|
||||
$newAddress->city = $participant->address->city;
|
||||
$newAddress->district = $participant->address->district;
|
||||
$newAddress->country = $participant->address->country;
|
||||
|
||||
$bookingData->participants[$participant->index]->address = $newAddress;
|
||||
}
|
||||
|
||||
if ($participant->email || $participant->mobile) {
|
||||
if (null === $bookingData->participants[$participant->index]->communication) {
|
||||
$bookingData->participants[$participant->index]->communication = new Communication();
|
||||
@@ -288,32 +469,16 @@ class BookingDataProcessor
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronizes applicant data with first participant's physical characteristics.
|
||||
*
|
||||
* The applicant (booking holder) inherits physical data from the first participant.
|
||||
*
|
||||
* @param object $bookingData The booking data object to update
|
||||
*/
|
||||
private function syncApplicantData(object $bookingData): void
|
||||
{
|
||||
if (false !== $firstParticipant = reset($bookingData->participants)) {
|
||||
$bookingData->applicant->height = $firstParticipant->height;
|
||||
$bookingData->applicant->weight = $firstParticipant->weight;
|
||||
$bookingData->applicant->shoeSize = $firstParticipant->shoeSize;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the base API payload structure with booking information.
|
||||
*
|
||||
* Creates the main structure that will be populated with detailed data sections.
|
||||
*
|
||||
* @param object $bookingData The booking data object
|
||||
* @param Booking $bookingData The booking data object
|
||||
*
|
||||
* @return array The base payload structure
|
||||
*/
|
||||
private function buildBasePayload(object $bookingData): array
|
||||
private function buildBasePayload(Booking $bookingData): array
|
||||
{
|
||||
return [
|
||||
'idbuchung' => $bookingData->id,
|
||||
@@ -347,10 +512,10 @@ class BookingDataProcessor
|
||||
*
|
||||
* Bank account information is required for direct debit payments.
|
||||
*
|
||||
* @param array $payload The payload array to modify
|
||||
* @param object $bookingData The booking data object
|
||||
* @param array $payload The payload array to modify
|
||||
* @param Booking $bookingData The booking data object
|
||||
*/
|
||||
private function addBankAccountToPayload(array &$payload, object $bookingData): void
|
||||
private function addBankAccountToPayload(array &$payload, Booking $bookingData): void
|
||||
{
|
||||
if (null !== $bookingData->bankAccount) {
|
||||
$payload['zahlung']['bankverbindung'] = [
|
||||
@@ -365,19 +530,42 @@ class BookingDataProcessor
|
||||
/**
|
||||
* Builds the participant list section of the payload.
|
||||
*
|
||||
* Includes status and personal data for each participant.
|
||||
* Includes status, personal data, and wishes (room remarks, license plate) for each participant.
|
||||
*
|
||||
* @param array $payload The payload array to modify
|
||||
* @param object $bookingData The booking data object
|
||||
* @param array $payload The payload array to modify
|
||||
* @param Booking $bookingData The booking data object
|
||||
* @param array $participantDtos The participant DTOs from the form (for wishes data)
|
||||
*/
|
||||
private function buildParticipantPayload(array &$payload, object $bookingData): void
|
||||
private function buildParticipantPayload(array &$payload, Booking $bookingData, array $participantDtos): void
|
||||
{
|
||||
foreach ($bookingData->participants as $index => $participant) {
|
||||
$payload['teilnehmerliste']['teilnehmer'][] = [
|
||||
$participantPayload = [
|
||||
'@id' => $index + 1,
|
||||
'status' => $bookingData->participantsStatus[$index],
|
||||
...$participant->toPayload(),
|
||||
];
|
||||
|
||||
// Add wishes (room remarks, license plate) from form DTO
|
||||
if (isset($participantDtos[$index])) {
|
||||
$dto = $participantDtos[$index];
|
||||
if (null !== $dto->remarksRoom || null !== $dto->licensePlate) {
|
||||
$wishes = [];
|
||||
|
||||
if (null !== $dto->remarksRoom && '' !== trim($dto->remarksRoom)) {
|
||||
$wishes['unterbringungswunsch'] = $dto->remarksRoom;
|
||||
}
|
||||
|
||||
if (null !== $dto->licensePlate && '' !== trim($dto->licensePlate)) {
|
||||
$wishes['beförderungswunsch'] = $dto->licensePlate;
|
||||
}
|
||||
|
||||
if (false === empty($wishes)) {
|
||||
$participantPayload['wünsche'] = $wishes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$payload['teilnehmerliste']['teilnehmer'][] = $participantPayload;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -387,10 +575,10 @@ class BookingDataProcessor
|
||||
* Includes additional services, transportation services, and accommodation details
|
||||
* with participant mappings and quantities.
|
||||
*
|
||||
* @param array $payload The payload array to modify
|
||||
* @param object $bookingData The booking data object
|
||||
* @param array $payload The payload array to modify
|
||||
* @param Booking $bookingData The booking data object
|
||||
*/
|
||||
private function buildServicesPayload(array &$payload, object $bookingData): void
|
||||
private function buildServicesPayload(array &$payload, Booking $bookingData): void
|
||||
{
|
||||
foreach ($bookingData->additionalServices as $service) {
|
||||
$payload['zusatzleistungen']['zusatzleistung'][] = [
|
||||
@@ -419,6 +607,20 @@ class BookingDataProcessor
|
||||
'@zuordnung' => implode(',', array_map(fn ($index) => $index + 1, $room->mapping)),
|
||||
];
|
||||
}
|
||||
|
||||
// Add insurances with participant mappings
|
||||
if (false === empty($bookingData->insurances)) {
|
||||
$payload['versicherungen']['versicherung'] = [];
|
||||
foreach ($bookingData->insurances as $insurance) {
|
||||
if (count($insurance->mapping) > 0) {
|
||||
$payload['versicherungen']['versicherung'][] = [
|
||||
'@idversicherung' => $insurance->id,
|
||||
'@anzahl' => count($insurance->mapping),
|
||||
'@zuordnung' => implode(',', array_map(fn ($index) => $index + 1, $insurance->mapping)),
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -426,10 +628,10 @@ class BookingDataProcessor
|
||||
*
|
||||
* Only included in payload if there are actual pickup assignments for bus transportation.
|
||||
*
|
||||
* @param array $payload The payload array to modify
|
||||
* @param object $bookingData The booking data object
|
||||
* @param array $payload The payload array to modify
|
||||
* @param Booking $bookingData The booking data object
|
||||
*/
|
||||
private function buildPickupPayload(array &$payload, object $bookingData): void
|
||||
private function buildPickupPayload(array &$payload, Booking $bookingData): void
|
||||
{
|
||||
if (0 < count($bookingData->pickupsOutbound)) {
|
||||
$payload['zustiege']['zustieg'] = [];
|
||||
@@ -451,13 +653,16 @@ class BookingDataProcessor
|
||||
* payment information. The booking type determines whether this is an inquiry validation
|
||||
* ('Anfrage') or a final booking commit ('Buchung').
|
||||
*
|
||||
* @param BookingCreateDto $bookingDto The booking creation form data
|
||||
* @param BookingDto $bookingDto The booking creation form data
|
||||
* @param string $bookingType Either 'Anfrage' (inquiry) or 'Buchung' (final booking)
|
||||
*
|
||||
* @return array The structured payload array for BusProNet API submission
|
||||
*/
|
||||
public function createBookingRequestPayload(BookingCreateDto $bookingDto, string $bookingType): array
|
||||
public function createBookingRequestPayload(BookingDto $bookingDto, string $bookingType): array
|
||||
{
|
||||
// Apply bulk insurance if enabled (modifies DTO in place)
|
||||
$this->applyBulkInsuranceIfActive($bookingDto);
|
||||
|
||||
$firstParticipant = $bookingDto->participants[0];
|
||||
|
||||
$payload = [
|
||||
@@ -802,4 +1007,59 @@ class BookingDataProcessor
|
||||
|
||||
return $insuranceMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies bulk insurance assignment if the applicant has enabled it.
|
||||
*
|
||||
* When bulk insurance is active (applicant's bulkInsuranceBooking = true), this method
|
||||
* assigns the applicant's insurance TYPE to all dependent participants with automatic
|
||||
* price tier adjustment based on each participant's total cost.
|
||||
*
|
||||
* IMPORTANT: In edit mode, bulk insurance only applies to participants who:
|
||||
* 1. Currently have NO insurance assigned (insurance === null)
|
||||
* 2. OR whose insurance was already assigned via previous bulk operation
|
||||
*
|
||||
* This prevents overriding individually selected insurances that are locked.
|
||||
*
|
||||
* @param BookingDto $bookingDto The booking DTO (create or edit flow)
|
||||
*/
|
||||
private function applyBulkInsuranceIfActive(BookingDto $bookingDto): void
|
||||
{
|
||||
$applicant = $bookingDto->getParticipant(0);
|
||||
|
||||
// Check if bulk insurance is enabled and applicant has selected an insurance
|
||||
if (null === $applicant || false === $applicant->bulkInsuranceBooking || null === $applicant->insurance) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get all available insurances from travel data
|
||||
$availableInsurances = array_values($bookingDto->travel->insurances);
|
||||
|
||||
// Use InsuranceMatchingService for proper type-based assignment with price tier matching
|
||||
$assignments = $this->insuranceMatchingService->batchAssignInsuranceToParticipants(
|
||||
$availableInsurances,
|
||||
$applicant->insurance,
|
||||
$bookingDto
|
||||
);
|
||||
|
||||
// Apply assignments to dependent participants (skip applicant at index 0)
|
||||
foreach ($assignments as $index => $insurance) {
|
||||
if (0 === $index) {
|
||||
continue; // Skip applicant
|
||||
}
|
||||
|
||||
$participant = $bookingDto->getParticipant($index);
|
||||
if (null === $participant) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// In edit mode: only apply bulk insurance if participant has no insurance
|
||||
// This respects the rule that once assigned, insurance cannot be changed
|
||||
if ($bookingDto instanceof BookingEditDto && null !== $participant->insurance) {
|
||||
continue; // Skip participants with existing insurance assignment
|
||||
}
|
||||
|
||||
$participant->insurance = $insurance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user