03 — User Journey (CRM Module)
- 1. Journey: Walk-in lead capture → conversion (happy path)
- 2. Journey: Duplicate / already-in-system
- 3. Journey: Uninterested lead → closed
- 4. Journey: Admission application → enrollment (happy path)
- 5. Journey: Rejection / waitlist
- 6. Journey: Campaign-driven leads
- 7. Cross-cutting journey points
End-to-end journeys through the CRM. Steps marked
(planned)are indocs/IMPLEMENTATION_PLAN.mdbut not implemented;(forward-looking)depends on infrastructure that does not exist yet (device registry, WS topics).
1. Journey: Walk-in lead capture → conversion (happy path)
Actors: Priya (receptionist) → Aditi (counselor)
- Parent walks in asking about Grade 6. Priya opens New Lead, picks entry
walk_in(source default). - Fills
firstName,lastName,email(required), phone, grade interest → submit.- Server: duplicate email → 409 inline (
crm.service.ts:42-47); else lead createdstatus: new, emitsLeadCreated(crm.service.ts:50-63).
- Server: duplicate email → 409 inline (
- Priya hands off: Aditi opens lead detail, sets
assignedToto herself, logs a follow-up "Called; parent interested in June intake"scheduledAt: +2d. - Next day: leads list filtered by her assignment (
statusfilter exists; assignment filter(planned)) → she sees the lead, calls, setsstatus: contacted. - School visit happens; Aditi sets
status: qualified, adds grade/academicYear/class (lead.schema.ts:64-74). - Aditi taps Convert → confirm dialog → server creates User (+ Student,
ADM{timestamp}admission number) and marks leadconvertedwithconvertedToStudentId(crm.service.ts:151-164), emitsLeadConverted(crm.service.ts:166-173). - Lead list: lead now shows converted badge; detail links to student.
2. Journey: Duplicate / already-in-system
- Receptionist types an email that already exists → 409 on submit
(
crm.service.ts:42-47). - UI shows "A lead with this email already exists" + actions: open existing lead /
edit fields. No merge endpoint exists — merge is
(planned).
3. Journey: Uninterested lead → closed
- Counselor sets
status: closed(+ optionalclosedReason). - Server auto-stamps
closedAtwhen no reason given (crm.service.ts:94-96). - Closed leads remain visible under the status filter (no hard delete anywhere;
soft-delete API
(planned),base.repository.ts:68-74). - Convert action is disabled for closed leads (server 400 guard,
crm.service.ts:123-125).
4. Journey: Admission application → enrollment (happy path)
Actors: applicant (submitted via office) → Aditi → interview panel → admin
- Office submits application (POST
/crm/admissions) → statussubmitted, workflow recordsdraft → submitted(admission.service.ts:39-59), emitsAdmissionSubmitted. - Aditi adds required documents (TC/marksheet/certificate/photo —
admission.schema.ts:27-33) → status auto-advances todocuments_pending(admission.service.ts:112-114). - Aditi schedules interview (datetime, panel staff, mode online/offline, feedback
later) → status
interview_scheduled(admission.service.ts:118-134). - Panel records feedback; Aditi moves status
under_review. - Decision: Approve with comment → status
approved,decidedAtset, emitsAdmissionApproved(admission.service.ts:136-170). Workflow history now shows approver + comment per hop (admission.schema.ts:52-67). - Admin/Aditi converts → User + Student created, status
converted+conversion{studentId, convertedAt}(admission.service.ts:172-225), emitsAdmissionConverted. - Reminder automation
(planned): reminder worker nudges incomplete docs; expiry worker auto-rejects stale (findStale,admission.repository.ts:53-62; IMPLEMENTATION_PLAN.md §1.3).
5. Journey: Rejection / waitlist
- Decision
rejectorwaitlistfrom a decidable status (ADMISSION_DECIDABLE_STATUSES,admission.schema.ts:20-25). - Status becomes terminal-immutable: further edits blocked
(
admission.service.ts:257-269); convert blocked unlessapproved(admission.service.ts:174-178). - Emits
AdmissionRejected(reject only — waitlist emits no event today).
6. Journey: Campaign-driven leads
- Kabir creates campaign (name, type, status, dates) —
POST
/crm/campaigns(crm.controller.ts:83-87). - Website/print leads arrive with
source: campaign(lead.schema.ts:58-59). - Kabir views campaign list (
crm.controller.ts:77-81) — no per-campaign lead drill-down today; source-based funnel analytics(planned). metrics(campaign.schema.ts:45-52) are schema-only; no write endpoint exists → metrics are(planned).
7. Cross-cutting journey points
- Search
(planned): leads join global search (IMPLEMENTATION_PLAN.md §3). - Bulk import of leads
(planned)(IMPLEMENTATION_PLAN.md §3). - Push follow-up reminders
(forward-looking): no device registry; WS has no CRM topics — the client polls; reminders surface only in-app today. - Coaching conversion
(planned): Lead → batch enrollment flow (not class/section) for coaching institutions (IMPLEMENTATION_PLAN.md §6.6).