Skoolsewa - School Management System Docs
Developer ResourcesAcademic Calendar

Academic Calendar Features and Flows

Complete feature list, actor journeys, state flows, business rules, edge cases, and diagrams for Academic Calendar.

Academic Calendar Features and Flows

1. Documentation Evidence

Source TypeFiles or DocsWhat Was Extracted
BackendSibling backend doc, calendar-generate.service.ts, holiday-day-apply.service.ts, school-week-config.service.tsGeneration algorithm, precedence recomputation, the unconfigured-state contract.
APISibling API doc, academic-calendar.controller.ts, school-holiday.controller.ts, school-week-config.controller.ts, calendar-portal.controller.tsRoute surface, actors, auth.
Schemapackages/db/src/schema/school/calendar.ts, packages/db/src/calendar/calendar-contract.tsClassification/source vocabulary, holiday precedence, attendance-required set.
Testsapps/api/src/modules/academic-calendar/__tests__/academic-calendar.integration.spec.tsConfirmed flow behavior and edge cases.

2. Feature Summary

FieldValue
ModuleAcademic Calendar
SubmoduleWeek configuration, day generation/read/correction, holidays, audience read
Primary user valueAn administrator states the school's weekend once, generates a full academic year's calendar in one click, corrects individual days by hand, and manages holidays — while every pupil, parent, and staff member can see the same calendar with no setup at all.
ActorsAdmin (superadmin by default seed), staff, teacher, guardian, student — the last four only through the read-only screen and the shared mobile read.
Main entry pointsPOST .../calendar/generate, the calendar/holiday admin panel screens, GET /api/mobile/calendar, GET /api/calendar/today.
Main outputsA materialised academic_calendar_days row per date, school_holidays records, the school-wide school_week_config row, and the derived attendanceExpected flag every downstream attendance flow reads.
Related docsAPI, backend.

3. Actor Matrix

ActorCan DoCannot DoAuth RequirementNotes
Admin (superadmin)Configure the week, generate/repair the calendar, correct any day, create/update/delete holidays.Delete a seeded holiday (must reclassify instead); classify a holiday as school_day.Admin JWT + AcademicCalendar_* permission codes.Only superadmin holds these codes by default seed — staff/teacher/guardian/student do not.
Staff / TeacherView the calendar screen.Any write to the calendar, holidays, or week config — they hold no AcademicCalendar_* code.JWT + AcademicCalendarView_READ (gates the panel screen only).AcademicCalendarView_READ appears on zero API routes; the actual data comes from the same no-permission mobile route every audience uses.
GuardianView the calendar screen; read GET /api/mobile/calendar for their household's context.Any write.JWT + AcademicCalendarView_READ.Same projection as every other audience — the calendar names no person, so there is no per-child scoping to apply.
StudentView the calendar screen; read GET /api/mobile/calendar.Any write.JWT + AcademicCalendarView_READ.Same as guardian.
Any authenticated user (mobile)GET /api/mobile/calendar — one BS month, defaulting to "now".See any administrator-internal field (note, reason, source, needsReview, any id, version).Valid JWT only — no permission or role check at all.Safety comes from the frozen, hand-enumerated response DTO, not from a guard.
Any authenticated user (header)GET /api/calendar/today — today's date in Nepal, its Bikram Sambat date, and a Devanagari rendering of the BS date and the weekday.Read anything beyond the date: the route touches no table.Valid JWT only — no permission or role check.Every audience's header shows it. When the date is outside the converter's range (BS 2000–2090) the response is still successful and the BS and Devanagari fields are null and only the Gregorian date and weekday number remain.

4. Capability Matrix

