01Informasi Dokumen
| Informasi | Nilai |
|---|---|
| Nama Fitur | Disposal Asset (penghapusan aset dari registri aktif) |
| Kode Fitur | PML04 (nomor transaksi PML04-YYYYMMNNN) |
| Modul | Asset Management — Pemeliharaan Aset |
| Backend | Laravel Lumen ^10.0, PHP ^8.1, JWT, Query Builder |
| Database | API/diagram-asset.dbml (tanda [REVISI PML04] / [NEW PML04]) + SQL manual Claude outputs/sql-pml04-disposal.sql. ⚠️ SQL harus dijalankan manual (belum dijalankan saat dokumen ditulis) |
| Mobile | Flutter (professional_flutter_3_35), flutter_bloc, dio, image_picker |
| Web | ⚠️ Belum ditemukan: tidak ada source web di workspace |
| Test otomatis | API tests/Asset/AssetDisposalTest.php (7 test) · Mobile test/asset/asset_disposal_test.dart (13 test) |
| Versi dokumen | 1.2 — 22 Oktober 2026 |
| Dokumen terkait | Perbaikan (PML02), Klaim Garansi (PML03), Pemeliharaan (PML01) |
03Quick Overview
Aset gudang yang rusak dan tidak layak dipertahankan dihapus dari daftar aset aktif lewat tiga langkah. Fase 1: admin meninjau riwayat aset (usia, harga perolehan, jumlah perbaikan dan klaim garansi) lalu menyubmit keputusan: alasan, metode (lelang/jual, donasi, pemusnahan), nilai sisa, catatan; aset menjadi menunggu_disposal. Fase 2: disposal fisik terjadi di luar sistem; admin mencatat hasilnya (pembeli dan harga, penerima donasi, atau cara pemusnahan) beserta bukti wajib. Fase 3: admin menekan Selesaikan Disposal; aset menjadi disposed dan tidak muncul lagi di daftar aktif, sementara datanya tetap tersimpan sebagai arsip audit.
| Aspek | Ringkasan |
|---|---|
| Aktor | Admin/HR saja (tanpa approval) |
| Status transaksi | proses (menunggu disposal) → selesai (dihapus/disposed) |
| Status asset | rusak → menunggu_disposal → disposed |
| Metode | lelang_jual, donasi, pemusnahan |
| Endpoint | 6 (semua admin) |
| Notifikasi | 1 event (disposal.selesai) ke admin |
04Ringkasan Fitur
| No | Fitur | Aktor |
|---|---|---|
| 1 | Pilih aset gudang rusak dengan riwayatnya | Admin |
| 2 | Submit keputusan disposal | Admin |
| 3 | Catat pelaksanaan sesuai metode + bukti (dapat diperbarui) | Admin |
| 4 | Selesaikan disposal (dengan konfirmasi) | Admin |
| 5 | Daftar (Menunggu/Selesai) dan detail | Admin |
| 6 | Kartu "perlu diproses" di beranda admin | Admin |
05Tujuan Fitur
5.1 Tujuan Bisnis (dari dokumen bisnis dan kode)
- Penghapusan aset punya keputusan, alasan, dan bukti yang dapat diaudit.
- Status perantara
menunggu_disposalmencegah penghapusan tanpa bukti pelaksanaan. - Data historis aset tidak dihapus, hanya dikeluarkan dari daftar aktif.
5.2 Tujuan Sistem
- Menjaga aset yang sedang di-disposal tidak bisa dipakai alur lain.
- Memvalidasi field wajib per metode dan bukti.
- Mencatat pelaksana, waktu,
asset_trx_log, dan notifikasi penyelesaian.
06Aktor
| Aktor | Peran |
|---|---|
| Admin/HR (admin asset) | Memutuskan, memilih metode, mencatat pelaksanaan, menyelesaikan |
| Sistem | Mengunci status aset, memvalidasi, menulis log dan notifikasi |
07Hak Akses
Seluruh endpoint hanya untuk admin asset (Asset_m::isAdmin). Non-admin menerima 403. Tidak ada endpoint karyawan/PIC.
08Terminologi
| Istilah | Arti |
|---|---|
| Disposal | Penghapusan aset dari registri aktif |
| MENUNGGU DISPOSAL | Keputusan sudah disubmit, penyelesaian belum dilakukan (status = proses, aset menunggu_disposal) |
| DIHAPUS (DISPOSED) | Disposal selesai (status = selesai, aset disposed) |
| Nilai sisa | Book value aset (opsional) |
| Bukti | Satu foto/scan kwitansi, berita acara, atau foto pemusnahan |
09Business Rules
BR-001 — Aset yang boleh di-disposal
Hanya pemegang_asset = gudang dan status_asset = rusak. Karena proses lain mengubah status (dalam_perbaikan, dalam_proses_klaim, menunggu serah terima), syarat "tidak ada proses aktif" terpenuhi otomatis. Tidak boleh masih dalam onboarding aktif. Pesan: "Hanya asset rusak di gudang yang dapat di-disposal."
BR-002 — Keputusan
alasan_disposal wajib; metode_disposal ∈ tiga nilai; nilai_sisa opsional dan tidak negatif; catatan opsional.
BR-003 — Status perantara
Setelah keputusan, aset menjadi menunggu_disposal dan keluar dari daftar pilihan aset gudang rusak (tidak bisa diajukan dua kali).
BR-004 — Pelaksanaan menurut metode
| Metode | Field wajib |
|---|---|
lelang_jual |
pembeli, harga_jual, tgl_pelaksanaan, bukti |
donasi |
penerima_donasi, tgl_pelaksanaan, bukti |
pemusnahan |
metode_pemusnahan, tgl_pelaksanaan, bukti |
Tanggal pelaksanaan tidak boleh setelah hari ini. Field milik metode lain dikosongkan.
BR-005 — Bukti wajib
Semua metode wajib bukti. Pembaruan tanpa bukti baru mempertahankan bukti lama; bila belum pernah ada bukti, ditolak.
BR-006 — Pelaksanaan dapat diperbarui
Selama status = proses, admin boleh memperbarui data pelaksanaan. Setelah selesai, tidak dapat diubah.
BR-007 — Penyelesaian
Hanya bila pelaksanaan sudah dicatat (tgl_pelaksanaan terisi) dan data masih lengkap sesuai metode. Aset harus masih di gudang dengan status menunggu_disposal. Hasil: aset disposed, transaksi selesai, asset_trx_log disposal_gudang.
BR-008 — Arsip, bukan hapus
Baris transaksi dan baris asset_data (status disposed) tetap ada. Daftar aset admin dan total ringkasan mengecualikan disposed bila filter status kosong; filter status disposed eksplisit tetap menampilkannya.
BR-009 — Penomoran
id = id_company + YYYYMM + 3 digit; no_trx = PML04-YYYYMMNNN; maksimum 999 per bulan per perusahaan.
BR-010 — Skema harus sudah diterapkan
skemaSiap() menjaga dashboard tetap berjalan sebelum SQL diterapkan.
10Business Flow
- Admin membuka Disposal → + → memilih satu aset gudang rusak dan meninjau riwayatnya.
- Admin mengisi alasan, metode, nilai sisa, catatan → Submit Keputusan. Aset
menunggu_disposal. - Disposal fisik dilakukan di luar sistem.
- Admin membuka detail → Catat Pelaksanaan (field sesuai metode + tanggal + bukti).
- Admin menekan Selesaikan Disposal dan mengonfirmasi.
- Aset
disposed; notifikasi ringkasan dikirim ke admin.
11Flow Berdasarkan Role
11.1 Admin/HR
Seluruh alur di atas.
11.2 System
Validasi, kunci status, log, notifikasi, penyaringan daftar aktif.
12Flow Diagram
flowchart TD
A[Aset gudang rusak] --> B[Admin tinjau riwayat]
B --> C[Submit keputusan<br/>alasan, metode, nilai sisa]
C --> D[status proses<br/>asset menunggu_disposal]
D --> E[Disposal fisik di luar sistem]
E --> F{Metode}
F -- lelang_jual --> G[Pembeli, harga, tanggal, bukti]
F -- donasi --> H[Penerima, tanggal, bukti]
F -- pemusnahan --> I[Cara, tanggal, bukti]
G --> J[Pelaksanaan tercatat]
H --> J
I --> J
J --> K[Selesaikan Disposal]
K --> L[status selesai<br/>asset disposed, data diarsipkan]
13Sequence Diagram
sequenceDiagram
actor Admin
participant App as Mobile
participant API
participant DB
Admin->>App: Pilih aset + isi keputusan
App->>API: POST asset/disposal
API->>DB: insert trx (proses), asset menunggu_disposal
Admin->>App: Catat pelaksanaan + foto bukti
App->>API: POST asset/disposal/{id}/pelaksanaan
API->>DB: simpan pelaksanaan + file_bukti
Admin->>App: Selesaikan Disposal (konfirmasi)
App->>API: POST asset/disposal/{id}/selesai
API->>DB: trx selesai, asset disposed, asset_trx_log
API-->>Admin: Notifikasi disposal.selesai
14Status Transaksi
status |
Kondisi | Label di aplikasi | Tab |
|---|---|---|---|
proses |
tgl_pelaksanaan kosong |
Menunggu Disposal | Menunggu |
proses |
tgl_pelaksanaan terisi |
Siap Diselesaikan | Menunggu |
selesai |
— | Dihapus (Disposed) | Selesai |
Tidak ada kolom fase terpisah; tahap pelaksanaan ditandai terisinya tgl_pelaksanaan.
15State Transition
stateDiagram-v2
[*] --> MenungguDisposal: Submit keputusan
MenungguDisposal --> SiapDiselesaikan: Catat pelaksanaan
SiapDiselesaikan --> SiapDiselesaikan: Perbarui pelaksanaan
SiapDiselesaikan --> Dihapus: Selesaikan Disposal
Dihapus --> [*]
16Struktur Database
| Tabel | Fungsi |
|---|---|
asset_trx_disposal |
Satu baris per disposal (keputusan, pelaksanaan, penyelesaian) |
asset_data |
Status aset (menunggu_disposal, disposed) |
asset_trx_log |
Jejak disposal_gudang |
Tabel lama asset_trx_disposal_item dan kolom approval dihapus (struktur lama berbasis approval, belum dipakai API).
17Relasi Database
erDiagram
asset_trx_disposal }o--|| asset_data : "asset"
asset_trx_disposal }o--o| asset_lokasi_m : "gudang asal"
asset_data ||--o{ asset_trx_log : "jejak"
18Data Dictionary
18.1 asset_trx_disposal
| Kolom | Tipe | Keterangan |
|---|---|---|
id_asset_trx_disposal |
varchar(50) PK | id_company+YYYYMM+3 digit |
no_trx |
varchar(50) | PML04-YYYYMMNNN |
id_asset_data |
varchar(50) FK | Aset |
nama_asset, deskripsi_asset, kategori_asset |
varchar | Snapshot saat keputusan |
id_asset_lokasi_m_asal |
varchar(50) FK null | Gudang asal |
alasan_disposal |
varchar(255) | Wajib |
metode_disposal |
enum | lelang_jual, donasi, pemusnahan |
nilai_sisa |
decimal(18,2) null | Book value, opsional |
catatan |
text null | Opsional |
tgl_pelaksanaan |
date null | NULL = pelaksanaan belum dicatat |
pembeli, harga_jual |
varchar, decimal(18,2) | Metode lelang/jual |
penerima_donasi |
varchar(255) | Metode donasi |
metode_pemusnahan |
varchar(255) | Metode pemusnahan |
file_bukti |
varchar(255) | Key object bukti (wajib semua metode) |
tgl_selesai, user_selesai |
timestamp, varchar | Klik Selesaikan Disposal |
status |
enum proses/selesai | Status transaksi |
id_company, tgl_input, user_input, tgl_update, user_update |
Audit; tgl_input/user_input = keputusan |
18.2 asset_trx_log (jenis_log = disposal_gudang)
Ditulis saat penyelesaian: posisi gudang → gudang, kondisi_awal/kondisi_akhir = kondisi aset, user_input = pelaksana.
19Database Index
| Index | Kolom | Fungsi |
|---|---|---|
atd_company_status_idx |
id_company, status, tgl_input, id |
Daftar per tab |
atd_company_asset_status_idx |
id_company, id_asset_data, status |
Cek disposal aktif dan riwayat per aset |
| FK | id_asset_data, id_asset_lokasi_m_asal |
Integritas |
20Backend Architecture
| Lapisan | File |
|---|---|
| Route | RouteAsset.php (disposal/*) |
| Controller | Asset/Admin/AssetAdminDisposal_c.php |
| Model | Models/Asset/AssetDisposal_m.php |
| Notifikasi | Support/Asset/AssetNotification.php (disposal.selesai, placeholder {aset}, {metode}) |
| Pendukung | Asset_c::mutateAssetTrx (event null = tanpa notifikasi), Asset_c::uploadAssetImage + AssetImageStorage (object storage) |
| Dashboard | AssetAdmin_m (disposal_perlu_proses, disposal_pending; pengecualian disposed) |
21Backend Business Logic
21.1 Keputusan — store
Kunci perusahaan; validasi alasan, metode, nilai sisa; kunci aset (lockForUpdate), cek BR-001; nomor urut; insert transaksi (snapshot aset dan gudang asal); aset → menunggu_disposal.
21.2 Pelaksanaan — pelaksanaan
Hanya status = proses. pelaksanaanFields memvalidasi tanggal, bukti, dan field sesuai metode; kolom metode lain dikosongkan; harga_jual dinormalisasi.
21.3 Penyelesaian — selesai
Validasi ulang kelengkapan pelaksanaan, cek aset masih menunggu_disposal di gudang; aset → disposed; asset_trx_log; transaksi selesai dengan tgl_selesai/user_selesai.
21.4 Query baca
getPage (tab proses|selesai, paginasi limit+1), getDetail (aset + riwayat, keputusan, pelaksanaan, penyelesaian, aksi), getAdminOptions (aset gudang rusak + metode), getPendingForAdmin, countPendingForAdmin.
21.5 Riwayat aset
riwayat() menghitung usia (bulan) dari tgl_perolehan, harga_perolehan, serta jumlah baris di asset_trx_perbaikan_item dan asset_trx_garansi_item untuk aset tersebut (0 bila tabel belum ada). Biaya perbaikan tidak dicatat sistem.
22Dampak terhadap asset_data
| Kejadian | status_asset |
Kolom lain |
|---|---|---|
| Keputusan disubmit | rusak → menunggu_disposal |
— |
| Disposal selesai | → disposed |
tgl_update, user_update |
Aset disposed tidak muncul di daftar aset admin bawaan dan tidak dihitung pada total_asset.
23API Documentation
| Kode | Method | Path | Fungsi |
|---|---|---|---|
| API-001 | GET | asset/disposal/options |
Aset gudang rusak + metode |
| API-002 | GET | asset/disposal |
Daftar |
| API-003 | POST | asset/disposal |
Submit keputusan |
| API-004 | POST | asset/disposal/{id}/pelaksanaan |
Catat/perbarui pelaksanaan |
| API-005 | POST | asset/disposal/{id}/selesai |
Selesaikan disposal |
| API-006 | GET | asset/disposal/{id} |
Detail |
24Detail Setiap API
API-001 — Opsi
{"asset": [{"id_asset_data": "LP-1", "kode_asset": "KD-LP-1", "nama_asset": "Laptop Lama",
"kategori_asset": "Laptop", "merek": "", "keterangan_rusak": "Mati total", "lokasi_gudang": "Gudang Utama",
"riwayat": {"tgl_perolehan": "2019-10-01", "usia_bulan": 85, "harga_perolehan": 15000000,
"jumlah_perbaikan": 2, "jumlah_klaim_garansi": 1}}],
"metode": [{"value": "lelang_jual", "label": "Lelang / Jual"}]}
API-002 — Daftar
Query tab (proses|selesai), limit (1–50, default 20), offset. Respons {filters, records[], pagination}; record: id, no_trx, status, status_label, tab, id_asset_data, nama_asset, kategori_asset, metode_disposal, metode_label, alasan_disposal, pelaksanaan_tercatat, tgl_input.
API-003 — Keputusan
{"id_asset_data": "LP-1", "alasan_disposal": "Motherboard rusak total",
"metode_disposal": "donasi", "nilai_sisa": 250000, "catatan": "Data sudah dihapus"}
Respons CREATED {id, no_trx, status, status_label, tab}.
API-004 — Pelaksanaan
{"tgl_pelaksanaan": "2026-10-09", "pembeli": "PT Pembeli", "harga_jual": 1500000,
"penerima_donasi": "", "metode_pemusnahan": "", "file_bukti": "<base64 atau file multipart>"}
Hanya field milik metode transaksi yang dipakai. file_bukti kosong = bukti lama dipertahankan. Bukti disimpan di object storage dengan key <company>/asset/disposal/<id_karyawan>/<hex>.<ext>; kolom file_bukti menyimpan key tersebut.
API-005 — Selesai
Tanpa body. Respons {id, no_trx, status: "selesai", status_label: "Dihapus (Disposed)"}.
API-006 — Detail
Grup asset (dengan riwayat), keputusan, pelaksanaan (tercatat, file_bukti_url), penyelesaian, dan aksi: {pelaksanaan, selesai}.
25Mobile Architecture
| Lapisan | File |
|---|---|
| Model | lib/Repository/Model/Asset/mAssetDisposal.dart |
| API client | lib/Repository/Service/assetDisposalApi.dart |
| Bloc | lib/Bloc/Asset/AssetDisposalBloc.dart |
| UI | lib/UI/Modul/Asset/Pemeliharaan/Disposal/* (termasuk AssetDisposalProsesWidgets; pratinjau foto layar penuh memakai Pemeliharaan/AssetFotoPreview.dart) |
| Teks | grup disposal di assets/{id,en}/asset.json (81 kunci) |
| Navigasi | NavigatorBloc (NavigateToAssetAdminDisposal, …DetailDisposal, …BuatDisposal), router.dart |
26Mobile Screen
26.1 Daftar — HalamanAssetDisposal
Tab Menunggu/Selesai, AssetDisposalRecordCard, tombol +.
26.2 Buat — HalamanBuatDisposal
Pilih satu aset (radio); aset terpilih menampilkan kartu riwayat; Lanjut membuka sheet keputusan (alasan, metode chip, nilai sisa, catatan).
26.3 Detail — HalamanDetailAssetDisposal
Kartu status, petunjuk langkah berikutnya, aset + riwayat, keputusan, pelaksanaan (dengan foto bukti), penyelesaian. Tombol bawah: Catat/Ubah Data Pelaksanaan dan Selesaikan Disposal (dialog konfirmasi).
26.4 Sheet pelaksanaan
Field mengikuti metode, picker tanggal (tidak boleh melewati hari ini), foto bukti dari kamera/galeri.
26.5 Entry point
Beranda admin: menu Disposal dan kartu "perlu diproses" (langkah berikutnya: Catat pelaksanaan / Selesaikan disposal). Notifikasi membuka asset_admin_detail_disposal.
27Mobile → API
| Aksi UI | Method | Endpoint |
|---|---|---|
| Muat daftar | GET | asset/disposal?tab= |
| Muat opsi | GET | asset/disposal/options |
| Muat detail | GET | asset/disposal/{id} |
| Submit keputusan | POST | asset/disposal |
| Simpan pelaksanaan | POST | asset/disposal/{id}/pelaksanaan |
| Selesaikan | POST | asset/disposal/{id}/selesai |
28API → Mobile
Respons dipetakan ke AssetDisposalRecord, Page, Options. Daftar mengirim data datar, detail mengirim grup; model menangani keduanya. 422 tampil snackbar warning, lainnya error; sukses menutup halaman dengan pesan dan memuat ulang daftar/ringkasan.
29Mapping Mobile → API → Database
| Field UI | Field API | Kolom DB |
|---|---|---|
| Aset terpilih | id_asset_data |
id_asset_data |
| Alasan | alasan_disposal |
alasan_disposal |
| Metode | metode_disposal |
metode_disposal |
| Nilai sisa | nilai_sisa |
nilai_sisa |
| Tanggal pelaksanaan | tgl_pelaksanaan |
tgl_pelaksanaan |
| Pembeli / Harga jual | pembeli / harga_jual |
pembeli / harga_jual |
| Penerima donasi | penerima_donasi |
penerima_donasi |
| Cara pemusnahan | metode_pemusnahan |
metode_pemusnahan |
| Foto bukti | file_bukti |
file_bukti (key object) |
30Mapping Database → API → Mobile
| Kolom DB | Field API | Tampil di |
|---|---|---|
status, tgl_pelaksanaan |
status, pelaksanaan_tercatat |
Badge fase |
file_bukti |
pelaksanaan.file_bukti_url |
Gambar bukti di detail |
user_selesai, tgl_selesai |
penyelesaian.* |
Bagian penyelesaian |
harga_perolehan, tgl_perolehan |
asset.riwayat.* |
Kartu riwayat |
31Validation
31.1 Mobile
Alasan dan metode wajib; nilai sisa angka tidak negatif; pada pelaksanaan: field metode, tanggal, dan bukti wajib.
31.2 Backend
Aturan Laravel: id_asset_data wajib, alasan_disposal wajib ≤255, metode_disposal ∈ tiga nilai, nilai_sisa numerik ≥ 0, tgl_pelaksanaan Y-m-d, harga_jual numerik ≥ 0. Aturan bisnis di model (§9).
31.3 Database
Enum metode_disposal, status; FK aset dan gudang.
32Error Handling
| Kondisi | Kode | Contoh pesan |
|---|---|---|
| Validasi request | 422 | "Alasan disposal wajib diisi." |
| Aturan bisnis | 422 BUSINESS_RULE_VIOLATION |
"Data pelaksanaan disposal belum dicatat." |
| Bukan admin | 403 | "Pengguna tidak memiliki akses admin asset." |
| Tidak ditemukan | 404 | "Disposal tidak ditemukan." |
| Sistem | 500 SYSTEM_ERROR |
Pesan umum |
33Audit Trail
user_input/tgl_input = keputusan, user_update/tgl_update = pelaksanaan terakhir, user_selesai/tgl_selesai = penyelesaian (format nik - nama). asset_trx_log disposal_gudang saat selesai.
34Notification
| Event | Penerima | Isi | Rute mobile |
|---|---|---|---|
disposal.selesai |
Seluruh admin asset | "Disposal {no_trx} selesai: {aset} ({metode}) telah dihapus dari daftar asset aktif." | asset_admin_detail_disposal |
Pencatatan pelaksanaan tidak mengirim notifikasi. Notifikasi dikirim setelah transaksi commit.
35Security
JWT; semua endpoint admin-only; filter id_company; transaksi + lockForUpdate; bukti diunggah ke bucket MinIO privat lewat Asset_c::uploadAssetImage (AssetImageStorage): hanya JPEG/PNG/WebP valid, maks 5 MB dan 25 megapiksel; key memuat id_company dan id_karyawan dari JWT; URL bukti berupa presigned URL berumur singkat (lihat dokumentasi-minio-object-storage.md).
36Manual Book User/Admin
A. Membuat keputusan disposal
- Beranda Asset → Disposal → tombol +.
- Pilih aset; tinjau usia, harga perolehan, jumlah perbaikan dan klaim.
- Lanjut → isi alasan, pilih metode, (opsional) nilai sisa dan catatan → Submit Keputusan.
B. Mencatat pelaksanaan
- Setelah disposal fisik selesai, buka transaksi di tab Menunggu.
- Catat Pelaksanaan: isi field sesuai metode, tanggal, dan foto bukti → Simpan.
- Data dapat diubah lagi lewat Ubah Data Pelaksanaan sebelum diselesaikan.
C. Menyelesaikan disposal
Selesaikan Disposal → baca konfirmasi → konfirmasi. Aset berstatus DIHAPUS dan hilang dari daftar aktif.
37Kondisi Button
| Tombol | Syarat |
|---|---|
| Catat / Ubah Data Pelaksanaan | status = proses (aksi.pelaksanaan) |
| Selesaikan Disposal | status = proses dan pelaksanaan tercatat (aksi.selesai) |
| Lanjut (halaman buat) | Satu aset dipilih |
38Testing Scenario
| No | Skenario | Hasil |
|---|---|---|
| T-01 | Aset karyawan / tersedia / tidak ada | Ditolak |
| T-02 | Alasan kosong, metode salah, nilai sisa negatif | Ditolak |
| T-03 | Keputusan valid | PML04-…, aset menunggu_disposal, hilang dari opsi |
| T-04 | Pelaksanaan tanpa pembeli/harga/penerima/cara/bukti/tanggal | Ditolak sesuai metode |
| T-05 | Tanggal pelaksanaan besok | Ditolak |
| T-06 | Perbarui tanpa bukti baru | Bukti lama dipertahankan |
| T-07 | Field metode lain ikut dikirim | Dikosongkan |
| T-08 | Selesai sebelum pelaksanaan | Ditolak |
| T-09 | Selesai valid | Aset disposed, asset_trx_log, transaksi selesai |
| T-10 | Ubah/selesai ulang setelah selesai | Ditolak |
| T-11 | Status aset berubah di luar alur | Ditolak |
| T-12 | Alur penuh via route; non-admin | 200; 403 |
Cakupan otomatis: API 7 test, mobile 13 test (teks id/en, parsing, payload, bloc, detail 320/400 px, konfirmasi selesai, validasi sheet, alur buat).
39Temuan Ketidaksesuaian
| ID | Temuan |
|---|---|
| INC-01 | Dokumen bisnis tidak menyebut batasan "hanya aset gudang"; implementasi menegakkannya |
| INC-02 | Dokumen bisnis menyebut tahap pelaksanaan per metode sebagai langkah; implementasi memakai satu tabel dengan kolom per metode dan penanda tgl_pelaksanaan |
| INC-03 | Dokumen menyebut "memindahkan data ke arsip"; implementasi mengarsipkan lewat status disposed (tanpa tabel arsip terpisah) |
| INC-04 | PML01 (hasil 5), PML02 (tidak bisa diperbaiki/unit lama diganti), dan PML03 (ditolak) hanya mencatat nama tabel asset_trx_disposal; transaksi disposal tidak dibuat otomatis |
| INC-05 | Tidak ada pembatalan disposal dari menunggu_disposal |
| INC-06 | Bukti hanya gambar; dokumen PDF belum didukung |
40Edge Case
- Dua admin memilih aset yang sama: yang kedua gagal karena aset sudah
menunggu_disposal. - Status aset berubah antara keputusan dan penyelesaian: penyelesaian ditolak.
- Harga jual berformat koma (
1500,50): dinormalisasi di mobile sebelum dikirim. - Tabel PML02/PML03 belum ada: riwayat menampilkan 0 tanpa error.
41Potential Bug / Technical Risk
RISK-001 — Skema dari SQL manual (TINGGI)
Endpoint gagal bila sql-pml04-disposal.sql belum dijalankan. Jalankan setelah SQL PML01 gudang, PML03, dan PML02 (blok enum menulis ulang daftar penuh).
RISK-002 — Tidak ada pembatalan (SEDANG)
Keputusan yang salah mengunci aset di menunggu_disposal; perbaikan membutuhkan intervensi manual di database.
RISK-003 — Penyelesaian tidak dapat dibatalkan (SEDANG)
Dialog konfirmasi memitigasi di UI; di API tidak ada rollback.
RISK-004 — Bukti lama di disk lokal (RENDAH)
Bukti baru disimpan di object storage. Bukti lama di UPLOAD_DIR tetap terbaca lewat fallback URL lokal. Object lama tidak dihapus saat bukti diganti; object baru yang yatim dibersihkan otomatis bila transaksi gagal.
RISK-005 — Kapasitas penomoran 999/bulan (RENDAH)
Batas 999 transaksi per bulan per perusahaan.
42Dependency
Asset_m/Asset_c, AssetImageStorage, AssetNotification, AssetPemeliharaanKaryawan_m (format tanggal), tabel asset_data, asset_kategori_m, asset_lokasi_m, asset_trx_log; asset_trx_perbaikan_item dan asset_trx_garansi_item (riwayat, opsional).
43Source Code Reference
| Area | Lokasi |
|---|---|
| Model | API/app/Models/Asset/AssetDisposal_m.php |
| Controller | API/app/Http/Controllers/Asset/Admin/AssetAdminDisposal_c.php |
| Route | API/routes/modules/asset/RouteAsset.php |
| SQL | Claude outputs/sql-pml04-disposal.sql |
| Test API | API/tests/Asset/AssetDisposalTest.php |
| Mobile UI | MOBILE/lib/UI/Modul/Asset/Pemeliharaan/Disposal/ |
| Test mobile | MOBILE/test/asset/asset_disposal_test.dart |
44Developer Notes
- Desain disederhanakan dari struktur header+item: satu aset per transaksi, satu tabel.
mutateAssetTrxmenerima eventnulluntuk langkah tanpa notifikasi.pelaksanaanFieldsdipakai ulang olehselesaiuntuk memastikan data tetap lengkap.- Teks UI memakai
AssetDisposalText.t(key); kunci dinamis:fase_*,hint_*,metode_*,bukti_*,tgl_pelaksanaan_*,aksi_*.
45End-to-End Traceability
| Kebutuhan | Rule | API | Mobile | DB |
|---|---|---|---|---|
| Hanya aset rusak di gudang | BR-001 | API-003 | Halaman buat | asset_data.status_asset, pemegang_asset |
| Bukti wajib | BR-005 | API-004 | Sheet pelaksanaan | file_bukti |
| Status perantara | BR-003 | API-003 | Badge Menunggu | asset_data.status_asset = menunggu_disposal |
| Arsip bukan hapus | BR-008 | API-005 | Tab Selesai | status = selesai, disposed |
45.4 Walkthrough Lengkap dengan Contoh Data
Bagian ini mengikuti satu disposal dari keputusan sampai asset dihapus dari daftar aktif, langkah demi langkah: apa yang dilakukan admin di mobile, apa yang dikirim, apa yang diproses API, dan apa yang tersimpan di database. Data bersifat sintetis, tetapi format ID, nama kolom, urutan proses, label status, dan pesan diambil dari kode (AssetDisposal_m, AssetAdminDisposal_c, AssetNotification). Disposal hanya dilakukan admin (tanpa approval) dan tidak ada PIC.
Skenario: Laptop lama di gudang rusak total dan tidak layak dipertahankan. Admin meninjau riwayatnya lalu memutuskan disposal dengan metode Lelang / Jual. Setelah laptop terjual, admin mencatat pelaksanaan (pembeli, harga, tanggal, bukti), memperbaruinya satu kali karena harga terkoreksi, lalu menyelesaikan disposal.
| Langkah | Pelaku | Aksi | Endpoint |
|---|---|---|---|
| 0 | Admin | Memuat asset gudang rusak beserta riwayat | API-001 GET /asset/disposal/options |
| 1 | Admin | Submit Keputusan | API-003 POST /asset/disposal |
| 2 | Admin | Catat Pelaksanaan + bukti | API-004 POST /asset/disposal/{id}/pelaksanaan |
| 3 | Admin | Perbarui Pelaksanaan | API-004 |
| 4 | Admin | Selesaikan Disposal | API-005 POST /asset/disposal/{id}/selesai |
Data awal
Company CMP001. Keputusan diambil 22 Oktober 2026, periode ID 202610. Belum ada transaksi disposal, log, atau notifikasi pada periode ini.
asset_data A70: CMP00120260100070 (LPT-0070), Laptop Lama, kategori Laptop, pemegang_asset = gudang, status_asset = rusak, kondisi_awal = rusak, ket_rusak = Mati total, id_asset_lokasi_m = CMP001202601001 (Gudang Pusat), tgl_perolehan = 2019-10-01, harga_perolehan = 15000000. Asset ini pernah 2 kali masuk Perbaikan dan 1 kali Klaim Garansi.
Admin: KRY0099 (ADM-001, Siti Admin) sebagai pelaksana, KRY0100 (ADM-002, Rudi Admin).
Penomoran: id_company + YYYYMM + 3 digit, no_trx = PML04-YYYYMMNNN; log dan notifikasi 5 digit. Satu transaksi = satu asset; tidak ada tabel item terpisah dan tidak ada kolom fase: tahap pelaksanaan ditandai terisinya tgl_pelaksanaan.
Langkah 0 — Admin memuat opsi dan riwayat
Mobile: Pemeliharaan (admin) → Disposal → buat → AssetDisposalApi memuat opsi.
Dikirim
GET /asset/disposal/options
Authorization: Bearer <access_token_admin>
Diproses API (AssetDisposal_m::getAdminOptions): asset gudang berstatus rusak (asset yang sudah menunggu_disposal tidak muncul lagi), masing-masing dengan riwayat: usia dalam bulan dari tgl_perolehan, harga_perolehan, jumlah baris di asset_trx_perbaikan_item, dan jumlah baris di asset_trx_garansi_item (0 bila tabel belum ada). Biaya perbaikan tidak dicatat sistem. Ditambah daftar metode.
Database: hanya SELECT.
Diterima mobile (ringkas)
{ "asset": [ { "id_asset_data": "CMP00120260100070", "kode_asset": "LPT-0070", "nama_asset": "Laptop Lama",
"kategori_asset": "Laptop", "keterangan_rusak": "Mati total", "lokasi_gudang": "Gudang Pusat",
"riwayat": { "tgl_perolehan": "2019-10-01", "usia_bulan": 84, "harga_perolehan": 15000000,
"jumlah_perbaikan": 2, "jumlah_klaim_garansi": 1 } } ],
"metode": [ { "value": "lelang_jual", "label": "Lelang / Jual" }, { "value": "donasi", "label": "Donasi" },
{ "value": "pemusnahan", "label": "Pemusnahan" } ] }
Langkah 1 — Admin menyubmit keputusan
Mobile: admin memilih laptop, meninjau riwayat, mengisi alasan, memilih metode, nilai sisa, dan catatan → Submit → AssetDisposalApi.store.
Dikirim
POST /asset/disposal
{ "id_asset_data": "CMP00120260100070", "alasan_disposal": "Motherboard rusak total",
"metode_disposal": "lelang_jual", "nilai_sisa": 250000, "catatan": "Data sudah dihapus" }
Diproses API (AssetAdminDisposal_c::storeDisposal → store, satu DB::transaction)
1. Validasi: alasan_disposal wajib, metode_disposal ∈ lelang_jual/donasi/pemusnahan; nilai_sisa opsional dan tidak negatif ("Nilai sisa harus berupa angka tidak negatif.").
2. lockCompany; cek admin; asset dikunci FOR UPDATE dan harus tepat satu ("Asset tidak ditemukan.").
3. Asset harus gudang dan rusak ("Hanya asset rusak di gudang yang dapat di-disposal."); tidak ada di onboarding aktif. Karena proses lain mengubah status (dalam_perbaikan, dalam_proses_klaim, menunggu serah terima), syarat "tidak ada proses aktif" terpenuhi otomatis.
4. Generate ID: CMP001202610001, no_trx = PML04-202610001.
5. INSERT transaksi (snapshot asset dan gudang asal, status = proses, tgl_pelaksanaan NULL) dan UPDATE asset_data → menunggu_disposal.
6. Tidak ada notifikasi pada tahap ini (event null).
Hasil database
asset_trx_disposal
| Kolom | Nilai |
|---|---|
| id_asset_trx_disposal / no_trx | CMP001202610001 / PML04-202610001 |
| id_asset_data / nama_asset / deskripsi_asset / kategori_asset | A70 / Laptop Lama / '' / Laptop |
| id_asset_lokasi_m_asal | CMP001202601001 |
| alasan_disposal | Motherboard rusak total |
| metode_disposal | lelang_jual |
| nilai_sisa | 250000.00 |
| catatan | Data sudah dihapus |
| tgl_pelaksanaan / pembeli / harga_jual / penerima_donasi / metode_pemusnahan / file_bukti | NULL / '' / NULL / '' / '' / '' |
| status | proses |
| tgl_selesai / user_selesai | NULL / '' |
| tgl_input / user_input | 2026-10-22 09:00:00 / ADM-001 - Siti Admin |
asset_data A70: status_asset rusak → menunggu_disposal, tgl_update = 09:00:00, user_update = ADM-001 - Siti Admin.
Diterima mobile (HTTP 201)
{ "code": "CREATED", "message": "…",
"data": { "id": "CMP001202610001", "no_trx": "PML04-202610001", "status": "proses",
"status_label": "Menunggu Disposal", "tab": "proses" } }
Asset kini hilang dari opsi sehingga tidak dapat diajukan dua kali.
Langkah 2 — Admin mencatat pelaksanaan
Mobile: detail disposal (aksi.pelaksanaan = true) → form pelaksanaan sesuai metode Lelang / Jual: tanggal, pembeli, harga jual, dan foto bukti (kamera atau galeri) → Simpan.
Dikirim (multipart karena ada file)
POST /asset/disposal/CMP001202610001/pelaksanaan
{ "tgl_pelaksanaan": "2026-10-23", "pembeli": "PT Pembeli Sejahtera", "harga_jual": 1500000,
"penerima_donasi": "", "metode_pemusnahan": "", "file_bukti": "<file multipart atau base64>" }
Diproses API (buktiPath lalu pelaksanaan)
1. Validasi controller: tgl_pelaksanaan wajib Y-m-d; harga_jual numerik ≥ 0.
2. prepareImageUpload memastikan transaksi masih proses sebelum file diunggah ("Disposal sudah selesai dan tidak dapat diubah."). File diunggah dengan key CMP001/asset/disposal/<id_karyawan>/<hex>.<ext>; hanya key yang disimpan. File tidak valid → "File bukti tidak valid.".
3. pelaksanaanFields menerapkan aturan menurut metode transaksi (bukan menurut input): tanggal wajib dan tidak boleh setelah hari ini; bukti wajib (bila tidak mengunggah baru, bukti lama dipertahankan; bila belum pernah ada, ditolak). Untuk lelang_jual: pembeli dan harga_jual wajib. Field milik metode lain dikosongkan.
4. UPDATE transaksi dengan kolom pelaksanaan, tgl_update, user_update. asset_data belum berubah. Tidak ada notifikasi.
Hasil database
Kolom (asset_trx_disposal) |
Sebelum | Sesudah |
|---|---|---|
| tgl_pelaksanaan | NULL | 2026-10-23 |
| pembeli | '' |
PT Pembeli Sejahtera |
| harga_jual | NULL | 1500000.00 |
| penerima_donasi / metode_pemusnahan | '' / '' |
'' / '' |
| file_bukti | '' |
CMP001/asset/disposal/KRY0099/9f3a1c2e.jpg |
| status | proses | proses |
| tgl_update / user_update | NULL / '' |
2026-10-23 10:00:00 / ADM-001 - Siti Admin |
Diterima mobile (HTTP 200): status = proses, status_label = "Siap Diselesaikan" (karena tgl_pelaksanaan sudah terisi), tab = "proses", message "Data pelaksanaan disposal berhasil disimpan.". Detail menampilkan pelaksanaan.tercatat = true dan file_bukti_url.
Langkah 3 — Admin memperbarui pelaksanaan
Pelaksanaan boleh diperbarui selama status = proses. Admin mengoreksi harga jual.
Dikirim: POST /asset/disposal/CMP001202610001/pelaksanaan dengan harga_jual = 1750000, tanpa file_bukti baru dan tanpa mengulang field lain.
Diproses API: pelaksanaanFields memakai nilai lama untuk field yang tidak dikirim (pakai(key, nilai lama)), sehingga pembeli dan tanggal tetap; bukti lama dipertahankan karena file_bukti kosong. Hasil database: harga_jual 1500000.00 → 1750000.00, tgl_update = 2026-10-23 10:30:00; kolom lain tetap.
Langkah 4 — Admin menyelesaikan disposal
Mobile: Selesaikan Disposal (dialog konfirmasi) → AssetDisposalApi.selesai.
Dikirim
POST /asset/disposal/CMP001202610001/selesai
Tanpa body.
Diproses API (selesai)
1. Transaksi harus proses ("Disposal sudah selesai.").
2. tgl_pelaksanaan harus terisi ("Data pelaksanaan disposal belum dicatat."); pelaksanaanFields dijalankan ulang untuk memastikan data masih lengkap menurut metode.
3. Asset dikunci dan harus masih gudang dengan status menunggu_disposal ("Status asset sudah berubah atau tidak lagi berada di gudang.").
4. UPDATE asset_data → disposed; INSERT asset_trx_log (jenis_log = disposal_gudang, posisi gudang → gudang, kondisi tidak berubah).
5. UPDATE transaksi: status = selesai, tgl_selesai, user_selesai, tgl_update, user_update.
6. Notifikasi disposal.selesai ke seluruh admin.
Hasil database
| Tabel | Perubahan |
|---|---|
| asset_data A70 | status_asset menunggu_disposal → disposed; user_update = ADM-001 - Siti Admin |
| asset_trx_disposal | status proses → selesai; tgl_selesai = 2026-10-23 11:00:00; user_selesai = ADM-001 - Siti Admin |
asset_trx_log (1 baris)
| Kolom | Nilai |
|---|---|
| id_asset_trx_log | CMP00120261000001 |
| id_asset_data / jenis_log | A70 / disposal_gudang |
| id_transaksi / nama_tabel_transaksi | CMP001202610001 / asset_trx_disposal |
| kondisi_awal / kondisi_akhir | rusak / rusak |
| posisi_awal / posisi_akhir | gudang / gudang |
| id_pemegang / nama_pemegang | '' / '' |
| user_input | ADM-001 - Siti Admin |
notif_data (2 baris, satu per admin)
| id_notif | id_karyawan | id_ref | rute | ket_notif_id |
|---|---|---|---|---|
| CMP00120261000001 | KRY0099 | CMP001202610001 | asset_admin_detail_disposal | Disposal PML04-202610001 selesai: Laptop Lama (Lelang / Jual) telah dihapus dari daftar asset aktif. |
| CMP00120261000002 | KRY0100 | CMP001202610001 | asset_admin_detail_disposal | Disposal PML04-202610001 selesai: Laptop Lama (Lelang / Jual) telah dihapus dari daftar asset aktif. |
Diterima mobile (HTTP 200)
{ "code": "UPDATED", "message": "Disposal berhasil diselesaikan.",
"data": { "id": "CMP001202610001", "no_trx": "PML04-202610001", "status": "selesai",
"status_label": "Dihapus (Disposed)", "tab": "selesai" } }
Kondisi akhir data
asset_trx_disposal: CMP001202610001 / PML04-202610001, status = selesai, metode_disposal = lelang_jual, harga_jual = 1750000.00, tgl_pelaksanaan = 2026-10-23, bukti tersimpan sebagai key object storage.
asset_data A70: status_asset = disposed. Baris asset tidak dihapus (arsip): daftar asset admin dan total ringkasan mengecualikan disposed bila filter status kosong, tetapi filter status disposed eksplisit tetap menampilkannya. asset_trx_log: 1 baris. notif_data: 2 baris.
Variasi
Donasi. metode_disposal = donasi: pelaksanaan wajib penerima_donasi ("Penerima donasi wajib diisi."), tanggal, dan bukti; pembeli, harga_jual, dan metode_pemusnahan dikosongkan. Notifikasi menyebut "(Donasi)".
Pemusnahan. metode_disposal = pemusnahan: pelaksanaan wajib metode_pemusnahan ("Metode pemusnahan wajib diisi."), tanggal, dan bukti; field metode lain dikosongkan. Notifikasi menyebut "(Pemusnahan)".
Disposal sebagai tindak lanjut modul lain. Asset rusak di gudang yang berasal dari Pemeliharaan (hasil 4–5), Perbaikan ("diganti unit baru" atau "tidak bisa diperbaiki"), atau Klaim Garansi (ditolak dengan tindak lanjut disposal) hanya ditandai nama_tabel_tindak_lanjut = asset_trx_disposal; transaksi Disposal tetap harus dibuat manual lewat langkah 1.
Kasus gagal yang umum
| Situasi | Langkah | HTTP | Pesan | Perubahan database |
|---|---|---|---|---|
| Asset belum dipilih / tidak ditemukan | 1 | 422 | Asset wajib dipilih. / Asset tidak ditemukan. | Tidak ada |
| Alasan kosong | 1 | 422 | Alasan disposal wajib diisi. | Tidak ada |
| Metode kosong atau tidak dikenal | 1 | 422 | Metode disposal wajib dipilih. / Metode disposal tidak valid. | Tidak ada |
| Nilai sisa negatif atau bukan angka | 1 | 422 | Nilai sisa harus berupa angka tidak negatif. | Tidak ada |
Asset bukan rusak di gudang (mis. sudah menunggu_disposal) |
1 | 422 | Hanya asset rusak di gudang yang dapat di-disposal. | Tidak ada |
| Asset di onboarding aktif | 1 | 422 | Asset sedang diproses pada onboarding. | Tidak ada |
| Tanggal pelaksanaan kosong | 2 | 422 | Tanggal pelaksanaan wajib diisi. | Tidak ada |
| Tanggal pelaksanaan setelah hari ini | 2 | 422 | Tanggal pelaksanaan tidak boleh setelah hari ini. | Tidak ada |
| Bukti belum pernah diunggah | 2 | 422 | Bukti pelaksanaan wajib diunggah. | Tidak ada |
| File bukti tidak valid | 2 | 422 | File bukti tidak valid. | Tidak ada (file tidak tersimpan) |
| Pembeli atau harga jual kosong (lelang/jual) | 2 | 422 | Nama pembeli wajib diisi. / Harga jual wajib diisi. | Tidak ada |
| Penerima donasi kosong (donasi) | 2 | 422 | Penerima donasi wajib diisi. | Tidak ada |
| Metode pemusnahan kosong (pemusnahan) | 2 | 422 | Metode pemusnahan wajib diisi. | Tidak ada |
| Mengubah pelaksanaan setelah selesai | 2, 3 | 422 | Disposal sudah selesai dan tidak dapat diubah. | Tidak ada |
| Selesaikan sebelum pelaksanaan dicatat | 4 | 422 | Data pelaksanaan disposal belum dicatat. | Tidak ada |
| Selesaikan dua kali | 4 | 422 | Disposal sudah selesai. | Tidak ada |
| Asset sudah berubah status | 4 | 422 | Status asset sudah berubah atau tidak lagi berada di gudang. | Tidak ada |
| Disposal tidak ditemukan | 2–4 | 422 | Disposal tidak ditemukan. | Tidak ada |
| Pengguna bukan admin asset | semua | 403 / 422 | Pengguna tidak memiliki akses admin asset. / Pengguna tidak terdaftar sebagai admin asset. | Tidak ada |
| Token kedaluwarsa | semua | 401 | Token telah expired. Mobile me-refresh token sekali lalu mengulang request. | Tidak ada |
Semua pelanggaran aturan dilempar sebagai InvalidArgumentException di dalam DB::transaction, sehingga seluruh perubahan request itu dibatalkan. Pesan tersedia dalam bahasa Indonesia dan Inggris mengikuti header locale.
Ringkasan siapa mengerjakan apa
| Tahap | Mobile | API | Database |
|---|---|---|---|
| Opsi | Daftar asset gudang rusak dengan riwayat | Hitung usia, jumlah perbaikan dan klaim | SELECT saja |
| Keputusan | Alasan, metode, nilai sisa, catatan | Validasi asset gudang rusak, snapshot, kunci status | asset_trx_disposal, asset_data → menunggu_disposal |
| Pelaksanaan | Form sesuai metode dan foto bukti | Validasi menurut metode, unggah bukti setelah validasi | asset_trx_disposal (kolom pelaksanaan) |
| Selesai | Dialog konfirmasi | Validasi ulang, asset → disposed, log, notifikasi |
asset_data, asset_trx_disposal, asset_trx_log, notif_data |
| Tampilan | JSON → model → widget; objek aksi |
statusMeta (Menunggu Disposal, Siap Diselesaikan, Dihapus) |
SELECT saja |
46Ringkasan Database Impact
Tabel asset_trx_disposal dibuat ulang (tabel lama asset_trx_disposal dan _item di-drop bila kosong). asset_data.status_asset +menunggu_disposal; asset_trx_log.jenis_log +disposal_gudang.
47Checklist Implementasi
Database
- DBML diperbarui
- SQL manual + guard + rollback
- SQL dijalankan di setiap environment
Backend
- Model, controller, route, notifikasi, pengecualian aset
disposed - Test otomatis
Mobile
- Daftar, buat, detail, sheet keputusan dan pelaksanaan
- Kartu beranda admin, rute notifikasi, teks id/en
- Test otomatis
Documentation
- Dokumen ini
48Changelog
| Versi | Tanggal | Perubahan |
|---|---|---|
| 1.0 | 8 Okt 2026 | Dokumen awal PML04 |
| 1.1 | 12 Okt 2026 | Sinkronisasi dengan kode: bukti kini disimpan di object storage MinIO (RISK-004 diturunkan ke RENDAH); path UI/model mobile mengikuti struktur folder baru; berkas AssetDisposalProsesWidgets dan AssetFotoPreview dicatat. Perbaikan 422 vs 500 pada controller Asset (import InvalidArgumentException/Throwable) tidak mengubah kontrak API |
| 1.2 | 22 Okt 2026 | Menambah §45.4 Walkthrough Lengkap dengan Contoh Data (keputusan dengan riwayat asset, pelaksanaan lelang/jual dengan bukti, pembaruan pelaksanaan, penyelesaian dan notifikasi; variasi donasi, pemusnahan, disposal sebagai tindak lanjut modul lain; kasus gagal). Sinkronisasi kode: tabel log ternyata asset_trx_log (id_asset_trx_log), bukan asset_log. |