From 48db290625755a2a3e61058cd6f099057c0f2575 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Fromme?=
Date: Mon, 24 Aug 2026 09:19:16 +0200
Subject: [PATCH] feat: manually create customer access link for offer and
update status
addresses #869eea8d7
---
.../GenerateAccessLinkController.php | 56 +++++++
src/Service/AccommodationBookingService.php | 16 ++
.../modal_generate_access_link.html.twig | 13 ++
.../accommodation_booking/show.html.twig | 23 ++-
.../GenerateAccessLinkControllerTest.php | 157 ++++++++++++++++++
.../AccommodationBookingServiceTest.php | 65 ++++++++
6 files changed, 323 insertions(+), 7 deletions(-)
create mode 100644 src/Controller/Admin/AccommodationBooking/GenerateAccessLinkController.php
create mode 100644 templates/admin/accommodation_booking/modal_generate_access_link.html.twig
create mode 100644 tests/Controller/Admin/AccommodationBooking/GenerateAccessLinkControllerTest.php
diff --git a/src/Controller/Admin/AccommodationBooking/GenerateAccessLinkController.php b/src/Controller/Admin/AccommodationBooking/GenerateAccessLinkController.php
new file mode 100644
index 0000000..8576815
--- /dev/null
+++ b/src/Controller/Admin/AccommodationBooking/GenerateAccessLinkController.php
@@ -0,0 +1,56 @@
+isDraft() && !$booking->isRequested()) {
+ return $this->redirectToRoute('app_admin_accommodationbooking_show', ['id' => $booking->getId()]);
+ }
+
+ if ($request->isMethod(Request::METHOD_POST)) {
+ if (!$this->isCsrfTokenValid('generate_access_link_accommodation_booking_'.$booking->getId(), $request->request->getString('_token'))) {
+ throw $this->createAccessDeniedException('Invalid CSRF token.');
+ }
+
+ $this->bookingService->generateAccessLink($booking);
+
+ $this->addFlash('success', 'Der Zugangslink wurde erzeugt. Die Buchung ist jetzt offen und wartet auf die Annahme durch den Kunden.');
+
+ $this->logger->info('Manually generated accommodation booking access link', [
+ 'id' => $booking->getId(),
+ ]);
+
+ return new HxRedirectResponse($this->generateUrl('app_admin_accommodationbooking_show', ['id' => $booking->getId()]));
+ }
+
+ return $this->render('admin/accommodation_booking/modal_generate_access_link.html.twig', [
+ 'booking' => $booking,
+ 'csrf_token_id' => 'generate_access_link_accommodation_booking_'.$booking->getId(),
+ ]);
+ }
+}
diff --git a/src/Service/AccommodationBookingService.php b/src/Service/AccommodationBookingService.php
index efab090..69544e0 100644
--- a/src/Service/AccommodationBookingService.php
+++ b/src/Service/AccommodationBookingService.php
@@ -534,6 +534,22 @@ class AccommodationBookingService
$this->sendCustomerConfirmationEmail($booking);
}
+ /**
+ * Publishes the offer the same way sendOffer() does — same guard, same transition into
+ * Offen, same issued link — but for staff handing the link to the customer themselves
+ * instead of through the automated mail. No email side effect.
+ */
+ public function generateAccessLink(AccommodationBooking $booking): void
+ {
+ if (!$booking->isDraft() && !$booking->isRequested()) {
+ return;
+ }
+
+ $booking->setStatus(AccommodationBookingStatus::Open);
+ $this->issueAccessLink($booking);
+ $this->entityManager->flush();
+ }
+
/**
* Moves a draft to another Gruppenhaus, for the case where the wrong one was picked at
* creation. The chosen Verpflegung and Zusatzleistungen belong to the old house's catalog —
diff --git a/templates/admin/accommodation_booking/modal_generate_access_link.html.twig b/templates/admin/accommodation_booking/modal_generate_access_link.html.twig
new file mode 100644
index 0000000..3731a03
--- /dev/null
+++ b/templates/admin/accommodation_booking/modal_generate_access_link.html.twig
@@ -0,0 +1,13 @@
+{% extends 'htmx_confirmation_modal.html.twig' %}
+
+{% block title %}Zugangslink manuell erzeugen{% endblock %}
+
+{% block content %}
+
+ Möchtest du den Zugangslink für {{ booking.groupName }} jetzt erzeugen?
+ Die Buchung wechselt dadurch in den Status „Offen“ — es wird aber keine E-Mail an den
+ Kunden verschickt.
+
+{% endblock %}
+
+{% block button_confirm %}Link erzeugen{% endblock %}
diff --git a/templates/admin/accommodation_booking/show.html.twig b/templates/admin/accommodation_booking/show.html.twig
index 4e2ea17..6166ba7 100644
--- a/templates/admin/accommodation_booking/show.html.twig
+++ b/templates/admin/accommodation_booking/show.html.twig
@@ -184,13 +184,22 @@
Das Angebot wurde dem Kunden noch nicht zugestellt. Beim Versand wird der Zugangslink
erzeugt und die Buchung wartet anschließend auf die Annahme durch den Kunden.