PRD 04 — Appointments
Status: draft · Priority tier: P0 · Size: L · Depends on: Locations & Facilities/Equipment, Staff shift hours, Customers, Pricing Plans (packages/memberships), Sales/Payments, PRD 03 Schedule
1. Summary
One-to-one (or small-group) bookable services: a catalogue with price variants by duration, staff and resource assignment and booking rules; studio-wide appointment settings; back-office booking (walk-in, pay now/later) with a booking drawer; and recurring appointments.
2. Problem & goals
Studios sell private sessions whose availability depends on staff shifts and rooms/equipment. Goals: create a service in <= 4 wizard steps; book an appointment from the calendar in <= 8 clicks (original ~12); availability never offers a staff/resource that is off-shift or already busy. Non-goals: CSV import (later); waiver form authoring; storefront booking UI (Storefront PRD); shift-hours screen itself (Staff/Settings PRD, referenced as dependency).
3. Users & roles
Owner/manager (catalogue, settings), front desk (book, check in, cancel), staff (view own), customer (books on storefront, self-cancels within rules).
4. Scope
In (v1)
- must: categories; service list (status/category filters); 4-step service wizard; price variants (fixed/free, duration, private variant); staff and resource (facility/equipment) assignment.
- must: booking rules: interval, buffer, rolling/fixed booking window, minimum notice, parallel customers.
- must: appointment settings (max concurrency per facility, self-cancel, late cancel, check-in mode/window, show remaining slots).
- must: back-office booking drawer; walk-in; pay now/later; booking drawer with Check in, Cancel, Late cancel; bookings list with date presets.
- should: recurring appointment booking; customer eligibility (all/specific); prerequisite; custom cancellation policy; publish later; SEO/slug; email/SMS customer; Activities log.
- could: CSV bulk import (flat-rate only, <=10 MB, <=1000 rows); waiver forms tab; show ratings. Later: group-appointment waitlist; peak-hour/time pricing rules (release notes mention them). Out: none.
5. User stories
- US-04-01 As an owner, I want to define a service with several durations and prices so that customers choose 30/60 min at different rates.
- US-04-02 As an owner, I want to restrict a service to certain staff and rooms so that only qualified, equipped resources are bookable.
- US-04-03 As an owner, I want buffer time and minimum notice so that I have time to reset and am not surprised.
- US-04-04 As a front desk user, I want to book a walk-in customer and collect payment later so that I do not slow the line.
- US-04-05 As a front desk user, I want to check in, cancel or late-cancel a booking from one drawer so that status and fees stay correct.
- US-04-06 As a front desk user, I want to book a customer for several dates at once so that regulars are quick to set up.
- US-04-07 As an owner, I want to stop two appointments overlapping in one room so that capacity is real.
- US-04-08 As a customer, I want to cancel myself within the allowed window so that I need not call.
6. Functional requirements
| ID | requirement | priority | evidence |
|---|---|---|---|
| FR-04-01 | Service list grouped by category: name, duration(s), Active/Hidden badge, created date; actions View Availability, View Pricing Plans; filters Status, Category; categories CRUD + sub-category. | must | S31, F13 |
| FR-04-02 | A category must exist before creating a service; wizard offers inline "create category" instead of a dead end (improvement). | must | S31, F13 |
| FR-04-03 | Step 1: name (<=250), calendar colour, short description (<=100), description (<=1000, required), image (required, JPG/PNG/GIF <10 MB), category (required) / sub-category, SEO title/description/slug. | must | S32, S32c |
| FR-04-04 | Step 2: one or more price variants {name, duration (hours+minutes, 15-min steps), price type fixed/free, price, private flag}; add/remove variants; at least one required. | must | S32d, S32e |
| FR-04-05 | Refund method for cancelled prepaid bookings: none / credit to store wallet. | should | S32d |
| FR-04-06 | Step 3: assign staff (toggle, select all) and/or resources (Equipment, Facility); at least one of staff or resource required. | must | S32f, S32h |
| FR-04-07 | Step 4: booking interval (e.g. 15 min), buffer before/after, booking window (rolling N weeks / fixed date range), minimum notice hours, parallel customers (pax), eligibility (all / specific customers), prerequisite flag. | should | S32i, S32j |
| FR-04-08 | Display options: show on online store (default on), publish later with date, custom cancellation policy. | should | S32l |
| FR-04-09 | Wizard chrome and status behaviour identical to class wizard (PRD 02): Next disabled until valid, Save as draft, Add => Active; duplicate, set inactive, delete (existing future bookings block delete or require cancel — assumption). | must | S32 |
| FR-04-10 | Availability computation: a slot is bookable if some assigned staff is on shift (shift assigned to that service) at that location, not blocked (time block), no overlapping booking beyond parallel-pax, resource free, within opening hours, interval/buffer/notice/window satisfied. | must | S209r, part 08 shifts, S13 |
| FR-04-11 | Setting: max concurrency per facility. Turning it on disables parallel-customer settings on services (confirm dialog). | should | S33, S33b |
| FR-04-12 | Setting: show remaining slots on fixed-fee services only; flat-rate (free/variant) never show counts. | should | S33 |
| FR-04-13 | Setting: check-in mode Manual or Auto; earliest check-in Anytime or N minutes before. | should | S33 |
| FR-04-14 | Setting: self-cancellation on/off (default on); late cancellation on/off (default on) with N-hour window. | must | S33 |
| FR-04-15 | Setting: show ratings and reviews on storefront. | could | S33 |
| FR-04-16 | Booking drawer (create): customer search or blank=walk-in; start date; location; service picker (name, duration, "from price"); price variant; calendar colour; start time (15-min steps, typeable); staff (only eligible staff); warning if outside opening hours; notify staff. | must | S34, S209m-q |
| FR-04-17 | Payment in drawer: pricing plan tabs All/Packages/Memberships with drop-in price and eligibility ("n credits left, required, not eligible"); When to pay: Pay now (select option -> Checkout) or Pay later (optional generate invoice -> Add booking). | must | S209s, S209t |
| FR-04-18 | Booking drawer (existing): header chips (Booked/Unpaid), ref + copy, Check in, kebab; customer card; tabs Booking details, Payment, Waiver forms, Activities; footer Cancel/Checkout. | must | S209v-y |
| FR-04-19 | Kebab: Email customer, SMS customer, Checked in, Cancel booking, Late cancel booking (late = inside window, may incur fee/forfeit). | must | S209z |
| FR-04-20 | Bookings list: grouped by date; time+duration, customer, service, staff, location, payment icon, status; filters search, date presets (Today default, Tomorrow, Yesterday, Next 7, Last 7/30, This/Last month, exact, custom), appointment, more filters; Reset; page size. | must | S30, S209zb, S209zc |
| FR-04-21 | Recurring appointment: customer, location, service(s)*, pick dates on month calendar, list per month, Review payment, Add bookings. All-or-nothing validation with per-date conflict report. | should | S13 recurring |
| FR-04-22 | Appointment blocks appear on the calendar (PRD 03) with customer, service, time, staff. | must | S209u |
| FR-04-23 | Customer self-cancel on storefront obeys setting FR-04-14; booked pack credits return on non-late cancel. | must | S33 |
| FR-04-24 | Bulk CSV import (flat-rate services, existing customers/walk-ins, <=10 MB, <=1000 rows) with template. | could | S35, F15 |
| FR-04-25 | Pay-later bookings create an unpaid invoice/transaction visible in Sales; marking paid updates drawer chip. | must | S209t, S209w |
7. Screens
| Our screen | Modelled on | States |
|---|---|---|
| Service list | S31 | no categories, empty, filled, filtered |
| Service wizard | S32 | per step invalid/valid; variants list; error; saving |
| Appointment settings | S33 | default, dirty, concurrency confirm dialog |
| Bookings list | S30, S209zc | empty (Today), filled, loading |
| Create-booking drawer | S34, S209m-t | empty, service picked, outside hours warning, no eligible staff, pay now/later |
| Booking detail drawer | S209v-z | booked, unpaid, checked in, cancelled |
| Recurring appointment page | S13 | no dates, dates picked, conflicts |
8. Flows
Create service: List > Add > wizard 4 steps > Add. Edge: no staff shifts => step 3 allowed but service not bookable; show warning. Book: Calendar Add > Appointment > customer (or skip) > service > date/time > staff > Pay later > Add booking (<= 8 clicks). Edge: outside opening hours warning (allow override for admin*); staff list empty when no shifts => hint linking to Shift hours. Cancel: drawer > kebab > Cancel booking, or Late cancel. Check in: Check In button.
9. Data model (guesses marked *)
- AppointmentService(id, name, colour, short_desc, description, images[], category_id, subcategory_id, seo, status, listing_visible, publish_at, interval_min, buffer_before, buffer_after, window_type rolling|fixed, window_weeks|from/to, min_notice_h, parallel_pax, eligibility all|specific, customer_ids[], is_prerequisite, cancel_policy, refund_method, staff_ids[], facility_ids[], equipment_ids[])
- PriceVariant(id, service_id, name, duration_min, price_type, price, is_private)
- AppointmentSettings(business_id, show_reviews, max_concurrency_per_facility, show_remaining_slots, checkin_mode, checkin_earliest_min, self_cancel, late_cancel, late_cancel_hours)
- AppointmentBooking(ref, customer_id|null, service_id, variant_id, location_id, staff_id, starts_at, ends_at, colour, status booked|checked_in|completed|cancelled|late_cancelled|no_show, payment_status unpaid|paid, pay_mode now|later, plan_used, transaction_id, notify_staff, series_id, events[])
- Shift (owned by Staff/Settings; consumed here): staff, location, weekday/date, start/end, service_ids.
10. Business rules & edge cases
- Walk-in (no customer) allowed; walk-in cannot use packages/memberships.
- Only variants that are not private are shown to customers; private variants admin-only.
- Pack credit eligibility is shown, not silently chosen.
- With max concurrency on, a facility hosts at most one appointment at a time regardless of pax.
- Late cancel inside window: flagged; fee/credit forfeit handling TBD.
- Past start times in create drawer: admin-allowed with warning*.
- Deleting a service with future bookings: blocked*.
- Time zone = business.
11. Permissions
Owner/manager: catalogue, settings, all bookings. Front desk: create/check-in/cancel/late-cancel bookings. Staff: view own bookings*. Customer: own self-cancel only. Matrix finalised in Roles PRD.
12. Acceptance criteria
- Given no category, when I open the wizard, then I can create a category inline.
- Given a service with variants 30 min/$40 and 60 min/$70, when booking, then picking 60 min blocks 60 min on staff and facility.
- Given buffer 10 min after, when a 10:00-11:00 booking exists, then 11:00 is unavailable and 11:10 is available (interval permitting).
- Given staff with no shift, then the staff picker shows no options and a hint to assign shifts.
- Given a customer left blank, when I add booking with Pay later, then a walk-in booking is created with unpaid status.
- Given self-cancel off, then the storefront shows no cancel option; admin can still cancel.
- Given late window 24h and a cancel at 10h, when I choose Cancel booking vs Late cancel, then the latter is recorded as late.
- Given max concurrency on and an existing booking in Room A, then a second booking at the same time in Room A is rejected.
- Given a recurring set of 4 dates where one conflicts, then none are saved and the conflicting date is reported.
13. Dependencies & open questions
Open questions
- Exact late-cancel consequences (fee? credit?) — not observed.
- Recurring appointment: rule-based (weekly) or hand-picked dates only? Only hand-picked calendar observed.
- How packages/memberships deduct credits (credits per variant?) — row shows "credit required" but rule not observed.
- Prerequisite semantics ("set as prerequisite") not observed.
- Waiver forms: only an empty tab observed.
- Pay-now checkout completion not observed (blocked by empty staff in recon, later seen for pay-later only).
- Time-based/peak pricing rules mentioned in release notes only.
- Plan gating of any of these: pricing tier TBD.
14. Evidence
See evidence/evidence.md.
