334 lines
14 KiB
PHP
334 lines
14 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\BusProNet\DataProcessor;
|
|
|
|
use App\BusProNet\Model\Booking;
|
|
use App\BusProNet\Model\Service;
|
|
use App\BusProNet\Model\Travel;
|
|
use App\Form\Model\ParticipantDto;
|
|
use Psr\Log\LoggerInterface;
|
|
|
|
/**
|
|
* Processes participant service selections for booking updates.
|
|
*
|
|
* Handles the assignment and mapping of services (additional, transportation,
|
|
* pickup, room, insurance) from participant DTOs to the booking model during
|
|
* the update request workflow.
|
|
*/
|
|
class ParticipantServiceProcessor
|
|
{
|
|
public function __construct(
|
|
private readonly LoggerInterface $logger,
|
|
) {
|
|
}
|
|
|
|
/**
|
|
* Resets all existing participant-to-service mappings to start with a clean slate.
|
|
*
|
|
* 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 Booking $bookingData The booking data object containing services to reset
|
|
*/
|
|
public function resetServiceMappings(Booking $bookingData): void
|
|
{
|
|
$servicesToReset = [
|
|
...$bookingData->additionalServices,
|
|
...$bookingData->transportationServices,
|
|
...$bookingData->pickups,
|
|
...$bookingData->dropOffs,
|
|
...$bookingData->rooms,
|
|
...$bookingData->insurances,
|
|
];
|
|
|
|
foreach ($servicesToReset as $service) {
|
|
$service->mapping = [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Processes all services for a single participant.
|
|
*
|
|
* This orchestrator method handles the complete service assignment workflow for one participant,
|
|
* including additional services, transportation services, pickup locations, and room assignments.
|
|
*
|
|
* @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
|
|
*/
|
|
public function processParticipantServices(ParticipantDto $participant, Booking $bookingData, Travel $travelData): void
|
|
{
|
|
if (true === $participant->isCanceled()) {
|
|
return;
|
|
}
|
|
|
|
$this->processAdditionalServices($participant, $bookingData, $travelData);
|
|
$this->processTransportationServices($participant, $bookingData, $travelData);
|
|
$this->processPickupLocations($participant, $bookingData);
|
|
$this->processDropOffLocations($participant, $bookingData);
|
|
$this->processRoomAssignment($participant, $bookingData);
|
|
$this->processInsurance($participant, $bookingData, $travelData);
|
|
}
|
|
|
|
/**
|
|
* 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, pickup locations, and insurances.
|
|
*
|
|
* @param Booking $bookingData The booking data object to clean up
|
|
*/
|
|
public function removeUnusedServices(Booking $bookingData): void
|
|
{
|
|
foreach ($bookingData->additionalServices as $service) {
|
|
if (0 === count($service->mapping)) {
|
|
unset($bookingData->additionalServices[$service->id]);
|
|
}
|
|
}
|
|
|
|
foreach ($bookingData->transportationServices as $service) {
|
|
if (0 === count($service->mapping)) {
|
|
unset($bookingData->transportationServices[$service->id]);
|
|
}
|
|
}
|
|
|
|
foreach ($bookingData->pickups as $pickup) {
|
|
if (0 === count($pickup->mapping)) {
|
|
unset($bookingData->pickups[$pickup->id]);
|
|
}
|
|
}
|
|
|
|
foreach ($bookingData->dropOffs as $dropOff) {
|
|
if (0 === count($dropOff->mapping)) {
|
|
unset($bookingData->dropOffs[$dropOff->id]);
|
|
}
|
|
}
|
|
|
|
foreach ($bookingData->insurances as $insurance) {
|
|
if (0 === count($insurance->mapping)) {
|
|
unset($bookingData->insurances[$insurance->id]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Collects additional services from a participant for mapping.
|
|
*
|
|
* Extracts all additional services (courses, board, rentals, ski pass, rental insurance,
|
|
* parking, additional services) from a participant into a flat array of Service objects.
|
|
*
|
|
* @param ParticipantDto $participant The participant data
|
|
*
|
|
* @return array<Service> Array of services selected by this participant
|
|
*/
|
|
private function collectParticipantAdditionalServices(ParticipantDto $participant): array
|
|
{
|
|
$services = [
|
|
...$participant->courses,
|
|
...$participant->additionalServices,
|
|
...$participant->board,
|
|
...$participant->rentals,
|
|
];
|
|
|
|
// Veg is encoded as an additional service in the edit/update payload.
|
|
if (null !== $participant->veg) {
|
|
$services[] = $participant->veg;
|
|
}
|
|
|
|
// Add ski pass if selected (single service, not an array)
|
|
if (null !== $participant->skiPass) {
|
|
$services[] = $participant->skiPass;
|
|
}
|
|
|
|
// Add rental insurance if selected (single service, not an array)
|
|
if (null !== $participant->rentalInsurance) {
|
|
$services[] = $participant->rentalInsurance;
|
|
}
|
|
|
|
// Add parking service if selected (for self-organized PKW transportation)
|
|
if (true === $participant->parking && null !== $participant->parkingService) {
|
|
$services[] = $participant->parkingService;
|
|
}
|
|
|
|
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;
|
|
if (null === $serviceToAdd) {
|
|
$this->logger->warning('Additional service not found in travel data, skipping mapping', [
|
|
'bookingId' => $bookingData->id,
|
|
'serviceId' => $service->id,
|
|
'participantIndex' => $participant->index,
|
|
]);
|
|
continue;
|
|
}
|
|
$bookingData->additionalServices[$service->id] = $serviceToAdd;
|
|
$bookingData->additionalServices[$service->id]->individualPrice[$participant->index] = $serviceToAdd->price;
|
|
}
|
|
|
|
$bookingData->additionalServices[$service->id]->mapping[] = $participant->index;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Processes transportation services for a participant.
|
|
*
|
|
* 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 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(ParticipantDto $participant, Booking $bookingData, Travel $travelData): void
|
|
{
|
|
foreach ([$participant->transportationOutbound, $participant->transportationInbound] as $service) {
|
|
if (null === $service) {
|
|
continue;
|
|
}
|
|
|
|
if (false === isset($bookingData->transportationServices[$service->id])) {
|
|
$serviceToAdd = $travelData->transportationServices[$service->id] ?? null;
|
|
if (null === $serviceToAdd) {
|
|
$this->logger->warning('Transportation service not found in travel data, skipping mapping', [
|
|
'bookingId' => $bookingData->id,
|
|
'serviceId' => $service->id,
|
|
'participantIndex' => $participant->index,
|
|
]);
|
|
continue;
|
|
}
|
|
$bookingData->transportationServices[$service->id] = $serviceToAdd;
|
|
$bookingData->transportationServices[$service->id]->individualPrice[$participant->index] = $serviceToAdd->price;
|
|
}
|
|
|
|
$bookingData->transportationServices[$service->id]->mapping[] = $participant->index;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Processes pickup locations for participants using bus transportation.
|
|
*
|
|
* Only processes pickup locations for bus transportation services and maps the participant
|
|
* to their selected pickup location.
|
|
*
|
|
* @param ParticipantDto $participant The participant data from the form
|
|
* @param Booking $bookingData The booking data object to update
|
|
*/
|
|
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;
|
|
$hasInboundBus = null !== $participant->transportationInbound && 'BUS' === $participant->transportationInbound->subType;
|
|
|
|
if (($hasOutboundBus || $hasInboundBus) && null !== $selectedPickup = $participant->pickup) {
|
|
if (false === isset($bookingData->pickups[$selectedPickup->id])) {
|
|
$bookingData->pickups[$selectedPickup->id] = $selectedPickup;
|
|
}
|
|
$bookingData->pickups[$selectedPickup->id]->mapping[] = $participant->index;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Processes drop-off locations for participants using bus transportation.
|
|
*
|
|
* Only processes drop-off locations for bus transportation services and maps the participant
|
|
* to their selected drop-off location.
|
|
*
|
|
* @param ParticipantDto $participant The participant data from the form
|
|
* @param Booking $bookingData The booking data object to update
|
|
*/
|
|
private function processDropOffLocations(ParticipantDto $participant, Booking $bookingData): void
|
|
{
|
|
$hasOutboundBus = null !== $participant->transportationOutbound
|
|
&& 'BUS' === $participant->transportationOutbound->subType;
|
|
$hasInboundBus = null !== $participant->transportationInbound
|
|
&& 'BUS' === $participant->transportationInbound->subType;
|
|
|
|
if (($hasOutboundBus || $hasInboundBus) && null !== $selectedDropOff = $participant->dropOff) {
|
|
if (false === isset($bookingData->dropOffs[$selectedDropOff->id])) {
|
|
$bookingData->dropOffs[$selectedDropOff->id] = $selectedDropOff;
|
|
}
|
|
$bookingData->dropOffs[$selectedDropOff->id]->mapping[] = $participant->index;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Processes room assignment for a participant.
|
|
*
|
|
* Maps the participant to their assigned room. Allows room reassignment during
|
|
* booking edits while maintaining the constraint that participants can only be
|
|
* assigned to room types that have already been booked.
|
|
*
|
|
* @param ParticipantDto $participant The participant data from the form
|
|
* @param Booking $bookingData The booking data object to update
|
|
*/
|
|
private function processRoomAssignment(ParticipantDto $participant, Booking $bookingData): void
|
|
{
|
|
if (null === $participant->assignedRoomId) {
|
|
return;
|
|
}
|
|
|
|
// Find the room in the existing booking by ID
|
|
foreach ($bookingData->rooms as $room) {
|
|
if ($room->id === $participant->assignedRoomId) {
|
|
$room->mapping[] = $participant->index;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*
|
|
* IMPORTANT: Excludes synthetic "no insurance" option from BPN transmission.
|
|
*
|
|
* @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
|
|
{
|
|
// Skip if no insurance or if participant selected "keine Versicherung gewünscht"
|
|
if (null === $participant->insurance || $participant->insurance->isNoInsurance()) {
|
|
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;
|
|
}
|
|
}
|
|
}
|