CapabilitySurfaceActorRoute/TriggerState ReadState WrittenLinked API Section
Configure the school weekAdminAdminPUT /api/school/week-configExisting config row, if anyschool_week_config insert/updateAPI §8.11
Read the school week configurationAdminAdminGET /api/school/week-configschool_week_config—API §8.10
Generate/repair the calendarAdminAdminPOST /api/academic-sessions/:id/calendar/generateacademic_sessions, school_week_config, existing calendar/holiday rowsacademic_calendar_days, school_holiday_daysAPI §8.1
List calendar days with coverageAdminAdminGET /api/academic-sessions/:id/calendaracademic_calendar_days, attendance existence, school_week_config—API §8.2
View a BS month gridAdminAdminGET /api/academic-sessions/:id/calendar/months/:y/:macademic_calendar_days, school_holiday_days/school_holidays—API §8.3
View one day's detailAdminAdminGET /api/academic-sessions/:id/calendar/days/:dateacademic_calendar_days, holidays, attendance existence—API §8.4
Correct one dayAdminAdminPATCH /api/academic-sessions/:id/calendar/days/:dateExisting day rowacademic_calendar_daysAPI §8.5
List holidays of a sessionAdminAdminGET /api/academic-sessions/:id/holidaysschool_holidays—API §8.6
Create a holidayAdminAdminPOST /api/academic-sessions/:id/holidaysacademic_sessions, academic_calendar_days in rangeschool_holidays, school_holiday_days, academic_calendar_daysAPI §8.7
Update a holidayAdminAdminPATCH /api/holidays/:idExisting holiday, academic_sessions, calendar daysSame three tablesAPI §8.8
Delete a holidayAdminAdminDELETE /api/holidays/:idExisting holiday, its linksschool_holidays, school_holiday_days (cascade), academic_calendar_daysAPI §8.9
View this month's calendarAny audienceAny authenticated userGET /api/mobile/calendaracademic_calendar_days—API §8.12
Show today's date in the page headerAny audienceAny authenticated userGET /api/calendar/todayThe clock only — no table—API §8.13
See the next days of the calendarAny audienceAny authenticated userGET /api/calendar/upcoming?days=Nacademic_calendar_days, academic_sessions (60 s cache)—API §8.14

5. User-Facing Flows

5.1 Configure the school week (first time)

Summary

Before anything else can happen, an administrator states which days the school does not operate and, optionally, adjusts the attendance cutoff time. There is deliberately no default — every school does this once, explicitly.

Preconditions

  • Caller holds AcademicCalendar_UPDATE.
  • No preconditions on data — this is legitimately the very first action available in a fresh school.

Main Flow

StepActor/SystemActionResultSource
1AdminOpens the week-configuration screen.GET .../school/week-config returns configured: false with the shipped defaults.API §8.10
2AdminSelects weekend days (e.g. Saturday only, or Sunday+Saturday) and a cutoff time.Local form state; nothing persisted yet.—
3AdminSaves.PUT .../school/week-config with expectedVersion: null.API §8.11
4BackendValidates no duplicate weekend day; inserts the single row.configured: true, version: 0.Backend §6.4

Sequence Diagram

Branches and Edge Cases

BranchConditionBehaviorError/Result
Duplicate weekend daye.g. [6, 6]RejectCALENDAR_WEEKEND_INVALID
Seven-day schoolweekendDays: []Accepted as a legal, complete configurationconfigured: true, weekendDays: []
First save claims a versionexpectedVersion is a number, not null, on the first saveReject — the caller cannot have read a version that never existedATTENDANCE_VERSION_CONFLICT
Read before any saveGET with no row presentNever errors; returns every field with shipped defaultsconfigured: false, weekendDays: [], schoolDayCutoffMinute: 1020, version: null

5.2 Generate the academic calendar

Summary

Once the week is configured, an administrator generates the full calendar for a session in one action — every Gregorian date from the session's start up to (but not including) its end becomes a row, classified as weekend or school_day from the configured weekend, with its Bikram Sambat date computed and stored.

Preconditions

  • Caller holds AcademicCalendar_CREATE.
  • school_week_config has a saved row (§5.1).
  • The academic session exists and every date in its range falls within Bikram Sambat 2000–2090.

Main Flow

StepActor/SystemActionResultSource
1AdminOpens a session's calendar screen for the first time.Empty list; coverage.presentDays = 0.API §8.2
2AdminClicks "Generate calendar".POST .../calendar/generate.API §8.1
3BackendReads the week config; refuses if absent.CALENDAR_WEEKEND_NOT_CONFIGURED, or continue.Backend §6.2
4BackendEnumerates every date, converts each to BS, classifies by weekend membership, and inserts.academic_calendar_days fully populated.Backend §6.2
5BackendReapplies every already-existing holiday of the session onto the freshly-inserted rows.Holidays created before generation are not lost.Backend §6.6
6AdminSees the full grid, with coverage.presentDays === coverage.expectedDays.—API §8.2

Sequence Diagram

Branches and Edge Cases

