Skip to content

Cruits Housing — Iteration 3: Story Specifications

GitHub: https://github.com/WAconsult/cruits-housing-ba/blob/main/01-scoping/planning-artifacts/iteration-3-story-specs.md

Iteration 3 goal: All actors can act on bookings end-to-end. A Flexworker's reservation progresses from RESERVED through provider confirmation, where the Provider confirms or rejects via Provider Admin (with an optional auto-confirm setting); a Payer can approve or reject a reservation waiting for their approval; any actor can cancel at the appropriate stage; a Provider can mark a booking as ended; and both Provider Admin and Payer Admin have an operational residents view.

Writing standard: This document is written for Manual QA and technical business analysts. Requirements and Acceptance Criteria describe observable system behavior — not implementation details.


Stories in This Document

Story Title
3.3 Provider Confirmation & Direct Booking Path
3.4 Payer Confirmation Flow
3.5 (full) Booking State Transition Notifications — Full
3.7 Pre-BOOKED Cancellation
3.8 WGV Compliance: ENDED Trigger, Overstay & Period-End Notification
3.9 BOOKED Booking Cancellation
5.1 Active Residents View
6.1 Payer Residents View & Reservation Management

Booking State Machine — Iteration 3 Scope

The full state machine for the Cruits Housing booking flow (through Iteration 3) is shown below. Signing states (Iteration 4) and GoMeddo provider states (Iteration 5) are noted but not implemented here.

Worker creates reservation
       RESERVED  ──── [hold window expires] ──→  RESERVATION_FAILED
  PENDING_PROVIDER  (automatic from RESERVED; hold window remains active)
    │     ↓ Provider confirms via Provider Admin (or auto-confirm setting ON)
    │     ┌───────────────────────────────────────┐
    │     │ Direct payment (payer = worker):      │
    │     │   → BOOKED                           │
    │     │                                      │
    │     │ Indirect payment (third-party Payer): │
    │     │   → PENDING_PAYER                    │
    │     │       ↓ confirmed by Payer           │
    │     │     BOOKED                           │
    │     │       ↓ rejected / timeout           │
    │     │     RESERVATION_FAILED               │
    │     └───────────────────────────────────────┘
    └──── [Provider rejects or hold window expires] ──→  RESERVATION_FAILED

  Exception: booking_type = provider_block → BOOKED directly (skips PENDING_PROVIDER)

  BOOKED  ──── [Provider marks check-in via Provider Admin] ──→  CHECKED_IN  (Story 3.10)
  BOOKED  ──── [cancellation] ──────────────────────────────→  CANCELLED

  CHECKED_IN  ──── [period.end passes, no checkout] ──→  stays CHECKED_IN; Overstay flag available
  CHECKED_IN  ──── [explicit checkout by Resident or Provider] ──→  ENDED

  Any pre-BOOKED state (Story 3.7): ──────────────────────────────→  CANCELLED
  BOOKED (Story 3.9): ────────────────────────────────────────────→  CANCELLED
  Note: cancellation is NOT available from CHECKED_IN.

  Note: SIGNING state (between provider/payer confirmation and BOOKED) is deferred to Iteration 4.

Terminal states: RESERVATION_FAILED, CANCELLED, ENDED.


Story 3.3: Provider Confirmation & Direct Booking Path

Epic: Epic 3 — Reservation & Booking Flow

User Story

As a Flexworker, I want the Provider to confirm my reservation via Provider Admin, and — when I am paying directly and no signing step is required — to reach BOOKED as soon as the Provider confirms, So that my housing is confirmed and I have certainty about my accommodation.

Functional Requirements Covered

FR Requirement
FR-12 Automatic PENDING_PROVIDER transition; Provider confirms or rejects via Provider Admin; optional auto-confirm setting per Provider
FR-14 Provider confirmation routes the booking to PENDING_PAYER (indirect payment) or BOOKED (direct payment without signing)
FR-16 RESERVATION_FAILED on provider rejection or timeout; hold released; parties notified
FR-17 BOOKED record stores all required fields including confirmed address and full state history
FR-21 Payment terms applied automatically on booking confirmation

Booking Flow — Provider Confirmation

RESERVED  ──→  PENDING_PROVIDER
                    ├── Provider confirms via Provider Admin
                    │   (or auto-confirm setting ON → system confirms)
                    │       ↓
                    │   direct payment → BOOKED
                    │   indirect payment → PENDING_PAYER  (Story 3.4)
                    └── Provider rejects  ──→  RESERVATION_FAILED
                        or hold window expires

When a Flexworker creates a reservation, the booking automatically transitions to PENDING_PROVIDER. The Provider sees it in Provider Admin and must explicitly confirm or reject the booking. On confirmation, the booking routes to BOOKED (direct payment) or PENDING_PAYER (indirect payment). On rejection, the booking moves to RESERVATION_FAILED and the unit hold is released.

Auto-confirm exception: If the Provider has the auto-confirm setting enabled in Provider Admin Settings, the system confirms automatically on the Provider's behalf — no manual action is needed. The BookingStateTransition entry in this case records actor = system.

Provider-block exception: When a Provider creates a booking with booking_type = provider_block (e.g., to block a unit for maintenance), the booking transitions directly to BOOKED without passing through PENDING_PROVIDER. No confirmation step is required because the Provider is the initiating actor.

The GoMeddo provider confirmation path (where the system calls an external API) is implemented in Iteration 5 (Story 8.4). The booking entity and state history must accommodate that path, but no GoMeddo-specific behaviour is activated here.

Provider Settings Extension — Auto-Confirm

The following setting is added to Provider Admin Settings (extension of Story 1.10):

Setting Label Type Default Description
auto_confirm_reservations Auto-confirm reservations Yes/No No When Yes, the system automatically confirms all incoming PENDING_PROVIDER bookings without requiring manual Provider action. The Provider still sees the confirmed bookings in the Booking List.

Entity Extension — Booking (confirmed address and checkout fields)

The Booking entity defined in Story 1.7 is extended with the following fields, populated when the booking reaches BOOKED:

