chore: cleanup documents

This commit is contained in:
Björn Fromme
2026-08-04 16:43:06 +02:00
parent 8ff13614d2
commit 045fdea923
3 changed files with 0 additions and 1827 deletions
-122
View File
@@ -1,122 +0,0 @@
# Body Dimensions Transition Plan (Stakeholder Draft)
> **STATUS (verified 2026-07-15): NOT IMPLEMENTED.** `src/Form/BodyDimensionsType.php` still declares `height`, `shoeSize`, and `weight` as `IntegerType` fields, and `src/Validator/Constraints/ParticipantValidator.php` has no regex/legacy-format handling — only numeric range checks. This plan has not been acted on.
## Goal
Prevent edit-form failures for existing bookings while we transition from legacy body-dimension values to a better long-term field model.
This plan introduces a temporary compatibility phase and keeps sunsetting manual and straightforward.
## Current Problem
- Existing bookings may contain legacy values such as `-148`, `149-157`, or `195+`.
- Integer-only form fields can fail when these legacy strings are loaded.
- Result: participant edit forms can crash before submit.
## Proposed Transition Strategy
Use **text input fields** for body dimensions during transition, with explicit server-side format validation.
### Key Decisions
- No gating by booking create date.
- One unified behavior for all bookings.
- Accept both legacy and numeric formats during transition.
- Keep manual sunsetting later (no automated migration switch).
## Functional Scope
Affected fields:
- `height`
- `weight`
- `shoeSize`
Affected components:
- `src/Form/BodyDimensionsType.php`
- `src/Validator/Constraints/ParticipantValidator.php`
- `src/Form/BookingParticipantType.php` (option passthrough already in place)
- `config/services.yaml` (ranges remain as validation/config source)
## Validation Rules During Transition
### Accepted formats
- Numeric value: `^\d+$` (e.g. `176`)
- Legacy lower/open/range tokens:
- `^-\d+$` (e.g. `-148`)
- `^\d+\+$` (e.g. `195+`)
- `^\d+\s*-\s*\d+$` (e.g. `149-157`)
### Requiredness
- Keep existing rule: body dimensions required when rentals are selected.
### Range checks
- Keep current numeric range checks based on `body_dimension_ranges`.
- Apply range checks only to plain numeric values in transition phase.
## UX/Behavior Expectations
- Existing legacy values render without form initialization errors.
- New numeric entries are accepted and validated.
- Invalid free text (e.g. `abc`, `17x`) shows clear validation errors.
- HTMX refresh behavior remains unchanged.
## Implementation Steps
1. **Form type change**
- In `BodyDimensionsType`, replace integer fields with text fields for all three body dimensions.
- Keep labels/help text; keep placeholders based on configured ranges.
2. **Validator extension**
- Add format validation for all three fields in `ParticipantValidator`.
- Keep existing required-when-rentals logic.
- Keep existing range validation for numeric values.
3. **Message tuning**
- Add one clear message for invalid format.
- Retain existing range message for numeric out-of-range values.
4. **Testing and QA**
- Validate old bookings with legacy values open/edit successfully.
- Validate numeric path (valid and invalid range).
- Validate invalid format handling.
- Validate rental-required behavior.
## Acceptance Criteria
- No 500 error when editing participants with legacy body-dimension values.
- Legacy values can be loaded and submitted in transition phase.
- Numeric values are accepted and range-validated.
- Invalid text is rejected with a user-facing validation message.
- Existing participant edit/refresh flows continue to work.
## Risks and Mitigations
- **Risk:** Free-text field semantics are less strict than integer fields.
- **Mitigation:** strict server-side regex + range checks.
- **Risk:** Inconsistent data representations during transition.
- **Mitigation:** explicit acceptance policy and manual sunset plan.
## Effort Estimate
- Implementation: 0.5 day
- Validation/message tuning: 0.25 day
- QA/manual testing: 0.5 day
- **Total:** ~1 to 1.5 days
## Manual Sunset Plan (Later)
When stakeholders approve end of transition:
1. Replace text fields with final semantic field type(s).
2. Remove legacy format acceptance from validator.
3. Keep only final numeric/range validation behavior.
4. Remove transition-specific tests and copy.
Expected cleanup effort: ~0.25 to 0.5 day.
-196
View File
@@ -1,196 +0,0 @@
# Booking Flow Reference
Last updated: 2026-07-15
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.
---
## Shared Concepts
### `BookingDto`
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`.
### `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.
### `BookingCreateContextFactory`
Assembles the view model (`BookingCreateContext`) passed to create-flow templates. Handles room grouping (by-pax vs. by-room) and wires summary sidebar data.
---
## Create Flow
```
Step 1 (room selection)
→ Step 2 (participants)
→ Step 2 sub-form (per-participant, HTMX)
→ Step 3 (payment + API pre-validation)
→ Step 4 (confirmation + final submission)
→ Success
```
### Entry / Bootstrap
`AbstractBookingCreateController::loadBookingCreateDto()` is called by every step controller and delegates to `BookingSessionManager::getOrCreateBookingCreateDto()`. If no DTO exists in session, `BookingConfigurator::startFreshBooking()` loads travel data and initialises a fresh `BookingDto`.
### Step 1 — Room Selection
**Controller:** `Create/Step1Controller`
**Route:** `GET|POST /bookings/create/rooms`
1. Load or create DTO via `getOrCreateBookingCreateDto()`
2. `BookingSessionManager::getOrCreateBaselineSnapshot()` — saves the current room selection as a comparison point for change detection
3. Render `BookingCreateStep1Type` form
4. On submit:
- If room selection changed from baseline: `BookingDto::resetParticipantAssignments()` — clears stale assignments
- `BookingConfigurator::updateBookingStatusFromRoomSelection()` — may switch booking to inquiry mode if capacity constraints require it
- `BookingSessionManager::saveBookingDto()`, advance `currentStep` to 2
- `BookingSessionManager::clearBaselineSnapshot()`
- Redirect to Step 2
HTMX refresh (`/bookings/create/refresh`): processes the form without validation and returns updated room selection + summary sidebar blocks.
### Step 2 — Participants (Card Overview)
**Controller:** `Create/Step2Controller`
**Route:** `GET|POST /bookings/create/participants`
1. Load DTO from session (or redirect to Step 1 if missing)
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.
4. `ParticipantDataPrefiller::shouldPrefillApplicant()` + `prefillApplicantFromUser()` — prefills participant[0] from user profile on first visit
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. `RoomAssigner::assignRoomsIfNeeded()` — auto-assigns participants to rooms
7. `BookingConfigurator::preselectDefaultServices()` — selects mandatory and auto-book services for all participants
8. `BookingConfigurator::applyCreateBookingStatusRules()` — re-evaluates booking status (book / inquiry / not-bookable)
9. `BookingSessionManager::saveBookingDto()`
10. On form submit (all participants valid): advance to Step 3, redirect
### Step 2 — Individual Participant Form (HTMX)
**Controller:** `Create/Step2ParticipantController`
**Routes:** `GET|POST /bookings/create/participants/{index}` and `/participants/{index}/refresh`
Each participant card lazy-loads its form via HTMX. The controller:
1. Verifies the participant slot exists
2. Refreshes availability
3. Handles dummy data fill (`ParticipantDataPrefiller::isDummyDataFillRequested()` / `fillDummyParticipant()`)
4. On submit: re-applies status rules, saves to session, redirects to card overview
5. `ParticipantFormSupport` is used for form creation options and notification draining
### Step 3 — Payment + API Pre-Validation
**Controller:** `Create/Step3Controller`
**Route:** `GET|POST /bookings/create/payment`
1. Load DTO from session
2. `BookingConfigurator::applyCreateBookingStatusRules()`
3. On submit:
- `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 otherwise: show error, stay on step
- 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
### Step 4 — Confirmation + Final Submission
**Controller:** `Create/Step4Controller`
**Route:** `GET|POST /bookings/create/confirmation`
1. Load DTO from session
2. Newsletter subscription check for opt-in display
3. On submit:
- `BookingConfigurator::applyCreateBookingStatusRules()`
- `ApiClient::createBooking()` — final booking submission (already validated in Step 3)
- Newsletter double-opt-in if selected
- On success: store booking number + total in flash (for analytics), clear travel data cache, `BookingSessionManager::clearBookingDto()`, redirect to success page
---
## Edit Flow
```
Entry (start)
→ Load (session or API + draft restore)
→ Participant sub-forms (HTMX, per-participant)
→ Submission
```
### Entry
**Controller:** `Edit/IndexController::start()`
**Route:** `GET /bookings/{id}/edit/start`
Clears any existing session DTO for edit mode and invalidates the API booking cache, then redirects to the edit page. This ensures a clean load from API on every intentional re-entry.
### Load
**Controller:** `Edit/IndexController::index()`
**Route:** `GET|POST /bookings/{id}/edit`
Loading is delegated entirely to `BookingEditDataLoader::loadFormData()`:
**If session DTO exists and matches the requested booking ID:**
- `TravelDataProvider::enrichWithFreshAvailabilities()` — refreshes room availability
- `TravelDataProvider::getMutabilityData()` + `patchMutability()` — applies cached mutability state to DTO
**If no session DTO (first load or after `start`):**
`initializeFromApi()`:
1. `fetchBookingData()``ApiClient::getBooking()` with 5-minute cache (tagged by user ID for bulk invalidation)
2. `TravelDataProvider::getTravelData()` — loads travel data (XML or snapshot)
3. `TravelDataProvider::getAvailabilityData(forceRefresh: true)` + `patchAvailabilities()` — force-refreshes availability at edit start, populates cache for subsequent loads within the session
4. `TravelDataProvider::getMutabilityData(cached: true)` + `patchMutability()` — applies which service categories are currently editable
5. `BookingDataProcessor::createBookingDtoFromBooking()` — hydrates `BookingDto` from API booking entity
6. Agency code resolved via `AgencyLoader` (used by field state conditions)
7. `BookingChangeTracker::generateFingerprint()` — sets `originalFingerprint` on DTO **before** draft is applied, so dirty detection compares against original API data
8. `BookingEditDraftManager::findDraft()` + `applyDraftToDto()` — if draft exists, merges user edits on top of fresh API data. Sets `draftWasRestored = true` if successful.
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()`.
### Individual Participant Form (HTMX)
**Controller:** `Edit/ParticipantController`
**Routes:** `GET|POST /bookings/{id}/edit/participants/{index}` and `/{index}/refresh`
Per-participant forms are lazy-loaded, matching the create flow pattern.
1. `BookingEditContextFactory::prepareBookingDto()` — refreshes availabilities on the DTO travel object
2. `ParticipantFormSupport` — form creation, notification draining
3. On submit: `BookingSessionManager::saveBookingDto()` + `BookingEditDraftManager::saveDraft()` — persists participant edits to both session and draft
### Submission
**Service:** `BookingEditSubmitter::handleSubmission()`
Called from `Edit/IndexController` on valid form submit.
1. Invalidate API cache, fetch fresh booking data
2. Force-refresh mutability data (`TravelDataProvider::getMutabilityData(forceRefresh: true)`) and patch DTO travel
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
5. On success: invalidate cache again, `BookingSessionManager::clearBookingDto()`, `BookingEditDraftManager::deleteDraft()`
### Other Edit Routes
| Route | Action |
|-------|--------|
| `GET /bookings/{id}/edit/reload` | Shows confirmation modal |
| `POST /bookings/{id}/edit/reload` | Clears session + **deletes draft**, reloads from API |
| `GET /bookings/{id}/edit/cancel` | Clears session, **preserves draft**, redirects to booking list |
The distinction between reload and cancel is intentional: cancel keeps the draft so the user can return to their changes later; reload discards everything.
---
## 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.
- 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.
- Drafts survive cancel but not reload, and are deleted on successful submission.
File diff suppressed because it is too large Load Diff