DAC Academy — Operational Risk Guide
RISK 1: Duplicate Lead Creation
Severity: Medium Scenario: Staff creates a lead with same (branch, parentPhone, studentName) as existing lead System Response: Unique constraint violation → error "A unique constraint was violated — duplicate data detected" Recovery: Edit the existing lead rather than creating duplicate Prevention: System checks duplicate at DB level; staff should search before creating
RISK 2: Duplicate Transaction ID
Severity: High (Financial) Scenario: Staff enters same transactionId for two different payments System Response: Unique partial index violation → error Recovery: Use a different transaction ID or omit it Prevention: Always use unique transaction IDs from payment gateway/bank
RISK 3: Batch Full — Race Condition
Severity: High Scenario: Two staff members submit admissions for same batch simultaneously System Response: Second submission hits row-level lock and sees updated count → "Batch is at full capacity" Recovery: Select a different batch or increase batch capacity in Settings Prevention: Monitor batch enrollment counts; set realistic capacity limits
RISK 4: Overpayment
Severity: High (Financial) Scenario: Staff records payment larger than remaining balance System Response: DB CHECK constraint violation → error Recovery: Reduce payment amount to remainingPaise or less Prevention: System shows remaining balance before payment entry
RISK 5: Voiding Without Receipt Cancellation
Severity: High (Financial) Scenario: Staff voids an installment but receipt remains valid System Response: Installment is voided (voidedAt, voidedBy, voidReason set). fee_record aggregates recalculated. Receipt is NOT voided. Recovery: If receipt needs cancellation, it must be voided separately (NO UI for this — requires DB) Prevention: Understand that voiding ≠ receipt cancellation. Receipts are immutable.
RISK 6: Accidental Override of Batch Default Fees
Severity: Medium Scenario: Staff enters ₹1 as admission fee, overrides batch defaults System Response: Uses staff-entered value (since > 0) instead of batch defaults Recovery: Cannot change admission fee after creation (admission is historical snapshot). Adjust via ad-hoc fee on fee record. Prevention: Enter 0 to use batch defaults; only override with explicit intent
RISK 7: Parent Account Created with Wrong Email
Severity: Medium Scenario: Staff types wrong email in admission form → parent account created with misspelled email System Response: Parent account created, temp password generated, linked to student Recovery: Create new parent account with correct email. Old account remains orphaned. Prevention: Verify email twice before submission
RISK 8: Admission PDF Generation Failure
Severity: Low
Scenario: Fire-and-forget PDF generation fails due to R2/network issue
System Response: Admission is already created. PDF is not stored. receipts.pdfStoragePath remains null.
Recovery: PDF can be regenerated by viewing/downloading receipt (calls PDF endpoint which regenerates)
Prevention: N/A — fire-and-forget is by design
RISK 9: Student Promoted to Non-Existent Session
Severity: Medium Scenario: Staff tries to promote student when next session doesn't exist System Response: Error — cannot promote without a destination session Recovery: Admin must create and activate the next academic session first Prevention: Plan session calendar ahead of promotion season
RISK 10: Receipt Sequence Gap
Severity: Low Scenario: Transaction rollback after receipt number was consumed System Response: Receipt number sequence increments but transaction is rolled back Effect: Receipt numbers will have gaps (e.g., 0042, 0044, 0045) Recovery: No recovery needed — gaps are expected and normal
RISK 11: Staff Accessing Wrong Branch Data
Severity: Medium Scenario: Counselor assigned to Branch A accidentally views Branch B data System Response: Counselors have cross-branch READ access — they CAN see all branches Risk: Read access is open; only write access is restricted Prevention: Manual oversight; branch filter in UI helps but doesn't prevent
RISK 12: Warden Not Assigned to a Hostel
Severity: High
Scenario: Warden account created without hostelId
System Response: Warden cannot access any students, outings, or medical requests — "You are not assigned to a hostel"
Recovery: Admin must edit the warden user and assign a hostel in /users
Prevention: Always assign hostelId when creating warden accounts
RISK 13: Batch Left Open for Too Long
Severity: Low Scenario: Batch status stays 'upcoming' after orientation System Response: No automatic status change — batch stays 'upcoming' Recovery: Admin manually changes batch status in Settings Prevention: Develop a batch status update SOP
RISK 14: Removing Student with Fee Arrears
Severity: High (Financial) Scenario: Staff marks student as "left" without clearing fee dues System Response: Application checks for arrears before allowing lifecycle transition Recovery: Clear fees first, then mark student status Prevention: Always verify fee clearance during status transitions
RISK 15: Duplicate Aadhaar Registration
Severity: High
Scenario: Same student registered twice with same Aadhaar
System Response: Partial unique index on aadharHash prevents duplicate entries
Recovery: System prevents this — no action needed
Prevention: N/A — enforced at DB level
RISK 16: Deactivated User Still Has Active Sessions
Severity: Medium Scenario: User account deactivated while logged in System Response: On next request, middleware checks isActive=false → redirects to /login. Current JWT may remain valid until expiry. Recovery: User is logged out on next request Prevention: Inform users before deactivating their account
RISK 17: WhatsApp Notification Silent Failure
Severity: Low Scenario: WhatsApp API unavailable, notification queue item fails after 3 retries System Response: Job marked as 'failed'. No automatic retry after exhaustion. Recovery: Manual retry via DB (update jobs set status='pending' where status='failed') Prevention: Monitor WhatsApp log for failed messages
RISK 18: Non-Terminal Lead Can Be Converted Multiple Times
Severity: High
Scenario: Staff navigates to /admissions/new?leadId=X twice
System Response: First admission creates student, updates lead to 'admitted'. Second attempt finds lead already admitted.
Recovery: System prevents this — admission page checks alreadyAdmitted flag
Prevention: N/A — system has guard
RISK 19: Student Photo Uploaded Without Admission Completion
Severity: Low Scenario: Student photo uploaded to R2 but admission transaction fails System Response: Cleanup code deletes the uploaded photo Recovery: Automatic — no action needed Prevention: N/A — cleanup is automatic
RISK 20: Financial Data Display — Historical vs Live Confusion
Severity: High (Financial Misunderstanding) Scenario: Staff looks at admission record's fee fields thinking they're current System Response: Admission records show SNAPSHOT at admission time. Live data is in fee_records. UI Note: Student detail page shows both: admission fee fields labeled as "Admission Fee Snapshot" and live fee record data Prevention: Always use fee_records for current financial status
RISK 21: Orphaned Registration Without Admission
Severity: Medium Scenario: Lead registered (paid fee) but never admitted System Response: Registration record exists, receipt exists, but no student/admission created Recovery: Staff converts lead to admission normally — registration is linked Prevention: Follow up on registered leads within 30 days
RISK 22: Inconsistent Student Session Status
Severity: Medium Scenario: Student promoted but previous session's status not updated System Response: Promotion creates NEW student_session for new session; OLD session retains its status Recovery: Old session status should be manually updated to 'completed' Prevention: Promotion workflow should be enhanced to close previous session
RISK 23: No Audit Log Cleanup
Severity: Low Scenario: Audit log grows unbounded over years System Response: No retention policy implemented Recovery: Manual cleanup or implement retention policy Prevention: Monitor audit log size; implement archival strategy
RISK 24: Phone Number Validation Rejects Valid International Numbers
Severity: Low
Scenario: Parent has non-Indian phone number
System Response: toE164() validation may fail if number doesn't match expected patterns
Recovery: Error shown: "Invalid phone number"
Prevention: Ensure international format with country code (+1, +44, etc.)
RISK 25: Receipt PDF Downloaded for Voided Receipt
Severity: Medium
Scenario: Receipt is voided but PDF is still downloadable
System Response: PDF endpoint does NOT check isVoided status
Recovery: Manual — implement void check in PDF endpoint
Prevention: N/A — current implementation gap
RISK 26: Unable to Change Student Branch
Severity: Medium Scenario: Student needs to transfer from Branch A to Branch B System Response: Student edit form rejects branch changes with "Branch changes are not allowed through student edit" Recovery: No dedicated branch transfer workflow exists. Requires DB-level change. Prevention: N/A — feature gap
RISK 27: Soft-Deleted Records in Queries
Severity: Low
Scenario: Staff wonders why a record is missing
System Response: Most queries filter isDeleted = false automatically
Recovery: Check DB directly for soft-deleted records
Prevention: N/A — soft delete is by design
RISK 28: Lead Status Backward Transition Possible for "Lost"
Severity: Low
Scenario: Lost lead can be set back to new/contacted/interested
System Response: Allowed — lost has special reversion logic
Recovery: N/A — intentional design for re-engagement
Prevention: N/A
RISK 29: Student Edit Form Shows Hostel Fields for Day Scholars
Severity: Low Scenario: Editing a day-scholar student shows hostel fields (hidden behind hostelOpted toggle) System Response: Form works correctly — hostel fields only appear when hostelOpted is true Risk: Low — UI handles this correctly
RISK 30: Enrollment Number Not Unique
Severity: Low
Scenario: Two students may share same first 8 UUID characters
System Response: Enrollment number is id.substring(0, 8).toUpperCase() — not guaranteed unique
Recovery: Use full UUID for unique identification
Prevention: Inform staff that enrollment numbers are display-only identifiers
RISK 31: GST Settings Affect All New Admissions
Severity: Medium Scenario: Admin changes GST settings mid-year System Response: New admissions use the NEW GST rate. Existing admissions' GST is historical snapshot. Recovery: N/A — by design. Only new admissions affected. Prevention: Communicate GST changes before implementation
RISK 32: Installment Auto-Generation May Create Misaligned Dates
Severity: Low Scenario: Batch payment schedule has percentages that don't divide remaining amount evenly System Response: Last installment takes the remainder (no even-split logic) Recovery: Manually adjust installment amounts after creation Prevention: Review batch payment schedule before admissions
RISK 33: Empty Counselor Route Directory
Severity: Low Scenario: Someone navigates to /counselor/ System Response: 404 — directory has no route files Recovery: Counselors use the main admin layout Prevention: N/A — intentional empty directory