chore: further cleanup and refactor
This commit is contained in:
@@ -15,6 +15,8 @@ use App\BusProNet\Constants;
|
||||
*/
|
||||
class Booking
|
||||
{
|
||||
public const string STATUS_CANCELED = 'S';
|
||||
|
||||
public ?int $id = null;
|
||||
public ?int $agencyId = null;
|
||||
public ?int $bookingNumber = null;
|
||||
|
||||
@@ -15,6 +15,10 @@ namespace App\BusProNet\Model;
|
||||
*/
|
||||
class BookingResponse
|
||||
{
|
||||
public const string BOOKING_POSSIBLE = 'möglich';
|
||||
public const string BOOKING_IMPOSSIBLE = 'nicht möglich';
|
||||
public const string BOOKING_PROCESSED = 'erfolgt';
|
||||
|
||||
/**
|
||||
* @param string $status Booking status (möglich|erfolgt)
|
||||
* @param int|null $bookingNumber Booking number (BPN XML: vorgang)
|
||||
@@ -37,7 +41,7 @@ class BookingResponse
|
||||
*/
|
||||
public function isInquiryValid(): bool
|
||||
{
|
||||
return 'möglich' === $this->status;
|
||||
return self::BOOKING_POSSIBLE === $this->status;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -45,7 +49,7 @@ class BookingResponse
|
||||
*/
|
||||
public function isBookingSuccessful(): bool
|
||||
{
|
||||
return 'erfolgt' === $this->status;
|
||||
return self::BOOKING_PROCESSED === $this->status;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controller\Booking\Create;
|
||||
|
||||
use App\Exception\BookingSessionNotFoundException;
|
||||
use App\Exception\HotelNotInTravelException;
|
||||
use App\Exception\TravelNotFoundException;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Service\BookingSessionManager;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Shared base controller for booking creation steps.
|
||||
*/
|
||||
abstract class AbstractBookingCreateController extends AbstractController
|
||||
{
|
||||
protected function loadBookingCreateDto(BookingSessionManager $bookingSessionService, Request $request): BookingDto
|
||||
{
|
||||
return $bookingSessionService->getOrCreateBookingCreateDto($request);
|
||||
}
|
||||
|
||||
protected function createBookingCreateFailureResponse(\Throwable $exception, bool $htmx): Response
|
||||
{
|
||||
if (true === $htmx) {
|
||||
return $this->createBookingCreateHtmxFailureResponse();
|
||||
}
|
||||
|
||||
return $this->createBookingCreateRedirectFailureResponse($exception);
|
||||
}
|
||||
|
||||
protected function validateStepAccess(BookingDto $bookingCreateDto, int $expectedStep): ?RedirectResponse
|
||||
{
|
||||
// Allow access to current step or any previous step
|
||||
if ($expectedStep > $bookingCreateDto->currentStep) {
|
||||
$this->addFlash('error', 'Bitte erst die vorherigen Schritte abschließen.');
|
||||
|
||||
return $this->redirectToCurrentStep($bookingCreateDto);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles API errors by logging and adding a flash message.
|
||||
*
|
||||
* @param array<string, mixed> $context
|
||||
*/
|
||||
protected function handleApiError(
|
||||
LoggerInterface $logger,
|
||||
string $logMessage,
|
||||
array $context,
|
||||
string $flashMessage,
|
||||
): void {
|
||||
$logger->error($logMessage, $context);
|
||||
$this->addFlash('error', $flashMessage);
|
||||
}
|
||||
|
||||
private function createBookingCreateRedirectFailureResponse(\Throwable $exception): Response
|
||||
{
|
||||
$message = match (true) {
|
||||
$exception instanceof BookingSessionNotFoundException => 'Deine Buchungssitzung ist abgelaufen. Bitte starte eine neue Buchung.',
|
||||
$exception instanceof TravelNotFoundException => 'Reisedaten sind nicht (mehr) verfügbar.',
|
||||
$exception instanceof HotelNotInTravelException => 'Das gewählte Hotel ist für diese Reise nicht verfügbar.',
|
||||
default => 'Die Buchung konnte nicht geladen werden.',
|
||||
};
|
||||
|
||||
$this->addFlash('error', $message);
|
||||
|
||||
return $this->redirectToRoute('app_booking_create_error');
|
||||
}
|
||||
|
||||
private function createBookingCreateHtmxFailureResponse(): Response
|
||||
{
|
||||
return new Response('', 400);
|
||||
}
|
||||
|
||||
private function redirectToCurrentStep(BookingDto $bookingCreateDto): RedirectResponse
|
||||
{
|
||||
$route = match ($bookingCreateDto->currentStep) {
|
||||
2 => 'app_booking_create_step_2',
|
||||
3 => 'app_booking_create_step_3',
|
||||
4 => 'app_booking_create_step_4',
|
||||
default => 'app_booking_create_step_1',
|
||||
};
|
||||
|
||||
return $this->redirectToRoute($route);
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Controller\Booking\Create;
|
||||
|
||||
use App\Controller\Booking\Traits\BookingCreateTrait;
|
||||
use App\Controller\Booking\Traits\BookingExceptionHandlerTrait;
|
||||
use App\Exception\BookingSessionNotFoundException;
|
||||
use App\Exception\HotelNotInTravelException;
|
||||
use App\Exception\TravelNotFoundException;
|
||||
use App\Form\BookingCreateStep1Type;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Htmx\HxTrait;
|
||||
@@ -13,7 +14,6 @@ use App\Service\BookingConfigurator;
|
||||
use App\Service\BookingCreateContextFactory;
|
||||
use App\Service\BookingSessionManager;
|
||||
use App\Service\RoomPricingCalculator;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
@@ -24,10 +24,8 @@ use Symfony\Component\Routing\Attribute\Route;
|
||||
* This controller manages room selection functionality where users
|
||||
* choose the types and quantities of rooms for their booking.
|
||||
*/
|
||||
class Step1Controller extends AbstractController
|
||||
class Step1Controller extends AbstractBookingCreateController
|
||||
{
|
||||
use BookingCreateTrait;
|
||||
use BookingExceptionHandlerTrait;
|
||||
use HxTrait;
|
||||
|
||||
public function __construct(
|
||||
@@ -43,11 +41,11 @@ class Step1Controller extends AbstractController
|
||||
#[Route('/bookings/create/rooms', name: 'app_booking_create_step_1')]
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
$result = $this->getOrCreateBookingCreateDto($this->bookingSessionService, $request);
|
||||
if ($result instanceof Response) {
|
||||
return $result;
|
||||
try {
|
||||
$bookingCreateDto = $this->loadBookingCreateDto($this->bookingSessionService, $request);
|
||||
} catch (BookingSessionNotFoundException|TravelNotFoundException|HotelNotInTravelException $exception) {
|
||||
return $this->createBookingCreateFailureResponse($exception, false);
|
||||
}
|
||||
$bookingCreateDto = $result;
|
||||
|
||||
// Get or create baseline snapshot for change detection
|
||||
$oldRoomSelectionSnapshot = $this->bookingSessionService->getOrCreateBaselineSnapshot($request, $bookingCreateDto);
|
||||
@@ -98,11 +96,11 @@ class Step1Controller extends AbstractController
|
||||
#[Route('/bookings/create/refresh', name: 'app_booking_create_step_1_refresh', methods: ['POST'])]
|
||||
public function refresh(Request $request): Response
|
||||
{
|
||||
$result = $this->getOrCreateBookingCreateDtoForHtmx($this->bookingSessionService, $request);
|
||||
if ($result instanceof Response) {
|
||||
return $result;
|
||||
try {
|
||||
$bookingCreateDto = $this->loadBookingCreateDto($this->bookingSessionService, $request);
|
||||
} catch (BookingSessionNotFoundException|TravelNotFoundException|HotelNotInTravelException $exception) {
|
||||
return $this->createBookingCreateFailureResponse($exception, true);
|
||||
}
|
||||
$bookingCreateDto = $result;
|
||||
|
||||
// Process form data without validation to capture current state
|
||||
$form = $this->createForm(BookingCreateStep1Type::class, $bookingCreateDto, [
|
||||
|
||||
@@ -5,19 +5,20 @@ declare(strict_types=1);
|
||||
namespace App\Controller\Booking\Create;
|
||||
|
||||
use App\BusProNet\Model\Travel;
|
||||
use App\Controller\Booking\Traits\BookingCreateTrait;
|
||||
use App\Controller\Booking\Traits\BookingExceptionHandlerTrait;
|
||||
use App\Exception\BookingSessionNotFoundException;
|
||||
use App\Exception\HotelNotInTravelException;
|
||||
use App\Exception\TravelNotFoundException;
|
||||
use App\Entity\User;
|
||||
use App\Form\BookingCreateStep2Type;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Model\ParticipantDto;
|
||||
use App\Form\Model\RoomSelectionDto;
|
||||
use App\Service\BookingConfigurator;
|
||||
use App\Service\BookingCreateContextFactory;
|
||||
use App\Service\BookingSessionManager;
|
||||
use App\Service\ParticipantDataPrefiller;
|
||||
use App\Service\RoomAssigner;
|
||||
use App\Service\TravelDataProvider;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
@@ -28,11 +29,8 @@ use Symfony\Component\Routing\Attribute\Route;
|
||||
* This controller uses a card overview and lazy-loaded individual participant forms
|
||||
* for better performance and UX with large groups (50+ participants).
|
||||
*/
|
||||
class Step2Controller extends AbstractController
|
||||
class Step2Controller extends AbstractBookingCreateController
|
||||
{
|
||||
use BookingCreateTrait;
|
||||
use BookingExceptionHandlerTrait;
|
||||
|
||||
public function __construct(
|
||||
private readonly BookingConfigurator $bookingService,
|
||||
private readonly BookingSessionManager $bookingSessionService,
|
||||
@@ -49,15 +47,14 @@ class Step2Controller extends AbstractController
|
||||
#[Route('/bookings/create/participants', name: 'app_booking_create_step_2')]
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
// Load or create booking DTO
|
||||
$result = $this->getOrCreateBookingCreateDto($this->bookingSessionService, $request);
|
||||
if ($result instanceof Response) {
|
||||
return $result;
|
||||
try {
|
||||
$bookingCreateDto = $this->loadBookingCreateDto($this->bookingSessionService, $request);
|
||||
} catch (BookingSessionNotFoundException|TravelNotFoundException|HotelNotInTravelException $exception) {
|
||||
return $this->createBookingCreateFailureResponse($exception, false);
|
||||
}
|
||||
$bookingCreateDto = $result;
|
||||
|
||||
// Validate step access
|
||||
if ($redirect = $this->validateStepAccess($bookingCreateDto, 2)) {
|
||||
if (null !== $redirect = $this->validateStepAccess($bookingCreateDto, 2)) {
|
||||
return $redirect;
|
||||
}
|
||||
|
||||
@@ -132,7 +129,9 @@ class Step2Controller extends AbstractController
|
||||
}
|
||||
}
|
||||
|
||||
/** @param array<int, \App\Form\Model\RoomSelectionDto> $roomSelections */
|
||||
/**
|
||||
* @param array<int, RoomSelectionDto> $roomSelections
|
||||
*/
|
||||
private function calculateParticipantsCount(array $roomSelections, Travel $travelData): int
|
||||
{
|
||||
$participantsCount = 0;
|
||||
|
||||
@@ -8,8 +8,9 @@ use App\BusProNet\ApiClient;
|
||||
use App\BusProNet\Exception\TimeoutException;
|
||||
use App\BusProNet\Model\BookingResponse;
|
||||
use App\BusProNet\Model\Notification;
|
||||
use App\Controller\Booking\Traits\BookingCreateTrait;
|
||||
use App\Controller\Booking\Traits\BookingExceptionHandlerTrait;
|
||||
use App\Exception\BookingSessionNotFoundException;
|
||||
use App\Exception\HotelNotInTravelException;
|
||||
use App\Exception\TravelNotFoundException;
|
||||
use App\Form\BookingCreateStep3Type;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Htmx\HxTrait;
|
||||
@@ -20,7 +21,6 @@ use App\Service\BookingPriceMismatchAnalyzer;
|
||||
use App\Service\BookingSessionManager;
|
||||
use App\Service\RoomPricingCalculator;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
@@ -29,10 +29,8 @@ use Symfony\Component\Routing\Attribute\Route;
|
||||
/**
|
||||
* Handles the third step of the booking creation process (payment method selection).
|
||||
*/
|
||||
class Step3Controller extends AbstractController
|
||||
class Step3Controller extends AbstractBookingCreateController
|
||||
{
|
||||
use BookingCreateTrait;
|
||||
use BookingExceptionHandlerTrait;
|
||||
use HxTrait;
|
||||
|
||||
public function __construct(
|
||||
@@ -52,14 +50,14 @@ class Step3Controller extends AbstractController
|
||||
#[Route('/bookings/create/payment', name: 'app_booking_create_step_3')]
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
$result = $this->getOrCreateBookingCreateDto($this->bookingSessionService, $request);
|
||||
if ($result instanceof Response) {
|
||||
return $result;
|
||||
try {
|
||||
$bookingCreateDto = $this->loadBookingCreateDto($this->bookingSessionService, $request);
|
||||
} catch (BookingSessionNotFoundException|TravelNotFoundException|HotelNotInTravelException $exception) {
|
||||
return $this->createBookingCreateFailureResponse($exception, false);
|
||||
}
|
||||
$bookingCreateDto = $result;
|
||||
|
||||
// Validate step access
|
||||
if ($redirect = $this->validateStepAccess($bookingCreateDto, 3)) {
|
||||
if (null !== $redirect = $this->validateStepAccess($bookingCreateDto, 3)) {
|
||||
return $redirect;
|
||||
}
|
||||
|
||||
@@ -203,11 +201,11 @@ class Step3Controller extends AbstractController
|
||||
#[Route('/bookings/create/payment/refresh', name: 'app_booking_create_step_3_refresh', methods: ['POST'])]
|
||||
public function refresh(Request $request): Response
|
||||
{
|
||||
$result = $this->getOrCreateBookingCreateDto($this->bookingSessionService, $request);
|
||||
if ($result instanceof Response) {
|
||||
return $result;
|
||||
try {
|
||||
$bookingCreateDto = $this->loadBookingCreateDto($this->bookingSessionService, $request);
|
||||
} catch (BookingSessionNotFoundException|TravelNotFoundException|HotelNotInTravelException $exception) {
|
||||
return $this->createBookingCreateFailureResponse($exception, true);
|
||||
}
|
||||
$bookingCreateDto = $result;
|
||||
|
||||
$form = $this->createForm(BookingCreateStep3Type::class, $bookingCreateDto, [
|
||||
'validation_groups' => false,
|
||||
@@ -248,7 +246,7 @@ class Step3Controller extends AbstractController
|
||||
private function shouldFallbackToInquiryMode(BookingResponse $response): bool
|
||||
{
|
||||
// Check for "nicht möglich" status + inquiry suggestion in message
|
||||
if ('nicht möglich' !== $response->status) {
|
||||
if (BookingResponse::BOOKING_IMPOSSIBLE !== $response->status) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,10 +7,11 @@ namespace App\Controller\Booking\Create;
|
||||
use App\BusProNet\ApiClient;
|
||||
use App\BusProNet\Exception\TimeoutException;
|
||||
use App\BusProNet\Model\Notification;
|
||||
use App\Controller\Booking\Traits\BookingCreateTrait;
|
||||
use App\Controller\Booking\Traits\BookingExceptionHandlerTrait;
|
||||
use App\Entity\User;
|
||||
use App\Exception\BookingSessionNotFoundException;
|
||||
use App\Exception\HotelNotInTravelException;
|
||||
use App\Exception\NewsletterProviderException;
|
||||
use App\Exception\TravelNotFoundException;
|
||||
use App\Form\BookingCreateStep4Type;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Htmx\HxTrait;
|
||||
@@ -20,7 +21,6 @@ use App\Service\BookingSessionManager;
|
||||
use App\Service\MailjetApiClient;
|
||||
use App\Service\NewsletterManager;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
@@ -30,10 +30,8 @@ use Symfony\Contracts\Cache\CacheInterface;
|
||||
/**
|
||||
* Handles the fourth step of the booking creation process (confirmation).
|
||||
*/
|
||||
class Step4Controller extends AbstractController
|
||||
class Step4Controller extends AbstractBookingCreateController
|
||||
{
|
||||
use BookingCreateTrait;
|
||||
use BookingExceptionHandlerTrait;
|
||||
use HxTrait;
|
||||
|
||||
public function __construct(
|
||||
@@ -54,14 +52,14 @@ class Step4Controller extends AbstractController
|
||||
#[Route('/bookings/create/confirmation', name: 'app_booking_create_step_4')]
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
$result = $this->getOrCreateBookingCreateDto($this->bookingSessionService, $request);
|
||||
if ($result instanceof Response) {
|
||||
return $result;
|
||||
try {
|
||||
$bookingCreateDto = $this->loadBookingCreateDto($this->bookingSessionService, $request);
|
||||
} catch (BookingSessionNotFoundException|TravelNotFoundException|HotelNotInTravelException $exception) {
|
||||
return $this->createBookingCreateFailureResponse($exception, false);
|
||||
}
|
||||
$bookingCreateDto = $result;
|
||||
|
||||
// Validate step access
|
||||
if ($redirect = $this->validateStepAccess($bookingCreateDto, 4)) {
|
||||
if (null !== $redirect = $this->validateStepAccess($bookingCreateDto, 4)) {
|
||||
return $redirect;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,11 +5,11 @@ declare(strict_types=1);
|
||||
namespace App\Controller\Booking\Edit;
|
||||
|
||||
use App\BusProNet\Model\Notification;
|
||||
use App\Controller\Booking\Traits\BookingExceptionHandlerTrait;
|
||||
use App\Entity\User;
|
||||
use App\Exception\TravelNotFoundException;
|
||||
use App\Form\BookingEditType;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Model\BookingEditSubmissionResult;
|
||||
use App\Htmx\HxTrait;
|
||||
use App\Service\BookingChangeTracker;
|
||||
use App\Service\BookingEditContextFactory;
|
||||
@@ -34,7 +34,6 @@ use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
*/
|
||||
class IndexController extends AbstractController
|
||||
{
|
||||
use BookingExceptionHandlerTrait;
|
||||
use HxTrait;
|
||||
|
||||
public function __construct(
|
||||
@@ -43,7 +42,7 @@ class IndexController extends AbstractController
|
||||
private readonly BookingSessionManager $bookingSessionService,
|
||||
private readonly BookingChangeTracker $fingerprintService,
|
||||
private readonly BookingEditContextFactory $editContextFactory,
|
||||
private readonly BookingEditSubmitter $submitService,
|
||||
private readonly BookingEditSubmitter $formSubmitter,
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -53,32 +52,32 @@ class IndexController extends AbstractController
|
||||
* Clears any existing session data and API cache to ensure fresh data
|
||||
* is loaded, then redirects to the main edit page.
|
||||
*/
|
||||
#[Route('/bookings/{id}/edit/start', name: 'app_booking_edit_start', requirements: ['id' => '\d+'])]
|
||||
#[Route('/bookings/{bookingId}/edit/start', name: 'app_booking_edit_start', requirements: ['bookingId' => '\d+'])]
|
||||
#[IsGranted('ROLE_USER')]
|
||||
public function start(int $id, Request $request): Response
|
||||
public function start(int $bookingId, Request $request): Response
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = $this->getUser();
|
||||
|
||||
$this->bookingSessionService->clearBookingDto($request, BookingDto::MODE_EDIT);
|
||||
$this->dataLoader->invalidateBookingCache($id, $user);
|
||||
$this->dataLoader->invalidateBookingCache($bookingId, $user);
|
||||
|
||||
return $this->redirectToRoute('app_booking_edit', ['id' => $id]);
|
||||
return $this->redirectToRoute('app_booking_edit', ['bookingId' => $bookingId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display participant cards overview.
|
||||
*/
|
||||
#[Route('/bookings/{id}/edit', name: 'app_booking_edit', requirements: ['id' => '\d+'])]
|
||||
#[Route('/bookings/{bookingId}/edit', name: 'app_booking_edit', requirements: ['bookingId' => '\d+'])]
|
||||
#[IsGranted('ROLE_USER')]
|
||||
public function index(int $id, Request $request): Response
|
||||
public function index(int $bookingId, Request $request): Response
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = $this->getUser();
|
||||
|
||||
// Load form data from session (or API on first load)
|
||||
try {
|
||||
$bookingDto = $this->dataLoader->loadFormData($request, $id, $user);
|
||||
$bookingDto = $this->dataLoader->loadFormData($request, $bookingId, $user);
|
||||
} catch (TravelNotFoundException) {
|
||||
$this->addFlash('error', 'Reisedaten sind nicht (mehr) verfügbar');
|
||||
|
||||
@@ -92,12 +91,12 @@ class IndexController extends AbstractController
|
||||
}
|
||||
|
||||
// Show flash message if draft was restored
|
||||
if (true === $this->dataLoader->wasDraftRestored()) {
|
||||
if (true === $this->dataLoader->isDraftRestored()) {
|
||||
$this->addFlash('info', 'Dein zuvor gespeicherter Entwurf wurde wiederhergestellt.');
|
||||
}
|
||||
|
||||
// Fetch booking data for display (surcharges, canceled status, etc.)
|
||||
$bookingData = $this->dataLoader->fetchBookingData($id, $user);
|
||||
$bookingData = $this->dataLoader->fetchBookingData($bookingId, $user);
|
||||
if (null === $bookingData || $bookingData instanceof Notification) {
|
||||
$this->addFlash('error', 'Buchungsdaten nicht (mehr) verfügbar');
|
||||
|
||||
@@ -110,7 +109,10 @@ class IndexController extends AbstractController
|
||||
|
||||
// Handle form submission (clicking "Buchung aktualisieren")
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
return $this->submitService->handleSubmission($request, $bookingDto, $id, $user);
|
||||
$submissionResult = $this->formSubmitter->handleSubmission($request, $bookingDto, $bookingId, $user);
|
||||
$this->applySubmissionResultFlashes($submissionResult);
|
||||
|
||||
return $this->redirectToRoute('app_booking_edit', ['bookingId' => $bookingId]);
|
||||
}
|
||||
|
||||
$bookingEditContext = $this->editContextFactory->createOverviewContext(
|
||||
@@ -129,6 +131,39 @@ class IndexController extends AbstractController
|
||||
return $this->render('booking/edit/index.html.twig', $templateData);
|
||||
}
|
||||
|
||||
private function applySubmissionResultFlashes(BookingEditSubmissionResult $result): void
|
||||
{
|
||||
if (true === $result->immutableChangesReverted) {
|
||||
$this->addFlash('info', 'Einige Änderungen wurden verworfen, da sie aktuell nicht mehr änderbar sind.');
|
||||
}
|
||||
|
||||
switch ($result->status) {
|
||||
case BookingEditSubmissionResult::STATUS_BOOKING_DATA_RELOAD_FAILED:
|
||||
$this->addFlash('error', 'Buchungsdaten konnten vor dem Speichern nicht neu geladen werden');
|
||||
break;
|
||||
case BookingEditSubmissionResult::STATUS_NOTIFICATION_ERROR:
|
||||
$this->addFlash('error', $result->message ?? 'Es ist ein Fehler in der Kommunikation mit dem Buchungssystem aufgetreten');
|
||||
break;
|
||||
case BookingEditSubmissionResult::STATUS_NOTIFICATION_INFO:
|
||||
if (null !== $result->message && '' !== trim($result->message)) {
|
||||
$this->addFlash('info', $result->message);
|
||||
}
|
||||
break;
|
||||
case BookingEditSubmissionResult::STATUS_SUCCESS:
|
||||
$this->addFlash('success', 'Buchung erfolgreich aktualisiert');
|
||||
break;
|
||||
case BookingEditSubmissionResult::STATUS_UNSUCCESSFUL:
|
||||
$this->addFlash('error', $result->message ?? 'Buchung konnte nicht aktualisiert werden');
|
||||
break;
|
||||
case BookingEditSubmissionResult::STATUS_TIMEOUT:
|
||||
$this->addFlash('error', 'Die Anfrage hat zu lange gedauert. Bitte versuche es erneut.');
|
||||
break;
|
||||
case BookingEditSubmissionResult::STATUS_API_CLIENT_ERROR:
|
||||
$this->addFlash('error', 'Es ist ein Fehler in der Kommunikation mit dem Buchungssystem aufgetreten');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads booking data from API, discarding all session changes.
|
||||
*
|
||||
@@ -136,12 +171,12 @@ class IndexController extends AbstractController
|
||||
* POST: Clears session and redirects to reload the booking
|
||||
*/
|
||||
#[Route(
|
||||
path: '/bookings/{id}/edit/reload',
|
||||
path: '/bookings/{bookingId}/edit/reload',
|
||||
name: 'app_booking_edit_reload',
|
||||
requirements: ['id' => '\d+']
|
||||
requirements: ['bookingId' => '\d+']
|
||||
)]
|
||||
#[IsGranted('ROLE_USER')]
|
||||
public function reloadFromApi(int $id, Request $request): Response
|
||||
public function reloadFromApi(int $bookingId, Request $request): Response
|
||||
{
|
||||
if (Request::METHOD_POST === $request->getMethod()) {
|
||||
// Clear session to discard all changes
|
||||
@@ -150,15 +185,15 @@ class IndexController extends AbstractController
|
||||
// Delete draft since user explicitly chose to discard changes
|
||||
/** @var User $user */
|
||||
$user = $this->getUser();
|
||||
$this->draftService->deleteDraft($user, $id);
|
||||
$this->draftService->deleteDraft($user, $bookingId);
|
||||
|
||||
$this->addFlash('success', 'Änderungen verworfen, Daten neu geladen');
|
||||
|
||||
return $this->redirectToRoute('app_booking_edit', ['id' => $id]);
|
||||
return $this->redirectToRoute('app_booking_edit', ['bookingId' => $bookingId]);
|
||||
}
|
||||
|
||||
return $this->render('booking/edit/modal_reload.html.twig', [
|
||||
'bookingId' => $id,
|
||||
'bookingId' => $bookingId,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -169,12 +204,12 @@ class IndexController extends AbstractController
|
||||
* Shows a flash message informing about the saved draft.
|
||||
*/
|
||||
#[Route(
|
||||
path: '/bookings/{id}/edit/cancel',
|
||||
path: '/bookings/{bookingId}/edit/cancel',
|
||||
name: 'app_booking_edit_cancel',
|
||||
requirements: ['id' => '\d+']
|
||||
requirements: ['bookingId' => '\d+']
|
||||
)]
|
||||
#[IsGranted('ROLE_USER')]
|
||||
public function cancelEdit(int $id, Request $request): Response
|
||||
public function cancelEdit(int $bookingId, Request $request): Response
|
||||
{
|
||||
// Clear session state
|
||||
$this->bookingSessionService->clearBookingDto($request, BookingDto::MODE_EDIT);
|
||||
@@ -182,7 +217,7 @@ class IndexController extends AbstractController
|
||||
// Check if a draft exists to show appropriate message
|
||||
/** @var User $user */
|
||||
$user = $this->getUser();
|
||||
$draft = $this->draftService->findDraft($user, $id);
|
||||
$draft = $this->draftService->findDraft($user, $bookingId);
|
||||
|
||||
if (null !== $draft) {
|
||||
$this->addFlash('info', 'Deine Änderungen wurden als Entwurf gespeichert.');
|
||||
|
||||
@@ -41,12 +41,12 @@ class ParticipantController extends AbstractController
|
||||
* Edit single participant form.
|
||||
*/
|
||||
#[Route(
|
||||
path: '/bookings/{id}/edit/participants/{index}',
|
||||
path: '/bookings/{bookingId}/edit/participants/{index}',
|
||||
name: 'app_booking_edit_participant',
|
||||
requirements: ['id' => '\d+', 'index' => '\d+']
|
||||
requirements: ['bookingId' => '\d+', 'index' => '\d+']
|
||||
)]
|
||||
#[IsGranted('ROLE_USER')]
|
||||
public function editParticipant(int $id, int $index, Request $request): Response
|
||||
public function editParticipant(int $bookingId, int $index, Request $request): Response
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = $this->getUser();
|
||||
@@ -56,12 +56,12 @@ class ParticipantController extends AbstractController
|
||||
if (null === $bookingDto) {
|
||||
$this->addFlash('error', 'Sitzung abgelaufen. Bitte neu laden.');
|
||||
|
||||
return $this->redirectToRoute('app_booking_edit', ['id' => $id]);
|
||||
return $this->redirectToRoute('app_booking_edit', ['bookingId' => $bookingId]);
|
||||
}
|
||||
|
||||
$this->participantFormSupportService->ensureParticipantExists($bookingDto, $index);
|
||||
|
||||
$bookingData = $this->dataLoader->fetchBookingData($id, $user);
|
||||
$bookingData = $this->dataLoader->fetchBookingData($bookingId, $user);
|
||||
if (null === $bookingData || $bookingData instanceof Notification) {
|
||||
$this->addFlash('error', 'Buchungsdaten nicht (mehr) verfügbar');
|
||||
|
||||
@@ -71,7 +71,7 @@ class ParticipantController extends AbstractController
|
||||
if ($this->isParticipantCanceled($bookingData, $index)) {
|
||||
$this->addFlash('warning', 'Stornierte Teilnehmer können nicht bearbeitet werden');
|
||||
|
||||
return $this->redirectToRoute('app_booking_edit', ['id' => $id]);
|
||||
return $this->redirectToRoute('app_booking_edit', ['bookingId' => $bookingId]);
|
||||
}
|
||||
|
||||
$this->editContextFactory->prepareBookingDto($bookingDto);
|
||||
@@ -90,10 +90,10 @@ class ParticipantController extends AbstractController
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$this->bookingSessionService->saveBookingDto($request, $bookingDto, BookingDto::MODE_EDIT);
|
||||
$this->draftService->saveDraft($user, $id, $bookingDto);
|
||||
$this->draftService->saveDraft($user, $bookingId, $bookingDto);
|
||||
$this->addNotificationsAsFlashMessages($notifications);
|
||||
|
||||
return $this->redirectToRoute('app_booking_edit', ['id' => $id]);
|
||||
return $this->redirectToRoute('app_booking_edit', ['bookingId' => $bookingId]);
|
||||
}
|
||||
|
||||
$context = $this->editContextFactory->createParticipantContext($bookingDto, $bookingData);
|
||||
@@ -106,9 +106,9 @@ class ParticipantController extends AbstractController
|
||||
'summaryData' => $context->summaryData,
|
||||
'mutableData' => $context->mutableData,
|
||||
'refreshRouteName' => 'app_booking_edit_participant_refresh',
|
||||
'refreshRouteParams' => ['id' => $id, 'index' => $index],
|
||||
'refreshRouteParams' => ['bookingId' => $bookingId, 'index' => $index],
|
||||
'cancelRouteName' => 'app_booking_edit',
|
||||
'cancelRouteParams' => ['id' => $id],
|
||||
'cancelRouteParams' => ['bookingId' => $bookingId],
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -116,13 +116,13 @@ class ParticipantController extends AbstractController
|
||||
* HTMX endpoint for refreshing participant form without validation.
|
||||
*/
|
||||
#[Route(
|
||||
path: '/bookings/{id}/edit/participants/{index}/refresh',
|
||||
path: '/bookings/{bookingId}/edit/participants/{index}/refresh',
|
||||
name: 'app_booking_edit_participant_refresh',
|
||||
requirements: ['id' => '\d+', 'index' => '\d+'],
|
||||
requirements: ['bookingId' => '\d+', 'index' => '\d+'],
|
||||
methods: ['POST']
|
||||
)]
|
||||
#[IsGranted('ROLE_USER')]
|
||||
public function refreshParticipantForm(int $id, int $index, Request $request): Response
|
||||
public function refreshParticipantForm(int $bookingId, int $index, Request $request): Response
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = $this->getUser();
|
||||
@@ -136,7 +136,7 @@ class ParticipantController extends AbstractController
|
||||
$this->participantFormSupportService->ensureParticipantExists($bookingDto, $index);
|
||||
$this->editContextFactory->prepareBookingDto($bookingDto);
|
||||
|
||||
$bookingData = $this->dataLoader->fetchBookingData($id, $user);
|
||||
$bookingData = $this->dataLoader->fetchBookingData($bookingId, $user);
|
||||
|
||||
$wrapper = $this->participantFormSupportService->createParticipantEditDto($bookingDto, $index);
|
||||
|
||||
@@ -165,9 +165,9 @@ class ParticipantController extends AbstractController
|
||||
'summaryData' => $context->summaryData,
|
||||
'mutableData' => $context->mutableData,
|
||||
'refreshRouteName' => 'app_booking_edit_participant_refresh',
|
||||
'refreshRouteParams' => ['id' => $id, 'index' => $index],
|
||||
'refreshRouteParams' => ['bookingId' => $bookingId, 'index' => $index],
|
||||
'cancelRouteName' => 'app_booking_edit',
|
||||
'cancelRouteParams' => ['id' => $id],
|
||||
'cancelRouteParams' => ['bookingId' => $bookingId],
|
||||
]
|
||||
);
|
||||
|
||||
@@ -192,6 +192,6 @@ class ParticipantController extends AbstractController
|
||||
|
||||
private function isParticipantCanceled(Booking $bookingData, int $index): bool
|
||||
{
|
||||
return 'S' === ($bookingData->participantsStatus[$index] ?? null);
|
||||
return Booking::STATUS_CANCELED === ($bookingData->participantsStatus[$index] ?? null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controller\Booking\Traits;
|
||||
|
||||
use App\Form\Model\BookingDto;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
|
||||
/**
|
||||
* Provides common functionality for booking creation controllers.
|
||||
*
|
||||
* This trait contains shared validation and redirect logic used across
|
||||
* all booking creation steps to ensure consistent behavior and reduce
|
||||
* code duplication.
|
||||
*/
|
||||
trait BookingCreateTrait
|
||||
{
|
||||
/**
|
||||
* Validates step access and returns redirect response if necessary.
|
||||
*
|
||||
* @return RedirectResponse|null Returns redirect response if validation fails, null if access is allowed
|
||||
*/
|
||||
private function validateStepAccess(BookingDto $bookingCreateDto, int $expectedStep): ?RedirectResponse
|
||||
{
|
||||
// Allow access to current step or any previous step
|
||||
if ($expectedStep > $bookingCreateDto->currentStep) {
|
||||
$this->addFlash('error', 'Bitte erst die vorherigen Schritte abschließen.');
|
||||
|
||||
return $this->redirectToCurrentStep($bookingCreateDto);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirects to the current step based on the DTO's currentStep.
|
||||
*/
|
||||
private function redirectToCurrentStep(BookingDto $bookingCreateDto): RedirectResponse
|
||||
{
|
||||
$route = match ($bookingCreateDto->currentStep) {
|
||||
2 => 'app_booking_create_step_2',
|
||||
3 => 'app_booking_create_step_3',
|
||||
4 => 'app_booking_create_step_4',
|
||||
default => 'app_booking_create_step_1',
|
||||
};
|
||||
|
||||
return $this->redirectToRoute($route);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles API errors by logging and adding a flash message.
|
||||
*/
|
||||
/**
|
||||
* @param array<string, mixed> $context
|
||||
*/
|
||||
private function handleApiError(
|
||||
LoggerInterface $logger,
|
||||
string $logMessage,
|
||||
array $context,
|
||||
string $flashMessage,
|
||||
): void {
|
||||
$logger->error($logMessage, $context);
|
||||
$this->addFlash('error', $flashMessage);
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controller\Booking\Traits;
|
||||
|
||||
use App\Exception\BookingSessionNotFoundException;
|
||||
use App\Exception\HotelNotInTravelException;
|
||||
use App\Exception\TravelNotFoundException;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Service\BookingSessionManager;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Provides centralized exception handling for booking controllers.
|
||||
*
|
||||
* This trait contains common exception handling logic for booking operations
|
||||
* including user-friendly error messages and appropriate redirects.
|
||||
*/
|
||||
trait BookingExceptionHandlerTrait
|
||||
{
|
||||
/**
|
||||
* Safely retrieves booking DTO with centralized exception handling.
|
||||
*
|
||||
* Handles all booking-related exceptions and provides appropriate user feedback
|
||||
* by redirecting to the error page with flash messages.
|
||||
*/
|
||||
protected function getOrCreateBookingCreateDto(BookingSessionManager $bookingSessionService, Request $request): BookingDto|RedirectResponse
|
||||
{
|
||||
try {
|
||||
return $bookingSessionService->getOrCreateBookingCreateDto($request);
|
||||
} catch (BookingSessionNotFoundException|TravelNotFoundException|HotelNotInTravelException $e) {
|
||||
$this->addFlash('error', 'Deine Buchungssitzung ist abgelaufen. Bitte starte eine neue Buchung.');
|
||||
|
||||
return $this->redirectToRoute('app_booking_create_error');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely retrieves booking DTO for HTMX requests with lightweight error responses.
|
||||
*
|
||||
* Returns empty 400 responses for HTMX requests when exceptions occur,
|
||||
* allowing the frontend to handle errors appropriately.
|
||||
*/
|
||||
protected function getOrCreateBookingCreateDtoForHtmx(BookingSessionManager $bookingSessionService, Request $request): mixed
|
||||
{
|
||||
try {
|
||||
return $bookingSessionService->getOrCreateBookingCreateDto($request);
|
||||
} catch (BookingSessionNotFoundException|TravelNotFoundException|HotelNotInTravelException $e) {
|
||||
return new Response('', 400);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Model;
|
||||
|
||||
final readonly class BookingEditSubmissionResult
|
||||
{
|
||||
public const string STATUS_BOOKING_DATA_RELOAD_FAILED = 'booking_data_reload_failed';
|
||||
public const string STATUS_NOTIFICATION_ERROR = 'notification_error';
|
||||
public const string STATUS_NOTIFICATION_INFO = 'notification_info';
|
||||
public const string STATUS_SUCCESS = 'success';
|
||||
public const string STATUS_UNSUCCESSFUL = 'unsuccessful';
|
||||
public const string STATUS_TIMEOUT = 'timeout';
|
||||
public const string STATUS_API_CLIENT_ERROR = 'api_client_error';
|
||||
|
||||
public function __construct(
|
||||
public string $status,
|
||||
public ?string $message = null,
|
||||
public bool $immutableChangesReverted = false,
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@ class BookingEditDataLoader
|
||||
{
|
||||
public const string CACHE_TAG_USER_PREFIX = 'user_bookings_';
|
||||
|
||||
private bool $draftWasRestored = false;
|
||||
private bool $draftRestored = false;
|
||||
|
||||
public function __construct(
|
||||
private readonly ApiClient $apiClient,
|
||||
@@ -48,9 +48,9 @@ class BookingEditDataLoader
|
||||
* This flag is reset on each call to loadFormData() and can be used
|
||||
* by the controller to show a flash message to the user.
|
||||
*/
|
||||
public function wasDraftRestored(): bool
|
||||
public function isDraftRestored(): bool
|
||||
{
|
||||
return $this->draftWasRestored;
|
||||
return $this->draftRestored;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,7 +71,7 @@ class BookingEditDataLoader
|
||||
*/
|
||||
public function loadFormData(Request $request, int $bookingId, User $user): ?BookingDto
|
||||
{
|
||||
$this->draftWasRestored = false;
|
||||
$this->draftRestored = false;
|
||||
|
||||
$formData = $this->bookingSessionService->getBookingDto($request, BookingDto::MODE_EDIT);
|
||||
|
||||
@@ -154,7 +154,7 @@ class BookingEditDataLoader
|
||||
if (null !== $draft) {
|
||||
$applied = $this->draftService->applyDraftToDto($draft, $formData, $travelData);
|
||||
if (true === $applied) {
|
||||
$this->draftWasRestored = true;
|
||||
$this->draftRestored = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,12 +9,10 @@ use App\BusProNet\Exception\ApiClientException;
|
||||
use App\BusProNet\Exception\TimeoutException;
|
||||
use App\BusProNet\Model\Notification;
|
||||
use App\Entity\User;
|
||||
use App\Model\BookingEditSubmissionResult;
|
||||
use App\Form\Model\BookingDto;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Session\FlashBagAwareSessionInterface;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
|
||||
/**
|
||||
* Handles the final booking edit submission workflow.
|
||||
@@ -28,12 +26,11 @@ class BookingEditSubmitter
|
||||
private readonly TravelDataProvider $travelDataService,
|
||||
private readonly BookingEditSubmitGuard $submitGuard,
|
||||
private readonly BookingSessionManager $bookingSessionService,
|
||||
private readonly UrlGeneratorInterface $urlGenerator,
|
||||
private readonly LoggerInterface $logger,
|
||||
) {
|
||||
}
|
||||
|
||||
public function handleSubmission(Request $request, BookingDto $bookingDto, int $bookingId, User $user): RedirectResponse
|
||||
public function handleSubmission(Request $request, BookingDto $bookingDto, int $bookingId, User $user): BookingEditSubmissionResult
|
||||
{
|
||||
$email = $user->getEmail();
|
||||
|
||||
@@ -45,14 +42,15 @@ class BookingEditSubmitter
|
||||
$this->dataLoader->invalidateBookingCache($bookingId, $user);
|
||||
$freshBookingData = $this->dataLoader->fetchBookingData($bookingId, $user);
|
||||
if (null === $freshBookingData || $freshBookingData instanceof Notification) {
|
||||
$this->addFlash($request, 'error', 'Buchungsdaten konnten vor dem Speichern nicht neu geladen werden');
|
||||
$this->logger->warning('Failed to refresh booking before update submission', [
|
||||
'email' => $email,
|
||||
'booking_id' => $bookingId,
|
||||
'has_notification' => $freshBookingData instanceof Notification,
|
||||
]);
|
||||
|
||||
return $this->redirectToEdit($bookingId);
|
||||
return new BookingEditSubmissionResult(
|
||||
BookingEditSubmissionResult::STATUS_BOOKING_DATA_RELOAD_FAILED,
|
||||
);
|
||||
}
|
||||
|
||||
$bookingDto->booking = $freshBookingData;
|
||||
@@ -68,66 +66,69 @@ class BookingEditSubmitter
|
||||
$immutableChangesReverted = $this->submitGuard->reconcileImmutableCategories($bookingDto, $freshBookingData);
|
||||
if (true === $immutableChangesReverted) {
|
||||
$this->bookingSessionService->saveBookingDto($request, $bookingDto, BookingDto::MODE_EDIT);
|
||||
$this->addFlash($request, 'info', 'Einige Änderungen wurden verworfen, da sie aktuell nicht mehr änderbar sind.');
|
||||
}
|
||||
|
||||
try {
|
||||
$response = $this->apiClient->updateBooking($bookingDto, true);
|
||||
if ($response instanceof Notification) {
|
||||
if (true === $response->isError()) {
|
||||
$this->addFlash($request, 'error', $response->message);
|
||||
} else {
|
||||
$this->addFlash($request, 'info', $response->message);
|
||||
}
|
||||
$this->logger->error('Booking update not successful', [
|
||||
'email' => $email,
|
||||
'booking_id' => $bookingId,
|
||||
'message' => $response->message,
|
||||
]);
|
||||
} elseif (true === $response->success) {
|
||||
return new BookingEditSubmissionResult(
|
||||
true === $response->isError()
|
||||
? BookingEditSubmissionResult::STATUS_NOTIFICATION_ERROR
|
||||
: BookingEditSubmissionResult::STATUS_NOTIFICATION_INFO,
|
||||
$response->message,
|
||||
$immutableChangesReverted,
|
||||
);
|
||||
}
|
||||
|
||||
if (true === $response->success) {
|
||||
$this->dataLoader->invalidateBookingCache($bookingId, $user);
|
||||
$this->bookingSessionService->clearBookingDto($request, BookingDto::MODE_EDIT);
|
||||
$this->draftService->deleteDraft($user, $bookingId);
|
||||
|
||||
$this->addFlash($request, 'success', 'Buchung erfolgreich aktualisiert');
|
||||
$this->logger->info('Booking update successful', [
|
||||
'email' => $email,
|
||||
'booking_id' => $bookingId,
|
||||
]);
|
||||
|
||||
return $this->redirectToEdit($bookingId);
|
||||
} else {
|
||||
$this->addFlash($request, 'error', $response->status ?? 'Buchung konnte nicht aktualisiert werden');
|
||||
$this->logger->warning('Booking update returned unsuccessful status', [
|
||||
'email' => $email,
|
||||
'booking_id' => $bookingId,
|
||||
'status' => $response->status,
|
||||
'valid' => $response->valid,
|
||||
]);
|
||||
return new BookingEditSubmissionResult(
|
||||
BookingEditSubmissionResult::STATUS_SUCCESS,
|
||||
null,
|
||||
$immutableChangesReverted,
|
||||
);
|
||||
}
|
||||
|
||||
$this->logger->warning('Booking update returned unsuccessful status', [
|
||||
'email' => $email,
|
||||
'booking_id' => $bookingId,
|
||||
'status' => $response->status,
|
||||
'valid' => $response->valid,
|
||||
]);
|
||||
|
||||
return new BookingEditSubmissionResult(
|
||||
BookingEditSubmissionResult::STATUS_UNSUCCESSFUL,
|
||||
$response->status ?? 'Buchung konnte nicht aktualisiert werden',
|
||||
$immutableChangesReverted,
|
||||
);
|
||||
} catch (TimeoutException) {
|
||||
$this->addFlash($request, 'error', 'Die Anfrage hat zu lange gedauert. Bitte versuchen Sie es erneut.');
|
||||
$this->logger->error('Booking update timeout', [
|
||||
'email' => $email,
|
||||
'booking_id' => $bookingId,
|
||||
]);
|
||||
return new BookingEditSubmissionResult(
|
||||
BookingEditSubmissionResult::STATUS_TIMEOUT,
|
||||
null,
|
||||
$immutableChangesReverted,
|
||||
);
|
||||
} catch (ApiClientException) {
|
||||
$this->addFlash($request, 'error', 'Es ist ein Fehler in der Kommunikation mit dem Buchungssystem aufgetreten');
|
||||
return new BookingEditSubmissionResult(
|
||||
BookingEditSubmissionResult::STATUS_API_CLIENT_ERROR,
|
||||
null,
|
||||
$immutableChangesReverted,
|
||||
);
|
||||
}
|
||||
|
||||
return $this->redirectToEdit($bookingId);
|
||||
}
|
||||
|
||||
private function addFlash(Request $request, string $type, string $message): void
|
||||
{
|
||||
$session = $request->getSession();
|
||||
if ($session instanceof FlashBagAwareSessionInterface) {
|
||||
$session->getFlashBag()->add($type, $message);
|
||||
}
|
||||
}
|
||||
|
||||
private function redirectToEdit(int $bookingId): RedirectResponse
|
||||
{
|
||||
return new RedirectResponse($this->urlGenerator->generate('app_booking_edit', ['id' => $bookingId]));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user