feat: pre-flight check of agency bookings

addresses #869bqxr4q
This commit is contained in:
Björn Fromme
2026-07-10 14:33:09 +02:00
parent f9142e3080
commit b41b19d4c6
26 changed files with 1818 additions and 304 deletions
@@ -597,6 +597,51 @@ class ParticipantEditDtoTest extends TestCase
$this->assertCount(0, $skiPassViolations, 'Ski pass validation should be skipped in edit mode');
}
public function testEditSubmissionValidatesApplicantAddressOnlyForFirstParticipant(): void
{
$travel = new Travel();
$travel->dateFrom = new \DateTimeImmutable('2025-06-01');
$travel->dateTo = new \DateTimeImmutable('2025-06-08');
$bookingDto = new BookingDto($travel, 1);
$bookingDto->booking = new Booking();
$bookingDto->agencyCode = AgencyLoader::INTERNAL_AGENCY_CODE;
$participant0 = $this->createAdultParticipant('[email protected]');
$participant0->index = 0;
$participant0->address = null;
$participant1 = $this->createAdultParticipant('[email protected]');
$participant1->index = 1;
$participant1->address = null;
$bookingDto->participants = [$participant0, $participant1];
$wrapper0 = new ParticipantEditDto(
participant: $bookingDto->participants[0],
bookingContext: $bookingDto,
);
$wrapper1 = new ParticipantEditDto(
participant: $bookingDto->participants[1],
bookingContext: $bookingDto,
);
$violations0 = $this->validator->validate($wrapper0, null, ['booking_edit', 'strict_required']);
$addressViolations0 = array_filter(
iterator_to_array($violations0),
fn ($v) => 'address' === $v->getPropertyPath() || 'participant.address' === $v->getPropertyPath()
);
$violations1 = $this->validator->validate($wrapper1, null, ['booking_edit', 'strict_required']);
$addressViolations1 = array_filter(
iterator_to_array($violations1),
fn ($v) => 'address' === $v->getPropertyPath() || 'participant.address' === $v->getPropertyPath()
);
$this->assertCount(1, $addressViolations0, 'Applicant address should still be required in edit submission');
$this->assertCount(0, $addressViolations1, 'Non-applicant address should not be required in edit submission');
}
public function testSkiPassAgeCalculatedAtTravelDate(): void
{
$travel = new Travel();
@@ -635,6 +680,54 @@ class ParticipantEditDtoTest extends TestCase
$this->assertCount(0, $skiPassViolations, 'Age should be calculated at travel date for baby exemption');
}
public function testDependentWithoutInsurancePassesWhenApplicantUsesBulkInsurance(): void
{
$applicant = $this->createAdultParticipant('[email protected]');
$applicant->bulkInsuranceBooking = true;
$dependent = $this->createAdultParticipant('[email protected]');
$dependent->insurance = null;
$bookingDto = $this->createBookingDtoWithParticipants([$applicant, $dependent]);
$wrapper = new ParticipantEditDto(
participant: $bookingDto->participants[1],
bookingContext: $bookingDto,
);
$violations = $this->validator->validate($wrapper, null, ['strict_required']);
$insuranceViolations = array_filter(
iterator_to_array($violations),
fn ($v) => 'participant.insurance' === $v->getPropertyPath()
);
$this->assertCount(0, $insuranceViolations);
}
public function testDependentWithoutInsuranceFailsWhenApplicantDoesNotUseBulkInsurance(): void
{
$applicant = $this->createAdultParticipant('[email protected]');
$applicant->bulkInsuranceBooking = false;
$dependent = $this->createAdultParticipant('[email protected]');
$dependent->insurance = null;
$bookingDto = $this->createBookingDtoWithParticipants([$applicant, $dependent]);
$wrapper = new ParticipantEditDto(
participant: $bookingDto->participants[1],
bookingContext: $bookingDto,
);
$violations = $this->validator->validate($wrapper, null, ['strict_required']);
$insuranceViolations = array_filter(
iterator_to_array($violations),
fn ($v) => 'participant.insurance' === $v->getPropertyPath()
);
$this->assertCount(1, $insuranceViolations);
}
private function createBookingDtoWithParticipants(array $participants): BookingDto
{
$travel = new Travel();