Students, enrollments & fees
One student row for life; one enrollment per (student, academic year); fees materialised per enrollment from the year's expense catalogue or added by hand; transfers recorded as history, never by mutating the student.
| Table | Columns (keys) | Notes |
|---|---|---|
students |
id PK · code UNIQUE (auto) · full_name · gender · birth_date · phone · email · guardian_name · guardian_phone · status (default active) · grade_id FK · class_id FK · created_by · created_at |
grade/class are the student's current placement; bulk CSV import/export hangs off the same table |
student_transfers |
id PK · student_id FK · from_grade_id/to_grade_id FK · from_class_id/to_class_id FK · transfer_type · note · date · created_by |
within/across grades and classes, plus leave/dropout/expelled and no-destination moves; the transfer form also updates the student's placement |
enrollments |
id PK · student_id FK · academic_year_id FK · grade_id FK · class_id FK · status (default active) |
one row per year; bulk class assignment writes here; promotion copies rows forward |
year_fees |
id PK · academic_year_id FK · name · amount · currency (default SYP) |
the year's expense catalogue ("year expenses" in the UI): defined once per academic year, inherited by every enrollment of that year |
student_fees |
id PK · enrollment_id FK · student_id FK · academic_year_id FK · year_fee_id (nullable) · name · amount · amount_paid · currency · status (unpaid/partially_paid/paid) · payment_id |
the concrete charge; year_fee_id marks inheritance (null = ad-hoc); amount_paid moves via ApplyPayment/RevertPayment inside the payment posting/void transactions |
Relationships
students 1 —< enrollments >— academic_years; enrollments 1 —< student_fees;
year_fees 1 —< student_fees (inheritance); students 1 —< student_fees;
students 1 —< student_transfers.
Invariants worth keeping in mind
- A fee's status is derived from money: fully covered →
paid(and it receives thepayment_id), partly →partially_paid, none →unpaid. - The debtors report (
/reports/debtors) reads unpaid/partially-paid fees; a cancel puts fees back on it because the credit is reverted, not deleted. - Disabling a currency that fees still reference is refused at the settings save (settings).
Rendered from simple-lms/data-model/students.md · OKF bundle · regenerate with npm run render