chore: update booking-flow reference to current class names in docs

This commit is contained in:
Björn Fromme
2026-04-16 13:34:54 +02:00
parent 132329361f
commit b68c4ff9b4
+36 -36
View File
@@ -1,6 +1,6 @@
# Booking Flow Reference # Booking Flow Reference
Last updated: 2026-04-13 Last updated: 2026-04-16
This document traces the booking create and edit flows from controller entry point through to session/DTO state. It is intended as orientation for developers unfamiliar with the flow, not as a substitute for reading the code. This document traces the booking create and edit flows from controller entry point through to session/DTO state. It is intended as orientation for developers unfamiliar with the flow, not as a substitute for reading the code.
@@ -12,7 +12,7 @@ This document traces the booking create and edit flows from controller entry poi
The central data object for both flows. Holds room selections, participants, payment method, and travel reference. Lives in the HTTP session between requests. Has two modes: `MODE_CREATE` and `MODE_EDIT`. The central data object for both flows. Holds room selections, participants, payment method, and travel reference. Lives in the HTTP session between requests. Has two modes: `MODE_CREATE` and `MODE_EDIT`.
### `BookingSessionService` ### `BookingSessionManager`
Owns all reads and writes to session-stored `BookingDto` instances. Also manages the baseline room snapshot used for change detection in the create flow. Owns all reads and writes to session-stored `BookingDto` instances. Also manages the baseline room snapshot used for change detection in the create flow.
@@ -35,7 +35,7 @@ Step 1 (room selection)
### Entry / Bootstrap ### Entry / Bootstrap
`BookingCreateTrait::getOrCreateBookingCreateDto()` is called by every step controller. If no DTO exists in session, it calls `BookingService::startFreshBooking()` which loads travel data and initialises a fresh `BookingDto`. `BookingExceptionHandlerTrait::getOrCreateBookingCreateDto()` is called by every step controller. If no DTO exists in session, it calls `BookingConfigurator::startFreshBooking()` which loads travel data and initialises a fresh `BookingDto`.
### Step 1 — Room Selection ### Step 1 — Room Selection
@@ -43,13 +43,13 @@ Step 1 (room selection)
**Route:** `GET|POST /bookings/create/rooms` **Route:** `GET|POST /bookings/create/rooms`
1. Load or create DTO via `getOrCreateBookingCreateDto()` 1. Load or create DTO via `getOrCreateBookingCreateDto()`
2. `BookingSessionService::getOrCreateBaselineSnapshot()` — saves the current room selection as a comparison point for change detection 2. `BookingSessionManager::getOrCreateBaselineSnapshot()` — saves the current room selection as a comparison point for change detection
3. Render `BookingCreateStep1Type` form 3. Render `BookingCreateStep1Type` form
4. On submit: 4. On submit:
- If room selection changed from baseline: `BookingDto::resetParticipantAssignments()` — clears stale assignments - If room selection changed from baseline: `BookingDto::resetParticipantAssignments()` — clears stale assignments
- `BookingService::updateBookingStatusFromRoomSelection()` — may switch booking to inquiry mode if capacity constraints require it - `BookingConfigurator::updateBookingStatusFromRoomSelection()` — may switch booking to inquiry mode if capacity constraints require it
- `BookingSessionService::saveBookingDto()`, advance `currentStep` to 2 - `BookingSessionManager::saveBookingDto()`, advance `currentStep` to 2
- `BookingSessionService::clearBaselineSnapshot()` - `BookingSessionManager::clearBaselineSnapshot()`
- Redirect to Step 2 - Redirect to Step 2
HTMX refresh (`/bookings/create/refresh`): processes the form without validation and returns updated room selection + summary sidebar blocks. HTMX refresh (`/bookings/create/refresh`): processes the form without validation and returns updated room selection + summary sidebar blocks.
@@ -60,14 +60,14 @@ HTMX refresh (`/bookings/create/refresh`): processes the form without validation
**Route:** `GET|POST /bookings/create/participants` **Route:** `GET|POST /bookings/create/participants`
1. Load DTO from session (or redirect to Step 1 if missing) 1. Load DTO from session (or redirect to Step 1 if missing)
2. `TravelDataService::enrichWithFreshAvailabilities()` — refreshes room availability 2. `TravelDataProvider::enrichWithFreshAvailabilities()` — refreshes room availability
3. `ensureCorrectNumberOfParticipants()` — syncs the `BookingDto::participants` array length with the room selections (e.g., 2× double room → 4 participants). Preserves existing participant data for slots that still exist. 3. `ensureCorrectNumberOfParticipants()` — syncs the `BookingDto::participants` array length with the room selections (e.g., 2× double room → 4 participants). Preserves existing participant data for slots that still exist.
4. `ParticipantPrepopulationService::shouldPrepopulateApplicant()` + `prepopulateApplicantFromUser()` — prefills participant[0] from user profile on first visit 4. `ParticipantDataPrefiller::shouldPrepopulateApplicant()` + `prepopulateApplicantFromUser()` — prefills participant[0] from user profile on first visit
5. `RoomAssignmentService::validateAndResetInvalidAssignments()` — clears any room assignments that no longer match the current room selection (handles back-navigation from Step 2 → Step 1 → Step 2) 5. `RoomAssigner::validateAndResetInvalidAssignments()` — clears any room assignments that no longer match the current room selection (handles back-navigation from Step 2 → Step 1 → Step 2)
6. `RoomAssignmentService::assignRoomsIfNeeded()` — auto-assigns participants to rooms 6. `RoomAssigner::assignRoomsIfNeeded()` — auto-assigns participants to rooms
7. `BookingService::preselectDefaultServices()` — selects mandatory and auto-book services for all participants 7. `BookingConfigurator::preselectDefaultServices()` — selects mandatory and auto-book services for all participants
8. `BookingService::applyCreateBookingStatusRules()` — re-evaluates booking status (book / inquiry / not-bookable) 8. `BookingConfigurator::applyCreateBookingStatusRules()` — re-evaluates booking status (book / inquiry / not-bookable)
9. `BookingSessionService::saveBookingDto()` 9. `BookingSessionManager::saveBookingDto()`
10. On form submit (all participants valid): advance to Step 3, redirect 10. On form submit (all participants valid): advance to Step 3, redirect
### Step 2 — Individual Participant Form (HTMX) ### Step 2 — Individual Participant Form (HTMX)
@@ -79,9 +79,9 @@ Each participant card lazy-loads its form via HTMX. The controller:
1. Verifies the participant slot exists 1. Verifies the participant slot exists
2. Refreshes availability 2. Refreshes availability
3. Handles dummy data fill (`ParticipantPrepopulationService::isDummyDataFillRequested()` / `fillDummyParticipant()`) 3. Handles dummy data fill (`ParticipantDataPrefiller::isDummyDataFillRequested()` / `fillDummyParticipant()`)
4. On submit: re-applies status rules, saves to session, redirects to card overview 4. On submit: re-applies status rules, saves to session, redirects to card overview
5. `ParticipantFormSupportService` is used for form creation options and notification draining 5. `ParticipantFormSupport` is used for form creation options and notification draining
### Step 3 — Payment + API Pre-Validation ### Step 3 — Payment + API Pre-Validation
@@ -89,12 +89,12 @@ Each participant card lazy-loads its form via HTMX. The controller:
**Route:** `GET|POST /bookings/create/payment` **Route:** `GET|POST /bookings/create/payment`
1. Load DTO from session 1. Load DTO from session
2. `BookingService::applyCreateBookingStatusRules()` 2. `BookingConfigurator::applyCreateBookingStatusRules()`
3. On submit: 3. On submit:
- `ApiClient::createBookingInquiry()` — submits booking to API for validation without committing it - `ApiClient::createBookingInquiry()` — submits booking to API for validation without committing it
- If inquiry fails with "nicht möglich" + "Anfrage" in message: auto-switch to inquiry mode, advance to Step 4 - If inquiry fails with "nicht möglich" + "Anfrage" in message: auto-switch to inquiry mode, advance to Step 4
- If inquiry fails otherwise: show error, stay on step - If inquiry fails otherwise: show error, stay on step
- Price verification: `BookingPriceCalculatorService::calculateGrandTotal()` is compared against the API response total (accounting for promotional/goodwill voucher discounts and API-applied group discounts). Mismatch triggers `BookingPriceMismatchDiagnosticsService` for detailed logging. - Price verification: `BookingPriceCalculator::calculateGrandTotal()` is compared against the API response total (accounting for promotional/goodwill voucher discounts and API-applied group discounts). Mismatch triggers `BookingPriceMismatchAnalyzer` for detailed logging.
- On success: advance `currentStep` to 4, redirect - On success: advance `currentStep` to 4, redirect
### Step 4 — Confirmation + Final Submission ### Step 4 — Confirmation + Final Submission
@@ -105,10 +105,10 @@ Each participant card lazy-loads its form via HTMX. The controller:
1. Load DTO from session 1. Load DTO from session
2. Newsletter subscription check for opt-in display 2. Newsletter subscription check for opt-in display
3. On submit: 3. On submit:
- `BookingService::applyCreateBookingStatusRules()` - `BookingConfigurator::applyCreateBookingStatusRules()`
- `ApiClient::createBooking()` — final booking submission (already validated in Step 3) - `ApiClient::createBooking()` — final booking submission (already validated in Step 3)
- Newsletter double-opt-in if selected - Newsletter double-opt-in if selected
- On success: store booking number + total in flash (for analytics), clear travel data cache, `BookingSessionService::clearBookingDto()`, redirect to success page - On success: store booking number + total in flash (for analytics), clear travel data cache, `BookingSessionManager::clearBookingDto()`, redirect to success page
--- ---
@@ -133,24 +133,24 @@ Clears any existing session DTO for edit mode and invalidates the API booking ca
**Controller:** `Edit/IndexController::index()` **Controller:** `Edit/IndexController::index()`
**Route:** `GET|POST /bookings/{id}/edit` **Route:** `GET|POST /bookings/{id}/edit`
Loading is delegated entirely to `BookingEditDataLoaderService::loadFormData()`: Loading is delegated entirely to `BookingEditDataLoader::loadFormData()`:
**If session DTO exists and matches the requested booking ID:** **If session DTO exists and matches the requested booking ID:**
- `TravelDataService::enrichWithFreshAvailabilities()` — refreshes room availability - `TravelDataProvider::enrichWithFreshAvailabilities()` — refreshes room availability
- `TravelDataService::getMutabilityData()` + `patchMutability()` — applies cached mutability state to DTO - `TravelDataProvider::getMutabilityData()` + `patchMutability()` — applies cached mutability state to DTO
**If no session DTO (first load or after `start`):** **If no session DTO (first load or after `start`):**
`initializeFromApi()`: `initializeFromApi()`:
1. `fetchBookingData()``ApiClient::getBooking()` with 5-minute cache (tagged by user ID for bulk invalidation) 1. `fetchBookingData()``ApiClient::getBooking()` with 5-minute cache (tagged by user ID for bulk invalidation)
2. `TravelDataService::getTravelData()` — loads travel data (XML or snapshot) 2. `TravelDataProvider::getTravelData()` — loads travel data (XML or snapshot)
3. `TravelDataService::getAvailabilityData(forceRefresh: true)` + `patchAvailabilities()` — force-refreshes availability at edit start, populates cache for subsequent loads within the session 3. `TravelDataProvider::getAvailabilityData(forceRefresh: true)` + `patchAvailabilities()` — force-refreshes availability at edit start, populates cache for subsequent loads within the session
4. `TravelDataService::getMutabilityData(cached: true)` + `patchMutability()` — applies which service categories are currently editable 4. `TravelDataProvider::getMutabilityData(cached: true)` + `patchMutability()` — applies which service categories are currently editable
5. `BookingDataProcessor::createBookingDtoFromBooking()` — hydrates `BookingDto` from API booking entity 5. `BookingDataProcessor::createBookingDtoFromBooking()` — hydrates `BookingDto` from API booking entity
6. Agency code resolved via `AgencyLoader` (used by field state conditions) 6. Agency code resolved via `AgencyLoader` (used by field state conditions)
7. `BookingFingerprintService::generateFingerprint()` — sets `originalFingerprint` on DTO **before** draft is applied, so dirty detection compares against original API data 7. `BookingChangeTracker::generateFingerprint()` — sets `originalFingerprint` on DTO **before** draft is applied, so dirty detection compares against original API data
8. `BookingEditDraftService::findDraft()` + `applyDraftToDto()` — if draft exists, merges user edits on top of fresh API data. Sets `draftWasRestored = true` if successful. 8. `BookingEditDraftManager::findDraft()` + `applyDraftToDto()` — if draft exists, merges user edits on top of fresh API data. Sets `draftWasRestored = true` if successful.
9. `BookingSessionService::saveBookingDto()` 9. `BookingSessionManager::saveBookingDto()`
Back in the controller: shows "draft restored" flash if applicable, fetches booking data for surcharge/status display, renders the card overview with `BookingEditContextFactory::createOverviewContext()`. Back in the controller: shows "draft restored" flash if applicable, fetches booking data for surcharge/status display, renders the card overview with `BookingEditContextFactory::createOverviewContext()`.
@@ -162,19 +162,19 @@ Back in the controller: shows "draft restored" flash if applicable, fetches book
Per-participant forms are lazy-loaded, matching the create flow pattern. Per-participant forms are lazy-loaded, matching the create flow pattern.
1. `BookingEditContextFactory::prepareBookingDto()` — refreshes availabilities on the DTO travel object 1. `BookingEditContextFactory::prepareBookingDto()` — refreshes availabilities on the DTO travel object
2. `ParticipantFormSupportService` — form creation, notification draining 2. `ParticipantFormSupport` — form creation, notification draining
3. On submit: `BookingSessionService::saveBookingDto()` + `BookingEditDraftService::saveDraft()` — persists participant edits to both session and draft 3. On submit: `BookingSessionManager::saveBookingDto()` + `BookingEditDraftManager::saveDraft()` — persists participant edits to both session and draft
### Submission ### Submission
**Service:** `BookingEditSubmitService::handleSubmission()` **Service:** `BookingEditSubmitter::handleSubmission()`
Called from `Edit/IndexController` on valid form submit. Called from `Edit/IndexController` on valid form submit.
1. Invalidate API cache, fetch fresh booking data 1. Invalidate API cache, fetch fresh booking data
2. Force-refresh mutability data (`TravelDataService::getMutabilityData(forceRefresh: true)`) and patch DTO travel 2. Force-refresh mutability data (`TravelDataProvider::getMutabilityData(forceRefresh: true)`) and patch DTO travel
3. `BookingEditSubmitGuardService::reconcileImmutableCategories()` — reverts any DTO fields that have become immutable since the session was loaded. Shows flash if anything was reverted. 3. `BookingEditSubmitGuard::reconcileImmutableCategories()` — reverts any DTO fields that have become immutable since the session was loaded. Shows flash if anything was reverted.
4. `ApiClient::updateBooking()` — submit 4. `ApiClient::updateBooking()` — submit
5. On success: invalidate cache again, `BookingSessionService::clearBookingDto()`, `BookingEditDraftService::deleteDraft()` 5. On success: invalidate cache again, `BookingSessionManager::clearBookingDto()`, `BookingEditDraftManager::deleteDraft()`
### Other Edit Routes ### Other Edit Routes
@@ -191,6 +191,6 @@ The distinction between reload and cancel is intentional: cancel keeps the draft
## Key Invariants ## Key Invariants
- `currentStep` on the DTO gates forward navigation in create mode; controllers redirect to the current step if a higher step is requested directly. - `currentStep` on the DTO gates forward navigation in create mode; controllers redirect to the current step if a higher step is requested directly.
- The original fingerprint is set before draft application in edit mode. Dirty detection (`BookingFingerprintService::isDirty()`) always compares the live session DTO against the original API state, not against the draft. - The original fingerprint is set before draft application in edit mode. Dirty detection (`BookingChangeTracker::isDirty()`) always compares the live session DTO against the original API state, not against the draft.
- Mutability is always patched from fresh or cached data before rendering. The DTO travel object is the authoritative source of which fields are editable at render time. - Mutability is always patched from fresh or cached data before rendering. The DTO travel object is the authoritative source of which fields are editable at render time.
- Drafts survive cancel but not reload, and are deleted on successful submission. - Drafts survive cancel but not reload, and are deleted on successful submission.