Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

03 — User Journey (Leave Module)

End-to-end flows mapped to the exact endpoints they exercise. API-enforced branches are cited to source; (planned) = IMPLEMENTATION_PLAN.md:121-164; (proposed) = product expectation.


Journey 1 — Teacher requests casual leave (happy path)

  1. Open Balance (GET /leave/balance/:meleave.controller.ts:55-59) → see daysRemaining per type (leave.service.ts:89-124).
  2. Open Request form → pick leave type (GET /leave/types, :67-71; defaults seeded lazily leave.service.ts:299-306), start/end dates, reason.
  3. Submit POST /leave/requests (:32-36) → server computes daysRequested (leave.service.ts:136), stamps userId from token (:138), status pending (:144), emits LeaveRequested (:146-154).
    • 400 if endDate < startDate (:133-134); 404 if leave type unknown (:129).
  4. Request appears in My requests (GET /leave/requests — own only, leave.service.ts:166-167; sorted createdAt desc :168), badge pending.
  5. Org admin opens Approvals queue (GET /leave/requests?status=pending, :38-47), decides PATCH /leave/requests/:id/approve (:49-53).
    • 409 if already decided (leave.service.ts:175-178), 409 if self-decision (:179-180), 409 if balance insufficient (:188-189).
  6. Teacher sees status flip to approved (event LeaveApproved, :210) → balance now reflects deduction on next fetch.

Journey 2 — Admin assigns a substitute (post-approval)

  1. From an approved request, admin opens Assign substitution form.
  2. POST /leave/substitutions (leave.controller.ts:73-77) with leaveRequestId, substituteTeacherId, classId, subjectId, date, startTime, endTime, notes?.
    • 409 if request not approved (leave.service.ts:227-230).
    • 404 if requester has no Teacher record (:232-238).
    • 409 if substitute already booked in the slot (:241-250).
  3. Substitution shows in substitute's My substitutions (GET /leave/substitutions/teacher/:id, :79-83; sorted by date :275-280).
  4. (planned) Timetable entry created + notifications to substitute and students (IMPLEMENTATION_PLAN.md:163).

Journey 3 — Teacher checks coverage before requesting

  1. Open Calendar (GET /leave/calendar?from&to, leave.controller.ts:85-91) → approved requests overlapping the range (leave.service.ts:282-296); default = current month (:283-287).
  2. Decide dates avoiding collisions with colleagues (calendar is tenant-scoped via BaseRepository, not filtered by user).

Journey 4 — Rejection and retry

  1. Admin rejects with note (PATCH /leave/requests/:id/approve, action reject, leave-decision.dto.ts:4-7) → decisionNote stored (leave.service.ts:199), event LeaveRejected (:210).
  2. Teacher sees rejected + note in My requests.
  3. Teacher edits dates in a new request and resubmits (no edit/retry of the old request — no PATCH-request endpoint; gap).
  4. (planned) notification of the decision reaches the teacher via the event bus (LeaveDecidedEvent, events/leave-events.ts:10-18).

Journey 5 — Admin configures leave types (org admin only)

  1. Open Leave types → list (GET /leave/types, :67-71), defaults always present (ensureDefaultTypes, leave.service.ts:299-306).
  2. Create custom type POST /leave/types (:61-65) — code, name, daysPerYear, carryForward?, maxCarryForward? (create-leave-type.dto.ts:4-28). Unique (tenantId, code) (leave-type.schema.ts:29) → 11000 duplicate-key error surfaces as a conflict (no dedicated handler in service — gap).
  3. Balance screen now includes the new type automatically (computed from types, leave.service.ts:89-124).

Journey 6 — Edge: leave overlaps and back-dating

  • No same-user overlap validation exists at create time (createRequest, leave.service.ts:126-156); two overlapping pending requests can both exist. QA treats this as a known gap (see 14_QA_Checklist §2); a future server rule or client-side warning (forward-looking).
  • Back-dated requests are allowed — no rule prevents startDate in the past.

Journey map (condensed)

[Balance check] → [Request] → pending → [Admin queue] → approve → [Calendar/status]
                                      ↘ reject → note → [Retry as new request]
[Approved] → [Assign substitution] → [Substitute sees assignment] → (planned) timetable+notify

Metrics (proposed)

Request → decision median; reject rate by reason; substitution coverage of approved leave; balance-sufficiency collisions.