Files
myep/src/Form/Service/EditFieldStateProvider.php
T

242 lines
11 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Form\Service;
use App\BusProNet\Utility\DirectionMapper;
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\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;
use App\Form\Service\Condition\RentalSelectionCondition;
use App\Form\Service\Condition\RoomSelectionCondition;
use App\Form\Service\Condition\ServiceSubTypeCondition;
use App\Form\Service\Condition\SkiPassSelectionCondition;
use App\Form\Service\Condition\TransportationServicesMutabilityCondition;
use App\Form\Service\Condition\TravelStartCutoffReachedCondition;
/**
* Field state provider for the booking edit workflow.
*
* This service calculates dynamic field states (readonly, disabled, etc.)
* for participant fields in the edit flow, using edit-specific conditions.
*
* It extends the common field state functionality provided by
* AbstractFieldStateProvider and adds edit-specific field state logic.
*/
class EditFieldStateProvider extends AbstractFieldStateProvider
{
/**
* Registers field state conditions for the edit workflow.
*
* This method defines the conditional logic for field states in the
* booking edit process. It makes fields readonly based on mutability flags
* and applies the same conditional visibility logic as the create flow.
*/
protected function registerFieldStateConditions(): void
{
// Mutability conditions
$additionalServicesMutabilityCondition = new AdditionalServicesMutabilityCondition();
$transportationServicesMutabilityCondition = new TransportationServicesMutabilityCondition();
$pickupsMutabilityCondition = new PickupsMutabilityCondition();
// Booking-level D-4 cutoff used in addition to BPN mutability flags.
$serviceCutoffReachedCondition = new TravelStartCutoffReachedCondition(
TravelStartCutoffReachedCondition::DEFAULT_DAYS_BEFORE_START
);
// Personal data protection in edit mode:
// 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
$firstParticipantReadOnlyCondition = new FirstParticipantReadOnlyCondition();
$personalDataMutabilityCondition = new PersonalDataMutabilityCondition();
$personalDataHiddenCondition = CompositeCondition::or(
$firstParticipantReadOnlyCondition,
$personalDataMutabilityCondition
);
// Core identity fields: static text if first participant in non-internal agency OR BPN indicates not mutable
foreach (['firstName', 'lastName', 'dateOfBirth', 'gender', 'nationality'] as $field) {
$this->fieldStateConditions[$field] = [
'static_text' => $personalDataHiddenCondition,
];
}
// Contact fields: only the applicant (index 0) is read-only — participants can always update email/phone
foreach (['email', 'mobile'] as $field) {
$this->fieldStateConditions[$field] = [
'static_text' => $firstParticipantReadOnlyCondition,
];
}
// 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 first participant in non-internal agency OR participant not mutable
$this->fieldStateConditions['address.street'] = [
'static_text' => $personalDataHiddenCondition,
];
$this->fieldStateConditions['address.postCode'] = [
'static_text' => $personalDataHiddenCondition,
];
$this->fieldStateConditions['address.city'] = [
'static_text' => $personalDataHiddenCondition,
];
$this->fieldStateConditions['address.country'] = [
'static_text' => $personalDataHiddenCondition,
];
// Room assignments are fixed in edit mode - always render as static text
// Agencies must ensure proper room assignments before booking submission
$this->fieldStateConditions['assignedRoomId'] = [
'static_text' => new BookingModeCondition(BookingDto::MODE_EDIT),
];
// Show remarks room field only when room with code 'mbz' (single bed) is selected
$sharedRoomCondition = new RoomSelectionCondition(['mbz']);
$this->fieldStateConditions['remarksRoom'] = [
'hidden' => CompositeCondition::not($sharedRoomCondition),
];
// Conditional visibility for service fields (same as create flow)
$rentalCondition = new RentalSelectionCondition();
$skiPassCondition = new SkiPassSelectionCondition();
$dateOfBirthProvidedCondition = new DateOfBirthProvidedCondition();
// Hide body dimensions unless rentals are selected
$this->fieldStateConditions['bodyDimensions'] = [
'hidden' => CompositeCondition::not($rentalCondition),
];
// Age-dependent service fields - hidden until birth date provided (except for first participant who always has DOB)
// Also readonly if services not mutable
// For first participant in edit mode: DOB is always available (patched from booking), so never hide
$hideUntilDobCondition = CompositeCondition::and(
CompositeCondition::not($dateOfBirthProvidedCondition),
CompositeCondition::not(new FirstParticipantCondition()) // Don't hide for first participant
);
$this->fieldStateConditions['courses'] = [
'hidden' => $hideUntilDobCondition,
'readonly' => $additionalServicesMutabilityCondition,
];
$this->fieldStateConditions['additionalServices'] = [
'hidden' => $hideUntilDobCondition,
'readonly' => $additionalServicesMutabilityCondition,
];
$this->fieldStateConditions['board'] = [
'hidden' => $hideUntilDobCondition,
'readonly' => $additionalServicesMutabilityCondition,
];
$this->fieldStateConditions['veg'] = [
'hidden' => $hideUntilDobCondition,
'readonly' => $additionalServicesMutabilityCondition,
];
// Skipass - hidden until birth date (except first participant), readonly if services not mutable
$this->fieldStateConditions['skiPass'] = [
'hidden' => $hideUntilDobCondition,
'readonly' => $additionalServicesMutabilityCondition,
];
// Rentals - shown only when skipass selected.
// Read-only when either BPN additional services are immutable OR the D-4 cutoff is reached.
// For first participant: only check skipass, not DOB
$this->fieldStateConditions['rentals'] = [
'hidden' => CompositeCondition::or(
$hideUntilDobCondition,
CompositeCondition::not($skiPassCondition)
),
'readonly' => CompositeCondition::or(
$additionalServicesMutabilityCondition,
$serviceCutoffReachedCondition
),
];
// Rental insurance - shown only when rentals selected AND LVS services exist, readonly if services not mutable
$rentalInsuranceAvailableCondition = new RentalInsuranceAvailableCondition();
$this->fieldStateConditions['rentalInsurance'] = [
'hidden' => CompositeCondition::or(
CompositeCondition::not($rentalCondition),
CompositeCondition::not($rentalInsuranceAvailableCondition)
),
'readonly' => $additionalServicesMutabilityCondition,
];
// Transportation fields - hidden until birth date (except first participant), readonly if transportation not mutable
$this->fieldStateConditions['transportationOutbound'] = [
'hidden' => $hideUntilDobCondition,
'readonly' => $transportationServicesMutabilityCondition,
];
$this->fieldStateConditions['transportationInbound'] = [
'hidden' => $hideUntilDobCondition,
'readonly' => $transportationServicesMutabilityCondition,
];
// Pickup field - shown only when outbound transportation is BUS (pickup is for outbound journey), readonly if pickups not mutable
$this->fieldStateConditions['pickup'] = [
'hidden' => CompositeCondition::not(
ServiceSubTypeCondition::equals('transportationOutbound', DirectionMapper::SUBTYPE_BUS_API)
),
'readonly' => $pickupsMutabilityCondition,
];
// Show differentDropOff checkbox only when BOTH outbound and inbound are BUS, readonly if pickups not mutable
$this->fieldStateConditions['differentDropOff'] = [
'hidden' => CompositeCondition::not(
CompositeCondition::and(
ServiceSubTypeCondition::equals('transportationOutbound', DirectionMapper::SUBTYPE_BUS_API),
ServiceSubTypeCondition::equals('transportationInbound', DirectionMapper::SUBTYPE_BUS_API)
)
),
'readonly' => $pickupsMutabilityCondition,
];
// Show drop-off when inbound is BUS (options provider further gates on checkbox state in BUS+BUS)
$this->fieldStateConditions['dropOff'] = [
'hidden' => CompositeCondition::not(
ServiceSubTypeCondition::equals('transportationInbound', DirectionMapper::SUBTYPE_BUS_API)
),
'readonly' => $pickupsMutabilityCondition,
];
// Parking - shown only when outbound transportation is PKW AND participant is 18+, readonly if transportation not mutable
// Participants under 18 cannot drive in Germany, so parking is not relevant for them
$minimumDrivingAgeCondition = new AgeRangeCondition(18);
$this->fieldStateConditions['parking'] = [
'hidden' => CompositeCondition::or(
CompositeCondition::not(
ServiceSubTypeCondition::equals('transportationOutbound', DirectionMapper::SUBTYPE_CAR_API)
),
CompositeCondition::not($minimumDrivingAgeCondition)
),
'readonly' => $transportationServicesMutabilityCondition,
];
// License plate - shown only when parking selected, readonly if transportation not mutable
$this->fieldStateConditions['licensePlate'] = [
'hidden' => FieldValueCondition::equals('parking', false),
'readonly' => $transportationServicesMutabilityCondition,
];
}
}