first
This commit is contained in:
98
docs/00-project-brief.md
Normal file
98
docs/00-project-brief.md
Normal file
@@ -0,0 +1,98 @@
|
||||
# Sistem Pengurusan Pusat Mengundi Pilihanraya Negeri - Project Brief
|
||||
|
||||
## Purpose
|
||||
|
||||
This project is a Laravel and MySQL web application for managing staffing, assignments, documentation, attendance, finance verification, exports, and operational records for Pusat Mengundi during a Pilihanraya Negeri.
|
||||
|
||||
The application supports a practical Malaysian government/PBT-style workflow where Admin users configure election geography and staffing needs, PPM users review and assign staff at their own Pusat Mengundi, KTM users manage KP under their own team, Admin Kewangan verifies banking data, and public Pemohon users apply through QR links tied to a specific Pusat Mengundi.
|
||||
|
||||
## Workspace Inspection
|
||||
|
||||
Inspection date: 2026-05-28
|
||||
|
||||
Original findings:
|
||||
|
||||
- Workspace path: `C:\Users\User\Aplikasi\prn2026`
|
||||
- No existing Laravel application was present.
|
||||
- No `.git` repository was initialized.
|
||||
- No `AGENTS.md` project instruction file was present.
|
||||
- PHP CLI was available: PHP 8.5.1.
|
||||
- Composer was available: Composer 2.9.4.
|
||||
- Node was available: Node 25.1.0.
|
||||
- PowerShell blocks `npm.ps1`; use `npm.cmd` for npm commands.
|
||||
- MySQL CLI was not available in PATH.
|
||||
|
||||
Current implementation findings:
|
||||
|
||||
- Laravel application has been scaffolded.
|
||||
- Installed Laravel skeleton version: 13.8.0.
|
||||
- Installed Laravel framework version: 13.12.0.
|
||||
- Local `.env` is configured for MySQL database `prn2026`.
|
||||
- MySQL connection was confirmed through Laravel/PDO during Phase 2.
|
||||
|
||||
## Phase 1 Snapshot
|
||||
|
||||
Update date: 2026-05-28
|
||||
|
||||
- Authentication scaffolding is available through Laravel Breeze.
|
||||
- Generic public account registration route is disabled.
|
||||
- Bootstrap 5 layout and role-based dashboard routes are available.
|
||||
- RBAC package, roles, permissions, and default Admin seeder are available.
|
||||
- `.env.example` is configured for MySQL.
|
||||
|
||||
## Phase 2 Snapshot
|
||||
|
||||
Update date: 2026-05-28
|
||||
|
||||
- Core election-domain migrations are implemented.
|
||||
- Domain models and Eloquent relationships are implemented.
|
||||
- Factory baseline is available for core hierarchy/application/assignment models.
|
||||
- Seeders create roles, permissions, positions, sample election hierarchy, quotas, sample role users, sample dual-role PPM/KTM assignment, and wheelchair allocation.
|
||||
|
||||
## Target Users
|
||||
|
||||
- Admin
|
||||
- Admin Kewangan
|
||||
- PPM
|
||||
- KTM
|
||||
- Pemohon
|
||||
|
||||
## Core Hierarchy
|
||||
|
||||
The system is built around this election hierarchy:
|
||||
|
||||
```text
|
||||
Bahagian Pilihanraya
|
||||
└── Daerah Mengundi
|
||||
└── Pusat Mengundi
|
||||
└── Saluran Mengundi
|
||||
```
|
||||
|
||||
Each Pusat Mengundi can have one PPM, multiple PAPM, KKM representative, JKM representative, Police escort data, wheelchair allocation, and multiple Saluran Mengundi. Each Saluran Mengundi can have one KTM, one Polis Pengiring, one KPDP, four KP, and one calon simpanan.
|
||||
|
||||
## Main Business Outcomes
|
||||
|
||||
- Configure election event, registration period, polling date, hierarchy, positions, and quotas.
|
||||
- Generate secure QR links for public application per Pusat Mengundi.
|
||||
- Collect applicant details and required documents through a public-facing form.
|
||||
- Enforce vacancy, role, period, and duplicate IC rules.
|
||||
- Allow PPM to approve, reject, change roles, and assign staff only within their own Pusat Mengundi.
|
||||
- Allow KTM to register and approve KP only under their own team.
|
||||
- Allow Admin to manage all data, including post-closing changes with required notes.
|
||||
- Allow Admin Kewangan to verify banking information without changing assignments.
|
||||
- Track wheelchairs and polling-day attendance.
|
||||
- Export operational and finance reports while logging every export.
|
||||
- Audit important changes, including previous values, new values, actor, timestamp, and notes where applicable.
|
||||
|
||||
## Phase 0 Scope
|
||||
|
||||
Phase 0 was documentation and planning only. No Laravel project scaffolding, package installation, migrations, or feature code was created in Phase 0.
|
||||
|
||||
## Phase 1 Scope
|
||||
|
||||
Phase 1 delivered Laravel setup, authentication, Bootstrap base layout, role and permission setup, and base dashboards.
|
||||
|
||||
## Phase 2 Scope
|
||||
|
||||
Phase 2 delivered the core database, models, relationships, factories, and seeders needed for the election domain.
|
||||
|
||||
458
docs/01-development-plan.md
Normal file
458
docs/01-development-plan.md
Normal file
@@ -0,0 +1,458 @@
|
||||
# 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`.
|
||||
209
docs/02-architecture.md
Normal file
209
docs/02-architecture.md
Normal file
@@ -0,0 +1,209 @@
|
||||
# Initial Architecture
|
||||
|
||||
## Technology Stack
|
||||
|
||||
- Backend: Laravel 13.12.0, installed through `laravel/laravel` 13.8.0.
|
||||
- Database: MySQL.
|
||||
- Views: Blade.
|
||||
- Styling: Bootstrap 5 with Bootstrap Icons.
|
||||
- JavaScript: Vite-managed assets, jQuery where it improves progressive enhancement, plus selected UI helpers.
|
||||
- Queue: Laravel queue system for email and heavier export-related tasks where useful.
|
||||
- Mail: Laravel Mail and Notifications with Markdown mail templates.
|
||||
- Authorization: Laravel policies, gates, middleware, and `spatie/laravel-permission`.
|
||||
- Auditing: `spatie/laravel-activitylog` plus domain-specific history tables where a structured audit timeline is needed.
|
||||
- Export: OpenSpout for current PHP 8.5 compatibility. `maatwebsite/excel` remains the preferred package from the original requirement but was not installable in this environment during Phase 1.
|
||||
|
||||
## Phase 1 Implemented Structure
|
||||
|
||||
Implemented:
|
||||
|
||||
- Breeze auth controllers and routes.
|
||||
- Generic `/register` routes disabled.
|
||||
- Bootstrap-based `layouts.app`, `layouts.guest`, and navigation.
|
||||
- Role-aware dashboards:
|
||||
- `/admin/dashboard`
|
||||
- `/kewangan/dashboard`
|
||||
- `/ppm/dashboard`
|
||||
- `/ktm/dashboard`
|
||||
- `/pemohon/dashboard`
|
||||
- `/dashboard` role redirect.
|
||||
- Spatie role middleware aliases:
|
||||
- `role`
|
||||
- `permission`
|
||||
- `role_or_permission`
|
||||
- `RolePermissionSeeder`.
|
||||
- Default Admin seed account from `config/prn.php`.
|
||||
|
||||
## Application Structure
|
||||
|
||||
Planned directories:
|
||||
|
||||
```text
|
||||
app/
|
||||
Actions/
|
||||
Exports/
|
||||
Http/
|
||||
Controllers/
|
||||
Requests/
|
||||
Middleware/
|
||||
Models/
|
||||
Notifications/
|
||||
Policies/
|
||||
Services/
|
||||
Support/
|
||||
```
|
||||
|
||||
Guidelines:
|
||||
|
||||
- Controllers orchestrate requests and responses only.
|
||||
- Form Request classes validate input and authorize request-specific actions.
|
||||
- Policies enforce model-level access.
|
||||
- Services hold business rules and transactional workflows.
|
||||
- Action classes may be used for high-value operations such as approving applications, changing assignments, registering KTM-created KP, and recording attendance.
|
||||
- Export classes live in `app/Exports`.
|
||||
- Report filters and query preparation live in services, not controllers.
|
||||
|
||||
## Architectural Layers
|
||||
|
||||
### Presentation Layer
|
||||
|
||||
- Blade templates with Bootstrap 5.
|
||||
- Responsive layouts for desktop, tablet, and mobile.
|
||||
- Shared layout components for navigation, cards, filters, badges, breadcrumbs, tables, empty states, and modals.
|
||||
- Mobile-friendly table alternatives for high-density data.
|
||||
|
||||
### HTTP Layer
|
||||
|
||||
- Named routes.
|
||||
- Route groups by role and module.
|
||||
- Middleware for authentication, role, registration period, and public QR validation.
|
||||
- Route model binding using internal IDs for authenticated admin areas where safe.
|
||||
- Public routes use UUID/ULID identifiers only.
|
||||
|
||||
### Domain Layer
|
||||
|
||||
Core services:
|
||||
|
||||
- `RegistrationPeriodService`
|
||||
- `VacancyService`
|
||||
- `ApplicationService`
|
||||
- `AssignmentService`
|
||||
- `KtmTeamService`
|
||||
- `PpmScopeService`
|
||||
- `BankVerificationService`
|
||||
- `AttendanceService`
|
||||
- `WheelchairService`
|
||||
- `ExportLoggingService`
|
||||
- `SensitiveDataMaskingService`
|
||||
|
||||
Potential action classes:
|
||||
|
||||
- `SubmitPublicApplication`
|
||||
- `CreateKtmApplicant`
|
||||
- `ApproveApplication`
|
||||
- `RejectApplication`
|
||||
- `ChangeApplicantRole`
|
||||
- `AssignStaffToSaluran`
|
||||
- `RecordAttendance`
|
||||
- `VerifyBankDetails`
|
||||
- `RecordWheelchairTransaction`
|
||||
|
||||
### Data Layer
|
||||
|
||||
- Eloquent models and relationships.
|
||||
- Foreign keys for domain integrity.
|
||||
- Soft deletes for records that should remain auditable.
|
||||
- Domain history tables for applications and assignments.
|
||||
- Activity log for actor/value snapshots and operational traceability.
|
||||
|
||||
## Role-Based Areas
|
||||
|
||||
Planned route groups:
|
||||
|
||||
```text
|
||||
/dashboard
|
||||
/admin/...
|
||||
/ppm/...
|
||||
/ktm/...
|
||||
/kewangan/...
|
||||
/pohon/{pusat_mengundi_uuid}
|
||||
```
|
||||
|
||||
Expected access:
|
||||
|
||||
- `Admin`: full operational management.
|
||||
- `Admin Kewangan`: finance verification and finance exports only.
|
||||
- `PPM`: assigned Pusat Mengundi only.
|
||||
- `KTM`: assigned Saluran/team only.
|
||||
- `Pemohon`: public form and own submission flow only if login is later required.
|
||||
|
||||
## Public Identifier Strategy
|
||||
|
||||
- Public QR links must use UUID or ULID stored on `pusat_mengundis.public_uuid` or equivalent.
|
||||
- Internal auto-increment IDs must not appear in public application URLs.
|
||||
- Uploaded documents are accessed only through authenticated authorization checks and signed/temporary download responses where needed.
|
||||
|
||||
## Dual-Role Assignment Strategy
|
||||
|
||||
The system must support a PPM who is also KTM. This should not be modeled as a single role column on a user or application.
|
||||
|
||||
Planned approach:
|
||||
|
||||
- Use `users` for login identity.
|
||||
- Use `applications` or staff records for applicant/staff biographical and banking data.
|
||||
- Use `staff_assignments` as the canonical placement table.
|
||||
- A person can have multiple active `staff_assignments` within the same election when business rules allow it.
|
||||
- `staff_assignments` references `position_id`, `pusat_mengundi_id`, optional `saluran_mengundi_id`, and optional supervisor/team references.
|
||||
- Uniqueness constraints prevent invalid duplicate active assignments, while allowing explicitly permitted dual role such as PPM + KTM.
|
||||
|
||||
## Audit Strategy
|
||||
|
||||
Use two audit levels:
|
||||
|
||||
- Structured domain histories for application status and assignment changes.
|
||||
- General activity log for before/after data changes, actor, timestamp, source, and notes.
|
||||
|
||||
Audit targets:
|
||||
|
||||
- Application approval/rejection.
|
||||
- Role changes.
|
||||
- Assignment changes.
|
||||
- Document upload/change.
|
||||
- Bank verification.
|
||||
- Admin edits after registration closes.
|
||||
- Attendance updates.
|
||||
- Wheelchair transactions.
|
||||
- Excel exports.
|
||||
|
||||
## File Storage Strategy
|
||||
|
||||
- IC documents and bank statements are stored on Laravel private storage disk.
|
||||
- Public symlink storage must not expose sensitive uploads.
|
||||
- File validation must restrict MIME type, size, and extension.
|
||||
- Document access must pass policy checks.
|
||||
- File records should be stored in `application_documents` with document type, disk, path, original name, MIME type, size, uploader, and timestamps.
|
||||
|
||||
## Export Strategy
|
||||
|
||||
- `maatwebsite/excel` will generate XLSX exports.
|
||||
- Every export inserts a row into `export_logs`.
|
||||
- Export logs include generated_by, generated_at, report_type, filter parameters, and file name.
|
||||
- Export services prepare queries and filters.
|
||||
- Export classes only shape spreadsheet data.
|
||||
|
||||
## Queue and Mail Strategy
|
||||
|
||||
- KTM-created applicant email notification should use Laravel Notifications or Mailables.
|
||||
- Queue should be enabled for mail in production.
|
||||
- Local development can use log mailer or local mail catcher.
|
||||
- Admin-keyed records do not trigger bank account email requests.
|
||||
|
||||
## Security Principles
|
||||
|
||||
- Use CSRF protection for all form submissions.
|
||||
- Use policies and middleware for every protected module.
|
||||
- Validate uploads strictly.
|
||||
- Store sensitive files privately.
|
||||
- Mask IC, bank account, phone, and email where full values are unnecessary.
|
||||
- Avoid raw SQL unless justified and documented.
|
||||
- Use transactions for approval, assignment, finance verification, and attendance updates.
|
||||
748
docs/03-database-design.md
Normal file
748
docs/03-database-design.md
Normal file
@@ -0,0 +1,748 @@
|
||||
# Proposed Database Design
|
||||
|
||||
## Phase 2 Implementation Status
|
||||
|
||||
Status: Implemented on 2026-05-28.
|
||||
|
||||
Implemented in migration:
|
||||
|
||||
- `user_profiles`
|
||||
- `elections`
|
||||
- `election_settings`
|
||||
- `bahagian_pilihanrayas`
|
||||
- `daerah_mengundis`
|
||||
- `pusat_mengundis`
|
||||
- `saluran_mengundis`
|
||||
- `positions`
|
||||
- `position_quotas`
|
||||
- `applications`
|
||||
- `application_documents`
|
||||
- `application_status_histories`
|
||||
- `staff_assignments`
|
||||
- `staff_assignment_histories`
|
||||
- `police_escorts`
|
||||
- `kkm_representatives`
|
||||
- `jkm_representatives`
|
||||
- `wheelchair_allocations`
|
||||
- `wheelchair_transactions`
|
||||
- `attendances`
|
||||
- `bank_verifications`
|
||||
- `export_logs`
|
||||
- `system_notes`
|
||||
|
||||
Implemented models and relationships are available under `app/Models`.
|
||||
|
||||
Implementation notes:
|
||||
|
||||
- Public-facing identifiers use UUID columns.
|
||||
- MySQL index names are explicitly shortened where Laravel's generated names would exceed MySQL's identifier limit.
|
||||
- Complex business constraints such as "four KP per KTM" are intentionally enforced by future services rather than only by database constraints.
|
||||
- A PPM plus KTM dual-role assignment is represented through two active rows in `staff_assignments`.
|
||||
|
||||
## Export Log Purge Columns (Fasa 15 — 2026-06-02)
|
||||
|
||||
Added via migration `2026_06_02_000001_add_purged_at_to_export_logs.php`.
|
||||
|
||||
| Column | Type | Purpose |
|
||||
|---|---|---|
|
||||
| `purged_at` | nullable timestamp | Set when the export file is deleted by `exports:purge` command |
|
||||
| `purged_by_user_id` | nullable FK → users | Reserved for future manual purge UI attribution |
|
||||
|
||||
Records with `purged_at IS NOT NULL` are skipped on subsequent purge runs.
|
||||
|
||||
## Performance Indexes (Fasa 13 — 2026-06-02)
|
||||
|
||||
Added via migration `2026_06_02_000000_add_performance_indexes.php`.
|
||||
|
||||
### `applications` table
|
||||
|
||||
| Index name | Columns | Purpose |
|
||||
|---|---|---|
|
||||
| `app_election_status_idx` | `(election_id, status)` | Dashboard count queries and admin list filtered by election |
|
||||
| `app_ktm_position_idx` | `(selected_ktm_assignment_id, requested_position_id)` | `KtmVacancyService` reserved-application count (called per KP/KPDP vacancy check) |
|
||||
|
||||
### `staff_assignments` table
|
||||
|
||||
| Index name | Columns | Purpose |
|
||||
|---|---|---|
|
||||
| `sa_reports_to_pos_status_idx` | `(reports_to_assignment_id, position_id, status)` | `KtmVacancyService` active-assignment count under a KTM (called per vacancy check) |
|
||||
|
||||
### Existing indexes (Phase 2)
|
||||
|
||||
| Table | Columns | Name |
|
||||
|---|---|---|
|
||||
| `applications` | `(election_id, ic_number)` | auto |
|
||||
| `applications` | `(election_id, pusat_mengundi_id, status)` | auto |
|
||||
| `staff_assignments` | `(election_id, pusat_mengundi_id, position_id)` | `sa_election_pusat_position_idx` |
|
||||
| `staff_assignments` | `(election_id, saluran_mengundi_id, position_id)` | `sa_election_saluran_position_idx` |
|
||||
| `attendances` | `(election_id, pusat_mengundi_id, status)` | `att_election_pusat_status_idx` |
|
||||
| `attendances` | `(election_id, staff_assignment_id)` | `att_election_staff_unique` (unique) |
|
||||
|
||||
### Why `bank_verifications` was not indexed with `election_id`
|
||||
|
||||
`bank_verifications` does not have a direct `election_id` column — it joins through `application_id` (unique FK). All finance queries that need election filtering use `whereHas('application', ...)` subqueries. The existing `status` index and unique `application_id` index are sufficient for the current query patterns.
|
||||
|
||||
## Design Goals
|
||||
|
||||
- Support one election or multiple election events over time.
|
||||
- Keep public identifiers separate from internal database IDs.
|
||||
- Support dual-role assignment, especially PPM also acting as KTM.
|
||||
- Keep sensitive documents private and access-controlled.
|
||||
- Preserve auditability through history tables and activity log.
|
||||
- Support exports, finance verification, attendance, wheelchair tracking, and post-closing notes.
|
||||
|
||||
## Naming Conventions
|
||||
|
||||
- Table names use Laravel plural snake_case where practical.
|
||||
- Existing Malay domain terms are retained for clarity.
|
||||
- Public UUID/ULID columns use names such as `public_uuid`.
|
||||
- Status fields use lowercase snake_case values.
|
||||
- Foreign keys use `foreignId()->constrained()` where possible.
|
||||
|
||||
## Core Identity Tables
|
||||
|
||||
### users
|
||||
|
||||
Laravel default user authentication table with additions as needed.
|
||||
|
||||
Key fields:
|
||||
|
||||
- `id`
|
||||
- `name`
|
||||
- `email`
|
||||
- `password`
|
||||
- `email_verified_at`
|
||||
- `remember_token`
|
||||
- timestamps
|
||||
- soft deletes if operationally useful
|
||||
|
||||
Notes:
|
||||
|
||||
- Roles are assigned through `spatie/laravel-permission`.
|
||||
- Not every public applicant must become a login user unless later required.
|
||||
|
||||
### user_profiles
|
||||
|
||||
Stores extended identity details for staff/admin users.
|
||||
|
||||
Key fields:
|
||||
|
||||
- `id`
|
||||
- `user_id`
|
||||
- `ic_number`
|
||||
- `phone_number`
|
||||
- `address`
|
||||
- `bank_name`
|
||||
- `bank_account_number`
|
||||
- timestamps
|
||||
|
||||
Notes:
|
||||
|
||||
- Sensitive fields should be masked in views where full values are not required.
|
||||
- IC uniqueness at profile level should be evaluated with election-specific application rules.
|
||||
|
||||
## Election and Settings
|
||||
|
||||
### elections
|
||||
|
||||
Represents each election event.
|
||||
|
||||
Key fields:
|
||||
|
||||
- `id`
|
||||
- `public_uuid`
|
||||
- `name`
|
||||
- `code`
|
||||
- `status`
|
||||
- `starts_at`
|
||||
- `polling_date`
|
||||
- timestamps
|
||||
- soft deletes
|
||||
|
||||
Suggested statuses:
|
||||
|
||||
- `draft`
|
||||
- `active`
|
||||
- `closed`
|
||||
- `archived`
|
||||
|
||||
### election_settings
|
||||
|
||||
Stores event-specific operational settings.
|
||||
|
||||
Key fields:
|
||||
|
||||
- `id`
|
||||
- `election_id`
|
||||
- `registration_start_date`
|
||||
- `registration_end_date`
|
||||
- `polling_date`
|
||||
- `is_registration_open`
|
||||
- `is_registration_open_override` nullable
|
||||
- `is_attendance_active`
|
||||
- timestamps
|
||||
|
||||
Notes:
|
||||
|
||||
- Registration open state is computed from dates and `is_registration_open`.
|
||||
- `is_registration_open_override` can be used later if the system needs to distinguish manual override from normal date-based availability.
|
||||
- Attendance can be toggled separately for rehearsal or polling day.
|
||||
|
||||
## Election Hierarchy
|
||||
|
||||
### bahagian_pilihanrayas
|
||||
|
||||
Key fields:
|
||||
|
||||
- `id`
|
||||
- `election_id`
|
||||
- `code`
|
||||
- `name`
|
||||
- timestamps
|
||||
- soft deletes
|
||||
|
||||
Relationships:
|
||||
|
||||
- belongs to election.
|
||||
- has many daerah mengundi.
|
||||
|
||||
### daerah_mengundis
|
||||
|
||||
Key fields:
|
||||
|
||||
- `id`
|
||||
- `election_id`
|
||||
- `bahagian_pilihanraya_id`
|
||||
- `code`
|
||||
- `name`
|
||||
- timestamps
|
||||
- soft deletes
|
||||
|
||||
Relationships:
|
||||
|
||||
- belongs to election.
|
||||
- belongs to bahagian pilihanraya.
|
||||
- has many pusat mengundi.
|
||||
|
||||
### pusat_mengundis
|
||||
|
||||
Key fields:
|
||||
|
||||
- `id`
|
||||
- `election_id`
|
||||
- `daerah_mengundi_id`
|
||||
- `public_uuid`
|
||||
- `code`
|
||||
- `name`
|
||||
- `address`
|
||||
- `registration_url`
|
||||
- `qr_code_path`
|
||||
- timestamps
|
||||
- soft deletes
|
||||
|
||||
Relationships:
|
||||
|
||||
- belongs to election.
|
||||
- belongs to daerah mengundi.
|
||||
- has many saluran mengundi.
|
||||
- has many position quotas.
|
||||
- has many staff assignments.
|
||||
- has one wheelchair allocation.
|
||||
|
||||
Notes:
|
||||
|
||||
- `public_uuid` is used by `/pohon/{pusat_mengundi_uuid}`.
|
||||
- `registration_url` can be generated dynamically; storing it is optional. If stored, it must be refreshed if domain changes.
|
||||
|
||||
### saluran_mengundis
|
||||
|
||||
Key fields:
|
||||
|
||||
- `id`
|
||||
- `election_id`
|
||||
- `pusat_mengundi_id`
|
||||
- `number`
|
||||
- `name`
|
||||
- `voter_count`
|
||||
- timestamps
|
||||
- soft deletes
|
||||
|
||||
Relationships:
|
||||
|
||||
- belongs to election.
|
||||
- belongs to pusat mengundi.
|
||||
- has many staff assignments.
|
||||
|
||||
## Roles and Permissions
|
||||
|
||||
Managed by `spatie/laravel-permission`:
|
||||
|
||||
- `roles`
|
||||
- `permissions`
|
||||
- `model_has_roles`
|
||||
- `model_has_permissions`
|
||||
- `role_has_permissions`
|
||||
|
||||
Application roles:
|
||||
|
||||
- Admin
|
||||
- Admin Kewangan
|
||||
- PPM
|
||||
- KTM
|
||||
- Pemohon
|
||||
|
||||
## Positions and Quotas
|
||||
|
||||
### positions
|
||||
|
||||
Stores operational staff positions.
|
||||
|
||||
Key fields:
|
||||
|
||||
- `id`
|
||||
- `code`
|
||||
- `name`
|
||||
- `scope`
|
||||
- `is_public_applyable`
|
||||
- `is_assignable`
|
||||
- `allows_dual_role`
|
||||
- timestamps
|
||||
- soft deletes
|
||||
|
||||
Suggested position codes:
|
||||
|
||||
- `PPM`
|
||||
- `KTM`
|
||||
- `KP`
|
||||
- `KPDP`
|
||||
- `PAPM`
|
||||
- `POLIS`
|
||||
- `KKM`
|
||||
- `JKM`
|
||||
- `CALON_SIMPANAN`
|
||||
|
||||
Suggested scopes:
|
||||
|
||||
- `pusat`
|
||||
- `saluran`
|
||||
- `external`
|
||||
|
||||
### position_quotas
|
||||
|
||||
Defines vacancy requirements by election, pusat, saluran, and role.
|
||||
|
||||
Key fields:
|
||||
|
||||
- `id`
|
||||
- `election_id`
|
||||
- `pusat_mengundi_id`
|
||||
- `saluran_mengundi_id` nullable
|
||||
- `position_id`
|
||||
- `quota`
|
||||
- timestamps
|
||||
- soft deletes
|
||||
|
||||
Notes:
|
||||
|
||||
- PPM, PAPM, KKM, JKM, and wheelchair-related staffing are usually Pusat scope.
|
||||
- KTM, KP, KPDP, Police escort, and calon simpanan are usually Saluran scope.
|
||||
- Use nullable `saluran_mengundi_id` for Pusat-level quota.
|
||||
|
||||
## Applications
|
||||
|
||||
### applications
|
||||
|
||||
Represents public, KTM-created, or Admin-keyed applicant/staff application record.
|
||||
|
||||
Key fields:
|
||||
|
||||
- `id`
|
||||
- `election_id`
|
||||
- `pusat_mengundi_id`
|
||||
- `selected_ktm_assignment_id` nullable
|
||||
- `user_id` nullable
|
||||
- `public_uuid`
|
||||
- `source`
|
||||
- `status`
|
||||
- `name`
|
||||
- `ic_number`
|
||||
- `phone_number`
|
||||
- `email`
|
||||
- `address`
|
||||
- `requested_position_id`
|
||||
- `approved_position_id` nullable
|
||||
- `bank_name`
|
||||
- `bank_account_number`
|
||||
- `created_by_user_id` nullable
|
||||
- `approved_by_user_id` nullable
|
||||
- `approved_at` nullable
|
||||
- `rejected_by_user_id` nullable
|
||||
- `rejected_at` nullable
|
||||
- `rejection_reason` nullable
|
||||
- timestamps
|
||||
- soft deletes
|
||||
|
||||
Suggested source values:
|
||||
|
||||
- `public`
|
||||
- `created_by_ktm`
|
||||
- `admin_manual`
|
||||
|
||||
Suggested status values are documented in `docs/05-workflow-design.md`.
|
||||
|
||||
Rules:
|
||||
|
||||
- IC uniqueness is enforced per election among active/non-cancelled records.
|
||||
- Public self-registration is blocked if an active `created_by_ktm` record exists for the same election and IC.
|
||||
- Admin may manually enter records after registration closes with required note.
|
||||
- Phase 4 public submissions create `source = public`, `status = submitted`, and a public UUID reference.
|
||||
- For KP and KPDP public applications, `selected_ktm_assignment_id` stores the selected active KTM assignment if vacancy remains.
|
||||
|
||||
### application_documents
|
||||
|
||||
Stores private uploaded documents.
|
||||
|
||||
Key fields:
|
||||
|
||||
- `id`
|
||||
- `application_id`
|
||||
- `document_type`
|
||||
- `disk`
|
||||
- `path`
|
||||
- `original_name`
|
||||
- `mime_type`
|
||||
- `size`
|
||||
- `uploaded_by_user_id` nullable
|
||||
- timestamps
|
||||
- soft deletes
|
||||
|
||||
Document types:
|
||||
|
||||
- `ic_document`
|
||||
- `bank_statement`
|
||||
- `other`
|
||||
|
||||
Rules:
|
||||
|
||||
- IC and bank statement documents are required before PPM approval.
|
||||
- Documents are stored on private disk.
|
||||
- Phase 4 public uploads store `ic_document` and `bank_statement` under private local storage using the application public UUID path.
|
||||
|
||||
### application_status_histories
|
||||
|
||||
Tracks application lifecycle status changes.
|
||||
|
||||
Key fields:
|
||||
|
||||
- `id`
|
||||
- `application_id`
|
||||
- `from_status` nullable
|
||||
- `to_status`
|
||||
- `changed_by_user_id` nullable
|
||||
- `note` nullable
|
||||
- `metadata` JSON nullable
|
||||
- timestamps
|
||||
|
||||
## Staff Assignments
|
||||
|
||||
### staff_assignments
|
||||
|
||||
Canonical table for approved staff placement.
|
||||
|
||||
Key fields:
|
||||
|
||||
- `id`
|
||||
- `election_id`
|
||||
- `application_id` nullable
|
||||
- `user_id` nullable
|
||||
- `position_id`
|
||||
- `pusat_mengundi_id`
|
||||
- `saluran_mengundi_id` nullable
|
||||
- `reports_to_assignment_id` nullable
|
||||
- `status`
|
||||
- `assigned_by_user_id`
|
||||
- `assigned_at`
|
||||
- `source`
|
||||
- timestamps
|
||||
- soft deletes
|
||||
|
||||
Suggested statuses:
|
||||
|
||||
- `active`
|
||||
- `pending`
|
||||
- `cancelled`
|
||||
- `replaced`
|
||||
|
||||
Notes:
|
||||
|
||||
- Allows one person to hold multiple assignments, including PPM and KTM.
|
||||
- `reports_to_assignment_id` can link KP/KPDP to the approved KTM assignment.
|
||||
- A KTM assignment to Saluran is created by PPM or Admin, not chosen by KTM.
|
||||
- Unique constraints should prevent duplicate active position assignment to the same role/scope where business rules require.
|
||||
|
||||
### staff_assignment_histories
|
||||
|
||||
Tracks assignment changes.
|
||||
|
||||
Key fields:
|
||||
|
||||
- `id`
|
||||
- `staff_assignment_id`
|
||||
- `from_position_id` nullable
|
||||
- `to_position_id` nullable
|
||||
- `from_pusat_mengundi_id` nullable
|
||||
- `to_pusat_mengundi_id` nullable
|
||||
- `from_saluran_mengundi_id` nullable
|
||||
- `to_saluran_mengundi_id` nullable
|
||||
- `from_reports_to_assignment_id` nullable
|
||||
- `to_reports_to_assignment_id` nullable
|
||||
- `changed_by_user_id`
|
||||
- `note` nullable
|
||||
- timestamps
|
||||
|
||||
## Representatives and External Operational Data
|
||||
|
||||
### police_escorts
|
||||
|
||||
Key fields:
|
||||
|
||||
- `id`
|
||||
- `election_id`
|
||||
- `pusat_mengundi_id`
|
||||
- `saluran_mengundi_id` nullable
|
||||
- `name`
|
||||
- `ic_number` nullable
|
||||
- `phone_number` nullable
|
||||
- `rank` nullable
|
||||
- `station` nullable
|
||||
- `notes` nullable
|
||||
- timestamps
|
||||
- soft deletes
|
||||
|
||||
### kkm_representatives
|
||||
|
||||
Key fields:
|
||||
|
||||
- `id`
|
||||
- `election_id`
|
||||
- `pusat_mengundi_id`
|
||||
- `name`
|
||||
- `ic_number` nullable
|
||||
- `phone_number` nullable
|
||||
- `agency` nullable
|
||||
- `notes` nullable
|
||||
- timestamps
|
||||
- soft deletes
|
||||
|
||||
### jkm_representatives
|
||||
|
||||
Key fields:
|
||||
|
||||
- `id`
|
||||
- `election_id`
|
||||
- `pusat_mengundi_id`
|
||||
- `name`
|
||||
- `ic_number` nullable
|
||||
- `phone_number` nullable
|
||||
- `agency` nullable
|
||||
- `notes` nullable
|
||||
- timestamps
|
||||
- soft deletes
|
||||
|
||||
## Wheelchair Management
|
||||
|
||||
### wheelchair_allocations
|
||||
|
||||
Key fields:
|
||||
|
||||
- `id`
|
||||
- `election_id`
|
||||
- `pusat_mengundi_id`
|
||||
- `allocated_quantity`
|
||||
- `notes` nullable
|
||||
- timestamps
|
||||
|
||||
### wheelchair_transactions
|
||||
|
||||
Key fields:
|
||||
|
||||
- `id`
|
||||
- `wheelchair_allocation_id`
|
||||
- `transaction_type`
|
||||
- `quantity`
|
||||
- `taken_at` nullable
|
||||
- `taken_by_name` nullable
|
||||
- `returned_at` nullable
|
||||
- `return_condition` nullable
|
||||
- `recorded_by_user_id`
|
||||
- `notes` nullable
|
||||
- timestamps
|
||||
|
||||
Suggested transaction types:
|
||||
|
||||
- `taken`
|
||||
- `returned`
|
||||
- `adjustment`
|
||||
|
||||
Return condition values:
|
||||
|
||||
- `baik`
|
||||
- `rosak`
|
||||
|
||||
## Attendance
|
||||
|
||||
### attendances
|
||||
|
||||
Key fields:
|
||||
|
||||
- `id`
|
||||
- `election_id`
|
||||
- `staff_assignment_id`
|
||||
- `pusat_mengundi_id`
|
||||
- `saluran_mengundi_id` nullable
|
||||
- `position_id`
|
||||
- `status`
|
||||
- `check_in_time` nullable
|
||||
- `recorded_by_user_id`
|
||||
- `note` nullable
|
||||
- timestamps
|
||||
|
||||
Attendance statuses:
|
||||
|
||||
- `present`
|
||||
- `absent`
|
||||
- `not_recorded`
|
||||
|
||||
Notes:
|
||||
|
||||
- Store denormalized pusat/saluran/position references for simpler reporting.
|
||||
- A unique key should prevent multiple active attendance rows for the same election and staff assignment.
|
||||
|
||||
## Finance
|
||||
|
||||
### bank_verifications
|
||||
|
||||
Key fields:
|
||||
|
||||
- `id`
|
||||
- `application_id`
|
||||
- `status`
|
||||
- `verified_by_user_id` nullable
|
||||
- `verified_at` nullable
|
||||
- `finance_note` nullable
|
||||
- timestamps
|
||||
|
||||
Statuses:
|
||||
|
||||
- `pending`
|
||||
- `verified`
|
||||
- `rejected`
|
||||
- `requires_correction`
|
||||
|
||||
Notes:
|
||||
|
||||
- Admin Kewangan can update this table only.
|
||||
- Assignment and placement edits are outside Admin Kewangan scope.
|
||||
- Phase 4 public submissions create an initial `pending` bank verification row.
|
||||
|
||||
## Exports and Notes
|
||||
|
||||
### export_logs
|
||||
|
||||
Key fields:
|
||||
|
||||
- `id`
|
||||
- `election_id` nullable
|
||||
- `generated_by_user_id`
|
||||
- `generated_at`
|
||||
- `report_type`
|
||||
- `filter_parameters` JSON nullable
|
||||
- `file_name`
|
||||
- `disk` nullable
|
||||
- `path` nullable
|
||||
- timestamps
|
||||
|
||||
Report types:
|
||||
|
||||
- `full_staff_list`
|
||||
- `staff_list_by_pusat`
|
||||
- `staff_list_by_role`
|
||||
- `missing_document_list`
|
||||
- `finance_verification_list`
|
||||
- `attendance_summary`
|
||||
- `attendance_detail_by_pusat`
|
||||
|
||||
### system_notes
|
||||
|
||||
Stores structured operational notes, especially post-registration changes.
|
||||
|
||||
Key fields:
|
||||
|
||||
- `id`
|
||||
- `election_id`
|
||||
- `noteable_type`
|
||||
- `noteable_id`
|
||||
- `note_type`
|
||||
- `note`
|
||||
- `created_by_user_id`
|
||||
- timestamps
|
||||
|
||||
Suggested note types:
|
||||
|
||||
- `post_registration_change`
|
||||
- `admin_correction`
|
||||
- `finance_note`
|
||||
- `general`
|
||||
|
||||
## Audit Log
|
||||
|
||||
### activity_log
|
||||
|
||||
Managed by `spatie/laravel-activitylog`.
|
||||
|
||||
Required logged details:
|
||||
|
||||
- actor
|
||||
- subject
|
||||
- event name
|
||||
- previous values
|
||||
- new values
|
||||
- timestamp
|
||||
- note or reason where available
|
||||
|
||||
## Key Constraints and Indexes
|
||||
|
||||
Planned indexes:
|
||||
|
||||
- `elections.public_uuid`
|
||||
- `pusat_mengundis.public_uuid`
|
||||
- `applications.public_uuid`
|
||||
- `applications.election_id, ic_number`
|
||||
- `applications.election_id, pusat_mengundi_id, status`
|
||||
- `staff_assignments.election_id, pusat_mengundi_id, position_id`
|
||||
- `staff_assignments.election_id, saluran_mengundi_id, position_id`
|
||||
- `bank_verifications.status`
|
||||
- `attendances.election_id, pusat_mengundi_id, status`
|
||||
- `export_logs.generated_by_user_id, generated_at`
|
||||
|
||||
Planned uniqueness:
|
||||
|
||||
- Unique active public UUIDs.
|
||||
- One active PPM per Pusat Mengundi.
|
||||
- One active KTM per Saluran Mengundi.
|
||||
- One active KPDP per KTM or Saluran based on final rule implementation.
|
||||
- Four active KP per KTM/team enforced by quota service, not only DB constraint.
|
||||
- Application IC uniqueness per election for active public/admin records, with special handling for deleted/cancelled KTM-created records.
|
||||
|
||||
## Documented Trade-Offs
|
||||
|
||||
- `staff_assignments` is used instead of a single role column because the system must support dual-role assignment.
|
||||
- Domain-specific history tables are retained in addition to generic activity log because workflows need reportable status/assignment timelines.
|
||||
- Representative tables are separate from applications because Police, KKM, and JKM records may not follow the public applicant workflow.
|
||||
- Attendance denormalizes role and location references to make election-day reporting faster and more stable.
|
||||
## Kemas kini operasi Pusat Mengundi - 2026-05-31
|
||||
|
||||
- `saluran_mengundis.name` dikekalkan sebagai nullable untuk keserasian rekod lama, tetapi tidak lagi diminta pada UI.
|
||||
- Semasa Pusat Mengundi baharu didaftarkan, Saluran bernombor `1..N` dijana secara automatik.
|
||||
- `position_quotas` menyimpan `PPM`, `PAPM`, `JKM` dan `KKM` pada skop Pusat serta `KTM`, `POLIS`, `KP`, `KPDP` dan `CALON_TAMBAHAN` pada skop Saluran.
|
||||
- Kod jawatan `CALON_SIMPANAN` dimigrasikan kepada `CALON_TAMBAHAN`.
|
||||
187
docs/04-rbac-permission-matrix.md
Normal file
187
docs/04-rbac-permission-matrix.md
Normal file
@@ -0,0 +1,187 @@
|
||||
# RBAC and Permission Matrix
|
||||
|
||||
## Roles
|
||||
|
||||
- Admin
|
||||
- Admin Kewangan
|
||||
- PPM
|
||||
- KTM
|
||||
- Pemohon
|
||||
|
||||
Permissions will be implemented with `spatie/laravel-permission`. Policies will still enforce record-level ownership, such as PPM only accessing assigned Pusat Mengundi and KTM only accessing own team.
|
||||
|
||||
## Permission Naming Pattern
|
||||
|
||||
Suggested naming:
|
||||
|
||||
```text
|
||||
module.action
|
||||
```
|
||||
|
||||
Examples:
|
||||
|
||||
- `admin.dashboard.view`
|
||||
- `election.manage`
|
||||
- `application.review`
|
||||
- `assignment.manage`
|
||||
- `finance.verify`
|
||||
- `attendance.record`
|
||||
- `export.generate`
|
||||
|
||||
## Matrix
|
||||
|
||||
| Capability | Admin | Admin Kewangan | PPM | KTM | Pemohon |
|
||||
|---|---:|---:|---:|---:|---:|
|
||||
| View own dashboard | Yes | Yes | Yes | Yes | Optional |
|
||||
| Manage users | Yes | No | No | No | No |
|
||||
| Manage roles and permissions | Yes | No | No | No | No |
|
||||
| Manage election settings (toggle attendance, override registration) | Yes (UI) | No | No | No | No |
|
||||
| Manage Bahagian Pilihanraya | Yes | No | No | No | No |
|
||||
| Manage Daerah Mengundi | Yes | No | No | No | No |
|
||||
| Manage Pusat Mengundi | Yes | No | No | No | No |
|
||||
| Manage Saluran Mengundi | Yes | No | No | No | No |
|
||||
| Manage Jawatan and quotas | Yes | No | No | No | No |
|
||||
| Generate QR links | Yes | No | View own only | No | No |
|
||||
| Submit public application | Manual only | No | No | No | Yes |
|
||||
| View all applications | Yes | Finance data only | No | No | No |
|
||||
| View own Pusat applications | Yes | No | Yes | No | No |
|
||||
| View own KTM team KP applications | Yes | No | Yes if same Pusat | Yes | No |
|
||||
| Create manual application | Yes | No | No after final rule; PPM reviews only | KP only | Public flow only |
|
||||
| Create KP applicant | Yes | No | No | Yes, own team only | No |
|
||||
| Approve/reject KTM application | Yes | No | Yes | No | No |
|
||||
| Approve/reject KP application | Yes | No | Yes | Yes, own team only | No |
|
||||
| Approve/reject KPDP application | Yes | No | Yes | No | No |
|
||||
| Approve/reject PAPM application | Yes | No | Yes | No | No |
|
||||
| Change applicant role before approval | Yes | No | Yes, own Pusat only | No | No |
|
||||
| Assign PPM | Yes | No | No | No | No |
|
||||
| Assign KTM to Saluran | Yes | No | Yes, own Pusat only | No | No |
|
||||
| Assign KP/KPDP to KTM | Yes | No | Yes, own Pusat only | No | No |
|
||||
| Assign self as KTM | Admin can assign if valid | No | No direct self-assignment | No | No |
|
||||
| Manage Police escort | Yes | No | View own Pusat only | View own Saluran only | No |
|
||||
| Manage KKM representative | Yes | No | View own Pusat only | No | No |
|
||||
| Manage JKM representative | Yes | No | View own Pusat only | No | No |
|
||||
| Manage wheelchair allocation | Yes | No | View or record if delegated later | No | No |
|
||||
| Record wheelchair transaction | Yes | No | Optional only if delegated | No | No |
|
||||
| Record attendance | Yes | No | Yes, own Pusat only | No | No |
|
||||
| View attendance dashboard | Yes | No | Own Pusat only | Own Saluran/team only if needed | No |
|
||||
| Verify bank account | View and override if needed | Yes | No | No | No |
|
||||
| Change assignment or placement | Yes | No | Own Pusat only | No | No |
|
||||
| Export full staff list | Yes | No | No | No | No |
|
||||
| Export staff list by Pusat | Yes | No | Own Pusat only if enabled | No | No |
|
||||
| Export missing document list | Yes | No | Own Pusat only if enabled | No | No |
|
||||
| Export finance verification list | Yes | Yes | No | No | No |
|
||||
| Export attendance list | Yes | No | Own Pusat only if enabled | No | No |
|
||||
| View audit trail | Yes | Limited finance-related if needed | Own Pusat events if needed | Own actions if needed | No |
|
||||
| Download ic_document | Yes (all) | No | Yes (own Pusat applications only) | No | No |
|
||||
| Download bank_statement | Yes (all) | Yes (all) | Yes (own Pusat applications only) | No | No |
|
||||
|
||||
## Initial Permission List
|
||||
|
||||
Admin:
|
||||
|
||||
- `admin.dashboard.view`
|
||||
- `users.manage`
|
||||
- `roles.manage`
|
||||
- `election.manage`
|
||||
- `hierarchy.manage`
|
||||
- `positions.manage`
|
||||
- `quotas.manage`
|
||||
- `qr.manage`
|
||||
- `applications.view_all`
|
||||
- `applications.create_manual`
|
||||
- `applications.update`
|
||||
- `applications.approve`
|
||||
- `applications.reject`
|
||||
- `assignments.manage_all`
|
||||
- `representatives.manage`
|
||||
- `wheelchairs.manage`
|
||||
- `attendance.view_all`
|
||||
- `attendance.record_all`
|
||||
- `finance.view`
|
||||
- `finance.verify`
|
||||
- `exports.generate_all`
|
||||
- `audit.view`
|
||||
- `system_notes.create`
|
||||
|
||||
Admin Kewangan:
|
||||
|
||||
- `finance.dashboard.view`
|
||||
- `finance.view`
|
||||
- `finance.verify`
|
||||
- `finance.export`
|
||||
- `applications.view_finance_fields`
|
||||
|
||||
PPM:
|
||||
|
||||
- `ppm.dashboard.view`
|
||||
- `applications.view_own_pusat`
|
||||
- `applications.review_own_pusat`
|
||||
- `applications.approve_own_pusat`
|
||||
- `applications.reject_own_pusat`
|
||||
- `applications.change_role_own_pusat`
|
||||
- `assignments.manage_own_pusat`
|
||||
- `representatives.view_own_pusat`
|
||||
- `attendance.record_own_pusat`
|
||||
- `attendance.view_own_pusat`
|
||||
- `exports.generate_own_pusat`
|
||||
|
||||
KTM:
|
||||
|
||||
- `ktm.dashboard.view`
|
||||
- `ktm.team.view`
|
||||
- `ktm.kp.create`
|
||||
- `ktm.kp.approve`
|
||||
- `ktm.kp.delete_created`
|
||||
- `applications.view_own_team`
|
||||
|
||||
Pemohon:
|
||||
|
||||
- `public_application.create`
|
||||
- `public_application.view_own_status` if applicant portal is implemented later
|
||||
|
||||
## Record-Level Authorization Rules
|
||||
|
||||
### PPM Scope
|
||||
|
||||
- PPM can only view and manage applications where `applications.pusat_mengundi_id` is assigned to their active PPM staff assignment.
|
||||
- PPM can only assign staff to Saluran under their assigned Pusat Mengundi.
|
||||
- PPM can approve only applications with required IC and bank statement documents.
|
||||
- Phase 5 implements this through `ApplicationPolicy` plus active PPM `staff_assignments` scope.
|
||||
- PPM document download is limited to documents attached to applications inside their assigned Pusat Mengundi.
|
||||
- Phase 10 allows PPM to record attendance only for active staff assignments under their own Pusat Mengundi and only when the attendance module is active.
|
||||
|
||||
### KTM Scope
|
||||
|
||||
- KTM can only view KP under their own KTM assignment.
|
||||
- KTM can only register KP, not KTM, KPDP, PAPM, PPM, Police, KKM, or JKM.
|
||||
- KTM can approve KP only where the KP belongs to their own team.
|
||||
- KTM cannot assign himself/herself to Saluran.
|
||||
- Phase 6 implements this through active KTM `staff_assignments` scope.
|
||||
- KTM-created records are marked `source = created_by_ktm`.
|
||||
- KTM can delete only their own unassigned `created_by_ktm` applicant records.
|
||||
|
||||
### Admin Kewangan Scope
|
||||
|
||||
- Admin Kewangan can view approved staff list and bank-related fields.
|
||||
- Admin Kewangan can update `bank_verifications`.
|
||||
- Admin Kewangan cannot update placement, position, Pusat Mengundi, or Saluran Mengundi.
|
||||
- Phase 7 confirms Admin Kewangan is blocked from Admin management routes such as assignment edit/listing.
|
||||
- Phase 8 implements `/kewangan/bank` for bank checking, status update, finance note, filters, and finance export.
|
||||
- Admin can also access finance bank routes for oversight, but assignment changes remain outside the finance module.
|
||||
|
||||
### Admin Management Scope
|
||||
|
||||
- Admin can view global application and assignment lists.
|
||||
- Admin can create manual application records.
|
||||
- Admin can edit applicant/staff details and direct assignment records.
|
||||
- Admin can key in Police, KKM, and JKM representatives.
|
||||
- Admin can manage wheelchair allocation and taken/returned transactions.
|
||||
- Admin can view attendance dashboard, attendance details by Pusat Mengundi, and generate attendance detail exports.
|
||||
- If registration is closed for the election, Admin create/edit/assign/representative changes require `catatan`.
|
||||
- Admin management changes write domain history/activity logs and `system_notes` for post-closing changes.
|
||||
|
||||
### Public Pemohon Scope
|
||||
|
||||
- Public users can access only QR application routes.
|
||||
- Public application is allowed only during registration period.
|
||||
- Public users cannot list other applicants or staff.
|
||||
299
docs/05-workflow-design.md
Normal file
299
docs/05-workflow-design.md
Normal file
@@ -0,0 +1,299 @@
|
||||
# Workflow Design
|
||||
|
||||
## Application Status Lifecycle
|
||||
|
||||
Final initial statuses:
|
||||
|
||||
```text
|
||||
draft
|
||||
submitted
|
||||
under_ppm_review
|
||||
approved
|
||||
rejected
|
||||
assigned
|
||||
cancelled
|
||||
deleted_by_ktm
|
||||
```
|
||||
|
||||
## Application Status Meaning
|
||||
|
||||
| Status | Meaning |
|
||||
|---|---|
|
||||
| `draft` | Created but not fully submitted. Used only if partial saves are implemented. |
|
||||
| `submitted` | Applicant or actor submitted the application for review. |
|
||||
| `under_ppm_review` | PPM has opened or started reviewing the application. |
|
||||
| `approved` | Application approved for a position but not necessarily assigned to final Saluran/team. |
|
||||
| `rejected` | Application rejected with reason. |
|
||||
| `assigned` | Approved applicant has active staff assignment. |
|
||||
| `cancelled` | Application cancelled by Admin or workflow rule. |
|
||||
| `deleted_by_ktm` | KTM-created placeholder/application removed so applicant can self-register. |
|
||||
|
||||
## Public QR Application Flow
|
||||
|
||||
Phase 4 implementation behavior:
|
||||
|
||||
- `/pohon/{pusat_mengundi_uuid}` resolves by public UUID and shows the public application form when registration is open.
|
||||
- If registration is closed, the same QR route shows a closed-registration page.
|
||||
- Successful submissions redirect to `/permohonan/{application_uuid}/berjaya`.
|
||||
- Uploaded IC documents and bank statements are stored on the private local disk under the application UUID path.
|
||||
- Submitted applications immediately create `application_documents`, `application_status_histories`, and pending `bank_verifications` records.
|
||||
|
||||
1. Applicant scans QR code for a Pusat Mengundi.
|
||||
2. System resolves `/pohon/{pusat_mengundi_uuid}`.
|
||||
3. System checks election and registration period.
|
||||
4. If registration is closed, show clear closed-registration message.
|
||||
5. Applicant selects role: KTM, KP, KPDP, or PAPM.
|
||||
6. If selected role is KP or KPDP, system shows approved KTM list for that Pusat Mengundi with available vacancy.
|
||||
7. Applicant fills personal, contact, bank, and address details.
|
||||
8. Applicant uploads IC document and bank statement.
|
||||
9. System validates IC uniqueness for current election.
|
||||
10. If an active KTM-created applicant exists for the same IC, system blocks self-registration and explains that KTM must delete the pre-created record first.
|
||||
11. System stores documents privately.
|
||||
12. Application status becomes `submitted`.
|
||||
13. Bank verification starts as `pending`.
|
||||
14. PPM reviews the application in Phase 5.
|
||||
|
||||
## Registration Period Rules
|
||||
|
||||
During registration period:
|
||||
|
||||
- Public Pemohon can register through QR.
|
||||
- KTM can register KP.
|
||||
- PPM can review and assign.
|
||||
|
||||
After registration period:
|
||||
|
||||
- Public self-registration is closed.
|
||||
- KTM cannot register new KP.
|
||||
- PPM cannot register new applicants.
|
||||
- Only Admin can register, edit, or assign staff.
|
||||
- Admin changes after closing require catatan.
|
||||
- System must show clear closed-registration messages.
|
||||
|
||||
Registration open calculation:
|
||||
|
||||
- `registration_start_date <= today <= registration_end_date`
|
||||
- plus `is_registration_open = true`.
|
||||
- if `is_registration_open_override` is not null, it explicitly opens or closes registration regardless of the normal date window.
|
||||
|
||||
## Applicant Status Portal
|
||||
|
||||
Pemohon awam boleh semak status permohonan melalui `/permohonan/{uuid}/status` tanpa perlu log masuk.
|
||||
|
||||
- UUID awam permohonan digunakan sebagai pengecam — tiada login diperlukan.
|
||||
- Pautan status dipaparkan pada halaman kejayaan permohonan dan boleh disalin.
|
||||
- Maklumat yang dipaparkan: nama (disamarkan 3 aksara pertama), jawatan dipohon, pusat mengundi, status, sebab penolakan (jika ditolak), nombor rujukan UUID.
|
||||
- Maklumat yang TIDAK dipaparkan: IC, akaun bank, nombor telefon, ID dalaman, saluran, nama KTM.
|
||||
- UUID tidak dikenali → 200 dengan mesej generik ("status tidak dapat dipaparkan") — tidak mendedahkan sama ada UUID pernah wujud.
|
||||
- Rekod `deleted_by_ktm` dan rekod soft-deleted juga mengembalikan mesej generik.
|
||||
|
||||
Status display mapping:
|
||||
|
||||
| Status DB | Paparan |
|
||||
|---|---|
|
||||
| `submitted` | Menunggu Semakan |
|
||||
| `under_ppm_review` | Sedang Disemak |
|
||||
| `approved` | Diluluskan |
|
||||
| `assigned` | Diluluskan & Ditempatkan |
|
||||
| `rejected` | Ditolak (+ sebab penolakan) |
|
||||
| `cancelled` | Dibatalkan |
|
||||
|
||||
## Admin Election Settings
|
||||
|
||||
Admin can manage the following settings per election via `/admin/setup/settings/{election}`:
|
||||
|
||||
- `is_attendance_active` — Toggle attendance recording on/off. Should only be activated on polling day. PPM cannot record attendance when this is false. Toggling is recorded in activity log with before/after values.
|
||||
- `is_registration_open_override` — Three states: null (Auto — follows date window), true (Force Open — opens even outside date window), false (Force Closed — closes even inside date window). Use override only in exceptional circumstances. When override is active, a warning badge is shown on the settings page.
|
||||
|
||||
Changes to election settings require Admin role and are written to `activity_log` with `log_name = 'admin_settings'`, recording who changed what and when.
|
||||
|
||||
Note: `is_registration_open` (the base toggle) is still set in the database during initial setup. The `is_registration_open_override` is the runtime control available via the Admin Settings UI.
|
||||
|
||||
## KTM Vacancy Rule for Public KP/KPDP Selection
|
||||
|
||||
For public KP or KPDP applications:
|
||||
|
||||
- Candidate KTM options come from active `staff_assignments` with position `KTM`, same election, same Pusat Mengundi, and assigned Saluran Mengundi.
|
||||
- Remaining vacancy is calculated from `position_quotas` for the KTM's Saluran Mengundi and requested role.
|
||||
- Used vacancy counts active team assignments under that KTM plus reserved public applications in `submitted`, `under_ppm_review`, or `approved` status.
|
||||
- If no vacancy remains, the KTM cannot be selected.
|
||||
|
||||
## PPM Review Flow
|
||||
|
||||
Phase 5 implementation behavior:
|
||||
|
||||
- PPM application URLs use application public UUID.
|
||||
- PPM list and detail views are scoped by active PPM `staff_assignments`.
|
||||
- `ApplicationPolicy` blocks PPM access to applications outside assigned Pusat Mengundi.
|
||||
- Approved applications are moved directly to `assigned` because Phase 5 approval also creates the active staff assignment.
|
||||
- Private document download is available only through the authorized PPM review route.
|
||||
|
||||
1. PPM opens own dashboard.
|
||||
2. System lists applications only for PPM's assigned Pusat Mengundi.
|
||||
3. PPM reviews applicant detail and documents.
|
||||
4. PPM may change applicant role before approval.
|
||||
5. System records any role change in audit trail.
|
||||
6. PPM approves only if IC document and bank statement exist.
|
||||
7. If approved as KTM, PPM assigns KTM to a Saluran Mengundi.
|
||||
8. If approved as KP or KPDP, PPM assigns applicant to an approved KTM under the same Pusat Mengundi.
|
||||
9. If approved as PAPM, system assigns at Pusat Mengundi level.
|
||||
10. System creates or updates `staff_assignments`.
|
||||
11. System records assignment history and activity log.
|
||||
|
||||
## KTM Flow
|
||||
|
||||
Phase 6 implementation behavior:
|
||||
|
||||
- KTM routes are under `/ktm/applications`.
|
||||
- KTM scope is based on active `staff_assignments` with position `KTM`, status `active`, and a Saluran Mengundi.
|
||||
- KTM can create only KP applicant records.
|
||||
- KTM-created records are stored as `source = created_by_ktm` and `status = submitted`.
|
||||
- Creating a KP applicant queues a Markdown email to the applicant.
|
||||
- KTM approval creates an active KP `staff_assignments` row under the KTM assignment and changes application status to `assigned`.
|
||||
- KTM can delete only unassigned records they created through the KTM flow, changing status to `deleted_by_ktm` before soft delete.
|
||||
|
||||
1. KTM logs in and opens own dashboard.
|
||||
2. System identifies active KTM assignment and Saluran.
|
||||
3. KTM views own KP list and remaining KP vacancy.
|
||||
4. During registration period, KTM can register KP only.
|
||||
5. KTM-created KP has `source = created_by_ktm`.
|
||||
6. System sends email notification to applicant.
|
||||
7. KTM can approve KP application only under own team while registration is open.
|
||||
8. If applicant wants to self-register with the same IC, system blocks until KTM deletes the KTM-created record.
|
||||
9. Deleting a KTM-created applicant changes status to `deleted_by_ktm`, records status history, and soft deletes the record.
|
||||
|
||||
## Admin Manual Entry and Management Flow
|
||||
|
||||
Phase 7 implementation behavior:
|
||||
|
||||
- Admin management routes are under `/admin/management`.
|
||||
- Admin can list all applications with filters for status, Pusat Mengundi, jawatan, and keyword.
|
||||
- Admin can create manual application records with `source = admin_manual`.
|
||||
- Manual Admin-created records create pending bank verification state but do not send KTM-created applicant email.
|
||||
- Admin can edit application personal, role, bank, status, and placement-related fields.
|
||||
- Admin can direct-assign an application to Pusat/Saluran/KTM team; this creates or updates `staff_assignments`, sets application status to `assigned`, and writes status/assignment history.
|
||||
- Admin can edit existing `staff_assignments`; changes are mirrored back to the related application where applicable.
|
||||
- Admin can key in and delete Police, KKM, and JKM representative records.
|
||||
- If registration is closed, Admin changes require `catatan`; the system stores it in `system_notes`.
|
||||
|
||||
1. Admin opens global application/staff list.
|
||||
2. Admin can create or edit application/staff records.
|
||||
3. Admin can directly assign staff to Pusat Mengundi and Saluran Mengundi.
|
||||
4. Admin can key in Police, KKM, and JKM records.
|
||||
5. If registration period has ended, Admin must provide `catatan` for any register/edit/assign action.
|
||||
6. System saves note in `system_notes`.
|
||||
7. System records activity log and assignment/application history.
|
||||
|
||||
## Admin Kewangan Flow
|
||||
|
||||
Phase 8 implementation behavior:
|
||||
|
||||
- Finance routes are under `/kewangan/bank`.
|
||||
- Finance list shows applications in `approved` or `assigned` status.
|
||||
- Filters support Pusat Mengundi, role, bank verification status, missing bank statement, missing bank account number, and keyword search.
|
||||
- Admin Kewangan can update only `bank_verifications.status` and `finance_note`.
|
||||
- Updating verification records sets `verified_by_user_id` and `verified_at` and writes activity log.
|
||||
- Admin Kewangan cannot access Admin assignment management routes.
|
||||
- Finance export creates an XLSX file in private storage using OpenSpout and inserts an `export_logs` row.
|
||||
|
||||
1. Admin Kewangan opens finance dashboard.
|
||||
2. System lists approved staff and bank verification status.
|
||||
3. Admin Kewangan filters by Pusat Mengundi, role, status, missing bank statement, or missing account number.
|
||||
4. Admin Kewangan marks bank verification as pending, verified, rejected, or requires correction.
|
||||
5. Admin Kewangan adds finance note.
|
||||
6. System records bank verification activity log.
|
||||
7. Admin Kewangan can export finance verification list.
|
||||
|
||||
## Wheelchair Flow
|
||||
|
||||
Phase 9 implementation behavior:
|
||||
|
||||
- Wheelchair management routes are under `/admin/management/wheelchairs`.
|
||||
- Admin can upsert one allocation record per election and Pusat Mengundi.
|
||||
- Admin can record `taken` transactions with quantity, date/time, person taking, and notes.
|
||||
- Admin can record `returned` transactions with quantity, date/time, return condition, and notes.
|
||||
- System calculates total taken, total returned, outstanding quantity, and available quantity.
|
||||
- Taken quantity is blocked when it exceeds available allocation.
|
||||
- Returned quantity is blocked when it exceeds outstanding quantity.
|
||||
- Allocation quantity is blocked when it is lower than outstanding quantity.
|
||||
- Allocation changes and transactions are recorded in activity log.
|
||||
|
||||
1. Admin enters allocated wheelchair quantity per Pusat Mengundi.
|
||||
2. Admin records taken quantity, date/time, and person taking.
|
||||
3. Admin records returned quantity, date/time, condition, and notes.
|
||||
4. System stores each movement in `wheelchair_transactions`.
|
||||
5. System records activity log.
|
||||
|
||||
## Attendance Flow
|
||||
|
||||
Phase 10 implementation behavior:
|
||||
|
||||
- PPM attendance routes are under `/ppm/attendance`.
|
||||
- PPM can only access Pusat Mengundi assigned through active PPM `staff_assignments`.
|
||||
- PPM can record attendance only when `election_settings.is_attendance_active = true`.
|
||||
- Attendance is recorded per active `staff_assignments` row and uses statuses `present`, `absent`, or `not_recorded`.
|
||||
- Present records store check-in time; absent and not-recorded records clear check-in time.
|
||||
- Attendance updates write activity log with actor, staff assignment, status, and check-in time.
|
||||
- Admin attendance routes are under `/admin/attendance`.
|
||||
- Admin first view lists Pusat Mengundi with total staff, present, absent, and not recorded.
|
||||
- Admin detail view shows staff name, role, Saluran, status, check-in time, notes, and recorded by.
|
||||
- Admin detail export generates XLSX with OpenSpout and writes `export_logs`.
|
||||
|
||||
1. Attendance module becomes active based on election setting.
|
||||
2. PPM opens attendance screen for own Pusat Mengundi.
|
||||
3. System lists all assigned staff under that Pusat Mengundi.
|
||||
4. PPM marks each staff as present or absent.
|
||||
5. If present, check-in time is stored.
|
||||
6. System stores recorded by and note.
|
||||
7. Admin dashboard shows first-level list of Pusat Mengundi.
|
||||
8. Admin clicks Pusat Mengundi to see staff-level attendance details.
|
||||
9. Admin exports summary or detail attendance reports.
|
||||
|
||||
Attendance statuses:
|
||||
|
||||
```text
|
||||
present
|
||||
absent
|
||||
not_recorded
|
||||
```
|
||||
|
||||
## Bank Verification Status
|
||||
|
||||
```text
|
||||
pending
|
||||
verified
|
||||
rejected
|
||||
requires_correction
|
||||
```
|
||||
|
||||
Status meaning:
|
||||
|
||||
- `pending`: Not yet checked by finance.
|
||||
- `verified`: Bank details accepted.
|
||||
- `rejected`: Bank details are invalid or unusable.
|
||||
- `requires_correction`: Applicant/staff record needs correction or additional document.
|
||||
|
||||
## Audit Events
|
||||
|
||||
Must be audited:
|
||||
|
||||
- Application submission.
|
||||
- Application approval/rejection.
|
||||
- Role change.
|
||||
- Assignment creation/change/cancellation.
|
||||
- Document upload/change.
|
||||
- Bank verification update.
|
||||
- Admin edit after registration closes.
|
||||
- Attendance update.
|
||||
- Wheelchair transaction.
|
||||
- Excel export.
|
||||
|
||||
Audit record should include:
|
||||
|
||||
- actor
|
||||
- action
|
||||
- subject
|
||||
- previous value
|
||||
- new value
|
||||
- timestamp
|
||||
- note/catatan if available
|
||||
229
docs/06-ui-ux-plan.md
Normal file
229
docs/06-ui-ux-plan.md
Normal file
@@ -0,0 +1,229 @@
|
||||
# UI/UX Plan
|
||||
|
||||
## Design Direction
|
||||
|
||||
The application should feel like a practical Malaysian government/PBT administration system:
|
||||
|
||||
- Clean.
|
||||
- Responsive.
|
||||
- Data-first.
|
||||
- Easy to scan.
|
||||
- Conservative use of color.
|
||||
- Clear status labels and actions.
|
||||
- Works well on PC/laptop, tablet, and mobile phone.
|
||||
|
||||
The UI should prioritize operational speed over decorative presentation.
|
||||
|
||||
## Frontend Stack
|
||||
|
||||
- Blade templates.
|
||||
- Bootstrap 5.
|
||||
- Bootstrap Icons.
|
||||
- jQuery for progressive enhancement where suitable.
|
||||
- Vite for compiled CSS and JavaScript.
|
||||
- SweetAlert2 for confirmations.
|
||||
- Select2 or Tom Select for searchable dropdowns.
|
||||
- Flatpickr for date/time fields.
|
||||
- DataTables only where it adds value; otherwise use Laravel server-side pagination with filters.
|
||||
|
||||
## Layout
|
||||
|
||||
### Authenticated Layout
|
||||
|
||||
Common elements:
|
||||
|
||||
- Top navigation bar.
|
||||
- Collapsible sidebar or offcanvas menu on mobile.
|
||||
- Role-aware navigation items.
|
||||
- Breadcrumbs.
|
||||
- Page title and primary action area.
|
||||
- Flash messages.
|
||||
- Main content area.
|
||||
|
||||
### Public Application Layout
|
||||
|
||||
Common elements:
|
||||
|
||||
- Simple header.
|
||||
- Pusat Mengundi context panel.
|
||||
- Step-friendly form sections.
|
||||
- Mobile-first form controls.
|
||||
- Clear closed-registration and duplicate-registration messages.
|
||||
|
||||
## Responsive Requirements
|
||||
|
||||
Desktop:
|
||||
|
||||
- Use tables with filters for large data.
|
||||
- Summary cards at top of dashboard.
|
||||
- Side navigation can remain visible.
|
||||
|
||||
Tablet:
|
||||
|
||||
- Use two-column card grids where appropriate.
|
||||
- Keep filters collapsible if space is limited.
|
||||
|
||||
Mobile:
|
||||
|
||||
- Use offcanvas navigation.
|
||||
- Convert dense tables into stacked card rows where practical.
|
||||
- Keep primary actions sticky or clearly visible on long forms.
|
||||
- Avoid horizontal scrolling except for exports or unavoidable wide data.
|
||||
|
||||
## Dashboard Plans
|
||||
|
||||
### Admin Dashboard
|
||||
|
||||
Cards:
|
||||
|
||||
- Total Pusat Mengundi.
|
||||
- Total Saluran.
|
||||
- Total applicants.
|
||||
- Approved count.
|
||||
- Pending review count.
|
||||
- Missing IC/bank statement count.
|
||||
- Bank verification pending count.
|
||||
- Attendance summary on polling day.
|
||||
|
||||
Primary views:
|
||||
|
||||
- Election readiness summary.
|
||||
- Registration period status.
|
||||
- Pusat Mengundi list with setup completeness.
|
||||
- Attendance summary when active.
|
||||
|
||||
### PPM Dashboard
|
||||
|
||||
Cards:
|
||||
|
||||
- Assigned Pusat Mengundi.
|
||||
- Pending applications.
|
||||
- Approved staff by role.
|
||||
- Saluran assignment status.
|
||||
- Attendance action button on polling day.
|
||||
|
||||
Primary views:
|
||||
|
||||
- Applications requiring review.
|
||||
- Saluran staffing completeness.
|
||||
- Police/KKM/JKM information.
|
||||
|
||||
### KTM Dashboard
|
||||
|
||||
Cards:
|
||||
|
||||
- Assigned Saluran.
|
||||
- KP list count.
|
||||
- Remaining KP vacancy.
|
||||
- Registration period status.
|
||||
|
||||
Primary actions:
|
||||
|
||||
- Register KP if registration period is open.
|
||||
- Review KP under own team.
|
||||
|
||||
### Admin Kewangan Dashboard
|
||||
|
||||
Cards:
|
||||
|
||||
- Bank verification pending.
|
||||
- Missing bank statement.
|
||||
- Rejected bank records.
|
||||
- Requires correction.
|
||||
|
||||
Primary views:
|
||||
|
||||
- Finance verification table.
|
||||
- Filters by Pusat Mengundi, role, finance status, missing document, and missing account number.
|
||||
- Export finance list.
|
||||
|
||||
## UI Components
|
||||
|
||||
Use reusable Blade components or partials for:
|
||||
|
||||
- Status badges.
|
||||
- Role badges.
|
||||
- Breadcrumbs.
|
||||
- Summary cards.
|
||||
- Filter forms.
|
||||
- Empty states.
|
||||
- Confirmation modals.
|
||||
- File/document rows.
|
||||
- Sensitive data display with mask/reveal authorization.
|
||||
- Pagination wrapper.
|
||||
|
||||
## Status Display
|
||||
|
||||
Application statuses should use badges:
|
||||
|
||||
- `submitted`: secondary/info.
|
||||
- `under_ppm_review`: warning.
|
||||
- `approved`: success.
|
||||
- `rejected`: danger.
|
||||
- `assigned`: primary/success.
|
||||
- `cancelled`: dark/secondary.
|
||||
- `deleted_by_ktm`: secondary.
|
||||
|
||||
Finance statuses:
|
||||
|
||||
- `pending`: warning.
|
||||
- `verified`: success.
|
||||
- `rejected`: danger.
|
||||
- `requires_correction`: info.
|
||||
|
||||
Attendance statuses:
|
||||
|
||||
- `present`: success.
|
||||
- `absent`: danger.
|
||||
- `not_recorded`: secondary.
|
||||
|
||||
## Forms
|
||||
|
||||
Guidelines:
|
||||
|
||||
- Use server-side validation with clear inline errors.
|
||||
- Use required markers for mandatory fields.
|
||||
- Group long forms into sections.
|
||||
- Keep upload guidance short and precise.
|
||||
- Show current selected Pusat Mengundi on public QR form.
|
||||
- For KP/KPDP role, dynamically show KTM dropdown only when required.
|
||||
- Do not ask KTM applicants to select Saluran.
|
||||
|
||||
## Tables and Lists
|
||||
|
||||
Guidelines:
|
||||
|
||||
- Use server-side pagination for large lists.
|
||||
- Add filters before data tables.
|
||||
- Use badges for status and role.
|
||||
- Show masked IC/account by default.
|
||||
- Keep actions in dropdown or grouped buttons.
|
||||
- Use mobile stacked cards for high-use mobile screens.
|
||||
|
||||
## Confirmation and Destructive Actions
|
||||
|
||||
- Use SweetAlert2 or Bootstrap modal for delete/cancel/reject confirmation.
|
||||
- Reject and post-closing changes must collect a reason/note.
|
||||
- Destructive actions should be rare and auditable.
|
||||
|
||||
## Accessibility
|
||||
|
||||
- Use semantic headings.
|
||||
- Use labels for all inputs.
|
||||
- Maintain adequate contrast.
|
||||
- Ensure keyboard focus is visible.
|
||||
- Avoid relying on color alone for status.
|
||||
- Provide meaningful button text or accessible labels.
|
||||
|
||||
## Empty States
|
||||
|
||||
Examples:
|
||||
|
||||
- No applications pending review.
|
||||
- No KP vacancy available.
|
||||
- Registration is closed.
|
||||
- No bank records require correction.
|
||||
- No attendance records yet.
|
||||
|
||||
Empty states should state the situation and provide the next valid action where applicable.
|
||||
|
||||
183
docs/07-testing-plan.md
Normal file
183
docs/07-testing-plan.md
Normal file
@@ -0,0 +1,183 @@
|
||||
# Testing Plan
|
||||
|
||||
## Testing Stack
|
||||
|
||||
Initial plan:
|
||||
|
||||
- Laravel built-in PHPUnit support as the baseline.
|
||||
- Pest may be installed if compatible and preferred after Laravel setup.
|
||||
- Factories and seeders for realistic election, hierarchy, user, application, and assignment data.
|
||||
- Laravel feature tests for workflows and authorization.
|
||||
- Unit tests for business-rule services.
|
||||
|
||||
## Test Data Strategy
|
||||
|
||||
Factories should cover:
|
||||
|
||||
- Election and settings.
|
||||
- Bahagian Pilihanraya.
|
||||
- Daerah Mengundi.
|
||||
- Pusat Mengundi.
|
||||
- Saluran Mengundi.
|
||||
- Users with roles.
|
||||
- Positions.
|
||||
- Position quotas.
|
||||
- Applications with document states.
|
||||
- Staff assignments.
|
||||
- Bank verifications.
|
||||
- Attendances.
|
||||
- Wheelchair allocations and transactions.
|
||||
|
||||
Seeders should create:
|
||||
|
||||
- Default roles and permissions.
|
||||
- Default positions.
|
||||
- Sample election.
|
||||
- Sample hierarchy.
|
||||
- Sample Admin user for local development.
|
||||
|
||||
## Feature Tests
|
||||
|
||||
Required feature tests:
|
||||
|
||||
- Applicant can register during registration period. Implemented in Phase 4.
|
||||
- Applicant cannot register after registration period. Implemented in Phase 4.
|
||||
- Applicant cannot register without IC and bank statement. Implemented in Phase 4.
|
||||
- KP/KPDP can choose KTM only if vacancy exists. KP coverage implemented in Phase 4.
|
||||
- KTM cannot register non-KP role. Implemented in Phase 6 by forcing KTM-created applications to KP only.
|
||||
- KTM cannot register after registration period. Implemented in Phase 6.
|
||||
- PPM can only view own Pusat Mengundi applications. Implemented in Phase 5.
|
||||
- PPM cannot approve without required documents. Implemented in Phase 5.
|
||||
- Admin can edit after closing only with catatan. Implemented in Phase 7.
|
||||
- Admin Kewangan can verify bank account but cannot change assignment. Implemented in Phase 8.
|
||||
- PPM can record attendance for own Pusat Mengundi only. Implemented in Phase 10.
|
||||
- Excel export creates export log. Implemented in Phase 8 for finance verification export and Phase 10 for attendance detail export.
|
||||
|
||||
Additional recommended feature tests:
|
||||
|
||||
- Public QR link uses Pusat Mengundi UUID and does not expose internal ID.
|
||||
- Duplicate IC is blocked within current election. Implemented in Phase 4.
|
||||
- Self-registration is blocked when active KTM-created applicant exists for same IC. Implemented in Phase 4.
|
||||
- KTM can delete KTM-created applicant to allow self-registration. Implemented in Phase 6.
|
||||
- Admin Kewangan cannot access assignment update routes.
|
||||
- Admin Kewangan cannot access Admin management routes. Implemented in Phase 7 for assignment listing.
|
||||
- PPM role change before approval is audited. Core assignment/history path implemented in Phase 5.
|
||||
- Admin post-closing assignment update creates system note and audit log. Implemented in Phase 7.
|
||||
- Sensitive document download requires authorization. PPM-owned document download implemented in Phase 5.
|
||||
- Sensitive document download requires authorization. Admin/Admin Kewangan document access implemented in Phase 11.
|
||||
- Core domain seeders create sample election hierarchy and dual-role assignment.
|
||||
- Admin can create manual application records. Implemented in Phase 7.
|
||||
- Admin can create Police/KKM/JKM representative records. JKM create covered in Phase 7 tests.
|
||||
- Finance filters for missing bank statement and missing account number. Implemented in Phase 8.
|
||||
- Finance export downloads XLSX and creates `export_logs`. Implemented in Phase 8.
|
||||
- Admin can create/update wheelchair allocation. Implemented in Phase 9.
|
||||
- Wheelchair taken quantity cannot exceed available allocation. Implemented in Phase 9.
|
||||
- Wheelchair return quantity cannot exceed outstanding quantity. Implemented in Phase 9.
|
||||
- Wheelchair allocation cannot be reduced below outstanding quantity. Implemented in Phase 9.
|
||||
- Admin Kewangan cannot access wheelchair management. Implemented in Phase 9.
|
||||
- Attendance recording requires active attendance module flag. Implemented in Phase 10.
|
||||
- Admin can view attendance dashboard and detail. Implemented in Phase 10.
|
||||
- Admin Kewangan cannot access Admin attendance dashboard. Implemented in Phase 10.
|
||||
|
||||
## Unit Tests
|
||||
|
||||
Required unit tests:
|
||||
|
||||
- Vacancy calculation. Implemented in Phase 4 for KTM vacancy service.
|
||||
- Registration period checker. Implemented in Phase 4.
|
||||
- Role assignment rules.
|
||||
- Sensitive data masking. Implemented in Phase 11.
|
||||
- Attendance summary calculation. Implemented in Phase 10.
|
||||
|
||||
Additional recommended unit tests:
|
||||
|
||||
- Public registration blocked/open state with override.
|
||||
- KP/KPDP KTM selection eligibility.
|
||||
- PPM plus KTM dual-role validation.
|
||||
- Bank verification state transition.
|
||||
- Export filter serialization.
|
||||
|
||||
## Policy and Authorization Tests
|
||||
|
||||
Policies should be tested for:
|
||||
|
||||
- Admin access to all records.
|
||||
- PPM access only to own Pusat Mengundi.
|
||||
- KTM access only to own team.
|
||||
- Admin Kewangan bank-only access.
|
||||
- Public applicant access only to QR routes and own flow.
|
||||
|
||||
## Upload Tests
|
||||
|
||||
Tests should verify:
|
||||
|
||||
- Required documents are enforced for public submission and PPM approval.
|
||||
- Invalid MIME type is rejected.
|
||||
- Oversized file is rejected.
|
||||
- Uploaded IC and bank statement are stored on private disk.
|
||||
- Unauthorized users cannot download sensitive documents.
|
||||
- Admin can download private uploaded documents. Implemented in Phase 11.
|
||||
- Admin Kewangan can download bank statement only. Implemented in Phase 11.
|
||||
|
||||
## Export Tests
|
||||
|
||||
Tests should verify:
|
||||
|
||||
- Export classes return expected headings.
|
||||
- Export services apply filters.
|
||||
- Export logs are created with generated_by, generated_at, report_type, filters, and file name.
|
||||
- Finance export log creation implemented in Phase 8.
|
||||
- Admin Kewangan finance export does not include unauthorized placement edit capability.
|
||||
|
||||
## Audit Tests
|
||||
|
||||
Tests should verify audit entries for:
|
||||
|
||||
- Approval/rejection.
|
||||
- Role changes.
|
||||
- Assignment changes.
|
||||
- Document changes.
|
||||
- Bank verification.
|
||||
- Post-closing Admin edit.
|
||||
- Attendance update.
|
||||
- Attendance update implemented in Phase 10 through activity log.
|
||||
- Wheelchair transaction.
|
||||
- Wheelchair transaction audit path implemented in Phase 9 through activity log.
|
||||
- Excel export.
|
||||
- Document downloads.
|
||||
|
||||
## Manual Testing Checklist
|
||||
|
||||
Desktop:
|
||||
|
||||
- Login and dashboard navigation.
|
||||
- Admin CRUD forms.
|
||||
- Application review.
|
||||
- Exports.
|
||||
|
||||
Tablet:
|
||||
|
||||
- Navigation.
|
||||
- Filters.
|
||||
- Review forms.
|
||||
- Attendance list.
|
||||
|
||||
Mobile:
|
||||
|
||||
- Public QR application form.
|
||||
- KTM dashboard and KP registration.
|
||||
- PPM attendance screen.
|
||||
- Stacked list/card views.
|
||||
|
||||
## Continuous Quality Checks
|
||||
|
||||
Planned commands after implementation exists:
|
||||
|
||||
```bash
|
||||
php artisan test
|
||||
vendor/bin/pint
|
||||
vendor/bin/phpstan analyse
|
||||
npm.cmd run build
|
||||
```
|
||||
|
||||
Actual commands may change based on installed packages and Windows shell behavior.
|
||||
247
docs/08-deployment-notes.md
Normal file
247
docs/08-deployment-notes.md
Normal file
@@ -0,0 +1,247 @@
|
||||
# Deployment Notes
|
||||
|
||||
## Target Environment
|
||||
|
||||
The application is intended for a standard Laravel production deployment using:
|
||||
|
||||
- PHP compatible with selected Laravel version.
|
||||
- Composer.
|
||||
- MySQL.
|
||||
- Web server such as Nginx or Apache.
|
||||
- Node/npm only for building frontend assets, not necessarily required on production runtime if assets are built during deployment.
|
||||
- Queue worker for mail and background jobs.
|
||||
- Scheduler for Laravel scheduled tasks where needed.
|
||||
|
||||
## Local Environment Findings
|
||||
|
||||
Inspection date: 2026-05-28
|
||||
|
||||
- PHP CLI: 8.5.1.
|
||||
- Composer: 2.9.4.
|
||||
- Node: 25.1.0.
|
||||
- npm works through `npm.cmd`; `npm` PowerShell shim is blocked by execution policy.
|
||||
- MySQL CLI is not available in PATH and must be confirmed before database setup.
|
||||
- `pdo_mysql` PHP extension is available.
|
||||
- MySQL 8.4.8 connection was confirmed through Laravel/PDO during Phase 2.
|
||||
- Local `.env` is configured for MySQL database `prn2026`.
|
||||
|
||||
## Environment Configuration
|
||||
|
||||
Required `.env` values:
|
||||
|
||||
```text
|
||||
APP_NAME="Sistem Pengurusan Pusat Mengundi"
|
||||
APP_ENV=production
|
||||
APP_KEY=
|
||||
APP_DEBUG=false
|
||||
APP_URL=
|
||||
APP_TIMEZONE=Asia/Kuala_Lumpur
|
||||
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST=
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=
|
||||
DB_USERNAME=
|
||||
DB_PASSWORD=
|
||||
|
||||
QUEUE_CONNECTION=database
|
||||
MAIL_MAILER=
|
||||
MAIL_HOST=
|
||||
MAIL_PORT=
|
||||
MAIL_USERNAME=
|
||||
MAIL_PASSWORD=
|
||||
MAIL_ENCRYPTION=
|
||||
MAIL_FROM_ADDRESS=
|
||||
MAIL_FROM_NAME=
|
||||
FILESYSTEM_DISK=local
|
||||
|
||||
SEED_ADMIN_NAME="Admin Sistem"
|
||||
SEED_ADMIN_EMAIL=admin@prn.local
|
||||
SEED_ADMIN_PASSWORD=password
|
||||
```
|
||||
|
||||
Production must override the seeded Admin password before deployment or disable default seeding after the first secure Admin account is created.
|
||||
|
||||
## Storage
|
||||
|
||||
Sensitive documents:
|
||||
|
||||
- IC document.
|
||||
- Bank statement.
|
||||
|
||||
Rules:
|
||||
|
||||
- Store sensitive documents on private disk.
|
||||
- Do not expose sensitive uploads through public storage symlink.
|
||||
- Serve document downloads through authorized controller actions only.
|
||||
- Use secure file validation for MIME type, extension, and size.
|
||||
|
||||
## Deployment Steps
|
||||
|
||||
Initial production deployment should follow this sequence:
|
||||
|
||||
1. Pull or upload application source.
|
||||
2. Install Composer dependencies without dev packages.
|
||||
3. Install/build frontend assets.
|
||||
4. Configure `.env`.
|
||||
5. Generate app key if not already configured.
|
||||
6. Run migrations.
|
||||
7. Run seeders for roles, permissions, positions, and initial Admin user where appropriate.
|
||||
8. Cache configuration, routes, and views.
|
||||
9. Start queue worker.
|
||||
10. Configure scheduler.
|
||||
11. Validate login and critical workflows.
|
||||
|
||||
Planned commands:
|
||||
|
||||
```bash
|
||||
composer install --no-dev --optimize-autoloader
|
||||
npm.cmd ci
|
||||
npm.cmd run build
|
||||
php artisan key:generate
|
||||
php artisan migrate --force
|
||||
php artisan db:seed --force
|
||||
php artisan config:cache
|
||||
php artisan route:cache
|
||||
php artisan view:cache
|
||||
php artisan queue:restart
|
||||
```
|
||||
|
||||
Commands may need adjustment for the actual hosting environment.
|
||||
|
||||
## Queue Worker
|
||||
|
||||
Queue should handle:
|
||||
|
||||
- KTM-created applicant email notifications.
|
||||
- Potentially larger exports.
|
||||
- Future notification jobs.
|
||||
|
||||
Recommended:
|
||||
|
||||
- Use database queue initially for simplicity.
|
||||
- Use Supervisor, Windows service wrapper, or hosting panel worker support to keep queue running.
|
||||
|
||||
## Scheduler
|
||||
|
||||
Active scheduled tasks:
|
||||
|
||||
| Command | Schedule | Purpose |
|
||||
|---|---|---|
|
||||
| `exports:purge --days=30` | Daily at 00:00 | Purge export files older than 30 days |
|
||||
|
||||
To change the retention period, edit `routes/console.php` and update `--days=30`.
|
||||
|
||||
Scheduler entry (add to OS cron):
|
||||
|
||||
```bash
|
||||
* * * * * php /path/to/artisan schedule:run >> /dev/null 2>&1
|
||||
```
|
||||
|
||||
To run purge manually:
|
||||
|
||||
```bash
|
||||
php artisan exports:purge --days=30
|
||||
php artisan exports:purge --days=7 # shorter retention for testing
|
||||
```
|
||||
|
||||
## Security Checklist
|
||||
|
||||
- `APP_DEBUG=false` in production.
|
||||
- Strong database credentials.
|
||||
- HTTPS enabled.
|
||||
- Private document storage.
|
||||
- Correct file permissions for storage and cache.
|
||||
- Document downloads must use authorized controller routes; do not expose `storage/app/private`.
|
||||
- Admin Kewangan document access is limited to bank statements.
|
||||
- Queue worker runs under least-privilege user.
|
||||
- Audit log retention policy defined.
|
||||
- Regular backups.
|
||||
- Admin accounts reviewed before launch.
|
||||
- Default development accounts removed or password-changed.
|
||||
|
||||
## Index Verification After Migration
|
||||
|
||||
After running migrations on a fresh database, verify the performance indexes exist:
|
||||
|
||||
```sql
|
||||
SHOW INDEX FROM applications WHERE Key_name IN (
|
||||
'app_election_status_idx',
|
||||
'app_ktm_position_idx'
|
||||
);
|
||||
|
||||
SHOW INDEX FROM staff_assignments WHERE Key_name IN (
|
||||
'sa_election_pusat_position_idx',
|
||||
'sa_election_saluran_position_idx',
|
||||
'sa_reports_to_pos_status_idx'
|
||||
);
|
||||
```
|
||||
|
||||
All of the above should return rows. If any are missing, re-run `php artisan migrate`.
|
||||
|
||||
Use `EXPLAIN` on critical service queries to confirm index usage:
|
||||
|
||||
```sql
|
||||
-- KtmVacancyService: reserved application count
|
||||
EXPLAIN SELECT * FROM applications
|
||||
WHERE selected_ktm_assignment_id = ? AND requested_position_id = ?
|
||||
AND status IN ('submitted', 'under_ppm_review', 'approved');
|
||||
|
||||
-- KtmVacancyService: active KP count under a KTM
|
||||
EXPLAIN SELECT COUNT(*) FROM staff_assignments
|
||||
WHERE reports_to_assignment_id = ? AND position_id = ? AND status = 'active';
|
||||
```
|
||||
|
||||
Both should show `key` as the respective composite index, not NULL.
|
||||
|
||||
## Final Quality Gate
|
||||
|
||||
Run before production release:
|
||||
|
||||
```bash
|
||||
php artisan route:list --except-vendor
|
||||
php artisan test
|
||||
vendor/bin/pint --test
|
||||
vendor/bin/phpstan analyse --memory-limit=1G
|
||||
npm.cmd run build
|
||||
```
|
||||
|
||||
The Windows local environment uses `.bat` wrappers for vendor binaries:
|
||||
|
||||
```bash
|
||||
vendor\bin\pint.bat --test
|
||||
vendor\bin\phpstan.bat analyse --memory-limit=1G
|
||||
```
|
||||
|
||||
## Backup Checklist
|
||||
|
||||
Back up:
|
||||
|
||||
- MySQL database.
|
||||
- Private storage documents.
|
||||
- Export files if retained.
|
||||
- `.env` securely through infrastructure secret management, not public repository.
|
||||
|
||||
Recommended frequency:
|
||||
|
||||
- Daily database backups during active registration and polling period.
|
||||
- More frequent backup before and on polling day.
|
||||
- Storage backup aligned with database backups.
|
||||
|
||||
## Performance Notes
|
||||
|
||||
- Use server-side pagination for large lists.
|
||||
- Add indexes for election, Pusat Mengundi, status, role, and IC lookup.
|
||||
- Avoid loading documents or large relationship graphs on index pages.
|
||||
- Use queued exports if datasets become large.
|
||||
- Cache role/permission checks through Spatie package defaults.
|
||||
- Keep attendance, finance, Admin management, and setup index pages paginated.
|
||||
- For very large datasets, move XLSX exports to queued jobs and notify users when complete.
|
||||
|
||||
## Release Notes Process
|
||||
|
||||
Every deployment should update:
|
||||
|
||||
- `docs/changelog.md` for user-facing changes.
|
||||
- `docs/progress-log.md` for implementation phase completion.
|
||||
- `docs/decision-log.md` for architecture, database, security, and package decisions.
|
||||
323
docs/09-improvement-plan.md
Normal file
323
docs/09-improvement-plan.md
Normal file
@@ -0,0 +1,323 @@
|
||||
# Improvement Plan
|
||||
|
||||
Dokumen ini mencatat pelan pembetulan dan penambahbaikan sistem PRN2026 selepas semakan kod menyeluruh selepas Phase 11 selesai. Setiap fasa mengikut susunan keutamaan: kritikal → prestasi → pengalaman pengguna → pengurusan data.
|
||||
|
||||
Setiap pembetulan dan perubahan wajib kemaskini:
|
||||
|
||||
- `docs/progress-log.md`
|
||||
- `docs/changelog.md`
|
||||
- `docs/decision-log.md` jika ada keputusan seni bina, pakej, pangkalan data atau keselamatan yang berubah
|
||||
|
||||
---
|
||||
|
||||
## Fasa 12: Pembetulan Kritikal dan Keselamatan
|
||||
|
||||
Status: Belum dimulakan.
|
||||
|
||||
Skop fasa ini ialah menangani kelemahan yang boleh menyebabkan data tidak konsisten, kawalan akses bocor, atau keperluan edit database secara manual dalam persekitaran produksi.
|
||||
|
||||
### 12-A: Admin Settings UI — Toggle Kehadiran dan Override Pendaftaran
|
||||
|
||||
Masalah semasa:
|
||||
|
||||
- `election_settings.is_attendance_active` hanya boleh diubah melalui edit database secara terus.
|
||||
- `election_settings.is_registration_open_override` juga tiada UI khusus; bergantung pada pengaturan tarikh semata-mata.
|
||||
- Ini tidak selamat dan tidak praktikal dalam persekitaran produksi semasa hari mengundi.
|
||||
|
||||
Tugas:
|
||||
|
||||
- Tambah halaman Admin Settings di bawah `/admin/settings` atau `/admin/setup/settings`.
|
||||
- Paparkan toggle untuk `is_attendance_active` dengan label jelas dan amaran impak.
|
||||
- Paparkan toggle atau dropdown untuk `is_registration_open_override` (null / buka paksa / tutup paksa).
|
||||
- Paparkan tarikh buka dan tarikh tutup pendaftaran semasa sebagai konteks.
|
||||
- Tambah `AdminSettingsController` dan `UpdateElectionSettingsRequest`.
|
||||
- Tambah `ElectionSettingsService` untuk enkapsulasi logik kemaskini.
|
||||
- Rekod perubahan dalam activity log: siapa, nilai lama, nilai baru, masa.
|
||||
- Tambah ujian ciri untuk kemaskini setiap toggle, halaman diakses Admin sahaja.
|
||||
- Tambah pautan ke Admin Settings dari Admin dashboard.
|
||||
|
||||
Kriteria keluar:
|
||||
|
||||
- Admin boleh toggle kehadiran dan override pendaftaran dari UI tanpa sentuh database.
|
||||
- Perubahan direkod dalam activity log.
|
||||
- Admin Kewangan, PPM, KTM tidak boleh akses halaman settings.
|
||||
|
||||
Dokumentasi yang perlu dikemaskini:
|
||||
|
||||
- `docs/01-development-plan.md` — tambah Fasa 12 dengan semua tugas dan kriteria keluar.
|
||||
- `docs/04-rbac-permission-matrix.md` — tambah permission `manage election settings` untuk Admin.
|
||||
- `docs/05-workflow-design.md` — tambah bahagian pengurusan tetapan pilihan raya.
|
||||
- `docs/progress-log.md` — rekod selepas siap.
|
||||
- `docs/changelog.md` — rekod selepas siap.
|
||||
- `docs/decision-log.md` — rekod keputusan reka bentuk halaman settings.
|
||||
|
||||
---
|
||||
|
||||
### 12-B: Pembetulan Semakan IC Duplikasi untuk Rekod Soft-Deleted
|
||||
|
||||
Masalah semasa:
|
||||
|
||||
- Semakan IC duplikasi dalam `StorePublicApplicationRequest` menapis rekod aktif sahaja, tetapi soft-deleted KTM-created records masih wujud dalam pangkalan data.
|
||||
- Jika KTM delete rekod pemohon (`deleted_by_ktm`), rekod tersebut soft-deleted tetapi IC dalam `applications` masih boleh menyebabkan konflik bergantung kepada cara query dikodkan.
|
||||
- Risiko: pemohon awam mungkin diblock padahal KTM sudah delete rekod mereka.
|
||||
|
||||
Tugas:
|
||||
|
||||
- Semak semula query semakan IC duplikasi dalam `StorePublicApplicationRequest` dan mana-mana service yang menjalankan semakan serupa.
|
||||
- Pastikan rekod `deleted_at IS NOT NULL` (soft-deleted) tidak diambil kira sebagai "aktif" dalam semakan duplikasi.
|
||||
- Pastikan `withTrashed()` atau scoped query digunakan secara eksplisit dan konsisten.
|
||||
- Tambah kes ujian: KTM delete rekod → pemohon awam dengan IC sama boleh mohon semula.
|
||||
- Semak `KtmVacancyService` dan `AssignmentVacancyService` juga untuk memastikan tiada pengiraan yang termasuk soft-deleted rows secara tidak sengaja.
|
||||
|
||||
Kriteria keluar:
|
||||
|
||||
- Ujian baru lulus: soft-deleted KTM record tidak menghalang permohonan awam baharu.
|
||||
- Semua semakan IC dalam system menggunakan kaedah yang sama dan konsisten.
|
||||
|
||||
Dokumentasi yang perlu dikemaskini:
|
||||
|
||||
- `docs/05-workflow-design.md` — perjelas bahawa soft-deleted records dikecualikan dari semakan IC aktif.
|
||||
- `docs/progress-log.md` dan `docs/changelog.md` — rekod selepas siap.
|
||||
|
||||
---
|
||||
|
||||
### 12-C: Perkukuh Enforcement Nota Catatan Post-Pendaftaran
|
||||
|
||||
Masalah semasa:
|
||||
|
||||
- `AdminPostCloseNoteService` menguatkuasakan keperluan `catatan` pada peringkat service layer.
|
||||
- Jika ada controller baru yang lupa panggil service ini, semakan tidak berlaku dan Admin boleh edit rekod tanpa nota selepas pendaftaran ditutup tanpa sebarang amaran.
|
||||
- Enforcement bergantung sepenuhnya pada konvensyen pemanggilan, bukan pada mekanisme framework.
|
||||
|
||||
Tugas:
|
||||
|
||||
- Semak semua controller Admin yang boleh mengubah `Application`, `StaffAssignment`, `PoliceEscort`, `KkmRepresentative`, atau `JkmRepresentative` selepas pendaftaran ditutup.
|
||||
- Tambah senarai semak di `AdminPostCloseNoteService` yang boleh dipanggil secara konsisten.
|
||||
- Pertimbangkan untuk tambah `middleware` khusus pada route group Admin management yang secara automatik menjalankan semakan ini, dan reject request tanpa `catatan` apabila pendaftaran ditutup.
|
||||
- Jika middleware digunakan, dokumentasikan dalam `docs/decision-log.md`.
|
||||
- Tambah ujian integrasi untuk setiap route Admin management yang sepatutnya enforce semakan ini.
|
||||
|
||||
Kriteria keluar:
|
||||
|
||||
- Semua route management Admin yang mengubah rekod domain enforce catatan secara konsisten apabila pendaftaran ditutup.
|
||||
- Ujian baru mengesahkan enforcement pada setiap route yang berkenaan.
|
||||
- Tiada jalan pintas yang boleh bypass semakan ini tanpa sengaja.
|
||||
|
||||
Dokumentasi yang perlu dikemaskini:
|
||||
|
||||
- `docs/05-workflow-design.md` — dokumentasikan mekanisme enforcement yang dipilih.
|
||||
- `docs/decision-log.md` — rekod keputusan sama ada guna middleware atau service pattern.
|
||||
- `docs/progress-log.md` dan `docs/changelog.md` — rekod selepas siap.
|
||||
|
||||
---
|
||||
|
||||
### 12-D: Pembetulan SampleElectionSeeder dan Konsistensi Nama Jawatan
|
||||
|
||||
Masalah semasa:
|
||||
|
||||
- Selepas migrasi Phase 11 menukar `CALON_SIMPANAN` kepada `CALON_TAMBAHAN`, ada kemungkinan `SampleElectionSeeder.php` masih mengandungi rujukan kepada nama atau slug lama.
|
||||
- Jika seeder dijalankan semula (fresh seed untuk UAT atau persekitaran baharu), data yang dijana mungkin tidak konsisten dengan `PositionSeeder`.
|
||||
|
||||
Tugas:
|
||||
|
||||
- Buka dan audit `SampleElectionSeeder.php` secara menyeluruh.
|
||||
- Cari semua rujukan kepada `CALON_SIMPANAN` atau string berkaitan dan gantikan dengan `CALON_TAMBAHAN`.
|
||||
- Pastikan semua slug jawatan dalam seeder sepadan tepat dengan apa yang `PositionSeeder` jana.
|
||||
- Jalankan `php artisan migrate:fresh --seed` dan pastikan tiada ralat.
|
||||
- Jalankan `php artisan test` dan pastikan semua 80 ujian lulus.
|
||||
|
||||
Kriteria keluar:
|
||||
|
||||
- `php artisan migrate:fresh --seed` selesai tanpa ralat.
|
||||
- `php artisan test` lulus sepenuhnya.
|
||||
- Tiada rujukan kepada `CALON_SIMPANAN` dalam mana-mana seeder.
|
||||
|
||||
Dokumentasi yang perlu dikemaskini:
|
||||
|
||||
- `docs/progress-log.md` dan `docs/changelog.md` — rekod selepas siap.
|
||||
|
||||
---
|
||||
|
||||
### 12-E: Konsistensikan Semakan Jenis Dokumen pada Semua Path Download
|
||||
|
||||
Masalah semasa:
|
||||
|
||||
- Admin Kewangan route download secara eksplisit mengehad kepada `bank_statement` sahaja.
|
||||
- Route download PPM, Admin, dan Admin Kewangan masing-masing menggunakan implementasi yang berbeza.
|
||||
- Jika ada ketidakkonsistenan dalam cara jenis dokumen disahkan, pengguna yang dibenarkan mungkin boleh download jenis dokumen yang sepatutnya terhad untuk mereka.
|
||||
|
||||
Tugas:
|
||||
|
||||
- Audit semua route download dokumen: PPM, Admin, dan Admin Kewangan.
|
||||
- Cipta satu senarai jenis dokumen yang dibenarkan per peranan (`document_type` allowlist).
|
||||
- Refactor semakan jenis dokumen ke dalam satu tempat — boleh dalam `DocumentDownloadController` atau policy `ApplicationPolicy`.
|
||||
- Tambah ujian untuk setiap peranan mengesahkan mereka:
|
||||
- Boleh download dokumen yang dibenarkan.
|
||||
- Tidak boleh download dokumen di luar skop mereka walaupun ID dokumen diketahui.
|
||||
- Pastikan 403 dikembalikan, bukan 404, untuk download yang diblock (supaya mesej ralat tidak mendedahkan kewujudan dokumen).
|
||||
|
||||
Kriteria keluar:
|
||||
|
||||
- Semua path download dokumen menggunakan semakan jenis yang konsisten.
|
||||
- Ujian baru mengesahkan boundary setiap peranan.
|
||||
- Tiada path yang boleh bypass semakan jenis dokumen.
|
||||
|
||||
Dokumentasi yang perlu dikemaskini:
|
||||
|
||||
- `docs/04-rbac-permission-matrix.md` — tambah baris untuk akses dokumen per peranan.
|
||||
- `docs/decision-log.md` — rekod keputusan centralize semakan jenis dokumen.
|
||||
- `docs/progress-log.md` dan `docs/changelog.md` — rekod selepas siap.
|
||||
|
||||
---
|
||||
|
||||
## Fasa 13: Indeks Pangkalan Data dan Prestasi
|
||||
|
||||
Status: Belum dimulakan.
|
||||
|
||||
Skop fasa ini ialah menambah composite indexes yang diperlukan sebelum sistem mula menerima data sebenar dalam kuantiti besar.
|
||||
|
||||
### 13-A: Tambah Composite Indexes untuk Query Kerap
|
||||
|
||||
Masalah semasa:
|
||||
|
||||
- Query senarai Admin global (`/admin/management/applications`) menyaring mengikut `election_id`, `status`, dan `pusat_mengundi_id` tanpa composite index.
|
||||
- Query `AssignmentVacancyService` dan `KtmVacancyService` menyaring `staff_assignments` mengikut `election_id`, `pusat_mengundi_id`, `position_id`, dan `is_active` tanpa composite index.
|
||||
- Untuk pilihan raya dengan 5,000–20,000 permohonan, query ini boleh menjadi lambat secara signifikan.
|
||||
|
||||
Tugas:
|
||||
|
||||
- Cipta migration baharu untuk menambah indexes (jangan ubah migration sedia ada).
|
||||
- Tambah pada jadual `applications`:
|
||||
- `(election_id, status)`
|
||||
- `(election_id, pusat_mengundi_id, status)`
|
||||
- `(election_id, ic_number)` — untuk semakan IC duplikasi
|
||||
- Tambah pada jadual `staff_assignments`:
|
||||
- `(election_id, pusat_mengundi_id, position_id, is_active)`
|
||||
- `(election_id, saluran_mengundi_id, position_id, is_active)`
|
||||
- `(reports_to_assignment_id, is_active)` — untuk KtmVacancyService
|
||||
- Tambah pada jadual `bank_verifications`:
|
||||
- `(election_id, status)`
|
||||
- Gunakan nama index yang pendek dan eksplisit (ikut konvensyen sedia ada dalam sistem).
|
||||
- Jalankan `php artisan migrate` dan sahkan indexes wujud.
|
||||
- Jalankan `php artisan test` dan pastikan semua ujian lulus.
|
||||
|
||||
Kriteria keluar:
|
||||
|
||||
- Migration berjaya tanpa ralat.
|
||||
- Semua ujian lulus.
|
||||
- Query utama pada senarai admin menggunakan indexes (boleh disahkan dengan `EXPLAIN` dalam MySQL Workbench).
|
||||
|
||||
Dokumentasi yang perlu dikemaskini:
|
||||
|
||||
- `docs/03-database-design.md` — tambah bahagian indexes dan justifikasi.
|
||||
- `docs/decision-log.md` — rekod keputusan indexes yang ditambah dan sebab.
|
||||
- `docs/08-deployment-notes.md` — tambah arahan semak indexes semasa deployment.
|
||||
- `docs/progress-log.md` dan `docs/changelog.md` — rekod selepas siap.
|
||||
|
||||
---
|
||||
|
||||
## Fasa 14: Portal Status Permohonan Pemohon Awam
|
||||
|
||||
Status: Belum dimulakan.
|
||||
|
||||
Skop fasa ini ialah memberi pemohon awam cara untuk semak status permohonan mereka tanpa perlu login atau hubungi Admin.
|
||||
|
||||
### 14-A: Halaman Status Permohonan via UUID Awam
|
||||
|
||||
Masalah semasa:
|
||||
|
||||
- Pemohon awam hanya menerima redirect ke `/permohonan/{uuid}/berjaya` selepas mohon.
|
||||
- Tiada cara untuk semak status kemudian (diluluskan? ditolak? kenapa ditolak?).
|
||||
- Ini menyebabkan beban sokongan kepada Admin dan PPM.
|
||||
|
||||
Tugas:
|
||||
|
||||
- Tambah route `GET /permohonan/{application:public_uuid}/status`.
|
||||
- Halaman ini tidak memerlukan login — cukup UUID awam permohonan sebagai pengecam.
|
||||
- Paparkan maklumat terhad: nama pemohon (sebahagian), jawatan dipohon, pusat mengundi, status semasa, tarikh kemaskini terakhir.
|
||||
- Jika status `rejected`, paparkan sebab penolakan jika ada.
|
||||
- Jika status `assigned`, paparkan pengesahan penempatan tanpa mendedahkan butiran dalaman (saluran ID, dsb.).
|
||||
- Jangan paparkan nombor IC penuh, nombor akaun bank, atau data sensitif lain.
|
||||
- Gunakan `SensitiveData` helper untuk mask nama sebahagian jika perlu.
|
||||
- Tambah ujian untuk: permohonan berstatus submitted, approved, assigned, rejected, dan UUID tidak wujud.
|
||||
|
||||
Kriteria keluar:
|
||||
|
||||
- Pemohon boleh semak status dengan hanya URL yang mengandungi UUID awam.
|
||||
- Data sensitif tidak didedahkan.
|
||||
- Halaman tidak mendedahkan sama ada UUID wujud atau tidak kepada orang lain (status 200 dengan mesej umum untuk UUID tidak dikenali, bukan 404).
|
||||
|
||||
Dokumentasi yang perlu dikemaskini:
|
||||
|
||||
- `docs/05-workflow-design.md` — tambah bahagian portal status pemohon.
|
||||
- `docs/06-ui-ux-plan.md` — tambah reka bentuk halaman status.
|
||||
- `docs/progress-log.md` dan `docs/changelog.md` — rekod selepas siap.
|
||||
- `docs/decision-log.md` — rekod keputusan reka bentuk privacy (status 200 vs 404 untuk UUID tidak dikenali).
|
||||
|
||||
---
|
||||
|
||||
## Fasa 15: Pengurusan Eksport dan Data Sensitif
|
||||
|
||||
Status: Belum dimulakan.
|
||||
|
||||
Skop fasa ini ialah memastikan fail eksport yang mengandungi data sensitif tidak kekal dalam storage tanpa had masa.
|
||||
|
||||
### 15-A: Purge Automatik Fail Eksport Lama
|
||||
|
||||
Masalah semasa:
|
||||
|
||||
- Fail XLSX eksport kewangan (disimpan di `exports/finance`) dan eksport kehadiran (disimpan di `exports/attendance`) disimpan dalam private storage tanpa had masa.
|
||||
- Fail-fail ini mengandungi data bank, nombor IC, dan maklumat peribadi lain.
|
||||
- Tiada mekanisme untuk padam fail lama secara automatik.
|
||||
|
||||
Tugas:
|
||||
|
||||
- Tambah `ExportPurgeCommand` (`php artisan exports:purge`) sebagai Artisan command.
|
||||
- Command menerima parameter `--days=` (default: 30) untuk menentukan tempoh simpanan.
|
||||
- Command akan:
|
||||
- Query `export_logs` untuk rekod lebih lama daripada tempoh yang ditetapkan.
|
||||
- Padam fail dari storage disk yang berkaitan.
|
||||
- Kemaskini `export_logs` dengan `purged_at` dan `purged_by` (system).
|
||||
- Log aktiviti purge.
|
||||
- Daftarkan command dalam `bootstrap/app.php` atau `routes/console.php` sebagai scheduled task (contoh: harian pada tengah malam).
|
||||
- Tambah kolum `purged_at` pada jadual `export_logs` melalui migration baharu.
|
||||
- Tambah ujian untuk: command berjaya padam fail lama, fail baru tidak terpadam, `export_logs` dikemaskini.
|
||||
|
||||
Kriteria keluar:
|
||||
|
||||
- Command boleh dijalankan secara manual dan secara scheduled.
|
||||
- Fail lebih lama dari tempoh yang ditetapkan dipadamkan dari storage.
|
||||
- `export_logs` menunjukkan rekod purge.
|
||||
- Fail yang masih dalam tempoh simpanan tidak terpadam.
|
||||
|
||||
Dokumentasi yang perlu dikemaskini:
|
||||
|
||||
- `docs/03-database-design.md` — tambah kolum `purged_at` dalam jadual `export_logs`.
|
||||
- `docs/08-deployment-notes.md` — tambah arahan konfigurasi scheduler dan polisi simpanan eksport.
|
||||
- `docs/decision-log.md` — rekod keputusan tempoh simpanan default dan strategi purge.
|
||||
- `docs/progress-log.md` dan `docs/changelog.md` — rekod selepas siap.
|
||||
|
||||
---
|
||||
|
||||
## Ringkasan Keutamaan
|
||||
|
||||
| Fasa | Tajuk | Keutamaan | Status |
|
||||
|------|-------|-----------|--------|
|
||||
| 12-A | Admin Settings UI (toggle kehadiran/pendaftaran) | Kritikal | **Selesai 2026-06-02** |
|
||||
| 12-B | Pembetulan semakan IC soft-deleted | Kritikal | **Selesai 2026-06-02** |
|
||||
| 12-C | Perkukuh enforcement nota catatan | Kritikal | **Selesai 2026-06-02** |
|
||||
| 12-D | Pembetulan SampleElectionSeeder | Kritikal | **Selesai 2026-06-02** |
|
||||
| 12-E | Konsistensikan semakan jenis dokumen download | Kritikal | **Selesai 2026-06-02** |
|
||||
| 13-A | Tambah composite indexes pangkalan data | Prestasi | **Selesai 2026-06-02** |
|
||||
| 14-A | Portal status permohonan pemohon awam | UX | **Selesai 2026-06-02** |
|
||||
| 15-A | Purge automatik fail eksport sensitif | Data | **Selesai 2026-06-02** |
|
||||
|
||||
---
|
||||
|
||||
## Nota Pelaksanaan
|
||||
|
||||
- Setiap item dalam Fasa 12 boleh dilaksanakan secara bebas dan tidak perlu tunggu item lain selesai.
|
||||
- Fasa 13 patut dilaksanakan sebelum data sebenar mula dimasukkan ke dalam sistem.
|
||||
- Fasa 14 dan 15 boleh dilaksanakan secara selari kerana tidak ada kebergantungan antara satu sama lain.
|
||||
- Setiap pembetulan mesti menjalankan semula suite ujian penuh (`php artisan test`) sebelum dianggap selesai.
|
||||
- Jalankan `vendor\bin\pint.bat --test` dan `vendor\bin\phpstan.bat analyse --memory-limit=1G` selepas setiap fasa.
|
||||
BIN
docs/N49-Kota Iskandar - senarai pusat mengundi.xlsx
Normal file
BIN
docs/N49-Kota Iskandar - senarai pusat mengundi.xlsx
Normal file
Binary file not shown.
395
docs/changelog.md
Normal file
395
docs/changelog.md
Normal file
@@ -0,0 +1,395 @@
|
||||
# Changelog
|
||||
|
||||
All notable user-facing changes to this project will be documented in this file.
|
||||
|
||||
## 2026-05-28 - Phase 0 Planning Records
|
||||
|
||||
Added:
|
||||
|
||||
- Initial project planning documentation.
|
||||
- Proposed architecture.
|
||||
- Proposed database design.
|
||||
- RBAC matrix.
|
||||
- Workflow design.
|
||||
- UI/UX plan.
|
||||
- Testing plan.
|
||||
- Deployment notes.
|
||||
- Decision and progress logs.
|
||||
|
||||
Notes:
|
||||
|
||||
- No application features have been implemented yet.
|
||||
- No user-facing web interface exists yet.
|
||||
|
||||
## 2026-05-28 - Phase 0 Documentation Review
|
||||
|
||||
Changed:
|
||||
|
||||
- Clarified registration-open setting in database and workflow planning.
|
||||
- Clarified Phase 1 package baseline.
|
||||
- Recorded local-only Telescope and later-only DOMPDF decisions.
|
||||
|
||||
## 2026-05-28 - Phase 1 Laravel Base Application
|
||||
|
||||
Added:
|
||||
|
||||
- Laravel 13 application base.
|
||||
- Login, logout, password reset, email verification, and profile routes from Breeze.
|
||||
- Bootstrap 5 authenticated and guest layouts.
|
||||
- Role-based dashboards for Admin, Admin Kewangan, PPM, KTM, and Pemohon.
|
||||
- RBAC roles and permissions seeder.
|
||||
- Default local Admin seed account.
|
||||
- Spatie permission and activity log foundations.
|
||||
- QR code package foundation.
|
||||
- Spreadsheet export foundation using OpenSpout.
|
||||
|
||||
Changed:
|
||||
|
||||
- Disabled generic public account registration.
|
||||
- Replaced generated Tailwind views with Bootstrap views.
|
||||
|
||||
Notes:
|
||||
|
||||
- `maatwebsite/excel` was not installed because Composer rejected it for the current PHP 8.5.1/Laravel 13 environment.
|
||||
|
||||
## 2026-05-28 - Phase 2 Core Database
|
||||
|
||||
Added:
|
||||
|
||||
- Core election-domain database schema.
|
||||
- Domain models and relationships.
|
||||
- Default position seeder.
|
||||
- Sample election hierarchy seed data.
|
||||
- Position quota seed data.
|
||||
- Sample role users for Admin Kewangan, PPM, and KTM.
|
||||
- Sample dual-role PPM plus KTM assignment.
|
||||
- Sample wheelchair allocation.
|
||||
- Core domain seeder test.
|
||||
|
||||
Changed:
|
||||
|
||||
- Confirmed local MySQL usage for migrations and seeders.
|
||||
- Shortened long database index names for MySQL compatibility.
|
||||
|
||||
## 2026-05-29 - Phase 3 Admin Setup Module
|
||||
|
||||
Added:
|
||||
|
||||
- Admin setup overview page.
|
||||
- CRUD screens for Bahagian Pilihanraya, Daerah Mengundi, Pusat Mengundi, Saluran Mengundi, and Jawatan.
|
||||
- Position quota setup for pusat-level and saluran-level vacancies.
|
||||
- PPM assignment from Pusat Mengundi detail screen.
|
||||
- QR generation and refresh for each Pusat Mengundi.
|
||||
- Public QR placeholder page at `/pohon/{pusat_mengundi_uuid}`.
|
||||
- Admin dashboard link to Election setup.
|
||||
- Feature tests for Admin setup access, hierarchy creation, QR generation, quota setup, PPM assignment, and public UUID QR route.
|
||||
|
||||
Changed:
|
||||
|
||||
- QR links now resolve using public UUID values instead of internal database IDs.
|
||||
- Public QR pages use a public layout without authenticated navigation.
|
||||
|
||||
Notes:
|
||||
|
||||
- The full public applicant form remains scheduled for Phase 4.
|
||||
- KKM, JKM, and Police operational management remains scheduled for the later Admin management phase.
|
||||
|
||||
## 2026-05-29 - Phase 4 Public Application Module
|
||||
|
||||
Added:
|
||||
|
||||
- Public application form at `/pohon/{pusat_mengundi_uuid}`.
|
||||
- Application roles KTM, KP, KPDP, and PAPM.
|
||||
- Dynamic KTM selection for KP/KPDP based on remaining vacancy.
|
||||
- Required IC document and bank statement uploads.
|
||||
- Private document storage for uploaded public application files.
|
||||
- Registration period open/closed enforcement.
|
||||
- Duplicate IC blocking for current election.
|
||||
- Specific blocking message for active KTM-created records with the same IC.
|
||||
- Application success page with public UUID reference.
|
||||
- Pending bank verification creation for submitted applications.
|
||||
- Feature and unit tests for public application rules, registration period, and vacancy calculation.
|
||||
|
||||
Changed:
|
||||
|
||||
- QR links now open the real public application flow instead of the Phase 3 placeholder.
|
||||
|
||||
Notes:
|
||||
|
||||
- PPM review and approval of submitted applications remains scheduled for Phase 5.
|
||||
|
||||
## 2026-05-29 - Phase 5 PPM Review and Assignment
|
||||
|
||||
Added:
|
||||
|
||||
- PPM application list scoped to assigned Pusat Mengundi.
|
||||
- PPM application detail page with applicant, bank, document, selected KTM, agency representative, and status history sections.
|
||||
- PPM approval flow with role change before approval.
|
||||
- KTM assignment to available Saluran Mengundi.
|
||||
- KP/KPDP assignment to active KTM with remaining vacancy.
|
||||
- PAPM assignment at Pusat Mengundi level.
|
||||
- PPM rejection flow with required reason.
|
||||
- Private document download for PPM-owned applications.
|
||||
- Record-level authorization policy for PPM application access.
|
||||
- PPM dashboard metrics and link to review queue.
|
||||
- Feature tests for PPM scoping, missing-document block, approval/assignment, role change, and rejection.
|
||||
|
||||
Changed:
|
||||
|
||||
- Application approval in PPM flow now creates active staff assignment and moves the application to `assigned`.
|
||||
- Base controller now includes Laravel authorization support for policies.
|
||||
|
||||
Notes:
|
||||
|
||||
- Polling-day attendance remains scheduled for Phase 10.
|
||||
|
||||
## 2026-05-29 - Phase 6 KTM Flow
|
||||
|
||||
Added:
|
||||
|
||||
- KTM team page showing assigned Saluran, KP team members, and KP vacancy.
|
||||
- KTM KP registration form.
|
||||
- KTM-created KP records with `source = created_by_ktm`.
|
||||
- Queued Markdown email notification for KTM-created applicants.
|
||||
- KTM approval flow for KP applications under own team.
|
||||
- KTM deletion flow for own unassigned KTM-created applicant records.
|
||||
- Registration-period enforcement for KTM registration and approval.
|
||||
- KTM dashboard metrics and links to team/KP registration pages.
|
||||
- Feature tests for KTM team scope, KP-only registration, email notification, registration closing, approval, non-KP block, other-team block, and deletion.
|
||||
|
||||
Changed:
|
||||
|
||||
- KTM dashboard now shows assigned Saluran, active KP count, and KP vacancy from live data.
|
||||
|
||||
Notes:
|
||||
|
||||
- KTM cannot upload documents for applicants in this phase; document collection remains with public/admin flows.
|
||||
|
||||
## 2026-05-29 - Phase 7 Admin Management and Manual Entry
|
||||
|
||||
Added:
|
||||
|
||||
- Admin management dashboard at `/admin/management`.
|
||||
- Global Admin application list with filters.
|
||||
- Manual application entry by Admin.
|
||||
- Admin application edit screen with direct assignment panel.
|
||||
- Global staff assignment list and edit screen.
|
||||
- Police, KKM, and JKM representative entry screen.
|
||||
- Required post-registration `catatan` validation for Admin changes after registration closes.
|
||||
- System note creation for post-closing Admin changes.
|
||||
- Activity and history logging for Admin application and assignment changes.
|
||||
- Feature tests for manual entry, post-closing notes, direct assignment, representative creation, and Admin Kewangan route blocking.
|
||||
|
||||
Changed:
|
||||
|
||||
- Admin dashboard now shows live counts for Pusat Mengundi, Saluran, applications, and pending review.
|
||||
- Admin dashboard links to the new management module.
|
||||
|
||||
Notes:
|
||||
|
||||
- Admin manual records do not send applicant email requests.
|
||||
|
||||
## 2026-05-29 - Phase 8 Admin Kewangan
|
||||
|
||||
Added:
|
||||
|
||||
- Admin Kewangan bank verification module at `/kewangan/bank`.
|
||||
- Finance list for approved/assigned staff.
|
||||
- Filters for Pusat Mengundi, role, verification status, missing bank statement, missing account number, and keyword.
|
||||
- Bank verification detail page.
|
||||
- Status update for pending, verified, rejected, and requires correction.
|
||||
- Finance note capture.
|
||||
- Finance XLSX export with export log recording.
|
||||
- Sensitive data masking for IC, phone, and bank account display.
|
||||
- Feature tests for finance list, verification update, filters, export logs, and assignment access blocking.
|
||||
|
||||
Changed:
|
||||
|
||||
- Admin Kewangan dashboard now shows live finance counts and links to the finance module.
|
||||
|
||||
Notes:
|
||||
|
||||
- Finance export uses OpenSpout due to the documented `maatwebsite/excel` compatibility exception.
|
||||
|
||||
## 2026-05-29 - Phase 9 Wheelchair Management
|
||||
|
||||
Added:
|
||||
|
||||
- Admin wheelchair management module at `/admin/management/wheelchairs`.
|
||||
- Wheelchair allocation setup per Pusat Mengundi.
|
||||
- Taken transaction recording with quantity, time, person taking, and notes.
|
||||
- Returned transaction recording with quantity, time, condition, and notes.
|
||||
- Summary counts for allocated, taken, returned, outstanding, and available quantities.
|
||||
- Transaction history table.
|
||||
- Audit logging for allocation changes and wheelchair transactions.
|
||||
- Feature tests for allocation, taken/returned transactions, quantity validation, and role access blocking.
|
||||
|
||||
Changed:
|
||||
|
||||
- Admin management dashboard and navigation now include Kerusi Roda.
|
||||
|
||||
Notes:
|
||||
|
||||
- Transaction rows are retained as movement history; current balance is calculated from transaction totals.
|
||||
|
||||
## 2026-05-29 - Phase 10 Attendance
|
||||
|
||||
Added:
|
||||
|
||||
- PPM attendance module at `/ppm/attendance`.
|
||||
- PPM-scoped Pusat Mengundi attendance list.
|
||||
- Bulk attendance recording for active staff assignments.
|
||||
- Attendance statuses: present, absent, and not recorded.
|
||||
- Check-in time, recorded by, and note fields.
|
||||
- Attendance activation guard using `election_settings.is_attendance_active`.
|
||||
- Admin attendance dashboard at `/admin/attendance`.
|
||||
- Admin attendance detail view by Pusat Mengundi.
|
||||
- Role-level attendance summary.
|
||||
- Attendance detail XLSX export with export log recording.
|
||||
- Feature and unit tests for PPM scoping, activation flag, Admin dashboard, export log, and summary calculation.
|
||||
|
||||
Changed:
|
||||
|
||||
- Admin dashboard now links to attendance.
|
||||
- PPM dashboard now links to attendance and shows present count.
|
||||
|
||||
Notes:
|
||||
|
||||
- Attendance export uses OpenSpout due to the documented `maatwebsite/excel` compatibility exception.
|
||||
|
||||
## 2026-05-29 - Phase 11 Polish and Hardening
|
||||
|
||||
Added:
|
||||
|
||||
- Authorized Admin document download route for private application documents.
|
||||
- Authorized Admin Kewangan bank statement download route.
|
||||
- Activity logging for Admin and finance document downloads.
|
||||
- Document access tests for Admin, Admin Kewangan, and unauthorized roles.
|
||||
- Sensitive data masking unit test.
|
||||
- Final deployment quality gate notes.
|
||||
|
||||
Changed:
|
||||
|
||||
- Admin application edit screen now shows uploaded documents with secure download links.
|
||||
- Admin Kewangan bank detail screen now links to bank statement download when available.
|
||||
|
||||
Notes:
|
||||
|
||||
- Admin Kewangan cannot download IC documents through the finance document route.
|
||||
## 2026-05-31
|
||||
|
||||
### Changed
|
||||
|
||||
- Borang daftar Pusat Mengundi kini menerima bilangan Saluran, KTM, Polis Iring, KP, PPM, KPDP, PAPM, JKM, KKM dan Calon Tambahan.
|
||||
- Saluran dijana secara automatik mengikut nombor dan tidak lagi memerlukan nama.
|
||||
- Istilah Calon Simpanan telah ditukar kepada Calon Tambahan.
|
||||
|
||||
## 2026-06-02 - Fasa 15-A: Purge Automatik Fail Eksport Sensitif
|
||||
|
||||
### Added
|
||||
|
||||
- Artisan command `exports:purge --days=30` — memadam fail XLSX eksport lama dari storage dan menandakan rekod `export_logs` dengan `purged_at`.
|
||||
- Scheduled task harian pada `00:00` dalam `routes/console.php` untuk jalankan purge secara automatik (retention default: 30 hari).
|
||||
- Kolum `purged_at` dan `purged_by_user_id` pada jadual `export_logs`.
|
||||
- Activity log `export_file_purged` untuk setiap fail yang dipadamkan.
|
||||
- 7 ujian baharu dalam `ExportPurgeCommandTest`.
|
||||
|
||||
### Changed
|
||||
|
||||
- `ExportLog` model kini mempunyai cast `purged_at` dan relationship `purgedBy()`.
|
||||
|
||||
## 2026-06-02 - Fasa 14-A: Portal Status Permohonan Pemohon Awam
|
||||
|
||||
### Added
|
||||
|
||||
- Halaman status permohonan di `/permohonan/{uuid}/status` — boleh dicapai tanpa log masuk menggunakan UUID awam sahaja.
|
||||
- Memaparkan: nama disamarkan (3 aksara pertama), jawatan, pusat mengundi, status dengan badge berwarna, sebab penolakan jika ditolak.
|
||||
- UUID tidak dikenali, rekod soft-deleted, dan rekod `deleted_by_ktm` mengembalikan 200 dengan mesej generik — tidak mendedahkan maklumat dalaman.
|
||||
- `SensitiveData::maskName()` — helper masking nama (3 aksara pertama + `***`).
|
||||
- Panel "Simpan pautan status" pada halaman kejayaan permohonan dengan butang salin URL.
|
||||
- 9 ujian baharu dalam `PublicApplicationStatusTest`.
|
||||
|
||||
## 2026-06-02 - Fasa 12-E: Konsistensikan Semakan Jenis Dokumen Download
|
||||
|
||||
### Added
|
||||
|
||||
- `ApplicationDocument` model kini mempunyai type constants (`TYPE_IC_DOCUMENT`, `TYPE_BANK_STATEMENT`) dan per-role allowlists (`ALLOWED_FOR_ADMIN`, `ALLOWED_FOR_PPM`, `ALLOWED_FOR_FINANCE`) sebagai satu sumber kebenaran.
|
||||
- Activity log untuk PPM document downloads: `document_downloaded_by_ppm`.
|
||||
- 9 ujian baharu dalam `DocumentDownloadAuthorizationTest` meliputi semua tiga path dan kes sempadan.
|
||||
|
||||
### Fixed
|
||||
|
||||
- PPM path kini mempunyai semakan jenis dokumen eksplisit menggunakan `ALLOWED_FOR_PPM`.
|
||||
- PPM path kini mengembalikan 403 (bukan 404) untuk ownership mismatch — tidak mendedahkan kewujudan ID dokumen.
|
||||
- Admin path kini menggunakan `ALLOWED_FOR_ADMIN` secara eksplisit — jenis dokumen baharu perlu didaftarkan untuk Admin dapat akses.
|
||||
- Finance path kini menggunakan `ALLOWED_FOR_FINANCE` constant (logik sama, kini konsisten).
|
||||
|
||||
## 2026-06-02 - Fasa 13-A: Composite Indexes Pangkalan Data
|
||||
|
||||
### Added
|
||||
|
||||
- Migration `2026_06_02_000000_add_performance_indexes.php` menambah 3 index baharu:
|
||||
- `applications.(election_id, status)` — dashboard count dan senarai admin bertapis election
|
||||
- `applications.(selected_ktm_assignment_id, requested_position_id)` — `KtmVacancyService` reserved-application count (dipanggil setiap semak kekosongan KP/KPDP)
|
||||
- `staff_assignments.(reports_to_assignment_id, position_id, status)` — `KtmVacancyService` active-assignment count di bawah KTM
|
||||
- Dokumentasi lengkap semua index dalam `docs/03-database-design.md`.
|
||||
- Arahan verifikasi index (`SHOW INDEX`, `EXPLAIN`) dalam `docs/08-deployment-notes.md`.
|
||||
|
||||
### Notes
|
||||
|
||||
- Dua index yang diancangkan dalam plan sudah wujud dalam migration Phase 2: `(election_id, ic_number)` dan `(election_id, pusat_mengundi_id, status)` pada `applications`.
|
||||
- `bank_verifications` tiada kolum `election_id` — index berkaitan tidak ditambah.
|
||||
|
||||
## 2026-06-02 - Fasa 12-C: Perkukuh Enforcement Nota Catatan Post-Pendaftaran
|
||||
|
||||
### Added
|
||||
|
||||
- Middleware `RequireAdminPostCloseNote` — automatically enforces that a `note` field is present in all write operations (`POST`, `PATCH`, `PUT`, `DELETE`) on Admin management routes when election registration is closed. Middleware alias: `admin.post_close_note`.
|
||||
- 5 ujian baharu meliputi semua mutation routes yang sebelum ini tidak mempunyai catatan enforcement tests: application create, assignment update, representative create, representative delete, dan ujian bahawa middleware tidak menyekat apabila pendaftaran terbuka.
|
||||
|
||||
### Changed
|
||||
|
||||
- Route group `admin/management` kini menggunakan middleware `['auth', 'role:Admin', 'admin.post_close_note']` — catatan dikuatkuasakan secara automatik pada semua write routes tanpa bergantung pada konvensyen pemanggilan service sahaja.
|
||||
|
||||
## 2026-06-02 - Fasa 12-B: Pembetulan Semakan IC Soft-Deleted
|
||||
|
||||
### Fixed
|
||||
|
||||
- Audit dan pengesahan bahawa semua query semakan IC duplikasi (`StorePublicApplicationRequest`, `KtmApplicationService`) menggunakan perlindungan dua lapis yang betul: SoftDeletes scope (mengecualikan `deleted_at IS NOT NULL`) dan `whereNotIn` (mengecualikan `cancelled` dan `deleted_by_ktm`).
|
||||
- Tambah comment dalam kod untuk mendokumentasikan intent perlindungan ini secara eksplisit.
|
||||
|
||||
### Added (Tests)
|
||||
|
||||
- Ujian baharu `test_public_registration_is_unblocked_after_ktm_deletes_applicant`: mengesahkan pemohon awam boleh mendaftar semula selepas KTM delete rekod yang dicipta.
|
||||
- Ujian baharu `test_cancelled_application_does_not_block_new_registration`: mengesahkan rekod `cancelled` tidak menyekat pendaftaran IC yang sama.
|
||||
|
||||
## 2026-06-02 - Fasa 12-A: Admin Settings UI
|
||||
|
||||
### Added
|
||||
|
||||
- Halaman Admin Settings di `/admin/setup/settings/{election}` untuk mengawal mod kehadiran dan override tempoh pendaftaran tanpa edit database.
|
||||
- Toggle radio untuk `is_attendance_active`: Tidak aktif / Aktif.
|
||||
- Toggle radio tiga-keadaan untuk `is_registration_open_override`: Auto (ikut tarikh) / Buka Paksa / Tutup Paksa.
|
||||
- Pautan "Tetapan" dalam nav setup dan setup index actions.
|
||||
- Amaran badge apabila override sedang aktif.
|
||||
- Activity log untuk setiap perubahan tetapan (nilai sebelum dan selepas, siapa yang ubah).
|
||||
- 9 ujian baharu untuk akses, kemaskini, dan activity log.
|
||||
|
||||
### Fixed
|
||||
|
||||
- `SampleElectionSeeder` kini query kod jawatan `CALON_TAMBAHAN` (bukan `CALON_SIMPANAN` lama) — `php artisan migrate:fresh --seed` kini berjalan tanpa ralat.
|
||||
- `AdminSetupTest` dikemaskini untuk menghantar medan operasi yang diperlukan semasa cipta Pusat, dan menggunakan nombor saluran '3' untuk elak konflik dengan saluran auto-jana.
|
||||
|
||||
## 2026-06-02 - Perancangan Fasa 12–15
|
||||
|
||||
### Added
|
||||
|
||||
- Dokumen pelan penambahbaikan `docs/09-improvement-plan.md` merangkumi lapan isu yang dikenal pasti daripada semakan kod menyeluruh selepas Phase 11.
|
||||
- Fasa 12–15 ditambah dalam `docs/01-development-plan.md` sebagai fasa penambahbaikan berprioritikan.
|
||||
|
||||
### Planned (belum dilaksanakan)
|
||||
|
||||
- Fasa 12: Pembetulan kritikal — Admin Settings UI untuk toggle kehadiran dan pendaftaran, pembetulan semakan IC soft-deleted, perkukuhan enforcement nota catatan, pembetulan seeder, dan konsistensi semakan jenis dokumen download.
|
||||
- Fasa 13: Composite indexes pangkalan data untuk prestasi query berskala besar.
|
||||
- Fasa 14: Portal status permohonan pemohon awam yang boleh dicapai via UUID tanpa login.
|
||||
- Fasa 15: Purge automatik fail eksport sensitif dengan polisi simpanan boleh dikonfigurasi.
|
||||
1198
docs/decision-log.md
Normal file
1198
docs/decision-log.md
Normal file
File diff suppressed because it is too large
Load Diff
1256
docs/progress-log.md
Normal file
1256
docs/progress-log.md
Normal file
File diff suppressed because it is too large
Load Diff
BIN
docs/~$N49-Kota Iskandar - senarai pusat mengundi.xlsx
Normal file
BIN
docs/~$N49-Kota Iskandar - senarai pusat mengundi.xlsx
Normal file
Binary file not shown.
Reference in New Issue
Block a user