Field Label Type Notes
confirmed_address Confirmed address Text snapshot Full building address at the time the booking reaches BOOKED. For concrete-mode bookings, also includes the slot identifier (floor / room / bed). Immutable after being set.
booking_confirmed_at Confirmed at Date and time Set automatically when the booking transitions to BOOKED.
checked_out_at Checked out at Date and time (nullable) Set when the booking transitions to ENDED.
checkout_actor Checked out by Enum (nullable) worker or provider. Set at the time of ENDED transition.
is_overstay Overstay flag Yes/No Default: No. Set to Yes when a Provider flags the booking as an overstay (Story 3.8).
overstay_flagged_at Overstay flagged at Date and time (nullable) Set when is_overstay is set to Yes.

Acceptance Criteria

  1. Automatic transition RESERVED → PENDING_PROVIDER
  2. Immediately after a Booking is created in RESERVED state, the system automatically transitions it to PENDING_PROVIDER.
  3. No actor action or delay is involved. This transition is recorded in BookingStateTransition with actor = system.

  4. Provider confirmation via Provider Admin

  5. While a booking is in PENDING_PROVIDER state, it appears in the Provider's Booking List in Provider Admin (Story 1.7) with a "Confirm" and "Reject" action.
  6. On confirm: the booking transitions to the next state (per AC3). The BookingStateTransition entry records actor = provider.
  7. On reject: the Provider must enter a rejection reason. The booking transitions to RESERVATION_FAILED. The unit hold is released. The BookingStateTransition entry records actor = provider and captures the reason in the notes field. The Flexworker is notified (Story 3.5 full).

2a. Auto-confirm setting - If the Provider has auto_confirm_reservations = Yes, the system immediately auto-confirms every incoming PENDING_PROVIDER booking without any Provider action. - The BookingStateTransition entry records actor = system. - The Provider still sees the booking in the Booking List after it has advanced past PENDING_PROVIDER.

2b. Provider-block exception - A booking with booking_type = provider_block (created by the Provider to block a unit, e.g., for maintenance) is created directly in BOOKED state. It does not pass through PENDING_PROVIDER. - The BookingStateTransition log records a single initial entry: null → BOOKED, actor = provider.

  1. Routing after provider confirmation
  2. Direct payment (payment_form = worker_direct): the booking transitions to BOOKED.
  3. Indirect payment (any other payment_form): the booking transitions to PENDING_PAYER.
  4. Note: If the Provider has document signing configured, the booking would enter a SIGNING state before BOOKED. The SIGNING state is defined here as a future intermediate state but is not activated until Iteration 4. In Iteration 3, all bookings skip the signing step regardless of Provider signing configuration.

  5. RESERVATION_FAILED on provider rejection or hold window expiry

  6. Provider rejection (Iteration 3, active): When a Provider rejects a PENDING_PROVIDER booking via Provider Admin, the booking transitions to RESERVATION_FAILED. The unit hold is released. The Flexworker and Provider are notified (Story 3.5 full).
  7. Hold window expiry (Iteration 3, active): If the Provider does not act (confirm or reject) before the hold window expires (Story 3.1), the system automatically transitions the booking to RESERVATION_FAILED and releases the hold. The Flexworker is notified.
  8. GoMeddo external rejection (Iteration 5, deferred): When GoMeddo integration is active (Story 8.4), an external API rejection or timeout also leads to RESERVATION_FAILED. The booking entity and state history are designed to accommodate this path.

  9. BOOKED record — confirmed address

  10. When a booking reaches BOOKED, the system sets confirmed_address and booking_confirmed_at on the Booking record.
  11. confirmed_address is a snapshot of the Building's address fields at the time BOOKED is reached: street, house number, addition (if set), postcode, city. For concrete-mode bookings, the slot identifier (floor / room / bed) is appended to the address.
  12. For pool-mode bookings, confirmed_address contains the Building address only. The specific unit is not yet known; it will be assigned by the Provider at check-in (Story 7.2, Iteration 5).
  13. confirmed_address is immutable after being set. If the Provider later changes the Building's address, the existing Booking record is unaffected.

  14. BOOKED record — full state history

  15. When a booking reaches BOOKED, the BookingStateTransition log must contain one entry for each state the booking has passed through: the initial entry (null → RESERVED), RESERVED → PENDING_PROVIDER, PENDING_PROVIDER → BOOKED (direct) or PENDING_PROVIDER → PENDING_PAYER → BOOKED (indirect with Payer confirmation).
  16. No entries may be missing or out of chronological order.

  17. Payment terms (FR-21) — scope in Iteration 3

  18. When a booking reaches BOOKED, the payment_form stored at reservation creation is the payment arrangement on the record.
  19. Full payment terms scheduling (prepay N days before arrival, postpay N days after, or custom schedule per payer-provider agreement) is implemented in Story 1.9 (Iteration 4). In Iteration 3, the payment form label is the only applied arrangement.

  20. Booking Status View updates (Cruits app)

  21. The Booking Status View (Story 3.6) must display two new statuses introduced in this story:
    • PENDING_PROVIDER"Confirming availability""We are confirming your unit with the housing provider."
    • BOOKED"Housing confirmed""Your unit at [address] is confirmed. Check-in: [date]. Check-out: [date]."
  22. The confirmed address is shown on the BOOKED status screen.

Dependencies

  • Story 3.2 (Reservation Creation) — booking must exist in RESERVED state.
  • Story 3.1 (Hold Window Enforcement) — hold window expiry applies during PENDING_PROVIDER; if the Provider does not act before expiry, the booking transitions to RESERVATION_FAILED.
  • Story 1.7 (Booking List) — Confirm and Reject actions for PENDING_PROVIDER bookings are surfaced in the Booking List in Provider Admin.
  • Story 1.10 (Provider Admin Settings) — extended with the auto-confirm setting defined in this story.
  • Story 3.4 (Payer Confirmation) — indirect-payment bookings route here after provider confirmation.

Out of Scope

  • GoMeddo provider confirmation via external API (Story 8.4 — Iteration 5).
  • DocuSign signing step before BOOKED (Story 4.2 — Iteration 4).
  • Webhook delivery of confirmed booking to Cruits (Story 7.1 — Iteration 5).
  • Platform Admin override of PENDING_PROVIDER confirmations (post-MVP).

Story 3.4: Payer Confirmation Flow

Epic: Epic 3 — Reservation & Booking Flow

