System-Wide Entity-Relationship Diagram¶
Auto-generated by c4docgen on 2026-04-27. Do not edit manually — regenerate with
./gradlew generateSystemErd.
erDiagram
%% ── Attendance ──
School ||--o{ AttendanceRecord : "has many"
Student ||--o{ AttendanceRecord : "has many"
Classroom ||--o{ AttendanceRecord : "has many"
PickupPerson ||--o{ AttendanceRecord : "has many"
School ||--o{ AttendanceSetting : "has many"
School ||--o{ PickupPerson : "has many"
School ||--o{ SectionSchedule : "has many"
Section ||--o{ SectionSchedule : "has many"
Student ||--o{ StudentPickupPerson : "has many"
PickupPerson ||--o{ StudentPickupPerson : "has many"
%% ── Canteen ──
Student ||--o{ CanteenOrder : "has many"
MealTicket ||--o{ CanteenOrder : "has many"
Menu ||--o{ CanteenOrder : "has many"
School ||--o{ CanteenOrder : "has many"
School ||--o{ CanteenSetting : "has many"
CateringService }o--o{ CanteenUser : "linked via"
School }o--o{ CanteenUser : "linked via"
School ||--o{ CateringService : "has many"
CateringService ||--o{ Item : "has many"
ItemCategory ||--o{ Item : "has many"
School ||--o{ Item : "has many"
School ||--o{ ItemCategory : "has many"
Item ||--o{ ItemVariant : "has many"
Menu ||--o{ MealTicket : "has many"
School ||--o{ MealTicket : "has many"
School ||--o{ Menu : "has many"
Menu }o--o{ MenuAudience : "linked via"
Campus }o--o{ MenuAudience : "linked via"
Classroom }o--o{ MenuAudience : "linked via"
MenuSection ||--o{ MenuSchedule : "has many"
Item ||--o{ MenuSchedule : "has many"
Menu ||--o{ MenuSection : "has many"
Item ||--o{ OrderItem : "has many"
MenuSection ||--o{ OrderItem : "has many"
MealTicket }o--o{ TicketPrice : "linked via"
%% ── Sample ──
%% ── School ──
AcademicYear ||--o{ AcademicHoliday : "has many"
AcademicPeriod ||--o{ AcademicHoliday : "has many"
AcademicYear ||--o{ AcademicPeriod : "has many"
School ||--o{ AcademicYear : "has many"
Campus ||--o{ AcademicYear : "has many"
Section ||--o{ AcademicYear : "has many"
School ||--o{ Campus : "has many"
Section ||--o{ Classroom : "has many"
School ||--o{ Family : "has many"
Family ||--o{ Guardian : "has many"
School ||--o{ SchoolStaff : "has many"
Campus ||--o{ SchoolStaff : "has many"
Campus ||--o{ Section : "has many"
School ||--o{ Student : "has many"
Classroom ||--o{ Student : "has many"
Family ||--o{ Student : "has many"
Student ||--o{ StudentGuardian : "has many"
Guardian ||--o{ StudentGuardian : "has many"
%% ── Wallet ──
Student }o--o{ WalletBalance : "linked via"
School }o--o{ WalletBalance : "linked via"
%% ── Attendance entities ──
AttendanceRecord {
UUID id PK
UUID schoolId FK
UUID studentId FK
UUID classroomId FK
LocalDate date
Instant checkInTime
UUID checkInPerformedById FK
String checkInPerformedByName
CheckMethod checkInMethod "SCAN, MANUAL"
Instant checkOutTime
UUID checkOutPerformedById FK
String checkOutPerformedByName
CheckMethod checkOutMethod "SCAN, MANUAL"
UUID dropoffPersonId FK
String dropoffPersonName
UUID pickupPersonId FK
String pickupPersonName
ArrivalClassification arrivalClassification "EARLY, ON_TIME, LATE"
DepartureClassification departureClassification "EARLY, ON_TIME, LATE"
AttendanceStatus status "PRESENT, ABSENT, LATE, EXCUSED"
String excuseReason
boolean isUnaccompanied
Instant createdAt
Instant updatedAt
}
AttendanceSetting {
UUID id PK
UUID schoolId FK
boolean parentSelfCheckInEnabled
AttendanceMode attendanceMode "SCAN, MANUAL, BOTH"
LocalTime selfCheckInStartTime
LocalTime selfCheckInEndTime
Instant createdAt
Instant updatedAt
}
PickupPerson {
UUID id PK
UUID schoolId FK
String fullName
String phoneNumber
String relationship
String photoUrl
Instant createdAt
Instant updatedAt
}
SectionSchedule {
UUID id PK
UUID schoolId FK
UUID sectionId FK
LocalTime startTime
LocalTime endTime
int lateThresholdMinutes
int pickupLateThresholdMinutes
int earlyDepartureThresholdMinutes
Instant createdAt
Instant updatedAt
}
StudentPickupPerson {
UUID id PK
UUID studentId FK
UUID pickupPersonId FK
AuthorisationMode authorisationMode "GENERAL, DAY_SPECIFIC"
PickupPermissionType permissionType "DROPOFF_ONLY, PICKUP_ONLY, BOTH"
boolean monday
boolean tuesday
boolean wednesday
boolean thursday
boolean friday
Instant createdAt
Instant updatedAt
}
%% ── Canteen entities ──
CanteenOrder {
UUID id PK
String orderNumber
UUID studentId FK
UUID mealTicketId FK
OrderType orderType "TICKET, PRE_ORDER, POS"
BigDecimal totalPrice
PaymentMode paymentMode "SIKA_ID, MOBILE_MONEY, CARD, CREDIT, CASH, ALLOCATION"
OrderStatus status "DRAFT, PENDING_PAYMENT, PAID, SERVED, PARTIALLY_SERVED, CANCELLED"
LocalDateTime scheduledDate
UUID batchId FK
UUID menuId FK
UUID createdBy
Instant redeemedAt
Instant cancelledAt
UUID cancelledBy
UUID schoolId FK
String invoiceId
BigDecimal serviceFee
Instant paidAt
Instant createdAt
Instant updatedAt
}
CanteenSetting {
UUID id PK
RedemptionMode redemptionMode "AUTO_REDEEM, AUTO_REDEEM_PRESENT, SCAN_TO_REDEEM"
UUID schoolId FK
Instant createdAt
Instant updatedAt
}
CanteenUser {
UUID id PK
UUID userId FK
UUID cateringServiceId FK
CanteenRole role "CANTEEN_ADMIN, CANTEEN_MANAGER, POS_OPERATOR"
boolean active
UUID schoolId FK
Instant createdAt
Instant updatedAt
}
CateringService {
UUID id PK
String name
String logo
String email
String phone
UUID schoolId FK
SettlementAccountType settlementAccountType "BANK, MOBILE_MONEY"
String settlementAccountName
String settlementAccountNumber
GhanaBank settlementBank "ABSA_BANK_GHANA, ACCESS_BANK_GHANA, AGRICULTURAL_DEVELOPMENT_BANK, BANK_OF_AFRICA_GHANA, CALBANK, CONSOLIDATED_BANK_GHANA, ECOBANK_GHANA, FBN_BANK_GHANA, FIDELITY_BANK_GHANA, FIRST_ATLANTIC_BANK, FIRST_NATIONAL_BANK_GHANA, GCB_BANK, GUARANTY_TRUST_BANK_GHANA, NATIONAL_INVESTMENT_BANK, OMNIBSIC_BANK_GHANA, PRUDENTIAL_BANK, REPUBLIC_BANK_GHANA, SOCIETE_GENERALE_GHANA, STANBIC_BANK_GHANA, STANDARD_CHARTERED_BANK_GHANA, UNITED_BANK_FOR_AFRICA_GHANA, UNIVERSAL_MERCHANT_BANK, ZENITH_BANK_GHANA"
MobileMoneyProvider settlementMobileMoneyProvider "MTN, TELECEL, AIRTELTIGO"
BigDecimal platformServiceFeeOverride
Instant createdAt
Instant updatedAt
}
Item {
UUID id PK
UUID cateringServiceId FK
UUID itemCategoryId FK
String name
String image
String description
String allergens
int maxQuantityPerDay
PricingStyle pricingStyle "FIXED, VARIANT, NO_PRICE"
BigDecimal defaultPrice
String currency
UUID schoolId FK
UUID createdBy
Instant createdAt
Instant updatedAt
}
ItemCategory {
UUID id PK
String name
String description
String themeColor
UUID schoolId FK
Instant createdAt
Instant updatedAt
}
ItemVariant {
UUID id PK
UUID itemId FK
String variantName
BigDecimal price
AudienceScope audience "ALL, SPECIFIC"
List targetCampusIds
List targetClassroomIds
Instant createdAt
Instant updatedAt
}
MealTicket {
UUID id PK
UUID menuId FK
String name
UUID academicTermId FK
PricingMethod pricingMethod "DAILY, MONTHLY, TERMLY"
PricingScheme pricingScheme "UNIFORM, DIFFERENTIAL"
BigDecimal uniformPrice
boolean active
UUID schoolId FK
Instant createdAt
Instant updatedAt
}
Menu {
UUID id PK
String name
String description
MenuPurpose purpose "INFO_ONLY, PARENTS_CAN_ORDER"
PaymentType paymentType "ORDER_AND_PAY_ITEM_PRICING, ORDER_ONLY_NO_PAYMENT, ORDER_AND_PAY_TICKET_PRICING"
LocalDate startDate
LocalDate endDate
DeadlineType orderingDeadlineType "DAILY, WEEKLY"
LocalTime dailyCutoffTime
int dailyCutoffDays
DayOfWeek weeklyCutoffDay "MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY"
LocalTime weeklyCutoffTime
int orderCancellationHours
boolean published
AudienceScope audience "ALL, SPECIFIC"
List targetCampusIds
List targetSectionIds
List targetClassroomIds
List excludedStudentIds
int visibilityDays
UUID schoolId FK
UUID updatedBy
String updatedByName
Instant createdAt
Instant updatedAt
}
MenuAudience {
UUID id PK
UUID menuId FK
UUID campusId FK
UUID classroomId FK
Instant createdAt
Instant updatedAt
}
MenuSchedule {
UUID id PK
UUID menuSectionId FK
UUID itemId FK
LocalDate date
UUID scheduledViaCategoryId FK
int stockAvailable
SchedulePattern recurrenceRule "SINGLE_DAY, EVERY_DAY, EVERY_WEEKDAY, SPECIFIC_DAYS"
Instant createdAt
Instant updatedAt
}
MenuSection {
UUID id PK
UUID menuId FK
String name
LocalTime servingStartTime
LocalTime servingEndTime
SelectionMode selectionMode "SINGLE, MULTIPLE"
String themeColor
Instant createdAt
Instant updatedAt
}
OrderItem {
UUID id PK
UUID orderId FK
UUID itemId FK
UUID variantId FK
UUID menuSectionId FK
int quantity
BigDecimal unitPrice
boolean served
Instant servedAt
long servedBy
String servedByName
Instant createdAt
Instant updatedAt
}
OrderStatusHistory {
UUID id PK
UUID orderId FK
OrderStatus fromStatus "DRAFT, PENDING_PAYMENT, PAID, SERVED, PARTIALLY_SERVED, CANCELLED"
OrderStatus toStatus "DRAFT, PENDING_PAYMENT, PAID, SERVED, PARTIALLY_SERVED, CANCELLED"
UUID changedBy
Instant changedAt
}
TicketPrice {
UUID id PK
UUID mealTicketId FK
UUID targetGroupId FK
BigDecimal price
Instant createdAt
Instant updatedAt
}
%% ── Sample entities ──
Todo {
UUID id PK
String title
String description
TodoStatus status "PENDING, IN_PROGRESS, COMPLETED"
Instant createdAt
Instant updatedAt
}
%% ── School entities ──
AcademicHoliday {
UUID id PK
UUID academicYearId FK
UUID academicPeriodId FK
String name
LocalDate startDate
LocalDate endDate
String description
Instant createdAt
Instant updatedAt
long legacyHolidayId
}
AcademicPeriod {
UUID id PK
UUID academicYearId FK
String name
LocalDate startDate
LocalDate endDate
Instant createdAt
Instant updatedAt
long legacyPeriodId
}
AcademicYear {
UUID id PK
UUID schoolId FK
UUID campusId FK
UUID sectionId FK
String name
LocalDate startDate
LocalDate endDate
AcademicPeriodType periodType "TERM, SEMESTER, QUARTER, TRIMESTER"
boolean isActive
Instant createdAt
Instant updatedAt
long legacyYearId
}
Campus {
UUID id PK
UUID schoolId FK
String name
String code
String address
String phone
String email
String principalName
boolean isActive
Instant createdAt
Instant updatedAt
long legacyCampusId
Map legacyConfiguration
}
Classroom {
UUID id PK
UUID sectionId FK
String name
String code
String academicYear
int capacity
UUID classTeacherId FK
String roomNumber
boolean isActive
Instant createdAt
Instant updatedAt
long legacyGradeId
}
Family {
UUID id PK
UUID schoolId FK
String familyName
String familyCode
String primaryAddress
String primaryPhone
String primaryEmail
String notes
boolean isActive
Instant createdAt
Instant updatedAt
}
Guardian {
UUID id PK
UUID familyId FK
GuardianType guardianType "FATHER, MOTHER, STEPFATHER, STEPMOTHER, GRANDFATHER, GRANDMOTHER, UNCLE, AUNT, OLDER_SIBLING, LEGAL_GUARDIAN, FOSTER_PARENT, RELATIVE, FAMILY_FRIEND, OTHER"
String firstName
String middleName
String lastName
String email
String phonePrimary
String phoneSecondary
String occupation
String employer
String address
String nationalId
String profilePhotoUrl
boolean isEmergencyContact
boolean canPickupStudent
boolean isPrimaryContact
long legacyUserId
boolean isActive
Instant createdAt
Instant updatedAt
}
School {
UUID id PK
String name
String code
String logoUrl
String address
String phone
String email
String website
String accreditationNumber
String defaultCurrency
long legacySchoolId
boolean isActive
Instant createdAt
Instant updatedAt
}
SchoolStaff {
UUID id PK
UUID schoolId FK
UUID campusId FK
String staffIdNumber
String firstName
String middleName
String lastName
String email
String phone
LocalDate dateOfBirth
Gender gender "MALE, FEMALE, OTHER"
String profilePhotoUrl
StaffRole role "TEACHER, ADMINISTRATOR, HEADMASTER, SUPPORT, OTHER"
String department
EmploymentType employmentType "FULL_TIME, PART_TIME, CONTRACT"
LocalDate hireDate
StaffStatus status "ACTIVE, INACTIVE, ON_LEAVE, TERMINATED"
long legacyUserId
Instant createdAt
Instant updatedAt
}
Section {
UUID id PK
UUID campusId FK
String name
String code
int levelOrder
String description
boolean isActive
Instant createdAt
Instant updatedAt
}
Student {
UUID id PK
UUID schoolId FK
UUID classroomId FK
UUID familyId FK
String studentIdNumber
String firstName
String middleName
String lastName
LocalDate dateOfBirth
Gender gender "MALE, FEMALE, OTHER"
String profilePhotoUrl
String nationality
String religion
String bloodGroup
String medicalNotes
LocalDate enrollmentDate
StudentStatus status "ACTIVE, INACTIVE, GRADUATED, WITHDRAWN, SUSPENDED"
Instant createdAt
Instant updatedAt
long legacyStudentId
}
StudentGuardian {
UUID studentId PK
UUID guardianId PK
boolean isPrimaryContact
boolean isEmergencyContact
boolean canPickupStudent
String relationshipNotes
}
%% ── Wallet entities ──
WalletBalance {
UUID id PK
UUID studentId FK
UUID schoolId FK
BigDecimal balance
String currency
Instant createdAt
Instant updatedAt
}