School management system with Ghana payroll and explainable anomaly detection (Laravel + React)
# AI-Based Payroll Anomaly Detection System
School management system for Ghanaian basic schools with automated payroll (SSNIT, loans and absence) and an **explainable, rule-based anomaly engine**. Built as a final-year project: Laravel REST API + React SPA.
## What it does
- Staff, students, parent emails, attendance, class-teacher assignment (Headteacher / HR), and two-step leave approval (HR → Headteacher)
- Payroll runs using 5.5% employee SSNIT, staff loans and absence — no PAYE deduction
- Automatic scan after every payroll run
- Anomaly dashboard with evidence, risk score, narrative, and recommended action
- Payroll cannot be approved or marked paid while **critical** anomalies are still open
## Detection rules
| Code | What it catches |
| Ghost_No_User_Account | Salary without a linked user account |
| Ghost_Inactive_Staff | Inactive employee on a payroll run |
| Payment_Without_Attendance | Pay with no attendance (approved leave suppresses this) |
| Duplicate_Period_Payment | Two payments for the same period |
| Unusually_High_Salary | Gross pay above 1.5× grade/basic |
| Ssnit_Mismatch | Employee SSNIT not 5.5% of capped basic |
| Bank_Details_Missing | Missing bank name or account |
| Loan_Over_Deduction | Deduction above outstanding loan |
| Unauthorized_Allowance | Unauthorized or over-cap allowances |
## Requirements
- PHP 8.3+ (developed on PHP 8.5) and Composer
- Node.js 20+
- SQLite (default) or MySQL
## Open in your browser (GitHub Codespaces)
GitHub hosts the source at Tesla-arch/payroll-anomaly-detection. The live app needs PHP and Node, so GitHub Pages cannot run it. Use **Codespaces** instead:
1. Open that badge (or on GitHub: **Code → Codespaces → Create codespace on main**)
2. Wait until the codespace finishes **setup** (first start can take several minutes; a 502 means the app is not running yet)
3. Open forwarded port **5173** — that is the school app
4. Sign in with a demo account below (password: `password`)
If port 5173 shows …