User Story

As a Payer, I want to be notified when a reservation is waiting for my approval and to be able to confirm or reject it via Payer Admin, So that I can control which housing bookings proceed on behalf of my residents, and the booking either advances to BOOKED or is released.

Functional Requirements Covered

FR Requirement
FR-15 Payer confirms or rejects a booking in PENDING_PAYER via Payer Admin UI or API
FR-16 RESERVATION_FAILED on Payer rejection or PENDING_PAYER window timeout; hold released; parties notified

Acceptance Criteria

  1. Auto-approval gate
  2. When a booking enters PENDING_PAYER, the system checks the Payer's auto_approve setting (defined in Story 2.1).
  3. If auto_approve = Yes: the booking is immediately and automatically transitioned to BOOKED by the system. No Payer action is required. The BookingStateTransition entry records actor = system.
  4. If auto_approve = No: the booking waits in PENDING_PAYER state for a manual Payer action (confirm or reject) or until the approval window expires.

  5. Payer confirmation

  6. A Payer can confirm a booking in PENDING_PAYER state via Payer Admin (Story 6.1).
  7. On confirmation, the booking transitions to BOOKED. The BookingStateTransition entry records actor = payer.
  8. Note: If the Provider has signing configured, the booking would transition to SIGNING instead of BOOKED. This path is not active in Iteration 3.

  9. Payer rejection

  10. A Payer can reject a booking in PENDING_PAYER state via Payer Admin. The Payer must select or enter a reason for rejection.
  11. On rejection, the booking transitions to RESERVATION_FAILED. The unit hold is released. The BookingStateTransition entry records actor = payer and captures the rejection reason in the notes field.
  12. The Flexworker is notified of the rejection (Story 3.5 full).

  13. PENDING_PAYER approval window expiry

  14. The PENDING_PAYER approval window is configured per Provider (Story 3.1) using the same hold window structure.
  15. When the approval window expires while the booking is in PENDING_PAYER, the system automatically transitions the booking to RESERVATION_FAILED and releases the unit hold. The BookingStateTransition entry records actor = system and notes "Payer approval window expired".
  16. The Flexworker and Payer are notified (Story 3.5 full).
  17. This is the enforcement of the PENDING_PAYER window configured in Iteration 2 (Story 3.1 AC 2).

  18. Booking Status View — new statuses (Cruits app)

  19. The Booking Status View (Story 3.6) must display the following statuses introduced in this story:
    • PENDING_PAYER"Awaiting payer approval""Your reservation is waiting for approval from [Payer name]. You will be notified when a decision is made."
    • RESERVATION_FAILED (payer rejected) → "Reservation rejected""Your reservation for [Listing Title] was rejected by [Payer name]. You may search for another listing."
    • RESERVATION_FAILED (payer timeout) → "Reservation expired""Approval from [Payer name] was not received in time. The unit hold has been released."

Dependencies

  • Story 3.3 (Provider Confirmation) — booking must be in PENDING_PAYER to trigger this flow.
  • Story 3.1 (Hold Window Config) — PENDING_PAYER approval window enforcement.
  • Story 6.1 (Payer Residents View) — Payer Admin UI for confirm/reject actions.

Out of Scope

  • DocuSign signing step after Payer confirmation (Story 4.2 — Iteration 4).
  • Payer confirmation via API (the API endpoint can be built as part of this story; Payer Admin is the primary UI path).

Story 3.5 (full): Booking State Transition Notifications — Full

Epic: Epic 3 — Reservation & Booking Flow

User Story

As a Flexworker (and Provider, Payer), I want to receive a notification at every booking state transition relevant to me — a push notification in the Cruits app for the Flexworker, and a real-time desktop notification in Provider Admin or Payer Admin for Provider and Payer — So that all parties are kept informed without having to manually poll for status changes.

Functional Requirements Covered

FR Requirement
FR-18 Notifications triggered at every relevant booking state transition (full coverage; partial delivered in Story 3.5 basic, Iteration 2)
FR-43 Delivery channels: push notification to Flexworker via Cruits app; real-time desktop notification via SSE to Provider and Payer in their respective Admin panels

Scope Note

Story 3.5 basic (Iteration 2) delivered push notifications to the Flexworker for RESERVED and RESERVATION_FAILED (hold window expiry). This story delivers the remaining notification triggers and adds the Provider and Payer notification channels.

Notification Triggers

State transition / event Recipient Channel Message
→ PENDING_PAYER Flexworker Push "Your reservation for [Listing Title] is awaiting approval from [Payer name]."
→ PENDING_PAYER Payer Desktop (SSE) "A reservation for [Resident name] at [Listing Title] requires your approval."
→ BOOKED (any path) Flexworker Push "Your housing at [Listing Title] is confirmed. Address: [confirmed address]. Check-in: [date]."
→ BOOKED (any path) Provider Desktop (SSE) "New confirmed booking: [Resident name] at [Listing Title], [period start] – [period end]."
→ BOOKED (indirect payment) Payer Desktop (SSE) "Booking confirmed: [Resident name] at [Listing Title], [period start] – [period end]."
→ RESERVATION_FAILED (Payer rejected) Flexworker Push "Your reservation for [Listing Title] was rejected by [Payer name]."
→ RESERVATION_FAILED (Payer timeout) Flexworker Push "Your reservation for [Listing Title] was not approved in time and has been released."
→ RESERVATION_FAILED (Payer rejected or timeout) Payer Desktop (SSE) "Reservation for [Resident name] at [Listing Title] has been released."
→ CANCELLED (pre-BOOKED, any actor) Flexworker Push "Your reservation for [Listing Title] has been cancelled."
→ CANCELLED (pre-BOOKED, from PENDING_PAYER) Payer Desktop (SSE) "Reservation for [Resident name] at [Listing Title] has been cancelled by the Resident."
→ CANCELLED (BOOKED, any actor) Flexworker Push "Your booking for [Listing Title] has been cancelled. Reason: [reason]."
→ CANCELLED (BOOKED, any actor) Provider Desktop (SSE) "Booking for [Resident name] at [Listing Title] has been cancelled. Reason: [reason]."
→ CANCELLED (BOOKED, indirect payment) Payer Desktop (SSE) "Booking for [Resident name] at [Listing Title] has been cancelled. Reason: [reason]."
→ CHECKED_IN Resident Push "You have been checked in at [Listing Title]. Your accommodation is ready."
→ ENDED Provider Desktop (SSE) "[Resident name] has checked out from [Listing Title]."
→ ENDED Resident Push "You have successfully checked out from [Listing Title]. Thank you."

