# Development Plan ## Delivery Approach The system will be built in controlled phases. Each phase must update: - `docs/progress-log.md` - `docs/changelog.md` - `docs/decision-log.md` when architecture, package, database, or security decisions change Controllers must remain thin. Business rules should live in service classes, policies, form requests, action classes, and model relationships. ## Phase 0: Project Inspection and Planning Status: Completed on 2026-05-28. Tasks: - Inspect current workspace. - Confirm local toolchain. - Create required documentation files. - Record assumptions. - Propose architecture. - Propose database schema. - Document RBAC, workflows, UI/UX, tests, deployment notes, and package choices. Exit criteria: - All required Phase 0 documentation files exist. - No application code has been created. ## Phase 1: Laravel Setup and Authentication Status: Completed on 2026-05-28. Tasks: - Create Laravel application using latest stable version compatible with PHP 8.5 and the available Composer environment. - Configure MySQL connection. - Install authentication scaffolding using Laravel Breeze Blade stack. - Install Bootstrap 5, Bootstrap Icons, and Vite asset pipeline. - Install Phase 1 package baseline: - `spatie/laravel-permission` - `spatie/laravel-activitylog` - `maatwebsite/excel` - compatible QR code package - Laravel Pint if not already included - Larastan/PHPStan if compatible - SweetAlert2 - Tom Select or Select2 - Flatpickr - Configure base responsive layout. - Install and configure `spatie/laravel-permission`. - Create roles and permissions seeders. - Create role-based dashboard routes. - Add middleware for authenticated and role-protected areas. - Configure Laravel Pint and testing baseline. Expected deliverables: - Running Laravel app. - Login flow. - Optional public registration path disabled for generic account creation unless needed for Pemohon flow. - Base dashboard per role. - Seeded roles and permissions. - Package choices recorded in `docs/decision-log.md`. Manual testing: - Login works. - Seeded Admin user can access admin dashboard. - Users cannot access dashboards for roles they do not have. - Mobile navigation works. ## Phase 2: Core Database and Seeders Status: Completed on 2026-05-28. Tasks: - Create migrations for elections, settings, hierarchy, positions, quotas, applications, documents, assignments, histories, representatives, wheelchair records, attendance, finance verification, export logs, and system notes. - Create Eloquent models and relationships. - Implement factories for test data. - Seed default positions, roles, permissions, sample election, sample Bahagian, Daerah, Pusat, and Saluran. Expected deliverables: - Core schema migrated successfully. - Seeders create a usable baseline. - Model relationships verified with tests or tinker checks. Manual testing: - Admin can see seeded hierarchy once UI exists. - Relationships return expected records. ## Phase 3: Admin Setup Module Status: Completed on 2026-05-29. Tasks: - CRUD for Bahagian Pilihanraya, Daerah Mengundi, Pusat Mengundi, and Saluran Mengundi. - CRUD for Jawatan and quotas. - Assign PPM to Pusat Mengundi through the assignment model. - Generate or refresh QR public UUID links per Pusat Mengundi. - Add server-side pagination for setup lists. - Provide a public QR placeholder route until the full Phase 4 applicant form is implemented. - Keep KKM, JKM, and Police detail management for Phase 7 Admin management because those operational forms need the richer post-registration edit-note and audit flow. Expected deliverables: - Admin can prepare election structure and registration links. - QR link format uses public UUID/ULID, not internal IDs. - PPM assignment writes to `staff_assignments` and assigns the `PPM` RBAC role to the selected user. Manual testing: - Admin can create hierarchy from top to bottom. - QR URL resolves to intended Pusat Mengundi. - Internal IDs are not exposed in public URLs. - Admin can create or update position quotas for pusat-level and saluran-level roles. - Non-Admin users cannot access setup routes. ## Phase 4: Public Application Module Status: Completed on 2026-05-29. Tasks: - Self-registers with public QR link. - Public QR route `/pohon/{pusat_mengundi_uuid}`. - Application form for KTM, KP, KPDP, and PAPM. - Dynamic KTM selection for KP/KPDP based on approved KTM with vacancy. - Secure private upload for IC document and bank statement. - Duplicate IC validation within current election. - Block self-registration when a KTM-created applicant exists for the same IC. - Registration period enforcement. - Create pending bank verification record for submitted public applications. - Record application submission, document upload, and status history. Expected deliverables: - Public applicant form works during registration period only. - Required documents are stored privately. - Clear blocked/closed messages. - QR route continues to use public UUID and never exposes internal Pusat Mengundi IDs. Manual testing: - Submit valid application. - Try duplicate IC. - Try missing documents. - Try after registration closes. - Try KP with a KTM that has remaining vacancy. - Try KP with a KTM whose KP quota is full. ## Phase 5: PPM Module Status: Completed on 2026-05-29. Tasks: - PPM dashboard scoped to assigned Pusat Mengundi. - Application review listing and details. - Approve/reject applications. - Change applicant role before approval. - Assign KTM to Saluran. - Assign KP/KPDP to approved KTM under same Pusat Mengundi. - View Police, KKM, and JKM information. - Audit role and assignment changes. - Download uploaded applicant documents through authorized PPM route. - Record-level authorization through policy and PPM assignment scope. - Polling-day attendance remains in Phase 10 to keep the later attendance reporting/export design together. Expected deliverables: - PPM can manage only own Pusat Mengundi. - Approval requires IC and bank statement documents. - Approval creates active `staff_assignments` and assignment history. Manual testing: - PPM cannot view another Pusat Mengundi. - PPM cannot approve missing-document applicants. - Assignment changes are audited. - PPM can approve KTM into an available Saluran. - PPM can change role and assign KP/KPDP to an active KTM with vacancy. ## Phase 6: KTM Module Status: Completed on 2026-05-29. Tasks: - KTM dashboard for assigned Saluran. - View KP team. - Register KP only. - Approve KP only under own team. - Delete KTM-created applicant where appropriate. - Send email notification to applicant for KTM-created record. - Enforce registration period. - Prevent KTM from managing non-KP roles or other KTM teams. - Prevent KTM from deleting assigned applications. Expected deliverables: - KTM cannot register or approve non-KP roles. - KTM cannot manage another KTM team. - Email notification is queued or sent through Laravel mail. - KTM-created records use `source = created_by_ktm`. Manual testing: - KTM creates KP. - Applicant receives email in local mail log/mailpit equivalent. - KTM cannot create KP after registration closes. - KTM can approve only KP under own Saluran/team. - KTM can delete own unassigned KTM-created applicant so public self-registration can proceed. ## Phase 7: Admin Management and Manual Entry Status: Completed on 2026-05-29. Tasks: - Admin global list of applications and assignments. - Manual staff entry. - Edit approved staff records. - Direct assignment to Pusat and Saluran. - Police, KKM, and JKM management. - Post-registration edit notes. - Full audit trail for Admin changes. Expected deliverables: - Admin can correct and maintain all operational data. - Edits after registration period require `catatan`. - Manual Admin-created records use `source = admin_manual` and do not trigger KTM/applicant bank request email behavior. - Direct Admin assignment uses `source = admin_direct` or `admin_update` and writes assignment history, application status history, system notes when required, and activity log entries. Manual testing: - Admin edits before and after registration closing. - Post-closing edit without note is rejected. - Audit record includes note. - Admin can create Police, KKM, and JKM representative records. - Admin Kewangan cannot access placement management routes. ## Phase 8: Finance Module Status: Completed on 2026-05-29. Tasks: - Admin Kewangan dashboard. - Bank verification listing and filters. - Mark bank record as pending, verified, rejected, or requires correction. - Add finance note. - Export finance checking list. - Prevent assignment modification by Admin Kewangan. Expected deliverables: - Finance users can verify bank data but cannot alter placement. - Finance list includes approved/assigned staff only. - Export creates `export_logs` row and stores XLSX file in private storage. Manual testing: - Finance status changes are audited. - Finance user cannot access assignment edit routes. - Filter by Pusat Mengundi, role, bank status, missing bank statement, missing account number, and keyword. - Download finance verification export and confirm export log is created. ## Phase 9: Wheelchair Module Status: Completed on 2026-05-29. Tasks: - Admin manages wheelchair allocation per Pusat Mengundi. - Record taken quantity, taken time, person taking. - Record return quantity, return time, condition, and notes. - Audit wheelchair transactions. Expected deliverables: - Wheelchair movement is traceable by Pusat Mengundi and election. - Taken quantity cannot exceed allocation available balance. - Returned quantity cannot exceed outstanding taken quantity. - Allocation cannot be reduced below outstanding quantity. Manual testing: - Quantity cannot exceed allocation without an explicit business exception. - Transaction history displays clearly. - Admin Kewangan cannot access wheelchair management routes. ## Phase 10: Attendance Module Status: Completed on 2026-05-29. Tasks: - PPM attendance screen scoped to own Pusat Mengundi. - Attendance status: present, absent, not recorded. - Check-in time, recorded by, and note. - Admin attendance dashboard by Pusat Mengundi. - Attendance summary and detail exports. Expected deliverables: - PPM can record polling-day attendance. - Admin can monitor attendance by role and Pusat. - Attendance recording is blocked unless `election_settings.is_attendance_active` is true. - Attendance detail export creates `export_logs` row. Manual testing: - PPM cannot record attendance outside own Pusat. - Attendance summary totals match detail records. - Excel export creates export log. - Admin Kewangan cannot access Admin attendance dashboard. ## Phase 11: Testing, Polish, and Hardening Status: Completed on 2026-05-29. Tasks: - Complete feature and unit tests. - Review authorization coverage. - Review upload and private document access. - Review masking of sensitive data. - Run Laravel Pint. - Run static analysis if installed and compatible. - Performance review for large lists and exports. - Final documentation update. Expected deliverables: - Production-ready baseline with documented deployment steps. - Authorized Admin and Admin Kewangan document downloads for sensitive uploaded files. - Final quality gate run with tests, Pint, PHPStan, route list, and Vite build. Manual testing: - End-to-end rehearsal from setup to attendance export. - Mobile checks for Admin, PPM, KTM, and public application forms. - Verify Admin can download IC and bank documents. - Verify Admin Kewangan can download bank statement only. ## Phase 12: Critical Fixes and Security Hardening Status: Not started. Scope: Address issues identified during post-Phase 11 code review that could cause data inconsistency, silent authorization bypass, or require direct database edits in production. Full detail in `docs/09-improvement-plan.md`. Tasks: - 12-A: Add Admin Settings UI for toggling `is_attendance_active` and `is_registration_open_override`. - 12-B: Fix IC duplicate check to explicitly exclude soft-deleted records. - 12-C: Harden `AdminPostCloseNoteService` catatan enforcement so it cannot be bypassed by new controllers. - 12-D: Audit and fix `SampleElectionSeeder` for stale position name references after Phase 11 rename migration. - 12-E: Centralize and consistently enforce document type checks across all download paths. Expected deliverables: - Admin can toggle attendance activation and registration override from UI without touching the database. - Soft-deleted KTM-created records do not block public re-registration. - Post-closing catatan enforcement is consistent across all Admin management routes. - Seeder runs cleanly on a fresh database without errors. - All document download routes enforce role-specific type restrictions consistently. Exit criteria: - `php artisan migrate:fresh --seed` passes. - `php artisan test` passes with new tests for each fix. - `vendor\bin\pint.bat --test` passes. - `vendor\bin\phpstan.bat analyse --memory-limit=1G` passes. Docs to update on completion: `docs/progress-log.md`, `docs/changelog.md`, `docs/decision-log.md`, `docs/04-rbac-permission-matrix.md`, `docs/05-workflow-design.md`. ## Phase 13: Database Indexes and Performance Status: Not started. Scope: Add composite indexes before real election data is loaded. Full detail in `docs/09-improvement-plan.md`. Tasks: - 13-A: Add composite indexes on `applications`, `staff_assignments`, and `bank_verifications` for common Admin and service-layer queries. Expected deliverables: - New migration with explicit short-named indexes. - Admin global list and vacancy service queries confirmed to use indexes. Exit criteria: - `php artisan migrate` passes. - `php artisan test` passes. - Key queries verified with MySQL `EXPLAIN`. Docs to update on completion: `docs/03-database-design.md`, `docs/08-deployment-notes.md`, `docs/decision-log.md`, `docs/progress-log.md`, `docs/changelog.md`. ## Phase 14: Applicant Status Portal Status: Not started. Scope: Give public applicants a read-only status page accessible via their application UUID, with no login required. Full detail in `docs/09-improvement-plan.md`. Tasks: - 14-A: Add `GET /permohonan/{application:public_uuid}/status` route and controller. - Display limited, masked applicant data: partial name, role applied, pusat, current status, rejection reason if applicable. - Do not expose sensitive fields (full IC, bank account) or internal IDs. - Return 200 with a generic message for unknown UUIDs rather than 404 to avoid enumeration. Expected deliverables: - Applicants can check their status without logging in. - Sensitive data is not revealed. Exit criteria: - Feature tests for submitted, approved, assigned, rejected, and unknown UUID states. - `php artisan test` passes. Docs to update on completion: `docs/05-workflow-design.md`, `docs/06-ui-ux-plan.md`, `docs/decision-log.md`, `docs/progress-log.md`, `docs/changelog.md`. ## Phase 15: Export Retention and Sensitive Data Purge Status: Not started. Scope: Automatically purge old export files that contain sensitive bank and personal data. Full detail in `docs/09-improvement-plan.md`. Tasks: - 15-A: Add `ExportPurgeCommand` Artisan command with configurable `--days=` retention period. - Command deletes files from private storage and marks `export_logs` rows with `purged_at`. - Add `purged_at` column to `export_logs` via new migration. - Register command as a scheduled daily task. Expected deliverables: - Old export files are purged automatically. - `export_logs` reflects purge status. - Scheduler configuration is documented in deployment notes. Exit criteria: - `php artisan exports:purge` runs without error. - Files older than retention period are deleted; newer files are kept. - `php artisan test` passes. Docs to update on completion: `docs/03-database-design.md`, `docs/08-deployment-notes.md`, `docs/decision-log.md`, `docs/progress-log.md`, `docs/changelog.md`.