Rental lifecycle
A rental moves from a future-tense booking to a present-tense active loan to a closed return. Each transition is auditable; financial side-effects happen at booking time (deposit) and return time (final charge).
Drafted from planning · v0.1
Slice 7 (Rentals) is schema-complete; UI is mockup-only.
States and transitions
State definitions
| State | What it means |
|---|---|
reserved | Booking exists for a future date range; deposit held |
active | Customer has picked up the bike; counting time |
returned | Bike is back; rental closed; final charges settled |
overdue | Past the return time + grace period; customer not contacted yet |
cancelled | Booking cancelled before pickup; deposit refunded per policy |
claimed | Bike presumed lost; insurance claim or write-off |
Side effects of transitions
| Transition | Side effects |
|---|---|
* → reserved | Insert booking row; Stripe Checkout session for deposit; bike's calendar marked busy |
reserved → active | Bike calendar still busy; pickup recorded; signature + ID check stored |
reserved → cancelled | Refund deposit per policy; calendar freed |
active → returned | Damage assessment; final transaction created; deposit captured or refunded |
active → overdue | Cron-detected; SMS to customer; manager notified |
overdue → claimed | Manager action; insurance flow starts; bike marked unavailable in fleet |
Deposit handling
Two patterns supported per shop:
- Auth-and-capture: deposit is authorized at booking (not charged), captured on return if damage
- Charge-and-refund: deposit is charged at booking, refunded at return if no damage
The shop chooses in shop_config.rental_deposit_mode. Helm wraps Stripe's PaymentIntent accordingly.
Damage assessment
At return, the staff member completes a quick form:
- Damage: yes/no
- If yes: severity (minor scratch / major repair / total loss); estimated cost
- Late: hours past return time
- Late fee per shop's
shop_config.rental_late_fee_per_hour_cents
These produce a final charge that's applied against the deposit. Net amount is captured or refunded.
Audit
Every transition writes:
audit_events:action='rental.*'(e.g.,rental.booked,rental.pickup,rental.returned)audit_mutations: before/after of the rental row
Signature image and ID-check photo (when applicable) are stored in R2 and referenced from the audit detail.
See also
- Slice 7 — Rentals
- Transaction lifecycle — deposit/final-charge transactions follow this
- Data flow diagrams
- ADR-0011: Stripe Terminal