Previously delivered in Story 3.5 basic (not repeated here): → RESERVED (Flexworker push) and → RESERVATION_FAILED via hold window expiry (Flexworker push).

Acceptance Criteria

  1. Provider and Payer desktop notifications (SSE)
  2. Provider Admin and Payer Admin each display a notification panel (e.g., a bell icon with a badge count) that shows incoming events in real time.
  3. When a relevant booking event occurs, a desktop notification appears without requiring the user to reload the page.
  4. Unread notifications are marked; the badge count decrements when the user opens the panel.
  5. Each notification entry shows: event description, Resident name (if applicable), listing title, and timestamp.
  6. Notifications persist in the panel until dismissed by the user; they are not lost on page reload.

  7. Push notification content rules

  8. The listing title in push notifications is the ListingUnit.title value at the time the notification is sent.
  9. Payer name is the payer_name stored in the session context at reservation creation.
  10. Confirmed address in the BOOKED push notification uses the confirmed_address field set on the Booking record.
  11. Cancellation reason uses the reason recorded on the Booking cancellation.
  12. All notifications are in English.

  13. Delivery guarantees

  14. Push notifications (Flexworker): delivered via the Cruits app push mechanism (same channel as Story 3.5 basic). Best-effort delivery; no retry logic in this iteration.
  15. Desktop SSE (Provider / Payer): delivered to all currently open sessions for that user. If the user is not online when an event occurs, the notification appears when they next load their Admin panel (stored server-side until dismissed).

  16. Notification for Provider: BOOKED bookings from any payment path

  17. The Provider receives a BOOKED desktop notification for every booking under their listings, regardless of payment form.

Dependencies

  • Story 3.3 (Provider Confirmation) — PENDING_PROVIDER → BOOKED transition triggers Provider and Flexworker notifications.
  • Story 3.4 (Payer Confirmation) — PENDING_PAYER transitions trigger Payer and Flexworker notifications.
  • Story 3.7 (Pre-BOOKED Cancellation) — → CANCELLED triggers Flexworker notification.
  • Story 3.9 (BOOKED Cancellation) — → CANCELLED (from BOOKED) triggers all-party notifications.
  • Story 3.8 (WGV Compliance) — → ENDED triggers Provider and Flexworker notifications.
  • Story 5.1 (Active Residents View) — Provider Admin panel must include the SSE notification component.
  • Story 6.1 (Payer Residents View) — Payer Admin panel must include the SSE notification component.

Out of Scope

  • Email fallback for push notifications (post-MVP per FR-43).
  • Notification preferences configuration per user (post-MVP).
  • Notifications for PENDING_PROVIDER state (this state is traversed instantly for non-integrated listings and produces no user-visible event).

Story 3.7: Pre-BOOKED Cancellation

Epic: Epic 3 — Reservation & Booking Flow

User Story

As a Flexworker, I want to cancel my reservation at any point before it reaches BOOKED state, So that the unit hold is released and I am not committed to housing I no longer need.

Functional Requirements Covered

FR Requirement
FR-75 Flexworker cancels a booking in any pre-BOOKED state; hold released; status → CANCELLED

Pre-BOOKED States

A Flexworker may cancel a booking while it is in any of the following states: - RESERVED - PENDING_PROVIDER - PENDING_PAYER

(SIGNING — deferred to Iteration 4.)

Acceptance Criteria

  1. Cancellation action in Booking Status View
  2. The Booking Status View in the Cruits app (Story 3.6) includes a "Cancel reservation" button for bookings in any pre-BOOKED state.
  3. Tapping the button shows a confirmation dialog: "Are you sure you want to cancel your reservation for [Listing Title]? This action cannot be undone." The worker must confirm to proceed.
  4. No reason is required for pre-BOOKED cancellations.

  5. On cancellation

  6. The booking transitions to CANCELLED.
  7. The unit hold is released immediately: the slot (concrete mode) or the pool slot (pool mode) becomes available again for new reservations.
  8. A BookingStateTransition entry is recorded: from_status = [current state], to_status = CANCELLED, actor = worker.
  9. The Flexworker receives a push notification (Story 3.5 full).

  10. Cancellation during PENDING_PAYER

  11. If the Payer has not yet acted on the reservation, the worker may cancel even while the booking is awaiting Payer approval.
  12. After cancellation, the Payer is not required to act; the booking is terminal.
  13. Because the Payer has already seen and acted on this reservation (it reached PENDING_PAYER), the Payer receives a desktop (SSE) notification when the Resident cancels at this stage (Story 3.5 full). For bookings cancelled in RESERVED or PENDING_PROVIDER state, no Payer notification is sent.

  14. Terminal state guard

  15. A booking already in a terminal state (RESERVATION_FAILED, CANCELLED, ENDED) cannot be cancelled again. Attempting to do so is a no-op; the system returns an appropriate error.

  16. Booking Status View — cancelled state display

  17. CANCELLED"Reservation cancelled""You have cancelled your reservation for [Listing Title]. You may search for another listing."
  18. A "Browse listings" button guides the worker back to the listing list.

Dependencies

  • Story 3.2 (Reservation Creation) — Booking must exist.
  • Story 3.6 (Booking Status View) — the Cancel button is added to the existing status screen.
  • Story 3.5 (full) — cancellation notification sent to Flexworker.

Out of Scope

  • Provider or Payer initiating pre-BOOKED cancellation — only the Flexworker can cancel pre-BOOKED.
  • Reason required for pre-BOOKED cancellation — reason is only required for BOOKED cancellations (Story 3.9).

Story 3.8: WGV Compliance: ENDED Trigger, Overstay & Period-End Notification

Epic: Epic 3 — Reservation & Booking Flow

User Story

