Files
mylesen/docs/audit-mylesen.md
2026-05-14 16:27:38 +08:00

493 lines
27 KiB
Markdown

# Audit Teknikal: Sistem myLesen
**Tarikh Audit:** 18 Mac 2026
**Juruaudit:** Claude Code (berdasarkan pembacaan kod semasa)
**Skop:** Keseluruhan projek `mylesen`
---
## A. Ringkasan Sistem Semasa
### A1. Maklumat Projek
| Butiran | Nilai |
|---|---|
| Nama Sistem | myLesen — Sistem Pengurusan Lesen Penjaja |
| Organisasi | MBIP (Majlis Bandaraya Iskandar Puteri) |
| Framework | Laravel 11.9 / PHP 8.2 |
| Database Utama | MySQL (`mylesen`) |
| Database Kedua | MySQL (`sistemutama`) via `second_mysql` connection — sistem EPBT |
| Frontend | Blade + Tailwind CSS + Alpine.js + jQuery |
| Penjanaan Dokumen | PHPOffice/PHPWord (output `.docx`, bukan PDF) |
| Authentication | Laravel Breeze (session-based) |
| API Luar | `http://epbt.mbip.gov.my/...` (jana bil pelbagai) |
| API Chrome Extension | **Tiada** — belum dibina |
### A2. Statistik Kod
| Komponen | Bilangan | Catatan |
|---|---|---|
| Controllers (aktif) | 15 | termasuk auth controllers |
| Controllers (dalam `asal/`) | 4 | fail backup lama — tidak digunakan |
| Models | 38 | 11 EPBT (second_mysql), 4 PbtPay (stub kosong) |
| Views (blade) | ~104 | termasuk `asal/`, `-ori.blade.php`, fail ujian |
| Migrations | 39 | 1 ada typo nama fail |
| Routes (web) | ~71 + auth routes | tiada `routes/api.php` |
| Middleware custom | 2 | `AdminMiddleware`, `KakitanganMiddleware` |
| Policies | 2 | kebanyakan method masih empty stub |
| FormRequest classes | 2 | `ProfileUpdateRequest`, `LoginRequest` sahaja |
| Tema Admin dipasang | 4 | AdminLTE, KaiAdmin, Corporate UI, Kapella — hanya 1 digunakan |
### A3. Seni Bina Semasa
```
[Browser]
|
↓ HTTP (web.php routes)
[Laravel Controllers]
| |
↓ ↓
[MySQL: mylesen] [MySQL: sistemutama/EPBT] [HTTP: epbt.mbip.gov.my]
(data permohonan) (bil, resit, akaun lesen) (jana bil API)
```
**Autentikasi & Kebenaran:**
- `role` column dalam jadual `users` — string (bukan enum)
- 2 middleware: `admin` (super sahaja), `kakitangan` (semua staf)
- 6 Gate definitions dalam `AppServiceProvider`
- 2 Policy classes (mostly stubs)
- Tiada konsistensi antara penggunaan Gate vs Policy
**Document Streaming:**
- Semua dokumen uploaded dilindungi — streaming melalui controller, bukan URL terus
### A4. Aliran Status Semasa (dari pembacaan kod)
Status permohonan disimpan dalam `status_progress` (string) dan `status_mesyuarat` (string) dalam jadual `lesen_penjajas`.
```
[PEMOHON HANTAR BORANG]
|
↓ hantar_permohonan()
┌─────────────┐
│ BARU │ ← Queue PT (penjaja-baru)
└─────────────┘
|
↓ simpanWangProses() — PT masukkan No. Bil EPBT
┌────────────────────────────┐
│ MENUNGGU BAYARAN PROSES │ ← Queue PT (penjaja-proses)
└────────────────────────────┘
|
↓ Auto: bila resit ditemui dalam EPBT (EpbtEcasResit)
┌────────────────────────────┐
│ SEMAKAN BAYARAN PROSES │ ← Queue PT (penjaja-bukafail)
└────────────────────────────┘
|
↓ hantarPPK() — PT hantar ke IK
┌─────────────────┐
│ LAWATAN TAPAK │ ← Queue IK (penjaja-pemeriksaan)
└─────────────────┘
|
↓ simpanUlasan() — IK lengkap borang ulasan
┌─────────────────────┐
│ ULASAN PEGAWAI │ ← Queue PP (penjaja-cadangan)
└─────────────────────┘
|
↓ simpan_cadangan() — PP tulis cadangan
┌─────────────────────┐
│ ULASAN PENGARAH │ ← Queue Pengarah (penjaja-ulasan-cadangan)
└─────────────────────┘
|
↓ simpan_ulasan_cadangan() — Pengarah setuju bawa ke mesyuarat
┌──────────────────────────────────┐
│ SOKONG DIBAWA KE MESYUARAT │
└──────────────────────────────────┘
|
↓ sahkanSenarai() — PT masukkan dalam agenda mesyuarat
┌────────────────────────────────────────┐
│ MENUNGGU KEPUTUSAN MESYUARAT │
└────────────────────────────────────────┘
|
↓ simpanKeputusanMesyuarat()
┌──────────────────────┐
│ KEPUTUSAN DITERIMA │
└──────────────────────┘
|
├── status_mesyuarat = 'diluluskan' → PT daftar lesen
├── status_mesyuarat = 'ditolak' → Tamat
└── status_mesyuarat = 'ditangguhkan' → Tunggu mesyuarat berikutnya
```
**Gap dalam status flow (isu penting):**
1. Tiada status terminal "LESEN DIKELUARKAN" atau "SELESAI" — `status_progress` kekal 'keputusan diperolehi' walaupun lesen sudah dikeluarkan
2. `senarai_pt_daftarlesen` (queue pendaftaran no lesen) menggunakan status `ulasan pegawai/pengarah/sokong` dengan `whereNull('no_akaun_lesen')` — ini adalah aliran berasingan tanpa status dedicated
3. Bayaran lesen (hutang lesen) dikesan melalui `whereNotNull('no_akaun_lesen')->whereNull('lesen_no_resit')` — tiada status formal untuk ini
4. Apabila Pengarah tangguhkan cadangan (bukan ke mesyuarat), status kembali ke 'lawatan tapak' tetapi borang IK baru tidak dicipta secara automatik
---
## B. Modul yang Masih Relevan
### B1. Permohonan Lesen Penjaja (Pemohon/Awam)
| | |
|---|---|
| Controller | `App\Http\Controllers\PenjajaController` |
| Routes Utama | `/mohon-penjaja`, `/simpan-mohon-penjaja`, `/mohon_edit/{id}`, `/mohon_hapus/{id}`, `/mohon_papar/{id}`, `/mohon_hantar` |
| Views Utama | `penjaja/mohon.blade.php`, `penjaja/mohon_papar.blade.php` |
| Dashboard Pemohon | `dashboard.blade.php` (route: `/dashboard`) → `PenjajaController@list` |
| Status | AKTIF |
### B2. Permohonan Lesen Penjaja (Admin — Mohon Atas Nama)
| | |
|---|---|
| Controller | `App\Http\Controllers\Admin\PenjajaController` |
| Routes Utama | `/mohon-penjaja-admin`, `/simpan-mohon-penjaja-admin` |
| Views Utama | `admin/penjaja/mohon_baru.blade.php` |
| Catatan | Lompat terus ke status `lawatan tapak` — fast-track untuk admin |
| Status | AKTIF |
### B3. PT Workflow (Pembantu Tadbir)
| | |
|---|---|
| Controllers | `Admin\PenjajaController` (senarai), `Admin\PtPenjajaController` (tindakan) |
| Queue Screens | `/penjaja-baru` (baru), `/penjaja-proses` (tunggu bayar), `/penjaja-bukafail` (bayar disahkan), `/penjaja-daftarlesen` (daftar lesen) |
| Tindakan | Simpan Wang Proses, Simpan No Fail, Hantar ke IK, Urus Mesyuarat, Cetak Lampiran B |
| Status | AKTIF |
### B4. IK Workflow (Inspektor Kesihatan)
| | |
|---|---|
| Controller | `Admin\IkPenjajaController` |
| Routes | `/penjaja-pemeriksaan`, `/ik-simpan-lokasi`, `/ik-simpan-ulasan` |
| Model Utama | `BorangUlasanIk` (30+ fields, 3 foto lawatan tapak) |
| Status | AKTIF |
### B5. PP & Pengarah Workflow
| | |
|---|---|
| Controller | `Admin\PegawaiPenjajaController` |
| Routes | `/penjaja-cadangan`, `/penjaja-ulasan-cadangan`, `/pegawai-simpan-cadangan`, `/pegawai-simpan-ulasan-cadangan` |
| Model Utama | `UlasanPegawai` (gabungkan cadangan PP dan ulasan Pengarah dalam satu model) |
| Status | AKTIF |
### B6. Mesyuarat Pelesenan + Lampiran B
| | |
|---|---|
| Controller | `Admin\PtPenjajaController` |
| Routes | `/senaraimesyuarat`, `/senaraibawamesyuarat/{id}`, `/simpan_mesyuarat`, `/sahkan_senarai`, `/simpan_keputusan_mesyuarat`, `/cetak_lampiran_b` |
| Model Utama | `MesyuaratPelesenan` + pivot `mesyuarat_pelesenan_lesen_penjajas` |
| Output | Export landscape `.docx` menggunakan PHPWord, dikelompok mengikut kawasan |
| Pivot Data | by_law, kodlesen, kadar_lesen, kadar_sampah, kadar_sewa_petak, kadar_patil, keputusan_mesyuarat |
| Status | AKTIF |
### B7. Bil Proses + EPBT Integration (Mode Manual)
| | |
|---|---|
| Controller | `Admin\PtPenjajaController::simpanWangProses()` |
| Cara Kerja | PT masukkan No. Bil dari EPBT secara manual → sistem baca dari `second_mysql` untuk dapatkan butiran bil → semak `EpbtEcasResit` untuk resit bayaran |
| Model | `BilPelbagai`, `BilPelbagaiItem`, `EpbtBpBil`, `EpbtEcasResit` |
| Status | AKTIF (mode manual) |
### B8. Bil Auto-Jana (EPBT API — Mode Automatik)
| | |
|---|---|
| Controller | `BilPelbagaiController::janaBil()` |
| Cara Kerja | HTTP POST ke EPBT API → dapat `accountNo` → simpan dalam `BilPelbagaiApi` |
| Status Sekarang | **KOD SIAP DITULIS** tetapi **ROUTE DITUKAR KE `doNothing()`** |
| Catatan | Logik API lengkap, tetapi dimatikan sengaja — mungkin belum disahkan untuk production |
### B9. Semak Bayaran Lesen (Hutang Lesen)
| | |
|---|---|
| Controller | `Admin\DashboardController::senaraiLesenBelumBayar()` + `semakBayaranLesen()` |
| Routes | `/penjaja-hutang-lesen`, `/penjaja-hutang-lesen-semak` |
| Model | `GrabResitLesen` (log batch semakan), `EpbtEcasResit` |
| Status | AKTIF |
### B10. Dashboard Admin
| | |
|---|---|
| Controller | `Admin\DashboardController::utama()` |
| Route | `/dashmin/{ctahun?}` |
| Kandungan | Count per status, filter tahun, semakan bayaran inline |
| Isu | N+1 query: semua rekod 'menunggu bayaran proses' di-query EPBT setiap page load |
| Status | AKTIF (dengan isu prestasi) |
### B11. Cetakan (IK + Cadangan Pegawai)
| | |
|---|---|
| Controller | `Admin\CetakanPenjajaController` |
| Routes | `/penjaja-cetak-pemeriksaan/{id}/{ik_id}`, `/penjaja-cetak-cadangan-pegawai/{id}/{ik_id}` |
| Output | Download `.docx` menggunakan PHPWord |
| Status | AKTIF |
### B12. Pengurusan Admin
| | |
|---|---|
| Controller | `Admin\PengurusanAdminController` |
| Routes | `/lantikan_admin`, `/cari_admin`, `/admin/users/{id}/update-role`, `/rem-admin/{nokp}` |
| Status | AKTIF |
### B13. Pengumuman & Carousel
| | |
|---|---|
| Controllers | `Admin\PengumumanController`, `Admin\GambarCarouselController` |
| Akses | Role `super` sahaja (middleware `admin`) |
| Status | AKTIF |
### B14. Profile + Document Streaming
| | |
|---|---|
| Controller | `ProfileController` |
| Routes | `/profile`, `/profil/stream/{filename}`, `/dokumen/stream/{id}/{filename}`, `/ik/stream/{id}/{filename}` |
| Status | AKTIF |
### B15. Data AJAX (Cascading Dropdowns)
| | |
|---|---|
| Controller | `DataController` |
| Routes | `/get_kawasan`, `/get_taman`, `/get_jalan`, `/get_penempatan` |
| Status | AKTIF |
### B16. Semakan EPBT Pelanggan
| | |
|---|---|
| Controller | `Admin\PtPenjajaController::searchPelanggan()` |
| Route | `/get-bil` |
| Model | `EpbtPelanggan` |
| Status | AKTIF |
---
## C. Modul/Fail yang Obsolete atau Mencurigakan
### C1. Fail Keselamatan — TINDAKAN SEGERA DIPERLUKAN
| Fail | Isu | Tindakan |
|---|---|---|
| `public/test_curl.php` | Fail ujian dengan IP server terus (`1.9.131.236`), endpoint EPBT nyata, `client_key = 'MPJBT'` hardcoded, dan struktur data pelanggan nyata. Boleh diakses oleh sesiapa tanpa sebarang auth kerana berada dalam folder `public/`. | **PADAM SEGERA** |
| `mylesen(2).sql` | Backup database penuh dalam root projek. Mengandungi data sensitif. Boleh terdedah jika repo dikongsi. | **PINDAH KELUAR dari projek** |
### C2. Fail Backup / Lama
| Fail/Direktori | Isu | Tindakan |
|---|---|---|
| `app/Http/Controllers/Admin/asal/` | Mengandungi 4 controllers lama: `DashboardController`, `PenjajaController`, `IkPenjajaController`, `PegawaiPenjajaController`. Tiada routes menuju ke namespace ini. Versi utama sudah jauh lebih lengkap. | Padam |
| `routes/asal/web.php` | Route lama sebelum refactor. Tidak dimuatkan oleh Laravel. | Padam |
| `resources/views/admin/penjaja/asal/` | Folder views lama dengan ~16 fail. Tiada routes aktif menggunakan views ini. | Padam |
| `resources/views/admin/penjaja/papar_permohonan-ori.blade.php` | Versi lama `papar_permohonan.blade.php`. | Padam |
| `resources/views/admin/penjaja/partials/dokumen-ori.blade.php` | Versi lama partial. | Padam |
| `resources/views/admin/penjaja/partials/ori_dokumen.blade.php` | Versi lama partial (nama berbeza). | Padam |
| `resources/views/admin/penjaja/partials/wangproses_nofail-ori.blade.php` | Versi lama partial. | Padam |
| `resources/views/layouts/appmin_lama.blade.php` | Layout admin lama. | Padam |
| `resources/views/fahmi.blade.php` | Test view. Route `/` mengembalikan ini bukan halaman utama sebenar. `welcome.blade.php` wujud tetapi tidak digunakan. | Padam, tukar route `/` ke view yang betul |
### C3. Modul Stub / Tidak Lengkap
| Modul | Fail | Isu | Cadangan |
|---|---|---|---|
| **PBTPay Payment Gateway** | `PbtpayController.php`, `PbtpayBil.php`, `PbtpayCart.php`, `PbtpayCartItem.php`, `PbtpayTransaksi.php`, `pbtpay/callback.blade.php`, `pbtpay/indirect.blade.php` | Controller `checkout()` tidak siap, tiada return value. 4 models kosong (tiada `$fillable`, tiada relations). Callback view hanya mengandungi teks "test". Routes wujud tetapi tidak berfungsi. | Keputusan diperlukan: implement atau padam semua |
| **Bil Auto-Jana** | `BilPelbagaiController.php` | `janaBil()` ditulis lengkap tetapi route (`/pt/jana-bil-automatik`) sengaja ditukar ke `doNothing()` — ini bukan bug, ini keputusan disengajakan. | Aktifkan setelah disahkan dengan MBIP IT |
| **Laporan Prestasi** | `Admin\LaporanPrestasiController.php` | Ada syntax error dalam method `utama()``->where()` tanpa parameter. Tiada route yang menuju ke controller ini. | Betulkan atau tulis semula |
| **LesenPenjajaHistory** | `LesenPenjajaHistory.php` | Model kosong — tiada `$fillable`, tiada relations. Table wujud dalam migration tetapi model tidak digunakan dalam mana-mana controller. | Implement sebagai audit trail atau padam |
| **UserPolicies** | `UserPolicies.php`, `LesenPenjajaPolicy.php` | Model `UserPolicies` dan table wujud. Policy class cuba guna model ini untuk semak `akses` tetapi kebanyakan method dalam Policy adalah empty stub. | Implement sepenuhnya atau guna Gate sahaja |
| **BilPelbagaiItemController** | `BilPelbagaiItemController.php` | Class wujud tetapi tiada sebarang method. | Implement atau padam |
### C4. Isu Penamaan dan Konsistensi
| Isu | Lokasi | Penerangan |
|---|---|---|
| **Bug: no_akaun_lesen vs no_fail_lesen** | `PtPenjajaController::simpanNoLesen()` baris 155 | Method simpan ke `no_akaun_lesen` dan `kod_lesen`, tetapi success message menyebut `$mohon->no_fail_lesen`. Ini adalah bug sebenar — nama field dalam mesej tidak sepadan dengan field yang disimpan. |
| **Ketidakjelasan dua field** | `LesenPenjaja` model | `no_akaun_lesen` = akaun dalam sistem EPBT. `no_fail_lesen` = nombor fail fizikal MBIP. Kedua-dua digunakan dalam konteks berbeza tanpa dokumentasi jelas. |
| **Typo nama migration** | `2025_06_19_084503_create_pengumumen_table.php` | Sepatutnya `pengumuman`, bukan `pengumumen`. Walaupun nama fail boleh kekal, jadual yang dicipta juga mungkin ada typo. |
| **Nama route bercampur** | `routes/web.php` | Campuran Melayu/Inggeris: `pt.bawa-mesyuarat`, `admin.papar`, `get-bilpelbagai`, `penjaja-mohon`. Tiada naming convention yang konsisten. |
| **Dua class bernama `PenjajaController`** | Namespace berbeza | `App\Http\Controllers\PenjajaController` (public) dan `App\Http\Controllers\Admin\PenjajaController` (admin). Dalam `web.php`, kedua-dua diimport dengan alias. Mengelirukan. |
| **`senaraibawamesyuarat.blade.php`** | `admin/penjaja/` | Fail lama wujud tapi sistem menggunakan `senaraibawamesyuaratbaru.blade.php` yang lebih baru. |
| **`laporandashboard.blade.php`** | `admin/penjaja/` | Nama mengelirukan — ia digunakan untuk paparan "semua permohonan" dalam `senarai_all()`, bukan laporan dashboard yang sebenar. |
| **Boolean flags tidak digunakan** | `users` table | `is_admin_lesen_penjaja`, `is_admin_lesen_anjing`, `is_admin_lesen_perniagaan` ada dalam `$fillable` User model tetapi tidak digunakan dalam mana-mana logic. Role-based auth menggunakan `role` column sahaja. |
| **`hantar_permohonan()` guna session** | `PenjajaController` | `lesen_penjaja_id` dibaca dari session — boleh gagal jika session expired atau tab dibuka berganda. |
### C5. Isu Seni Bina
| Isu | Butiran |
|---|---|
| **Fat Controllers** | `Admin\PenjajaController::store()` melakukan dalam satu method: buat user, buat syarikat, buat user_syarikat, buat lesen_penjaja, upload dokumen, buat bil pelbagai, query EPBT, buat borang IK. Tiada DB transaction. Jika gagal di tengah, data tidak konsisten. |
| **Status strings hardcoded** | Nilai seperti `'menunggu bayaran proses'`, `'lawatan tapak'`, `'ulasan pegawai'` tersebar dalam `PtPenjajaController`, `PenjajaController`, `Admin\PenjajaController`, dan `DashboardController`. Typo dalam satu tempat menyebabkan permohonan "hilang" dari semua queue. |
| **N+1 query dalam Dashboard** | `DashboardController::utama()` dan `senarai_pt_proses()` loop semua rekod 'menunggu bayaran proses' dan buat query ke EPBT (`second_mysql`) untuk setiap rekod. Semakin banyak permohonan, semakin lambat halaman loads. |
| **Logik EPBT diduplikasi** | Logik query `EpbtBpBil` dan `EpbtEcasResit` muncul dalam 4 tempat berbeza: `PtPenjajaController`, `Admin\PenjajaController`, `DashboardController`, dan lain-lain. Jika EPBT berubah struktur, perlu kemaskini 4 tempat. |
| **Tiada Enum** | Semua nilai status adalah string literals. PHP 8.1 menyokong Enum natively. |
| **Tiada Service layer** | Semua business logic dalam controllers. Tidak ada Service, Action, atau Command classes. |
| **Gates vs Policies bercampur** | `AppServiceProvider` mendefinisikan 6 Gates, ada juga Policy classes, penggunaan tidak konsisten antara keduanya. |
| **Tiada DB transaction** | Method-method yang menulis ke banyak jadual sekaligus (seperti `store()`) tidak wrapped dalam `DB::transaction()`. |
| **Tiada event/job/observer** | Semua operasi synchronous. Tiada queue, tiada listeners, tiada observers. |
| **Tiada FormRequest** | Validation berlaku secara inline dalam controllers. Hanya 2 FormRequest classes wujud untuk keseluruhan sistem. |
| **Soft delete tidak digunakan** | `hapus_permohonan()` menggunakan `->delete()` — data dipadam terus tanpa boleh dipulihkan. |
### C6. Jadual Lengkap Routes dengan Status
| Route | Method | Controller@Method | Status |
|---|---|---|---|
| `/` | GET | view('fahmi') | TEST VIEW — perlu tukar |
| `/utama` | GET | Auth\AuthenticatedSessionController@create | aktif |
| `/dashboard` | GET | PenjajaController@list | aktif |
| `/mohon-penjaja` | GET | PenjajaController@create | aktif |
| `/simpan-mohon-penjaja` | POST | PenjajaController@store | aktif |
| `/mohon_hapus/{id}` | GET | PenjajaController@hapus | aktif |
| `/mohon_edit/{id}` | GET | PenjajaController@edit | aktif |
| `/mohon_papar/{id}` | GET | PenjajaController@papar | aktif |
| `/mohon_hantar` | POST | PenjajaController@hantar | aktif |
| `/pbtpay/checkout/{modul}/{id}` | POST | PbtpayController@checkout | **STUB** |
| `/pbtpay/callback` | POST | view('pbtpay.callback') | **STUB** |
| `/pbtpay/indirect` | POST | view('pbtpay.indirect') | **STUB** |
| `/get-bil` | GET/POST | PtPenjajaController@searchPelanggan | aktif |
| `/get_kawasan` | GET | DataController@getKawasan | aktif |
| `/get_taman` | GET | DataController@getTaman | aktif |
| `/get_jalan` | GET | DataController@getJalan | aktif |
| `/get_penempatan` | GET | DataController@getPenempatan | aktif |
| `/dashmin/{ctahun?}` | GET | DashboardController@utama | aktif |
| `/penjaja-baru` | GET | Admin\PenjajaController@senarai_pt | aktif |
| `/penjaja-proses` | GET | Admin\PenjajaController@senarai_pt_proses | aktif |
| `/penjaja-bukafail` | GET | Admin\PenjajaController@senarai_pt_bukafail | aktif |
| `/penjaja-daftarlesen` | GET | Admin\PenjajaController@senarai_pt_daftarlesen | aktif |
| `/penjaja-pemeriksaan` | GET | IkPenjajaController@senarai_ik | aktif |
| `/penjaja-cadangan` | GET | PegawaiPenjajaController@senarai_cadangan | aktif |
| `/penjaja-ulasan-cadangan` | GET | PegawaiPenjajaController@senarai_ulasan_cadangan | aktif |
| `/penjaja-mesyuarat` | GET | Admin\PenjajaController@senarai_mesyuarat | aktif |
| `/penjaja-minitmesyuarat/{id}` | GET | Admin\PenjajaController@senarai_bawa_mesyuarat | aktif |
| `/penjaja-keputusanmesyuarat` | GET | Admin\PenjajaController@senarai_keputusan | aktif |
| `/penjaja-all` | GET | Admin\PenjajaController@senarai_all | aktif |
| `/penjaja-hutang-lesen` | GET | DashboardController@senaraiLesenBelumBayar | aktif |
| `/penjaja-hutang-lesen-semak` | GET | DashboardController@semakBayaranLesen | aktif |
| `/simpan_wangproses` | POST | PtPenjajaController@simpanWangProses | aktif |
| `/simpan_nofail` | POST | PtPenjajaController@simpanNoFail | aktif |
| `/simpan_nolesen` | POST | PtPenjajaController@simpanNoLesen | aktif |
| `/hantar_ppk` | POST | PtPenjajaController@hantarPPK | aktif |
| `/simpan_mesyuarat` | POST | PtPenjajaController@simpanMesyuarat | aktif |
| `/cetak_lampiran_b` | POST | PtPenjajaController@exportWord | aktif |
| `/sahkan_senarai` | POST | PtPenjajaController@sahkanSenarai | aktif |
| `/simpan_keputusan_mesyuarat` | POST | PtPenjajaController@simpanKeputusanMesyuarat | aktif |
| `/ik-simpan-lokasi` | POST | IkPenjajaController@simpanLokasi | aktif |
| `/ik-simpan-ulasan` | POST | IkPenjajaController@simpanUlasan | aktif |
| `/pegawai-simpan-cadangan` | POST | PegawaiPenjajaController@simpan_cadangan | aktif |
| `/pegawai-simpan-ulasan-cadangan` | POST | PegawaiPenjajaController@simpan_ulasan_cadangan | aktif |
| `/pt/jana-bil-automatik` | POST | BilPelbagaiController@**doNothing** | **DIMATIKAN** (stub) |
| `/mohon-penjaja-admin` | GET | Admin\PenjajaController@create | aktif |
| `/simpan-mohon-penjaja-admin` | POST | Admin\PenjajaController@store | aktif |
| `/lantikan_admin` | GET | PengurusanAdminController@showList | aktif (super) |
| `/admin/users/{id}/update-role` | PATCH | PengurusanAdminController@updateRole | aktif (super) |
| `/rem-admin/{nokp}` | GET | PengurusanAdminController@removeAdmin | aktif (super) |
| `/admin/pengumuman/*` | CRUD | PengumumanController | aktif (super) |
| `/carousel/*` | CRUD | GambarCarouselController | aktif (super) |
---
## D. Risiko Jika Terus Refactor Tanpa Pelan
### D1. Risiko Keselamatan — Perlu Tindakan Segera
| Risiko | Punca | Kesan |
|---|---|---|
| `public/test_curl.php` boleh diakses tanpa auth | Fail ujian tertinggal dalam folder public | Pihak luar boleh guna `client_key = 'MPJBT'` untuk jana bil palsu ke sistem EPBT. Data pelanggan ujian terdedah. |
| `mylesen(2).sql` dalam projek | Developer simpan backup dalam folder projek | Jika folder projek dikongsi atau di-commit ke git, seluruh database terdedah. |
| Tiada HTTPS enforcement dalam kod | Tiada `ForceScheme` middleware | `BilPelbagaiController::janaBil()` memanggil endpoint EPBT melalui `http://` — komunikasi tidak selamat |
### D2. Risiko Integriti Data
| Risiko | Punca | Kesan |
|---|---|---|
| Status "hilang" dari queue | Status string typo dalam satu controller | Permohonan tidak muncul dalam mana-mana queue — staf tidak dapat tindakan |
| Data tidak konsisten jika simpan gagal | Tiada DB transaction dalam `store()` | Permohonan dicipta tetapi dokumen tidak diupload, atau syarikat dicipta tetapi lesen tidak |
| Permohonan terlepas semak bayaran | Semak bayaran manual atau bila dashboard diload | Jika admin tidak reload dashboard, bayaran yang masuk tidak dikemaskini |
| `no_fail_lesen` vs `no_akaun_lesen` kekeliruan | Dua field dengan nama serupa, peranan berbeza | Laporan salah, carian gagal, integration dengan EPBT gagal |
### D3. Risiko Prestasi
| Risiko | Punca | Kesan |
|---|---|---|
| Dashboard lambat bila permohonan banyak | N+1 query ke EPBT `second_mysql` dalam loop | Setiap rekod 'menunggu bayaran proses' buat satu query. 50 rekod = 50 queries setiap kali dashboard dibuka |
| Fail cetakan besar | PHPWord tanpa compression | `.docx` yang besar boleh timeout jika banyak permohonan dalam satu mesyuarat |
### D4. Risiko Kebolehselenggaraan
| Risiko | Punca | Kesan |
|---|---|---|
| Developer baru keliru dengan `asal/` directories | Tiada dokumentasi tentang fail backup | Developer mungkin edit fail lama dalam `asal/` dan hairan kenapa tiada perubahan dalam sistem |
| Perubahan status flow memerlukan tukar 4 tempat | Status strings tersebar | Kesilapan dalam satu tempat tidak akan kelihatan dalam ujian manual biasa |
| `LaporanPrestasiController` tidak boleh diaktifkan | Syntax error | Jika developer cuba aktifkan, akan dapat 500 error |
| Integrasi EPBT sukar dimodifikasi | Logic tersebar dalam 4 controllers | Jika EPBT tukar endpoint atau struktur data, perlu cari dan tukar di banyak tempat |
### D5. Risiko Integrasi
| Risiko | Punca | Kesan |
|---|---|---|
| PBTPay tidak berfungsi jika diaktifkan | Semua stub, tiada implementasi sebenar | Pengguna boleh click butang bayar tetapi proses tidak berlaku, tiada makluman ralat yang berguna |
| `janaBil()` `client_key` tidak disahkan | `'MPJBT'` hardcoded, tapi `test_curl.php` guna SHA-1 hash | Ada kemungkinan format key yang berbeza untuk production |
---
## E. Cadangan Struktur Baru Secara Ringkas
### E1. Pembersihan Segera (Lakukan Sebelum Sebarang Refactor)
Tindakan berikut tidak memerlukan perubahan kepada mana-mana business logic:
1. **PADAM** `public/test_curl.php` — risiko keselamatan kritikal
2. **PINDAH** `mylesen(2).sql` keluar dari folder projek ke lokasi selamat
3. **PADAM** `app/Http/Controllers/Admin/asal/` (4 controllers lama)
4. **PADAM** `routes/asal/web.php`
5. **PADAM** `resources/views/admin/penjaja/asal/` (16 views lama)
6. **PADAM** semua fail dengan nama `-ori.blade.php`, `_lama.blade.php`, `-ori/`
7. **TUKAR** route `/` — dari `view('fahmi')` ke `view('welcome')` atau redirect ke `/utama`
*Selepas setiap tindakan, jalankan semula semua routes untuk pastikan tiada broken link.*
### E2. Cadangan Struktur Direktori Baru
```
app/
Enums/
StatusPermohonan.php ← gantikan string hardcoded
StatusMesyuarat.php
RolePengguna.php
Services/
ApplicationService.php ← business logic permohonan
EpbtService.php ← semua EPBT API + DB calls
BilService.php ← bil proses + lesen
MesyuaratService.php ← mesyuarat + Lampiran B
Http/
Controllers/
Pemohon/ ← public controllers
Admin/
PT/ ← pembantu tadbir
IK/ ← inspektor kesihatan
Pegawai/ ← pp + pengarah
Pengurusan/ ← admin management
Laporan/ ← reports
Api/ ← chrome extension API
Requests/
Permohonan/
Bil/
Mesyuarat/
```
**Rujuk `docs/rebuild-architecture.md` untuk pelan rebuild penuh termasuk status flow, role matrix, dashboard plan, integration design, API design, dan implementation roadmap.**
---
*Dokumen ini adalah hasil audit statik berdasarkan pembacaan kod. Untuk maklumat terkini, sila semak kod semasa.*