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 (Timetable Module)

StudyLyon — multi-tenant ERP / School Management API. This package designs the Timetable module client (Flutter, forward-looking spec) against the implemented NestJS backend. All endpoints, DTO fields, schemas, events, and business rules are derived directly from src/modules/timetable/**, src/modules/academics/** (class/subject references), src/modules/teachers/**, src/modules/rooms/**, src/modules/rbac/**, and docs/IMPLEMENTATION_PLAN.md. No feature is invented; gaps are flagged and marked (planned) / (proposed) / (forward-looking).


1. Purpose

Timetable is the scheduling domain: the weekly grid of teaching slots — one timetable_entries document per (class, subject, teacher, day, time range), optionally pinned to a room, all scoped to one academic year. It answers "what happens where and with whom, on which day and at what time" for classes, teachers, and rooms.

ResponsibilitySource
Create a timetable entry (only write op implemented)timetable.controller.ts:14-18
Conflict detection: teacher double-booking + room overlap on the same daytimetable.service.ts:16-30
Read timetable by class — sorted dayOfWeek, startTimetimetable.service.ts:48-53
Read timetable by teacher — sorted dayOfWeek, startTimetimetable.service.ts:55-60
Six-day week model (Monday–Saturday; no Sunday)timetable.schema.ts:7-14
Time range stored as zero-padded HH:MM strings (lexical compare)timetable.schema.ts:33-37; timetable.service.ts:62-69
Tenant isolation + soft-delete scope on every querybase.repository.ts:20-30 (via TimetableRepository)
Domain event on createtimetable.service.ts:33-44 (TimetableEntryCreated)
Weekly-grid feature flag (school + coaching)IMPLEMENTATION_PLAN.md:327-328

2. Module boundary

AspectTimetableNeighbouring module
Ownstimetable_entries docs (day/time slot semantics, conflict rule)
ReferencesClass, Subject, Teacher, Room, AcademicYear as ObjectIds (raw refs, no population server-side)timetable.schema.ts:18-40
Does not ownclasses, subjects, teachers, rooms, academic years — all read from their modules' catalogsclass.schema.ts, subject.schema.ts, teacher.schema.ts, room.schema.ts
Todaycreate + two read pathstimetable.controller.ts:10
Roadmapbulk create, conflict UI, substitution, workload, exportIMPLEMENTATION_PLAN.md:226

A Class may carry its own default roomId string (class.schema.ts:30-31) — a separate field from the timetable entry's roomId ObjectId; the client must not treat them as the same source of truth.

3. Entry lifecycle (end-to-end)

Catalog ready (classes, subjects, teachers, rooms, academic years exist)
   → Entry created   POST /timetable          [conflict check → 409]
      → TimetableEntryCreated event emitted (no queue routing found — gap, see §6)
      → Read back     GET /timetable?classId= | GET /timetable?teacherId=
   → Edit / Delete    — **no endpoints implemented** (planned)

Deletion semantics today: none — there is no DELETE /timetable/:id. The schema extends BaseSchema (soft-delete columns exist) but no route, service method, or repository call uses them for timetable. Any "remove slot" UI is (planned).

4. Conflict detection — the one real business rule

TimetableService.create loads existing entries for the same day where teacherId or roomId matches the new entry (timetable.service.ts:17-22), then throws ConflictException('Schedule conflict detected') if any of them overlaps in time (timetable.service.ts:24-30, 62-69):

CheckCovered?Evidence
Same teacher, same day, overlapping time✅ 409$or clause {teacherId, dayOfWeek} (timetable.service.ts:19)
Same room, same day, overlapping time✅ 409 (only when roomId set){roomId, dayOfWeek} (timetable.service.ts:20)
Same class, same day, overlapping timenot checked — two entries for one class may overlapquery has no classId clause (timetable.service.ts:17-22)
Boundary overlap (end == start of another entry)✅ no overlap (start1 < end2 && start2 < end1, timetable.service.ts:68)back-to-back periods are legal
Overlap with other day❌ no check (correct by design — weekly model)day filter only

When roomId is absent from the DTO, Mongoose strips undefined from the query, so the roomId clause is a no-op — effectively teacher-only conflict detection for room-less entries.

5. Permissions reality

  • timetable.read and timetable.create exist in the permission catalog (permissions.constants.ts:44-45).
  • TimetableController is guarded only by @UseGuards(JwtAuthGuard) (timetable.controller.ts:9); no @Roles/@Permissions metadata is applied, so the global RbacGuard passes any authenticated request (rbac.guard.ts:29).
  • Therefore the client must enforce timetable.read / timetable.create in UI/routing itself (mirror guard), and should not assume server-side 403s.

6. Events & side-effects

EventEmitted atQueue routingSide-effect
TimetableEntryCreatedtimetable.service.ts:33-44 (payload {entryId, classId, teacherId})none found — grep of event-queue-map.ts shows no mappingnothing wired; notifications/audit/search (planned)

Gap: the event is emitted but never routed; subscribers (e.g. teacher notification, audit trail) are (planned).

7. Dependencies

DependencyRoleSource
Academics — ClassclassId target of a slotclass.schema.ts:8-35 (name, gradeId, sectionId, capacity, classTeacherId)
Academics — SubjectsubjectId of a slotsubject.schema.ts:8-35 (code, name, shortName)
Academics — AcademicYearacademicYearId scopingtimetable.schema.ts:39-40
Teachers — TeacherteacherId of a slotteacher.schema.ts:15-52
Rooms — Roomoptional roomId of a slotroom.schema.ts:16-35; rooms.controller.ts:20
RBACtimetable.read / timetable.create perms (client-side gate only)permissions.constants.ts:44-45
Event busTimetableEntryCreated emissiontimetable.service.ts:33

8. Business goals

GoalMeasure
Build a weekly grid in < 2 min≤ N create calls, one per slot; conflict feedback ≤ 1 round trip
No teacher double-booking409 Schedule conflict detected (timetable.service.ts:28)
No room overlap409 when roomId set and overlapping
Tenant isolation structuralevery repo call injects tenantId + isDeleted:false (base.repository.ts:20-30)
Sorting contract stabledayOfWeek, startTime ascending on both read paths (timetable.service.ts:51,58)

9. Success metrics

  • Grid renders from a single GET /timetable?classId= response, server-sorted (timetable.service.ts:48-53) — client never re-sorts.
  • Entry create with conflict → inline AppBanner in the editor, form values kept.
  • Grid cells render ≥ 40 slots without jank on low-end devices (see 15 §9).
  • Zero tenantId leaks in bodies; cross-tenant reads are 404s (base.repository.ts:20-30).

10. Edge cases (derived)

  • GET /timetable with neither classId nor teacherId[] (timetable.controller.ts:26-28).
  • Both params present → classId wins (timetable.controller.ts:26-27).
  • Conflict on create → 409 ConflictExceptionno error code in body beyond the default NestJS shape; client matches on 409 status + message.
  • Times are plain strings (@IsString, create-timetable-entry.dto.ts:28-33): overlap math is lexical string comparison (timetable.service.ts:62-69) — valid only while times are zero-padded HH:MM; client must enforce the format (server does not).
  • dayOfWeek values: mondaysaturday only (timetable.schema.ts:7-14); sunday is rejected by @IsEnum (create-timetable-entry.dto.ts:23-25).

11. Assumptions (module)

  • Client is forward-looking: backend implements only create + two reads; the full client (grid UX, editors, room view) is specced here per user instruction. The PRD lists native mobile as a later-phase, read-only companion; this package specs a full client (read + write) anyway — the two mandates coexist; write surfaces target admin/coordinator roles.
  • Update/delete/bulk-import/substitution/workload/export are not implemented (IMPLEMENTATION_PLAN.md:226 marks "Bulk create, conflicts, substitution, workload, export" as a planned phase-4 row) → all such surfaces are (planned).
  • Room view exists only as a client composition (fetch per-class grids and merge); a dedicated GET /timetable?roomId= is (planned).
  • No Sunday teaching; saturday is a legal teaching day.

12. Open questions (module-level)

#ItemImpact
OQ-1No class-vs-class overlap check on create (timetable.service.ts:17-22 excludes classId) — is a class double-booked during the same period intentional (e.g. split classes)?Conflict banner logic, editor warnings
OQ-2No update/delete endpoints — how are mistakes corrected today (delete+recreate via planned routes)?Editor UX (edit-in-place vs delete+create)
OQ-3TimetableEntryCreated is emitted but unrouted (timetable.service.ts:33-44, no event-queue-map.ts entry) — intended consumers?Notification/audit roadmap
OQ-4No RBAC metadata on endpoints (timetable.controller.ts:9) despite timetable.read/create existing (permissions.constants.ts:44-45) — who may write?Permission UI
OQ-5Time format is unvalidated @IsString (create-timetable-entry.dto.ts:27-33) yet overlap detection depends on zero-padded lexical order — enforce HH:MM server-side?Validation + conflict correctness