feat: pre-flight check of agency bookings
addresses #869bqxr4q
This commit is contained in:
@@ -15,6 +15,7 @@ use App\Service\BookingChangeTracker;
|
||||
use App\Service\BookingEditContextFactory;
|
||||
use App\Service\BookingEditDataLoader;
|
||||
use App\Service\BookingEditDraftManager;
|
||||
use App\Service\BookingEditPreFlightChecker;
|
||||
use App\Service\BookingEditSubmitter;
|
||||
use App\Service\BookingSessionManager;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
@@ -42,6 +43,7 @@ class IndexController extends AbstractController
|
||||
private readonly BookingSessionManager $bookingSessionService,
|
||||
private readonly BookingChangeTracker $fingerprintService,
|
||||
private readonly BookingEditContextFactory $editContextFactory,
|
||||
private readonly BookingEditPreFlightChecker $preFlightChecker,
|
||||
private readonly BookingEditSubmitter $formSubmitter,
|
||||
) {
|
||||
}
|
||||
@@ -107,8 +109,28 @@ class IndexController extends AbstractController
|
||||
$form = $this->createForm(BookingEditType::class, $bookingDto);
|
||||
$form->handleRequest($request);
|
||||
|
||||
// Pre-flight check is display-only: it helps users find participants that still need work
|
||||
// without turning the overview into a blocking validation state.
|
||||
$missingValueLabelsByParticipantIndex = $this->preFlightChecker
|
||||
->findMissingValueLabelsByParticipantIndex($bookingDto);
|
||||
|
||||
// Keep these states separate:
|
||||
// - $isOverviewSubmitted: the footer form was posted
|
||||
// - $isOverviewFormValid: Symfony accepted the posted data
|
||||
// - $hasBlockingValidationErrors: only non-internal bookings block submission
|
||||
$isOverviewSubmitted = $form->isSubmitted();
|
||||
$isOverviewFormValid = false;
|
||||
|
||||
if (true === $isOverviewSubmitted) {
|
||||
$isOverviewFormValid = $form->isValid();
|
||||
}
|
||||
|
||||
$hasBlockingValidationErrors = true === $isOverviewSubmitted
|
||||
&& false === $isOverviewFormValid
|
||||
&& false === $bookingDto->isInternalAgencyBooking();
|
||||
|
||||
// Handle form submission (clicking "Buchung aktualisieren")
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
if (true === $isOverviewSubmitted && (true === $isOverviewFormValid || true === $bookingDto->isInternalAgencyBooking())) {
|
||||
$submissionResult = $this->formSubmitter->handleSubmission($request, $bookingDto, $bookingId, $user);
|
||||
$this->applySubmissionResultFlashes($submissionResult);
|
||||
|
||||
@@ -119,8 +141,9 @@ class IndexController extends AbstractController
|
||||
$bookingDto,
|
||||
$bookingData,
|
||||
$this->fingerprintService->isDirty($bookingDto),
|
||||
$form->isSubmitted(),
|
||||
$form->isSubmitted() && false === $form->isValid(),
|
||||
$isOverviewSubmitted,
|
||||
$hasBlockingValidationErrors,
|
||||
$missingValueLabelsByParticipantIndex,
|
||||
);
|
||||
|
||||
$templateData = [
|
||||
|
||||
@@ -13,9 +13,11 @@ use App\Htmx\HxTrait;
|
||||
use App\Service\BookingEditContextFactory;
|
||||
use App\Service\BookingEditDataLoader;
|
||||
use App\Service\BookingEditDraftManager;
|
||||
use App\Service\ParticipantDataPrefiller;
|
||||
use App\Service\BookingSessionManager;
|
||||
use App\Service\ParticipantFormSupport;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
@@ -33,6 +35,7 @@ class ParticipantController extends AbstractController
|
||||
private readonly BookingEditDraftManager $draftService,
|
||||
private readonly BookingEditContextFactory $editContextFactory,
|
||||
private readonly BookingSessionManager $bookingSessionService,
|
||||
private readonly ParticipantDataPrefiller $prepopulationService,
|
||||
private readonly ParticipantFormSupport $participantFormSupportService,
|
||||
) {
|
||||
}
|
||||
@@ -76,16 +79,26 @@ class ParticipantController extends AbstractController
|
||||
|
||||
$this->editContextFactory->prepareBookingDto($bookingDto);
|
||||
|
||||
$wrapper = $this->participantFormSupportService->createParticipantEditDto($bookingDto, $index);
|
||||
|
||||
$form = $this->createForm(
|
||||
BookingParticipantType::class,
|
||||
$wrapper,
|
||||
$this->participantFormSupportService->getParticipantFormOptions($bookingDto)
|
||||
);
|
||||
$form = $this->createParticipantForm($bookingDto, $index);
|
||||
|
||||
$form->handleRequest($request);
|
||||
|
||||
$isDummyDataFill = $this->prepopulationService->isDummyDataFillRequested(
|
||||
$bookingDto->participants[$index],
|
||||
$bookingDto->getMode()
|
||||
);
|
||||
|
||||
if (true === $form->isSubmitted() && true === $isDummyDataFill) {
|
||||
$this->prepopulationService->fillDummyParticipant($bookingDto->participants[$index], $index);
|
||||
|
||||
$this->bookingSessionService->saveBookingDto($request, $bookingDto, BookingDto::MODE_EDIT);
|
||||
$this->draftService->saveDraft($user, $bookingId, $bookingDto);
|
||||
|
||||
$form = $this->createParticipantForm($bookingDto, $index);
|
||||
|
||||
return $this->renderParticipantForm($form, $index, $bookingDto, $bookingId, $bookingData);
|
||||
}
|
||||
|
||||
$notifications = $this->participantFormSupportService->collectAndClearNotifications($bookingDto);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
@@ -96,20 +109,7 @@ class ParticipantController extends AbstractController
|
||||
return $this->redirectToRoute('app_booking_edit', ['bookingId' => $bookingId]);
|
||||
}
|
||||
|
||||
$context = $this->editContextFactory->createParticipantContext($bookingDto, $bookingData);
|
||||
|
||||
return $this->render('booking/edit/participant.html.twig', [
|
||||
'form' => $form,
|
||||
'participantIndex' => $index,
|
||||
'bookingEditContext' => $context,
|
||||
'bookingDto' => $context->bookingDto,
|
||||
'summaryData' => $context->summaryData,
|
||||
'mutableData' => $context->mutableData,
|
||||
'refreshRouteName' => 'app_booking_edit_participant_refresh',
|
||||
'refreshRouteParams' => ['bookingId' => $bookingId, 'index' => $index],
|
||||
'cancelRouteName' => 'app_booking_edit',
|
||||
'cancelRouteParams' => ['bookingId' => $bookingId],
|
||||
]);
|
||||
return $this->renderParticipantForm($form, $index, $bookingDto, $bookingId, $bookingData);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -190,6 +190,48 @@ class ParticipantController extends AbstractController
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FormInterface<mixed>
|
||||
*/
|
||||
private function createParticipantForm(BookingDto $bookingDto, int $index): FormInterface
|
||||
{
|
||||
$wrapper = $this->participantFormSupportService->createParticipantEditDto($bookingDto, $index);
|
||||
|
||||
return $this->createForm(
|
||||
BookingParticipantType::class,
|
||||
$wrapper,
|
||||
$this->participantFormSupportService->getParticipantFormOptions($bookingDto)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Booking|null $bookingData
|
||||
* @param FormInterface<mixed> $form
|
||||
*/
|
||||
private function renderParticipantForm(
|
||||
FormInterface $form,
|
||||
int $index,
|
||||
BookingDto $bookingDto,
|
||||
int $bookingId,
|
||||
?Booking $bookingData,
|
||||
): Response
|
||||
{
|
||||
$context = $this->editContextFactory->createParticipantContext($bookingDto, $bookingData);
|
||||
|
||||
return $this->render('booking/edit/participant.html.twig', [
|
||||
'form' => $form,
|
||||
'participantIndex' => $index,
|
||||
'bookingEditContext' => $context,
|
||||
'bookingDto' => $context->bookingDto,
|
||||
'summaryData' => $context->summaryData,
|
||||
'mutableData' => $context->mutableData,
|
||||
'refreshRouteName' => 'app_booking_edit_participant_refresh',
|
||||
'refreshRouteParams' => ['bookingId' => $bookingId, 'index' => $index],
|
||||
'cancelRouteName' => 'app_booking_edit',
|
||||
'cancelRouteParams' => ['bookingId' => $bookingId],
|
||||
]);
|
||||
}
|
||||
|
||||
private function isParticipantCanceled(Booking $bookingData, int $index): bool
|
||||
{
|
||||
return Booking::STATUS_CANCELED === ($bookingData->participantsStatus[$index] ?? null);
|
||||
|
||||
Reference in New Issue
Block a user