BranchConditionBehaviorError/Result
Week not configuredschool_week_config has no rowRefuseCALENDAR_WEEKEND_NOT_CONFIGURED
Session dates beyond BS 2090Any enumerated date converts out of rangeRefuse the whole operation, nothing partially writtenCALENDAR_BS_DATE_OUT_OF_RANGE
Repeat generation, nothing changedCalled a second time with no admin editsEvery unlinked auto row is deleted and reinserted; result is the same coverage, different diff countersgenerated/deleted both equal the previous run's row count
Repeat generation after week changeschool_week_config changed since the last generateEvery auto row is rewritten against the new weekend rule; staleDays in the pre-generation coverage read drops to 0 after—
An admin_modified day existsAn operator previously corrected a dayExcluded from the delete predicate entirely — never touched by generationgenerated/deleted are one less than a run with no such day
Holiday created before first generationA holiday exists for a session with zero calendar rowsPicked up on the next generate() call via the holiday-reapply step, not lostrepaired > 0 on that run

5.3 Correct a calendar day by hand

Summary

An administrator overrides one day's classification, adds an event name visible to every audience, or writes an internal note. This is how a school marks an unplanned event, a special school day, or corrects a seeded holiday date that turned out wrong for their calendar — without waiting for a data update.

Preconditions

  • Caller holds AcademicCalendar_UPDATE.
  • The calendar for that session/date has already been generated (CALENDAR_DAY_NOT_FOUND otherwise).
  • Caller has the current version of the day, from a prior read.

Main Flow

StepActor/SystemActionResultSource
1AdminOpens a day's detail drawer.GET .../calendar/days/:date returns the current row plus its version.API §8.4
2AdminChanges classification and/or adds an event name/note.Local form state.—
3AdminSaves.PATCH .../calendar/days/:date with expectedVersion.API §8.5
4BackendMoves the day's source to admin_modified if it was auto/seed.Protected from the next regeneration and from holiday recomputation.Backend §6.3

Sequence Diagram

Branches and Edge Cases

BranchConditionBehaviorError/Result
Stale versionA second admin already edited this dayRejectATTENDANCE_VERSION_CONFLICT
Invalid free texteventName/note empty, padded, or with control charactersRejectCALENDAR_TEXT_FORMAT_INVALID
No-op patchOnly expectedVersion sentStill succeeds; version still bumps by one200, source unchanged if already not auto/seed
Day already admin_modifiedEditing an already-corrected dayStays admin_modified—
Day not yet generatedDate outside what generate() has producedRejectCALENDAR_DAY_NOT_FOUND

5.4 Manage holidays (create, narrow/widen, reclassify, delete)

Summary

An administrator records a holiday — a single lunar festival, a multi-day school closure, or an emergency — and the platform applies it to every covered calendar day immediately. Editing the range or classification later re-applies the change; deleting reverts the covered days to whatever the remaining holidays (or the plain weekend rule) say they should be.

Preconditions

  • Caller holds the relevant AcademicCalendar_CREATE/_UPDATE/_DELETE code.
  • Every date in the holiday's range must already be generated (ACADEMIC_CALENDAR_NOT_GENERATED otherwise).
  • The range must be wholly inside the session (CALENDAR_RANGE_OUTSIDE_SESSION otherwise).

Main Flow

StepActor/SystemActionResultSource
1AdminCreates a holiday with a name, classification, and date range.POST .../holidays.API §8.7
2BackendLinks every covered calendar day and recomputes each.Days show the holiday's classification and its source.Backend §6.6
3AdminLater narrows, widens, or reclassifies the holiday.PATCH /holidays/:id.API §8.8
4BackendUnlinks every day, relinks from the new range, recomputes both the newly-covered and the newly-dropped days.—Backend §6.6
5AdminDeletes a school-sourced holiday no longer needed.DELETE /holidays/:id.API §8.9
6BackendCascades the link deletion, recomputes each formerly-covered day from the holidays that remain.—Backend §6.6

Sequence Diagram

Branches and Edge Cases

BranchConditionBehaviorError/Result
Holiday classified school_dayclassification: "school_day" on create or updateReject — a contradiction, not a variantCALENDAR_HOLIDAY_RANGE_INVALID
Range not generatedA date in the range has no calendar rowReject the whole holiday, never a partial linkACADEMIC_CALENDAR_NOT_GENERATED
Range outside sessionAny date falls outside [startDate, endDate)RejectCALENDAR_RANGE_OUTSIDE_SESSION
Two holidays cover one dayA school closure overlaps a public holidayBoth link; HOLIDAY_PRECEDENCE decides the visible classificationpublic_holiday outranks school_holiday, which outranks special_school_day
Delete the higher-precedence holidayThe winning holiday of an overlapping pair is deletedThe day falls back to the remaining, lower-precedence holidayDay's classification changes to the remaining holiday's
Delete the last holiday on a dayNo holiday remains linkedThe day falls back to weekend/school_day against the live week config, source: auto—
Delete a seeded holidaysource: "seed"Reject — reclassify insteadSCHOOL_HOLIDAY_NOT_DELETABLE
Holiday covers an admin_modified dayCreate, update, or delete touches a day an admin already correctedThe link is created/removed, but the day's classification/source is left untouched—
Stale version on updateTwo admins edit the same holidayRejectATTENDANCE_VERSION_CONFLICT

