Entity Relationship Diagrams
Logical data model for Raisd campus portals. Attributes and relationships
mirror Zod contracts in student-portal and admissions entities
from SDD-04. Split by domain so each diagram stays readable.
CMS and LMS tables share one ERD with distinct colours.
PortalRecordGraph. Marked proposed — not Live.
StudentProfile → ProgrammeEnrolment
→ programmeVersionId → ProgrammeVersion → Programme.
Keep ProgrammeVersion: enrolment stays on the curriculum/rules under which
the student enrolled (e.g. BBA 129 vs 122 credits) — never resolve from “current”
programme config. See domain map,
student record, and
architecture.
ProgrammeCampusOffering (programme version × campus), not
“faculty belongs to one campus ⇒ programme belongs to that campus”. The same
academic programme may be delivered at Botswana, Uganda, Sierra Leone, etc.
Faculty remains organisational. Enrolment still stores
ProgrammeEnrolment.campusId for the student’s study campus.
See curriculum and
architecture.
r_student / r_stdprogram / …
Legacy IDs live in LegacyEntityMap; messy translation lives in the
CAP-53 anti-corruption layer. Details:
LUCT CMS / Portal compatibility.
PORTAL_RECORD_SCHEMA_VERSION = 3).
University-default rows (campusId null) plus campus overrides
drive active-student / valid-semester membership, credit classification, CGPA
repeat rules, document numbering and adjustment enablement.
Per-campus Live confirmation remains readiness work — not a Schema blocker.
CS decision page.
0. Domain map
High-level aggregates and ownership boundaries. Blue = CMS · Green = LMS · Amber = shared bridge.
Aggregate hubs Shared
ProgrammeEnrolment and Campus are the primary CMS FK hubs.
ModuleOffering is the LMS delivery hub hanging off curriculum + term.
erDiagram
CAMPUS ||--o{ FACULTY : hosts
FACULTY ||--o{ PROGRAMME : organises
PROGRAMME ||--o{ PROGRAMME_VERSION : versions
PROGRAMME_VERSION ||--o{ PROGRAMME_CAMPUS_OFFERING : offered_at
CAMPUS ||--o{ PROGRAMME_CAMPUS_OFFERING : delivers
PROGRAMME_VERSION ||--o{ PROGRAMME_ENROLMENT : enrols_into
STUDENT_PROFILE ||--o{ PROGRAMME_ENROLMENT : has
CAMPUS ||--o{ PROGRAMME_ENROLMENT : studies_at
PROGRAMME_ENROLMENT ||--|| FINANCE_ACCOUNT : bills
PROGRAMME_ENROLMENT ||--o{ STUDY_PERIOD : spans
STUDY_PERIOD ||--o{ MODULE_REGISTRATION : registers
MODULE_OFFERING ||--o{ MODULE_REGISTRATION : offered_as
PROGRAMME_VERSION ||--o{ CURRICULUM_MODULE : contains
MODULE ||--o{ CURRICULUM_MODULE : listed_in
MODULE ||--o{ MODULE_OFFERING : delivered_as
MODULE_OFFERING ||--o{ MODULE_WEEK : weeks
MODULE_WEEK ||--o{ MODULE_LEARNING_ITEM : items
MODULE_OFFERING ||--o{ MODULE_ASSESSMENT : assesses
MODULE_REGISTRATION ||--o{ ASSIGNMENT_SUBMISSION : submits
MODULE_REGISTRATION ||--o{ LEARNING_PROGRESS : progress
CAMPUS {
string id PK
string code
string name
}
STUDENT_PROFILE {
string id PK
string studentNumber
}
PROGRAMME_ENROLMENT {
string id PK
string status
}
PROGRAMME_CAMPUS_OFFERING {
string id PK
string programmeVersionId FK
string campusId FK
}
FINANCE_ACCOUNT {
string id PK
string currencyCode
}
MODULE_OFFERING {
string id PK
string sectionCode
}
MODULE_WEEK {
string id PK
int weekNumber
}
MODULE_LEARNING_ITEM {
string id PK
string kind
}
ASSIGNMENT_SUBMISSION {
string id PK
string fileName
}
ProgrammeEnrolment.programmeVersionId pins curriculum/fee/rules.
Do not determine requirements dynamically from the programme’s current config.
ProgrammeVersion × Campus offerings (same pattern as
ModuleOffering).
1. Institution
Campus organisation, staff, and campus-scoped configuration.
Faculty is organisational (staff, programme ownership for admin).
Where a programme version is delivered is modelled by
ProgrammeCampusOffering, not by assuming one faculty ⇒ one campus
for academic offering.
Campus · Faculty · Staff · Config · Policy CMS
Prefer effective-dated campus policy tables for operational differences —
not scattered if (campus === …) in domain code.
1:1 finance profile; N immigration / graduation / tax configs;
university-default policy rows use campusId null.
erDiagram
CAMPUS ||--o{ FACULTY : has
FACULTY ||--o{ STAFF_MEMBER : employs
PERSON ||--o| STAFF_MEMBER : owns
PERSON ||--|| USER_ACCOUNT : may_login
USER_ACCOUNT ||--o{ USER_ROLE_GRANT : grants
ROLE ||--o{ USER_ROLE_GRANT : to_account
ROLE ||--o{ ROLE_PERMISSION : grants
PERMISSION ||--o{ ROLE_PERMISSION : used_by
CAMPUS ||--|| CAMPUS_FINANCE_PROFILE : configures
CAMPUS ||--o{ CAMPUS_SERVICE_TAX_PROFILE : taxes
CAMPUS ||--o{ CAMPUS_IMMIGRATION_CONFIG : immigration
CAMPUS ||--o{ CAMPUS_GRADUATION_CONFIG : graduation
CAMPUS ||--o| CAMPUS_PRIVACY_SECURITY : privacy
CAMPUS ||--o{ ANNOUNCEMENT : publishes
CAMPUS ||--o{ UNIVERSITY_POLICY : policies
CAMPUS ||--o{ CAMPUS_SERVICE_PAGE : services
CAMPUS ||--o{ REVIEW_QUESTIONNAIRE : reviews
CAMPUS ||--o{ CAMPUS_STATUS_MAPPING : status_map
CAMPUS ||--o{ CAMPUS_ACADEMIC_STATUS_POLICY : academic_status
CAMPUS ||--o{ CAMPUS_CREDIT_POLICY : credit
CAMPUS ||--o{ CAMPUS_CGPA_POLICY : cgpa
CAMPUS ||--o{ CAMPUS_DOCUMENT_NUMBER_POLICY : document_numbers
CAMPUS ||--o{ CAMPUS_ADJUSTMENT_POLICY : adjustments
CAMPUS {
string id PK
string code
string name
string countryCode
string timeZone
}
FACULTY {
string id PK
string campusId FK
string code
string name
}
STAFF_MEMBER {
string id PK
string personId FK
string facultyId FK
string staffNumber
string displayName
string academicTitle
string emailAddress
}
PERSON {
string id PK
string displayName
string personType
string studentProfileId FK
string staffMemberId FK
}
USER_ACCOUNT {
string id PK
string personId FK
string loginName
string status
datetime disabledAt
}
ROLE {
string id PK
string code
string name
}
PERMISSION {
string id PK
string code
string name
}
USER_ROLE_GRANT {
string id PK
string userAccountId FK
string roleId FK
datetime grantedAt
}
CAMPUS_FINANCE_PROFILE {
string id PK
string campusId FK
string currencyCode
string bankDetails
int processingDays
}
CAMPUS_SERVICE_TAX_PROFILE {
string id PK
string campusId FK
string jurisdiction
string registration
int rateBasisPoints
date effectiveFrom
date effectiveTo
}
CAMPUS_IMMIGRATION_CONFIG {
string id PK
string campusId FK
string programmeVersionId FK
string feeItemId FK
}
CAMPUS_GRADUATION_CONFIG {
string id PK
string campusId FK
string programmeVersionId FK
string feeItemId FK
}
ANNOUNCEMENT {
string id PK
string campusId FK
string title
string department
datetime publishedAt
boolean isPinned
}
REVIEW_QUESTIONNAIRE {
string id PK
string campusId FK
string version
}
CAMPUS_STATUS_MAPPING {
string id PK
string campusId FK
string sourceSystem
string sourceStatus
string canonicalStatus
string target
date effectiveFrom
date effectiveTo
}
CAMPUS_ACADEMIC_STATUS_POLICY {
string id PK
string campusId FK
string academicStatus
boolean countsAsValidStudyPeriod
boolean countsAsActiveStudent
date effectiveFrom
date effectiveTo
}
CAMPUS_CREDIT_POLICY {
string id PK
string campusId FK
string outcome
boolean countsAsAttempted
boolean countsAsEarned
boolean countsInGpa
date effectiveFrom
date effectiveTo
}
CAMPUS_CGPA_POLICY {
string id PK
string campusId FK
string repeatRule
date effectiveFrom
date effectiveTo
}
CAMPUS_DOCUMENT_NUMBER_POLICY {
string id PK
string campusId FK
string documentType
string prefix
string sequencePattern
date effectiveFrom
date effectiveTo
}
CAMPUS_ADJUSTMENT_POLICY {
string id PK
string campusId FK
string category
boolean enabled
string approvalRequirement
date effectiveFrom
date effectiveTo
}
Programme.facultyId). Multi-campus academic delivery uses
ProgrammeCampusOffering.
Person owns at most one StudentProfile and/or StaffMember;
login lives on UserAccount (disable/lock does not delete history).
Role and permission codes are demonstration seeds until the real RBAC matrix is confirmed.
Campus policy tables (campus_*_policies / campus_status_mappings)
store university defaults with campusId null; a campus row overrides
the default while both are effective on the reference date.
2. Curriculum
Programmes, versions, campus offerings, module catalogue, prerequisites, grading, and fee plans.
Programme · ProgrammeCampusOffering · Module · FeePlan CMS
Programme = academic identity · ProgrammeVersion = curriculum/rules · ProgrammeCampusOffering = where that version is delivered.
erDiagram
FACULTY ||--o{ PROGRAMME : organises
PROGRAMME ||--o{ PROGRAMME_VERSION : versions
PROGRAMME_VERSION ||--o{ PROGRAMME_CAMPUS_OFFERING : offered_at
CAMPUS ||--o{ PROGRAMME_CAMPUS_OFFERING : at_campus
PROGRAMME_VERSION ||--o{ CURRICULUM_MODULE : includes
MODULE ||--o{ CURRICULUM_MODULE : appears_in
CURRICULUM_MODULE ||--o{ MODULE_PREREQUISITE : requires
MODULE ||--o{ MODULE_PREREQUISITE : is_prereq_of
CAMPUS ||--o{ GRADING_SCHEME : defines
GRADING_SCHEME ||--o{ GRADE_BAND : bands
PROGRAMME_VERSION ||--o| FEE_PLAN : priced_by
FEE_PLAN ||--o{ FEE_ITEM : lines
MODULE ||--o{ FEE_ITEM : tuition_for
FUNDING_SCHEME ||--o{ STUDENT_FUNDING_AWARD : awards
PROGRAMME {
string id PK
string facultyId FK
string code
string title
string awardLevel
}
PROGRAMME_VERSION {
string id PK
string programmeId FK
string versionLabel
date effectiveFrom
date effectiveTo
int totalSemesterCount
float totalCreditsRequired
int minimumSemesterCredits
int maximumSemesterCredits
float minimumGraduationCgpa
float minimumAttendancePercent
}
PROGRAMME_CAMPUS_OFFERING {
string id PK
string programmeVersionId FK
string campusId FK
string localProgrammeCode
date effectiveFrom
date effectiveTo
string status
}
MODULE {
string id PK
string code
string title
string description
float defaultCredits
}
CURRICULUM_MODULE {
string id PK
string programmeVersionId FK
string moduleId FK
int recommendedSemesterNumber
float credits
string requirementType
}
MODULE_PREREQUISITE {
string id PK
string curriculumModuleId FK
string prerequisiteModuleId FK
string minimumGrade
}
GRADING_SCHEME {
string id PK
string campusId FK
string name
float maximumPoints
}
GRADE_BAND {
string id PK
string gradingSchemeId FK
string label
float minimumScore
float points
string outcome
}
FEE_PLAN {
string id PK
string programmeVersionId FK
string currencyCode
}
FEE_ITEM {
string id PK
string feePlanId FK
string moduleId FK
string category
string label
int amountMinor
string serviceTaxTreatment
string serviceTaxExemptionCode
}
FUNDING_SCHEME {
string id PK
string name
string kind
boolean renewable
}
FeeItem.category: tuition | registration | books | resource |
international-services | repeat-administration | other.
Tuition items must reference a Module; non-tuition must not.
ProgrammeCampusOffering for the version — not Faculty alone.
Student study location remains on ProgrammeEnrolment.campusId.
3. Delivery & LMS core
Academic terms and class offerings are the shared bridge.
Weeks, learning items and assessments are the LMS core
(CAP-17–19, 27). Keep AcademicTerm (calendar e.g. 2026-03)
distinct from StudyPeriod.programmeSemesterNumber
(student’s semester in the programme).
Term · Offering · Schedule · Materials LMS Shared
Amber entities: offering / schedule / session / teaching / assessment outline. Green entities: weeks and learning items (materials surface).
erDiagram
CAMPUS ||--o{ ACADEMIC_TERM : runs
MODULE ||--o{ MODULE_OFFERING : offered_as
ACADEMIC_TERM ||--o{ MODULE_OFFERING : in_term
CAMPUS ||--o{ MODULE_OFFERING : at_campus
MODULE_OFFERING ||--o{ TEACHING_ASSIGNMENT : taught_by
STAFF_MEMBER ||--o{ TEACHING_ASSIGNMENT : assigned
MODULE_OFFERING ||--o{ CLASS_SCHEDULE : scheduled
CLASS_SCHEDULE ||--o{ CLASS_SESSION : instances
MODULE_OFFERING ||--o{ MODULE_WEEK : weeks
MODULE_WEEK ||--o{ MODULE_LEARNING_ITEM : items
MODULE_OFFERING ||--o{ MODULE_ASSESSMENT : assesses
MODULE_ASSESSMENT ||--o{ MODULE_LEARNING_ITEM : may_link
ACADEMIC_TERM {
string id PK
string campusId FK
string code
string title
int sequenceNumber
datetime startsAt
datetime endsAt
datetime resultPublishedAt
string status
}
MODULE_OFFERING {
string id PK
string moduleId FK
string academicTermId FK
string campusId FK
string sectionCode
string deliveryMode
}
TEACHING_ASSIGNMENT {
string id PK
string moduleOfferingId FK
string staffMemberId FK
string role
int displayOrder
}
CLASS_SCHEDULE {
string id PK
string moduleOfferingId FK
int dayOfWeek
string startsAtLocal
string endsAtLocal
string location
date effectiveFrom
date effectiveTo
}
CLASS_SESSION {
string id PK
string classScheduleId FK
datetime startsAt
datetime endsAt
string status
}
MODULE_WEEK {
string id PK
string moduleOfferingId FK
int weekNumber
date startsOn
string title
}
MODULE_LEARNING_ITEM {
string id PK
string moduleWeekId FK
string moduleAssessmentId FK
string kind
string title
datetime availableFrom
string fileName
string mimeType
int sizeBytes
}
MODULE_ASSESSMENT {
string id PK
string moduleOfferingId FK
string title
string kind
float weightPercent
datetime availableFrom
datetime dueAt
datetime scheduledStartsAt
datetime scheduledEndsAt
}
ModuleLearningItem.
Contract: lecture-note | assignment | quiz | test | midterm | final.
Route-facing Materials also project resource/quiz/exam views
(module-materials.ts).
Full LMS catalogue including proposed CAP-24/25/26 tables:
§10.
4. Student record & enrolment
Person ownership, external identifiers, visa pass, programme enrolment, and study periods.
Academic placement is always through a programme version
on ProgrammeEnrolment. Study-period status is the timeline
position; academicStatus is the Registry status used for active-student
and credit reporting.
Person · identifiers · enrolment · study period CMS
ProgrammeEnrolment links
student ↔ programme version ↔ campus.
Effective-dated status events keep enrolment, period and registration history.
erDiagram
PERSON ||--o| STUDENT_PROFILE : owns
STUDENT_PROFILE ||--o{ STUDENT_IDENTIFIER : identifiers
STUDENT_IDENTIFIER ||--o| STUDENT_IDENTIFIER : superseded_by
STUDENT_PROFILE ||--o{ EMERGENCY_CONTACT : contacts
STUDENT_PROFILE ||--o{ ACADEMIC_QUALIFICATION : quals
STUDENT_PROFILE ||--o{ ENGLISH_PROFICIENCY : english
STUDENT_PROFILE ||--o{ STUDENT_DOCUMENT : documents
STUDENT_PROFILE ||--o| STUDENT_RESUME : resume
STUDENT_RESUME ||--o{ RESUME_WORK_EXPERIENCE : work
STUDENT_RESUME ||--o{ RESUME_EDUCATION : education
STUDENT_RESUME ||--o{ RESUME_SKILL : skills
STUDENT_RESUME ||--o{ RESUME_RECOGNITION : awards
STUDENT_PROFILE ||--o{ PORTFOLIO_PROJECT : portfolio
STUDENT_PROFILE ||--o{ PROGRAMME_ENROLMENT : enrolments
STUDENT_PROFILE ||--o{ STUDENT_VISA_PASS : passes
STUDENT_IDENTIFIER ||--o{ STUDENT_VISA_PASS : passport_used
PROGRAMME_ENROLMENT ||--o{ STUDENT_VISA_PASS : for_enrolment
PROGRAMME_VERSION ||--o{ PROGRAMME_ENROLMENT : of_version
CAMPUS ||--o{ PROGRAMME_ENROLMENT : at
PROGRAMME_ENROLMENT ||--o{ STUDY_PERIOD : periods
PROGRAMME_ENROLMENT ||--o{ PROGRAMME_ENROLMENT_STATUS_EVENT : history
ACADEMIC_TERM ||--o{ STUDY_PERIOD : term
STUDY_PERIOD ||--o{ STUDY_PERIOD_STATUS_EVENT : history
STUDY_PERIOD ||--o| CREDIT_LOAD_OVERRIDE : override
STAFF_MEMBER ||--o{ CREDIT_LOAD_OVERRIDE : approves
STUDENT_PROFILE ||--o{ STUDENT_ACCOUNT_ISSUE : issues
PERSON {
string id PK
string displayName
string personType
}
STUDENT_PROFILE {
string id PK
string personId FK
string studentNumber
string displayName
string nationality
string emailAddress
date passportExpiresOn
}
STUDENT_IDENTIFIER {
string id PK
string studentProfileId FK
string type
string countryCode
string value
boolean isPrimary
string supersededByIdentifierId FK
}
PROGRAMME_ENROLMENT {
string id PK
string studentProfileId FK
string programmeVersionId FK
string campusId FK
datetime enrolledAt
date expectedCompletionOn
string status
}
PROGRAMME_ENROLMENT_STATUS_EVENT {
string id PK
string programmeEnrolmentId FK
string status
datetime effectiveFrom
datetime effectiveTo
string source
}
STUDY_PERIOD {
string id PK
string programmeEnrolmentId FK
string academicTermId FK
int programmeSemesterNumber
string status
string academicStatus
datetime statusChangedAt
string statusReason
datetime registrationOpensAt
datetime registrationClosesAt
}
STUDY_PERIOD_STATUS_EVENT {
string id PK
string studyPeriodId FK
string status
datetime effectiveFrom
datetime effectiveTo
string source
}
STUDENT_VISA_PASS {
string id PK
string studentProfileId FK
string programmeEnrolmentId FK
string passportIdentifierId FK
string passType
string passportNumber
string passNumber
string status
}
CREDIT_LOAD_OVERRIDE {
string id PK
string studyPeriodId FK
int approvedMaximumCredits
string approvedByStaffMemberId FK
string status
}
STUDENT_ACCOUNT_ISSUE {
string id PK
string studentProfileId FK
string kind
string status
int amountMinor
datetime dueAt
}
StudentProfile → ProgrammeEnrolment
→ programmeVersionId → ProgrammeVersion → Programme.
Study periods, module registrations, finance account, visa pass, and most
journey records hang off that enrolment.
academicStatus ∈ {active, repeat, outstanding, enrolled, credit-transfer}
counts as a valid semester for EMGS / active-student reporting
(current_active_students view).
Identifier history (passport, NRIC, student number, EMGS reference) lives on
StudentIdentifier; the visa pass links the passport it was issued against.
Statuses are layered (identity / enrolment / study period / registration /
immigration / finance) — see
active student definitions.
5. Registration, LMS activity & results
Module registration is the junction for LMS attendance, submissions
and progress, and for CMS published results.
Attempts are never overwritten. TermResult is a calculated snapshot;
published history is versioned via ResultPublication.
ModuleRegistration lifecycle LMS CMS Shared
Green: learning progress, assignment submission. Amber: attendance, assessment result. Blue: module/term results, transfers, publications.
erDiagram
STUDY_PERIOD ||--o{ MODULE_REGISTRATION : registers
MODULE_OFFERING ||--o{ MODULE_REGISTRATION : into
MODULE_REGISTRATION ||--o{ MODULE_REGISTRATION_STATUS_EVENT : history
MODULE_REGISTRATION ||--o{ ATTENDANCE : attendance
CLASS_SESSION ||--o{ ATTENDANCE : session
MODULE_REGISTRATION ||--o{ LEARNING_PROGRESS : progress
MODULE_LEARNING_ITEM ||--o{ LEARNING_PROGRESS : item
MODULE_REGISTRATION ||--o{ ASSIGNMENT_SUBMISSION : submits
MODULE_LEARNING_ITEM ||--o{ ASSIGNMENT_SUBMISSION : for_item
MODULE_REGISTRATION ||--o{ ASSESSMENT_RESULT : scored
MODULE_ASSESSMENT ||--o{ ASSESSMENT_RESULT : of
MODULE_REGISTRATION ||--|| MODULE_RESULT : final
GRADE_BAND ||--o{ MODULE_RESULT : band
STUDY_PERIOD ||--|| TERM_RESULT : term_gpa
PROGRAMME_ENROLMENT ||--o{ STUDENT_CREDIT_TRANSFER : transfers
STUDY_PERIOD ||--o{ STUDENT_CREDIT_TRANSFER : recorded_in
CURRICULUM_MODULE ||--o{ STUDENT_CREDIT_TRANSFER : target
PROGRAMME_ENROLMENT ||--o{ CURRICULUM_SUBSTITUTION : substitutions
CURRICULUM_MODULE ||--o{ CURRICULUM_SUBSTITUTION : replaced
MODULE ||--o{ CURRICULUM_SUBSTITUTION : substitute
PROGRAMME_VERSION ||--o{ MODULE_EQUIVALENCY : equivalencies
MODULE ||--o{ MODULE_EQUIVALENCY : maps
STUDY_PERIOD ||--o{ RESULT_PUBLICATION : publications
RESULT_PUBLICATION ||--o| RESULT_PUBLICATION : supersedes
MODULE_REGISTRATION ||--o{ LECTURER_REVIEW : reviewed
TEACHING_ASSIGNMENT ||--o{ LECTURER_REVIEW : by
STUDENT_PROFILE ||--o{ LECTURER_REVIEW : from
MODULE_REGISTRATION {
string id PK
string studyPeriodId FK
string moduleOfferingId FK
int attemptNumber
string registrationType
string status
datetime registeredAt
}
MODULE_REGISTRATION_STATUS_EVENT {
string id PK
string moduleRegistrationId FK
string status
datetime effectiveFrom
datetime effectiveTo
}
ATTENDANCE {
string id PK
string moduleRegistrationId FK
string classSessionId FK
string status
datetime recordedAt
}
LEARNING_PROGRESS {
string id PK
string moduleRegistrationId FK
string moduleLearningItemId FK
string status
datetime completedAt
}
ASSIGNMENT_SUBMISSION {
string id PK
string moduleRegistrationId FK
string moduleLearningItemId FK
string fileName
string mimeType
int sizeBytes
datetime submittedAt
datetime deletedAt
}
ASSESSMENT_RESULT {
string id PK
string moduleRegistrationId FK
string moduleAssessmentId FK
float scorePercent
float weightedScore
datetime publishedAt
}
MODULE_RESULT {
string id PK
string moduleRegistrationId FK
string gradeBandId FK
string grade
string outcome
float creditsAttempted
float creditsEarned
float finalPoints
boolean includedInCgpa
datetime publishedAt
}
TERM_RESULT {
string id PK
string studyPeriodId FK
float gpa
float cgpa
float termCreditsAttempted
float termCreditsEarned
float termGpaCredits
float termPoints
float transcriptCreditsAttempted
float cumulativeCreditsEarned
float cumulativeGpaCredits
float cumulativePoints
datetime publishedAt
}
STUDENT_CREDIT_TRANSFER {
string id PK
string programmeEnrolmentId FK
string studyPeriodId FK
string targetCurriculumModuleId FK
float creditsGranted
string sourceInstitution
datetime approvedAt
}
CURRICULUM_SUBSTITUTION {
string id PK
string programmeEnrolmentId FK
string replacedCurriculumModuleId FK
string substituteModuleId FK
}
MODULE_EQUIVALENCY {
string id PK
string programmeVersionId FK
string moduleId FK
string equivalentModuleId FK
}
LECTURER_REVIEW {
string id PK
string studentProfileId FK
string moduleRegistrationId FK
string teachingAssignmentId FK
}
ModuleRegistration rows
(attemptNumber, registrationType).
Latest attempt per module counts toward CGPA; failed attempts still count in term GPA.
includedInCgpa = false is valid.
Term totals must equal the values derived from results + credit transfers
(student_credit_summary view mirrors the TypeScript derivation).
AssignmentSubmission / LearningProgress are LMS metadata
(Demo: no durable file bytes yet — SDD-10 Q5).
ModuleResult / TermResult remain CMS-published academic outcomes.
Attendance is shared: CMS SoR this phase; LMS student/lecturer surfaces read via Portal API.
6. Finance
One finance account per enrolment; payments allocate to invoices (N:M). Reversals, refunds, credit notes and voids are first-class corrections — not in-place rewrites. Result visibility uses rule-based ResultAccessPolicy (balance, funding, holds).
Account · Invoice · Payment · Corrections · Funding CMS
Payment proof (uploaded file) is distinct from ledger Payment / Allocation. Adjustments carry an explicit category.
erDiagram
PROGRAMME_ENROLMENT ||--|| FINANCE_ACCOUNT : has
FINANCE_ACCOUNT ||--o{ INVOICE : invoices
STUDY_PERIOD ||--o{ INVOICE : may_bill
INVOICE ||--o{ INVOICE_LINE : lines
FEE_ITEM ||--o{ INVOICE_LINE : from
INVOICE ||--o| INVOICE_SERVICE_TAX : tax
CAMPUS_SERVICE_TAX_PROFILE ||--o{ INVOICE_SERVICE_TAX : profile
INVOICE ||--o| INVOICE_VOID : voided_by
INVOICE ||--o{ CREDIT_NOTE : credited
FINANCE_ACCOUNT ||--o{ PAYMENT : payments
PAYMENT ||--o{ PAYMENT_ALLOCATION : allocates
INVOICE ||--o{ PAYMENT_ALLOCATION : receives
PAYMENT ||--o| PAYMENT_REVERSAL : reversed
FINANCE_ACCOUNT ||--o{ REFUND : refunds
PAYMENT ||--o{ REFUND : source
PROGRAMME_ENROLMENT ||--o{ EDUCATION_SERVICE_TAX_EXEMPTION : exemptions
FUNDING_SCHEME ||--o{ STUDENT_FUNDING_AWARD : grants
PROGRAMME_ENROLMENT ||--o{ STUDENT_FUNDING_AWARD : receives
STUDENT_FUNDING_AWARD ||--o{ STUDENT_FUNDING_BENEFIT : benefits
FINANCE_ACCOUNT ||--o{ FINANCIAL_ADJUSTMENT : adjustments
INVOICE ||--o{ FINANCIAL_ADJUSTMENT : may_adjust
STUDENT_FUNDING_BENEFIT ||--o{ FINANCIAL_ADJUSTMENT : from_benefit
FINANCE_ACCOUNT {
string id PK
string programmeEnrolmentId FK
string currencyCode
string status
}
INVOICE {
string id PK
string financeAccountId FK
string studyPeriodId FK
string invoiceNumber
int totalMinor
string status
}
CREDIT_NOTE {
string id PK
string invoiceId FK
string creditNoteNumber
int amountMinor
int serviceTaxAmountMinor
datetime issuedAt
}
INVOICE_VOID {
string id PK
string invoiceId FK
string replacementInvoiceId FK
datetime voidedAt
string reason
}
PAYMENT {
string id PK
string financeAccountId FK
string reference
datetime paidAt
int amountMinor
string method
string status
}
PAYMENT_REVERSAL {
string id PK
string paymentId FK
datetime reversedAt
string reason
}
REFUND {
string id PK
string financeAccountId FK
string sourcePaymentId FK
int amountMinor
datetime paidAt
string method
}
PAYMENT_ALLOCATION {
string id PK
string paymentId FK
string invoiceId FK
int amountMinor
}
FINANCIAL_ADJUSTMENT {
string id PK
string financeAccountId FK
string invoiceId FK
string studentFundingBenefitId FK
string kind
string category
int amountMinor
}
INV-###### (INV-000000 reserved);
credit notes use CN-######.
A reversed payment has exactly one reversal and no allocations; a void invoice has
exactly one void record. Account balance =
billed − credit notes + adjustments − paid + refunds
(student_outstanding_balances view).
Historical SST stays on InvoiceServiceTax — never recalculated from today's rate.
7. Admissions (SDD-04 target)
Logical CMS entities for applicant journey — not yet exposed as PortalApi methods.
Applicant → Application → Offer → Enrolment CMS
Evidence bytes live on the evidence volume; metadata in CMS / planned Postgres.
erDiagram
APPLICANT_ACCOUNT ||--o{ APPLICATION : submits
CAMPUS ||--o{ APPLICATION : campus
PROGRAMME ||--o{ APPLICATION : programme
APPLICATION ||--o{ EVIDENCE_OBJECT : evidence
APPLICATION ||--o{ DECLARATION : declarations
APPLICATION ||--o| OFFER : may_receive
OFFER ||--o| FIRST_ENROLMENT : on_accept
FIRST_ENROLMENT ||--|| STUDENT_PROFILE : creates
FIRST_ENROLMENT ||--|| PROGRAMME_ENROLMENT : seeds
APPLICANT_ACCOUNT {
string id PK
string identitySubject
string email
string contactNumber
}
APPLICATION {
string id PK
string applicantAccountId FK
string programmeId FK
string campusId FK
string intake
string status
}
EVIDENCE_OBJECT {
string id PK
string applicationId FK
string evidenceType
string fileId
string checksum
datetime uploadedAt
}
DECLARATION {
string id PK
string applicationId FK
string policyVersion
datetime acceptedAt
}
OFFER {
string id PK
string applicationId FK
string conditions
date expiresOn
string decision
}
FIRST_ENROLMENT {
string id PK
string offerId FK
string studentId
string programmeId
date startOn
}
8. Immigration, online forms & graduation
Case management and form submissions tied to enrolment / invoices.
ImmigrationCase · OnlineForm · GraduationRecord CMS
Optional 1:1 links to form submission, visa pass, and invoice.
erDiagram
STUDENT_PROFILE ||--o{ IMMIGRATION_CASE : cases
PROGRAMME_ENROLMENT ||--o{ IMMIGRATION_CASE : for
CAMPUS ||--o{ IMMIGRATION_CASE : at
IMMIGRATION_CASE ||--o{ IMMIGRATION_DOCUMENT_CHECK : checks
IMMIGRATION_CASE ||--o{ IMMIGRATION_CASE_EVENT : events
ONLINE_FORM_SUBMISSION ||--o| IMMIGRATION_CASE : may_drive
STUDENT_VISA_PASS ||--o| IMMIGRATION_CASE : may_attach
INVOICE ||--o| IMMIGRATION_CASE : may_bill
CAMPUS ||--o{ ONLINE_FORM_DEFINITION : forms
ONLINE_FORM_DEFINITION ||--o{ ONLINE_FORM_SUBMISSION : submissions
STUDENT_PROFILE ||--o{ ONLINE_FORM_SUBMISSION : submits
PROGRAMME_ENROLMENT ||--o{ ONLINE_FORM_SUBMISSION : context
ONLINE_FORM_SUBMISSION ||--o{ ONLINE_FORM_MESSAGE : messages
STUDENT_PROFILE ||--o{ GRADUATION_RECORD : graduation
PROGRAMME_ENROLMENT ||--o{ GRADUATION_RECORD : for
CAMPUS ||--o{ GRADUATION_RECORD : at
ONLINE_FORM_SUBMISSION ||--o| GRADUATION_RECORD : clearance
ONLINE_FORM_SUBMISSION ||--o| GRADUATION_RECORD : convocation
INVOICE ||--o| GRADUATION_RECORD : fee
IMMIGRATION_CASE {
string id PK
string studentProfileId FK
string programmeEnrolmentId FK
string campusId FK
string caseType
string status
string formSubmissionId FK
string visaPassId FK
string invoiceId FK
}
IMMIGRATION_DOCUMENT_CHECK {
string id PK
string immigrationCaseId FK
string label
string status
}
IMMIGRATION_CASE_EVENT {
string id PK
string immigrationCaseId FK
string label
datetime occurredAt
}
ONLINE_FORM_DEFINITION {
string id PK
string campusId FK
string version
string eligibility
}
ONLINE_FORM_SUBMISSION {
string id PK
string formDefinitionId FK
string studentProfileId FK
string programmeEnrolmentId FK
int revision
string status
}
ONLINE_FORM_MESSAGE {
string id PK
string submissionId FK
int sequence
string sender
string text
}
GRADUATION_RECORD {
string id PK
string studentProfileId FK
string programmeEnrolmentId FK
string campusId FK
string clearanceSubmissionId FK
string convocationSubmissionId FK
string invoiceId FK
}
9. Integration, audit & data quality
Canonical IDs are not legacy CMS IDs. Mapping, DQ, idempotency, reporting views and append-only audit live with the portal/integration layer — see architecture.
Status history · campus policy · reporting views · AuditEvent · LegacyEntityMap CMS
Effective-dated status events and campus policy are first-class collections.
Postgres reporting views mirror the TypeScript projection helpers and resolve
academic-status membership through campus_academic_status_policies.
erDiagram
PROGRAMME_ENROLMENT ||--o{ PROGRAMME_ENROLMENT_STATUS_EVENT : history
STUDY_PERIOD ||--o{ STUDY_PERIOD_STATUS_EVENT : history
MODULE_REGISTRATION ||--o{ MODULE_REGISTRATION_STATUS_EVENT : history
CAMPUS ||--o{ CAMPUS_ACADEMIC_STATUS_POLICY : policy
CAMPUS_ACADEMIC_STATUS_POLICY ||--o{ CURRENT_ACTIVE_STUDENT : filters
CAMPUS_ACADEMIC_STATUS_POLICY ||--o{ LATEST_VALID_STUDY_PERIOD : filters
PROGRAMME_ENROLMENT ||--o| LATEST_VALID_STUDY_PERIOD : reports
PROGRAMME_ENROLMENT ||--o| STUDENT_CREDIT_SUMMARY : reports
FINANCE_ACCOUNT ||--o| STUDENT_OUTSTANDING_BALANCE : reports
STUDENT_PROFILE ||--o| CURRENT_ACTIVE_STUDENT : may_appear
LEGACY_ENTITY_MAP }o--|| STUDENT_PROFILE : may_map
DATA_QUALITY_ISSUE }o--o| STUDENT_PROFILE : may_flag
PROGRAMME_ENROLMENT_STATUS_EVENT {
string id PK
string programmeEnrolmentId FK
string status
datetime effectiveFrom
datetime effectiveTo
string changedByUserAccountId FK
string reason
string source
}
CAMPUS_ACADEMIC_STATUS_POLICY {
string id PK
string campusId FK
string academicStatus
boolean countsAsValidStudyPeriod
boolean countsAsActiveStudent
date effectiveFrom
date effectiveTo
}
LATEST_VALID_STUDY_PERIOD {
string programmeEnrolmentId FK
string studyPeriodId FK
string academicStatus
int programmeSemesterNumber
}
CURRENT_ACTIVE_STUDENT {
string studentProfileId FK
string studentNumber
string programmeEnrolmentId FK
string studyPeriodId FK
string academicStatus
}
STUDENT_CREDIT_SUMMARY {
string programmeEnrolmentId FK
float creditsRequired
float creditsEarned
float transferredCredits
float cgpa
float remainingCredits
}
STUDENT_OUTSTANDING_BALANCE {
string financeAccountId FK
string programmeEnrolmentId FK
int balanceMinor
int openInvoiceBalanceMinor
string balanceState
}
LEGACY_ENTITY_MAP {
string id PK
string canonicalEntityType
string canonicalEntityId
string sourceSystem
string legacyTable
string legacyId
}
AUDIT_EVENT {
bigint id PK
datetime occurredAt
bigint revision
int schemaVersion
string actorStudentProfileId
string actorUserAccountId
string collection
string recordId
string action
string before
string after
}
DATA_QUALITY_ISSUE {
string id PK
string entityType
string entityId
string issueType
string severity
datetime detectedAt
}
INTEGRATION_OPERATION {
string id PK
string idempotencyKey
string operationType
string status
datetime startedAt
}
portal_meta.schema_version tracks the record contract
(PORTAL_RECORD_SCHEMA_VERSION, currently 3 with campus policy collections).
A mismatch reseeds record tables and keeps
student_portal_state / audit_events.
Every graph save appends insert/update/delete rows to audit_events.
Demo tables still carry no physical FK constraints — db-admin infers edges from
*_id column names (including policy campus_id → campuses).
Reporting views resolve academic-status membership via campus_academic_status_policies.
10. LMS database tables
Logical LMS tables for Raisd learning surfaces (CAP + F01–F37). Demo contract tables already project as snake_case collections in Neon; proposed tables close Moodle-like gaps (quizzes, live class, forums, feedback, QA) and are not Live. See lms-schema.md and LMS hub.
In-contract LMS & shared tables LMS Shared
Postgres projection names match PortalRecordGraph collections
(PORTAL_RECORD_SCHEMA_VERSION = 3.
| Table | Domain | Owns / purpose | CAP | Status |
|---|---|---|---|---|
module_offerings |
Shared | Module × term × campus section; LMS delivery root | 11–14 | Demo |
teaching_assignments |
Shared | Lead / co-lecturer / tutor on an offering | 40, 44 | Demo |
class_schedules |
Shared | Recurring timetable slots | 11 | Demo |
class_sessions |
Shared | Dated session instances from a schedule | 11, 12 | Demo |
attendances |
Shared | present | absent per registration × session (CMS SoR this phase) | 12 | Demo |
module_assessments |
Shared | Weighted assessment outline (quiz / assignment / exam kinds) | 19, 24 | Demo |
assessment_results |
Shared | Published component scores against an assessment | 13, 28 | Demo |
module_weeks |
LMS | Teaching week / topic container on an offering | 23, 27 | Demo |
module_learning_items |
LMS | Resources, briefs, quizzes, exams with release time | 17–19, 24, 27 | Partial / Demo |
learning_progress |
LMS | Student item status: not-started | viewed | completed | locked | 27, 32 | Partial |
assignment_submissions |
LMS | File metadata for submit / replace / withdraw (bytes need Live store) | 20 | Demo (metadata) |
lecturer_reviews |
LMS | Student CAP-30 course evaluation of a teaching assignment | 30 | Demo (session) |
Proposed LMS tables (CAP gaps) Proposed
Research-backed extensions for a complete LMS (BASE-44). Do not treat as implemented.
Join keys follow existing hubs: module_offering_id,
module_registration_id, module_learning_item_id,
module_assessment_id.
erDiagram
MODULE_OFFERING ||--o{ LIVE_CLASS_SESSION : hosts
CLASS_SESSION ||--o| LIVE_CLASS_SESSION : may_link
MODULE_REGISTRATION ||--o{ LIVE_CLASS_ATTENDANCE : joins
LIVE_CLASS_SESSION ||--o{ LIVE_CLASS_ATTENDANCE : attendance
MODULE_ASSESSMENT ||--o{ QUIZ_ATTEMPT : attempts
MODULE_REGISTRATION ||--o{ QUIZ_ATTEMPT : sits
QUIZ_ATTEMPT ||--o{ QUIZ_ATTEMPT_ANSWER : answers
MODULE_ASSESSMENT ||--o{ QUIZ_QUESTION : questions
MODULE_ASSESSMENT ||--o{ ASSESSMENT_FEEDBACK : feedback
MODULE_REGISTRATION ||--o{ ASSESSMENT_FEEDBACK : receives
STAFF_MEMBER ||--o{ ASSESSMENT_FEEDBACK : writes
MODULE_OFFERING ||--o{ MODULE_FORUM : forums
MODULE_FORUM ||--o{ MODULE_FORUM_POST : posts
MODULE_FORUM_POST ||--o| MODULE_FORUM_POST : replies_to
STUDENT_PROFILE ||--o{ MODULE_FORUM_POST : authors
STAFF_MEMBER ||--o{ MODULE_FORUM_POST : may_moderate
MODULE_LEARNING_ITEM ||--o| LEARNING_RELEASE_RULE : release
MODULE_LEARNING_ITEM ||--o{ MATERIAL_QA_REVIEW : qa
STAFF_MEMBER ||--o{ MATERIAL_QA_REVIEW : reviews
MODULE_LEARNING_ITEM ||--o{ COPYRIGHT_PERMISSION : rights
LIVE_CLASS_SESSION {
string id PK
string moduleOfferingId FK
string classSessionId FK
string meetingUrl
string provider
datetime startsAt
datetime endsAt
string status
}
LIVE_CLASS_ATTENDANCE {
string id PK
string liveClassSessionId FK
string moduleRegistrationId FK
datetime joinedAt
datetime leftAt
}
QUIZ_ATTEMPT {
string id PK
string moduleAssessmentId FK
string moduleRegistrationId FK
int attemptNumber
datetime startedAt
datetime submittedAt
float scorePercent
string integrityStatus
}
QUIZ_QUESTION {
string id PK
string moduleAssessmentId FK
int displayOrder
string questionType
string prompt
float points
}
QUIZ_ATTEMPT_ANSWER {
string id PK
string quizAttemptId FK
string quizQuestionId FK
string response
boolean isCorrect
float pointsAwarded
}
ASSESSMENT_FEEDBACK {
string id PK
string moduleAssessmentId FK
string moduleRegistrationId FK
string staffMemberId FK
string body
datetime publishedAt
}
MODULE_FORUM {
string id PK
string moduleOfferingId FK
string title
string visibility
}
MODULE_FORUM_POST {
string id PK
string moduleForumId FK
string authorStudentProfileId FK
string authorStaffMemberId FK
string parentPostId FK
string body
datetime postedAt
}
LEARNING_RELEASE_RULE {
string id PK
string moduleLearningItemId FK
string ruleType
datetime availableFrom
string prerequisiteItemId FK
}
MATERIAL_QA_REVIEW {
string id PK
string moduleLearningItemId FK
string reviewerStaffMemberId FK
string decision
datetime reviewedAt
}
COPYRIGHT_PERMISSION {
string id PK
string moduleLearningItemId FK
string rightsHolder
string licence
date expiresOn
}
Logical DDL sketch (LMS core) LMS
Illustrative column shapes for the in-contract LMS set.
Live Neon Demo uses id + record jsonb per collection —
this sketch is the logical relational target, not a migration script.
-- LMS core (logical). Shared bridge tables omitted here; see §3 / §5. module_weeks ( id PK, module_offering_id FK, week_number, starts_on, title ) module_learning_items ( id PK, module_week_id FK, module_assessment_id FK NULL, kind, title, available_from, file_name, mime_type, size_bytes ) learning_progress ( id PK, module_registration_id FK, module_learning_item_id FK, status, completed_at NULL ) assignment_submissions ( id PK, module_registration_id FK, module_learning_item_id FK, file_name, mime_type, size_bytes, submitted_at, deleted_at NULL ) -- Proposed (not in PortalRecordGraph yet) quiz_attempts, quiz_questions, quiz_attempt_answers assessment_feedback live_class_sessions, live_class_attendances module_forums, module_forum_posts learning_release_rules, material_qa_reviews, copyright_permissions
Attribute quick reference — hub entities
Most referenced keys CMS
| Entity | Primary key | Important foreign keys | Status / enums |
|---|---|---|---|
Campus |
id |
— | countryCode, timeZone |
StudentProfile |
id |
— | maritalStatus, passport fields |
Faculty |
id |
campusId — organisational home |
Not the sole definition of programme campus offering |
Programme |
id |
facultyId — organisational owner |
certificate | diploma | bachelor | master | doctorate |
ProgrammeCampusOffering |
id |
programmeVersionId, campusId |
Where a version is delivered; optional localProgrammeCode |
ProgrammeEnrolment |
id |
studentProfileId,
programmeVersionId (maps student to a version),
campusId
|
active | completed | withdrawn | deferred |
ProgrammeVersion |
id |
programmeId |
Credits, semester count, CGPA/attendance mins; curriculum + fee plan attach here |
StudyPeriod |
id |
programmeEnrolmentId, academicTermId |
completed | current | planned | deferred |
ModuleRegistration |
id |
studyPeriodId, moduleOfferingId |
registered | completed | withdrawn | pending-confirmation |
FinanceAccount |
id |
programmeEnrolmentId (1:1) |
active | closed |
Invoice |
id |
financeAccountId, studyPeriodId? |
invoiceNumber INV-###### |
ModuleOffering Shared |
id |
moduleId, academicTermId, campusId |
online | physical | hybrid |
ModuleWeek LMS |
id |
moduleOfferingId |
weekNumber, startsOn, title |
ModuleLearningItem LMS |
id |
moduleWeekId, moduleAssessmentId? |
lecture-note | assignment | quiz | test | midterm | final |
AssignmentSubmission LMS |
id |
moduleRegistrationId, moduleLearningItemId |
metadata; file bytes Live-only (Q5) |
LearningProgress LMS |
id |
moduleRegistrationId, moduleLearningItemId |
not-started | viewed | completed | locked |
Attendance Shared |
id |
moduleRegistrationId, classSessionId |
present | absent |