feat: unify pickup selection

This commit is contained in:
Björn Fromme
2025-10-06 18:07:07 +02:00
parent b7b80296e9
commit b66d453881
19 changed files with 119 additions and 325 deletions
+1 -2
View File
@@ -86,8 +86,7 @@ services:
- 'App\Form\Service\ParticipantSkiPassFieldHandler' - 'App\Form\Service\ParticipantSkiPassFieldHandler'
- 'App\Form\Service\ParticipantTransportationOutboundFieldHandler' - 'App\Form\Service\ParticipantTransportationOutboundFieldHandler'
- 'App\Form\Service\ParticipantTransportationInboundFieldHandler' - 'App\Form\Service\ParticipantTransportationInboundFieldHandler'
- 'App\Form\Service\ParticipantPickupOutboundFieldHandler' - 'App\Form\Service\ParticipantPickupFieldHandler'
- 'App\Form\Service\ParticipantPickupInboundFieldHandler'
- 'App\Form\Service\ParticipantParkingFieldHandler' - 'App\Form\Service\ParticipantParkingFieldHandler'
- 'App\Form\Service\ParticipantRentalInsuranceFieldHandler' - 'App\Form\Service\ParticipantRentalInsuranceFieldHandler'
- 'App\Form\Service\ParticipantLicensePlateFieldHandler' - 'App\Form\Service\ParticipantLicensePlateFieldHandler'
@@ -178,7 +178,11 @@ class BookingDataProcessor
*/ */
private function processPickupLocations(object $participant, object $bookingData): void private function processPickupLocations(object $participant, object $bookingData): void
{ {
if ('BUS' === $participant->transportationOutbound->subType && null !== $selectedPickup = $participant->pickupOutbound) { // 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->pickupsOutbound[$selectedPickup->id])) { if (false === isset($bookingData->pickupsOutbound[$selectedPickup->id])) {
$bookingData->pickupsOutbound[$selectedPickup->id] = $selectedPickup; $bookingData->pickupsOutbound[$selectedPickup->id] = $selectedPickup;
} }
@@ -729,8 +733,8 @@ class BookingDataProcessor
/** /**
* Collects pickup location mappings. * Collects pickup location mappings.
* *
* Only collects outbound pickups as the API doesn't support different pickups * Collects the unified pickup selection which applies to both directions.
* for inbound direction. Both directions use the same pickup location. * The API receives this as outbound pickup data.
* *
* @return array<string, array<int>> Map of pickup ID to participant IDs * @return array<string, array<int>> Map of pickup ID to participant IDs
*/ */
@@ -741,9 +745,8 @@ class BookingDataProcessor
foreach ($bookingDto->participants as $index => $participant) { foreach ($bookingDto->participants as $index => $participant) {
$participantId = $index + 1; $participantId = $index + 1;
// Only use outbound pickups (inbound uses same location) if (null !== $participant->pickup) {
if (null !== $participant->pickupOutbound) { $pickupMap[$participant->pickup->id][] = $participantId;
$pickupMap[$participant->pickupOutbound->id][] = $participantId;
} }
} }
+5 -5
View File
@@ -231,9 +231,9 @@ class BookingEditParticipantType extends AbstractType
'choice_attr' => $transportationChoiceAttributes, 'choice_attr' => $transportationChoiceAttributes,
]); ]);
// Pickup // Pickup (unified for both directions)
$form->add('pickupOutbound', ChoiceType::class, [ $form->add('pickup', ChoiceType::class, [
'label' => 'Zustieg', 'label' => 'Zu- und Ausstieg',
'multiple' => false, 'multiple' => false,
'expanded' => false, 'expanded' => false,
'choices' => $options['selectable_pickups'], 'choices' => $options['selectable_pickups'],
@@ -323,8 +323,8 @@ class BookingEditParticipantType extends AbstractType
$transportation = $options['travel']->pickups[$transportationId] ?? null; $transportation = $options['travel']->pickups[$transportationId] ?? null;
if (null !== $transportation && 'PKW' === $transportation->subType) { if (null !== $transportation && 'PKW' === $transportation->subType) {
$form->remove('pickupOutbound'); $form->remove('pickup');
unset($data['pickupOutbound']); unset($data['pickup']);
} }
// forcibly select mandatory services that potentially have been disabled in PRE_SET_DATA // forcibly select mandatory services that potentially have been disabled in PRE_SET_DATA
+1 -2
View File
@@ -233,8 +233,7 @@ class BookingParticipantType extends AbstractType
'skiPass' => ChoiceType::class, 'skiPass' => ChoiceType::class,
'transportationOutbound' => ChoiceType::class, 'transportationOutbound' => ChoiceType::class,
'transportationInbound' => ChoiceType::class, 'transportationInbound' => ChoiceType::class,
'pickupOutbound' => ChoiceType::class, 'pickup' => ChoiceType::class,
'pickupInbound' => ChoiceType::class,
'parking' => CheckboxType::class, 'parking' => CheckboxType::class,
'licensePlate' => TextType::class, 'licensePlate' => TextType::class,
'bulkInsuranceBooking' => CheckboxType::class, 'bulkInsuranceBooking' => CheckboxType::class,
+2 -2
View File
@@ -57,8 +57,8 @@ class BookingEditDto implements BookingDtoInterface
$participantData->transportationInbound = $inboundTransportation; $participantData->transportationInbound = $inboundTransportation;
// Pickup handling (currently only supports outbound pickup) // Pickup handling (currently only supports outbound pickup)
$pickupOutbound = $booking->getPickupForParticipant($index); $pickup = $booking->getPickupForParticipant($index);
$participantData->pickupOutbound = $pickupOutbound; $participantData->pickup = $pickup;
$instance->participants[$index] = $participantData; $instance->participants[$index] = $participantData;
} }
+3 -5
View File
@@ -27,8 +27,7 @@ class ParticipantDto
'skiPass', 'skiPass',
'transportationOutbound', 'transportationOutbound',
'transportationInbound', 'transportationInbound',
'pickupOutbound', 'pickup',
'pickupInbound',
'parking', 'parking',
'licensePlate', 'licensePlate',
'bulkInsuranceBooking', 'bulkInsuranceBooking',
@@ -86,9 +85,8 @@ class ParticipantDto
public ?Service $transportationOutbound = null; public ?Service $transportationOutbound = null;
public ?Service $transportationInbound = null; public ?Service $transportationInbound = null;
// Pickup locations for each direction // Pickup location (applies to both directions)
public ?Pickup $pickupOutbound = null; public ?Pickup $pickup = null;
public ?Pickup $pickupInbound = null;
// Parking service for self-organized transportation (boolean: true if parking requested) // Parking service for self-organized transportation (boolean: true if parking requested)
public bool $parking = false; public bool $parking = false;
+6 -10
View File
@@ -179,17 +179,13 @@ class CreateFieldStateProvider extends AbstractFieldStateProvider
), ),
]; ];
// Show outbound pickup only when transportation is BUS (hidden by default) // Show pickup only when either outbound or inbound transportation is BUS (hidden by default)
$this->fieldStateConditions['pickupOutbound'] = [ $this->fieldStateConditions['pickup'] = [
'hidden' => CompositeCondition::not( 'hidden' => CompositeCondition::not(
ServiceSubTypeCondition::equals('transportationOutbound', DirectionMapper::SUBTYPE_BUS_API) CompositeCondition::or(
), ServiceSubTypeCondition::equals('transportationOutbound', DirectionMapper::SUBTYPE_BUS_API),
]; ServiceSubTypeCondition::equals('transportationInbound', DirectionMapper::SUBTYPE_BUS_API)
)
// Show inbound pickup only when transportation is BUS (hidden by default)
$this->fieldStateConditions['pickupInbound'] = [
'hidden' => CompositeCondition::not(
ServiceSubTypeCondition::equals('transportationInbound', DirectionMapper::SUBTYPE_BUS_API)
), ),
]; ];
+6 -10
View File
@@ -121,17 +121,13 @@ class EditFieldStateProvider extends AbstractFieldStateProvider
'readonly' => $transportationServicesMutabilityCondition, 'readonly' => $transportationServicesMutabilityCondition,
]; ];
// Pickup fields - shown only when transportation is BUS, readonly if pickups not mutable // Pickup field (unified) - shown only when either transportation direction is BUS, readonly if pickups not mutable
$this->fieldStateConditions['pickupOutbound'] = [ $this->fieldStateConditions['pickup'] = [
'hidden' => CompositeCondition::not( 'hidden' => CompositeCondition::not(
ServiceSubTypeCondition::equals('transportationOutbound', DirectionMapper::SUBTYPE_BUS_API) CompositeCondition::or(
), ServiceSubTypeCondition::equals('transportationOutbound', DirectionMapper::SUBTYPE_BUS_API),
'readonly' => $pickupsMutabilityCondition, ServiceSubTypeCondition::equals('transportationInbound', DirectionMapper::SUBTYPE_BUS_API)
]; )
$this->fieldStateConditions['pickupInbound'] = [
'hidden' => CompositeCondition::not(
ServiceSubTypeCondition::equals('transportationInbound', DirectionMapper::SUBTYPE_BUS_API)
), ),
'readonly' => $pickupsMutabilityCondition, 'readonly' => $pickupsMutabilityCondition,
]; ];
@@ -5,7 +5,6 @@ declare(strict_types=1);
namespace App\Form\Service; namespace App\Form\Service;
use App\BusProNet\Constants; use App\BusProNet\Constants;
use App\BusProNet\Model\Insurance;
use App\BusProNet\Model\Pickup; use App\BusProNet\Model\Pickup;
use App\BusProNet\Model\Service; use App\BusProNet\Model\Service;
use App\BusProNet\Utility\DirectionMapper; use App\BusProNet\Utility\DirectionMapper;
@@ -377,28 +376,17 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
], ],
]; ];
// Outbound Pickup (conditional - only shown when outbound transportation is bus) // Pickup (conditional - only shown when either transportation direction is bus)
$this->fieldOptionProviders['pickupOutbound'] = fn (BookingDtoInterface $bookingDto, int $participantIndex, array $options = []) => [ // Uses outbound pickups list, applies to both directions
'label' => 'Zustieg Hinfahrt', $this->fieldOptionProviders['pickup'] = fn (BookingDtoInterface $bookingDto, int $participantIndex, array $options = []) => [
'label' => 'Zu- und Ausstieg',
'choices' => $bookingDto->travel->pickupsOutbound, 'choices' => $bookingDto->travel->pickupsOutbound,
'choice_label' => fn (?Pickup $pickup) => $this->formatPickupLabelWithPrice($pickup), 'choice_label' => fn (?Pickup $pickup) => $this->formatPickupLabelWithPrice($pickup),
'choice_value' => 'id', 'choice_value' => 'id',
'expanded' => false, // Dropdown for pickups 'expanded' => false, // Dropdown for pickups
'multiple' => false, 'multiple' => false,
'required' => true, 'required' => true,
'placeholder' => 'Zustieg auswählen', 'placeholder' => 'Zu- und Ausstieg auswählen',
];
// Inbound Pickup (conditional - only shown when inbound transportation is bus)
$this->fieldOptionProviders['pickupInbound'] = fn (BookingDtoInterface $bookingDto, int $participantIndex, array $options = []) => [
'label' => 'Ausstieg Rückfahrt',
'choices' => $bookingDto->travel->pickupsInbound,
'choice_label' => fn (?Pickup $pickup) => $this->formatPickupLabelWithPrice($pickup),
'choice_value' => 'id',
'expanded' => false,
'multiple' => false,
'required' => true,
'placeholder' => 'Ausstieg auswählen',
]; ];
// Parking (conditional - only shown when outbound transportation is PKW) // Parking (conditional - only shown when outbound transportation is PKW)
@@ -574,29 +562,6 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
return $this->formatServiceLabelWithPrice($parkingService); return $this->formatServiceLabelWithPrice($parkingService);
} }
/**
* Filters services based on remaining availability in the current booking session.
*
* Removes services that have been fully booked by other participants in
* the current booking session. This prevents overbooking within a single
* booking workflow while maintaining accurate availability counts.
*
* @param array $services Array of Service objects to filter
* @param BookingDtoInterface $bookingDto The booking DTO containing participant data
* @param int $participantIndex Index of the participant currently selecting services
*
* @return array Filtered array containing only services with remaining availability
*/
private function filterServicesByAvailability(array $services, BookingDtoInterface $bookingDto, int $participantIndex): array
{
if (!$bookingDto instanceof BookingCreateDto) {
// For non-create workflows, return all services (no availability tracking needed)
return $services;
}
return $this->serviceAvailabilityCalculator->filterAvailableServices($services, $bookingDto, $participantIndex);
}
/** /**
* Checks if a service should be rendered as read-only due to unavailability. * Checks if a service should be rendered as read-only due to unavailability.
* *
@@ -710,27 +675,4 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
$bookingDto $bookingDto
); );
} }
/**
* Formats insurance label with pricing and type information.
*
* @param Insurance|null $insurance The insurance to format, or null for "No Insurance" option
*
* @return string The formatted insurance label
*/
private function formatInsuranceLabel(?Insurance $insurance): string
{
if (null === $insurance) {
return 'Keine Versicherung';
}
$label = $insurance->label;
// Add pricing information (consistent with other services)
if (null !== $insurance->price && $insurance->price > 0) {
$label .= sprintf(' (€%s)', number_format($insurance->price, 2, ',', '.'));
}
return $label;
}
} }
@@ -69,8 +69,7 @@ class ParticipantInsuranceFieldHandler extends AbstractParticipantFieldHandler
'board', // Affects travel price 'board', // Affects travel price
'transportationOutbound', // Affects travel price 'transportationOutbound', // Affects travel price
'transportationInbound', // Affects travel price 'transportationInbound', // Affects travel price
'pickupOutbound', // Affects travel price 'pickup', // Affects travel price
'pickupInbound', // Affects travel price
'parking', // Affects travel price 'parking', // Affects travel price
]; ];
} }
@@ -10,36 +10,26 @@ use App\Form\Model\BookingDtoInterface;
use App\Form\Service\Abstract\AbstractParticipantFieldHandler; use App\Form\Service\Abstract\AbstractParticipantFieldHandler;
/** /**
* Handles outbound pickup location selection. * Handles unified pickup location selection.
* *
* Pickup selection is only processed when outbound transportation * Pickup selection is only processed when either outbound or inbound
* is bus type. Automatically clears pickup when transportation * transportation is bus type. Automatically clears pickup when both
* changes to self-organized (PKW). * transportation services change to self-organized (PKW).
*
* The pickup applies to both directions and uses the outbound pickup list.
*/ */
class ParticipantPickupOutboundFieldHandler extends AbstractParticipantFieldHandler class ParticipantPickupFieldHandler extends AbstractParticipantFieldHandler
{ {
public function getFieldName(): string public function getFieldName(): string
{ {
return 'pickupOutbound'; return 'pickup';
} }
public function getDependencies(): array public function getDependencies(): array
{ {
return ['transportationOutbound']; // Must process transportation first return ['transportationOutbound', 'transportationInbound'];
} }
/**
* Determines if this handler should process the field based on submitted data.
*
* For pickup selection fields, we always need to process to handle cases
* where the pickup is cleared due to transportation changes. This ensures the
* participant DTO is updated correctly when transportation switches from bus to PKW.
*
* @param array<string, mixed> $submittedData The submitted participant form data
* @param int $participantIndex The index of the participant being processed
*
* @return bool Always returns true for pickup selection fields
*/
public function shouldProcess(array $submittedData, int $participantIndex): bool public function shouldProcess(array $submittedData, int $participantIndex): bool
{ {
return true; // Always process to handle clearing pickup return true; // Always process to handle clearing pickup
@@ -52,10 +42,15 @@ class ParticipantPickupOutboundFieldHandler extends AbstractParticipantFieldHand
return; return;
} }
// Only process pickup if outbound transportation is bus // Check if either outbound or inbound transportation is bus
if (null === $participant->transportationOutbound || DirectionMapper::SUBTYPE_BUS_API !== $participant->transportationOutbound->subType) { $hasOutboundBus = null !== $participant->transportationOutbound
$participant->pickupOutbound = null; // Clear pickup for non-bus transport && DirectionMapper::SUBTYPE_BUS_API === $participant->transportationOutbound->subType;
$participant->pickup = null; // Backward compatibility $hasInboundBus = null !== $participant->transportationInbound
&& DirectionMapper::SUBTYPE_BUS_API === $participant->transportationInbound->subType;
// Only process pickup if at least one direction has bus transport
if (false === $hasOutboundBus && false === $hasInboundBus) {
$participant->pickup = null; // Clear pickup for non-bus transport
return; return;
} }
@@ -68,8 +63,7 @@ class ParticipantPickupOutboundFieldHandler extends AbstractParticipantFieldHand
$validSelection = $this->findValidPickup($selectedPickup, $bookingDto->travel->pickupsOutbound); $validSelection = $this->findValidPickup($selectedPickup, $bookingDto->travel->pickupsOutbound);
} }
$participant->pickupOutbound = $validSelection; $participant->pickup = $validSelection;
$participant->pickup = $validSelection; // Backward compatibility
} }
/** /**
@@ -96,4 +90,4 @@ class ParticipantPickupOutboundFieldHandler extends AbstractParticipantFieldHand
return null; return null;
} }
} }
@@ -1,97 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Form\Service;
use App\BusProNet\Model\Pickup;
use App\BusProNet\Utility\DirectionMapper;
use App\Form\Model\BookingDtoInterface;
use App\Form\Service\Abstract\AbstractParticipantFieldHandler;
/**
* Handles inbound pickup location selection.
*
* Pickup selection is only processed when inbound transportation
* is bus type. Automatically clears pickup when transportation
* changes to self-organized (PKW).
*/
class ParticipantPickupInboundFieldHandler extends AbstractParticipantFieldHandler
{
public function getFieldName(): string
{
return 'pickupInbound';
}
public function getDependencies(): array
{
return ['transportationInbound']; // Must process transportation first
}
/**
* Determines if this handler should process the field based on submitted data.
*
* For pickup selection fields, we always need to process to handle cases
* where the pickup is cleared due to transportation changes. This ensures the
* participant DTO is updated correctly when transportation switches from bus to PKW.
*
* @param array<string, mixed> $submittedData The submitted participant form data
* @param int $participantIndex The index of the participant being processed
*
* @return bool Always returns true for pickup selection fields
*/
public function shouldProcess(array $submittedData, int $participantIndex): bool
{
return true; // Always process to handle clearing pickup
}
public function processField(array $submittedData, BookingDtoInterface $bookingDto, int $participantIndex): void
{
$participant = $this->getParticipant($bookingDto, $participantIndex);
if (null === $participant) {
return;
}
// Only process pickup if inbound transportation is bus
if (null === $participant->transportationInbound || DirectionMapper::SUBTYPE_BUS_API !== $participant->transportationInbound->subType) {
$participant->pickupInbound = null; // Clear pickup for non-bus transport
return;
}
$selectedPickup = $this->getFieldValue($submittedData, $this->getFieldName());
// Validate pickup selection against available inbound pickups
$validSelection = null;
if (null !== $selectedPickup) {
$validSelection = $this->findValidPickup($selectedPickup, $bookingDto->travel->pickupsInbound);
}
$participant->pickupInbound = $validSelection;
}
/**
* Finds a valid pickup from available pickups.
*
* @param mixed $selectedPickupId The submitted pickup ID
* @param array<int, Pickup> $availablePickups Array of available pickup locations
*
* @return Pickup|null The valid pickup object, or null if invalid
*/
private function findValidPickup(mixed $selectedPickupId, array $availablePickups): ?Pickup
{
if (null === $selectedPickupId || false === is_string($selectedPickupId) && false === is_int($selectedPickupId)) {
return null;
}
$pickupId = (int) $selectedPickupId;
foreach ($availablePickups as $pickup) {
if ($pickup->id === $pickupId) {
return $pickup;
}
}
return null;
}
}
+5 -14
View File
@@ -333,14 +333,9 @@ class BookingPriceCalculatorService
$participantPickupCost += $participant->transportationInbound->price; $participantPickupCost += $participant->transportationInbound->price;
} }
// Pickup pricing (outbound) // Pickup pricing (unified for both directions)
if (null !== $participant->pickupOutbound && null !== $participant->pickupOutbound->price) { if (null !== $participant->pickup && null !== $participant->pickup->price) {
$participantPickupCost += $participant->pickupOutbound->price; $participantPickupCost += $participant->pickup->price;
}
// Pickup pricing (inbound)
if (null !== $participant->pickupInbound && null !== $participant->pickupInbound->price) {
$participantPickupCost += $participant->pickupInbound->price;
} }
// Parking service pricing // Parking service pricing
@@ -563,12 +558,8 @@ class BookingPriceCalculatorService
$serviceTotal += $participant->transportationInbound->price; $serviceTotal += $participant->transportationInbound->price;
} }
if (null !== $participant->pickupOutbound && null !== $participant->pickupOutbound->price) { if (null !== $participant->pickup && null !== $participant->pickup->price) {
$serviceTotal += $participant->pickupOutbound->price; $serviceTotal += $participant->pickup->price;
}
if (null !== $participant->pickupInbound && null !== $participant->pickupInbound->price) {
$serviceTotal += $participant->pickupInbound->price;
} }
if (null !== $participant->parkingService && null !== $participant->parkingService->price) { if (null !== $participant->parkingService && null !== $participant->parkingService->price) {
@@ -54,13 +54,16 @@ class ParticipantValidator extends ConstraintValidator
public function assertPickupSelected(ParticipantDto $participant): void public function assertPickupSelected(ParticipantDto $participant): void
{ {
if ( // Check if either outbound or inbound transportation is bus
null !== $participant->transportationOutbound $hasOutboundBus = null !== $participant->transportationOutbound
&& 'BUS' === $participant->transportationOutbound->subType && 'BUS' === $participant->transportationOutbound->subType;
&& null === $participant->pickupOutbound $hasInboundBus = null !== $participant->transportationInbound
) { && 'BUS' === $participant->transportationInbound->subType;
// Require pickup if at least one direction has bus transport
if (($hasOutboundBus || $hasInboundBus) && null === $participant->pickup) {
$this->context->buildViolation('Bitte auswählen') $this->context->buildViolation('Bitte auswählen')
->atPath('pickupOutbound') ->atPath('pickup')
->addViolation() ->addViolation()
; ;
} }
+34 -47
View File
@@ -103,15 +103,12 @@
{{ form_row(participant.mobile) }} {{ form_row(participant.mobile) }}
</div> </div>
{% if participant.address is defined %} {% if participant.address is defined %}
<fieldset class="border border-gray-300 rounded p-4 mb-4"> <div class="grid grid-cols-2 gap-4">
<legend class="font-semibold px-2">{{ participant.address.vars.label }}</legend> {{ form_row(participant.address.street) }}
<div class="grid grid-cols-2 gap-4"> {{ form_row(participant.address.postCode) }}
{{ form_row(participant.address.street) }} {{ form_row(participant.address.city) }}
{{ form_row(participant.address.postCode) }} {{ form_row(participant.address.country) }}
{{ form_row(participant.address.city) }} </div>
{{ form_row(participant.address.country) }}
</div>
</fieldset>
{% endif %} {% endif %}
{% if participant.bodyDimensions is defined %} {% if participant.bodyDimensions is defined %}
<div class="grid grid-cols-2 gap-4"> <div class="grid grid-cols-2 gap-4">
@@ -268,34 +265,35 @@
} }
}) }} }) }}
{% endif %} {% endif %}
{% if participant.pickupOutbound is defined %} {% if participant.pickup is defined or participant.parking is defined or participant.licensePlate is defined %}
<div class="mt-4"> {% if participant.pickup is defined %}
{{ form_row(participant.pickupOutbound, { <div class="mt-4">
'attr': { {{ form_row(participant.pickup, {
'hx-trigger': 'change', 'attr': {
'hx-post': path('app_booking_create_step_2_refresh'), 'hx-trigger': 'change',
'hx-swap': 'none' 'hx-post': path('app_booking_create_step_2_refresh'),
} 'hx-swap': 'none'
}) }} }
</div> }) }}
</div>
{% endif %}
{% if participant.parking is defined %}
<div class="mt-4">
{{ form_row(participant.parking, {
'attr': {
'hx-trigger': 'change',
'hx-post': path('app_booking_create_step_2_refresh'),
'hx-swap': 'none'
}
}) }}
</div>
{% endif %}
{% if participant.licensePlate is defined %}
<div class="mt-4">
{{ form_row(participant.licensePlate) }}
</div>
{% endif %}
{% endif %} {% endif %}
{% if participant.parking is defined %}
<div class="mt-4">
{{ form_row(participant.parking, {
'attr': {
'hx-trigger': 'change',
'hx-post': path('app_booking_create_step_2_refresh'),
'hx-swap': 'none'
}
}) }}
</div>
{% endif %}
{% if participant.licensePlate is defined %}
<div class="mt-4">
{{ form_row(participant.licensePlate) }}
</div>
{% endif %}
</div> </div>
<div> <div>
{% if participant.transportationInbound is defined %} {% if participant.transportationInbound is defined %}
@@ -307,17 +305,6 @@
} }
}) }} }) }}
{% endif %} {% endif %}
{% if participant.pickupInbound is defined %}
<div class="mt-4">
{{ form_row(participant.pickupInbound, {
'attr': {
'hx-trigger': 'change',
'hx-post': path('app_booking_create_step_2_refresh'),
'hx-swap': 'none'
}
}) }}
</div>
{% endif %}
</div> </div>
</div> </div>
</div> </div>
+4 -10
View File
@@ -128,17 +128,11 @@
</div> </div>
{% endif %} {% endif %}
{# Pickup Locations #} {# Pickup Location #}
{% if participant.pickupOutbound %} {% if participant.pickup %}
<div> <div>
<dt class="text-gray-600 font-semibold">Zustieg Hinfahrt</dt> <dt class="text-gray-600 font-semibold">Zu- und Ausstieg</dt>
<dd>{{ participant.pickupOutbound.label }}</dd> <dd>{{ participant.pickup.label }}</dd>
</div>
{% endif %}
{% if participant.pickupInbound %}
<div>
<dt class="text-gray-600 font-semibold">Zustieg Rückfahrt</dt>
<dd>{{ participant.pickupInbound.label }}</dd>
</div> </div>
{% endif %} {% endif %}
+3 -13
View File
@@ -336,9 +336,9 @@
} }
}) }} }) }}
{% endif %} {% endif %}
{% if participant.pickupOutbound is defined %} {% if participant.pickup is defined %}
<div class="mt-4"> <div class="mt-4">
{{ form_row(participant.pickupOutbound, { {{ form_row(participant.pickup, {
'attr': { 'attr': {
'hx-trigger': 'change', 'hx-trigger': 'change',
'hx-post': path('app_booking_edit_refresh', {'id': bookingData.id}), 'hx-post': path('app_booking_edit_refresh', {'id': bookingData.id}),
@@ -381,17 +381,7 @@
} }
}) }} }) }}
{% endif %} {% endif %}
{% if participant.pickupInbound is defined %} {# Pickup inbound removed - now unified with pickup field #}
<div class="mt-4">
{{ form_row(participant.pickupInbound, {
'attr': {
'hx-trigger': 'change',
'hx-post': path('app_booking_edit_refresh', {'id': bookingData.id}),
'hx-swap': 'none'
}
}) }}
</div>
{% endif %}
</div> </div>
</div> </div>
</div> </div>
@@ -262,7 +262,7 @@ class BookingDataProcessorTest extends TestCase
$busService = $this->createMockService(1); $busService = $this->createMockService(1);
$busService->subType = 'BUS'; $busService->subType = 'BUS';
$participant->transportationOutbound = $busService; $participant->transportationOutbound = $busService;
$participant->pickupOutbound = $this->createMockPickup(1); $participant->pickup = $this->createMockPickup(1);
return $formData; return $formData;
} }
@@ -456,7 +456,7 @@ class BookingDataProcessorTest extends TestCase
$participant->rentals = []; $participant->rentals = [];
$participant->transportationOutbound = $this->createMockService(1); $participant->transportationOutbound = $this->createMockService(1);
$participant->transportationInbound = $this->createMockService(2); $participant->transportationInbound = $this->createMockService(2);
$participant->pickupOutbound = null; $participant->pickup = null;
return $participant; return $participant;
} }
@@ -112,12 +112,12 @@ class ParticipantValidatorTest extends ConstraintValidatorTestCase
$busService = new Service(); $busService = new Service();
$busService->subType = 'BUS'; $busService->subType = 'BUS';
$participant->transportationOutbound = $busService; $participant->transportationOutbound = $busService;
$participant->pickupOutbound = null; $participant->pickup = null;
$this->validator->validate($participant, new Participant()); $this->validator->validate($participant, new Participant());
$this->buildViolation('Bitte auswählen') $this->buildViolation('Bitte auswählen')
->atPath('property.path.pickupOutbound') ->atPath('property.path.pickup')
->assertRaised(); ->assertRaised();
} }
@@ -128,7 +128,7 @@ class ParticipantValidatorTest extends ConstraintValidatorTestCase
$trainService = new Service(); $trainService = new Service();
$trainService->subType = 'TRAIN'; $trainService->subType = 'TRAIN';
$participant->transportationOutbound = $trainService; $participant->transportationOutbound = $trainService;
$participant->pickupOutbound = null; // Not required for non-bus $participant->pickup = null; // Not required for non-bus
$this->validator->validate($participant, new Participant()); $this->validator->validate($participant, new Participant());