Skoolsewa - Ecommerce Docs
Developer ResourcesAcademic sessions

Academic Sessions Features and Flows

Feature list, actor journeys, business rules, and edge cases for the academic sessions module.

Academic Sessions - Features and Flows

1. Documentation Evidence

Source TypeFiles or DocsWhat Was Extracted
APISibling API doc and academic-sessions.controller.tsRoute surface and actors.
BackendSibling backend doc and academic-sessions.service.tsBusiness behavior and side effects.
Schemapackages/db/src/schema/school/academic-sessions.tsConstraint-level enforcement.
Testsapps/api/src/modules/academic-sessions/__tests__/academic-sessions.service.integration.spec.tsProven behaviours.

2. Feature Summary

FieldValue
Moduleacademic-sessions
SubmoduleN/A
Primary user valueGives the school office one authoritative record of which school year is "current," so every dated record has an unambiguous year to file against.
ActorsAdmin only — no other role is verified to hold any AcademicSessions_* permission.
Main entry pointsGET/POST/PATCH/DELETE /api/academic-sessions.
Main outputsPersisted academic_sessions rows; the single "current" designation other modules will read once they exist.
Related docsAPI, Backend

3. Actor Matrix

ActorCan DoCannot DoAuth RequirementNotes
AdminList, search, create, update (including making a session current), retire, hard delete.Delete the current session; unset the only current session.JWT + AcademicSessions_* permissionThe only actor verified to hold any permission in this module.
GuestNothing.Everything.N/ANo @Public() route exists.

4. Capability Matrix

CapabilitySurfaceActorRoute/TriggerState ReadState WrittenLinked API Section
List sessionsAdminAdminGET /api/academic-sessionsacademic_sessions, cache7.1
Create a sessionAdminAdminPOST /api/academic-sessionsacademic_sessions (name check)academic_sessions7.2
Create a session as currentAdminAdminPOST /api/academic-sessions with isCurrent: trueacademic_sessionsacademic_sessions (two rows in one transaction)7.2
Rename / redate / retire a sessionAdminAdminPATCH /api/academic-sessions/:publicIdacademic_sessionsacademic_sessions7.3
Make a session currentAdminAdminPATCH /api/academic-sessions/:publicId with isCurrent: trueacademic_sessionsacademic_sessions (incumbent unset + this row set, one transaction)7.3
Hard delete a sessionAdminAdminDELETE /api/academic-sessions/:publicIdacademic_sessionsacademic_sessions7.4

5. User-Facing Flows

5.1 Handing the current year over

Summary

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.

Preconditions

  • 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.

Main Flow

StepActor/SystemActionResultSource
1AdminPOST /api/academic-sessions with isCurrent: true (or PATCH an existing session).Request validated.Controller/DTO.
2BackendUnsets the existing current session, then sets the new one, in one transaction.Exactly one session remains current.Service.
3BackendInvalidates the list cache.Next list read reflects the change.Service.

Sequence Diagram

Branches and Edge Cases

BranchConditionBehaviorError/Result
Race between two adminsBoth PATCH different sessions to current at onceOne wins, one hits the partial unique index409 ACADEMIC_SESSION_CURRENT_CONFLICT
Unset the only current sessionPATCH { isCurrent: false } on the sole current sessionRefused before any write409 ACADEMIC_SESSION_CURRENT_REQUIRED
Delete the current sessionDELETE on the current sessionRefused409 ACADEMIC_SESSION_IS_CURRENT

6. Lifecycle and State Transitions

EntityFromEvent/ActionToGuard ConditionSide Effects
Academic sessionisCurrent=falsePATCH {isCurrent:true}isCurrent=trueNone database-side beyond the partial unique indexIncumbent (if any) set to isCurrent=false in the same transaction.
Academic sessionisCurrent=truePATCH {isCurrent:false}RefusedMust not be the only current session409 ACADEMIC_SESSION_CURRENT_REQUIRED if it is.
Academic sessionisActive=truePATCH {isActive:false}isActive=trueisActive=false (retired)NoneRow removed from active pick lists; existing references (once any exist) are untouched.
Academic sessionAnyDELETERow removedMust not be the current session409 ACADEMIC_SESSION_IS_CURRENT if it is.

7. Business Rules and Policy Traceability

RuleBusiness ReasonActor ImpactEnforced InAPI ImpactTests
At most one session is currentEvery 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 currentDownstream 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 orderedAn 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-insensitivelyAvoids "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 deletedDeleting 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"

8. Flow Edge-Case Matrix

FlowEdge CaseTriggerExpected BehaviorSource
CreateInverted date rangeendDate <= startDateRejected409 ACADEMIC_SESSION_DATES_INVALID
CreateZero-length rangeendDate === startDateRejected — a zero-length session is a broken row, not a valid edge case409 ACADEMIC_SESSION_DATES_INVALID
CreateDuplicate name, different case"2026-27" vs "2026-27".toUpperCase()Rejected409 ACADEMIC_SESSION_NAME_TAKEN
UpdatePartial date PATCH inverts range against stored valuePATCH {startDate} only, checked against stored endDateRejected409 ACADEMIC_SESSION_DATES_INVALID
UpdateUnset the only current sessionPATCH {isCurrent:false}Rejected, no write attempted409 ACADEMIC_SESSION_CURRENT_REQUIRED
UpdateConcurrent handover raceTwo PATCHes to different sessions, both isCurrent:trueOne succeeds, one is refused409 ACADEMIC_SESSION_CURRENT_CONFLICT
DeleteTarget is currentDELETE on current sessionRejected409 ACADEMIC_SESSION_IS_CURRENT
DeleteTarget is not currentDELETE 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 foundUnknown publicIdPATCH/DELETE an id that does not exist404 ACADEMIC_SESSION_NOT_FOUND...spec.ts, "reports an unknown session as not found"

9. Completion Checklist

  • Every route and its actor is listed.
  • Both halves of the "exactly one current" rule are documented, with the exact enforcement point named for each.
  • Every error code this module produces is traced to a trigger.
  • Lifecycle transitions and their guards are documented.
  • Every flow links to the API and backend docs.

See Also

  • API doc: /docs/developer/academic-sessions/api
  • Backend doc: /docs/developer/academic-sessions/backend