PRD 07 — Customers (directory, profile, tags, groups, notes, account rules)
Status: draft · Priority tier: P0 (list, add, detail core, suspend/delete) / P1 (notes, tags, waivers, lifecycle) / P2 (family, mobile verification) · Size: L · Depends on: PRD 01 (workspace, roles), existing auth (email-OTP, Google), PRD 06 (plans, pricing groups), PRD 08 (transactions)
1. Summary
The back-office customer directory and per-customer workspace: find, create, understand and manage customers, with tags, pricing groups, notes, waiver signatures, lifecycle status, and account-level rules (family accounts, mobile verification). Every other module (bookings, sales, plans) points at a customer record.
2. Problem & goals
- Front desk finds any customer by name, email or phone in ≤ 2 interactions; the list responds in < 1 s for 10k customers.
- Add a customer in ≤ 7 clicks (original: ~7), duplicates prevented.
- One screen shows everything about a customer (spend, bookings, plans, notes, waivers).
- Non-goals (v1): bulk import/export (not present in original; list as could), customer self-service portal (store PRD), marketing sends (marketing PRD), saved payment methods (needs gateway, PRD 08).
3. Users & roles
Owner, manager, front desk (create/view customers, sell), instructor (view-only on own attendance, assumption), customer (own profile via store, covered in the store PRD). See section 11.
4. Scope
In (v1)
- must: Customer list with search, joined-date, lifecycle, status filters, sortable columns, 50 per page, row actions
- must: Add customer (2 steps): email or mobile login, then name and optional details
- must: Customer detail with Overview, Profile, Attendance, Packages, Memberships, Transactions, Notes, Forms, Settings tabs
- must: Suspend (with reason) and delete (soft)
- must: Pricing groups assignment (CRUD is PRD 06) and tags CRUD + assignment
- should: Notes with categories and attachments
- should: Lifecycle computed (member, non-member, lost member, trial)
- should: Waiver form builder and signatures (customer Forms tab)
- should: Overview KPIs and charts
-
should: Marketing consent per channel on profile Later (v2+)
-
should: Mobile verification rules (4 triggers)
- should: Family account (parent with child profiles)
- could: Events (courses) tab, once courses ship; bulk import/export; column chooser; membership rules for recurring billing live in PRD 08
-
could: Saved payment method on profile Out
-
A "RezervPass" cross-business identity (original has a platform-wide customer id; we use our own account id only; cross-business identity is a marketplace decision, not v1).
5. User stories
- US-07-01 As a front desk user, I want to search by name, phone or email so that I find the customer at the counter.
- US-07-02 As a front desk user, I want to add a walk-in with only a phone number so that I can book them immediately.
- US-07-03 As an owner, I want to see lifecycle (member, non-member, lost, trial) so that I know who to win back.
- US-07-04 As an owner, I want to tag and group customers so that I target offers and pricing.
- US-07-05 As a staff member, I want internal notes with attachments so that team knowledge is shared.
- US-07-06 As an owner, I want to suspend a customer with a reason so that they cannot book or buy.
- US-07-07 As an owner, I want customers to sign a waiver so that liability forms are on file.
- US-07-08 As a parent, I want to manage my children under my account so that I can book for them.
- US-07-09 As an owner, I want to require a verified mobile at chosen moments so that no-shows can be reached.
- US-07-10 As an owner, I want to delete a customer but let them rejoin so that data requests are honoured.
6. Functional requirements
| ID | requirement | priority | evidence |
|---|---|---|---|
| FR-07-01 | List columns: name, joined date/time, lifecycle, phone, email, status chip, row menu; sortable; 50 per page with pager | must | S63, S200 |
| FR-07-02 | Filters: free-text search (name, email, phone); customer id; joined date presets (today, yesterday, 7/30/90 days, last month, last year, till date, exact, custom range); lifecycle multi-select; status (active, suspended); reset | must | S63b-g |
| FR-07-03 | Row actions: view, add pricing plan (opens PRD 08 flow), assign pricing group, assign tag, suspend, delete | must | S63g |
| FR-07-04 | Empty state with Add customer call to action | must | S63 |
| FR-07-05 | Add customer step 1: choose login method email or mobile (country code picker), enter it; Next disabled until valid; reject if already used (inline error, link to existing customer) | must | S64, F25 |
| FR-07-06 | Add customer step 2: first and last name required; optional DOB, pricing groups (multi), gender, address; waiver list; Add creates customer as non-member, active, then opens detail | must | S64d, F25, F72 |
| FR-07-07 | Quick add-customer drawer reusable from the add-transaction screen | should | S74 |
| FR-07-08 | Detail header: avatar, name, joined date, contact, lifecycle chip, status chip, KPI card (total spend, total bookings, classes attended) | must | S65 |
| FR-07-09 | Overview tab: refunds total, cancellations total, avg bookings per month with trend, date-range selector, line charts (spend, bookings, appointments) | should | S65b |
| FR-07-10 | Profile tab: account number, verified mobile and email with edit, resend verification email, marketing consent per channel (email, SMS), lifecycle, gender, DOB/age, pricing groups, tags, address | must | S66 |
| FR-07-11 | Edit profile modal incl. marketing consent; every consent change stored with timestamp and actor | must | S66d, reports (marketing consent) |
| FR-07-12 | Attendance tab: segmented class / appointment / (course, access later); filters search, date, location, staff, status, plan used | should | S67, S209zf |
| FR-07-13 | Packages and Memberships tabs: owned plans with credits left, start, end, status, transaction number, "first purchase" tag; filters location, group, status; Add Package / Add Membership (manual sale via PRD 08) | must | S68, S209ze |
| FR-07-14 | Transactions tab: search by number; date range, method, status, type filters; 20 per page | must | S68d, S209zg |
| FR-07-15 | Notes: add with optional category, text ≤500, attachments (JPEG, PNG, GIF, PDF, Word, Excel, PowerPoint; ≤1 MB each); card shows author, time; delete with icon; search, date, category filters | should | S69 |
| FR-07-16 | Note categories: add/edit/delete page | should | S69c |
| FR-07-17 | Tags: list (name, customer count, preview, updated), add/edit drawer with name + hex colour (default grey) and live preview, delete confirm, "assign customers" page, assign from customer profile drawer | must | S62, F26 |
| FR-07-18 | Pricing group assignment from add form, profile, list row action and group's own assign page | must | S61, S64e, F26 |
| FR-07-19 | Suspend: modal with reason (≤250), confirm; suspended customer cannot book or purchase and sees a clear message; reason visible on Settings tab; can be reactivated | must | S69g, F29 |
| FR-07-20 | Delete: confirm "cannot be recovered"; soft delete removes customer from lists; the same email/phone can rejoin as a new record; financial records remain for reporting | must | S69h, F29 |
| FR-07-21 | Lifecycle computed nightly and on plan events: member = has active membership; trial = holds a plan flagged trial and no membership; lost member = had a membership that ended with no active one; non-member = otherwise | should | S63d, S192 trial flag |
| FR-07-22 | Waiver forms (settings): title ≤100, rich text body, staff booking enforcement: none / warn but allow / must resolve (class bookings only); list, add, edit | should | S152, S152b |
| FR-07-23 | Customer Forms tab lists signed and unsigned waivers; staff can add a waiver to a customer; signature stores timestamp, version of text | should | S69e, S209x |
| FR-07-24 | Family account toggle (business setting) enabling parent with child profiles; children get no login; booking/purchase on behalf | could | S153, S74 note |
| FR-07-25 | Mobile verification rules (business settings): require mobile number; verify at sign-up; at first purchase of selected plans; at first booking of selected classes/appointments | could | S154 |
| FR-07-26 | Verification flow for mobile (code via messaging channel) and email (link); verified tick on profile | should | S66, S154 |
| FR-07-27 | Customer API list endpoint supports the filters in FR-07-02 server-side with keyset or offset pagination | must | derived |
| FR-07-28 | Audit trail of suspend, delete, consent, group and tag changes (who, when) | should | S209l events pattern ✱ |
7. Screens
| Our screen | Modelled on | States |
|---|---|---|
| Customers list | S63 | empty, loading, filled, filtered-empty, error |
| Add customer (2 steps) | S64 | step 1, invalid, step 2, duplicate error |
| Customer detail shell + 9 tabs (Events later = 10th) | S65-S69 | per tab: empty, filled |
| Tags list/drawer, assign page | S62 | empty, filled |
| Notes drawer, note categories page | S69 | empty, filled |
| Suspend / delete modals | S69 | open, error |
| Waiver list/editor | S152 | empty, edit |
| Settings: family account, mobile verification | S153, S154 | off, on |
8. Flows
- Add customer (target ≤ 7 clicks): Add → pick email/mobile → value → Next → first, last → Add → detail. Edge: existing contact shows "already exists, open record"; invalid contact keeps Next disabled; email customers get a verification/sign-in email, mobile ones a code only when verification is required.
- Tag/group assign: from list row, profile, tag page, or plan automation (PRD 06).
- Suspend: Settings tab or row menu → reason → confirm → status chip changes; existing future bookings stay (assumption, confirm).
- Delete: confirm → soft delete; future bookings cancelled with notification ✱ (confirm).
9. Data model (✱ = guess)
- Customer(id, account_no, first_name, last_name, email, email_verified_at, mobile, mobile_verified_at, dob, gender, address_*, status active|suspended|deleted, suspend_reason, lifecycle, marketing_email, marketing_sms, marketing_updated_at, parent_customer_id ✱, user_id null (link to auth user when they sign in), joined_at, created_by)
- Tag(id, name, colour) + CustomerTag; PricingGroup + CustomerPricingGroup (owned by PRD 06)
- CustomerNote(id, customer_id, category_id, text, author_id, created_at, deleted_at) + NoteAttachment(note_id, file_ref, name, size) + NoteCategory(id, name)
- Waiver(id, title, body, version, enforcement none|warn|block) + WaiverSignature(customer_id, waiver_id, version, signed_at, signed_by ✱ staff or customer)
- BusinessCustomerSettings(family_enabled, mobile_required, verify_on_signup, verify_plan_ids, verify_class_ids, verify_appt_ids)
- Reuses existing
user/auth identity: a Customer may be unlinked (staff-created) until the person signs in with matching verified email/mobile, then linked.
10. Business rules & edge cases
- Email and mobile are unique per business among non-deleted customers; stored normalised (lowercase email, E.164 mobile).
- At least one of email or mobile is required.
- New customers start non-member, active.
- Suspended customers cannot book, buy or check in; staff can still see them and sell on their behalf only after reactivation.
- Deleting is soft and rejoin creates a fresh customer record but keeps financial history on the old one.
- Tag/group deletion removes assignments, not customers.
- Attachments over 1 MB or other types rejected with a message.
- Marketing consent default off for staff-created customers ✱ (legal review).
- Waiver enforcement "must resolve" blocks staff-added class bookings until signed.
- Pagination and filters are stable when new customers arrive (keyset preferred).
11. Permissions
| Action | Owner | Manager | Front desk | Instructor |
|---|---|---|---|---|
| View list/detail | yes | yes | yes | view (own classes) ✱ |
| Create/edit customer | yes | yes | yes | no |
| Suspend/delete | yes | yes | no ✱ | no |
| Notes add/delete | yes | yes | add | no |
| Tags / pricing groups CRUD | yes | yes | no | no |
| Waiver, family, verification settings | yes | no | no | no |
| Customer detail sub-areas (overview, profile, bookings, packages, transactions, notes, forms, settings) are separately view/edit-able in the original matrix; we mirror at tab level. |
12. Acceptance criteria
- Given 120 customers, When I open the list, Then 50 appear with a pager and sorting by joined date works.
- Given filter lifecycle = Lost member and status = Active, Then only matching customers show; Reset clears both.
- Given email [email protected] exists, When I add another customer with it, Then I see a duplicate error with a link and nothing is created.
- Given I add a customer with only a mobile number, When I finish step 2, Then the customer exists with status active and lifecycle non-member.
- Given a customer is suspended, When they try to book, Then the booking is refused with the suspension message and no reason text is shown to them ✱.
- Given a customer is deleted, When I search their email, Then no result; When the same email signs up, Then a new customer is created.
- Given a membership purchase with auto tag "VIP", When the transaction is paid, Then the tag appears on the profile.
- Given a note with a 2 MB file, Then the upload is rejected.
- Given waiver enforcement "must resolve", When staff add the customer to a class without a signed waiver, Then the booking is blocked.
13. Dependencies & open questions
Depends on existing auth/profile, PRD 06, 08, 02/04 (bookings tabs), 01 (roles). Open questions:
- Merge of staff-created customers with self-signup users: auto-link on verified email only?
- What happens to future bookings and active plans on suspend and on delete?
- Lifecycle exact rules and "lost member" threshold (days since membership ended) — the original rules are not documented in recon.
- Family accounts: child login, shared credits? Plan-gated in original (pricing tier: TBD); behaviour not observed.
- Mobile verification channel (SMS, WhatsApp) and provider cost: not observed.
- Data protection obligations (Indonesia PDP law, others) for consent, deletion and attachments.
- Import/export of customers: absent in original but commonly needed for migration.
- Waiver customer-side signing UI not observed (staff side only).
14. Evidence
See evidence/evidence.md.
