Academic Sessions Features and Flows
Developer Resources Academic sessions Academic Sessions Features and Flows Feature list, actor journeys, business rules, and edge cases for the academic sessions module.
Source Type Files or Docs What Was Extracted API Sibling API doc and academic-sessions.controller.ts Route surface and actors. Backend Sibling backend doc and academic-sessions.service.ts Business behavior and side effects. Schema packages/db/src/schema/school/academic-sessions.tsConstraint-level enforcement. Tests apps/api/src/modules/academic-sessions/__tests__/academic-sessions.service.integration.spec.tsProven behaviours.
Field Value Module academic-sessionsSubmodule N/A Primary user value Gives the school office one authoritative record of which school year is "current," so every dated record has an unambiguous year to file against. Actors Admin only — no other role is verified to hold any AcademicSessions_* permission. Main entry points GET/POST/PATCH/DELETE /api/academic-sessions.Main outputs Persisted academic_sessions rows; the single "current" designation other modules will read once they exist. Related docs API , Backend
Actor Can Do Cannot Do Auth Requirement Notes Admin List, search, create, update (including making a session current), retire, hard delete. Delete the current session; unset the only current session. JWT + AcademicSessions_* permission The only actor verified to hold any permission in this module. Guest Nothing. Everything. N/A No @Public() route exists.
Capability Surface Actor Route/Trigger State Read State Written Linked API Section List sessions Admin Admin GET /api/academic-sessionsacademic_sessions, cache— 7.1 Create a session Admin Admin POST /api/academic-sessionsacademic_sessions (name check)academic_sessions7.2 Create a session as current Admin Admin POST /api/academic-sessions with isCurrent: trueacademic_sessionsacademic_sessions (two rows in one transaction)7.2 Rename / redate / retire a session Admin Admin PATCH /api/academic-sessions/:publicIdacademic_sessionsacademic_sessions7.3 Make a session current Admin Admin PATCH /api/academic-sessions/:publicId with isCurrent: trueacademic_sessionsacademic_sessions (incumbent unset + this row set, one transaction)7.3 Hard delete a session Admin Admin DELETE /api/academic-sessions/:publicIdacademic_sessionsacademic_sessions7.4
At the start of a new school year, an admin creates the new session and marks it current. The previous current session is automatically unset — there is no separate action to "close" the old year.
The admin holds AcademicSessions_CREATE (or AcademicSessions_UPDATE, if the new session already exists).
The new session's name is not already in use, and its date range does not end on or before its start.
Step Actor/System Action Result Source 1 Admin POST /api/academic-sessions with isCurrent: true (or PATCH an existing session).Request validated. Controller/DTO. 2 Backend Unsets the existing current session, then sets the new one, in one transaction. Exactly one session remains current. Service. 3 Backend Invalidates the list cache. Next list read reflects the change. Service.
Branch Condition Behavior Error/Result Race between two admins Both PATCH different sessions to current at once One wins, one hits the partial unique index 409 ACADEMIC_SESSION_CURRENT_CONFLICTUnset the only current session PATCH { isCurrent: false } on the sole current sessionRefused before any write 409 ACADEMIC_SESSION_CURRENT_REQUIREDDelete the current session DELETE on the current sessionRefused 409 ACADEMIC_SESSION_IS_CURRENT
Entity From Event/Action To Guard Condition Side Effects Academic session isCurrent=falsePATCH {isCurrent:true}isCurrent=trueNone database-side beyond the partial unique index Incumbent (if any) set to isCurrent=false in the same transaction. Academic session isCurrent=truePATCH {isCurrent:false}Refused Must not be the only current session 409 ACADEMIC_SESSION_CURRENT_REQUIRED if it is.Academic session isActive=truePATCH {isActive:false}isActive=true → isActive=false (retired)None Row removed from active pick lists; existing references (once any exist) are untouched. Academic session Any DELETERow removed Must not be the current session 409 ACADEMIC_SESSION_IS_CURRENT if it is.
Rule Business Reason Actor Impact Enforced In API Impact Tests At most one session is current Every dated record needs one unambiguous "current year" answer. Admin cannot make two sessions current. academic_sessions_one_current_idx (partial unique index)409 ACADEMIC_SESSION_CURRENT_CONFLICT on a losing race...spec.ts, "hands the current flag over in one transaction"At least one session is current Downstream modules need an answer to "which year is this," with no fallback defined. Admin cannot unset the last current session. AcademicSessionsService.update (application code — not expressible as a constraint)409 ACADEMIC_SESSION_CURRENT_REQUIRED...spec.ts, "refuses to unset the only current session"A session's dates must be ordered An inverted or zero-length range silently returns nothing from every date-range query. Admin cannot create/PATCH into an invalid range. Both the service (assertDatesOrdered, readable message) and the CHECK constraint academic_sessions_dates_ordered (actual guarantee) 409 ACADEMIC_SESSION_DATES_INVALID...spec.ts, "refuses a session that ends before it starts", "refuses a PATCH that inverts the range one field at a time"Session names are unique, case-insensitively Avoids "2026-27" and "2026-27" (different case/whitespace) coexisting as distinct sessions. Admin cannot create a duplicate. Service pre-check + academic_sessions_name_unique (unique index on lower(name)) 409 ACADEMIC_SESSION_NAME_TAKEN...spec.ts, "refuses a duplicate name regardless of case"The current session cannot be deleted Deleting it would leave the school with no current session. Admin must switch current before deleting. AcademicSessionsService.remove409 ACADEMIC_SESSION_IS_CURRENT...spec.ts, "refuses to delete the current session and permits deleting another"
Flow Edge Case Trigger Expected Behavior Source Create Inverted date range endDate <= startDateRejected 409 ACADEMIC_SESSION_DATES_INVALIDCreate Zero-length range endDate === startDateRejected — a zero-length session is a broken row, not a valid edge case 409 ACADEMIC_SESSION_DATES_INVALIDCreate Duplicate name, different case "2026-27" vs "2026-27".toUpperCase()Rejected 409 ACADEMIC_SESSION_NAME_TAKENUpdate Partial date PATCH inverts range against stored value PATCH {startDate} only, checked against stored endDateRejected 409 ACADEMIC_SESSION_DATES_INVALIDUpdate Unset the only current session PATCH {isCurrent:false}Rejected, no write attempted 409 ACADEMIC_SESSION_CURRENT_REQUIREDUpdate Concurrent handover race Two PATCHes to different sessions, both isCurrent:true One succeeds, one is refused 409 ACADEMIC_SESSION_CURRENT_CONFLICTDelete Target is current DELETE on current sessionRejected 409 ACADEMIC_SESSION_IS_CURRENTDelete Target is not current DELETE on a non-current sessionSucceeds, hard delete — Search % in search term?search=%Escaped before entering ILIKE; matched as a literal, not a wildcard ...spec.ts, "treats a percent sign in the search term as a literal, not a wildcard"Not found Unknown publicId PATCH/DELETE an id that does not exist404 ACADEMIC_SESSION_NOT_FOUND...spec.ts, "reports an unknown session as not found"
API doc: /docs/developer/academic-sessions/api
Backend doc: /docs/developer/academic-sessions/backend