feat: split booking between participants and applicant

addresses #869f2q1vu
This commit is contained in:
fromme committed 2026-10-16 17:49:04 +02:00
1 parent 0ee8fafbe1
commit ad6e42ab0c
44 files changed
+2024 -316

No files matched your search

@@ -10,15 +10,19 @@ use App\Form\Service\Contract\FieldConditionInterface;
/**
* Condition that determines if the first participant's personal data should be read-only.
*
* This condition evaluates read-only state based on agency type and authentication state,
* replacing the previous personId-based detection with agency code-based detection.
* It fires only where participant[0] IS the booking's applicant, which
* BookingDto::isFirstParticipantApplicant() decides: the booking-level choice in create mode,
* and anmelder vs. participant[0] person id in edit mode. Internal agency bookings answer
* false there, which preserves the behaviour this class used to spell out itself.
*
* Business Rules:
* - CREATE mode (anonymous user): First participant data is editable (user must fill it in)
* - CREATE mode (logged-in, non-internal agency): First participant = applicant, data is read-only
* - CREATE mode (logged-in, internal agency): First participant ≠ applicant, data is editable
* - EDIT mode (internal agency): First participant ≠ applicant, data is editable (mutability applies separately)
* - EDIT mode (non-internal agency): First participant = applicant, always read-only
* - CREATE mode (logged-in, participant[0] is the applicant): data is prepopulated, read-only
* - CREATE mode (applicant does not travel, or internal agency): participant[0] is an ordinary
* traveller, data is editable
* - EDIT mode (participant[0] is the applicant): always read-only
* - EDIT mode (split booking, or internal agency): participant[0] ≠ applicant, editable here
* (PersonalDataMutabilityCondition still applies separately)
*
* Non-first participants are never affected by this condition - they return false here
* but may still be read-only via PersonalDataMutabilityCondition in edit mode.
@@ -36,45 +40,31 @@ class FirstParticipantReadOnlyCondition implements FieldConditionInterface
*/
public function evaluate(BookingDto $bookingDto, int $participantIndex, array $formData): bool
{
// This condition only applies to first participant (agency-based read-only)
// This condition only applies to the first participant.
// 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()) {
// participant[0] is a traveller in their own right: nothing to protect here.
if (false === $bookingDto->isFirstParticipantApplicant()) {
return false;
}
// Non-internal agency = first participant = applicant = always read-only
if (BookingDto::MODE_CREATE === $bookingDto->getMode()) {
// Anonymous user (no personId) = must fill in data = editable
return null !== $bookingDto->getParticipant($participantIndex)?->personId;
}
// EDIT mode: participant[0] is the applicant, whose record is owned by the account.
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.
* This condition is based on the applicant split 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
*/
@@ -90,6 +80,6 @@ class FirstParticipantReadOnlyCondition implements FieldConditionInterface
*/
public function getDescription(): string
{
return 'First participant personal data is read-only for non-internal agency bookings with logged-in users';
return 'First participant personal data is read-only when they are also the applicant';
}
}