As a Provider (and Platform Admin), I want bookings to transition to ENDED only on an explicit checkout action, to be notified — along with the Resident — when a booking period ends without checkout, to be able to flag a booking as Overstay, and for Residents to receive advance period-end reminder notifications, So that the Housing Module meets all obligations under Wet goed verhuurderschap (WGV) and housing law art. 7:231 BW regarding tenancy continuity and resident rights.

Functional Requirements Covered

FR Requirement
FR-80 The system must NOT automatically transition a booking to ENDED at period_end; only an explicit actor action triggers the ENDED transition
FR-81 When period_end passes without checkout: notification to Provider and Resident; booking remains BOOKED; Provider may flag the booking as Overstay
FR-83 Period-end notification: sent 28 days before period_end for stays of 28 days or longer; included in the BOOKED confirmation notification for stays shorter than 28 days

WGV Compliance Rationale

Under Dutch housing law (art. 7:231 BW) and WGV, a tenancy cannot be ended unilaterally without explicit notice and acknowledgement. An automatic system-triggered checkout would not constitute a valid termination of tenancy. All checkout transitions must therefore be actor-initiated.

Acceptance Criteria

  1. No automatic ENDED transition (FR-80)
  2. The system does not automatically transition any booking to ENDED when period_end is reached.
  3. A booking in CHECKED_IN state on or after period_end remains in CHECKED_IN state until an explicit checkout action is taken by the Resident or the Provider.
  4. This rule has no exceptions and no configurable override.

  5. Resident self-checkout (Cruits app)

  6. The Booking Status View in the Cruits app shows a "Check out" button for bookings in CHECKED_IN state only. The button is not available in BOOKED state — the Resident must wait for the Provider to mark them as checked in (Story 3.10).
  7. The button is not restricted to dates on or after period_end — early checkout is permitted.
  8. Tapping the button shows a confirmation dialog: "Are you sure you want to check out from [Listing Title]? This will end your booking." The Resident must confirm.
  9. On confirmation, the booking transitions to ENDED. checked_out_at is set to the current date and time; checkout_actor = worker.

  10. Provider checkout from Active Residents View (Provider Admin)

  11. The Active Residents View (Story 5.1) includes a "Mark as checked out" action for each booking in CHECKED_IN state.
  12. On confirmation, the booking transitions to ENDED. checked_out_at is set to the current date and time; checkout_actor = provider.
  13. A confirmation dialog is shown before the action is executed.

  14. ENDED state — notifications

  15. When a booking transitions to ENDED, the Flexworker receives a push notification and the Provider receives a desktop (SSE) notification (Story 3.5 full).

  16. Overstay detection and notification (FR-81)

  17. When a booking in CHECKED_IN state passes its period_end date without a checkout action, the system detects this and sends:
    • A desktop (SSE) notification to the Provider: "Booking for [Resident name] at [Listing Title] has passed its end date. The booking remains active — no automatic checkout has occurred."
    • A push notification to the Flexworker: "Your booking period at [Listing Title] has ended ([period_end date]). Please check out or contact the housing manager if you intend to extend your stay."
  18. These overstay notifications are sent once — on the morning of the first day after period_end — and are not repeated on subsequent days.
  19. The booking remains in BOOKED state and continues to occupy the unit slot until a checkout action is taken.

  20. Provider: Overstay flag (FR-81)

  21. After a booking has passed its period_end without checkout, the Active Residents View (Story 5.1) shows a "Flag as overstay" action for that booking.
  22. Flagging records is_overstay = Yes and overstay_flagged_at on the Booking record.
  23. The Overstay flag is informational only and has no effect on booking state or unit availability.
  24. Once flagged, the action changes to "Remove overstay flag" (reversible).
  25. The flag is visible in the Active Residents View as a badge or label on the booking row.

  26. Period-end notification — 28-day advance reminder (FR-83)

  27. For bookings with a stay of 28 days or more (period_end − period_start ≥ 28 days): the system sends a push notification to the Flexworker exactly 28 days before period_end: "Your housing at [Listing Title] ends on [period_end date]. If you need to extend your stay, contact the housing manager."
  28. For bookings with a stay shorter than 28 days: the BOOKED confirmation notification (Story 3.5 full) includes a brief note: "Your booking period ends on [period_end date]. You will need to arrange alternative accommodation after this date."
  29. The 28-day advance notification is sent once and is not repeated.

Dependencies

  • Story 3.3 (Provider Confirmation) — bookings must reach BOOKED before checkout is relevant.
  • Story 5.1 (Active Residents View) — Provider checkout and Overstay flag actions are in Provider Admin.
  • Story 3.5 (full) — ENDED transition notifications.

Out of Scope

  • Automatic lease extension or renewal flow (post-MVP).
  • Financial penalties for overstay (logging only; no penalty calculation in MVP).
  • WGV compliance for GDPR consent (Story 1.5 — deferred).

Story 3.9: BOOKED Booking Cancellation

Epic: Epic 3 — Reservation & Booking Flow

User Story

As a Flexworker (and Provider, Payer), I want to cancel a confirmed booking, providing a reason and acknowledging that I take responsibility as defined by the accommodation contract and applicable legislation, So that the unit is released, all parties are notified of the cancellation and its stated reason, and the event is permanently recorded in the booking history.

Functional Requirements Covered

FR Requirement
FR-76 Flexworker, Provider, and Payer may cancel a BOOKED booking; reason and disclaimer acknowledgement required; cancellation window configurable per Provider and Payer

Cancellation Window Configuration

The minimum notice period required for a BOOKED cancellation is configurable per Provider and per Payer.

Provider-level setting — added to Provider Admin Settings (Story 1.10 extension):

Setting Description
Cancellation notice period Minimum number of days before period_start by which a cancellation must be submitted. If the check-in date is within this window, cancellation is blocked. If not set, no minimum is enforced.

The Payer-level cancellation_window_days field is already defined in the Payer entity (Story 2.1).

