fix: correctly distinguish between first participant and applicant

This commit is contained in:
Björn Fromme committed 2026-03-16 12:01:09 +01:00
1 parent 15dbd72f93
commit ce828a6ef5
5 files changed
+96 -97

No files matched your search

@@ -1,45 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Form\Service\Condition;
use App\Form\Model\BookingDto;
use App\Form\Service\Contract\FieldConditionInterface;
/**
* Condition that checks if the participant is the applicant.
*
* This is used to apply field state logic (e.g., enable/disable fields)
* specifically for the applicant participant in the booking.
*
* The default logic assumes the applicant is the first participant (index 0),
* but this can be adjusted if your domain uses a different rule or property.
*/
class ApplicantCondition implements FieldConditionInterface
{
/**
* Evaluates if the participant is the applicant.
*
* @param BookingDto $bookingDto The current booking data (create or edit)
* @param int $participantIndex The index of the participant being evaluated
* @param array<string, mixed> $formData Current form data (unused)
*
* @return bool True if the participant is the applicant, false otherwise
*/
public function evaluate(BookingDto $bookingDto, int $participantIndex, array $formData): bool
{
// Default: applicant is the first participant (index 0)
return 0 === $participantIndex;
}
public function getDependentFields(): array
{
return [];
}
public function getDescription(): string
{
return 'Checks if the participant is the applicant (index 0)';
}
}
@@ -8,46 +8,37 @@ use App\Form\Model\BookingDto;
use App\Form\Service\Contract\FieldConditionInterface;
/**
* Condition that determines if personal data fields should be hidden for authenticated users.
* Condition that determines if personal data fields should be shown as static text.
*
* ⚠️ IMPORTANT: This condition is used in CREATE MODE ONLY to protect authenticated
* applicant data during booking creation. It is NOT used in edit mode.
* This condition protects the logged-in user's personal data from being modified
* within the booking form. Instead, users should edit their data via the personal
* data form to prevent:
* - Creating duplicate customer records in BPN
* - Disconnecting bookings from the user's account
* - Causing data inconsistencies between booking and account data
*
* In CREATE mode:
* - When a logged-in user creates a booking, their personal data is prepopulated from
* their BPN account (including personId).
* - Their personal data fields should be hidden and displayed as static text to prevent
* modifications that could:
* - Create duplicate customer records in BPN
* - Disconnect bookings from the user's account
* - Cause data inconsistencies between booking and account data
* The condition compares the participant's personId with the booking applicant's
* personId to determine if the participant IS the logged-in user:
* - Customer-initiated bookings: Participant 0's personId matches applicant's personId
* - Agency-initiated bookings: Participant 0's personId differs from applicant's personId
*
* In EDIT mode:
* - DO NOT use this condition. Edit mode uses PersonalDataMutabilityCondition instead,
* which respects the BPN API's per-participant `mutable` flag (aenderungmoeglich).
* - The mutability flag determines editability for ALL participants uniformly in edit mode.
*
* 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 via the field_or_static macro
* In CREATE mode: Checks if participant has a personId (prepopulated from BPN account)
* In EDIT mode: Compares participant's personId with applicant's personId
*/
class AuthenticatedUserPersonalDataCondition implements FieldConditionInterface
{
/**
* Evaluates if personal data fields should be hidden (not editable) in CREATE mode.
* Evaluates if personal data fields should be shown as static text.
*
* Returns true when the participant has a personId set, indicating they are
* a logged-in user whose data was prepopulated from their BPN account.
* When true, personal data fields should be hidden and displayed as static text.
* In CREATE mode: Returns true when participant has a personId (prepopulated).
* In EDIT mode: Returns true when participant's personId matches applicant's personId,
* indicating the participant IS the logged-in user.
*
* This condition is only used during booking creation, not in edit mode.
* Edit mode uses PersonalDataMutabilityCondition to respect BPN's mutability flag.
*
* @param BookingDto $bookingDto The current booking data (create flow)
* @param BookingDto $bookingDto The 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 (authenticated user in create mode)
* @return bool True if fields should be shown as static text
*/
public function evaluate(BookingDto $bookingDto, int $participantIndex, array $formData): bool
{
@@ -56,8 +47,18 @@ class AuthenticatedUserPersonalDataCondition implements FieldConditionInterface
return false;
}
// Hide personal data fields if participant has BPN person ID (authenticated user in create mode)
// The personId is set during prepopulation when a logged-in user starts creating a booking
// 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;
}
@@ -81,6 +82,6 @@ class AuthenticatedUserPersonalDataCondition implements FieldConditionInterface
*/
public function getDescription(): string
{
return 'Personal data is not editable for authenticated users in create mode (prevents duplicate records)';
return 'Personal data is shown as static text for the logged-in user (edit via personal data form)';
}
}
@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace App\Form\Service\Condition;
use App\Form\Model\BookingDto;
use App\Form\Service\Contract\FieldConditionInterface;
/**
* Condition that checks if the participant is the first participant (index 0).
*
* This is used to apply field state logic specifically for the first participant
* in the booking, such as requiring address fields or showing DOB-dependent fields.
*
* Note: This checks if the participant is at index 0, NOT if they are the "applicant"
* in the BPN sense (the logged-in user). For agency-initiated bookings, the first
* participant may be different from the applicant/logged-in user.
*/
class FirstParticipantCondition implements FieldConditionInterface
{
/**
* Evaluates if the participant is the first participant.
*
* @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 the participant is the first participant (index 0)
*/
public function evaluate(BookingDto $bookingDto, int $participantIndex, array $formData): bool
{
return 0 === $participantIndex;
}
public function getDependentFields(): array
{
return [];
}
public function getDescription(): string
{
return 'Checks if the participant is the first participant (index 0)';
}
}