5.5 Read the calendar as any audience (mobile)

Summary

A pupil, guardian, teacher, or staff member opens the calendar tab and sees the current Bikram Sambat month — with no configuration, no permission, and no per-audience difference in what is shown.

Preconditions

  • Caller holds a valid JWT (any role).
  • The requested session/month has already been generated by an administrator (§5.2) — otherwise the read is refused, not silently empty.

Main Flow

StepActor/SystemActionResultSource
1UserOpens the calendar tab.GET /api/mobile/calendar with no query params.API §8.12
2BackendResolves "today's" session and BS month via SchoolClockService.—Backend §6.7
3BackendReads the month's rows, maps each by hand to the reduced audience DTO.note, source, ids, and version never leave the server.Backend §6.7
4UserNavigates to another month.GET /api/mobile/calendar?bsYear=...&bsMonth=....API §8.12

Sequence Diagram

Branches and Edge Cases

BranchConditionBehaviorError/Result
Half a month specifiedOnly bsYear OR only bsMonth in the queryReject rather than half-defaultCALENDAR_BS_DATE_INVALID
No session contains todayEvery session has ended or not yet startedRejectACADEMIC_SESSION_NOT_FOUND
Month not generatedThe resolved session/month has zero rowsReject, never an empty-but-200 responseACADEMIC_CALENDAR_NOT_GENERATED
Same request from any rolePupil vs. parent vs. teacher vs. staffByte-identical response shapeNo audience branch exists in the handler at all

5.6 See the next days of the calendar (any audience)

Summary

A dashboard shows the next few days: which are school days, which are holidays, and any multi-day events such as a festival break.

Main Flow

StepActor/SystemActionResultSource
1ClientGET /api/calendar/upcoming?days=N, N from 1 to 31 (default 7)The request.CalendarUpcomingController.upcoming
2SystemBuilds N consecutive school-local dates from today and picks one calendar row per date.days, each with its BS date, classification and isOperatingDay.CalendarUpcomingService
3SystemMerges consecutive notable days that share a classification and event name.events, each with start and end dates in both calendars.CalendarUpcomingService

Branches and Edge Cases

BranchConditionBehaviorResult
Day with no calendar rowThe calendar has not decided that dateClassification and isOperatingDay are null; shown as undecided, not as a school day or a holiday.200
Invalid daysNot an integer, below 1 or above 31Rejected.400 VALIDATION_FAILED
Redis slow or downCache read exceeds its 250 ms budget or failsRead from the database.200
Calendar edited moments agoA holiday or day correction was just savedThe previous result is served until the 60-second cache expires.200

6. Admin Flows

6.1 Create (holiday)

See §5.4, step 1. Route: POST /api/academic-sessions/:id/holidays. Permission: AcademicCalendar_CREATE.

6.2 List / Read detail (calendar days and holidays)

See §5.2 step 6, §5.3 step 1, and API §8.2/§8.3/§8.4/§8.6. Every list is paginated (QueryDto defaults: page size 20, cap 100, unpaginated hard cap 1000); every detail read is by exact key (date, or holiday id).

6.3 Update (holiday, calendar day, week config)

See §5.3 (calendar day) and §5.4 step 3 (holiday). Both require expectedVersion and are refused with ATTENDANCE_VERSION_CONFLICT on a stale token. Week-config update is covered in §5.1.

6.4 Delete (holiday only)

See §5.4 steps 5–6. There is no delete for calendar days or the week configuration — a calendar day is corrected via PATCH, never removed, and the week-config row is never deleted once created.

6.5 Reorder

Not applicable. No orderable list exists in this module — calendar days are ordered by date and holidays by fromDate, both fixed sort orders with no user-controlled reordering.

6.6 Activate/deactivate

Not applicable. Neither a calendar day nor a holiday nor the week configuration has an active/inactive flag; a day's classification is the closest analogue and is covered under Update.

