wip: modernized edit flow, fix insurance tier calculation
This commit is contained in:
@@ -15,9 +15,7 @@ use App\BusProNet\Model\PersonalData;
|
||||
use App\BusProNet\Model\Travel;
|
||||
use App\BusProNet\Traits\ApiClientTrait;
|
||||
use App\BusProNet\XmlParser\ApiResponseParser;
|
||||
use App\Form\Model\BookingCreateDto;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Model\BookingEditDto;
|
||||
use App\Form\Model\RegistrationDto;
|
||||
use League\Flysystem\FilesystemException;
|
||||
use League\Flysystem\FilesystemOperator;
|
||||
@@ -215,14 +213,14 @@ class ApiClient
|
||||
* First phase of the two-phase booking process. Validates all booking data
|
||||
* and returns pricing information without creating an actual booking.
|
||||
*
|
||||
* @param BookingCreateDto $bookingDto The booking creation form data
|
||||
* @param bool $debug Enable debug mode (XML dumps)
|
||||
* @param BookingDto $bookingDto The booking creation form data
|
||||
* @param bool $debug Enable debug mode (XML dumps)
|
||||
*
|
||||
* @return Notification|BookingResponse Notification on error, BookingResponse on success
|
||||
*
|
||||
* @throws ApiClientException If the API request fails
|
||||
*/
|
||||
public function createBookingInquiry(BookingCreateDto $bookingDto, bool $debug = false): Notification|BookingResponse
|
||||
public function createBookingInquiry(BookingDto $bookingDto, bool $debug = false): Notification|BookingResponse
|
||||
{
|
||||
$payload = $this->bookingDataProcessor->createBookingRequestPayload($bookingDto, 'Anfrage');
|
||||
|
||||
@@ -242,14 +240,14 @@ class ApiClient
|
||||
* Second phase of the two-phase booking process. Creates the actual booking
|
||||
* after successful inquiry validation.
|
||||
*
|
||||
* @param BookingCreateDto $bookingDto The booking creation form data
|
||||
* @param bool $debug Enable debug mode (XML dumps)
|
||||
* @param BookingDto $bookingDto The booking creation form data
|
||||
* @param bool $debug Enable debug mode (XML dumps)
|
||||
*
|
||||
* @return Notification|BookingResponse Notification on error, BookingResponse with booking number on success
|
||||
*
|
||||
* @throws ApiClientException If the API request fails
|
||||
*/
|
||||
public function createBooking(BookingCreateDto $bookingDto, bool $debug = false): Notification|BookingResponse
|
||||
public function createBooking(BookingDto $bookingDto, bool $debug = false): Notification|BookingResponse
|
||||
{
|
||||
$payload = $this->bookingDataProcessor->createBookingRequestPayload($bookingDto, 'Buchung');
|
||||
|
||||
|
||||
@@ -12,9 +12,7 @@ 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;
|
||||
|
||||
@@ -39,6 +37,12 @@ class BookingDataProcessor
|
||||
*
|
||||
* Extracts all service selections from the booking entity and assigns them
|
||||
* to participant DTOs, creating a unified data structure identical to create mode.
|
||||
*
|
||||
* IMPORTANT: Address handling for edit mode
|
||||
* - ParticipantDto::fromPersonalData() clones address objects to prevent shared references
|
||||
* - First participant's address is NOT auto-populated from applicant
|
||||
* - Template uses placeholders to show applicant's address as hints
|
||||
* - This prevents address data from being inadvertently modified during form binding
|
||||
*/
|
||||
public function createBookingDtoFromBooking(Booking $booking, Travel $travel): BookingDto
|
||||
{
|
||||
@@ -351,8 +355,8 @@ class BookingDataProcessor
|
||||
* 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
|
||||
* @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
|
||||
{
|
||||
@@ -654,7 +658,7 @@ class BookingDataProcessor
|
||||
* ('Anfrage') or a final booking commit ('Buchung').
|
||||
*
|
||||
* @param BookingDto $bookingDto The booking creation form data
|
||||
* @param string $bookingType Either 'Anfrage' (inquiry) or 'Buchung' (final booking)
|
||||
* @param string $bookingType Either 'Anfrage' (inquiry) or 'Buchung' (final booking)
|
||||
*
|
||||
* @return array The structured payload array for BusProNet API submission
|
||||
*/
|
||||
@@ -822,11 +826,11 @@ class BookingDataProcessor
|
||||
* - abreise (departure date)
|
||||
* - anzahl (number of rooms of this type booked)
|
||||
*
|
||||
* @param array $payload The payload array to modify
|
||||
* @param array $roomMap Map of room ID to participant IDs
|
||||
* @param BookingCreateDto $bookingDto The booking data for accessing room details and quantities
|
||||
* @param array $payload The payload array to modify
|
||||
* @param array $roomMap Map of room ID to participant IDs
|
||||
* @param BookingDto $bookingDto The booking data for accessing room details and quantities
|
||||
*/
|
||||
private function addRoomMappingsToPayload(array &$payload, array $roomMap, BookingCreateDto $bookingDto): void
|
||||
private function addRoomMappingsToPayload(array &$payload, array $roomMap, BookingDto $bookingDto): void
|
||||
{
|
||||
if (empty($roomMap)) {
|
||||
return;
|
||||
@@ -870,7 +874,7 @@ class BookingDataProcessor
|
||||
*
|
||||
* @return array<string, array<int>> Map of room ID to participant IDs
|
||||
*/
|
||||
private function collectRoomMappings(BookingCreateDto $bookingDto): array
|
||||
private function collectRoomMappings(BookingDto $bookingDto): array
|
||||
{
|
||||
$roomMap = [];
|
||||
|
||||
@@ -893,7 +897,7 @@ class BookingDataProcessor
|
||||
*
|
||||
* @return array<string, array<int>> Map of service ID to participant IDs
|
||||
*/
|
||||
private function collectServiceMappings(BookingCreateDto $bookingDto): array
|
||||
private function collectServiceMappings(BookingDto $bookingDto): array
|
||||
{
|
||||
$serviceMap = [];
|
||||
|
||||
@@ -944,7 +948,7 @@ class BookingDataProcessor
|
||||
*
|
||||
* @return array<string, array<int>> Map of transportation service ID to participant IDs
|
||||
*/
|
||||
private function collectTransportationMappings(BookingCreateDto $bookingDto): array
|
||||
private function collectTransportationMappings(BookingDto $bookingDto): array
|
||||
{
|
||||
$transportationMap = [];
|
||||
|
||||
@@ -971,7 +975,7 @@ class BookingDataProcessor
|
||||
*
|
||||
* @return array<string, array<int>> Map of pickup ID to participant IDs
|
||||
*/
|
||||
private function collectPickupMappings(BookingCreateDto $bookingDto): array
|
||||
private function collectPickupMappings(BookingDto $bookingDto): array
|
||||
{
|
||||
$pickupMap = [];
|
||||
|
||||
@@ -993,7 +997,7 @@ class BookingDataProcessor
|
||||
*
|
||||
* @return array<string, array<int>> Map of insurance ID to participant IDs
|
||||
*/
|
||||
private function collectInsuranceMappings(BookingCreateDto $bookingDto): array
|
||||
private function collectInsuranceMappings(BookingDto $bookingDto): array
|
||||
{
|
||||
$insuranceMap = [];
|
||||
|
||||
@@ -1033,7 +1037,12 @@ class BookingDataProcessor
|
||||
}
|
||||
|
||||
// Get all available insurances from travel data
|
||||
$availableInsurances = array_values($bookingDto->travel->insurances);
|
||||
$availableInsurances = $bookingDto->travel->insurances;
|
||||
|
||||
// Exclude complementary insurances from bulk assignment logic
|
||||
// They are only available as part of packages and cannot be directly selected
|
||||
$availableInsurances = array_filter($availableInsurances, fn ($insurance) => !$insurance->complementary);
|
||||
$availableInsurances = array_values($availableInsurances);
|
||||
|
||||
// Use InsuranceMatchingService for proper type-based assignment with price tier matching
|
||||
$assignments = $this->insuranceMatchingService->batchAssignInsuranceToParticipants(
|
||||
@@ -1055,7 +1064,7 @@ class BookingDataProcessor
|
||||
|
||||
// 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) {
|
||||
if (BookingDto::MODE_EDIT === $bookingDto->getMode() && null !== $participant->insurance) {
|
||||
continue; // Skip participants with existing insurance assignment
|
||||
}
|
||||
|
||||
|
||||
@@ -96,4 +96,14 @@ class Service
|
||||
|
||||
#[Groups(['api:single', 'api:list'])]
|
||||
public ?string $description = null;
|
||||
|
||||
/**
|
||||
* Indicates whether this service should be included in insurance eligibility price calculation.
|
||||
*
|
||||
* Maps to XML attribute 'versicherungsberechnung' (J=true, N=false).
|
||||
* When false, this service's price is excluded when determining which insurance
|
||||
* tier is appropriate for a participant.
|
||||
*/
|
||||
#[Groups(['api:single', 'api:list'])]
|
||||
public bool $includeInInsuranceCalculation = true;
|
||||
}
|
||||
|
||||
@@ -145,6 +145,9 @@ class TravelParser extends AbstractParser
|
||||
// Parse age constraints
|
||||
$this->parseServiceAgeConstraints($serviceNode, $service);
|
||||
|
||||
// Parse insurance calculation flag
|
||||
$service->includeInInsuranceCalculation = $this->stringToBool($serviceNode->attr('versicherungsberechnung'));
|
||||
|
||||
$additionalServices[$serviceId] = $service;
|
||||
});
|
||||
|
||||
@@ -192,6 +195,9 @@ class TravelParser extends AbstractParser
|
||||
$service->dayTime = (new DayTimeUtility())->mapTime($timeFrom);
|
||||
}
|
||||
|
||||
// Parse insurance calculation flag
|
||||
$service->includeInInsuranceCalculation = $this->stringToBool($serviceNode->attr('versicherungsberechnung'));
|
||||
|
||||
$transportationServices[$serviceId] = $service;
|
||||
});
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Controller\Booking;
|
||||
|
||||
use App\Form\Model\BookingCreateDto;
|
||||
use App\Form\Model\BookingDto;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
|
||||
/**
|
||||
@@ -21,7 +21,7 @@ trait BookingCreateTrait
|
||||
*
|
||||
* @return RedirectResponse|null Returns redirect response if validation fails, null if access is allowed
|
||||
*/
|
||||
private function validateStepAccess(BookingCreateDto $bookingCreateDto, int $expectedStep): ?RedirectResponse
|
||||
private function validateStepAccess(BookingDto $bookingCreateDto, int $expectedStep): ?RedirectResponse
|
||||
{
|
||||
// Allow access to current step or any previous step
|
||||
if ($expectedStep > $bookingCreateDto->currentStep) {
|
||||
@@ -36,7 +36,7 @@ trait BookingCreateTrait
|
||||
/**
|
||||
* Redirects to the current step based on the DTO's currentStep.
|
||||
*/
|
||||
private function redirectToCurrentStep(BookingCreateDto $bookingCreateDto): RedirectResponse
|
||||
private function redirectToCurrentStep(BookingDto $bookingCreateDto): RedirectResponse
|
||||
{
|
||||
$route = match ($bookingCreateDto->currentStep) {
|
||||
2 => 'app_booking_create_step_2',
|
||||
@@ -51,7 +51,7 @@ trait BookingCreateTrait
|
||||
/**
|
||||
* Returns the total number of participants based on room selections.
|
||||
*/
|
||||
private function getParticipantsCount(BookingCreateDto $bookingCreateDto): int
|
||||
private function getParticipantsCount(BookingDto $bookingCreateDto): int
|
||||
{
|
||||
return $this
|
||||
->bookingService
|
||||
@@ -63,7 +63,7 @@ trait BookingCreateTrait
|
||||
*
|
||||
* @return array<string, mixed> Array containing all variables needed for the summary partial
|
||||
*/
|
||||
private function getSummaryVariables(BookingCreateDto $bookingCreateDto): array
|
||||
private function getSummaryVariables(BookingDto $bookingCreateDto): array
|
||||
{
|
||||
$participantsCount = $this->getParticipantsCount($bookingCreateDto);
|
||||
$summary = $this->bookingService->getRoomSummaryAndParticipantCount($bookingCreateDto);
|
||||
|
||||
@@ -9,7 +9,9 @@ use App\Exception\HotelNotFoundException;
|
||||
use App\Exception\HotelNotInTravelException;
|
||||
use App\Exception\NoRoomsAvailableException;
|
||||
use App\Exception\TravelNotFoundException;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Service\BookingService;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
@@ -27,7 +29,7 @@ trait BookingExceptionHandlerTrait
|
||||
* Handles all booking-related exceptions and provides appropriate user feedback
|
||||
* by redirecting to the error page with flash messages.
|
||||
*/
|
||||
protected function getOrCreateBookingCreateDto(BookingService $bookingService, Request $request): mixed
|
||||
protected function getOrCreateBookingCreateDto(BookingService $bookingService, Request $request): BookingDto|RedirectResponse
|
||||
{
|
||||
try {
|
||||
return $bookingService->getOrCreateBookingCreateDto($request);
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace App\Controller\Booking;
|
||||
|
||||
use App\Controller\Traits\HtmxControllerTrait;
|
||||
use App\Form\BookingCreateStep2Type;
|
||||
use App\Form\Model\BookingCreateDto;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Model\ParticipantDto;
|
||||
use App\Service\BookingPriceCalculatorService;
|
||||
use App\Service\BookingService;
|
||||
@@ -67,7 +67,7 @@ class CreateStep2Controller extends AbstractController
|
||||
// Pre-select mandatory services for participants with birth dates
|
||||
$this->bookingService->preselectMandatoryServices($bookingCreateDto);
|
||||
|
||||
$this->bookingService->saveBookingCreateDto($request, $bookingCreateDto);
|
||||
$this->bookingService->saveBookingDto($request, $bookingCreateDto, BookingDto::MODE_CREATE);
|
||||
|
||||
$form = $this->createForm(BookingCreateStep2Type::class, $bookingCreateDto, [
|
||||
'attr' => ['novalidate' => 'novalidate'],
|
||||
@@ -78,7 +78,7 @@ class CreateStep2Controller extends AbstractController
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$bookingCreateDto->currentStep = 3;
|
||||
$this->bookingService->saveBookingCreateDto($request, $bookingCreateDto);
|
||||
$this->bookingService->saveBookingDto($request, $bookingCreateDto, BookingDto::MODE_CREATE);
|
||||
|
||||
return $this->redirectToRoute('app_booking_create_step_3');
|
||||
}
|
||||
@@ -129,7 +129,7 @@ class CreateStep2Controller extends AbstractController
|
||||
// Pre-select mandatory services after form processing but before pricing calculation
|
||||
$this->bookingService->preselectMandatoryServices($bookingCreateDto);
|
||||
|
||||
$this->bookingService->saveBookingCreateDto($request, $bookingCreateDto);
|
||||
$this->bookingService->saveBookingDto($request, $bookingCreateDto, BookingDto::MODE_CREATE);
|
||||
|
||||
// Collect notifications from all participants
|
||||
$notifications = $this->collectParticipantNotifications($bookingCreateDto);
|
||||
@@ -173,9 +173,9 @@ class CreateStep2Controller extends AbstractController
|
||||
* based on room selections. Preserves existing participant data when possible
|
||||
* and assigns proper index values.
|
||||
*
|
||||
* @param BookingCreateDto $bookingCreateDto The booking DTO to update
|
||||
* @param BookingDto $bookingCreateDto The booking DTO to update
|
||||
*/
|
||||
private function ensureCorrectNumberOfParticipants(BookingCreateDto $bookingCreateDto): void
|
||||
private function ensureCorrectNumberOfParticipants(BookingDto $bookingCreateDto): void
|
||||
{
|
||||
$participantsCount = $this->getParticipantsCount($bookingCreateDto);
|
||||
|
||||
@@ -195,13 +195,13 @@ class CreateStep2Controller extends AbstractController
|
||||
* to ensure service availability is reasonably up-to-date while reducing API calls.
|
||||
* This is essential for accurate pricing and service selection during the booking process.
|
||||
*
|
||||
* @param BookingCreateDto $bookingCreateDto The booking DTO containing travel data to enrich
|
||||
* @param BookingDto $bookingCreateDto The booking DTO containing travel data to enrich
|
||||
*/
|
||||
private function enrichWithFreshAvailabilities(BookingCreateDto $bookingCreateDto): void
|
||||
private function enrichWithFreshAvailabilities(BookingDto $bookingCreateDto): void
|
||||
{
|
||||
$dateId = $bookingCreateDto->travel->id;
|
||||
|
||||
$availabilities = $this->travelDataService->getAvailabilityDataCached($dateId);
|
||||
$availabilities = $this->travelDataService->getAvailabilityData($dateId, true);
|
||||
|
||||
if (null !== $availabilities) {
|
||||
$this->travelDataService->patchAvailabilities($bookingCreateDto->travel, $availabilities);
|
||||
@@ -214,9 +214,9 @@ class CreateStep2Controller extends AbstractController
|
||||
* This is called when entering Step 2 to ensure all participants have room assignments
|
||||
* based on the selected rooms from Step 1. Only assigns if participants are unassigned.
|
||||
*
|
||||
* @param BookingCreateDto $bookingCreateDto The booking DTO with participants and room selections
|
||||
* @param BookingDto $bookingCreateDto The booking DTO with participants and room selections
|
||||
*/
|
||||
private function autoAssignRoomsIfNeeded(BookingCreateDto $bookingCreateDto): void
|
||||
private function autoAssignRoomsIfNeeded(BookingDto $bookingCreateDto): void
|
||||
{
|
||||
// Check if any participants need room assignment
|
||||
$needsAssignment = false;
|
||||
@@ -235,11 +235,11 @@ class CreateStep2Controller extends AbstractController
|
||||
/**
|
||||
* Collects all notifications from participants and clears them.
|
||||
*
|
||||
* @param BookingCreateDto $bookingCreateDto The booking DTO containing participants
|
||||
* @param BookingDto $bookingCreateDto The booking DTO containing participants
|
||||
*
|
||||
* @return array<array{type: string, message: string}> Array of notification messages
|
||||
*/
|
||||
private function collectParticipantNotifications(BookingCreateDto $bookingCreateDto): array
|
||||
private function collectParticipantNotifications(BookingDto $bookingCreateDto): array
|
||||
{
|
||||
$notifications = [];
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace App\Form;
|
||||
|
||||
use App\Form\Model\BookingCreateDto;
|
||||
use App\Form\Model\BookingDto;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
@@ -30,7 +30,7 @@ class BookingCreateStep1Type extends AbstractType
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => BookingCreateDto::class,
|
||||
'data_class' => BookingDto::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace App\Form;
|
||||
|
||||
use App\Form\Model\BookingCreateDto;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\ParticipantFieldHandlerRegistry;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
|
||||
@@ -31,7 +31,7 @@ class BookingCreateStep2Type extends AbstractType
|
||||
*/
|
||||
public function onPreSetData(FormEvent $event): void
|
||||
{
|
||||
/** @var BookingCreateDto|null $data */
|
||||
/** @var BookingDto|null $data */
|
||||
$data = $event->getData();
|
||||
if (null === $data) {
|
||||
return;
|
||||
@@ -43,7 +43,7 @@ class BookingCreateStep2Type extends AbstractType
|
||||
/**
|
||||
* Handles dynamic participant form field updates on POST requests (e.g., from HTMX).
|
||||
*
|
||||
* This listener synchronizes the BookingCreateDto with the submitted participant data *before*
|
||||
* This listener synchronizes the BookingDto with the submitted participant data *before*
|
||||
* the form's children are processed. It then rebuilds the participants
|
||||
* field to ensure choice loaders are created with the fresh state.
|
||||
*/
|
||||
@@ -52,7 +52,7 @@ class BookingCreateStep2Type extends AbstractType
|
||||
$form = $event->getForm();
|
||||
$submittedData = $event->getData();
|
||||
|
||||
/** @var BookingCreateDto $bookingDto */
|
||||
/** @var BookingDto $bookingDto */
|
||||
$bookingDto = $form->getData();
|
||||
|
||||
// Process field handlers and synchronize submitted data with cleaned DTO state
|
||||
@@ -81,7 +81,7 @@ class BookingCreateStep2Type extends AbstractType
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => BookingCreateDto::class,
|
||||
'data_class' => BookingDto::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ declare(strict_types=1);
|
||||
namespace App\Form;
|
||||
|
||||
use App\BusProNet\Constants;
|
||||
use App\Form\Model\BookingCreateDto;
|
||||
use App\Form\Model\BookingDto;
|
||||
use Carbon\CarbonImmutable;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
@@ -69,7 +69,7 @@ class BookingCreateStep3Type extends AbstractType
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => BookingCreateDto::class,
|
||||
'data_class' => BookingDto::class,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ class BookingCreateStep3Type extends AbstractType
|
||||
*
|
||||
* Direct debit is only available if the travel starts at least 14 days from now.
|
||||
*/
|
||||
private function isDebitAvailable(BookingCreateDto $bookingDto): bool
|
||||
private function isDebitAvailable(BookingDto $bookingDto): bool
|
||||
{
|
||||
$travelStartDate = CarbonImmutable::instance($bookingDto->travel->dateFrom);
|
||||
$now = CarbonImmutable::now();
|
||||
|
||||
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Form;
|
||||
|
||||
use App\Form\Model\BookingCreateDto;
|
||||
use App\Form\Model\BookingDto;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
@@ -31,7 +31,7 @@ class BookingCreateStep4Type extends AbstractType
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => BookingCreateDto::class,
|
||||
'data_class' => BookingDto::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,151 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Form\Model;
|
||||
|
||||
use App\BusProNet\Constants;
|
||||
use App\BusProNet\Model\Travel;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
|
||||
class BookingCreateDto implements BookingDtoInterface
|
||||
{
|
||||
public int $currentStep = 1;
|
||||
|
||||
/**
|
||||
* @var array<int, RoomSelectionDto>
|
||||
*/
|
||||
#[Assert\Valid]
|
||||
public array $roomSelections = [];
|
||||
|
||||
/**
|
||||
* @var array<int, ParticipantDto>
|
||||
*/
|
||||
#[Assert\Valid]
|
||||
public array $participants = [];
|
||||
|
||||
#[Assert\Choice(
|
||||
choices: [Constants::PAYMENT_METHOD_TRANSFER, Constants::PAYMENT_METHOD_DEBIT],
|
||||
message: 'Bitte wählen Sie eine gültige Zahlungsart.'
|
||||
)]
|
||||
public ?string $paymentMethod = Constants::PAYMENT_METHOD_TRANSFER;
|
||||
|
||||
public ?BankAccountDto $bankAccount = null;
|
||||
|
||||
public ?int $agencyId = null;
|
||||
|
||||
public function __construct(public Travel $travel, public int $hotelId)
|
||||
{
|
||||
}
|
||||
|
||||
public function getMode(): string
|
||||
{
|
||||
return BookingDtoInterface::MODE_CREATE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, RoomSelectionDto>
|
||||
*/
|
||||
public function getSelectedRooms(): array
|
||||
{
|
||||
return array_filter($this->roomSelections, function (RoomSelectionDto $roomSelection) {
|
||||
return 0 < $roomSelection->quantity;
|
||||
});
|
||||
}
|
||||
|
||||
public function getParticipants(): array
|
||||
{
|
||||
return $this->participants;
|
||||
}
|
||||
|
||||
public function hasParticipant(int $index): bool
|
||||
{
|
||||
return isset($this->participants[$index]);
|
||||
}
|
||||
|
||||
public function getParticipant(int $index): ?ParticipantDto
|
||||
{
|
||||
return $this->participants[$index] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if this is a family booking based on participant age distribution.
|
||||
*
|
||||
* A family booking is defined as:
|
||||
* - 1 or 2 participants aged 18 or older (adults)
|
||||
* - At least 1 participant younger than 18 (children)
|
||||
*
|
||||
* @return bool True if this qualifies as a family booking
|
||||
*/
|
||||
public function isFamilyBooking(): bool
|
||||
{
|
||||
$adults = 0; // Count of participants >= 18 years
|
||||
$children = 0; // Count of participants < 18 years
|
||||
|
||||
// Use travel start date for age calculation
|
||||
$travelStartDate = $this->travel->dateFrom;
|
||||
|
||||
foreach ($this->participants as $participant) {
|
||||
$age = $participant->getAge($travelStartDate);
|
||||
|
||||
if (null === $age) {
|
||||
continue; // Skip participants without birth date
|
||||
}
|
||||
|
||||
if ($age >= 18) {
|
||||
++$adults;
|
||||
} else {
|
||||
++$children;
|
||||
}
|
||||
}
|
||||
|
||||
return ($adults >= 1 && $adults <= 2) && ($children >= 1);
|
||||
}
|
||||
|
||||
#[Assert\Callback(callback: 'validateRoomSelection', groups: ['booking_create_step_1'])]
|
||||
public function validateRoomSelection(ExecutionContextInterface $context): void
|
||||
{
|
||||
$selectedRooms = $this->getSelectedRooms();
|
||||
|
||||
if (0 === count($selectedRooms)) {
|
||||
$context->buildViolation('Bitte mindestens ein Zimmer/Bett auswählen')
|
||||
->addViolation();
|
||||
}
|
||||
}
|
||||
|
||||
#[Assert\Callback]
|
||||
public function validateBankAccount(ExecutionContextInterface $context): void
|
||||
{
|
||||
if (Constants::PAYMENT_METHOD_DEBIT !== $this->paymentMethod) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (null === $this->bankAccount) {
|
||||
$context->buildViolation('Bitte geben Sie Ihre Bankverbindung an.')
|
||||
->atPath('bankAccount')
|
||||
->addViolation();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate IBAN
|
||||
if (null === $this->bankAccount->iban || '' === trim($this->bankAccount->iban)) {
|
||||
$context->buildViolation('Bitte geben Sie Ihre IBAN ein.')
|
||||
->atPath('bankAccount.iban')
|
||||
->addViolation();
|
||||
}
|
||||
|
||||
// Validate account holder
|
||||
if (null === $this->bankAccount->accountHolder || '' === trim($this->bankAccount->accountHolder)) {
|
||||
$context->buildViolation('Bitte geben Sie den Kontoinhaber ein.')
|
||||
->atPath('bankAccount.accountHolder')
|
||||
->addViolation();
|
||||
}
|
||||
|
||||
// Validate SEPA mandate
|
||||
if (false === $this->bankAccount->sepaMandateAccepted) {
|
||||
$context->buildViolation('Bitte akzeptieren Sie das SEPA-Mandat.')
|
||||
->atPath('bankAccount.sepaMandateAccepted')
|
||||
->addViolation();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Form\Model;
|
||||
|
||||
/**
|
||||
* Interface for unified access to booking data across create and edit workflows.
|
||||
*
|
||||
* This interface provides a consistent API for accessing common booking data
|
||||
* regardless of whether the data comes from a creation workflow (BookingCreateDto)
|
||||
* or an edit workflow (BookingEditDto). This enables code reuse and simplifies
|
||||
* form field handlers and state management systems.
|
||||
*/
|
||||
interface BookingDtoInterface
|
||||
{
|
||||
public const MODE_CREATE = 'create';
|
||||
public const MODE_EDIT = 'edit';
|
||||
|
||||
/**
|
||||
* Gets the booking mode (create or edit).
|
||||
*
|
||||
* @return string One of MODE_CREATE or MODE_EDIT constants
|
||||
*/
|
||||
public function getMode(): string;
|
||||
|
||||
/**
|
||||
* Gets all participants in the booking.
|
||||
*
|
||||
* @return array<int, ParticipantDto> Array of participant DTOs indexed by participant index
|
||||
*/
|
||||
public function getParticipants(): array;
|
||||
|
||||
/**
|
||||
* Checks if a participant exists at the given index.
|
||||
*
|
||||
* @param int $index The participant index to check
|
||||
*
|
||||
* @return bool True if a participant exists at the given index
|
||||
*/
|
||||
public function hasParticipant(int $index): bool;
|
||||
|
||||
/**
|
||||
* Gets a participant by index.
|
||||
*
|
||||
* @param int $index The participant index
|
||||
*
|
||||
* @return ParticipantDto|null The participant DTO or null if not found
|
||||
*/
|
||||
public function getParticipant(int $index): ?ParticipantDto;
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Form\Model;
|
||||
|
||||
use App\BusProNet\Constants;
|
||||
use App\BusProNet\Model\Booking;
|
||||
use App\BusProNet\Model\PersonalData;
|
||||
use App\BusProNet\Model\Travel;
|
||||
use App\BusProNet\Utility\DirectionMapper;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
class BookingEditDto implements BookingDtoInterface
|
||||
{
|
||||
/**
|
||||
* @var array<int, ParticipantDto>
|
||||
*/
|
||||
#[Assert\Valid]
|
||||
public array $participants = [];
|
||||
|
||||
public function __construct(public Booking $booking, public Travel $travel)
|
||||
{
|
||||
}
|
||||
|
||||
public function getMode(): string
|
||||
{
|
||||
return BookingDtoInterface::MODE_EDIT;
|
||||
}
|
||||
|
||||
public static function fromBooking(Booking $booking, Travel $travel): static
|
||||
{
|
||||
$instance = new static($booking, $travel);
|
||||
|
||||
$instance->booking = $booking;
|
||||
$instance->travel = $travel;
|
||||
|
||||
foreach ($booking->participants as $index => $participant) {
|
||||
/** @var PersonalData $participant */
|
||||
// For the first participant (applicant), use applicant data instead of participant data
|
||||
$personalData = 0 === $index && null !== $booking->applicant ? $booking->applicant : $participant;
|
||||
$participantData = ParticipantDto::fromPersonalData($personalData);
|
||||
$participantData->index = $index;
|
||||
|
||||
$participantData->courses = $booking
|
||||
->getAdditionalServicesForParticipantByGroup($index, Constants::TOKEN_COURSES);
|
||||
|
||||
// Skipass is single selection - use dedicated method
|
||||
$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);
|
||||
// Transportation services using improved direction mapping
|
||||
// Direction mapping handles BusProNet's inconsistent codes (H <=> HIN, R <=> RUECK)
|
||||
$outboundTransportation = $booking
|
||||
->getTransportationServiceForParticipantAndDirection($index, DirectionMapper::OUTBOUND_BOOKING);
|
||||
$inboundTransportation = $booking
|
||||
->getTransportationServiceForParticipantAndDirection($index, DirectionMapper::INBOUND_BOOKING);
|
||||
|
||||
// Set new improved property names
|
||||
$participantData->transportationOutbound = $outboundTransportation;
|
||||
$participantData->transportationInbound = $inboundTransportation;
|
||||
|
||||
// Pickup handling (currently only supports outbound pickup)
|
||||
$pickup = $booking->getPickupForParticipant($index);
|
||||
$participantData->pickup = $pickup;
|
||||
|
||||
// Insurance - get insurance for participant
|
||||
$insurance = $booking->getInsuranceForParticipant($index);
|
||||
$participantData->insurance = $insurance;
|
||||
|
||||
// Room assignment - extract from booking room mappings
|
||||
$room = $booking->getRoomForParticipant($index);
|
||||
$participantData->assignedRoomId = $room?->id;
|
||||
|
||||
$instance->participants[$index] = $participantData;
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
public function isCanceled(): bool
|
||||
{
|
||||
return 'S' === $this->booking->status;
|
||||
}
|
||||
|
||||
public function isOption(): bool
|
||||
{
|
||||
return 'O' === $this->booking->status;
|
||||
}
|
||||
|
||||
public function getParticipants(): array
|
||||
{
|
||||
return $this->participants;
|
||||
}
|
||||
|
||||
public function hasParticipant(int $index): bool
|
||||
{
|
||||
return isset($this->participants[$index]);
|
||||
}
|
||||
|
||||
public function getParticipant(int $index): ?ParticipantDto
|
||||
{
|
||||
return $this->participants[$index] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all selected rooms for the booking (edit context).
|
||||
*
|
||||
* In edit mode, rooms are fixed and not selectable - returns empty array.
|
||||
* Participant count should be derived from actual participants, not room selections.
|
||||
*
|
||||
* @return array<int, RoomSelectionDto>
|
||||
*/
|
||||
public function getSelectedRooms(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ declare(strict_types=1);
|
||||
namespace App\Form;
|
||||
|
||||
use App\BusProNet\Constants;
|
||||
use App\Form\Model\BookingCreateDto;
|
||||
use App\Form\Model\BookingDto;
|
||||
use Carbon\CarbonImmutable;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
@@ -95,7 +95,7 @@ class PaymentType extends AbstractType
|
||||
*
|
||||
* Direct debit is only available if the travel starts at least 14 days from now.
|
||||
*/
|
||||
private function isDebitAvailable(BookingCreateDto $bookingDto): bool
|
||||
private function isDebitAvailable(BookingDto $bookingDto): bool
|
||||
{
|
||||
$travelStartDate = CarbonImmutable::instance($bookingDto->travel->dateFrom);
|
||||
$now = CarbonImmutable::now();
|
||||
|
||||
@@ -194,6 +194,16 @@ class CreateFieldStateProvider extends AbstractFieldStateProvider
|
||||
'hidden' => FieldValueCondition::equals('parking', false),
|
||||
];
|
||||
|
||||
// Make mobile field required for applicant (participant index 0)
|
||||
$this->fieldStateConditions['mobile'] = [
|
||||
'required' => new ApplicantCondition(),
|
||||
];
|
||||
|
||||
// Make address field required for applicant (participant index 0)
|
||||
$this->fieldStateConditions['address'] = [
|
||||
'required' => new ApplicantCondition(),
|
||||
];
|
||||
|
||||
// Example field state conditions would be registered here
|
||||
// For demonstration purposes, here are some example patterns:
|
||||
|
||||
|
||||
@@ -8,9 +8,7 @@ use App\BusProNet\Constants;
|
||||
use App\BusProNet\Model\Pickup;
|
||||
use App\BusProNet\Model\Service;
|
||||
use App\BusProNet\Utility\DirectionMapper;
|
||||
use App\Form\Model\BookingCreateDto;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Model\BookingEditDto;
|
||||
use App\Form\Service\Abstract\AbstractFieldOptionsProvider;
|
||||
use App\Form\Service\Factory\ParticipantRoomChoiceLoaderFactory;
|
||||
use App\Service\InsuranceMatchingService;
|
||||
@@ -462,9 +460,9 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
* as the participant's selected skipass. This ensures rental equipment
|
||||
* is only available for the exact duration of the skipass.
|
||||
*
|
||||
* @param array $rentals Array of rental Service objects to filter
|
||||
* @param array $rentals Array of rental Service objects to filter
|
||||
* @param BookingDto $bookingDto The booking DTO containing participant data
|
||||
* @param int $participantIndex Index of the participant to evaluate
|
||||
* @param int $participantIndex Index of the participant to evaluate
|
||||
*
|
||||
* @return array Filtered array of rentals matching skipass duration
|
||||
*/
|
||||
@@ -587,15 +585,15 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
/**
|
||||
* Checks if a service should be rendered as read-only due to unavailability.
|
||||
*
|
||||
* @param Service $service The service to check
|
||||
* @param Service $service The service to check
|
||||
* @param BookingDto $bookingDto The booking DTO containing participant data
|
||||
* @param int $participantIndex Index of the participant currently selecting services
|
||||
* @param int $participantIndex Index of the participant currently selecting services
|
||||
*
|
||||
* @return bool True if the service should be read-only due to unavailability
|
||||
*/
|
||||
private function isServiceUnavailableForParticipant(Service $service, BookingDto $bookingDto, int $participantIndex): bool
|
||||
{
|
||||
if (!$bookingDto instanceof BookingCreateDto) {
|
||||
if (BookingDto::MODE_EDIT === $bookingDto->getMode()) {
|
||||
// For non-create workflows, don't apply availability restrictions
|
||||
return false;
|
||||
}
|
||||
@@ -610,9 +608,9 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
* If no age evaluator is configured or participant has no birth date,
|
||||
* returns empty array to be handled by field visibility conditions.
|
||||
*
|
||||
* @param array $services Array of Service objects to filter
|
||||
* @param array $services Array of Service objects to filter
|
||||
* @param BookingDto $bookingDto The booking DTO containing participant data
|
||||
* @param int $participantIndex Index of the participant to evaluate
|
||||
* @param int $participantIndex Index of the participant to evaluate
|
||||
*
|
||||
* @return array Filtered array of available services
|
||||
*/
|
||||
@@ -667,7 +665,7 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
* Gets eligible insurances for a participant based on eligibility criteria.
|
||||
*
|
||||
* @param BookingDto $bookingDto The booking DTO containing travel and participant data
|
||||
* @param int $participantIndex The index of the participant to get eligible insurances for
|
||||
* @param int $participantIndex The index of the participant to get eligible insurances for
|
||||
*
|
||||
* @return array Array of eligible insurance objects filtered by age, family status, and other constraints
|
||||
*/
|
||||
|
||||
@@ -125,6 +125,10 @@ class ParticipantInsuranceFieldHandler extends AbstractParticipantFieldHandler
|
||||
$currentInsurance = $participant->insurance;
|
||||
$availableInsurances = $bookingDto->travel->insurances ?? [];
|
||||
|
||||
// Exclude complementary insurances from reassignment logic
|
||||
// They are only available as part of packages and cannot be directly selected
|
||||
$availableInsurances = array_filter($availableInsurances, fn ($insurance) => !$insurance->complementary);
|
||||
|
||||
// Determine if this is a new user selection or just form resubmission
|
||||
$isNewSelection = null !== $selectedInsuranceId
|
||||
&& (null === $currentInsurance || !$this->isSameInsurance($selectedInsuranceId, $currentInsurance));
|
||||
|
||||
@@ -8,7 +8,6 @@ use App\BusProNet\Constants;
|
||||
use App\BusProNet\Model\Insurance;
|
||||
use App\BusProNet\Model\Room;
|
||||
use App\BusProNet\Model\Service;
|
||||
use App\Form\Model\BookingCreateDto;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Model\ParticipantDto;
|
||||
|
||||
@@ -57,10 +56,6 @@ class BookingPriceCalculatorService
|
||||
{
|
||||
$roomPricing = [];
|
||||
|
||||
if (false === $bookingDto instanceof BookingCreateDto) {
|
||||
return $roomPricing;
|
||||
}
|
||||
|
||||
$selectedRooms = $bookingDto->getSelectedRooms();
|
||||
if (true === empty($selectedRooms)) {
|
||||
return $roomPricing;
|
||||
@@ -196,16 +191,12 @@ class BookingPriceCalculatorService
|
||||
* including their room allocation (full room price) and all selected services.
|
||||
*
|
||||
* @param BookingDto $bookingDto The booking data containing all participants
|
||||
* @param int $participantIndex The index of the participant to calculate for
|
||||
* @param int $participantIndex The index of the participant to calculate for
|
||||
*
|
||||
* @return float The total price for the specified participant
|
||||
*/
|
||||
public function calculateIndividualParticipantPrice(BookingDto $bookingDto, int $participantIndex): float
|
||||
{
|
||||
if (false === $bookingDto instanceof BookingCreateDto) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
$participant = $bookingDto->getParticipant($participantIndex);
|
||||
if (null === $participant) {
|
||||
return 0.0;
|
||||
@@ -237,10 +228,6 @@ class BookingPriceCalculatorService
|
||||
*/
|
||||
public function calculateAllParticipantIndividualPrices(BookingDto $bookingDto): array
|
||||
{
|
||||
if (false === $bookingDto instanceof BookingCreateDto) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$participantPrices = [];
|
||||
$participants = $bookingDto->getParticipants();
|
||||
|
||||
@@ -257,17 +244,17 @@ class BookingPriceCalculatorService
|
||||
* This method is used for insurance eligibility filtering to avoid circular dependency
|
||||
* where insurance selection affects travel price which affects insurance eligibility.
|
||||
*
|
||||
* @param BookingDto $bookingDto The booking data containing all participants
|
||||
* @param int $participantIndex The index of the participant to calculate for
|
||||
* Only includes services where versicherungsberechnung='J' in the XML. Services with
|
||||
* versicherungsberechnung='N' (like CO² compensation) are excluded from the calculation
|
||||
* as per BPN API requirements for insurance tier determination.
|
||||
*
|
||||
* @return float The total price for the specified participant excluding insurance
|
||||
* @param BookingDto $bookingDto The booking data containing all participants
|
||||
* @param int $participantIndex The index of the participant to calculate for
|
||||
*
|
||||
* @return float The total price for the specified participant excluding insurance and non-calculated services
|
||||
*/
|
||||
public function calculateIndividualParticipantPriceExcludingInsurance(BookingDto $bookingDto, int $participantIndex): float
|
||||
{
|
||||
if (false === $bookingDto instanceof BookingCreateDto) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
$participant = $bookingDto->getParticipant($participantIndex);
|
||||
if (null === $participant) {
|
||||
return 0.0;
|
||||
@@ -285,7 +272,8 @@ class BookingPriceCalculatorService
|
||||
}
|
||||
|
||||
// Add service prices for this participant (excluding insurance)
|
||||
$totalPrice += $this->calculateParticipantServiceTotal($participant, false);
|
||||
// For insurance eligibility calculation, only include services marked with versicherungsberechnung='J'
|
||||
$totalPrice += $this->calculateParticipantServiceTotal($participant, false, null, true);
|
||||
|
||||
return $totalPrice;
|
||||
}
|
||||
@@ -528,23 +516,28 @@ class BookingPriceCalculatorService
|
||||
/**
|
||||
* Calculates the total service cost for a single participant.
|
||||
*
|
||||
* @param ParticipantDto $participant The participant to calculate services for
|
||||
* @param bool $includeInsurance Whether to include insurance pricing (default: true)
|
||||
* @param BookingDto|null $bookingDto Optional booking DTO for bulk insurance resolution
|
||||
* @param ParticipantDto $participant The participant to calculate services for
|
||||
* @param bool $includeInsurance Whether to include insurance pricing (default: true)
|
||||
* @param BookingDto|null $bookingDto Optional booking DTO for bulk insurance resolution
|
||||
* @param bool $onlyInsuranceCalculationServices Only include services with includeInInsuranceCalculation=true (default: false)
|
||||
*
|
||||
* @return float The total service cost for this participant
|
||||
*/
|
||||
private function calculateParticipantServiceTotal(ParticipantDto $participant, bool $includeInsurance = true, ?BookingDto $bookingDto = null): float
|
||||
private function calculateParticipantServiceTotal(ParticipantDto $participant, bool $includeInsurance = true, ?BookingDto $bookingDto = null, bool $onlyInsuranceCalculationServices = false): float
|
||||
{
|
||||
$serviceTotal = 0.0;
|
||||
|
||||
// Single service selections
|
||||
if (null !== $participant->skiPass && null !== $participant->skiPass->price) {
|
||||
$serviceTotal += $participant->skiPass->price;
|
||||
if (false === $onlyInsuranceCalculationServices || true === $participant->skiPass->includeInInsuranceCalculation) {
|
||||
$serviceTotal += $participant->skiPass->price;
|
||||
}
|
||||
}
|
||||
|
||||
if (null !== $participant->rentalInsurance && null !== $participant->rentalInsurance->price) {
|
||||
$serviceTotal += $participant->rentalInsurance->price;
|
||||
if (false === $onlyInsuranceCalculationServices || true === $participant->rentalInsurance->includeInInsuranceCalculation) {
|
||||
$serviceTotal += $participant->rentalInsurance->price;
|
||||
}
|
||||
}
|
||||
|
||||
// Get effective insurance (considering bulk insurance for dependent participants)
|
||||
@@ -555,11 +548,15 @@ class BookingPriceCalculatorService
|
||||
|
||||
// Transportation services
|
||||
if (null !== $participant->transportationOutbound && null !== $participant->transportationOutbound->price) {
|
||||
$serviceTotal += $participant->transportationOutbound->price;
|
||||
if (false === $onlyInsuranceCalculationServices || true === $participant->transportationOutbound->includeInInsuranceCalculation) {
|
||||
$serviceTotal += $participant->transportationOutbound->price;
|
||||
}
|
||||
}
|
||||
|
||||
if (null !== $participant->transportationInbound && null !== $participant->transportationInbound->price) {
|
||||
$serviceTotal += $participant->transportationInbound->price;
|
||||
if (false === $onlyInsuranceCalculationServices || true === $participant->transportationInbound->includeInInsuranceCalculation) {
|
||||
$serviceTotal += $participant->transportationInbound->price;
|
||||
}
|
||||
}
|
||||
|
||||
if (null !== $participant->pickup && null !== $participant->pickup->price) {
|
||||
@@ -567,7 +564,9 @@ class BookingPriceCalculatorService
|
||||
}
|
||||
|
||||
if (null !== $participant->parkingService && null !== $participant->parkingService->price) {
|
||||
$serviceTotal += $participant->parkingService->price;
|
||||
if (false === $onlyInsuranceCalculationServices || true === $participant->parkingService->includeInInsuranceCalculation) {
|
||||
$serviceTotal += $participant->parkingService->price;
|
||||
}
|
||||
}
|
||||
|
||||
// Multiple service selections
|
||||
@@ -582,7 +581,9 @@ class BookingPriceCalculatorService
|
||||
if (true === is_array($serviceArray)) {
|
||||
foreach ($serviceArray as $service) {
|
||||
if ($service instanceof Service && null !== $service->price) {
|
||||
$serviceTotal += $service->price;
|
||||
if (false === $onlyInsuranceCalculationServices || true === $service->includeInInsuranceCalculation) {
|
||||
$serviceTotal += $service->price;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -594,7 +595,7 @@ class BookingPriceCalculatorService
|
||||
/**
|
||||
* Retrieves a room by ID from the booking's travel data.
|
||||
*/
|
||||
private function getRoomById(BookingCreateDto $bookingDto, ?int $roomId): ?Room
|
||||
private function getRoomById(BookingDto $bookingDto, ?int $roomId): ?Room
|
||||
{
|
||||
if (null === $roomId) {
|
||||
return null;
|
||||
@@ -644,7 +645,7 @@ class BookingPriceCalculatorService
|
||||
* This method is used for pricing calculations to show correct prices when bulk
|
||||
* insurance is enabled, even though the actual assignment happens in the processor.
|
||||
*
|
||||
* @param ParticipantDto $participant The participant to get insurance for
|
||||
* @param ParticipantDto $participant The participant to get insurance for
|
||||
* @param BookingDto|null $bookingDto Optional booking DTO for bulk insurance check
|
||||
*
|
||||
* @return Insurance|null The effective insurance for pricing purposes
|
||||
|
||||
@@ -6,7 +6,6 @@ use App\BusProNet\Model\Room;
|
||||
use App\BusProNet\Model\Travel;
|
||||
use App\Exception\BookingSessionNotFoundException;
|
||||
use App\Exception\NoRoomsAvailableException;
|
||||
use App\Form\Model\BookingCreateDto;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Model\RoomSelectionDto;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
@@ -32,7 +31,7 @@ class BookingService
|
||||
* is first loaded, before any HTMX modifications. This ensures accurate change
|
||||
* detection for room assignment resets.
|
||||
*/
|
||||
public function getOrCreateBaselineSnapshot(Request $request, BookingCreateDto $bookingCreateDto): array
|
||||
public function getOrCreateBaselineSnapshot(Request $request, BookingDto $bookingCreateDto): array
|
||||
{
|
||||
$baselineKey = self::BOOKING_CREATE_BASELINE_KEY;
|
||||
|
||||
@@ -66,11 +65,11 @@ class BookingService
|
||||
*
|
||||
* @param Request $request The HTTP request containing session data
|
||||
*
|
||||
* @return BookingCreateDto The booking DTO from session
|
||||
* @return BookingDto The booking DTO from session
|
||||
*
|
||||
* @throws BookingSessionNotFoundException When no valid booking session exists
|
||||
*/
|
||||
public function getOrCreateBookingCreateDto(Request $request): BookingCreateDto
|
||||
public function getOrCreateBookingCreateDto(Request $request): BookingDto
|
||||
{
|
||||
$bookingCreateDto = $request->getSession()->get(self::BOOKING_CREATE_KEY);
|
||||
|
||||
@@ -130,9 +129,9 @@ class BookingService
|
||||
* across multiple HTTP requests during the booking flow.
|
||||
*
|
||||
* @param Request $request The HTTP request with session
|
||||
* @param BookingCreateDto $bookingCreateDto The booking DTO to persist
|
||||
* @param BookingDto $bookingCreateDto The booking DTO to persist
|
||||
*/
|
||||
public function saveBookingCreateDto(Request $request, BookingCreateDto $bookingCreateDto): void
|
||||
public function saveBookingCreateDto(Request $request, BookingDto $bookingCreateDto): void
|
||||
{
|
||||
$this->saveBookingDto($request, $bookingCreateDto, BookingDto::MODE_CREATE);
|
||||
}
|
||||
@@ -164,11 +163,11 @@ class BookingService
|
||||
/**
|
||||
* Creates a fresh booking session with the provided travel parameters.
|
||||
*
|
||||
* This method initializes a new BookingCreateDto with empty room selections
|
||||
* This method initializes a new BookingDto with empty room selections
|
||||
* and saves it to the session. It's designed to be called from the clean
|
||||
* booking entry point without requiring UID parameters.
|
||||
*/
|
||||
public function startFreshBooking(Request $request, int $dateId, int $hotelId, ?int $agencyId = null): BookingCreateDto
|
||||
public function startFreshBooking(Request $request, int $dateId, int $hotelId, ?int $agencyId = null): BookingDto
|
||||
{
|
||||
$travelData = $this->travelDataService->getTravelData($dateId, $hotelId);
|
||||
if (null === $travelData) {
|
||||
@@ -188,7 +187,7 @@ class BookingService
|
||||
$availableRooms
|
||||
);
|
||||
|
||||
$bookingCreateDto = new BookingCreateDto($travelData, $hotelId);
|
||||
$bookingCreateDto = new BookingDto($travelData, $hotelId);
|
||||
$bookingCreateDto->roomSelections = $roomSelections;
|
||||
$bookingCreateDto->currentStep = 1;
|
||||
$bookingCreateDto->agencyId = $agencyId;
|
||||
@@ -344,9 +343,9 @@ class BookingService
|
||||
* invalid assignments. Called when users modify their room selections
|
||||
* in step 1 to ensure participants are reassigned appropriately.
|
||||
*
|
||||
* @param BookingCreateDto $dto The booking DTO to reset assignments for
|
||||
* @param BookingDto $dto The booking DTO to reset assignments for
|
||||
*/
|
||||
public function resetParticipantAssignments(BookingCreateDto $dto): void
|
||||
public function resetParticipantAssignments(BookingDto $dto): void
|
||||
{
|
||||
foreach ($dto->participants as $participant) {
|
||||
$participant->assignedRoomId = null;
|
||||
@@ -358,7 +357,7 @@ class BookingService
|
||||
*
|
||||
* @return array<int, array{int, int}> Array of [roomId, quantity] pairs
|
||||
*/
|
||||
public function createRoomSelectionSnapshot(BookingCreateDto $dto): array
|
||||
public function createRoomSelectionSnapshot(BookingDto $dto): array
|
||||
{
|
||||
return array_map(
|
||||
fn ($roomSelection) => [(int) $roomSelection->roomId, (int) $roomSelection->quantity],
|
||||
@@ -373,11 +372,11 @@ class BookingService
|
||||
* to detect changes that would require participant reassignment.
|
||||
*
|
||||
* @param array $oldSnapshot The baseline room selection snapshot
|
||||
* @param BookingCreateDto $newDto The current booking DTO
|
||||
* @param BookingDto $newDto The current booking DTO
|
||||
*
|
||||
* @return bool True if room selections have changed, false otherwise
|
||||
*/
|
||||
public function hasRoomSelectionChanged(array $oldSnapshot, BookingCreateDto $newDto): bool
|
||||
public function hasRoomSelectionChanged(array $oldSnapshot, BookingDto $newDto): bool
|
||||
{
|
||||
$newSnapshot = $this->createRoomSelectionSnapshot($newDto);
|
||||
|
||||
@@ -395,9 +394,9 @@ class BookingService
|
||||
* have at least one skipass available for their age. Ineligible participants are
|
||||
* skipped to prevent their mandatory services from being included in pricing.
|
||||
*
|
||||
* @param BookingCreateDto $bookingDto The booking DTO to update with mandatory services
|
||||
* @param BookingDto $bookingDto The booking DTO to update with mandatory services
|
||||
*/
|
||||
public function preselectMandatoryServices(BookingCreateDto $bookingDto): void
|
||||
public function preselectMandatoryServices(BookingDto $bookingDto): void
|
||||
{
|
||||
$additionalServices = $bookingDto->travel->getAdditionalServicesBySubTypes(\App\BusProNet\Constants::TOKEN_ADDITIONAL);
|
||||
$mandatoryServices = array_filter($additionalServices, fn ($service) => true === $service->mandatory);
|
||||
|
||||
@@ -6,7 +6,6 @@ namespace App\Service;
|
||||
|
||||
use App\BusProNet\Model\Insurance;
|
||||
use App\BusProNet\Traits\SortByPriceTrait;
|
||||
use App\Form\Model\BookingCreateDto;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Model\ParticipantDto;
|
||||
use App\Model\InsuranceEligibilityCriteria;
|
||||
@@ -31,9 +30,9 @@ class InsuranceMatchingService
|
||||
/**
|
||||
* Filters insurances based on participant and booking criteria.
|
||||
*
|
||||
* @param array<Insurance> $insurances Available insurances to filter
|
||||
* @param ParticipantDto $participant The participant to match insurances for
|
||||
* @param BookingDto $booking The booking context for additional criteria
|
||||
* @param array<Insurance> $insurances Available insurances to filter
|
||||
* @param ParticipantDto $participant The participant to match insurances for
|
||||
* @param BookingDto $booking The booking context for additional criteria
|
||||
*
|
||||
* @return array<Insurance> Filtered array of eligible insurances
|
||||
*/
|
||||
@@ -60,10 +59,10 @@ class InsuranceMatchingService
|
||||
* current insurance is no longer eligible. It finds the same insurance type
|
||||
* (subType + familyInsurance) with the correct price tier.
|
||||
*
|
||||
* @param array<Insurance> $availableInsurances All available insurances
|
||||
* @param Insurance $currentInsurance The currently selected insurance
|
||||
* @param ParticipantDto $participant The participant to reassign for
|
||||
* @param BookingDto $booking The booking context
|
||||
* @param array<Insurance> $availableInsurances All available insurances
|
||||
* @param Insurance $currentInsurance The currently selected insurance
|
||||
* @param ParticipantDto $participant The participant to reassign for
|
||||
* @param BookingDto $booking The booking context
|
||||
*
|
||||
* @return Insurance|null The reassigned insurance or null if no suitable match found
|
||||
*/
|
||||
@@ -90,9 +89,9 @@ class InsuranceMatchingService
|
||||
* (subType + familyInsurance) to all participants, but selects the appropriate price tier
|
||||
* based on each participant's individual travel price.
|
||||
*
|
||||
* @param array<Insurance> $availableInsurances All available insurances
|
||||
* @param Insurance $selectedInsurance The insurance selected by the applicant
|
||||
* @param BookingDto $booking The booking with all participants
|
||||
* @param array<Insurance> $availableInsurances All available insurances
|
||||
* @param Insurance $selectedInsurance The insurance selected by the applicant
|
||||
* @param BookingDto $booking The booking with all participants
|
||||
*
|
||||
* @return array<int, Insurance|null> Array indexed by participant index with assigned insurances
|
||||
*
|
||||
@@ -187,8 +186,8 @@ class InsuranceMatchingService
|
||||
*/
|
||||
private function checkFamilyInsuranceConstraints(Insurance $insurance, BookingDto $booking): bool
|
||||
{
|
||||
// Family booking detection only available in BookingCreateDto
|
||||
if (!$booking instanceof BookingCreateDto) {
|
||||
// Family booking detection only available in create mode
|
||||
if (BookingDto::MODE_EDIT === $booking->getMode()) {
|
||||
return true; // Skip family constraints for edit mode
|
||||
}
|
||||
|
||||
@@ -316,14 +315,34 @@ class InsuranceMatchingService
|
||||
* affects travel price which then affects insurance eligibility.
|
||||
*
|
||||
* @param BookingDto $booking The booking to calculate price for
|
||||
* @param int $participantIndex The participant index to calculate for
|
||||
* @param int $participantIndex The participant index to calculate for
|
||||
*
|
||||
* @return float The total travel price for the participant excluding insurance
|
||||
*/
|
||||
private function calculateTravelPrice(BookingDto $booking, int $participantIndex): float
|
||||
{
|
||||
$participant = $booking->getParticipant($participantIndex);
|
||||
|
||||
// Debug logging to understand price calculation
|
||||
if (null !== $participant && null !== $participant->insurance) {
|
||||
error_log(sprintf(
|
||||
'[InsuranceMatching] Participant %d: calculating travel price WITH insurance=%d (€%.2f) currently selected',
|
||||
$participantIndex,
|
||||
$participant->insurance->id,
|
||||
$participant->insurance->price ?? 0.0
|
||||
));
|
||||
}
|
||||
|
||||
// Use the price calculator to get the participant's individual price excluding insurance
|
||||
return $this->priceCalculatorService->calculateIndividualParticipantPriceExcludingInsurance($booking, $participantIndex);
|
||||
$travelPrice = $this->priceCalculatorService->calculateIndividualParticipantPriceExcludingInsurance($booking, $participantIndex);
|
||||
|
||||
error_log(sprintf(
|
||||
'[InsuranceMatching] Participant %d: calculated travel price (excluding insurance) = €%.2f',
|
||||
$participantIndex,
|
||||
$travelPrice
|
||||
));
|
||||
|
||||
return $travelPrice;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use App\Form\Model\BookingCreateDto;
|
||||
use App\Form\Model\BookingDto;
|
||||
|
||||
/**
|
||||
* Handles automatic room assignment for booking participants.
|
||||
@@ -26,9 +26,9 @@ class RoomAssignmentService
|
||||
* - 2x "Doppelzimmer" (capacity 2) = participants 0-1 → room A, participants 2-3 → room A
|
||||
* - 1x "3-Bett-Zimmer" (capacity 3) = participants 4-6 → room B
|
||||
*
|
||||
* @param BookingCreateDto $dto The booking DTO containing room selections and participants
|
||||
* @param BookingDto $dto The booking DTO containing room selections and participants
|
||||
*/
|
||||
public function assignParticipantsToRooms(BookingCreateDto $dto): void
|
||||
public function assignParticipantsToRooms(BookingDto $dto): void
|
||||
{
|
||||
$participantIndex = 0;
|
||||
$selectedRooms = $dto->getSelectedRooms();
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace App\Service;
|
||||
use App\BusProNet\Constants;
|
||||
use App\BusProNet\Model\Service;
|
||||
use App\BusProNet\Utility\DirectionMapper;
|
||||
use App\Form\Model\BookingCreateDto;
|
||||
use App\Form\Model\BookingDto;
|
||||
|
||||
/**
|
||||
* Calculates dynamic service availability based on current booking selections.
|
||||
@@ -22,12 +22,12 @@ class ServiceAvailabilityCalculator
|
||||
/**
|
||||
* Calculate remaining availability for all services based on current participant selections.
|
||||
*
|
||||
* @param BookingCreateDto $bookingDto The booking data with participant selections
|
||||
* @param int $currentParticipantIndex The index of the participant currently filling the form
|
||||
* @param BookingDto $bookingDto The booking data with participant selections
|
||||
* @param int $currentParticipantIndex The index of the participant currently filling the form
|
||||
*
|
||||
* @return array<int, int> Array mapping service IDs to remaining availability counts
|
||||
*/
|
||||
public function calculateRemainingAvailability(BookingCreateDto $bookingDto, int $currentParticipantIndex): array
|
||||
public function calculateRemainingAvailability(BookingDto $bookingDto, int $currentParticipantIndex): array
|
||||
{
|
||||
$serviceUsage = $this->calculateServiceUsage($bookingDto, $currentParticipantIndex);
|
||||
$remainingAvailability = [];
|
||||
@@ -52,13 +52,13 @@ class ServiceAvailabilityCalculator
|
||||
/**
|
||||
* Filter services array to only include those with remaining availability.
|
||||
*
|
||||
* @param array $services Array of Service objects to filter
|
||||
* @param BookingCreateDto $bookingDto The booking data with participant selections
|
||||
* @param int $participantIndex The index of the participant currently filling the form
|
||||
* @param array $services Array of Service objects to filter
|
||||
* @param BookingDto $bookingDto The booking data with participant selections
|
||||
* @param int $participantIndex The index of the participant currently filling the form
|
||||
*
|
||||
* @return array Filtered array containing only available services
|
||||
*/
|
||||
public function filterAvailableServices(array $services, BookingCreateDto $bookingDto, int $participantIndex): array
|
||||
public function filterAvailableServices(array $services, BookingDto $bookingDto, int $participantIndex): array
|
||||
{
|
||||
$remainingAvailability = $this->calculateRemainingAvailability($bookingDto, $participantIndex);
|
||||
|
||||
@@ -76,13 +76,13 @@ class ServiceAvailabilityCalculator
|
||||
/**
|
||||
* Check if a specific service is unavailable (sold out) for the current participant.
|
||||
*
|
||||
* @param int $serviceId The ID of the service to check
|
||||
* @param BookingCreateDto $bookingDto The booking data with participant selections
|
||||
* @param int $participantIndex The index of the participant currently filling the form
|
||||
* @param int $serviceId The ID of the service to check
|
||||
* @param BookingDto $bookingDto The booking data with participant selections
|
||||
* @param int $participantIndex The index of the participant currently filling the form
|
||||
*
|
||||
* @return bool True if the service is unavailable (has availability limit and remaining is 0)
|
||||
*/
|
||||
public function isServiceUnavailable(int $serviceId, BookingCreateDto $bookingDto, int $participantIndex): bool
|
||||
public function isServiceUnavailable(int $serviceId, BookingDto $bookingDto, int $participantIndex): bool
|
||||
{
|
||||
$allServices = $this->getAllServicesFromTravel($bookingDto);
|
||||
$service = null;
|
||||
@@ -107,12 +107,12 @@ class ServiceAvailabilityCalculator
|
||||
/**
|
||||
* Calculate how many times each service has been selected by other participants.
|
||||
*
|
||||
* @param BookingCreateDto $bookingDto The booking data with participant selections
|
||||
* @param int $currentParticipantIndex The index of the participant currently filling the form
|
||||
* @param BookingDto $bookingDto The booking data with participant selections
|
||||
* @param int $currentParticipantIndex The index of the participant currently filling the form
|
||||
*
|
||||
* @return array<int, int> Array mapping service IDs to usage counts
|
||||
*/
|
||||
private function calculateServiceUsage(BookingCreateDto $bookingDto, int $currentParticipantIndex): array
|
||||
private function calculateServiceUsage(BookingDto $bookingDto, int $currentParticipantIndex): array
|
||||
{
|
||||
$serviceUsage = [];
|
||||
|
||||
@@ -187,11 +187,11 @@ class ServiceAvailabilityCalculator
|
||||
/**
|
||||
* Get all services from the travel data for availability calculation.
|
||||
*
|
||||
* @param BookingCreateDto $bookingDto The booking data containing travel information
|
||||
* @param BookingDto $bookingDto The booking data containing travel information
|
||||
*
|
||||
* @return array<Service> Array of all available services
|
||||
*/
|
||||
private function getAllServicesFromTravel(BookingCreateDto $bookingDto): array
|
||||
private function getAllServicesFromTravel(BookingDto $bookingDto): array
|
||||
{
|
||||
$allServices = [];
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace App\Validator\Constraints;
|
||||
use App\BusProNet\ApiClient;
|
||||
use App\BusProNet\Model\BookingUpdate;
|
||||
use App\BusProNet\Model\Notification;
|
||||
use App\Form\Model\BookingEditDto;
|
||||
use App\Form\Model\BookingDto;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
|
||||
@@ -17,7 +17,7 @@ class BookingValidator extends ConstraintValidator
|
||||
|
||||
public function validate(mixed $value, Constraint $constraint): void
|
||||
{
|
||||
/** @var BookingEditDto $booking */
|
||||
/** @var BookingDto $booking */
|
||||
$bookingData = $value;
|
||||
|
||||
$result = $this->apiClient->updateBooking($bookingData);
|
||||
|
||||
@@ -216,9 +216,8 @@
|
||||
{% set applicantInsurance = form.vars.data.participants[0].insurance %}
|
||||
{% if applicantInsurance %}
|
||||
{{ applicantInsurance.label }}
|
||||
{% set participantPrice = participantPrices[loop.index0] %}
|
||||
{% if participantPrice.insurance and participantPrice.insurance > 0 %}
|
||||
<span class="text-gray-500">(€{{ participantPrice.insurance|number_format(2, ',', '.') }})</span>
|
||||
{% if applicantInsurance.price and applicantInsurance.price > 0 %}
|
||||
<span class="text-gray-500">(€{{ applicantInsurance.price|number_format(2, ',', '.') }})</span>
|
||||
{% endif %}
|
||||
<span class="italic text-gray-500 ml-2">– wie Anmelder</span>
|
||||
{% else %}
|
||||
|
||||
@@ -14,7 +14,7 @@ use App\BusProNet\Model\Pickup;
|
||||
use App\BusProNet\Model\Room;
|
||||
use App\BusProNet\Model\Service;
|
||||
use App\BusProNet\Model\Travel;
|
||||
use App\Form\Model\BookingEditDto;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Model\ParticipantDto;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
@@ -210,29 +210,31 @@ class BookingDataProcessorTest extends TestCase
|
||||
$this->assertArrayNotHasKey('zustiege', $result);
|
||||
}
|
||||
|
||||
private function createCompleteFormData(): BookingEditDto
|
||||
private function createCompleteFormData(): BookingDto
|
||||
{
|
||||
$formData = new BookingEditDto($this->createMockBooking(), $this->createMockTravel());
|
||||
$formData->participants = [
|
||||
$bookingDto = new BookingDto($this->createMockTravel(), 1);
|
||||
$bookingDto->booking = $this->createMockBooking();
|
||||
$bookingDto->participants = [
|
||||
$this->createMockParticipantDto(0, 'F'),
|
||||
$this->createMockParticipantDto(1, 'F'),
|
||||
];
|
||||
|
||||
return $formData;
|
||||
return $bookingDto;
|
||||
}
|
||||
|
||||
private function createFormDataWithCanceledParticipant(): BookingEditDto
|
||||
private function createFormDataWithCanceledParticipant(): BookingDto
|
||||
{
|
||||
$formData = new BookingEditDto($this->createMockBooking(), $this->createMockTravel());
|
||||
$formData->participants = [
|
||||
$bookingDto = new BookingDto($this->createMockTravel(), 1);
|
||||
$bookingDto->booking = $this->createMockBooking();
|
||||
$bookingDto->participants = [
|
||||
$this->createMockParticipantDto(0, 'F'),
|
||||
$this->createMockParticipantDto(1, 'S'), // Canceled
|
||||
];
|
||||
|
||||
return $formData;
|
||||
return $bookingDto;
|
||||
}
|
||||
|
||||
private function createFormDataWithAdditionalServices(): BookingEditDto
|
||||
private function createFormDataWithAdditionalServices(): BookingDto
|
||||
{
|
||||
$formData = $this->createCompleteFormData();
|
||||
|
||||
@@ -243,7 +245,7 @@ class BookingDataProcessorTest extends TestCase
|
||||
return $formData;
|
||||
}
|
||||
|
||||
private function createFormDataWithTransportation(): BookingEditDto
|
||||
private function createFormDataWithTransportation(): BookingDto
|
||||
{
|
||||
$formData = $this->createCompleteFormData();
|
||||
|
||||
@@ -254,7 +256,7 @@ class BookingDataProcessorTest extends TestCase
|
||||
return $formData;
|
||||
}
|
||||
|
||||
private function createFormDataWithBusPickup(): BookingEditDto
|
||||
private function createFormDataWithBusPickup(): BookingDto
|
||||
{
|
||||
$formData = $this->createCompleteFormData();
|
||||
|
||||
@@ -267,7 +269,7 @@ class BookingDataProcessorTest extends TestCase
|
||||
return $formData;
|
||||
}
|
||||
|
||||
private function createFormDataWithNonBusTransportation(): BookingEditDto
|
||||
private function createFormDataWithNonBusTransportation(): BookingDto
|
||||
{
|
||||
$formData = $this->createCompleteFormData();
|
||||
|
||||
@@ -279,7 +281,7 @@ class BookingDataProcessorTest extends TestCase
|
||||
return $formData;
|
||||
}
|
||||
|
||||
private function createFormDataWithUnusedServices(): BookingEditDto
|
||||
private function createFormDataWithUnusedServices(): BookingDto
|
||||
{
|
||||
$formData = $this->createCompleteFormData();
|
||||
|
||||
@@ -289,7 +291,7 @@ class BookingDataProcessorTest extends TestCase
|
||||
return $formData;
|
||||
}
|
||||
|
||||
private function createFormDataWithUpdatedPersonalData(): BookingEditDto
|
||||
private function createFormDataWithUpdatedPersonalData(): BookingDto
|
||||
{
|
||||
$formData = $this->createCompleteFormData();
|
||||
|
||||
@@ -302,7 +304,7 @@ class BookingDataProcessorTest extends TestCase
|
||||
return $formData;
|
||||
}
|
||||
|
||||
private function createFormDataWithInactiveParticipant(): BookingEditDto
|
||||
private function createFormDataWithInactiveParticipant(): BookingDto
|
||||
{
|
||||
$formData = $this->createCompleteFormData();
|
||||
|
||||
@@ -318,7 +320,7 @@ class BookingDataProcessorTest extends TestCase
|
||||
return $formData;
|
||||
}
|
||||
|
||||
private function createFormDataForApplicantSync(): BookingEditDto
|
||||
private function createFormDataForApplicantSync(): BookingDto
|
||||
{
|
||||
$formData = $this->createCompleteFormData();
|
||||
|
||||
@@ -330,7 +332,7 @@ class BookingDataProcessorTest extends TestCase
|
||||
return $formData;
|
||||
}
|
||||
|
||||
private function createFormDataWithBankAccount(): BookingEditDto
|
||||
private function createFormDataWithBankAccount(): BookingDto
|
||||
{
|
||||
$formData = $this->createCompleteFormData();
|
||||
|
||||
@@ -345,7 +347,7 @@ class BookingDataProcessorTest extends TestCase
|
||||
return $formData;
|
||||
}
|
||||
|
||||
private function createFormDataWithoutBankAccount(): BookingEditDto
|
||||
private function createFormDataWithoutBankAccount(): BookingDto
|
||||
{
|
||||
$formData = $this->createCompleteFormData();
|
||||
$formData->booking->bankAccount = null;
|
||||
@@ -353,7 +355,7 @@ class BookingDataProcessorTest extends TestCase
|
||||
return $formData;
|
||||
}
|
||||
|
||||
private function createFormDataWithRooms(): BookingEditDto
|
||||
private function createFormDataWithRooms(): BookingDto
|
||||
{
|
||||
$formData = $this->createCompleteFormData();
|
||||
|
||||
@@ -371,7 +373,7 @@ class BookingDataProcessorTest extends TestCase
|
||||
return $formData;
|
||||
}
|
||||
|
||||
private function createFormDataWithoutExistingCommunication(): BookingEditDto
|
||||
private function createFormDataWithoutExistingCommunication(): BookingDto
|
||||
{
|
||||
$formData = $this->createCompleteFormData();
|
||||
|
||||
@@ -384,7 +386,7 @@ class BookingDataProcessorTest extends TestCase
|
||||
return $formData;
|
||||
}
|
||||
|
||||
private function createFormDataWithoutPickups(): BookingEditDto
|
||||
private function createFormDataWithoutPickups(): BookingDto
|
||||
{
|
||||
$formData = $this->createCompleteFormData();
|
||||
$formData->booking->pickupsOutbound = [];
|
||||
|
||||
@@ -6,8 +6,7 @@ namespace App\Tests\Form\Service\Condition;
|
||||
|
||||
use App\BusProNet\Model\Booking;
|
||||
use App\BusProNet\Model\Travel;
|
||||
use App\Form\Model\BookingCreateDto;
|
||||
use App\Form\Model\BookingEditDto;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\Condition\InsuranceMutabilityCondition;
|
||||
use Carbon\Carbon;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
@@ -35,7 +34,7 @@ class InsuranceMutabilityConditionTest extends TestCase
|
||||
$travel = new Travel();
|
||||
$travel->dateFrom = new \DateTimeImmutable('+10 days');
|
||||
|
||||
$bookingDto = new BookingCreateDto($travel, 123);
|
||||
$bookingDto = new BookingDto($travel, 123);
|
||||
|
||||
$result = $this->condition->evaluate($bookingDto, 0, []);
|
||||
|
||||
@@ -154,7 +153,7 @@ class InsuranceMutabilityConditionTest extends TestCase
|
||||
$this->assertStringContainsString('3', $result);
|
||||
}
|
||||
|
||||
private function createEditDto(\DateTimeImmutable $travelDate, \DateTimeImmutable $bookingDate): BookingEditDto
|
||||
private function createEditDto(\DateTimeImmutable $travelDate, \DateTimeImmutable $bookingDate): BookingDto
|
||||
{
|
||||
$booking = new Booking();
|
||||
$booking->bookingDate = $bookingDate;
|
||||
@@ -162,6 +161,9 @@ class InsuranceMutabilityConditionTest extends TestCase
|
||||
$travel = new Travel();
|
||||
$travel->dateFrom = $travelDate;
|
||||
|
||||
return new BookingEditDto($booking, $travel);
|
||||
$bookingDto = new BookingDto($travel, 1);
|
||||
$bookingDto->booking = $booking;
|
||||
|
||||
return $bookingDto;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace App\Tests\Form\Service;
|
||||
|
||||
use App\BusProNet\Model\Insurance;
|
||||
use App\BusProNet\Model\Travel;
|
||||
use App\Form\Model\BookingCreateDto;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Model\ParticipantDto;
|
||||
use App\Form\Service\ParticipantInsuranceFieldHandler;
|
||||
use App\Service\InsuranceMatchingService;
|
||||
@@ -229,8 +229,8 @@ class ParticipantInsuranceFieldHandlerTest extends TestCase
|
||||
return $insurance;
|
||||
}
|
||||
|
||||
private function createMockBookingDto(): BookingCreateDto
|
||||
private function createMockBookingDto(): BookingDto
|
||||
{
|
||||
return $this->createMock(BookingCreateDto::class);
|
||||
return $this->createMock(BookingDto::class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ declare(strict_types=1);
|
||||
namespace App\Tests\Form\Service;
|
||||
|
||||
use App\BusProNet\Model\Travel;
|
||||
use App\Form\Model\BookingCreateDto;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Model\ParticipantDto;
|
||||
use App\Form\Service\ParticipantLicensePlateFieldHandler;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
@@ -38,7 +38,7 @@ class ParticipantLicensePlateFieldHandlerTest extends TestCase
|
||||
public function testProcessFieldWithoutParticipant(): void
|
||||
{
|
||||
$travel = new Travel();
|
||||
$bookingDto = new BookingCreateDto($travel, 1);
|
||||
$bookingDto = new BookingDto($travel, 1);
|
||||
$submittedData = ['licensePlate' => 'AB-CD 123'];
|
||||
|
||||
$this->handler->processField($submittedData, $bookingDto, 0);
|
||||
@@ -54,7 +54,7 @@ class ParticipantLicensePlateFieldHandlerTest extends TestCase
|
||||
$participant->licensePlate = 'AB-CD 123'; // Should be cleared
|
||||
|
||||
$travel = new Travel();
|
||||
$bookingDto = new BookingCreateDto($travel, 1);
|
||||
$bookingDto = new BookingDto($travel, 1);
|
||||
$bookingDto->participants = [$participant];
|
||||
|
||||
$submittedData = ['licensePlate' => 'XY-ZZ 999'];
|
||||
@@ -70,7 +70,7 @@ class ParticipantLicensePlateFieldHandlerTest extends TestCase
|
||||
$participant->parking = true; // Parking selected
|
||||
|
||||
$travel = new Travel();
|
||||
$bookingDto = new BookingCreateDto($travel, 1);
|
||||
$bookingDto = new BookingDto($travel, 1);
|
||||
$bookingDto->participants = [$participant];
|
||||
|
||||
$submittedData = ['licensePlate' => 'AB-CD 123'];
|
||||
@@ -86,7 +86,7 @@ class ParticipantLicensePlateFieldHandlerTest extends TestCase
|
||||
$participant->parking = true; // Parking selected
|
||||
|
||||
$travel = new Travel();
|
||||
$bookingDto = new BookingCreateDto($travel, 1);
|
||||
$bookingDto = new BookingDto($travel, 1);
|
||||
$bookingDto->participants = [$participant];
|
||||
|
||||
$submittedData = ['licensePlate' => ''];
|
||||
@@ -102,7 +102,7 @@ class ParticipantLicensePlateFieldHandlerTest extends TestCase
|
||||
$participant->parking = true; // Parking selected
|
||||
|
||||
$travel = new Travel();
|
||||
$bookingDto = new BookingCreateDto($travel, 1);
|
||||
$bookingDto = new BookingDto($travel, 1);
|
||||
$bookingDto->participants = [$participant];
|
||||
|
||||
$submittedData = ['licensePlate' => null];
|
||||
@@ -118,7 +118,7 @@ class ParticipantLicensePlateFieldHandlerTest extends TestCase
|
||||
$participant->parking = true; // Parking selected
|
||||
|
||||
$travel = new Travel();
|
||||
$bookingDto = new BookingCreateDto($travel, 1);
|
||||
$bookingDto = new BookingDto($travel, 1);
|
||||
$bookingDto->participants = [$participant];
|
||||
|
||||
$submittedData = ['licensePlate' => ' AB-CD 123 '];
|
||||
@@ -134,7 +134,7 @@ class ParticipantLicensePlateFieldHandlerTest extends TestCase
|
||||
$participant->parking = true; // Parking selected
|
||||
|
||||
$travel = new Travel();
|
||||
$bookingDto = new BookingCreateDto($travel, 1);
|
||||
$bookingDto = new BookingDto($travel, 1);
|
||||
$bookingDto->participants = [$participant];
|
||||
|
||||
$submittedData = ['licensePlate' => ' '];
|
||||
@@ -150,7 +150,7 @@ class ParticipantLicensePlateFieldHandlerTest extends TestCase
|
||||
$participant->parking = true; // Parking selected
|
||||
|
||||
$travel = new Travel();
|
||||
$bookingDto = new BookingCreateDto($travel, 1);
|
||||
$bookingDto = new BookingDto($travel, 1);
|
||||
$bookingDto->participants = [$participant];
|
||||
|
||||
// Test with integer value
|
||||
@@ -170,7 +170,7 @@ class ParticipantLicensePlateFieldHandlerTest extends TestCase
|
||||
$participant->parking = true; // Parking selected
|
||||
|
||||
$travel = new Travel();
|
||||
$bookingDto = new BookingCreateDto($travel, 1);
|
||||
$bookingDto = new BookingDto($travel, 1);
|
||||
$bookingDto->participants = [$participant];
|
||||
|
||||
$submittedData = []; // No licensePlate field
|
||||
@@ -189,7 +189,7 @@ class ParticipantLicensePlateFieldHandlerTest extends TestCase
|
||||
$participant2->parking = true;
|
||||
|
||||
$travel = new Travel();
|
||||
$bookingDto = new BookingCreateDto($travel, 1);
|
||||
$bookingDto = new BookingDto($travel, 1);
|
||||
$bookingDto->participants = [$participant1, $participant2];
|
||||
|
||||
$submittedData = ['licensePlate' => 'AB-CD 123'];
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace App\Tests\Service;
|
||||
use App\BusProNet\Model\Room;
|
||||
use App\BusProNet\Model\Service;
|
||||
use App\BusProNet\Model\Travel;
|
||||
use App\Form\Model\BookingCreateDto;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Model\ParticipantDto;
|
||||
use App\Form\Model\RoomSelectionDto;
|
||||
use App\Service\BookingPriceCalculatorService;
|
||||
@@ -38,7 +38,7 @@ class BookingPriceCalculatorServiceTest extends TestCase
|
||||
$roomSelection->roomId = 1;
|
||||
$roomSelection->quantity = 2; // 2 rooms selected
|
||||
|
||||
$bookingDto = new BookingCreateDto($travel, 1);
|
||||
$bookingDto = new BookingDto($travel, 1);
|
||||
$bookingDto->roomSelections = [$roomSelection];
|
||||
|
||||
// Test the calculation
|
||||
@@ -70,7 +70,7 @@ class BookingPriceCalculatorServiceTest extends TestCase
|
||||
$roomSelection->roomId = 2;
|
||||
$roomSelection->quantity = 1; // 1 room selected
|
||||
|
||||
$bookingDto = new BookingCreateDto($travel, 1);
|
||||
$bookingDto = new BookingDto($travel, 1);
|
||||
$bookingDto->roomSelections = [$roomSelection];
|
||||
|
||||
// Test the calculation
|
||||
@@ -112,7 +112,7 @@ class BookingPriceCalculatorServiceTest extends TestCase
|
||||
$doubleRoomSelection->roomId = 2;
|
||||
$doubleRoomSelection->quantity = 2; // 2 double rooms
|
||||
|
||||
$bookingDto = new BookingCreateDto($travel, 1);
|
||||
$bookingDto = new BookingDto($travel, 1);
|
||||
$bookingDto->roomSelections = [$singleRoomSelection, $doubleRoomSelection];
|
||||
|
||||
// Test the calculation
|
||||
@@ -155,7 +155,7 @@ class BookingPriceCalculatorServiceTest extends TestCase
|
||||
$roomSelection->roomId = 1;
|
||||
$roomSelection->quantity = 1;
|
||||
|
||||
$bookingDto = new BookingCreateDto($travel, 1);
|
||||
$bookingDto = new BookingDto($travel, 1);
|
||||
$bookingDto->roomSelections = [$roomSelection];
|
||||
|
||||
$result = $this->service->calculateRoomPricing($bookingDto);
|
||||
@@ -179,7 +179,7 @@ class BookingPriceCalculatorServiceTest extends TestCase
|
||||
$roomSelection->roomId = 1;
|
||||
$roomSelection->quantity = 0; // Zero quantity - not selected
|
||||
|
||||
$bookingDto = new BookingCreateDto($travel, 1);
|
||||
$bookingDto = new BookingDto($travel, 1);
|
||||
$bookingDto->roomSelections = [$roomSelection];
|
||||
|
||||
$result = $this->service->calculateRoomPricing($bookingDto);
|
||||
@@ -212,7 +212,7 @@ class BookingPriceCalculatorServiceTest extends TestCase
|
||||
$participant->skiPass = $skiPass;
|
||||
$participant->courses = [$course];
|
||||
|
||||
$bookingDto = new BookingCreateDto($travel, 1);
|
||||
$bookingDto = new BookingDto($travel, 1);
|
||||
$bookingDto->participants = [$participant];
|
||||
|
||||
$result = $this->service->calculateIndividualParticipantPrice($bookingDto, 0);
|
||||
@@ -234,7 +234,7 @@ class BookingPriceCalculatorServiceTest extends TestCase
|
||||
$participant->assignedRoomId = null; // No room assigned
|
||||
$participant->skiPass = $skiPass;
|
||||
|
||||
$bookingDto = new BookingCreateDto($travel, 1);
|
||||
$bookingDto = new BookingDto($travel, 1);
|
||||
$bookingDto->participants = [$participant];
|
||||
|
||||
$result = $this->service->calculateIndividualParticipantPrice($bookingDto, 0);
|
||||
@@ -246,7 +246,7 @@ class BookingPriceCalculatorServiceTest extends TestCase
|
||||
public function testCalculateIndividualParticipantPriceForNonExistentParticipant(): void
|
||||
{
|
||||
$travel = new Travel();
|
||||
$bookingDto = new BookingCreateDto($travel, 1);
|
||||
$bookingDto = new BookingDto($travel, 1);
|
||||
$bookingDto->participants = [];
|
||||
|
||||
$result = $this->service->calculateIndividualParticipantPrice($bookingDto, 0);
|
||||
@@ -290,7 +290,7 @@ class BookingPriceCalculatorServiceTest extends TestCase
|
||||
$participant3 = new ParticipantDto();
|
||||
$participant3->assignedRoomId = null; // No room assigned
|
||||
|
||||
$bookingDto = new BookingCreateDto($travel, 1);
|
||||
$bookingDto = new BookingDto($travel, 1);
|
||||
$bookingDto->participants = [$participant1, $participant2, $participant3];
|
||||
|
||||
$result = $this->service->calculateAllParticipantIndividualPrices($bookingDto);
|
||||
@@ -308,7 +308,7 @@ class BookingPriceCalculatorServiceTest extends TestCase
|
||||
public function testCalculateAllParticipantIndividualPricesWithEmptyBooking(): void
|
||||
{
|
||||
$travel = new Travel();
|
||||
$bookingDto = new BookingCreateDto($travel, 1);
|
||||
$bookingDto = new BookingDto($travel, 1);
|
||||
$bookingDto->participants = [];
|
||||
|
||||
$result = $this->service->calculateAllParticipantIndividualPrices($bookingDto);
|
||||
|
||||
@@ -5,7 +5,7 @@ declare(strict_types=1);
|
||||
namespace App\Tests\Service;
|
||||
|
||||
use App\BusProNet\Model\Insurance;
|
||||
use App\Form\Model\BookingCreateDto;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Model\ParticipantDto;
|
||||
use App\Service\BookingPriceCalculatorService;
|
||||
use App\Service\InsuranceMatchingService;
|
||||
@@ -254,10 +254,10 @@ class InsuranceMatchingServiceTest extends TestCase
|
||||
return $participant;
|
||||
}
|
||||
|
||||
private function createBooking(string $travelDateFrom, string $travelDateTo): BookingCreateDto
|
||||
private function createBooking(string $travelDateFrom, string $travelDateTo): BookingDto
|
||||
{
|
||||
$travel = $this->createTravel($travelDateFrom, $travelDateTo);
|
||||
$booking = new BookingCreateDto($travel, 1);
|
||||
$booking = new BookingDto($travel, 1);
|
||||
|
||||
return $booking;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user