Skip to main content

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

StateWhat it means
reservedBooking exists for a future date range; deposit held
activeCustomer has picked up the bike; counting time
returnedBike is back; rental closed; final charges settled
overduePast the return time + grace period; customer not contacted yet
cancelledBooking cancelled before pickup; deposit refunded per policy
claimedBike presumed lost; insurance claim or write-off

Side effects of transitions

TransitionSide effects
* → reservedInsert booking row; Stripe Checkout session for deposit; bike's calendar marked busy
reserved → activeBike calendar still busy; pickup recorded; signature + ID check stored
reserved → cancelledRefund deposit per policy; calendar freed
active → returnedDamage assessment; final transaction created; deposit captured or refunded
active → overdueCron-detected; SMS to customer; manager notified
overdue → claimedManager 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