Acceptance Criteria

  1. Cancellation window enforcement
  2. Before allowing a BOOKED cancellation, the system checks the applicable cancellation notice period:
    • For Flexworker or Provider cancellations: the Provider's configured cancellation notice period applies.
    • For Payer cancellations: the Payer's cancellation_window_days applies.
    • If the booking's period_start is within the configured window from today, the cancellation is blocked. The actor sees a clear error: "Cancellation is no longer permitted within [N] days of the check-in date. Please contact the housing manager."
  3. If no window is configured for the applicable actor type, no minimum is enforced and the cancellation may proceed at any time before period_start.

  4. Reason required

  5. All BOOKED cancellations require the initiating actor to provide a reason. Submitting without a reason is not permitted.
  6. Reason options may be presented as a dropdown of predefined options with an optional free-text field, or as a free-text field only — this is an implementation decision for the design team.

  7. Disclaimer acknowledgement

  8. Before submitting the cancellation, the actor must acknowledge a disclaimer: "I understand that by cancelling this confirmed booking, I take responsibility for any consequences as defined by the accommodation contract and applicable legislation. This action cannot be undone."
  9. The actor must tick a checkbox or tap an explicit acknowledgement button. Submitting without acknowledgement is not permitted.

  10. On cancellation

  11. The booking transitions from BOOKED to CANCELLED.
  12. The unit is released: the slot (concrete mode) or the pool slot (pool mode) becomes available again for new reservations.
  13. The cancellation reason is recorded in the BookingStateTransition entry for this transition.
  14. A BookingStateTransition entry is recorded: from_status = BOOKED, to_status = CANCELLED, actor = [worker / provider / payer], notes = [reason].

  15. Entry points by actor

  16. Resident: "Cancel booking" action on the Booking Status View in the Cruits app. The button is visible for BOOKED bookings only before period_start. On or after period_start, the Resident has moved in and the only available action is checkout (Story 3.8). If the cancellation window has already passed, the button is replaced by "Request cancellation" (flow TBD — new story).
  17. Provider: "Cancel booking" action in the Active Residents View (Story 5.1) for each BOOKED row.
  18. Payer: "Cancel booking" action in the Payer Residents View (Story 6.1) for each BOOKED row.

  19. Notifications

  20. All parties are notified per the cancellation notification rules in Story 3.5 full.

  21. Financial penalties (out of scope)

  22. No financial penalty is calculated or applied in MVP. The reason and actor are logged for manual reference.

Dependencies

  • Story 3.3 (Provider Confirmation) — booking must be in BOOKED state.
  • Story 5.1 (Active Residents View) — Provider-initiated BOOKED cancellation entry point.
  • Story 6.1 (Payer Residents View) — Payer-initiated BOOKED cancellation entry point.
  • Story 3.5 (full) — cancellation notifications to all parties.

Out of Scope

  • Financial penalties on cancellation (post-MVP; only logged).
  • Cancellation policy templates or DocuSign-based cancellation agreements (post-MVP).

Story 5.1: Active Residents View

Epic: Epic 5 — Provider Operations

User Story

As a Provider, I want to see a list of all current residents across my properties — with their booking status and payment arrangement per booking — via Provider Admin, So that I have operational visibility over who is occupying my units and can act on bookings that require my attention.

Functional Requirements Covered

FR Requirement
FR-29 Provider Admin UI — Active Residents section
FR-30 Active Residents view: Resident name, unit, period, booking status, payment arrangement

Wireframes

Provider Admin — Active Residents screen: [TBD — to be added by the design team for Iteration 3.]

Acceptance Criteria

  1. Active Residents section in Provider Admin
  2. Provider Admin includes an Active Residents section accessible from the main navigation.
  3. The section displays BOOKED and CHECKED_IN resident bookings (booking_type = resident) across all of the Provider's listings. Pre-confirmation bookings (RESERVED, PENDING_PROVIDER, PENDING_PAYER) are visible in the Booking List (Story 1.7), not here.
  4. Provider blocks (booking_type = provider_block) are not shown here. They remain visible only in the Booking List section (Story 1.7).

  5. Table columns

  6. Each row displays:
    • Resident full name
    • Listing title and unit identifier (floor / room / bed for concrete-mode; "Pool" for pool-mode)
    • Period: check-in and check-out dates
    • Booking status with a visual status badge
    • Payment arrangement: "Direct" for worker_direct; "[Payer name]" for indirect payment forms
  7. The table is sorted by check-in date ascending by default (soonest arrivals first).

  8. Status badge labels

