feat: authenticated booking

This commit is contained in:
Björn Fromme
2026-03-16 11:59:11 +01:00
parent 1e464cef0a
commit dc6de1734a
13 changed files with 866 additions and 50 deletions
@@ -0,0 +1,76 @@
<?php
declare(strict_types=1);
namespace App\Form\Service\Condition;
use App\Form\Model\BookingDto;
use App\Form\Service\Contract\FieldConditionInterface;
/**
* Condition that determines if personal data fields should be hidden for authenticated users.
*
* When a participant is linked to an existing BPN account (has addressId and personId),
* their personal data should not be editable during the booking process. Changes to
* master personal data should only happen through the dedicated personal data management
* interface to prevent:
* - Creating duplicate customer records in BPN
* - Disconnecting bookings from the user's account
* - Data inconsistencies between booking and account data
*
* When this condition is satisfied (returns true), the template should:
* - Hide the form fields for personal data
* - Display the values as static, read-only text
*
* This follows the same pattern as bulk insurance booking.
*/
class AuthenticatedUserPersonalDataCondition implements FieldConditionInterface
{
/**
* Evaluates if personal data fields should be hidden (not editable).
*
* Returns true when the participant has both addressId and personId set,
* indicating they are linked to an existing BPN account. When true,
* personal data fields should be hidden and displayed as static text.
*
* @param BookingDto $bookingDto The current booking data
* @param int $participantIndex The index of the participant being evaluated
* @param array<string, mixed> $formData Current form data (unused)
*
* @return bool True if fields should be hidden (participant linked to BPN account)
*/
public function evaluate(BookingDto $bookingDto, int $participantIndex, array $formData): bool
{
$participant = $bookingDto->getParticipant($participantIndex);
if (null === $participant) {
return false;
}
// Hide personal data fields if participant has BPN account IDs
// (indicates prepopulation from authenticated user)
return null !== $participant->addressId && null !== $participant->personId;
}
/**
* Returns field names that this condition depends on.
*
* This condition is based on addressId/personId which are set during prepopulation
* and don't change during the 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 not editable when participant is linked to BPN account (prevents duplicate records)';
}
}
+59 -5
View File
@@ -7,6 +7,7 @@ namespace App\Form\Service;
use App\BusProNet\Utility\DirectionMapper;
use App\Form\Service\Abstract\AbstractFieldStateProvider;
use App\Form\Service\Condition\ApplicantCondition;
use App\Form\Service\Condition\AuthenticatedUserPersonalDataCondition;
use App\Form\Service\Condition\BookingEligibilityCondition;
use App\Form\Service\Condition\BulkInsuranceBookingCondition;
use App\Form\Service\Condition\CompositeCondition;
@@ -73,6 +74,64 @@ class CreateFieldStateProvider extends AbstractFieldStateProvider
$rentalCondition = new RentalSelectionCondition();
$skiPassCondition = new SkiPassSelectionCondition();
// Authenticated user personal data protection
// Hide personal data fields for participants linked to BPN accounts (prevents duplicate records)
$authenticatedUserCondition = new AuthenticatedUserPersonalDataCondition();
$this->fieldStateConditions['firstName'] = [
'hidden' => $authenticatedUserCondition,
];
$this->fieldStateConditions['lastName'] = [
'hidden' => $authenticatedUserCondition,
];
$this->fieldStateConditions['gender'] = [
'hidden' => $authenticatedUserCondition,
];
$this->fieldStateConditions['nationality'] = [
'hidden' => $authenticatedUserCondition,
];
$this->fieldStateConditions['dateOfBirth'] = [
'hidden' => $authenticatedUserCondition,
];
$this->fieldStateConditions['email'] = [
'hidden' => $authenticatedUserCondition,
];
// Mobile field: hidden for authenticated users, required for guest applicants
$this->fieldStateConditions['mobile'] = [
'hidden' => $authenticatedUserCondition,
'required' => new ApplicantCondition(),
];
// Address subfields - must be hidden to prevent creating duplicate BPN records
$this->fieldStateConditions['address.street'] = [
'hidden' => $authenticatedUserCondition,
];
$this->fieldStateConditions['address.postCode'] = [
'hidden' => $authenticatedUserCondition,
];
$this->fieldStateConditions['address.city'] = [
'hidden' => $authenticatedUserCondition,
];
$this->fieldStateConditions['address.country'] = [
'hidden' => $authenticatedUserCondition,
];
$this->fieldStateConditions['address.district'] = [
'hidden' => $authenticatedUserCondition,
];
// Note: Body dimensions (height, weight, shoeSize) are NOT hidden for authenticated users
// These are preferences/measurements that can be updated without creating duplicate records
// Hide body dimensions section unless rental services are selected
$this->fieldStateConditions['bodyDimensions'] = [
'hidden' => CompositeCondition::not($rentalCondition),
@@ -195,11 +254,6 @@ 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(),