diff --git a/src/BusProNet/XmlLoader/AgencyLoader.php b/src/BusProNet/XmlLoader/AgencyLoader.php index 4bd9e34..dd2cc5f 100644 --- a/src/BusProNet/XmlLoader/AgencyLoader.php +++ b/src/BusProNet/XmlLoader/AgencyLoader.php @@ -15,6 +15,7 @@ use Symfony\Contracts\Cache\ItemInterface; class AgencyLoader { public const DEFAULT_AGENCY_CODE = 'INTERN'; + public const INTERNAL_AGENCY_CODE = '0004'; public function __construct( private readonly CacheInterface $cache, diff --git a/src/Form/BookingEditType.php b/src/Form/BookingEditType.php index 33de2c0..e465572 100644 --- a/src/Form/BookingEditType.php +++ b/src/Form/BookingEditType.php @@ -35,8 +35,8 @@ class BookingEditType extends AbstractType $groups = ['booking_edit']; - // Edit mode: strict only if applicant is immutable - if (false === ($data->participants[0]?->mutable ?? true)) { + // Strict validation in edit mode except for internal agency bookings + if (false === $data->isInternalAgencyBooking()) { $groups[] = 'strict_required'; } diff --git a/src/Form/BookingParticipantType.php b/src/Form/BookingParticipantType.php index f698131..395f822 100644 --- a/src/Form/BookingParticipantType.php +++ b/src/Form/BookingParticipantType.php @@ -162,10 +162,15 @@ class BookingParticipantType extends AbstractType // Helper to get field state or empty array $getFieldState = fn (string $fieldName) => $allFieldStates[$fieldName] ?? []; + // Personal data fields are optional only for internal agency bookings in edit mode + $personalDataOptional = BookingDto::MODE_EDIT === $bookingDto->getMode() + && $bookingDto->isInternalAgencyBooking(); + // Add personal data fields with conditional inclusion for authenticated users if ($this->fieldStateProvider->shouldIncludeField('firstName', $bookingDto, $participantIndex)) { $form->add('firstName', TextType::class, $this->mergeFieldState([ 'label' => 'Vorname', + 'required' => !$personalDataOptional, 'sanitize_html' => true, 'property_path' => 'participant.firstName', 'attr' => [ @@ -177,6 +182,7 @@ class BookingParticipantType extends AbstractType if ($this->fieldStateProvider->shouldIncludeField('lastName', $bookingDto, $participantIndex)) { $form->add('lastName', TextType::class, $this->mergeFieldState([ 'label' => 'Nachname', + 'required' => !$personalDataOptional, 'sanitize_html' => true, 'property_path' => 'participant.lastName', 'attr' => [ @@ -188,6 +194,7 @@ class BookingParticipantType extends AbstractType if ($this->fieldStateProvider->shouldIncludeField('dateOfBirth', $bookingDto, $participantIndex)) { $form->add('dateOfBirth', BirthdayType::class, $this->mergeFieldState([ 'label' => 'Geburtsdatum', + 'required' => !$personalDataOptional, 'widget' => 'text', 'input' => 'datetime_immutable', 'html5' => false, @@ -197,7 +204,7 @@ class BookingParticipantType extends AbstractType if ($this->fieldStateProvider->shouldIncludeField('gender', $bookingDto, $participantIndex)) { $form->add('gender', ChoiceType::class, $this->mergeFieldState([ - 'label' => 'Geschlecht', + 'label' => 'Gender', 'required' => false, 'placeholder' => 'keine Angabe', 'choices' => [ @@ -212,6 +219,7 @@ class BookingParticipantType extends AbstractType if ($this->fieldStateProvider->shouldIncludeField('nationality', $bookingDto, $participantIndex)) { $form->add('nationality', CountryType::class, $this->mergeFieldState([ 'label' => 'Nationalität', + 'required' => !$personalDataOptional, 'property' => 'nationality', 'preferred_choices' => ['D', 'A', 'CH'], 'property_path' => 'participant.nationality', @@ -221,6 +229,7 @@ class BookingParticipantType extends AbstractType if ($this->fieldStateProvider->shouldIncludeField('email', $bookingDto, $participantIndex)) { $form->add('email', EmailType::class, $this->mergeFieldState([ 'label' => 'E-Mail', + 'required' => !$personalDataOptional, 'property_path' => 'participant.email', 'attr' => [ 'autocomplete' => 'leave-me-alone-chrome', @@ -457,9 +466,8 @@ class BookingParticipantType extends AbstractType } else { $groups[] = 'booking_edit'; - // Determine if strict validation applies in edit mode - // Edit mode with immutable applicant requires strict validation - if (false === ($bookingContext->participants[0]?->mutable ?? true)) { + // Strict validation in edit mode except for internal agency bookings + if (false === $bookingContext->isInternalAgencyBooking()) { $groups[] = 'strict_required'; } } diff --git a/src/Form/Model/BookingDto.php b/src/Form/Model/BookingDto.php index 451bdc1..5730cc2 100644 --- a/src/Form/Model/BookingDto.php +++ b/src/Form/Model/BookingDto.php @@ -7,6 +7,7 @@ namespace App\Form\Model; use App\BusProNet\Constants; use App\BusProNet\Model\Booking; use App\BusProNet\Model\Travel; +use App\BusProNet\XmlLoader\AgencyLoader; use App\Validator\Constraints as AppAssert; use Symfony\Component\Validator\Constraints as Assert; use Symfony\Component\Validator\Context\ExecutionContextInterface; @@ -49,6 +50,8 @@ class BookingDto public ?int $agencyId = null; + public ?string $agencyCode = null; + /** * Booking status code for API submission. * Values: 'F' (Final/Frei), 'A' (Anfrage/Inquiry). @@ -310,4 +313,16 @@ class BookingDto return $oldSnapshot !== $newSnapshot; } + + /** + * Determines if this booking is initiated by the internal agency. + * + * Internal agency bookings have a different relationship between applicant + * and first participant: the applicant is a staff member, not the first + * participant. This affects personal data editability rules. + */ + public function isInternalAgencyBooking(): bool + { + return AgencyLoader::INTERNAL_AGENCY_CODE === $this->agencyCode; + } } diff --git a/src/Form/Service/Condition/AuthenticatedUserPersonalDataCondition.php b/src/Form/Service/Condition/AuthenticatedUserPersonalDataCondition.php deleted file mode 100644 index 4617d07..0000000 --- a/src/Form/Service/Condition/AuthenticatedUserPersonalDataCondition.php +++ /dev/null @@ -1,87 +0,0 @@ - $formData Current form data (unused) - * - * @return bool True if fields should be shown as static text - */ - public function evaluate(BookingDto $bookingDto, int $participantIndex, array $formData): bool - { - $participant = $bookingDto->getParticipant($participantIndex); - if (null === $participant) { - return false; - } - - // In edit mode, compare participant's personId with applicant's personId - // This distinguishes customer-initiated (match) from agency-initiated (no match) bookings - if (BookingDto::MODE_EDIT === $bookingDto->getMode()) { - $applicantPersonId = $bookingDto->booking?->applicant?->personId; - if (null === $applicantPersonId) { - return false; - } - - return $participant->personId === $applicantPersonId; - } - - // In create mode, check if participant has personId (prepopulated from BPN account) - return null !== $participant->personId; - } - - /** - * Returns field names that this condition depends on. - * - * This condition is based on personId which is set during prepopulation - * and doesn't change during form interaction, so no field dependencies. - * - * @return string[] Empty array - no field dependencies - */ - public function getDependentFields(): array - { - return []; - } - - /** - * Returns a human-readable description of this condition. - * - * @return string Description of the authenticated user personal data protection logic - */ - public function getDescription(): string - { - return 'Personal data is shown as static text for the logged-in user (edit via personal data form)'; - } -} diff --git a/src/Form/Service/Condition/FirstParticipantReadOnlyCondition.php b/src/Form/Service/Condition/FirstParticipantReadOnlyCondition.php new file mode 100644 index 0000000..d027379 --- /dev/null +++ b/src/Form/Service/Condition/FirstParticipantReadOnlyCondition.php @@ -0,0 +1,95 @@ + $formData Current form data (unused) + * + * @return bool True if fields should be shown as static text (read-only) + */ + public function evaluate(BookingDto $bookingDto, int $participantIndex, array $formData): bool + { + // This condition only applies to first participant (agency-based read-only) + // Non-first participants: return false here, but may still be read-only via PersonalDataMutabilityCondition + if (0 !== $participantIndex) { + return false; + } + + $participant = $bookingDto->getParticipant($participantIndex); + + // CREATE mode logic + if (BookingDto::MODE_CREATE === $bookingDto->getMode()) { + // Anonymous user (no personId) = must fill in data = editable + if (null === $participant?->personId) { + return false; + } + + // Internal agency = first participant ≠ applicant = editable + if ($bookingDto->isInternalAgencyBooking()) { + return false; + } + + // Logged-in user with non-internal agency = data prepopulated = read-only + return true; + } + + // EDIT mode logic + // Internal agency = first participant ≠ applicant = editable (mutability check applies separately) + if ($bookingDto->isInternalAgencyBooking()) { + return false; + } + + // Non-internal agency = first participant = applicant = always read-only + return true; + } + + /** + * Returns field names that this condition depends on. + * + * This condition is based on agency code and personId which are set during initialization + * and don't change during form interaction, so no field dependencies. + * + * @return string[] Empty array - no field dependencies + */ + public function getDependentFields(): array + { + return []; + } + + /** + * Returns a human-readable description of this condition. + * + * @return string Description of the first participant read-only logic + */ + public function getDescription(): string + { + return 'First participant personal data is read-only for non-internal agency bookings with logged-in users'; + } +} diff --git a/src/Form/Service/Condition/PersonalDataMutabilityCondition.php b/src/Form/Service/Condition/PersonalDataMutabilityCondition.php index 19b96eb..8bd36e9 100644 --- a/src/Form/Service/Condition/PersonalDataMutabilityCondition.php +++ b/src/Form/Service/Condition/PersonalDataMutabilityCondition.php @@ -10,12 +10,13 @@ use App\Form\Service\Contract\FieldConditionInterface; /** * Condition that determines if personal data fields should be hidden based on BPN mutability flag. * - * This condition is used in EDIT MODE ONLY to respect the BPN API's per-participant - * mutability flag (`aenderungmoeglich`). When a participant's data is not mutable - * (mutable=false), personal data fields should be hidden and displayed as static text. + * This condition respects the BPN API's per-participant mutability flag (`aenderungmoeglich`). + * When a participant's data is not mutable (mutable=false), personal data fields should be + * hidden and displayed as static text. * - * This differs from AuthenticatedUserPersonalDataCondition which is used in CREATE MODE - * to protect authenticated applicant data. + * This condition is combined with FirstParticipantReadOnlyCondition via OR in edit mode. + * FirstParticipantReadOnlyCondition handles agency-based read-only logic for first participant, + * while this condition applies BPN mutability rules to all participants. * * In edit mode, BPN determines mutability based on business rules (e.g., payment status, * booking state, etc.). We must respect this flag to prevent users from attempting to diff --git a/src/Form/Service/CreateFieldStateProvider.php b/src/Form/Service/CreateFieldStateProvider.php index 54d4e83..7d80f27 100644 --- a/src/Form/Service/CreateFieldStateProvider.php +++ b/src/Form/Service/CreateFieldStateProvider.php @@ -7,7 +7,6 @@ namespace App\Form\Service; use App\BusProNet\Utility\DirectionMapper; use App\Form\Service\Abstract\AbstractFieldStateProvider; use App\Form\Service\Condition\AgeRangeCondition; -use App\Form\Service\Condition\AuthenticatedUserPersonalDataCondition; use App\Form\Service\Condition\BabyAgeCondition; use App\Form\Service\Condition\BookingEligibilityCondition; use App\Form\Service\Condition\BulkInsuranceBookingCondition; @@ -16,6 +15,7 @@ use App\Form\Service\Condition\DateOfBirthProvidedCondition; use App\Form\Service\Condition\FieldValueCondition; use App\Form\Service\Condition\FinalBookingOnlyCondition; use App\Form\Service\Condition\FirstParticipantCondition; +use App\Form\Service\Condition\FirstParticipantReadOnlyCondition; use App\Form\Service\Condition\MultipleParticipantsCondition; use App\Form\Service\Condition\RentalInsuranceAvailableCondition; use App\Form\Service\Condition\RentalSelectionCondition; @@ -80,59 +80,60 @@ class CreateFieldStateProvider extends AbstractFieldStateProvider $rentalCondition = new RentalSelectionCondition(); $skiPassCondition = new SkiPassSelectionCondition(); - // Authenticated user personal data protection - // Render personal data fields as static text for participants linked to BPN accounts (prevents duplicate records) - $authenticatedUserCondition = new AuthenticatedUserPersonalDataCondition(); + // First participant read-only condition + // Render personal data fields as static text for first participant in non-internal agency bookings + // when user is logged in (data is prepopulated from BPN account) + $firstParticipantReadOnlyCondition = new FirstParticipantReadOnlyCondition(); $this->fieldStateConditions['firstName'] = [ - 'static_text' => $authenticatedUserCondition, + 'static_text' => $firstParticipantReadOnlyCondition, ]; $this->fieldStateConditions['lastName'] = [ - 'static_text' => $authenticatedUserCondition, + 'static_text' => $firstParticipantReadOnlyCondition, ]; $this->fieldStateConditions['gender'] = [ - 'static_text' => $authenticatedUserCondition, + 'static_text' => $firstParticipantReadOnlyCondition, ]; $this->fieldStateConditions['nationality'] = [ - 'static_text' => $authenticatedUserCondition, + 'static_text' => $firstParticipantReadOnlyCondition, ]; $this->fieldStateConditions['dateOfBirth'] = [ - 'static_text' => $authenticatedUserCondition, + 'static_text' => $firstParticipantReadOnlyCondition, ]; $this->fieldStateConditions['email'] = [ - 'static_text' => $authenticatedUserCondition, + 'static_text' => $firstParticipantReadOnlyCondition, ]; - // Mobile field: static text for authenticated users, required for first participant (guest bookings) + // Mobile field: static text for first participant (when read-only), required for first participant (guest bookings) $this->fieldStateConditions['mobile'] = [ - 'static_text' => $authenticatedUserCondition, + 'static_text' => $firstParticipantReadOnlyCondition, 'required' => new FirstParticipantCondition(), ]; // Address subfields - must render as static text to prevent creating duplicate BPN records $this->fieldStateConditions['address.street'] = [ - 'static_text' => $authenticatedUserCondition, + 'static_text' => $firstParticipantReadOnlyCondition, ]; $this->fieldStateConditions['address.postCode'] = [ - 'static_text' => $authenticatedUserCondition, + 'static_text' => $firstParticipantReadOnlyCondition, ]; $this->fieldStateConditions['address.city'] = [ - 'static_text' => $authenticatedUserCondition, + 'static_text' => $firstParticipantReadOnlyCondition, ]; $this->fieldStateConditions['address.country'] = [ - 'static_text' => $authenticatedUserCondition, + 'static_text' => $firstParticipantReadOnlyCondition, ]; $this->fieldStateConditions['address.district'] = [ - 'static_text' => $authenticatedUserCondition, + 'static_text' => $firstParticipantReadOnlyCondition, ]; // Note: Body dimensions (height, weight, shoeSize) are NOT hidden for authenticated users @@ -280,9 +281,9 @@ class CreateFieldStateProvider extends AbstractFieldStateProvider ]; // Make address field required for first participant (index 0) - // Also render as static text for authenticated users to prevent duplicate BPN records + // Also render as static text for first participant when read-only $this->fieldStateConditions['address'] = [ - 'static_text' => $authenticatedUserCondition, + 'static_text' => $firstParticipantReadOnlyCondition, 'required' => new FirstParticipantCondition(), ]; diff --git a/src/Form/Service/EditFieldStateProvider.php b/src/Form/Service/EditFieldStateProvider.php index 2d6b536..67f1641 100644 --- a/src/Form/Service/EditFieldStateProvider.php +++ b/src/Form/Service/EditFieldStateProvider.php @@ -9,12 +9,12 @@ use App\Form\Model\BookingDto; use App\Form\Service\Abstract\AbstractFieldStateProvider; use App\Form\Service\Condition\AdditionalServicesMutabilityCondition; use App\Form\Service\Condition\AgeRangeCondition; -use App\Form\Service\Condition\AuthenticatedUserPersonalDataCondition; use App\Form\Service\Condition\BookingModeCondition; use App\Form\Service\Condition\CompositeCondition; use App\Form\Service\Condition\DateOfBirthProvidedCondition; use App\Form\Service\Condition\FieldValueCondition; use App\Form\Service\Condition\FirstParticipantCondition; +use App\Form\Service\Condition\FirstParticipantReadOnlyCondition; use App\Form\Service\Condition\PersonalDataMutabilityCondition; use App\Form\Service\Condition\PickupsMutabilityCondition; use App\Form\Service\Condition\RentalInsuranceAvailableCondition; @@ -50,17 +50,17 @@ class EditFieldStateProvider extends AbstractFieldStateProvider $pickupsMutabilityCondition = new PickupsMutabilityCondition(); // Personal data protection in edit mode: - // 1. Participant with personId (linked to BPN account) - edit via personal data form instead + // 1. First participant in non-internal agency booking - always read-only (first participant = applicant) // 2. BPN mutability flag (mutable=false) - respects BPN business rules // Show fields as static text if EITHER condition is true - $authenticatedUserCondition = new AuthenticatedUserPersonalDataCondition(); + $firstParticipantReadOnlyCondition = new FirstParticipantReadOnlyCondition(); $personalDataMutabilityCondition = new PersonalDataMutabilityCondition(); $personalDataHiddenCondition = CompositeCondition::or( - $authenticatedUserCondition, + $firstParticipantReadOnlyCondition, $personalDataMutabilityCondition ); - // Render all personal data fields as static text if authenticated user OR BPN indicates not mutable + // Render all personal data fields as static text if first participant in non-internal agency OR BPN indicates not mutable // Using 'static_text' state (not 'hidden') so template renders values as static text $personalDataFields = [ 'firstName', @@ -77,12 +77,12 @@ class EditFieldStateProvider extends AbstractFieldStateProvider ]; } - // Address fields - render as static text if authenticated user OR participant not mutable + // Address fields - render as static text if first participant in non-internal agency OR participant not mutable $this->fieldStateConditions['address'] = [ 'static_text' => $personalDataHiddenCondition, ]; - // Address subfields - render as static text if authenticated user OR participant not mutable + // Address subfields - render as static text if first participant in non-internal agency OR participant not mutable $this->fieldStateConditions['address.street'] = [ 'static_text' => $personalDataHiddenCondition, ]; diff --git a/src/Form/Service/ParticipantAssignedRoomFieldHandler.php b/src/Form/Service/ParticipantAssignedRoomFieldHandler.php index 00e45f3..262b6be 100644 --- a/src/Form/Service/ParticipantAssignedRoomFieldHandler.php +++ b/src/Form/Service/ParticipantAssignedRoomFieldHandler.php @@ -189,7 +189,9 @@ class ParticipantAssignedRoomFieldHandler extends AbstractParticipantFieldHandle $participant->assignedRoomId = null; // Add notification for unassigned participant - $participantLabel = $participant->isApplicant() ? 'Anmelder:in' : 'Teilnehmer:in '.($participant->index + 1); + // For internal agency bookings, first participant is not the applicant + $isApplicant = $participant->isApplicant() && false === $bookingDto->isInternalAgencyBooking(); + $participantLabel = $isApplicant ? 'Anmelder:in' : 'Teilnehmer:in '.($participant->index + 1); $participant->addNotification('warning', sprintf('%s wurde von %s entfernt', $roomLabel, $participantLabel)); ++$unassignedCount; diff --git a/src/Form/Service/ParticipantFieldOptionsProvider.php b/src/Form/Service/ParticipantFieldOptionsProvider.php index 130bc88..1303219 100644 --- a/src/Form/Service/ParticipantFieldOptionsProvider.php +++ b/src/Form/Service/ParticipantFieldOptionsProvider.php @@ -421,7 +421,10 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider 'label' => 'Für alle Teilnehmer buchen', 'required' => false, 'attr' => [ - 'data-description' => 'Der hier angezeigte Preis gilt nur für den/die Anmelder:in. Die Preise für die anderen Teilnehmer:innen werden automatisch aktualisiert.', + 'data-description' => sprintf( + 'Der hier angezeigte Preis gilt nur für %s. Die Preise für die anderen Teilnehmer:innen werden automatisch aktualisiert.', + $bookingDto->isInternalAgencyBooking() ? 'Teilnehmer:in 1' : 'den/die Anmelder:in' + ), ], ]; diff --git a/src/Service/BookingEditDataLoaderService.php b/src/Service/BookingEditDataLoaderService.php index acd8505..9ceb557 100644 --- a/src/Service/BookingEditDataLoaderService.php +++ b/src/Service/BookingEditDataLoaderService.php @@ -8,6 +8,7 @@ use App\BusProNet\ApiClient; use App\BusProNet\DataProcessor\BookingDataProcessor; use App\BusProNet\Model\Booking; use App\BusProNet\Model\Notification; +use App\BusProNet\XmlLoader\AgencyLoader; use App\Entity\User; use App\Form\Model\BookingDto; use App\Security\Crypt; @@ -35,6 +36,7 @@ class BookingEditDataLoaderService private readonly BookingFingerprintService $fingerprintService, private readonly TravelDataService $travelDataService, private readonly BookingEditDraftService $draftService, + private readonly AgencyLoader $agencyLoader, private readonly Crypt $crypt, private readonly TagAwareCacheInterface $bpnCache, ) { @@ -125,6 +127,11 @@ class BookingEditDataLoaderService $formData = $this->bookingDataProcessor->createBookingDtoFromBooking($bookingData, $travelData); + // Set agency code for internal agency detection (used by field state conditions) + $formData->agencyCode = null !== $bookingData->agencyId + ? $this->agencyLoader->loadById($bookingData->agencyId)?->code + : null; + // Set original fingerprint BEFORE applying draft, so dirty detection // compares against the original API data (not the draft-modified data) $formData->originalFingerprint = $this->fingerprintService->generateFingerprint($formData, true); diff --git a/src/Service/BookingService.php b/src/Service/BookingService.php index fc7757e..c9933e4 100644 --- a/src/Service/BookingService.php +++ b/src/Service/BookingService.php @@ -5,6 +5,7 @@ namespace App\Service; use App\BusProNet\Constants; use App\BusProNet\Model\Room; use App\BusProNet\Model\Travel; +use App\BusProNet\XmlLoader\AgencyLoader; use App\Exception\BookingSessionNotFoundException; use App\Exception\NoRoomsAvailableException; use App\Form\Model\BookingDto; @@ -28,6 +29,7 @@ class BookingService private readonly TravelDataService $travelDataService, private readonly BookingPriceCalculatorService $priceCalculator, private readonly ParticipantEligibilityService $participantEligibilityService, + private readonly AgencyLoader $agencyLoader, #[Autowire('%default_booking_status%')] private readonly string $defaultBookingStatus, ) { @@ -267,6 +269,9 @@ class BookingService $bookingCreateDto->roomSelections = $roomSelections; $bookingCreateDto->currentStep = 1; $bookingCreateDto->agencyId = $agencyId; + $bookingCreateDto->agencyCode = null !== $agencyId + ? $this->agencyLoader->loadById($agencyId)?->code + : null; $bookingCreateDto->bookingStatus = $bookingStatus; $this->saveBookingDto($request, $bookingCreateDto, BookingDto::MODE_CREATE); diff --git a/src/Service/ParticipantCardDataService.php b/src/Service/ParticipantCardDataService.php index 1165870..fc7edf6 100644 --- a/src/Service/ParticipantCardDataService.php +++ b/src/Service/ParticipantCardDataService.php @@ -36,7 +36,7 @@ class ParticipantCardDataService } // Extract participant name with fallback - $name = $this->getParticipantName($participant, $index); + $name = $this->getParticipantName($bookingDto, $participant, $index); // Extract email $email = $participant->email ?? ''; @@ -78,7 +78,7 @@ class ParticipantCardDataService /** * Get participant name with fallback to generic label. */ - private function getParticipantName(object $participant, int $index): string + private function getParticipantName(BookingDto $bookingDto, object $participant, int $index): string { $firstName = $participant->firstName ?? ''; $lastName = $participant->lastName ?? ''; @@ -86,7 +86,12 @@ class ParticipantCardDataService $name = trim($firstName.' '.$lastName); if ('' === $name) { - return 0 === $index ? 'Anmelder:in' : 'Teilnehmer:in'; + // For internal agency bookings, first participant is not the applicant + if (0 === $index && false === $bookingDto->isInternalAgencyBooking()) { + return 'Anmelder:in'; + } + + return 'Teilnehmer:in'; } return $name; diff --git a/templates/booking/_participant_form.html.twig b/templates/booking/_participant_form.html.twig index b071f08..eba5faa 100644 --- a/templates/booking/_participant_form.html.twig +++ b/templates/booking/_participant_form.html.twig @@ -155,7 +155,7 @@ {{ participantIndex + 1 }}
- {{ participantIndex == 0 ? 'Anmelder:in' : 'Teilnehmer:in ' ~ (participantIndex + 1) }} + {{ participantIndex == 0 and not bookingDto.isInternalAgencyBooking() ? 'Anmelder:in' : 'Teilnehmer:in ' ~ (participantIndex + 1) }}
@@ -445,7 +445,7 @@
- Reiseversicherung* – wie Anmelder + Reiseversicherung* – wie {{ bookingDto.isInternalAgencyBooking() ? 'Teilnehmer:in 1' : 'Anmelder:in' }} {% if applicantInsurance %} @@ -480,7 +480,7 @@ {% else %}
- + diff --git a/templates/booking/create/step_2.html.twig b/templates/booking/create/step_2.html.twig index 15a0002..b057df7 100644 --- a/templates/booking/create/step_2.html.twig +++ b/templates/booking/create/step_2.html.twig @@ -32,7 +32,7 @@

- Teilnehmer + Teilnehmer:innen

{# Display form-level validation errors #} diff --git a/templates/booking/create/step_4.html.twig b/templates/booking/create/step_4.html.twig index d7900fd..85501f3 100644 --- a/templates/booking/create/step_4.html.twig +++ b/templates/booking/create/step_4.html.twig @@ -191,7 +191,7 @@
{{ participant.firstName }} {{ participant.lastName }} - {% if loop.first %}(Anmelder){% endif %} + {% if loop.first and not bookingCreateDto.isInternalAgencyBooking() %}(Anmelder:in){% endif %} {% if participantPrices is defined and participantPrices[loop.index0] is defined %} {{ participantPrices[loop.index0]|format_currency('EUR') }} diff --git a/templates/booking/edit/index.html.twig b/templates/booking/edit/index.html.twig index 1893aea..176609d 100644 --- a/templates/booking/edit/index.html.twig +++ b/templates/booking/edit/index.html.twig @@ -44,7 +44,7 @@ {# Scrollable content #}
{% block participant_cards %} -

Teilnehmer

+

Teilnehmer:innen

{% if isDirty %} {% include '_partials/_alert.html.twig' with { diff --git a/tests/Service/BookingServiceBabyTest.php b/tests/Service/BookingServiceBabyTest.php index 853dbf4..576f9b3 100644 --- a/tests/Service/BookingServiceBabyTest.php +++ b/tests/Service/BookingServiceBabyTest.php @@ -7,6 +7,7 @@ namespace App\Tests\Service; use App\BusProNet\Constants; use App\BusProNet\Model\Service; use App\BusProNet\Model\Travel; +use App\BusProNet\XmlLoader\AgencyLoader; use App\Form\Model\BookingDto; use App\Form\Model\ParticipantDto; use App\Service\BookingPriceCalculatorService; @@ -25,11 +26,14 @@ class BookingServiceBabyTest extends TestCase $travelDataService = $this->createMock(TravelDataService::class); $priceCalculator = $this->createMock(BookingPriceCalculatorService::class); $this->participantEligibilityService = $this->createMock(ParticipantEligibilityService::class); + $agencyLoader = $this->createMock(AgencyLoader::class); $this->bookingService = new BookingService( $travelDataService, $priceCalculator, - $this->participantEligibilityService + $this->participantEligibilityService, + $agencyLoader, + 'F' // default booking status ); } diff --git a/tests/Service/BookingServiceStatusTest.php b/tests/Service/BookingServiceStatusTest.php index df8e539..7266177 100644 --- a/tests/Service/BookingServiceStatusTest.php +++ b/tests/Service/BookingServiceStatusTest.php @@ -7,6 +7,7 @@ namespace App\Tests\Service; use App\BusProNet\Constants; use App\BusProNet\Model\Room; use App\BusProNet\Model\Travel; +use App\BusProNet\XmlLoader\AgencyLoader; use App\Exception\NoRoomsAvailableException; use App\Service\BookingPriceCalculatorService; use App\Service\BookingService; @@ -27,11 +28,14 @@ class BookingServiceStatusTest extends TestCase $this->travelDataService = $this->createMock(TravelDataService::class); $priceCalculator = $this->createMock(BookingPriceCalculatorService::class); $participantEligibility = $this->createMock(ParticipantEligibilityService::class); + $agencyLoader = $this->createMock(AgencyLoader::class); $this->bookingService = new BookingService( $this->travelDataService, $priceCalculator, - $participantEligibility + $participantEligibility, + $agencyLoader, + 'F' // default booking status ); }
wie Anmelderwie {{ bookingDto.isInternalAgencyBooking() ? 'Teilnehmer:in 1' : 'Anmelder:in' }}