feat: make email optional for under-16 participants except applicant

This commit is contained in:
fromme committed 2026-10-19 09:18:22 +02:00
1 parent b958d1a371
commit 1d9c852113
18 files changed
+435 -336

No files matched your search

+91
View File
@@ -429,6 +429,97 @@ class BookingDtoTest extends TestCase
$this->assertSame(['participants[0].address'], $this->collectFirstParticipantAddressPaths($dto));
}
public function testChildApplicantWithoutEmailIsReportedOnce(): void
{
$dto = $this->createCreateModeDto();
$dto->participants[0] = $this->createParticipantWithoutEmail(10);
$this->assertSame(
['participants[0].email'],
$this->collectFirstParticipantEmailPaths($dto),
'The applicant is the booking contact and needs an email at any age'
);
}
public function testChildAtIndexZeroOfASplitBookingMayLeaveTheEmailBlank(): void
{
$dto = $this->createCreateModeDto();
$dto->applicantTravels = false;
$dto->participants[0] = $this->createParticipantWithoutEmail(10);
$this->assertSame([], $this->collectFirstParticipantEmailPaths($dto));
}
public function testAdultAtIndexZeroOfASplitBookingStillNeedsAnEmail(): void
{
$dto = $this->createCreateModeDto();
$dto->applicantTravels = false;
$dto->participants[0] = $this->createParticipantWithoutEmail(30);
$this->assertSame(['participants[0].email'], $this->collectFirstParticipantEmailPaths($dto));
}
public function testApplicantEmailComesFromParticipantZeroWhenTheApplicantTravels(): void
{
$dto = $this->createCreateModeDto();
$dto->participants[0]->email = ' [email protected] ';
$dto->applicant = new ParticipantDto();
$dto->applicant->email = '[email protected]';
$this->assertSame('[email protected]', $dto->getApplicantEmail());
}
public function testApplicantEmailComesFromTheApplicantBlockOnACreateModeSplit(): void
{
$dto = $this->createCreateModeDto();
$dto->participants[0]->email = '[email protected]';
$dto->applicantTravels = false;
$dto->applicant = new ParticipantDto();
$dto->applicant->email = '[email protected]';
$this->assertSame('[email protected]', $dto->getApplicantEmail());
}
public function testApplicantEmailComesFromTheAnmelderOnAnEditModeSplit(): void
{
$dto = $this->createEditModeDto(100, 200);
$dto->participants[0]->email = '[email protected]';
$dto->booking->applicant->communication->email = '[email protected]';
$this->assertSame('[email protected]', $dto->getApplicantEmail());
}
/**
* Ages relative to today, since isChild() measures against the current date.
*/
private function createParticipantWithoutEmail(int $years): ParticipantDto
{
$participant = new ParticipantDto();
$participant->index = 0;
$participant->dateOfBirth = (new \DateTimeImmutable('today'))->modify(sprintf('-%d years', $years));
$participant->address = $this->createAddress();
return $participant;
}
/**
* @return list<string>
*/
private function collectFirstParticipantEmailPaths(BookingDto $dto): array
{
$validator = Validation::createValidatorBuilder()->enableAttributeMapping()->getValidator();
$violations = $validator->validate($dto, null, ['booking_create', 'strict_required']);
$paths = [];
foreach ($violations as $violation) {
if ('participants[0].email' === $violation->getPropertyPath()) {
$paths[] = $violation->getPropertyPath();
}
}
return $paths;
}
/**
* @return list<string>
*/
+100 -11
View File
@@ -909,7 +909,7 @@ class ParticipantEditDtoTest extends TestCase
* available ski pass. Ski pass validation depends on the travel actually offering
* passes, so a travel without them is a distinct fixture, not the default.
*/
public function testChildWithoutEmailFailsStrictValidation(): void
public function testChildWithoutEmailPassesStrictValidation(): void
{
$bookingDto = $this->createBookingDtoWithParticipants([
$this->createAdultParticipant('[email protected]'),
@@ -918,14 +918,10 @@ class ParticipantEditDtoTest extends TestCase
$violations = $this->validateStrict($bookingDto, 1);
$this->assertSame(
['Bitte angeben'],
$this->emailRequiredMessages($violations),
'BusPro requires a Mail-Adresse for every participant, children included'
);
$this->assertSame([], $this->emailRequiredMessages($violations));
}
public function testChildWithNullEmailFailsStrictValidation(): void
public function testChildWithNullEmailPassesStrictValidation(): void
{
$child = $this->createChildParticipant('');
$child->email = null;
@@ -937,7 +933,7 @@ class ParticipantEditDtoTest extends TestCase
$violations = $this->validateStrict($bookingDto, 1);
$this->assertSame(['Bitte angeben'], $this->emailRequiredMessages($violations));
$this->assertSame([], $this->emailRequiredMessages($violations));
}
public function testChildSharingTheApplicantsEmailPassesStrictValidation(): void
@@ -952,7 +948,7 @@ class ParticipantEditDtoTest extends TestCase
$this->assertSame(
[],
$this->emailRequiredMessages($violations),
'A child inherits the applicant address, which BusPro accepts'
'A child may use the applicant\'s address instead of leaving it blank'
);
}
@@ -1021,6 +1017,99 @@ class ParticipantEditDtoTest extends TestCase
$this->assertSame(['Bitte angeben'], $this->emailRequiredMessages($violations));
}
public function testChildAtIndexZeroOfASplitBookingMayLeaveTheEmailBlank(): void
{
$bookingDto = $this->createBookingDtoWithParticipants([
$this->createChildParticipant(''),
]);
$bookingDto->applicantTravels = false;
$violations = $this->validateStrict($bookingDto, 0);
$this->assertSame(
[],
$this->emailRequiredMessages($violations),
'Where the applicant does not travel, participant[0] is an ordinary traveller'
);
}
public function testAdultAtIndexZeroOfASplitBookingStillNeedsAnEmail(): void
{
$bookingDto = $this->createBookingDtoWithParticipants([
$this->createAdultParticipant(''),
]);
$bookingDto->applicantTravels = false;
$violations = $this->validateStrict($bookingDto, 0);
$this->assertSame(['Bitte angeben'], $this->emailRequiredMessages($violations));
}
public function testAdultTravellerMayNotReuseTheNonTravellingApplicantsEmail(): void
{
$bookingDto = $this->createBookingDtoWithParticipants([
$this->createAdultParticipant('[email protected]'),
]);
$bookingDto->applicantTravels = false;
$bookingDto->applicant = $this->createValidParticipant(new \DateTimeImmutable('1980-01-01'), '[email protected]');
$violations = $this->validator->validate(
new ParticipantEditDto(participant: $bookingDto->participants[0], bookingContext: $bookingDto),
null,
['booking_create']
);
$this->assertCount(1, $violations);
$this->assertSame('participant.email', $violations->get(0)->getPropertyPath());
$this->assertSame(
'Diese E-Mail Adresse wird bereits von der anmeldenden Person verwendet',
$violations->get(0)->getMessage()
);
}
public function testChildTravellerMayReuseTheNonTravellingApplicantsEmail(): void
{
$bookingDto = $this->createBookingDtoWithParticipants([
$this->createChildParticipant('[email protected]'),
]);
$bookingDto->applicantTravels = false;
$bookingDto->applicant = $this->createValidParticipant(new \DateTimeImmutable('1980-01-01'), '[email protected]');
$violations = $this->validator->validate(
new ParticipantEditDto(participant: $bookingDto->participants[0], bookingContext: $bookingDto),
null,
['booking_create']
);
$this->assertCount(0, $violations);
}
public function testAdultTravellerMayNotReuseTheAnmelderEmailOfASplitBookingInEditMode(): void
{
$applicant = new PersonalData();
$applicant->personId = 100;
$applicant->communication->email = '[email protected]';
$bookingDto = $this->createBookingDtoWithParticipants([
$this->createAdultParticipant('[email protected]'),
]);
$bookingDto->booking = new Booking();
$bookingDto->booking->applicant = $applicant;
$bookingDto->participants[0]->personId = 200;
$violations = $this->validator->validate(
new ParticipantEditDto(participant: $bookingDto->participants[0], bookingContext: $bookingDto),
null,
['booking_edit']
);
$this->assertCount(1, $violations);
$this->assertSame(
'Diese E-Mail Adresse wird bereits von der anmeldenden Person verwendet',
$violations->get(0)->getMessage()
);
}
public function testDependentTurningExactlySixteenRequiresEmail(): void
{
$participant = $this->createAdultParticipant('');
@@ -1036,7 +1125,7 @@ class ParticipantEditDtoTest extends TestCase
$this->assertSame(['Bitte angeben'], $this->emailRequiredMessages($violations));
}
public function testDependentOneDayShortOfSixteenStillRequiresEmail(): void
public function testDependentOneDayShortOfSixteenDoesNotRequireEmail(): void
{
$participant = $this->createAdultParticipant('');
$participant->dateOfBirth = (new \DateTimeImmutable('today'))->modify('-16 years')->modify('+1 day');
@@ -1048,7 +1137,7 @@ class ParticipantEditDtoTest extends TestCase
$violations = $this->validateStrict($bookingDto, 1);
$this->assertSame(['Bitte angeben'], $this->emailRequiredMessages($violations));
$this->assertSame([], $this->emailRequiredMessages($violations));
}
/**