Booking status Badge label
BOOKED Confirmed
CHECKED_IN Checked in
CHECKED_IN (past period_end, no checkout) Overstay
  1. Filters
  2. The Provider can filter the list by:

    • Listing (dropdown of the Provider's listings)
    • Status (All / Confirmed / Checked in / Overstay)
  3. Actions per row

Booking status Available actions
BOOKED "Check in" (Story 3.10), "Cancel booking" (Story 3.9)
CHECKED_IN "Mark as checked out" (Story 3.8)
CHECKED_IN past period_end "Mark as checked out", "Flag as overstay" / "Remove overstay flag" (Story 3.8)
  • Each action that changes state requires a confirmation dialog before it is executed.

  • Real-time updates

  • When a booking status changes (e.g., a Payer approves a PENDING_PAYER booking and it becomes BOOKED), the Active Residents View reflects the updated status without requiring a page reload. This is delivered via the same SSE channel as the desktop notifications (Story 3.5 full).

  • Access control

  • A Provider can only view bookings for their own listings. Bookings from other Providers are not visible.

Dependencies

  • Story 3.3 (Provider Confirmation) — BOOKED bookings must exist.
  • Story 1.6 (ListingUnit) — listing and unit data.
  • Story 3.8 (WGV Compliance) — checkout and overstay actions.
  • Story 3.9 (BOOKED Cancellation) — cancel action.
  • Story 3.5 (full) — SSE channel for real-time updates and notifications panel.

Out of Scope

  • Booking archive (past bookings in ENDED/CANCELLED) — Story 5.2, Iteration 6.
  • Ghost Alarm management — Story 5.3, Iteration 6.
  • In-app messaging with residents — Story 9.1, Iteration 6.

Story 6.1: Payer Residents View & Reservation Management

Epic: Epic 6 — Payer Operations

User Story

As a Payer, I want to view all active and historical bookings for my residents with their booking and payment status, and to confirm or reject reservations awaiting my approval — via Payer Admin, So that I have full visibility over housing for workers in my care and can act on pending approvals in one place.

Functional Requirements Covered

FR Requirement
FR-34 Payer Admin UI — Residents section (Payer Admin initialized in this story)
FR-35 Payer Residents view: Resident name, unit, Provider, period, booking status, payment arrangement
FR-15 Payer confirms or rejects a PENDING_PAYER booking from Payer Admin

Wireframes

Payer Admin — Residents screen (full view and pending approvals): [TBD — to be added by the design team for Iteration 3.]

Payer Admin Initialization

This story introduces the Payer Admin application — the web interface used by Payer users. Payer Admin is hosted within the same housing-admin-fe application as Provider Admin but is accessible only to users with the Payer role.

Payer Admin navigation in this iteration: - Residents (this story — fully implemented) - Settlements (Story 6.2, Iteration 6 — shown as a placeholder / coming soon) - Notifications (Story 6.3, Iteration 6 — placeholder)

Payer User accounts: Individual Payer Admin login accounts are defined and created in this story. Each Payer organisation (the Payer entity defined in Story 2.1) may have one or more Payer User accounts. Platform Admin creates Payer User accounts via Django Admin and assigns them to a Payer organisation. A Payer User can only view and act on bookings associated with their Payer organisation.

Acceptance Criteria

  1. Payer Admin — entry and navigation
  2. Payer users log in to the housing-admin-fe application using their credentials (created by Platform Admin).
  3. After login, they are directed to Payer Admin — a view restricted to the Payer role. Provider Admin content is not visible or accessible to Payer users.
  4. The Residents section is accessible from the Payer Admin navigation.

  5. Residents table

  6. The Residents section displays all bookings for Residents associated with this Payer — both active (non-terminal) and historical (ENDED, CANCELLED, RESERVATION_FAILED).
  7. Each row shows:
    • Resident full name
    • Provider name
    • Listing title and unit identifier (concrete: floor / room / bed; pool: "Pool")
    • Period: check-in and check-out dates
    • Booking status with a visual status badge
    • Payment arrangement label: "Direct" or "[Payer name] pays"
  8. The table is sorted by booking creation date descending by default (most recent first).

  9. Pending Approvals — highlighted view

  10. Bookings in PENDING_PAYER state are visually highlighted (e.g., a dedicated "Pending approval" tab or a distinct section at the top of the table).
  11. The count of pending approvals is shown as a badge in the Payer Admin navigation.

  12. Confirm action

  13. The Payer can confirm a PENDING_PAYER booking by clicking a "Confirm" button on the booking row.
  14. A confirmation dialog is shown: "Confirm reservation for [Resident name] at [Listing Title]? The booking will be confirmed and the unit will be allocated."
  15. On confirmation, the booking transitions to BOOKED (per Story 3.4 AC 2). The row updates immediately.

  16. Reject action

  17. The Payer can reject a PENDING_PAYER booking by clicking a "Reject" button on the booking row.
  18. A rejection dialog is shown requiring a reason for rejection. The Payer must enter or select a reason before submitting.
  19. On rejection, the booking transitions to RESERVATION_FAILED (per Story 3.4 AC 3). The row updates immediately.

  20. Cancel action (BOOKED bookings)

  21. The Payer can cancel a BOOKED booking from the Residents table (Story 3.9 entry point for Payer).
  22. The same reason and disclaimer requirements as Story 3.9 apply.

  23. Real-time updates

  24. When a new PENDING_PAYER booking appears for this Payer, a desktop (SSE) notification is shown (Story 3.5 full) and the pending approvals badge updates without requiring a page reload.

  25. Filters

  26. The Payer can filter the Residents table by:

    • Booking status (All / Pending approval / Active / Historical)
    • Provider (dropdown of Providers with whom the Payer has an agreement)
  27. Access control

  28. A Payer user can only view bookings for Residents associated with their Payer organisation. Bookings for other Payers are not visible.
  29. Platform Admin has read access to all Payer Admin views via Django Admin.

Dependencies

  • Story 2.1 (Context Handshake) — Payer entity and payer_id on Booking records.
  • Story 1.2 (Provider Admin frontend) — Payer Admin is hosted in the same application; same design system.
  • Story 3.4 (Payer Confirmation Flow) — confirm and reject mechanics are defined there.
  • Story 3.9 (BOOKED Cancellation) — cancel action entry point.
  • Story 3.5 (full) — SSE notification channel and desktop notification panel.

Out of Scope

  • Settlements view (Story 6.2 — Iteration 6).
  • Ghost Alarm management (Story 6.3 — Iteration 6).
  • Automatic invoice generation (Story 6.4 — Iteration 6).

Story 3.10: Provider Check-in

Epic: Epic 3 — Reservation & Booking Flow

User Story

As a Provider, I want to mark a Resident as checked in via Provider Admin when they arrive, So that the booking transitions to CHECKED_IN state, the Resident is notified, and the checkout action becomes available.

Functional Requirements Covered

FR Requirement
FR-91 Provider marks a BOOKED booking as checked in via Provider Admin; booking transitions to CHECKED_IN; Resident notified

Booking Flow — Check-in

BOOKED  ──→  [Provider marks check-in in Provider Admin]  ──→  CHECKED_IN
CHECKED_IN  ──→  [Explicit checkout by Resident or Provider]  ──→  ENDED

Acceptance Criteria

  1. Check-in action in Active Residents View
  2. The Active Residents View (Story 5.1) includes a "Check in" action for each booking in BOOKED state.
  3. A confirmation dialog is shown: "Mark [Resident name] as checked in at [Listing Title]?"
  4. On confirmation, the booking transitions to CHECKED_IN. A BookingStateTransition entry is recorded: from_status = BOOKED, to_status = CHECKED_IN, actor = provider.

  5. Resident notification

  6. When the booking transitions to CHECKED_IN, the Resident receives a push notification (Story 3.5 full): "You have been checked in at [Listing Title]. Your accommodation is ready."
  7. The Resident's Booking Status View updates to the CHECKED_IN status screen.

  8. Checkout only from CHECKED_IN

  9. The checkout action (Resident self-checkout via Cruits app and Provider checkout via Provider Admin) is only available from CHECKED_IN state.
  10. In BOOKED state, no checkout action is shown to any actor.

  11. Cancellation not available after check-in

  12. Once a booking reaches CHECKED_IN, cancellation is not available to any actor. The only available action is checkout.
  13. The "Cancel booking" action is not shown for CHECKED_IN bookings in any UI.

  14. Booking Status View — Resident (Cruits app)

  15. When a booking transitions to CHECKED_IN, the Resident sees the CHECKED_IN status screen (screen S-12 in the UX spec).
  16. Status label: "Checked in"
  17. Body text: "You are checked in at [Listing Title]. Check-out: [date]."
  18. Button: "Check out" (opens S-11).

Dependencies

  • Story 5.1 (Active Residents View) — Check-in action is added to this view.
  • Story 3.5 (full) — Check-in notification to Resident.
  • Story 3.8 (WGV Compliance) — Checkout from CHECKED_IN state.
  • Story 3.9 (BOOKED Cancellation) — Cancel only available from BOOKED, not CHECKED_IN.

Out of Scope

  • Resident self-check-in via Cruits app (post-MVP).
  • Automated check-in via door access or IoT integrations (post-MVP).
  • Unit assignment for pool-mode bookings at check-in (Story 7.2, Iteration 5).

Summary Table

Story Epic FRs Deliverable
3.3 Epic 3 FR-12, FR-14, FR-16, FR-17, FR-21 Provider confirms or rejects PENDING_PROVIDER via Provider Admin; optional auto-confirm setting; provider_block → BOOKED directly; confirmed address recorded; full state history
3.4 Epic 3 FR-15, FR-16 Payer confirms or rejects PENDING_PAYER; auto-approve gate; PENDING_PAYER window enforcement
3.5 (full) Epic 3 FR-18, FR-43 Full notification coverage — all remaining state transitions for Flexworker (push) and Provider / Payer (SSE desktop)
3.7 Epic 3 FR-75 Pre-BOOKED cancellation by Flexworker from Booking Status View; hold released; state logged
3.8 Epic 3 FR-80, FR-81, FR-83 No automatic ENDED; explicit Resident and Provider checkout; overstay notification and flag; 28-day period-end reminder
3.9 Epic 3 FR-76 BOOKED cancellation by any actor; reason + disclaimer required; cancellation window enforced; all parties notified
3.10 Epic 3 FR-91 Provider Check-in — Provider marks Resident as checked in via Active Residents View; booking → CHECKED_IN; Resident notified
5.1 Epic 5 FR-29, FR-30 Provider Admin — Active Residents View: BOOKED and CHECKED_IN resident bookings; check-in, checkout, cancel, and overstay actions
6.1 Epic 6 FR-34, FR-35, FR-15 Payer Admin initialized; Payer Residents View: all bookings with pending approval highlight; confirm and reject actions

Wireframes Required for Iteration 3

The following new wireframe screens must be designed before or during development of Iteration 3. All screens should follow the design system established in Iteration 1 (Provider Admin) and the Cruits app designs.

# Screen Story Notes
W-1 Provider Admin — Active Residents View 5.1 Table with BOOKED booking rows, status badges (Active / Overstay), action buttons (checkout, cancel, overstay flag). Requires filter bar (by listing, by status).
W-1a Provider Admin — Booking List: PENDING_PROVIDER confirmation 3.3 Confirm and Reject action buttons on PENDING_PROVIDER rows in the Booking List. Confirm shows booking summary modal; Reject requires reason input.
W-2 Provider Admin — Mark Checkout dialog 3.8 Confirmation modal for "Mark as checked out" action.
W-3 Provider Admin — Cancel BOOKED booking dialog 3.9 Modal with reason input (dropdown or free-text) and disclaimer checkbox.
W-4 Provider Admin — Notification panel 3.5 (full) Bell icon with badge; sliding or dropdown panel showing real-time events.
W-5 Payer Admin — Residents View (full table) 6.1 Full-width table with Pending Approvals section at top; filters; status badges.
W-6 Payer Admin — Confirm Reservation dialog 3.4 / 6.1 Confirmation modal with booking summary.
W-7 Payer Admin — Reject Reservation dialog 3.4 / 6.1 Modal with required reason input.
W-8 Payer Admin — Cancel BOOKED booking dialog 3.9 Same structure as W-3.
W-9 Payer Admin — Notification panel 3.5 (full) Same structure as W-4; Payer-specific events.
W-10 Cruits App — Booking Status View: BOOKED state 3.3 Updated status screen showing confirmed address, check-in/out dates, "Check out" button, "Cancel booking" button.
W-11 Cruits App — Booking Status View: PENDING_PAYER state 3.4 Status screen showing "Awaiting [Payer name] approval" with explanatory text.
W-12 Cruits App — Pre-BOOKED cancellation confirmation 3.7 Simple confirmation dialog: "Are you sure you want to cancel?"
W-13 Cruits App — BOOKED cancellation flow 3.9 Multi-step screen: reason selection → disclaimer acknowledgement → confirmation.
W-14 Cruits App — Self-checkout confirmation 3.8 Confirmation dialog for "Check out" action.
W-15 Provider Admin — Settings: Cancellation notice period 3.9 Additional field in Provider Settings screen (extension of Story 1.10).
W-16 Provider Admin — Settings: Auto-confirm reservations 3.3 Yes/No toggle in Provider Settings screen (extension of Story 1.10).
W-17 Provider Admin — Check-in confirmation dialog 3.10 Modal confirming check-in action for a specific Resident.
W-18 Cruits App — Booking Status View: CHECKED_IN state 3.10 Status screen showing "Checked in" label, confirmed address, check-out date, and "Check out" button.

Deferred from Iteration 3

Story Title Deferred to
4.1 Contract Template Setup & Signing Mode Configuration Iteration 4
4.2 DocuSign Signing Envelope: Per-Booking Mode Iteration 4
4.3 Framework Contract Mode Iteration 4
4.4 Signed Document Storage & Access Iteration 4
1.9 Full Payment Terms Configuration (prepay/postpay/custom schedule) Iteration 4
5.2 Booking Archive (Provider) Iteration 6
5.3 Ghost Alarm Management (Provider) Iteration 6
6.2 Settlements View (Payer) Iteration 6
6.3 Ghost Alarm Management (Payer) Iteration 6
7.1 Booking Status Webhook Delivery to Cruits Iteration 5

Comments