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

StudyLyon — multi-tenant ERP / School Management API. This package designs the Parents module client (guardian profiles, student–parent linking, multi-child support) against the implemented NestJS backend. All endpoints, DTO fields, schemas, events, and wire contracts are derived directly from src/modules/parents/**, src/modules/students/**, src/modules/users/**, src/modules/rbac/**, src/infrastructure/**, studylyon-blueprint/04-Modules/Parents.md, and studylyon-blueprint/03-Database/COLLECTIONS.md. No feature is invented; gaps are flagged (planned) / (forward-looking) / (proposed) and itemized in the Assumptions & Open Questions section.


1. Purpose

The Parents module manages guardian identity (occupation, company, income, emergency-contact priority, pickup authorization) and the many-to-many graph between guardians and students (student_parent_links). The blueprint defines the module as "Guardians and emergency contacts" (04-Modules/Parents.md:3) with four responsibilities: guardian profile, student–parent linking, relationship type, and emergency-contact/pickup/financial flags (04-Modules/Parents.md:11-17).

ResponsibilitySource
Parent profile CRUD (occupation, company, income, emergency flags)parent.schema.ts:8-32, parent.controller.ts:29-46
One parents doc per users doc (unique tenantId+userId)parent.schema.ts:36
Link parents ↔ students (many-to-many) with relationship metadatastudent-parent-link.schema.ts:16-41, student-parent-link.service.ts:16-41
Relationship type enum (mother, father, guardian, grandparent, relative, foster_parent)student-parent-link.schema.ts:7-14
isPrimaryGuardian — primary-communication flag on the linkstudent-parent-link.schema.ts:27-29
Financial responsibility / pickup authorization / emergency prioritystudent-parent-link.schema.ts:30-38, parent.schema.ts:24-29
Domain events → BullMQ (in-app notification, audit)parent.service.ts:38-45,78-86,92-99, event-queue-map.ts:37-39
Soft delete (never hard delete; links survive parent deletion)parent.service.ts:89-100, base.repository.ts:68-74

Identity split (critical architectural rule): parents stores guardian business data only. Name, email, phone, avatar live on users (COLLECTIONS.md §2.3 "Domain collections store domain data"; users/schemas/user.schema.ts:16-78). The client must always resolve a parent's display identity through the linked User (parent.schema.ts:9-10).

2. Business goals

GoalMeasure / evidence
One guardian profile, many childrenLink table is the only M2M (student-parent-link.schema.ts:18-25); no child list stored on parents
Never duplicate profile dataparents.userId required ref (parent.schema.ts:9); identity lives in users
Primary-guardian semantics per childisPrimaryGuardian per link (student-parent-link.schema.ts:27)
Emergency-contact order per childemergencyPriority on both profile (default 0, parent.schema.ts:24-25) and link (default 0, student-parent-link.schema.ts:36-37)
Pickup authorization per childpickupAuthorization (profile, default false) + pickupAllowed (link, default true) — note the opposite defaults (parent.schema.ts:27-28, student-parent-link.schema.ts:33-34)
Removing a link never deletes a parent linked elsewhereDELETE /parents only soft-deletes the parent doc (parent.service.ts:89-100); link rows are independent (student-parent-link.schema.ts)
Multi-tenant isolationevery query scoped tenantId + isDeleted:false (base.repository.ts:20-30); parent index {tenantId, userId} unique (parent.schema.ts:36)

3. User goals

  • Org admin / admission staff: create a guardian profile from an existing user, link the guardian to one or more students, mark the primary guardian, switch primary when custody changes, unlink a guardian cleanly.
  • Parent / guardian: log in and see only their linked children (privacy boundary — persona: "Access: Only linked children", USER_PERSONAS.md:48-53), identify their own relationship per child (mother/father/…), see who is the primary guardian, update their own emergency-contact/pickup flags.
  • Multiple-guardian households: mother + father + grandparent each have their own login; each sees the same children; primary flag is per child.
  • Admission staff: during enrollment, attach guardians to the newly created student record (POST /students → then link).

4. Stakeholders

Institution admins, admission/front-desk staff, teachers (view linked guardians), parents/guardians (self-service, forward-looking), accountants (financial-responsibility flag feeds billing — fees module), platform support (audit trail via ParentUpdated / ParentDeleted audit events, event-queue-map.ts:38-39), QA + design + engineering.

5. Why this exists

Guardians are the school's primary external stakeholder: they receive attendance, results, and fee communications (blueprint: "Notifications — absentee/result alerts to linked parents", 04-Modules/Parents.md:49). Correctness of the link graph and the primary-guardian flag determines who gets billed, who gets pickups, and who gets called in an emergency — three flows where a wrong link is a real-world incident.

6. Dependencies

DependencyRoleSource
Users moduleidentity profile the parent links to (userId)parent.schema.ts:9; users.service.ts:49
Students modulestudents must exist before linkingstudent-parent-link.service.ts:27 (imports StudentRepository)
RBACglobal RbacGuard + parent role (student.read only)app.module.ts:131, role.schema.ts:50-56
Events / BullMQParentCreated → in-app parent-created; ParentUpdated/ParentDeleted → auditevent-queue-map.ts:37-39
Mongo collectionsparents, student_parent_linksCOLLECTIONS.md (§ parents / § student_parent_links)
BaseRepository / TenantContexttenant scoping + soft-delete filteringbase.repository.ts:20-36

7. Success metrics (proposed)

  • Zero cross-tenant parent reads (repository scoping structural, base.repository.ts:20-30).
  • Link graph integrity: every link's studentId/parentId resolves to a non-deleted doc in the same tenant (today not enforced — see OQ-3).
  • Parent self-service adoption: % of parents who log in (requires (forward-looking) my-children endpoints — see OQ-1).
  • Duplicate-link incidents = 0 (today not prevented — OQ-2).
  • Emergency-contact list accuracy: % of children with ≥ 1 primary guardian (not enforced).

8. Edge cases

  • Duplicate profile: second POST /parents with the same userId → 409 DUPLICATE_RESOURCE "Parent profile already exists for this user." (parent.service.ts:30-34, enforced by unique index parent.schema.ts:36).
  • Parent not found: GET/PATCH/DELETE /parents/:id and GET /parents/:id/students → 404 RESOURCE_NOT_FOUND "Parent not found." (parent.service.ts:51,77,91,70).
  • Link to missing student: POST /parents/link/:studentId calls studentRepo.findById(studentId) but discards the result — a link to a non-existent student is silently created (OQ-3; student-parent-link.service.ts:27).
  • Duplicate link: same (studentId, parentId) twice → two link docs allowed (index non-unique, student-parent-link.schema.ts:46) (OQ-2).
  • Multiple primary guardians: no server rule prevents two links with isPrimaryGuardian:true for one student (OQ-5).
  • Unlink: DELETE /parents/link/:linkId soft-deletes the link; if it was the only link, no parent deletion happens and no re-promotion of another guardian occurs (OQ-4).
  • Delete parent with active links: parent soft-deleted, links remain orphaned (pointing at a soft-deleted parent) — no cascade (OQ-3).
  • Invalid relationship string: LinkParentDto.relationship is @IsString() with no enum validation (link-parent.dto.ts:19-20); schema enum rejects on save → Mongoose ValidationError → 500 INTERNAL_SERVER_ERROR instead of 400 (OQ-6).
  • Invalid ObjectId in :id: CastError → 400 VALIDATION_ERROR "Invalid resource identifier." (http-exception.filter.ts:48,92).
  • Parent list ignores q and sort: find() applies only skip/limit (parent.service.ts:58-61) — search UI must not rely on q (OQ-7).

9. Assumptions (module)

  • Mobile client is forward-looking: backend is complete; this package is the UI-side spec (same policy as the auth package — PRD Phase 1 excludes native apps, 00-shared/12 A1).
  • RBAC is not wired to these endpoints. Global RbacGuard runs (app.module.ts:131), but ParentController declares only @UseGuards(JwtAuthGuard) (parent.controller.ts:23) and no @Permissions(...); the parent.* permissions in the blueprint (04-Modules/Parents.md:64-70) do not exist in ALL_PERMISSIONS (permissions.constants.ts:1-97). Any authenticated user can create/read/update/delete any parent in their tenant. UI must be built with intended-role gating and harden when RBAC lands (OQ-8).
  • There is no "my parent profile" resolution. No GET /parents/me and no userId filter on GET /parents; a logged-in parent cannot resolve their own parent profile or children with today's API. The my-children surface is (forward-looking) and needs either a new endpoint or a client-side mapping (OQ-1). Parent role carries only student.read (role.schema.ts:55).
  • Parent user accounts are created by admins via POST /users (no invite/password bootstrap specific to parents; no auth_accounts creation from the users module) (OQ-9). ParentCreated fires only after POST /parents (parent.service.ts:38-45).
  • No email on parent creation: ParentCreated routes to the in-app queue (event-queue-map.ts:37); email.worker.ts handles only UserRegistered and PasswordResetRequested (email.worker.ts:25-42).
  • Blueprint link routes (POST /students/:id/parents, DELETE /students/:id/parents/:linkId, 04-Modules/Parents.md:29-30) differ from the implemented controller routes (/parents/link/..., parent.controller.ts:47-60). Implementation wins; blueprint is historical.

10. Open questions (module-level; global ledger in 00-shared/12)

#ItemImpact
OQ-1No endpoint to fetch "my parent profile / my children" for the parent role (no /parents/me, no userId filter). Add?my-children screen is (forward-looking); privacy boundary cannot be exercised today
OQ-2Duplicate (studentId, parentId) links not prevented (non-unique index, no check). Add unique index + 409?QA duplicate-link case, link sheet double-submit
OQ-3linkStudentParent ignores the findById result; links to non-existent students, and links referencing soft-deleted parents, are stored. Enforce?QA link-validation case; data integrity
OQ-4Unlink does not reassign isPrimaryGuardian when the primary is unlinked; no rule enforces exactly one primary per student.primary-guardian switch UX; "who gets billed/called"
OQ-5isPrimaryGuardian on two links for one student is allowed. Client must self-enforce (or server adds exclusivity).QA primary-guardian case
OQ-6relationship in LinkParentDto is free string (@IsString()), schema enum is the only gate → invalid value yields 500, not 400. Add @IsEnum(RelationshipType)?client must always send valid enum; error mapping
OQ-7GET /parents ignores q and sort query params (only page/limit applied, parent.service.ts:58-61).parents list has no search/sort; UI must not fake it
OQ-8No parent.* permissions exist in ALL_PERMISSIONS; endpoints JWT-only. When does RBAC land?role-gating of every screen; who may link/unlink
OQ-9No parent-invitation flow (user + auth account + welcome email). Backend auth_accounts creation is out of the users module's scope.how a parent obtains login credentials
OQ-10GET /parents/:id/students returns raw link docs (studentId ObjectIds, student-parent-link.service.ts:34-36) — no populate or student summary. Client must join.linked-children cards need N+1 fetches or a (planned) enriched endpoint

11. Glossary (this module)

TermMeaning
Parent / GuardianBusiness profile (parents) linked to identity (users.userId)
Linkstudent_parent_links doc: one parent–student relationship with metadata
RelationshipTypemother | father | guardian | grandparent | relative | foster_parent (student-parent-link.schema.ts:7-14)
Primary guardianisPrimaryGuardian flag per link; intended recipient of primary communications (04-Modules/Parents.md:57)
Emergency priorityemergencyContactPriority (profile) / emergencyPriority (link), default 0
PickuppickupAuthorization (profile, default false) / pickupAllowed (link, default true)
Financial responsibilityfinancialResponsibility (link, default false)
Envelope{success,message,data,meta?,timestamp,requestId} (response-envelope.interceptor.ts:11-62)