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

01 — Product Overview (Leave Module)

Employee leave requests, balance tracking, and substitute-teacher assignment. Derived from src/modules/leave/** (controller, service, schemas, DTOs, events, repositories), src/modules/rbac/schemas/role.schema.ts, src/modules/rbac/permissions.constants.ts, src/modules/users/schemas/user.schema.ts, studylyon-blueprint/03-Database/COLLECTIONS.md, studylyon-blueprint/04-Modules/Attendance.md, and docs/IMPLEMENTATION_PLAN.md. Nothing here is invented; plan-only capability is marked (planned), client-only or roadmap-only (forward-looking), analytics (proposed). Shared platform context: 00-shared/01.


1. What the module is

The Leave module lets an authenticated user of a tenant request time off, an org admin decide (approve/reject) those requests, and admins assign a substitute teacher for an approved absence. Balances are computed live from leave types plus approved requests — there is no stored balance collection (leave.service.ts:87-88 — "live-computed balance from leave types + approved requests; no stored balance collection to drift from ground truth").

The blueprint anticipated this module but does not specify it: 04-Modules/ has no Leave doc (only Attendance.md:58 lists "Leave" as an attendance status value), and COLLECTIONS.md:3643 places leave_requests under "27. Future Collections" → Human Resources. The implementation exists and is ahead of the blueprint; docs/IMPLEMENTATION_PLAN.md:121-164 (Phase 2.2, "Leave & Substitution Module", 7.5 days) is the authoritative plan.

2. Core domain facts (from source)

  • Request modelschemas/leave-request.schema.ts:16-48: userId (ref User, required), leaveTypeId (ref LeaveType, required), startDate, endDate (required Date), daysRequested (number, min: 1, server-computed), reason?, status (default pending), decidedBy?, decidedAt?, decisionNote?. Collection leave_requests. Indexes (tenantId, userId, startDate) and (tenantId, status) (:52-53).
  • Status enumleave-request.schema.ts:7-12: pending | approved | rejected | cancelled. Note: cancelled is defined but no cancel endpoint exists — the status is unreachable today (gap).
  • Leave type modelschemas/leave-type.schema.ts:9-25: code, name, daysPerYear (min: 0), carryForward (default false), maxCarryForward?, isDefault. Unique (tenantId, code) (:29).
  • Default typesleave.service.ts:30-64: CL Casual 12 (no carry), SL Sick 10 (carry, max 30), EL Earned 15 (carry, max 60), ML Maternity 180, PL Paternity 15. Seeded lazily and idempotently on first access via bulkWrite $setOnInsert upserts (ensureDefaultTypes, leave.service.ts:299-306).
  • Balance mathleave.service.ts:89-124: carried forward = carryForward ? min(maxCarryForward, max(0, daysPerYear − usedPrevYear)) : 0 (:107-112); daysRemaining = daysPerYear + carriedForward − daysUsed (:121). Balances use calendar years, lookback to Jan 1 of the previous year (:92-97).
  • Day countingleave.service.ts:310-312: countDays is an inclusive calendar-day count ((end − start)/86 400 000 + 1); weekends and holidays are counted as leave days (ponytail comment: "switch to working-day count if the school policy needs it").
  • Approval semanticsleave.service.ts:171-212: only pending requests can be decided (:175-178, else 409); a user cannot decide their own request (:179-180, 409); approval re-checks live balance and rejects with 409 "Insufficient leave balance." when daysRemaining < daysRequested (:183-190). Decision writes status, decidedBy, decidedAt, decisionNote (:195-200) and emits LeaveApproved / LeaveRejected.
  • Substitutionsschemas/substitution.schema.ts:14-47: leaveRequestId, absentTeacherId, substituteTeacherId, classId, subjectId, date, startTime, endTime, status (assigned | completed | cancelled, :7-11), notes?. Only approved requests can get a substitution (leave.service.ts:227-230, 409); the requester must have a Teacher record (:232-238, 404); time-slot clash for the substitute is rejected (:241-250, 409, timeOverlaps :314-321).
  • Scope rules — non-admins see only their own requests (leave.service.ts:166-167); org admins see all, optionally filtered by userId (:163-167). Requester identity always comes from the tenant context, never the body (:138).

3. Who uses it (roles from role.schema.ts:8-65)

RoleIn leave module
org_admin (priority 100, ALL_PERMISSIONS role.schema.ts:22-24)Requests, lists all, decides, creates types, assigns substitutions
teacher (priority 50, role.schema.ts:26-32)Requests leave; receives substitutions (needs a Teacher record)
staff (priority 40, role.schema.ts:33-40)Can request leave (any authenticated user can)
student / parentNo leave surface; schema allows but nothing gates it (gap)

No leave.* permissions existpermissions.constants.ts:1-97 contains no leave entries, and leave endpoints carry only JwtAuthGuard (leave.controller.ts:27), with the org-admin check done inline via ctx.roles (leave.service.ts:163). This is a documented security gap.

4. Scope in / out

In scope (implemented)Out of scope / gaps
Create request (POST /leave/requests)Cancel request (cancelled status unreachable — no endpoint)
List requests (own / all for admin, status+userId filters)Edit/withdraw a pending request
Approve / reject (balance-checked)leave.* RBAC permissions (permissions.constants.ts has none)
Live-computed balanceStored leave-balance.schema.ts(planned) (IMPLEMENTATION_PLAN.md:130, not in code)
Leave types CRUD (create + list; seeded defaults)Update/delete leave type
Substitutions (assign, list for teacher, clash check)Substitution accept/decline by substitute, status transitions completed/cancelled
Calendar (approved only, month range)Working-day counting (weekends/holidays) — see leave.service.ts:310-312
Events: LeaveRequested, LeaveApproved/Rejected, SubstitutionAssigned (events/leave-events.ts:1-26)Substitution → Timetable entry + notifications — (planned) (IMPLEMENTATION_PLAN.md:163)

5. PRD native-app exclusion (flagged)

Per 00-shared/01 §9: PRODUCT_REQUIREMENTS_DOCUMENT.md:144 puts native mobile apps out of Phase 1 scope (roadmap Phase 3 plans a read-only companion). The decision recorded with the product owner is that these docs specify a full-featured Flutter client now, to the complete leave API surface; any conflict with the web-first roadmap resolves in favor of these docs unless the roadmap is amended.

6. Client surface (screens) — see 05/06

Request form · My requests list · Approvals queue (org admin) · Balance card · Leave types admin (org admin) · Substitutions (list + assign) · Calendar.