feat: additional confirmation cycle and status for groups inquiries

This commit is contained in:
Björn Fromme
2026-08-18 12:48:17 +02:00
parent 5045846eed
commit 8dee65d0dc
28 changed files with 677 additions and 95 deletions
@@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
namespace App\Controller\Admin\AccommodationBooking;
use App\Entity\Groups\AccommodationBooking;
use App\Htmx\HxRedirectResponse;
use App\Service\AccommodationBookingService;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[IsGranted('ROLE_GROUPS_MANAGER')]
class ConfirmController extends AbstractController
{
public function __construct(
private readonly AccommodationBookingService $bookingService,
private readonly LoggerInterface $logger,
) {
}
#[Route('/admin/accommodation-booking/{id}/confirm', name: 'app_admin_accommodationbooking_confirm')]
public function index(AccommodationBooking $booking, Request $request): Response
{
// Only a booking awaiting validation can be confirmed — everything else has either
// not been committed to by the customer yet or is already dealt with.
if (!$booking->isReceived()) {
return $this->redirectToRoute('app_admin_accommodationbooking_show', ['id' => $booking->getId()]);
}
if ($request->isMethod(Request::METHOD_POST)) {
if (!$this->isCsrfTokenValid('confirm_accommodation_booking_'.$booking->getId(), $request->request->getString('_token'))) {
throw $this->createAccessDeniedException('Invalid CSRF token.');
}
// Confirming is what makes the booking binding for the customer, so it must not
// happen when the confirmation itself cannot be delivered.
if (null === $booking->getEmail() || '' === trim($booking->getEmail())) {
$this->addFlash('error', 'Für diese Buchung ist keine E-Mail-Adresse hinterlegt. Bitte ergänze sie, um die Buchung zu bestätigen.');
return new HxRedirectResponse($this->generateUrl('app_admin_accommodationbooking_edit', ['id' => $booking->getId()]));
}
$this->bookingService->confirmBooking($booking);
$this->addFlash('success', 'Die Buchung wurde bestätigt und dem Kunden per E-Mail zugestellt.');
$this->logger->info('Confirmed accommodation booking', [
'id' => $booking->getId(),
]);
return new HxRedirectResponse($this->generateUrl('app_admin_accommodationbooking_show', ['id' => $booking->getId()]));
}
return $this->render('admin/accommodation_booking/modal_confirm.html.twig', [
'booking' => $booking,
'csrf_token_id' => 'confirm_accommodation_booking_'.$booking->getId(),
]);
}
}
@@ -128,9 +128,11 @@ class EditController extends AbstractController
$this->entityManager->persist($booking);
$this->entityManager->flush();
// Entering Offen or Bestätigt is what the customer needs to hear about: the offer
// becomes viewable, or the booking is confirmed. Entwurf and Absage stay silent.
$notifiableStatuses = [AccommodationBookingStatus::Open, AccommodationBookingStatus::Accepted];
// Entering Offen is what the customer needs to hear about: the offer becomes
// viewable. Every other status stays silent here — in particular Bestätigt, which
// is only ever reached through the explicit confirm action so that the binding
// confirmation is never sent as a side effect of editing.
$notifiableStatuses = [AccommodationBookingStatus::Open];
if ($previousStatus !== $booking->getStatus() && in_array($booking->getStatus(), $notifiableStatuses, true)) {
$this->bookingService->issueAccessLink($booking);
$this->bookingService->sendCustomerConfirmationEmail($booking);
+1 -1
View File
@@ -108,7 +108,7 @@ class OfferController extends AbstractController
// The textarea is prefilled, so the submitted value is the complete remark —
// an emptied field arrives as null and must clear the stored one, not keep it.
$this->bookingService->acceptBooking($booking, $dto->remarks ?? '');
$this->addFlash('success', 'Deine Buchung ist bestätigt.');
$this->addFlash('success', 'Deine Buchung ist bei uns eingegangen. Sobald sie geprüft ist, erhältst du eine Bestätigung per E-Mail.');
return $this->redirectToOfferPage($request, $uuid);
}