feat: pre-flight check of agency bookings
addresses #869bqxr4q
This commit is contained in:
1 parent
f9142e3080
commit
b41b19d4c6
26 files changed
+1818
-304
No files matched your search
@@ -8,6 +8,7 @@ use App\BusProNet\Constants;
|
||||
use App\Validator\Constraints as AppAssert;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
use function Symfony\Component\String\u;
|
||||
|
||||
/**
|
||||
* Wrapper DTO for editing a participant within a booking context.
|
||||
@@ -36,14 +37,14 @@ class ParticipantEditDto
|
||||
* (participants aged 0-2 years at travel date). Babies don't qualify for any
|
||||
* ski pass and the field is hidden for them.
|
||||
*
|
||||
* In edit mode, this validation is skipped as the ski pass is readonly.
|
||||
* In edit submissions, this validation is skipped as the ski pass is readonly.
|
||||
*
|
||||
* This validation only runs when strict_required group is active.
|
||||
*/
|
||||
#[Assert\Callback(groups: ['strict_required'])]
|
||||
public function validateSkiPassRequired(ExecutionContextInterface $context): void
|
||||
{
|
||||
// Skip validation in edit mode - ski pass is readonly
|
||||
// Skip validation in edit submissions - ski pass is readonly there.
|
||||
if (BookingDto::MODE_EDIT === $this->bookingContext->getMode()) {
|
||||
return;
|
||||
}
|
||||
@@ -70,50 +71,27 @@ class ParticipantEditDto
|
||||
* the insurance field is hidden and will be automatically assigned by the bulk
|
||||
* insurance handler.
|
||||
*
|
||||
* In edit mode, this validation is skipped entirely because insurance data is
|
||||
* readonly and preserved as-is from the BPN API (the insurance field handler
|
||||
* does not process insurance in edit mode).
|
||||
* In edit submissions, this validation is skipped entirely because insurance
|
||||
* data is readonly and preserved as-is from the BPN API.
|
||||
*
|
||||
* This validation only runs when strict_required group is active.
|
||||
*/
|
||||
#[Assert\Callback(groups: ['strict_required'])]
|
||||
public function validateInsuranceRequired(ExecutionContextInterface $context): void
|
||||
{
|
||||
// Skip validation in edit mode - insurance is readonly and preserved as-is
|
||||
// Skip validation in edit submissions - insurance is readonly and preserved as-is.
|
||||
if (BookingDto::MODE_EDIT === $this->bookingContext->getMode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Insurance is always required for applicant in create mode
|
||||
if (true === $this->participant->isApplicant()) {
|
||||
if (null === $this->participant->insurance) {
|
||||
$context->buildViolation('Bitte auswählen')
|
||||
->atPath('participant.insurance')
|
||||
->addViolation();
|
||||
if (false === $this->participant->isApplicant()) {
|
||||
$applicant = $this->bookingContext->getParticipant(0);
|
||||
|
||||
if (true === $applicant?->bulkInsuranceBooking) {
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// For dependent participants, check if bulk insurance is active
|
||||
$applicant = $this->bookingContext->getParticipant(0);
|
||||
if (null === $applicant) {
|
||||
// Applicant not found - shouldn't happen, but validate insurance to be safe
|
||||
if (null === $this->participant->insurance) {
|
||||
$context->buildViolation('Bitte auswählen')
|
||||
->atPath('participant.insurance')
|
||||
->addViolation();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// If bulk insurance booking is active, skip validation (field is hidden, will be auto-assigned)
|
||||
if (true === $applicant->bulkInsuranceBooking) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Bulk insurance not active - insurance is required
|
||||
if (null === $this->participant->insurance) {
|
||||
$context->buildViolation('Bitte auswählen')
|
||||
->atPath('participant.insurance')
|
||||
@@ -138,7 +116,7 @@ class ParticipantEditDto
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip validation for applicant (index 0) - applicant's email can be shared with dependents
|
||||
// In regular create/edit submission flows, the applicant can share an email with dependents.
|
||||
if (true === $this->participant->isApplicant()) {
|
||||
return;
|
||||
}
|
||||
@@ -149,12 +127,12 @@ class ParticipantEditDto
|
||||
}
|
||||
|
||||
// Skip if email is null or empty (handled by @Email and @NotBlank constraints)
|
||||
if (null === $this->participant->email || '' === trim($this->participant->email)) {
|
||||
if (null === $this->participant->email || u($this->participant->email)->trim()->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Normalize current participant's email for comparison
|
||||
$normalizedEmail = strtolower(trim($this->participant->email));
|
||||
$normalizedEmail = u($this->participant->email)->trim()->lower()->toString();
|
||||
|
||||
// Check against all adult participants in booking context
|
||||
foreach ($this->bookingContext->participants as $index => $otherParticipant) {
|
||||
@@ -169,12 +147,12 @@ class ParticipantEditDto
|
||||
}
|
||||
|
||||
// Skip null or empty emails
|
||||
if (null === $otherParticipant->email || '' === trim($otherParticipant->email)) {
|
||||
if (null === $otherParticipant->email || u($otherParticipant->email)->trim()->isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Compare normalized emails
|
||||
$otherNormalizedEmail = strtolower(trim($otherParticipant->email));
|
||||
$otherNormalizedEmail = u($otherParticipant->email)->trim()->lower()->toString();
|
||||
|
||||
if ($normalizedEmail === $otherNormalizedEmail) {
|
||||
// Add violation to participant.email path
|
||||
|
||||
Reference in new issue
Block a user