fix: enforce submit-time immutability, scope booking cache per user

This commit is contained in:
Björn Fromme
2026-03-16 11:20:05 +01:00
parent d05524222b
commit 03cb74370b
6 changed files with 342 additions and 282 deletions
@@ -21,6 +21,7 @@ use App\Service\BookingEditDataLoaderService;
use App\Service\BookingEditDraftService;
use App\Service\BookingFingerprintService;
use App\Service\BookingService;
use App\Service\BookingEditSubmitGuardService;
use App\Service\BookingSummaryDataService;
use App\Service\ParticipantCardDataService;
use App\Service\TravelDataService;
@@ -52,6 +53,7 @@ class IndexController extends AbstractController
private readonly BookingEditDraftService $draftService,
private readonly TravelDataService $travelDataService,
private readonly BookingService $bookingService,
private readonly BookingEditSubmitGuardService $submitGuard,
private readonly BookingFingerprintService $fingerprintService,
private readonly BookingSummaryDataService $summaryDataService,
private readonly ParticipantCardDataService $participantCardService,
@@ -69,8 +71,11 @@ class IndexController extends AbstractController
#[IsGranted('ROLE_USER')]
public function start(int $id, Request $request): Response
{
/** @var User $user */
$user = $this->getUser();
$this->bookingService->clearBookingDto($request, BookingDto::MODE_EDIT);
$this->dataLoader->invalidateBookingCache($id);
$this->dataLoader->invalidateBookingCache($id, $user);
return $this->redirectToRoute('app_booking_edit', ['id' => $id]);
}
@@ -103,10 +108,6 @@ class IndexController extends AbstractController
// Show flash message if draft was restored
if (true === $this->dataLoader->wasDraftRestored()) {
$this->addFlash('info', 'Dein zuvor gespeicherter Entwurf wurde wiederhergestellt.');
if (true === $this->dataLoader->draftHadImmutableSkips()) {
$this->addFlash('info', 'Einige Entwurfsänderungen wurden nicht übernommen, da diese aktuell nicht mehr änderbar sind.');
}
}
// Fetch booking data for display (surcharges, canceled status, etc.)
@@ -412,6 +413,35 @@ class IndexController extends AbstractController
'booking_id' => $id,
]);
$this->dataLoader->invalidateBookingCache($id, $user);
$freshBookingData = $this->dataLoader->fetchBookingData($id, $user);
if (null === $freshBookingData || $freshBookingData instanceof Notification) {
$this->addFlash('error', 'Buchungsdaten konnten vor dem Speichern nicht neu geladen werden');
$this->logger->warning('Failed to refresh booking before update submission', [
'email' => $email,
'booking_id' => $id,
'has_notification' => $freshBookingData instanceof Notification,
]);
return $this->redirectToRoute('app_booking_edit', ['id' => $id]);
}
$bookingDto->booking = $freshBookingData;
$mutableData = $this->travelDataService->getMutabilityData(
$freshBookingData->dateId,
forceRefresh: true
);
if (null !== $mutableData) {
$this->travelDataService->patchMutability($bookingDto->travel, $mutableData);
}
$immutableChangesReverted = $this->submitGuard->reconcileImmutableCategories($bookingDto, $freshBookingData);
if (true === $immutableChangesReverted) {
$this->bookingService->saveBookingDto($request, $bookingDto, BookingDto::MODE_EDIT);
$this->addFlash('info', 'Einige Änderungen wurden verworfen, da sie aktuell nicht mehr änderbar sind.');
}
try {
$response = $this->apiClient->updateBooking($bookingDto, true);
if ($response instanceof Notification) {
@@ -427,7 +457,7 @@ class IndexController extends AbstractController
]);
} elseif (true === $response->success) {
// Invalidate cache and clear session on success
$this->dataLoader->invalidateBookingCache($id);
$this->dataLoader->invalidateBookingCache($id, $user);
$this->bookingService->clearBookingDto($request, BookingDto::MODE_EDIT);
// Delete draft on successful submission