6.7 Soft delete / Restore

Not applicable. Every table in this module (school_week_config, school_holidays, academic_calendar_days, school_holiday_days) uses hard delete only, and only school_holidays (non-seed) is ever deleted at all — see §6.4.

6.8 Export/import

Not applicable. No export or import endpoint exists in this module.

6.9 Moderation

Not applicable. There is no moderation queue or approval workflow in this module — every admin write takes effect immediately.

6.10 Manual retry

Not applicable in the queue-retry sense — there is no async job in this module to retry. The closest analogue is re-running POST .../calendar/generate, which is designed to be safely repeatable (§5.2) and functions as the module's own "repair" action.

7. Lifecycle and State Transitions

7.1 Calendar day classification/source lifecycle

EntityFromEvent/ActionToGuard ConditionSide Effects
academic_calendar_days(absent)generate() insertsweekend/school_day, source: autoDate not already presentgenerated_from_weekend_days stamped with the live config
academic_calendar_days (source: auto)weekend/school_dayHoliday create/reapply covers the dayHoliday's classification, source = holiday's own source (school/seed)Holiday range includes the dayschool_holiday_days link created
academic_calendar_days (source: school/seed)Holiday classificationHoliday deleted, another still covers the dayRemaining holiday's classification per HOLIDAY_PRECEDENCEAt least one holiday still linkedLink removed for the deleted holiday
academic_calendar_days (source: school/seed)Holiday classificationLast covering holiday deletedweekend/school_day against the live week config, source: autoNo holiday remains linkedgenerated_from_weekend_days re-stamped
academic_calendar_days (any source except admin_modified)anyAdmin PATCHWhatever the PATCH set, source: admin_modifiedexpectedVersion matchesExcluded from every future regeneration/recomputation
academic_calendar_days (source: admin_modified)anyRegeneration or holiday recomputation attempts to touch itUnchanged—The row is skipped entirely — never deleted, never recomputed

Diagram:

7.2 School week configuration lifecycle

EntityFromEvent/ActionToGuard ConditionSide Effects
school_week_config(no row)First PUT .../week-configRow exists, configured: trueexpectedVersion is null/omittedCalendar generation becomes possible
school_week_configexistsLater PUT .../week-configUpdated weekendDays/schoolDayCutoffMinute, version + 1expectedVersion matches stored versionEvery auto day's generated_from_weekend_days becomes stale until the next generate()

Diagram:

9. Data and Side Effects by Flow

FlowDB WritesCache EffectsJobsRealtimeAnalyticsNotifications
Configure the school weekschool_week_configNoneNoneNoneNoneNone
Generate the calendaracademic_calendar_days (delete + insert), school_holiday_days (via reapply)NoneNoneNoneNoneNone
Correct a day (PATCH)academic_calendar_daysNoneNoneNoneNoneNone
Create/update/delete a holidayschool_holidays, school_holiday_days, academic_calendar_daysNoneNoneNoneNoneNone
Read the calendar (admin or mobile)NoneNoneNoneNoneNoneNone

No flow in this module writes to a cache, enqueues a job, emits a realtime event, records an analytics event, or sends a notification. The attendance module separately reads academic_calendar_days.classification to decide whether to expect a register and whether to alert a guardian, but those side effects belong to the attendance module's own flows, not to this one.

10. Error and Recovery Flows

ScenarioTriggerUser/System ExperienceRecoverySource
Week not configuredGeneration attempted with no school_week_config row409 on generateSave a week configuration, then retry generateCalendarGenerateService.generate
Version conflictTwo admins edit the same day/holiday/week-config concurrently409 on the second writerReload the row and retryATTENDANCE_VERSION_CONFLICT throughout
Holiday range not generatedHoliday create/update targets dates the calendar has not produced yet409 on the holiday writeGenerate the calendar for that range, then retryACADEMIC_CALENDAR_NOT_GENERATED
BS range exceededA session's dates fall outside BS 2000–2090422 on generateCorrect the session's Gregorian datesCALENDAR_BS_DATE_OUT_OF_RANGE
Portal month not generatedA pupil/parent opens a month an admin has not generated yet409 on the mobile readAn administrator must generate that far aheadACADEMIC_CALENDAR_NOT_GENERATED
Seed holiday delete attemptedAn admin tries to delete a platform-populated holiday409, with the reclassify path suggestedPATCH the holiday's classification insteadSCHOOL_HOLIDAY_NOT_DELETABLE

