AqooN Multi-School ERP System Architecture

By A Clusy user · Published July 24, 2026

Complete Prisma schema and core library setup for a multi-tenant school management system with 27 database models, RBAC permissions, and Next.js dev server deployment.

  • erp
  • prisma
  • nextjs
  • database-design
  • rbac
  • school-management
6 cells1 experiment25 views0 forks

Inside this notebook

## AqooN SMS — Prisma Schema Generator The **Prisma schema** was written directly to `prisma/schema.prisma` via `write_file` — see the saved file for the complete schema. ### Database Models (27 total) **Core (3):** Tenant, Branch, User **Academic (4):** AcademicYear, Term, Class, Subject **People (4):** Student, Teacher, Parent, Enrollment **Attendance (1):** Attendance **Timetable (1):** Timetable **Exams (2):** Exam, Mark **Finance (4):** FeeStructure, FeeAllocation, Invoice, Payment **HR (2):** Payroll, Leave **Library (2):** LibraryBook, LibraryIssue **Hostel (3):** HostelBlock, HostelRoom, HostelAllocation **Transport (3):** TransportRoute, TransportStop, TransportAllocation **Inventory (1):** InventoryItem **Notifications (1):** Notification **Audit (1):** AuditLog (in `audit` schema) ### Enums (12) UserRole, Gender, BloodGroup, AttendanceStatus, ExamType, PaymentStatus, FeeType, LeaveStatus, BookStatus, TransportVehicleType, InventoryCategory, MaritalStatus ### Key Features - Multi-tenant isolation via `Tenant` model - `multiSchema` preview feature with separate `audit` schema - All models map to snake_case table names via `@@map` - Audit logging with JSON metadata - Cascade deletes on tenant-scoped relations

# Fix ambiguous OTHER enum values in Prisma schema
with open('/home/user/aqoon-sms/prisma/schema.prisma', 'r') as f:
    content = f.read()

# FeeType OTHER -> FEE_OTHER
content = content.replace(
    'enum FeeType {\n  TUITION\n  ADMISSION\n  EXAMINATION\n  LIBRARY\n  SPORTS\n  TRANSPORT\n  HOSTEL\n  LABORATORY\n  ACTIVITY\n  OTHER\n}',
    'enum FeeType {\n  TUITION\n  ADMISSION\n  EXAMINATION\n  LIBRARY\n  SPORTS\n  TRANSPORT\n  HOSTEL\n  LABORATORY\n  ACTIVITY\n  FEE_OTHER\n}'
)
# Also fix any remaining @default(OTHER) that should now point to something
# Only InventoryCategory @default(OTHER) should remain OTHER
# Actually since we changed FeeType, the OTHER in FeeType was renamed to FEE_OTHER
# So @default(OTHER) on InventoryCategory fields is still fine

with open('/home/user/aqoon-sms/prisma/schema.prisma', 'w') as f:
    f.write(content)

print('Fixed. Running validate...')
Fixed. Running validate...

## AqooN SMS — Core Library Setup The following core library files were written via `write_file`: | File | Purpose | |------|---------| | `.env.example` | Environment template for all required variables | | `src/lib/prisma.ts` | Prisma client singleton (hot-reload safe) | | `src/lib/supabase.ts` | Supabase client (browser + admin with service role) | | `src/lib/permissions.ts` | RBAC system, navigation, audit helper, pagination | ### Permission System **12 roles** with hierarchy: SUPER_ADMIN (100) → STUDENT (20) **22 module permissions** control access by role — e.g.: - `fees` → SUPER_ADMIN, SCHOOL_OWNER, PRINCIPAL, ADMIN, ACCOUNTANT - `results` → SUPER_ADMIN, PRINCIPAL, ADMIN, TEACHER, STUDENT, PARENT - `audit` → SUPER_ADMIN, SCHOOL_OWNER (most restricted) **23 navigation items** are filterable by module permission via `getNavigation(role)`. ### Utilities - `createAuditLog()` — writes to `audit` schema - `buildPagination()` — `skip/take/page/pageSize` from query params

import subprocess, time, sys, os

# Kill any existing server on port 3000
os.system("pkill -f 'next dev' 2>/dev/null || true")
time.sleep(1)

# Start Next.js dev server in background
proc = subprocess.Popen(
    ["npx", "next", "dev", "-p", "3000"],
    cwd="/home/user/aqoon-sms",
    stdout=subprocess.PIPE,
    stderr=subprocess.STDOUT,
    text=True
)

# Wait for the server to be ready
for attempt in range(30):
    time.sleep(0.5)
…
▲ Next.js 16.2.11 (Turbopack)
- Local:         http://localhost:3000
- Network:       http://169.254.0.21:3000
✓ Ready in 361ms

✅ Dev server is running!

Access the app at the URL shown above (port 3000)
Dashboard: http://localhost:3000/dashboard
Login:     http://localhost:3000/login
import subprocess, time, sys, os, threading
CWD = "/home/user/aqoon-sms"

# Kill any lingering processes
os.system("pkill -f 'next dev' 2>/dev/null || true")
os.system("pkill -f localtunnel 2>/dev/null || true")
time.sleep(1)

# 1. Start Next.js dev server
dev = subprocess.Popen(
    ["npx", "next", "dev", "-p", "3000"],
    cwd=CWD, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True
)
print(f"🚀 Dev server PID: {dev.pid}")

# Wait for it to be ready
for i in range(60):
    ret = subprocess.run(
…
🚀 Dev server PID: 4368
✅ Server ready! (HTTP 200)

🌍 Public URL: https://aqoon-sms.loca.lt

✅ Download link: https://aqoon-sms.loca.lt/aqoon-sms-full.zip
import subprocess, time, sys, os, threading

CWD = "/home/user/aqoon-sms"

# Kill any lingering processes
os.system("pkill -f 'next dev' 2>/dev/null || true")
os.system("pkill -f localtunnel 2>/dev/null || true")
time.sleep(1)

# 1. Start Next.js dev server
dev = subprocess.Popen(
    ["npx", "next", "dev", "-p", "3000"],
    cwd=CWD,
    stdout=subprocess.PIPE,
    stderr=subprocess.STDOUT,
    text=True
)
print(f"🚀 Dev server PID: {dev.pid}")
…
AqooN Multi-School ERP System Architecture | Clusy