No queue or cache exists in this module, so there is no queue-failure or cache-failure recovery flow to document — the "Recovery" column above is always a direct retry of the same HTTP call after fixing the stated condition.

11. Diagrams Required Per Module

  • Actor capability diagram: §3, §4 (tables; the actor set is small enough that a table is clearer than a diagram).
  • High-level module flow diagram: §6.10, §9.1 of the backend doc.
  • Sequence diagram for each major flow: §5.1–§5.5.
  • State machine diagram for every lifecycle: §7.1, §7.2.
  • Data side-effect diagram: §9 (table; no flow has enough distinct side-effect categories to warrant a graph).
  • Error branch diagram: §6.10 (generic admin-write branch); each flow's own error table in §5 covers its specific branches.

12. Mandatory Feature and Flow Deep-Dive Pack

12.1 Feature Inventory With Minor Behaviors

FeatureMinor BehaviorActorTriggerUser/System ResultBackend Side EffectSource
Week configurationRead before any saveAdminGET .../week-configFull shape with shipped defaults, never a partial objectNoneschool-week-config.service.ts:38-49
Week configurationSeven-day schoolAdminweekendDays: [] on saveAccepted as complete, not incompleteRow saved with empty arrayschool-week-config.service.ts
Week configurationDuplicate day rejectedAdminweekendDays with a repeat422Noneschool-week-config.service.ts:143-149
Calendar generationRepeat call, no changesAdmingenerate() twiceSame coverage, different diff countersDelete + reinsert every unlinked auto rowcalendar-generate.service.ts:31-38
Calendar generationadmin_modified day preservedAdmingenerate() after a PATCHDay untouchedExcluded from the delete predicateacademic-calendar.integration.spec.ts
Calendar generationHoliday created before first generationAdmingenerate() after an out-of-order holiday createHoliday applied retroactivelyreapplyAllForSessionholiday-day-apply.service.ts:171-176
Calendar generationBS out-of-range refusalAdminSession dated beyond BS 2090422, nothing writtenWhole transaction rolled backcalendar-generate.service.ts:74-79
Day correctionNo-op patch still bumps versionAdminPATCH with only expectedVersion200, version + 1needsReview self-assigned so the trigger firescalendar-day-write.service.ts:107-112
Day correctionText trimmed server-sideAdmineventName/note with leading/trailing spaceStored trimmed—calendar-day-write.service.ts:156-161
Day correctionClearing a fieldAdmineventName: nullField cleared—calendar-day.dto.ts:155-163
Holiday createSingle-day holidayAdminfromDate === toDateAccepted—school-holidays_dates_ordered
Holiday createOverlap with a different classificationAdminTwo holidays cover one dayBoth link; precedence decides the visible classificationschool_holiday_days gets two rows for that dayacademic-calendar.integration.spec.ts
Holiday updateNarrowing reverts fallen-out daysAdminShrinking toDateDropped days recomputerecomputeMany on the dropped setschool-holiday.service.ts:294-319
Holiday updateWidening applies to newly-covered daysAdminGrowing the rangeNewly-covered days recomputelinkAndRecompute on the new setschool-holiday.service.ts:306-314
Holiday deleteCascading unlinkAdminDelete a school-sourced holidayLinks removed via FK cascadeFormerly-covered days recomputeschool-holiday.service.ts:352-364
Holiday deleteSeed protectionAdminDelete a source: seed holiday409, no writeNoneschool-holiday.service.ts:345-350
Portal readHalf-a-month refusalAny userOnly one of bsYear/bsMonth400Nonecalendar-portal.service.ts:44-49
Portal readDefault to "now"Any userNo query params at allCurrent session/month resolved via SchoolClockServiceNonecalendar-portal.service.ts:52-55
Portal readFrozen field setAny userAny successful callIdentical shape regardless of roleMapper never spreads a rowportal-calendar.dto.ts:71-93

12.2 Business Process Diagram Pack

12.3 Business Rules and Policy Traceability

RuleBusiness ReasonActor ImpactEnforced InAPI ImpactBackend ImpactTests
No default school weekNepal's standard week differs from international-curriculum schools; a wrong default silently mis-classifies a year of Fridays.Admin must configure once before generatingMigration (no seed row)CALENDAR_WEEKEND_NOT_CONFIGURED on generateCalendarGenerateService.generate's pre-checkacademic-calendar.integration.spec.ts:"refuses generation while school_week_config has no row"
A holiday is never school_dayA holiday recorded as a school day is a contradiction.Admin sees a clear rejection rather than a silently-ignored valueSchoolHolidayService.assertNotSchoolDay400 CALENDAR_HOLIDAY_RANGE_INVALIDService guard, not a CHECK—
Holiday precedence is fixed and orderedTwo holidays may cover one day; deletion must recompute, not revert.Admin sees the correct classification survive a partial deletionHOLIDAY_PRECEDENCE, HolidayDayApplyService.recomputeDayDay-detail response reflects the winneracademic_calendar_days classification/sourceacademic-calendar.integration.spec.ts:"keeps a day classified by the remaining holiday..."
admin_modified survives everythingAn operator's correction must never be silently overwritten.Admin's edits are durableCalendarGenerateService's delete predicate; recomputeDay's early return—academic_calendar_days.sourceacademic-calendar.integration.spec.ts:"preserves an admin_modified day across regeneration"
Seed holidays are not deletablePreserves the record of what the platform populated.Admin must reclassify instead of deleteSchoolHolidayService.remove409 SCHOOL_HOLIDAY_NOT_DELETABLE—academic-calendar.integration.spec.ts:"refuses to delete a seed holiday"
The audience read carries no administrator-internal fieldThe route has no permission check; the DTO shape is the actual control.Every pupil/parent/staff sees the same, safe projectionPortalCalendarDayDto's hand enumerationResponse never grows a field by inheritance——
Only superadmin holds full CRUD by defaultThe day/holiday rows carry an administrator's free-text notes and identity.Staff/teacher/guardian/student can view but never writeseed-auth.ts role grants403 for any non-superadmin admin route call——

12.4 Tradeoffs and Product Rationale

Product DecisionUser BenefitEngineering BenefitAlternativeTradeoffRisk
No default weekend, everA school never silently gets the wrong calendarNo branch to keep in sync with "which country/curriculum"Ship a Nepal-standard defaultEvery school configures once, even one that would have wanted the defaultAn admin who skips this step is blocked at generation, not silently wrong
Materialised calendar, not computed on readFast, stable reads; per-day edits are possible at allOne row per date is simple to reason about and indexCompute on the fly from a rule + holiday listStorage cost, explicit staleness trackingA school that changes its week must remember to regenerate
Audience read has no permission checkEvery pupil/parent/staff sees the calendar with zero extra setupOne shared implementation instead of four audience-scoped onesRequire AcademicCalendarView_READ on the route itselfThe DTO shape becomes the only safety net for this routeA future careless change (spreading a row) would need catching in review
Seed holidays are curated for one BS year onlyNo school is shown a guessed festival dateNo temptation to extrapolate incorrect data forwardExtrapolate approximate lunar dates for future yearsAdministrators must add next year's festivals by hand until the table is extendedA school relying entirely on seed data for a future year gets none

12.5 Flow Edge-Case Matrix

FlowEdge CaseTriggerExpected BehaviorUser/System FeedbackSource
Week configurationEmpty stateNo row ever savedFull shape with defaults, never a crashconfigured: falseschool-week-config.service.ts:38-49
Week configurationFirst useFirst PUTInsert, configured: true200school-week-config.service.ts:85-102
Calendar generationDuplicate actionCalled twice with no changesResult-idempotentSame coverage both timesacademic-calendar.integration.spec.ts
Calendar generationConcurrent actionTwo admins generate at onceBoth succeed; the transaction serializes the writesLast-committed state wins; no partial corruption(structural — one transaction per call)
Calendar generationMissing dependencyNo week configRefusedCALENDAR_WEEKEND_NOT_CONFIGUREDcalendar-generate.service.ts:58-63
Day correctionExpired state (stale version)Two admins edit one daySecond writer refusedATTENDANCE_VERSION_CONFLICTcalendar-day-write.service.ts:125-131
Holiday createPermission mismatchNon-superadmin without AcademicCalendar_CREATERefused403permission-catalog.ts, seed-auth.ts
Holiday createGuest limitationNo unauthenticated caller exists for this routeN/A401JwtAuthGuard
Holiday createMissing dependencyRange not yet generatedRefusedACADEMIC_CALENDAR_NOT_GENERATEDholiday-day-apply.service.ts:50-60
Holiday listUnsupported filter/sortsort/order/search suppliedAccepted by the DTO, silently ignored — the list is always ordered by fromDate200, unaffected orderingschool-holiday.service.ts:114-121
Calendar listUnsupported filter/sortSame as above, ordered by gregorianDateSameSamecalendar-read.service.ts:183-189
Portal readCache stale/missN/A — no cache existsN/AN/A—
Portal readQueue failureN/A — no queue existsN/AN/A—
Portal readLast itemRequesting the final day of a session's last generated monthReturns normally, no special-casing at the boundary200calendar-portal.service.ts

12.6 Flow-to-Data Trace

FlowReadsWritesCacheJobs/EventsResponse Fields
Configure weekschool_week_configschool_week_configNoneNoneconfigured, weekendDays, schoolDayCutoffMinute, version
Generate calendaracademic_sessions, school_week_config, academic_calendar_days, school_holidays, school_holiday_daysacademic_calendar_days, school_holiday_daysNoneNonegenerated, deleted, repaired, coverage
Correct a dayacademic_calendar_daysacademic_calendar_daysNoneNoneFull CalendarDayDetailDto
Manage a holidayschool_holidays, academic_calendar_days, academic_sessionsschool_holidays, school_holiday_days, academic_calendar_daysNoneNoneSchoolHolidayDto, and every recomputed day's classification/source (visible on the next day/month read)
Read the calendar (admin or mobile)academic_calendar_days, school_holiday_days/school_holidays (admin month view only)NoneNoneNoneCalendarDayDto/MonthCalendarDayDto/CalendarDayDetailDto (admin) or PortalCalendarDayDto (mobile)

12.7 Experience Quality Checklist

  • The doc explains what the actor is trying to accomplish (configure once, generate once, correct as needed, view without setup).
  • The doc explains what the backend does that the actor does not see (precedence recomputation, staleness tracking, the admin_modified protection).
  • The doc covers every minor flow and branch, including no-op patches, seed protection, and unsupported-filter behavior.
  • The doc includes admin and shared-audience flows; there is no distinct worker/system-triggered flow in this module (no queue or scheduler exists here).
  • The doc explains business logic, tradeoffs, and rationale, including the deliberate absence of a default weekend and the reasoning behind the no-permission portal route.
  • The doc maps every flow to API routes and backend side effects.
  • The doc includes diagrams appropriate to each flow type.
  • The doc covers edge cases and failure recovery, including staleness, version conflicts, and the BS-range boundary.

13. Completion Checklist

  • Every feature, minor action, and submodule capability is listed.
  • Every actor has allowed and forbidden behavior documented (§3).
  • Every major and minor flow includes steps, branches, and diagrams (§5, §12.1, §12.5).
  • Every lifecycle has a transition table and state diagram (§7.1, §7.2).
  • Every flow links to the API and backend docs.
  • No TDD doc exists for this module, so no TDD dependency is called out; the API and backend docs are the complete set of siblings produced.

See Also

On this page

Academic Calendar Features and Flows1. Documentation Evidence2. Feature Summary3. Actor Matrix4. Capability Matrix5. User-Facing Flows5.1 Configure the school week (first time)SummaryPreconditionsMain FlowSequence DiagramBranches and Edge Cases5.2 Generate the academic calendarSummaryPreconditionsMain FlowSequence DiagramBranches and Edge Cases5.3 Correct a calendar day by handSummaryPreconditionsMain FlowSequence DiagramBranches and Edge Cases5.4 Manage holidays (create, narrow/widen, reclassify, delete)SummaryPreconditionsMain FlowSequence DiagramBranches and Edge Cases5.5 Read the calendar as any audience (mobile)SummaryPreconditionsMain FlowSequence DiagramBranches and Edge Cases5.6 See the next days of the calendar (any audience)SummaryMain FlowBranches and Edge Cases6. Admin Flows6.1 Create (holiday)6.2 List / Read detail (calendar days and holidays)6.3 Update (holiday, calendar day, week config)6.4 Delete (holiday only)6.5 Reorder6.6 Activate/deactivate6.7 Soft delete / Restore6.8 Export/import6.9 Moderation6.10 Manual retry7. Lifecycle and State Transitions7.1 Calendar day classification/source lifecycle7.2 School week configuration lifecycle9. Data and Side Effects by Flow10. Error and Recovery Flows11. Diagrams Required Per Module12. Mandatory Feature and Flow Deep-Dive Pack12.1 Feature Inventory With Minor Behaviors12.2 Business Process Diagram Pack12.3 Business Rules and Policy Traceability12.4 Tradeoffs and Product Rationale12.5 Flow Edge-Case Matrix12.6 Flow-to-Data Trace12.7 Experience Quality Checklist13. Completion ChecklistSee Also