01Informasi Dokumen
| Informasi | Nilai |
|---|---|
| Nama Fitur | Retur Asset Ruangan (UI Admin: item "Retur" di dashboard Ruangan; UI PIC: tombol "Retur Asset" dan banner di halaman "PIC Ruangan") |
| Kode Fitur | ALK-RGN06 (nomor transaksi RGN06-YYYYMMNNN) |
| Modul | Asset Management — Ruangan |
| Backend | Laravel Lumen ^10.0, PHP ^8.1, JWT, Query Builder |
| Database | API/diagram-asset.dbml (tanda [NEW ALK-RGN06]) + SQL manual Claude outputs/sql-alk-rgn06-retur-ruangan.sql dan Claude outputs/sql-alk-rgn06-no-trx-unik-per-company.sql |
| Mobile | Flutter (professional_flutter_3_35), flutter_bloc, dio |
| Web | ⚠️ Belum ditemukan: tidak ada source web di workspace |
| Test otomatis | API tests/Asset/AssetReturRuanganTest.php (16 test, 269 assertion) · Mobile test/asset/asset_retur_ruangan_test.dart (25 test) |
| Versi dokumen | 1.1 — 22 Oktober 2026 |
| Dokumen terkait | Retur Asset Karyawan (KRY04), Onboarding Ruangan (ALK-RGN01), Penambahan Asset Ruangan (ALK-RGN04), Master Lokasi & PIC Ruangan |
02Daftar Isi
Lihat panel navigasi di samping.
03Quick Overview
PIC Ruangan mengajukan retur satu atau beberapa asset yang dipegang ruangannya, masing-masing dengan alasan (rusak, tidak digunakan, atau tukar unit). Admin/HR memutuskan per asset (setujui atau tolak), memilih asset pengganti dari gudang bila alasannya tukar unit atau rusak dengan pengganti, lalu menentukan metode serah terima (dijadwalkan atau langsung). Setelah serah terima fisik, Admin/HR menekan Terima Asset dan memilih gudang tujuan untuk setiap asset. Asset lama masuk gudang (tersedia, atau rusak bila alasannya rusak) dan asset pengganti menjadi terpakai di ruangan. PIC tidak memiliki aksi terima dan tidak ada aksi edit/batal pengajuan.
| Aspek | Ringkasan |
|---|---|
| Aktor | PIC Ruangan (mengajukan), Admin/HR (memutuskan dan menerima), Sistem |
| Transaksi utama | asset_trx_retur_ruangan (header) + asset_trx_retur_ruangan_item (item asal dan item pengganti lewat parent_id) |
| Alasan retur | rusak (tanpa/dengan pengganti), tidak_digunakan, tukar_unit |
| Status header | status_approval: pengajuan → disetujui atau ditolak; status_transaksi: proses → selesai |
| Tab daftar | Pengajuan (termasuk yang ditolak) · Dikonfirmasi · Diterima |
| Status asset | Asal: terpakai/ruangan → tersedia atau rusak/gudang. Pengganti: tersedia → proses → terpakai/ruangan |
| Log | asset_trx_log dengan jenis_log = retur_ruangan (tabel asset_trx_log, sama dengan KRY04) |
| Endpoint | 9: 5 admin + 4 PIC |
| Notifikasi | 4 event (retur_ruangan.*) |
PIC Ruangan: pilih ruangan + asset + alasan per asset → Ajukan Retur [pengajuan / proses · notif Admin/HR]
asset TIDAK dikunci di asset_data (dijaga lewat pengecekan retur aktif)
Admin/HR: Setujui / Tolak tiap asset (+ pengganti) + metode serah terima
[disetujui / proses · notif PIC] semua ditolak → [ditolak / selesai · notif PIC]
Admin/HR: Terima Asset (pilih gudang per asset)
[disetujui / selesai · asset diperbarui · asset_trx_log · notif PIC]
04Ringkasan Fitur
| Fitur | Aktor | Keterangan |
|---|---|---|
| Ajukan retur | PIC Ruangan | Pilih ruangan miliknya, asset ruangan itu, alasan per asset, catatan tambahan. Status pengajuan/proses. |
| Keputusan per asset | Admin/HR | Setujui atau tolak (alasan wajib). Asset yang butuh pengganti wajib diberi pengganti. |
| Asset pengganti | Admin/HR | Dipilih dari asset tersedia di gudang; langsung proses saat dikonfirmasi. |
| Metode serah terima | Admin/HR | Dijadwalkan (tanggal + jam) atau Langsung (tanpa jadwal). |
| Terima Asset | Admin/HR | Memilih gudang untuk setiap asset asal yang disetujui; finalisasi asset dan log. |
| Pantau retur | PIC Ruangan | Daftar 3 tab, detail hanya-baca, banner di halaman PIC Ruangan. |
| Ringkasan admin | Admin/HR | Panel "Retur Asset Ruangan Perlu Diproses" dan tile "Retur" di dashboard Ruangan. |
05Tujuan Fitur
- Mengembalikan asset milik ruangan ke gudang secara terdokumentasi, dengan alasan per asset dan keputusan Admin/HR per asset.
- Menjamin asset pengganti (tukar unit atau rusak dengan pengganti) berpindah ke ruangan dalam satu transaksi serah terima.
- Mencatat gudang tujuan setiap asset yang kembali, kondisi akhir (tersedia atau rusak), pelaku, dan waktu.
- Memakai ulang alur dan layar KRY04 supaya perilaku dan tampilannya seragam.
06Aktor
| Aktor | Peran |
|---|---|
| PIC Ruangan | Karyawan di asset_lokasi_pic_m untuk ruangan tersebut. Mengajukan retur, memantau status. Tidak menerima asset. |
| Admin/HR | Terdaftar di asset_admin. Memutuskan per asset, memilih pengganti, menetapkan metode serah terima, menerima asset dan memilih gudang. |
| Sistem | Memeriksa kepemilikan dan kunci lintas flow, mereservasi asset pengganti (proses), memperbarui asset_data, menulis log, mengirim notifikasi setelah transaksi sukses. |
07Hak Akses
| Aksi | Admin/HR | PIC Ruangan | Keterangan |
|---|---|---|---|
| Lihat opsi pengganti, daftar, dan detail (admin) | ✔ | ✘ | accessError memeriksa asset_admin; selain admin mendapat 403 FORBIDDEN. |
| Konfirmasi dan Terima Asset | ✔ | ✘ | Model juga memeriksa findAssetAdmin: "Pengguna tidak terdaftar sebagai admin asset." |
| Lihat opsi retur satu ruangan | ✘ | ✔ | Hanya PIC ruangan aktif tersebut; selain itu 403 FORBIDDEN. |
| Ajukan retur | ✘ | ✔ | Hanya untuk ruangan yang PIC-nya pengaju. |
| Lihat daftar/detail | ✘ | ✔ | Dibatasi ke ruangan yang saat ini dipegang PIC (join asset_lokasi_pic_m). PIC lain mendapat 404 NOT_FOUND. |
| Terima / edit / batal | ✘ | ✘ | ⚠️ Belum ditemukan untuk PIC: tidak ada route terima, edit, maupun batal. |
08Terminologi
| Istilah | Arti |
|---|---|
| Ruangan | asset_lokasi_m bertipe ruangan, status_aktif='1', punya PIC di asset_lokasi_pic_m. |
| Asset asal / item asal | Asset ruangan yang diretur; parent_id NULL. |
| Asset pengganti / item pengganti | Asset gudang yang dipilih Admin/HR; baris item dengan parent_id = id item asal. |
rusak_dengan_pengganti |
Hanya untuk alasan rusak: 1 tanpa pengganti, 2 dengan pengganti; selain itu 0. |
| Dijadwalkan / Langsung | Metode serah terima. Dijadwalkan: flag_jadwalkan='1' + tanggal dan jam. Langsung: flag_jadwalkan='0', jadwal NULL. |
| Terima Asset | Finalisasi oleh Admin/HR setelah serah terima fisik; status transaksi selesai. |
| Dikonfirmasi / Diterima | Label tab: disetujui+proses / disetujui+selesai. |
09Business Rules
| No | Aturan | Implementasi |
|---|---|---|
| BR-01 | Retur hanya untuk ruangan aktif yang PIC-nya pengaju | findRuanganPic: tipe ruangan, status_aktif='1', asset_lokasi_pic_m.id_karyawan = pengaju. Pesan: "Ruangan tidak ditemukan, tidak aktif, atau Anda bukan PIC ruangan ini." |
| BR-02 | Pengaju harus karyawan aktif | findActiveKaryawan → "Karyawan tidak ditemukan atau tidak aktif." |
| BR-03 | Hanya asset milik ruangan itu dan berstatus terpakai |
pemegang_asset='ruangan' dan id_asset_lokasi_m = ruangan; bila tidak → "Satu atau beberapa asset bukan milik ruangan atau sudah tidak dapat diretur."; status ≠ terpakai → "Satu atau beberapa asset tidak berstatus terpakai dan tidak dapat diretur." |
| BR-04 | Minimal satu asset, tanpa duplikat | "Minimal satu asset wajib dipilih." / "Asset yang sama tidak boleh dipilih lebih dari sekali." |
| BR-05 | Aturan per alasan | Lihat tabel di bawah. |
| BR-06 | Asset tidak boleh sedang di flow lain | Retur ruangan aktif lain, onboarding ruangan proses, penambahan ruangan (RGN04) proses. ⚠️ Kerusakan/pemeliharaan ruangan belum ada model/API sehingga belum dicek. |
| BR-07 | Pengajuan tidak mengunci asset_data |
Asset tetap terpakai sampai Terima Asset; penjagaan lewat hasActiveReturRuanganAsset dan penyaringan opsi. |
| BR-08 | Admin memutuskan seluruh item asal | "Seluruh asset retur wajib dikonfirmasi." Tolak wajib alasan; item ditolak tidak boleh memiliki pengganti. |
| BR-09 | Asset masih dipegang ruangan saat konfirmasi dan terima | "Satu atau beberapa asset sudah tidak dipegang oleh ruangan." |
| BR-10 | Asset pengganti | Wajib untuk tukar unit dan rusak dengan pengganti, dilarang untuk alasan lain, unik, harus tersedia @gudang (getLockedAssets, FOR UPDATE). Tidak ada syarat kategori sama. |
| BR-11 | Metode serah terima wajib bila ada asset disetujui | dijadwalkan wajib tanggal + jam, tanggal ≥ tanggal pengajuan. |
| BR-12 | Hasil keputusan | ≥ 1 disetujui → disetujui/proses; semua ditolak → ditolak/selesai dengan ket_tolak "Seluruh asset retur ditolak." |
| BR-13 | Terima Asset hanya oleh Admin/HR dan wajib memilih gudang per asset | Hanya untuk disetujui+proses; tiap item asal yang disetujui dipetakan tepat satu kali ke lokasi bertipe gudang pada company yang sama. |
| BR-14 | Dampak finalisasi | Asal → gudang (tersedia atau rusak+ket_rusak); pengganti → terpakai @ruangan; log retur_ruangan; header selesai. Item yang ditolak tetap di ruangan. |
| BR-15 | Penomoran dan keunikan | ID id_company+YYYYMM+3 digit; no_trx RGN06-YYYYMMNNN, unik per company (uq_retur_rgn_company_no_trx). |
| BR-16 | PIC tidak menerima, mengubah, atau membatalkan | Tidak ada route untuk itu. |
Aturan per alasan (BR-05):
| Alasan | rusak_dengan_pengganti |
ket_retur |
Pengganti |
|---|---|---|---|
rusak |
wajib 1 atau 2 ("Asset rusak wajib memilih tanpa atau dengan pengganti.") | wajib ("Keterangan kerusakan asset wajib diisi.") | wajib bila 2 |
tidak_digunakan |
dipaksa 0 | opsional | tidak boleh |
tukar_unit |
dipaksa 0 | wajib ("Alasan tukar unit wajib diisi.") | wajib |
10Business Flow
1. PIC Ruangan: PIC Ruangan → Retur Asset → (+) Ajukan Retur Asset Ruangan
→ pilih Ruangan → Tambah Retur Asset (hanya asset ruangan itu yang bebas dari flow lain)
→ per asset: Alasan Retur Asset → (Rusak: Tanpa/Dengan Pengganti) → Keterangan
→ Catatan Tambahan → Ajukan Retur [pengajuan/proses · notif Admin/HR]
2. Admin/HR: Ruangan → Retur → tab Pengajuan → Detail
→ per asset: Tolak (+alasan) / Setujui / Ganti Asset (pilih pengganti)
→ Konfirmasi Asset → sheet: Dijadwalkan (tanggal + jam) / Serahkan Langsung, Catatan → Submit
[disetujui/proses atau ditolak/selesai · notif PIC]
3. Serah terima fisik sesuai jadwal atau langsung
4. Admin/HR: Detail → Terima Asset → dialog Lokasi Tujuan Asset (gudang per asset)
[disetujui/selesai · asset diperbarui · log · notif PIC]
11Flow Berdasarkan Role
| Role | Langkah |
|---|---|
| PIC Ruangan | Ajukan → (tunggu keputusan) → lihat status di banner dan detail → serahkan asset sesuai jadwal atau langsung → selesai saat Admin/HR menekan Terima Asset. |
| Admin/HR | Tab Pengajuan → keputusan per asset → Konfirmasi Asset → (tab Dikonfirmasi) setelah serah terima fisik → Terima Asset + gudang per asset. |
| Sistem | Memeriksa kepemilikan dan kunci lintas flow, mereservasi pengganti, memperbarui asset dan log, mengirim notifikasi. |
12Flow Diagram
flowchart TD
A([Mulai]) --> B[PIC pilih ruangan, asset, alasan per asset]
B --> C{Validasi}
C -->|Gagal| X([Pesan error])
C -->|OK| D[Header pengajuan/proses, notif Admin/HR]
D --> E{Admin putuskan per asset}
E -->|Semua ditolak| F([ditolak/selesai, notif PIC])
E -->|Ada disetujui| G{Butuh pengganti?}
G -->|Tukar unit atau rusak dengan pengganti| H[Pilih asset pengganti, pengganti jadi proses]
G -->|Tidak| I[Tanpa pengganti]
H --> J{Metode serah terima}
I --> J
J -->|Dijadwalkan| K[Simpan tanggal dan jam]
J -->|Langsung| L[Jadwal tetap NULL]
K --> M[disetujui/proses, notif PIC]
L --> M
M --> N[Admin Terima Asset, pilih gudang per asset]
N --> O[Asset asal ke gudang: tersedia atau rusak]
N --> P[Pengganti ke ruangan: terpakai]
O --> Q([disetujui/selesai, log retur_ruangan])
P --> Q13Sequence Diagram
sequenceDiagram
actor PIC as PIC Ruangan
actor ADM as Admin/HR
participant M as Mobile
participant CP as AssetKaryawanReturRuangan_c
participant CA as AssetAdminReturRuangan_c
participant R as AssetReturRuangan_m
participant DB as Database
participant N as AssetNotification
PIC->>M: Pilih ruangan, asset, alasan, Ajukan Retur
M->>CP: POST /asset/karyawan/pic-ruangan/retur
CP->>R: store()
R->>DB: lock company, cek PIC ruangan, kepemilikan, kunci flow lain
R->>DB: INSERT header pengajuan + item asal
CP->>N: retur_ruangan.diajukan (Admin/HR)
ADM->>M: Keputusan per asset, pengganti, metode serah terima
M->>CA: POST /asset/retur-ruangan/{id}/konfirmasi
CA->>R: konfirmasiAdmin()
R->>DB: UPDATE item keputusan, INSERT item pengganti, pengganti jadi proses
R->>DB: UPDATE header disetujui/proses atau ditolak/selesai
CA->>N: retur_ruangan.dikonfirmasi atau .ditolak (PIC)
ADM->>M: Terima Asset, pilih gudang per asset
M->>CA: POST /asset/retur-ruangan/{id}/terima
CA->>R: terimaAdmin()
R->>DB: UPDATE asset asal ke gudang, pengganti ke ruangan, INSERT asset_trx_log
R->>DB: UPDATE header selesai
CA->>N: retur_ruangan.diterima_admin (PIC)14Status Transaksi
Header memiliki dua kolom status: status_approval (enum status_approval_retur: pengajuan, disetujui, ditolak, proses, serah_terima) dan status_transaksi (enum status: proses, selesai).
| status_approval | status_transaksi | Label | Tab | Arti | Aksi berikutnya |
|---|---|---|---|---|---|
pengajuan |
proses |
Menunggu Konfirmasi | pengajuan | Menunggu Admin/HR | Admin/HR: konfirmasi |
ditolak |
selesai |
Ditolak | pengajuan | Semua asset ditolak | – |
disetujui |
proses |
Dikonfirmasi | dikonfirmasi | Menunggu serah terima dan Terima Asset | Admin/HR: Terima Asset |
disetujui |
selesai |
Selesai (mobile: "Diterima") | diterima | Serah terima selesai | – |
proses / serah_terima |
– | Diperlakukan seperti disetujui |
– | ⚠️ Nilai enum ada, tetapi tidak pernah di-set oleh kode | – |
Status item (status_approval item, enum approval_item): NULL (belum ditinjau) → disetujui / ditolak. Item pengganti langsung disetujui.
15State Transition
stateDiagram-v2
[*] --> pengajuan: Ajukan Retur (PIC)
pengajuan --> ditolak: Konfirmasi, semua ditolak (Admin/HR)
pengajuan --> disetujui_proses: Konfirmasi, ada disetujui (Admin/HR)
disetujui_proses --> disetujui_selesai: Terima Asset (Admin/HR)
ditolak --> [*]
disetujui_selesai --> [*]| Status awal | Aksi | Status akhir | Aktor |
|---|---|---|---|
| – | Ajukan Retur | pengajuan / proses | PIC Ruangan |
| pengajuan / proses | Konfirmasi, semua ditolak | ditolak / selesai | Admin/HR |
| pengajuan / proses | Konfirmasi, ≥ 1 disetujui | disetujui / proses | Admin/HR |
| disetujui / proses | Terima Asset | disetujui / selesai | Admin/HR |
16Struktur Database
| Tabel | Fungsi |
|---|---|
asset_trx_retur_ruangan |
Header transaksi: snapshot ruangan dan PIC, jadwal serah terima, status. |
asset_trx_retur_ruangan_item |
Satu baris per asset asal; baris pengganti memakai parent_id = id item asal. |
asset_trx_log |
Diperluas: nilai enum jenis_log ditambah retur_ruangan. |
SQL manual: sql-alk-rgn06-retur-ruangan.sql (guard, 2 tabel, ALTER enum jenis_log, verifikasi, rollback) dan sql-alk-rgn06-no-trx-unik-per-company.sql (mengganti unique global uq_retur_rgn_no_trx menjadi per company). Skrip pertama sudah dijalankan pengguna; skrip kedua perlu dijalankan/dikonfirmasi (status eksekusi belum dikonfirmasi pengguna).
17Relasi Database
erDiagram
asset_lokasi_m ||--o{ asset_trx_retur_ruangan : "id_asset_lokasi_m"
asset_trx_retur_ruangan ||--o{ asset_trx_retur_ruangan_item : "id_asset_trx_retur_ruangan"
asset_trx_retur_ruangan_item |o--o{ asset_trx_retur_ruangan_item : "parent_id (pengganti)"
asset_data ||--o{ asset_trx_retur_ruangan_item : "id_asset_data"
asset_lokasi_m ||--o{ asset_trx_retur_ruangan_item : "id_asset_lokasi_m_tujuan (gudang)"
asset_lokasi_m ||--o{ asset_lokasi_pic_m : "PIC ruangan"PIC pada header disimpan sebagai id_karyawan_pic (snapshot nama_pic, nik_pic). Hak akses lihat/daftar dihitung dari master asset_lokasi_pic_m saat ini, bukan dari snapshot. parent_id pada item tidak memakai foreign key (hanya kolom referensi).
18Data Dictionary
asset_trx_retur_ruangan
| Kolom | Tipe | Keterangan |
|---|---|---|
id_asset_trx_retur_ruangan |
varchar(50) PK | id_company + YYYYMM + 3 digit urut |
no_trx |
varchar(50) | RGN06-YYYYMMNNN; unik bersama id_company |
id_asset_lokasi_m |
varchar(50) FK, NULL | Ruangan (FK ke asset_lokasi_m) |
nama_asset_ruangan, nama_cabang, nama_departemen |
varchar(255) | Snapshot saat transaksi |
id_karyawan_pic, nama_pic, nik_pic |
varchar | PIC pengaju; dasar notifikasi |
keterangan |
text | Catatan tambahan PIC |
ket_tolak |
varchar(255) | "Seluruh asset retur ditolak." bila semua ditolak |
id_karyawan, nik_karyawan, nama_karyawan |
varchar | Pembuat transaksi (PIC Ruangan) |
tgl_pengajuan |
date | Tanggal hari ini |
tgl_pengambilan, jam_pengambilan |
date / time, NULL | Jadwal serah terima bila flag_jadwalkan='1' |
flag_jadwalkan |
enum(0,1) |
1 dijadwalkan, 0 serah langsung (default 0) |
tgl_serah_terima, jam_serah_terima |
date / time, NULL | Diisi saat Terima Asset |
keterangan_serah_terima |
varchar(255) | Catatan Admin/HR (dipotong 255) |
status_approval |
enum(pengajuan,disetujui,ditolak,proses,serah_terima) |
Default pengajuan |
status_transaksi |
enum(proses,selesai) |
Default proses |
user_approval, tgl_approval |
varchar / timestamp NULL | Admin/HR pemutus dan waktunya |
id_company, tgl_input, user_input, tgl_update, user_update |
Audit standar |
asset_trx_retur_ruangan_item
| Kolom | Tipe | Keterangan |
|---|---|---|
id_asset_trx_retur_ruangan_item |
varchar(50) PK | id_company + YYYYMM + 3 digit urut |
parent_id |
varchar(50), NULL | NULL = item asal; id item asal = item pengganti |
id_asset_trx_retur_ruangan |
varchar(50) FK | Header |
id_asset_data |
varchar(50) FK | Asset |
nama_asset, deskripsi_asset, kategori_asset |
varchar(255) | Snapshot |
status_approval |
enum(disetujui,ditolak), NULL |
NULL sebelum direview |
ket_tolak |
varchar(255) | Wajib bagi item ditolak |
alasan_retur |
enum(tukar_unit,rusak,tidak_digunakan) |
Default rusak |
rusak_dengan_pengganti |
int(1) | 0 / 1 tanpa pengganti / 2 dengan pengganti |
ket_retur |
varchar(255) | Keterangan kerusakan / alasan tukar unit / tidak digunakan |
id_asset_lokasi_m_tujuan |
varchar(50) FK, NULL | Gudang tujuan item asal; NULL sebelum diterima |
id_company, tgl_input, user_input |
Audit |
19Database Index
Nama index di SQL manual (DBML mendefinisikan kolom yang sama dengan nama otomatis, kecuali unique yang diberi nama).
| Tabel | Index | Kegunaan |
|---|---|---|
asset_trx_retur_ruangan |
unik uq_retur_rgn_company_no_trx (id_company, no_trx) |
Nomor transaksi unik per company |
atrr_admin_list_idx (id_company, status_approval, status_transaksi, tgl_input, id_asset_trx_retur_ruangan) |
Daftar admin per tab | |
atrr_pic_list_idx (id_company, id_karyawan_pic, status_approval, status_transaksi) |
Daftar PIC | |
atrr_lokasi_idx (id_company, id_asset_lokasi_m, status_approval, status_transaksi) |
Pencarian per ruangan | |
asset_trx_retur_ruangan_item |
atrri_status_idx (id_asset_trx_retur_ruangan, id_company, status_approval, parent_id, id_asset_data) |
Item per header dan pengganti |
atrri_asset_idx (id_asset_data) |
Pengecekan retur aktif per asset | |
atrri_lokasi_tujuan_idx (id_asset_lokasi_m_tujuan) |
FK gudang tujuan |
Foreign key: fk_atrr_retur_id_asset_lokasi_m, fk_atrri_retur_header, fk_atrri_retur_asset_data, fk_atrri_retur_lokasi_tujuan.
20Backend Architecture
| Lapisan | File |
|---|---|
| Route | API/routes/modules/asset/RouteAsset.php (grup retur-ruangan dan karyawan/pic-ruangan/retur*) |
| Controller Admin | app/Http/Controllers/Asset/Admin/AssetAdminReturRuangan_c.php |
| Controller PIC | app/Http/Controllers/Asset/Karyawan/AssetKaryawanReturRuangan_c.php |
| Model | app/Models/Asset/AssetReturRuangan_m.php (mewarisi Asset_m; opsi admin memakai ulang AssetReturKaryawan_m::getAdminOptions) |
| Notifikasi | app/Support/Asset/AssetNotification.php (event retur_ruangan.*) |
| Pendukung | AssetAdmin_m (summary ruangan_retur_*), AssetLokasiPic_m::getAssetRuanganSaya (key retur_ruangan di beranda PIC), helper Asset_m (getLockedAssets, resolveItemLocations, warehouseLocations) |
Controller memvalidasi input, memanggil model dalam DB::transaction (model), lalu mengirim notifikasi setelah transaksi sukses. Pengecualian InvalidArgumentException menjadi BUSINESS_RULE_VIOLATION; Throwable lain menjadi SYSTEM_ERROR dan dicatat di log.
21Backend Business Logic
getOptionsPic: memastikan karyawan PIC ruangan aktif tersebut (null → 403). Mengembalikanruangan, daftarassets(milik ruangan,terpakai, belum terkunci retur/onboarding/penambahan ruangan, urut nama lalu kode), danalasan.store: kunci company; validasi karyawan aktif, PIC ruangan,normalizeItems(BR-04, BR-05); asset dikunciFOR UPDATE; cek milik ruangan, kunci flow lain, statusterpakai; buat headerpengajuan/proses(jadwal NULL,flag_jadwalkan='0') dan item asal.asset_datatidak diubah.konfirmasiAdmin: kunci company; cek admin; header haruspengajuan/proses; seluruh item asal wajib diputuskan; asset asal masih dipegang ruangan; validasi pengganti dan jadwal; update status item; sisipkan item pengganti (parent_id,status_approval='disetujui') dan set aset penggantiproses; update header (disetujui/prosesatauditolak/selesai).terimaAdmin: kunci company; cek admin; header harusdisetujui/proses;resolveItemLocationsmemetakan gudang per item asal yang disetujui; cek asal masih dipegang ruangan dan pengganti masihproses@gudang; update asset,id_asset_lokasi_m_tujuan, sisipkanasset_trx_log, headerselesaidengantgl/jam_serah_terima= sekarang.- Daftar dan detail:
getPageAdmin,getPagePic(filter PIC lewatwherePicRuangan),getDetail,getDetailAdmin(menambahlokasi_gudang). - Ringkasan:
countPendingForAdmin,getPendingForAdmin(maks. 3, terlama di atas),getPerluTindakanPic(maks. 50,prosesdan bukanditolak).
22Dampak terhadap asset_data
| Tahap | Asset | status_asset |
pemegang_asset |
Catatan |
|---|---|---|---|---|
| Pengajuan | asal | tetap terpakai |
ruangan |
Tidak dikunci; dijaga lewat pengecekan retur aktif |
| Konfirmasi (disetujui) | asal | tetap terpakai |
ruangan |
Belum berubah |
| Konfirmasi | pengganti | tersedia → proses |
gudang |
Direservasi |
| Konfirmasi (ditolak) | asal | tidak berubah | ruangan |
Tetap di ruangan |
| Terima Asset | asal, alasan tidak digunakan / tukar unit / rusak | → tersedia |
gudang |
id_pemegang_asset='', id_asset_lokasi_m = gudang pilihan admin, ket_rusak='' |
| Terima Asset | asal, alasan rusak | → rusak |
gudang |
ket_rusak = ket_retur; lokasi = gudang pilihan |
| Terima Asset | pengganti | proses → terpakai |
ruangan |
id_pemegang_asset = id_asset_lokasi_m = id ruangan |
| Semua ditolak | – | tidak berubah | – | Tidak ada asset yang berpindah |
23API Documentation
Base asset/. Semua memakai JWT dan envelope ApiResponse.
| ID | Method | Path | Peran | Fungsi |
|---|---|---|---|---|
| API-01 | GET | retur-ruangan/options |
Admin | Opsi asset pengganti (asset tersedia @gudang) dan kategori |
| API-02 | GET | retur-ruangan?tab&limit&offset |
Admin | Daftar (pengajuan/dikonfirmasi/diterima) |
| API-03 | GET | retur-ruangan/{id} |
Admin | Detail + lokasi_gudang[] |
| API-04 | POST | retur-ruangan/{id}/konfirmasi |
Admin | Keputusan per asset, pengganti, metode serah terima |
| API-05 | POST | retur-ruangan/{id}/terima |
Admin | Terima Asset + gudang per asset |
| API-06 | GET | karyawan/pic-ruangan/retur/options?id_asset_lokasi_m |
PIC | Asset dan alasan untuk satu ruangan |
| API-07 | GET | karyawan/pic-ruangan/retur?tab&limit&offset |
PIC | Daftar milik PIC |
| API-08 | POST | karyawan/pic-ruangan/retur |
PIC | Ajukan retur |
| API-09 | GET | karyawan/pic-ruangan/retur/{id} |
PIC | Detail (tanpa lokasi_gudang) |
Tambahan pada endpoint lain: GET karyawan/pic-ruangan (beranda PIC) memuat key retur_ruangan dan ruangan[] (dipakai form untuk memilih ruangan); GET summary (admin) memuat ruangan_retur_perlu_proses dan ruangan_retur_pending.
24Detail Setiap API
Header umum: Authorization: Bearer <token>, Content-Type: application/json. Format { code, message, data }.
API-08 · POST asset/karyawan/pic-ruangan/retur (PIC)
{
"id_asset_lokasi_m": "RGN-1",
"keterangan": "Renovasi ruangan",
"items": [
{ "id_asset_data": "AST-1", "alasan_retur": "rusak", "rusak_dengan_pengganti": 2, "ket_retur": "Layar pecah" },
{ "id_asset_data": "AST-2", "alasan_retur": "tidak_digunakan", "rusak_dengan_pengganti": 0, "ket_retur": "" },
{ "id_asset_data": "AST-3", "alasan_retur": "tukar_unit", "rusak_dengan_pengganti": 0, "ket_retur": "Perlu spek lebih tinggi" }
]
}
Respons CREATED ("Pengajuan retur asset ruangan berhasil dikirim."):
{ "code": "CREATED", "message": "Pengajuan retur asset ruangan berhasil dikirim.",
"data": { "id": "COMP202610001", "no_trx": "RGN06-202610001", "status_approval": "pengajuan",
"status_transaksi": "proses", "status_label": "Menunggu Konfirmasi", "tab": "pengajuan" } }
| Field | Aturan | Pesan |
|---|---|---|
id_asset_lokasi_m |
required, string, max 50 | Ruangan wajib dipilih. |
keterangan |
nullable string | – |
items |
required, array, min 1 | Minimal satu asset wajib dipilih. |
items.*.id_asset_data |
required, max 50, distinct | Asset retur wajib dipilih. / Asset yang sama tidak boleh dipilih lebih dari sekali. |
items.*.alasan_retur |
required, in rusak,tidak_digunakan,tukar_unit |
Alasan retur setiap asset wajib dipilih. / Alasan retur asset tidak valid. |
items.*.rusak_dengan_pengganti |
nullable integer in 0,1,2 | Pilihan pengganti asset rusak tidak valid. |
items.*.ket_retur |
nullable, max 255 | Keterangan retur maksimal 255 karakter. |
Pesan umum validasi: "Data retur asset ruangan belum lengkap." Aturan model tambahan: §9 (BR-01 sampai BR-06). Database impact: INSERT header, INSERT item, INSERT notif_data; tanpa UPDATE asset_data.
API-06 · GET asset/karyawan/pic-ruangan/retur/options?id_asset_lokasi_m=RGN-1 (PIC)
{ "code": "SUCCESS", "message": "Opsi retur asset ruangan berhasil ditemukan.",
"data": {
"ruangan": { "id_asset_lokasi_m": "RGN-1", "nama_ruangan": "Ruang Finance Lt.2", "cabang": "Pusat", "divisi": "Finance" },
"assets": [
{ "id_asset_data": "AST-1", "kode_asset": "KOM-0001", "nama_asset": "Komputer R1",
"kategori_asset": "Komputer", "merek": "MerekX", "deskripsi": "" } ],
"alasan": [
{ "value": "rusak", "label": "Asset Rusak" },
{ "value": "tidak_digunakan", "label": "Asset Tidak Digunakan" },
{ "value": "tukar_unit", "label": "Tukar Unit" } ] } }
id_asset_lokasi_m kosong → 422 ("Ruangan wajib dipilih."). Bukan PIC ruangan, ruangan nonaktif, atau lokasi bertipe gudang → 403 FORBIDDEN ("Ruangan tidak ditemukan, tidak aktif, atau Anda bukan PIC ruangan ini."). Key alasan di respons adalah alasan (bukan alasan_retur seperti retur karyawan).
API-02 / API-07 · Daftar
GET /asset/retur-ruangan?tab=pengajuan&limit=20&offset=0 (admin)
GET /asset/karyawan/pic-ruangan/retur?tab=dikonfirmasi (PIC)
tab: pengajuan | dikonfirmasi | diterima ("Filter retur asset ruangan tidak valid."); limit 1–50 ("Limit maksimal adalah 50 data."). Pesan umum: "Parameter daftar retur asset ruangan tidak valid."
{ "code": "SUCCESS", "message": "Data retur asset ruangan berhasil ditemukan.",
"data": {
"filters": [ {"value":"pengajuan","label":"Pengajuan"}, {"value":"dikonfirmasi","label":"Dikonfirmasi"}, {"value":"diterima","label":"Diterima"} ],
"records": [ {
"id": "COMP202610001", "no_trx": "RGN06-202610001", "tgl_pengajuan": "2026-10-15",
"tgl_pengambilan": "2026-10-20", "jam_pengambilan": "09:00:00", "flag_jadwalkan": "1",
"tgl_serah_terima": "", "jam_serah_terima": "",
"status_approval": "disetujui", "status_transaksi": "proses", "status_label": "Dikonfirmasi",
"tab": "dikonfirmasi", "ket_tolak": "", "jumlah_asset": 3,
"ruangan": { "id_asset_lokasi_m": "RGN-1", "nama_ruangan": "Ruang Finance Lt.2", "cabang": "Pusat", "divisi": "Finance" },
"pic": { "id_karyawan": "PIC-1", "nik": "111", "nama": "Pic Satu" } } ],
"pagination": { "limit": 20, "offset": 0, "next_offset": 1, "has_more": false } } }
Tanggal/jam yang NULL ditampilkan sebagai string kosong "".
API-03 / API-09 · Detail
{
"id": "COMP202610001", "no_trx": "RGN06-202610001", "tgl_pengajuan": "2026-10-15",
"keterangan": "Renovasi ruangan", "ket_tolak": "",
"tgl_pengambilan": "", "jam_pengambilan": "", "flag_jadwalkan": "0",
"tgl_serah_terima": "", "jam_serah_terima": "", "keterangan_serah_terima": "",
"status_approval": "disetujui", "status_transaksi": "proses", "status_label": "Dikonfirmasi",
"tab": "dikonfirmasi", "jumlah_asset": 1,
"ruangan": { "id_asset_lokasi_m": "RGN-1", "nama_ruangan": "Ruang Finance Lt.2", "cabang": "Pusat", "divisi": "Finance" },
"pic": { "id_karyawan": "PIC-1", "nik": "111", "nama": "Pic Satu" },
"items": [ {
"id_item": "COMP202610011", "parent_id": "", "id_asset_data": "AST-1", "kode_asset": "KOM-0001",
"nama_asset": "Komputer R1", "kategori_asset": "Komputer", "merek": "MerekX", "deskripsi": "",
"status_item": "disetujui", "ket_tolak": "",
"alasan_retur": "rusak", "alasan_label": "Asset Rusak", "rusak_dengan_pengganti": 2, "ket_retur": "Layar pecah",
"id_asset_lokasi_m_tujuan": "", "nama_lokasi_tujuan": "",
"asset_pengganti": { "id_item": "COMP202610012", "parent_id": "COMP202610011", "id_asset_data": "AST-9",
"kode_asset": "KOM-0009", "nama_asset": "Komputer Gudang", "status_item": "disetujui" } } ]
}
Item pengganti tidak muncul di items[] utama, melainkan di asset_pengganti (objek item dengan field yang sama; null bila tidak ada). Khusus admin (API-03) ditambah lokasi_gudang[] (id_asset_lokasi_m, nama_lokasi, id_cabang). 404 NOT_FOUND: "Retur asset ruangan tidak ditemukan."
API-04 · POST asset/retur-ruangan/{id}/konfirmasi (Admin)
{
"items": [
{ "id_item": "COMP202610011", "status_item": "disetujui", "ket_tolak": "", "id_asset_pengganti": "AST-9" },
{ "id_item": "COMP202610013", "status_item": "ditolak", "ket_tolak": "Masih dipakai" }
],
"metode_serah_terima": "dijadwalkan",
"tgl_pengambilan": "2026-10-20",
"jam_pengambilan": "09:00",
"keterangan_serah_terima": "Bawa kabel daya"
}
| Field | Aturan | Pesan |
|---|---|---|
items |
required, array, min 1 | Keputusan asset retur wajib diisi. |
items.*.id_item |
required, max 50, distinct | Asset retur yang sama tidak boleh dikirim lebih dari sekali. |
items.*.status_item |
required, in disetujui,ditolak |
Keputusan setiap asset wajib dipilih. / Keputusan asset harus disetujui atau ditolak. |
items.*.ket_tolak |
nullable, max 255, wajib bila ditolak | Alasan penolakan asset wajib diisi. |
items.*.id_asset_pengganti |
nullable, max 50, distinct | Asset pengganti yang sama tidak boleh dipilih lebih dari sekali. |
metode_serah_terima |
nullable, in dijadwalkan,langsung (wajib di model bila ada yang disetujui) |
Metode serah terima retur tidak valid. |
tgl_pengambilan |
Y-m-d, wajib bila dijadwalkan |
Tanggal serah terima wajib diisi. / Format tanggal serah terima harus YYYY-MM-DD. |
jam_pengambilan |
H:i, wajib bila dijadwalkan |
Jam serah terima wajib diisi. / Format jam serah terima harus HH:MM. |
keterangan_serah_terima |
nullable, max 255 | – |
Pesan umum: "Data konfirmasi retur asset ruangan belum lengkap." Database impact: UPDATE item (status_approval, ket_tolak), INSERT item pengganti (parent_id), UPDATE asset_data pengganti → proses, UPDATE header, INSERT notif_data.
{ "code": "UPDATED", "message": "Retur asset ruangan berhasil dikonfirmasi.",
"data": { "id": "COMP202610001", "no_trx": "RGN06-202610001", "status_approval": "disetujui",
"status_transaksi": "proses", "status_label": "Dikonfirmasi", "tab": "dikonfirmasi",
"jumlah_disetujui": 1, "jumlah_ditolak": 1, "jumlah_pengganti": 1 } }
Semua ditolak: pesan "Retur asset ruangan berhasil ditolak.", status_approval ditolak, status_transaksi selesai, event notifikasi retur_ruangan.ditolak.
API-05 · POST asset/retur-ruangan/{id}/terima (Admin)
{ "items": [
{ "id_asset_trx_retur_item": "COMP202610011", "id_asset_lokasi_m": "GDG-1" },
{ "id_asset_trx_retur_item": "COMP202610014", "id_asset_lokasi_m": "GDG-2" } ] }
| Field | Aturan | Pesan |
|---|---|---|
items |
required, array, min 1 | Lokasi gudang tujuan setiap asset wajib dipilih. |
items.*.id_asset_trx_retur_item |
required, max 50, distinct | – |
items.*.id_asset_lokasi_m |
required, max 50 | Lokasi gudang tujuan wajib dipilih. |
Pesan umum: "Data penerimaan retur belum lengkap." Item yang dikirim harus tepat sama dengan item asal yang disetujui. Database impact: UPDATE asset_data (asal dan pengganti), UPDATE item (id_asset_lokasi_m_tujuan), INSERT asset_trx_log, UPDATE header, INSERT notif_data.
{ "code": "UPDATED", "message": "Retur asset ruangan berhasil diterima.",
"data": { "id": "COMP202610001", "no_trx": "RGN06-202610001", "status_approval": "disetujui",
"status_transaksi": "selesai", "status_label": "Selesai", "tab": "diterima",
"jumlah_diterima": 2, "jumlah_pengganti": 1 } }
API-01 · GET asset/retur-ruangan/options (Admin): mengembalikan data yang sama dengan opsi admin retur karyawan (kategori_asset[] dan assets[] asset tersedia @gudang) karena memanggil AssetReturKaryawan_m::getAdminOptions. Pesan: "Opsi retur asset ruangan berhasil ditemukan."
Ringkasan admin dan beranda PIC (bukan endpoint baru): GET summary → ruangan_retur_perlu_proses (jumlah) dan ruangan_retur_pending[] (id, no_trx, nama_asset, nama_ruangan, nama_karyawan, status_approval); GET karyawan/pic-ruangan → retur_ruangan[] (bentuk sama dengan records[]).
25Mobile Architecture
Halaman retur karyawan dipakai ulang lewat parameter untukRuangan, supaya kode tidak digandakan.
| Komponen | Cara pakai ulang |
|---|---|
AssetReturApi(base: AssetReturApi.picRuanganBase) |
Basis asset/karyawan/pic-ruangan/retur; metode baru getOptionsRuangan(id) dan getRuanganPic() (membaca ruangan[] dari beranda PIC) |
AssetReturAdminApi(base: AssetReturAdminApi.ruanganBase) |
Basis asset/retur-ruangan |
AssetReturBloc |
Event baru LoadAssetReturRuangan; LoadAssetReturOptions(idAssetLokasi) memuat opsi satu ruangan; state ruangan, ruanganLoaded |
HalamanAssetRetur(untukRuangan:) |
Daftar 3 tab dan kartu menampilkan nama ruangan |
HalamanTambahAssetRetur(untukRuangan:) |
Form dengan pemilih ruangan (PIC satu ruangan: dipilih otomatis) |
HalamanDetailAssetRetur(untukRuangan:) |
Detail PIC hanya-baca + kartu AssetReturRuanganCard |
HalamanAssetAdminRetur(untukRuangan:), HalamanAssetAdminDetailRetur(untukRuangan:) |
Daftar dan detail Admin/HR; sheet konfirmasi dan dialog gudang dipakai bersama |
AssetReturRuanganCard |
Kartu "Data Ruangan" (ruangan, PIC, cabang, divisi), pengganti kartu data karyawan |
File: model Repository/Model/Asset/mAssetRetur.dart (AssetReturRuangan, AssetReturRecord.ruangan/pic/untukRuangan), mAssetSummary.dart (ruanganReturPerluProses, ruanganReturPending), mAssetPicRuangan.dart (returRuangan); API Repository/Service/assetReturApi.dart, assetReturAdminApi.dart; bloc Bloc/Asset/AssetReturBloc.dart; UI UI/Modul/Asset/AssetSaya/Retur/*, UI/Modul/Asset/AssetAdmin/AssetAdminKaryawan/Retur/*, UI/Modul/Asset/Shared/Widgets/AssetReturRuanganCard.dart, UI/Modul/Asset/PicRuangan/HalamanAssetPicRuangan.dart, UI/Modul/Asset/AssetAdmin/AssetAdminRuangan/HalamanAssetAdminRuangan.dart.
26Mobile Screen
| Layar | Rute | Pengguna |
|---|---|---|
| PIC Ruangan: tombol Retur Asset + banner retur | /asset_pic_ruangan |
PIC |
| Daftar retur (3 tab); dari notifikasi dengan id langsung ke detail | /asset_pic_ruangan_retur |
PIC |
| Form Ajukan Retur Asset Ruangan | /asset_pic_ruangan_tambah_retur |
PIC |
| Detail retur (hanya-baca) | /asset_pic_ruangan_detail_retur |
PIC |
| Dashboard Ruangan → tile Retur dan panel "Retur Asset Ruangan Perlu Diproses" | halaman Asset Admin Ruangan | Admin/HR |
| Daftar retur ruangan (3 tab) | /asset_admin_retur_ruangan |
Admin/HR |
| Detail retur + keputusan + Terima Asset | /asset_admin_detail_retur_ruangan |
Admin/HR |
Form PIC. Dropdown Ruangan (dari ruangan[] beranda PIC; satu ruangan dipilih otomatis; bila kosong tampil pemberitahuan "Tidak ada ruangan yang dapat diretur."), lalu Pilih Asset yang Diretur → picker. Mengganti ruangan membuang asset terpilih dan memuat asset ruangan baru. Per asset: Alasan Retur Asset, pilihan Tanpa/Dengan Pengganti (bila rusak), Keterangan. Lalu Catatan Tambahan dan tombol Ajukan Retur.
Detail. Kartu info (nomor, tanggal, status, catatan, alasan penolakan, jadwal), AssetAlurStepper.retur, kartu timeline per aset, kartu Data Ruangan, daftar asset retur (alasan, keputusan, pengganti, gudang tujuan). Admin pada status pengajuan melihat tombol keputusan per asset; pada disetujui/proses melihat Terima Asset. PIC hanya melihat pemberitahuan serah terima (jadwal atau langsung).
Beranda PIC. Tombol Retur Asset dan banner untuk retur berjalan ("Menunggu konfirmasi Admin/HR" atau jumlah asset menunggu serah terima + jadwal). Dashboard Admin Ruangan. Panel ringkasan memakai ruangan_retur_*.
27Mobile → API
| Aksi di layar | Pemanggilan |
|---|---|
| Buka form retur (PIC) | getRuanganPic() → GET karyawan/pic-ruangan; pilih ruangan → getOptionsRuangan(id) → API-06 |
| Ajukan Retur | store → API-08 |
| Buka daftar / pindah tab (PIC) | getRetur(tab) → API-07 |
| Buka detail (PIC) | getDetail → API-09 |
| Buka daftar / pindah tab (Admin) | getRetur(tab) → API-02 |
| Buka detail (Admin) | getDetail → API-03 dan getOptions → API-01 |
| Konfirmasi Asset (sheet → Submit) | konfirmasi → API-04 |
| Terima Asset (dialog gudang) | terima → API-05 |
| Beranda PIC / Dashboard Admin | getHome (retur_ruangan) / getSummary (ruangan_retur_*) |
28API → Mobile
Model: mAssetRetur.dart (AssetReturRuangan dengan id_asset_lokasi_m/nama_ruangan/id_cabang/cabang/divisi; AssetReturRecord.ruangan, .pic, .untukRuangan = ruangan tidak kosong; AssetReturOptions.ruangan; AssetReturRequest.idAssetLokasi hanya dikirim bila retur ruangan), mAssetPicRuangan.dart (returRuangan, toleran bila key retur_ruangan tidak ada), mAssetSummary.dart (ruanganReturPerluProses, ruanganReturPending, toleran bila key tidak ada). Daftar ruangan PIC (AssetReturRuangan.listFrom) membuang entri tanpa id. Opsi alasan dibaca dari key alasan_retur, reasons, atau alasan.
29Mapping Mobile → API → Database
| UI | JSON | Tabel | Kolom |
|---|---|---|---|
| Ruangan | id_asset_lokasi_m |
header | id_asset_lokasi_m + snapshot nama_asset_ruangan, nama_cabang, nama_departemen, id_karyawan_pic, nama_pic, nik_pic |
| Asset dipilih | items[].id_asset_data |
item | id_asset_data + snapshot nama/deskripsi/kategori |
| Alasan Retur Asset | items[].alasan_retur |
item | alasan_retur |
| Tanpa/Dengan Pengganti | items[].rusak_dengan_pengganti |
item | rusak_dengan_pengganti (dipaksa 0 bila bukan rusak) |
| Keterangan | items[].ket_retur |
item / asset_data |
ket_retur / ket_rusak (saat Terima, alasan rusak) |
| Catatan Tambahan | keterangan |
header | keterangan |
| Setujui / Tolak | items[].status_item |
item | status_approval |
| Alasan Penolakan | items[].ket_tolak |
item | ket_tolak |
| Asset pengganti | items[].id_asset_pengganti |
item (baru, parent_id) / asset_data |
id_asset_data; status_asset='proses' |
| Metode Serah Terima | metode_serah_terima |
header | flag_jadwalkan |
| Jadwal / Jam | tgl_pengambilan, jam_pengambilan |
header | tgl_pengambilan, jam_pengambilan (NULL bila langsung) |
| Catatan (sheet) | keterangan_serah_terima |
header | keterangan_serah_terima |
| Terima Asset + Lokasi Tujuan | items[].id_asset_lokasi_m |
header / item / asset_data / asset_trx_log |
status_transaksi, tgl/jam_serah_terima / id_asset_lokasi_m_tujuan / status, pemegang, lokasi / jenis_log='retur_ruangan' |
30Mapping Database → API → Mobile
| Kolom | API | Tampilan |
|---|---|---|
status_approval + status_transaksi |
status_label, tab |
Badge, tab, tombol Terima (Admin) |
tgl_pengambilan, jam_pengambilan, flag_jadwalkan |
idem (NULL → "") |
"Jadwal Serah Terima Retur Asset" atau info serah langsung |
tgl_serah_terima, jam_serah_terima |
idem | Waktu diterima |
nama_asset_ruangan, nama_cabang, nama_departemen |
ruangan.nama_ruangan/cabang/divisi |
Kartu Data Ruangan, judul kartu daftar |
nama_pic, nik_pic |
pic.nama/nik |
"PIC Ruangan: …" |
item asal (COUNT parent_id kosong) |
jumlah_asset |
"{jumlah} Asset" |
item alasan_retur, rusak_dengan_pengganti, ket_retur |
alasan_label dst. |
Kartu asset retur |
item status_approval, ket_tolak |
status_item, ket_tolak |
Disetujui/Ditolak Admin |
item pengganti (parent_id) |
items[].asset_pengganti |
Asset Pengganti |
item id_asset_lokasi_m_tujuan + asset_lokasi_m.nama_lokasi |
id_asset_lokasi_m_tujuan, nama_lokasi_tujuan |
"Lokasi Tujuan Asset" |
asset_lokasi_m bertipe gudang |
lokasi_gudang[] (admin) |
Dropdown gudang saat Terima Asset |
31Validation
Mobile
| Lokasi | Aturan | Pesan |
|---|---|---|
| Form | Ruangan wajib dipilih | "Ruangan wajib dipilih" / "Pilih ruangan terlebih dahulu." |
| Form | Minimal satu asset | "Tambahkan minimal satu asset untuk diretur." |
| Form | Alasan per asset wajib | "Alasan retur setiap asset wajib dipilih." |
| Form | Rusak: pilih tanpa/dengan pengganti | "Pilih tanpa pengganti atau dengan pengganti." |
| Form | Keterangan wajib bila rusak atau tukar unit | "Keterangan kerusakan wajib diisi." / "Alasan tukar unit wajib diisi." |
| Form | Ruangan belum punya asset yang dapat diretur | "Ruangan ini belum memiliki asset yang dapat diretur." |
| Detail admin | Semua diputuskan; tolak wajib alasan; pengganti wajib bila perlu | "Pilih keputusan untuk seluruh asset." / "Alasan penolakan wajib diisi." / "Asset pengganti wajib dipilih." |
| Sheet admin | Tanggal dan jam wajib bila Dijadwalkan | "Tanggal/Jam serah terima wajib dipilih." |
| Terima (admin) | Setiap asset wajib punya gudang | "Pilih lokasi gudang tujuan untuk seluruh asset." / "Belum ada lokasi gudang." |
Backend: lihat §9 dan §24. Database: enum status_approval_retur, status, approval_item, retur_aset, kondisi_on_off; tgl_pengambilan, jam_pengambilan, tgl_serah_terima, jam_serah_terima, id_asset_lokasi_m_tujuan, parent_id boleh NULL.
| Field | Mobile | Backend | Database |
|---|---|---|---|
id_asset_lokasi_m |
Dropdown PIC | PIC ruangan aktif | FK asset_lokasi_m |
id_asset_data |
Dari picker | milik ruangan, terpakai, bebas flow lain |
FK asset_data |
alasan_retur |
Wajib | in 3 nilai | enum |
rusak_dengan_pengganti |
Wajib bila rusak | 1/2 bila rusak, 0 lainnya | int(1) |
ket_retur |
Wajib rusak/tukar unit | wajib rusak/tukar unit, ≤ 255 | varchar(255) |
id_asset_pengganti |
Wajib bila perlu | wajib/dilarang sesuai alasan, tersedia @gudang, unik |
item pengganti |
| jadwal | Wajib bila dijadwalkan | wajib bila dijadwalkan, ≥ tgl_pengajuan |
date/time NULL |
| gudang per asset | Wajib semua | tepat satu per item, bertipe gudang, satu company | FK asset_lokasi_m |
32Error Handling
| Kondisi | HTTP | Kode | Pesan |
|---|---|---|---|
| Field tidak valid | 422 | VALIDATION_ERROR |
Pesan per field + pesan umum §24 |
| Tanpa token | 401 | UNAUTHORIZED |
– |
| Bukan admin (endpoint admin) | 403 | FORBIDDEN |
Pengguna tidak memiliki akses admin asset. |
| Opsi: bukan PIC / ruangan nonaktif / lokasi gudang | 403 | FORBIDDEN |
Ruangan tidak ditemukan, tidak aktif, atau Anda bukan PIC ruangan ini. |
| Ajukan: bukan PIC ruangan | 422 | BUSINESS_RULE_VIOLATION |
Ruangan tidak ditemukan, tidak aktif, atau Anda bukan PIC ruangan ini. |
| Ajukan: asset bukan milik ruangan | 422 | BUSINESS_RULE_VIOLATION |
Satu atau beberapa asset bukan milik ruangan atau sudah tidak dapat diretur. |
| Ajukan: asset di flow lain | 422 | BUSINESS_RULE_VIOLATION |
…sedang diproses pada retur lain. / …pada onboarding ruangan. / …pada penambahan asset ruangan. |
Ajukan: asset tidak terpakai |
422 | BUSINESS_RULE_VIOLATION |
Satu atau beberapa asset tidak berstatus terpakai dan tidak dapat diretur. |
| Detail bukan milik PIC / tidak ada | 404 | NOT_FOUND |
Retur asset ruangan tidak ditemukan. |
| Konfirmasi: sudah diputuskan | 422 | BUSINESS_RULE_VIOLATION |
Retur asset sudah tidak menunggu konfirmasi. |
| Konfirmasi: tidak lengkap | 422 | BUSINESS_RULE_VIOLATION |
Seluruh asset retur wajib dikonfirmasi. |
| Konfirmasi/Terima: asset pindah tangan | 422 | BUSINESS_RULE_VIOLATION |
Satu atau beberapa asset sudah tidak dipegang oleh ruangan. |
| Konfirmasi: pengganti | 422 | BUSINESS_RULE_VIOLATION |
Asset pengganti wajib dipilih untuk retur ini. / Alasan retur ini tidak memerlukan asset pengganti. / Asset yang ditolak tidak boleh memiliki asset pengganti. / Satu atau beberapa asset pengganti tidak tersedia. |
| Konfirmasi: jadwal | 422 | BUSINESS_RULE_VIOLATION |
Metode serah terima retur tidak valid. / Tanggal dan jam serah terima wajib diisi. / Format jadwal serah terima tidak valid. / Tanggal serah terima tidak boleh sebelum tanggal pengajuan. |
| Terima: belum dikonfirmasi / sudah selesai | 422 | BUSINESS_RULE_VIOLATION |
Retur asset belum dikonfirmasi atau sudah selesai. |
| Terima: gudang | 422 | BUSINESS_RULE_VIOLATION |
Lokasi gudang tujuan setiap asset wajib dipilih. / Lokasi gudang tujuan tidak valid. / Lokasi gudang tujuan asset tidak sesuai. |
| Terima: pengganti tidak siap | 422 | BUSINESS_RULE_VIOLATION |
Asset pengganti sudah tidak siap diserahterimakan. |
| Kesalahan sistem | 500 | SYSTEM_ERROR |
Terjadi kesalahan saat menyimpan / mengonfirmasi / menerima retur asset ruangan. (detail di log) |
| Timeout (mobile) | – | – | "Tidak dapat terhubung ke server. Periksa koneksi Anda." |
33Audit Trail
| Data | Disimpan? | Lokasi |
|---|---|---|
| Pengaju dan waktu | ✅ | user_input, tgl_input, tgl_pengajuan, snapshot PIC |
| Admin/HR pemutus | ✅ | user_approval, tgl_approval |
| Keputusan dan alasan per asset | ✅ | item status_approval, ket_tolak |
| Asset pengganti | ✅ | item anak (parent_id, user_input = Admin/HR) |
| Pelaku dan waktu Terima Asset | ✅ | user_update, tgl_serah_terima, jam_serah_terima |
| Gudang tujuan per asset | ✅ | item id_asset_lokasi_m_tujuan |
| Perubahan asset | ✅ | asset_trx_log jenis_log='retur_ruangan' (asal dan pengganti; id_transaksi = id header, nama_tabel_transaksi='asset_trx_retur_ruangan') |
| Histori status | ❌ | ⚠️ Belum ditemukan |
34Notification
| Event | Trigger | Penerima | Rute di aplikasi |
|---|---|---|---|
retur_ruangan.diajukan |
Ajukan retur | Semua Admin/HR | asset_admin_detail_retur_ruangan |
retur_ruangan.dikonfirmasi |
Konfirmasi, ≥ 1 disetujui | PIC pengaju | asset_pic_ruangan_retur |
retur_ruangan.ditolak |
Konfirmasi, semua ditolak | PIC pengaju | asset_pic_ruangan_retur |
retur_ruangan.diterima_admin |
Terima Asset | PIC pengaju | asset_pic_ruangan_retur |
Judul/isi dua bahasa: mis. "Pengajuan Retur Asset Ruangan — {pic} mengajukan retur asset untuk {ruangan} ({no_trx})." Penerima PIC diambil dari snapshot id_karyawan_pic pada header (lihat RISK-005). Notifikasi dibuat setelah transaksi database sukses; kegagalan kirim tidak membatalkan transaksi. Rute /asset_pic_ruangan_retur dari notifikasi membawa id sehingga langsung membuka detail; ketiga rute terdaftar di assetNotifRoutes.
35Security
- JWT wajib;
ctxmemuatid_companydanid_karyawan. Seluruh query dibatasiid_company. - Aksi admin memeriksa keanggotaan
asset_admindi controller (accessError) dan di model (findAssetAdmin). ⚠️ Padakonfirmasidanterima, validasi input dijalankan sebelum pemeriksaan admin, sehingga non-admin dengan payload tidak valid menerima 422 alih-alih 403 (pola yang sama dengan KRY04). - PIC hanya melihat dan mengajukan untuk ruangan yang dipegangnya; detail milik ruangan lain menghasilkan 404, bukan 403.
- Company dikunci per transaksi dan asset dikunci
FOR UPDATEuntuk mencegah nomor ganda dan keputusan ganda; kepemilikan asset dicek ulang saat konfirmasi dan terima. - Gudang tujuan divalidasi bertipe
gudangdan satu company; tidak ada query mentah dari input pengguna. - Foto/berkas: tidak dipakai di alur ini.
36Manual Book User/Admin
Label dari
assets/id/asset.json(grupmenu_asset.pic_ruangan) dan teks retur karyawan yang dipakai bersama. ⚠️ Screenshot menyusul.
A. PIC Ruangan: mengajukan retur
- Buka Asset → PIC Ruangan, ketuk Retur Asset (daftar retur), lalu + (Ajukan Retur Asset Ruangan).
- Pilih Ruangan (bila hanya satu, sudah terpilih otomatis).
- Ketuk Tambah Retur Asset, pilih asset ruangan, ketuk Pilih. Asset yang sedang diproses di retur lain, onboarding, atau penambahan ruangan tidak tampil.
- Untuk setiap asset pilih Alasan Retur Asset:
- Asset Rusak: pilih Tanpa Pengganti atau Dengan Pengganti, isi Keterangan Kerusakan.
- Asset Tidak Digunakan: keterangan opsional.
- Tukar Unit: isi alasan.
- Isi Catatan Tambahan bila perlu, ketuk Ajukan Retur.
B. PIC Ruangan: memantau
- Status tampil sebagai banner di halaman PIC Ruangan ("Menunggu konfirmasi Admin/HR", lalu jumlah asset dan jadwal serah terima) dan di daftar retur (tab Pengajuan, Dikonfirmasi, Diterima).
- Setelah dikonfirmasi, serahkan asset kepada Admin/HR sesuai jadwal atau langsung, dan terima unit pengganti bila ada. PIC tidak menekan tombol terima; Admin/HR yang menyelesaikan.
- Bila ditolak, alasan penolakan terlihat di detail.
C. Admin/HR: meninjau retur
- Buka Asset → Ruangan → Retur, tab Pengajuan, pilih transaksi.
- Per asset: Tolak (isi Alasan Penolakan), Setujui, atau Ganti Asset (pilih pengganti dari gudang) untuk tukar unit dan rusak dengan pengganti.
- Ketuk Konfirmasi Asset, pilih Metode Serah Terima: Dijadwalkan (tanggal + jam) atau Serahkan Langsung, isi catatan, Submit.
D. Admin/HR: menerima asset
- Setelah serah terima fisik, buka transaksi di tab Dikonfirmasi, ketuk Terima Asset.
- Pada dialog Lokasi Tujuan Asset, pilih gudang untuk setiap asset, lalu ketuk Terima Asset.
- Asset lama masuk gudang yang dipilih (tersedia atau rusak), asset pengganti tercatat milik ruangan, status menjadi Diterima.
37Kondisi Button
| Tombol | Peran | Halaman | Tampil bila | Aktif bila |
|---|---|---|---|---|
| Retur Asset | PIC | PIC Ruangan | PIC memegang ruangan atau ada retur berjalan | selalu |
| + (Ajukan Retur Asset Ruangan) | PIC | Daftar retur | selalu | selalu |
| Ajukan Retur | PIC | Form | di form | tidak sedang mengirim; isian kosong menampilkan error |
| Tambah Retur Asset (picker) | PIC | Form | di form | form aktif dan ada asset pada ruangan terpilih |
| Tolak / Setujui / Ganti Asset | Admin/HR | Detail | status_approval = pengajuan |
tidak sedang mengirim |
| Konfirmasi Asset | Admin/HR | Detail | status_approval = pengajuan |
tidak sedang mengirim; sheet menolak data tidak lengkap |
| Terima Asset | Admin/HR | Detail | disetujui dan proses |
dialog aktif setelah semua asset punya gudang |
| Terima / Edit / Batal | PIC | – | ⚠️ Belum ditemukan | – |
38Testing Scenario
Hasil terakhir (dijalankan 15 Oktober 2026): API AssetReturRuanganTest 16 test (269 assertion) lolos; seluruh tests/Asset 224 test (2697 assertion) lolos; tests/Routing 4 test gagal (assertion jumlah route 506, sedangkan hasil nyata 537). Mobile asset_retur_ruangan_test.dart 25 test lolos; flutter test test/asset +338 -5 (5 kegagalan, semuanya di asset_onboarding_widget_test dan asset_peminjaman_test, bukan berkas retur ruangan). Belum diuji di server, emulator, atau MariaDB nyata.
| ID | Skenario | Hasil yang diharapkan | Otomatis |
|---|---|---|---|
| TC-01 | Opsi PIC | Hanya asset ruangan itu, terpakai, tidak terkunci; bukan PIC / ruangan nonaktif / gudang → null |
✔ API |
| TC-02 | Ajukan dengan tiga alasan | RGN06- + snapshot ruangan/PIC; jadwal NULL; flag pengganti dipaksa 0 selain rusak; asset tetap terpakai |
✔ API |
| TC-03 | Validasi alasan | Rusak tanpa pilihan pengganti, rusak tanpa keterangan, tukar unit tanpa alasan, alasan tidak dikenal, daftar kosong, dan asset ganda ditolak; tidak digunakan tanpa keterangan diterima | ✔ API |
| TC-04 | Bukan PIC ruangan / asset ruangan lain / gudang | 422, tidak ada baris tersimpan | ✔ API |
| TC-05 | Asset terkunci flow lain | Retur lain, onboarding, penambahan ruangan, status ≠ terpakai ditolak; opsi menyaring asset terkunci; retur ditolak seluruhnya melepas kunci |
✔ API |
| TC-06 | Semua asset ditolak | ditolak/selesai; asset tidak berubah |
✔ API |
| TC-07 | Pengganti wajib/dilarang | Tukar unit dan rusak-dengan-pengganti wajib pengganti; selain itu dilarang | ✔ API |
| TC-08 | Konfirmasi dengan pengganti + jadwal | Pengganti proses; jadwal tersimpan; item pengganti parent_id |
✔ API |
| TC-09 | Serah langsung | Jadwal tetap NULL; flag_jadwalkan='0' |
✔ API |
| TC-10 | Terima Asset | Gudang per asset, tersedia/rusak+ket_rusak, pengganti terpakai @ruangan, ditolak tetap di ruangan, 5 baris asset_trx_log pada skenario uji, tidak bisa diterima dua kali |
✔ API |
| TC-11 | Asset pindah tangan sebelum Terima | 422; header tetap proses; tanpa log |
✔ API |
| TC-12 | Konfirmasi oleh non-admin | Ditolak "Pengguna tidak terdaftar sebagai admin asset."; header tetap pengajuan |
✔ API |
| TC-13 | Daftar/detail PIC | Hanya ruangan yang dipegang PIC; paginasi; PIC lain 404 | ✔ API |
| TC-14 | Beranda PIC dan summary admin | retur_ruangan, ruangan_retur_* terisi dan kosong setelah selesai |
✔ API |
| TC-15 | Endpoint dan notifikasi | 403/422/201/404 sesuai peran; notif admin lalu PIC; terima ganda 422 | ✔ API |
| TC-16 | Semua ditolak mengirim event penolakan | Notifikasi retur_ruangan.ditolak |
✔ API |
| TC-17 | Model mobile | Parsing ruangan/PIC, fase, opsi, daftar ruangan, request, beranda, summary, basis API | ✔ Mobile |
| TC-18 | Bloc | Memuat ruangan PIC lalu opsi ruangan; opsi tanpa ruangan memakai endpoint karyawan | ✔ Mobile |
| TC-19 | Layar PIC | Daftar, tombol +, form butuh ruangan dulu, satu ruangan otomatis, tanpa ruangan, detail hanya-baca, beranda dengan/tanpa retur_ruangan |
✔ Mobile |
| TC-20 | Layar Admin | Daftar, keputusan per asset, tolak wajib alasan, Terima Asset memilih gudang, detail karyawan tidak berubah, dashboard Ruangan dengan/tanpa ruangan_retur |
✔ Mobile |
| TC-21 | Push notifikasi end-to-end | Notifikasi tiba dan membuka rute yang benar | ✘ manual |
| TC-22 | Alur penuh di server/emulator dengan MariaDB nyata | Pengajuan sampai Terima Asset | ✘ manual |
| TC-23 | Skema: unique per company | Dua company dapat memakai no_trx sama |
✘ manual (SQL perlu dijalankan) |
39Temuan Ketidaksesuaian
⚠️ Tidak ada dokumen bisnis sumber untuk RGN06; persyaratan = "alur sama dengan KRY04". Tabel berikut membandingkan implementasi RGN06 dengan KRY04.
| ID | KRY04 (acuan) | RGN06 (implementasi) | Dampak |
|---|---|---|---|
| INC-01 | Pemilik asset karyawan | Pemilik asset ruangan; pengaju PIC Ruangan; asset harus pemegang_asset='ruangan' di ruangan itu |
Wajib memilih ruangan di form |
| INC-02 | Pengaju hanya melihat miliknya (id_karyawan) |
Daftar/detail PIC mengikuti PIC ruangan saat ini (join asset_lokasi_pic_m); notifikasi mengikuti snapshot pengaju |
RISK-005 |
| INC-03 | Tanggal/jam jadwal dan serah terima 0000-00-00 |
NULL; API mengembalikan "" |
Aman untuk strict mode |
| INC-04 | id_asset_lokasi_m_tujuan kosong sampai diterima (lihat dokumen KRY04) |
NULL sebelum diterima (kolom FK) | Tampil "" di API |
| INC-05 | Opsi: alasan_retur + assets |
Opsi PIC: ruangan + assets + alasan (key alasan) |
Mobile membaca ketiga key |
| INC-06 | Cek asset pinjaman, offboarding, pemeliharaan, upgrade, kerusakan | Cek retur ruangan lain, onboarding ruangan, penambahan ruangan; kerusakan/pemeliharaan ruangan belum ada | RISK-006 |
| INC-07 | asset_data.id_asset_lokasi_m pengganti diset NULL |
Pengganti: lokasi = id ruangan | Konsisten dengan RGN01/RGN04 |
| INC-08 | Gudang default per cabang karyawan (karyawan.id_cabang) |
ruangan.id_cabang tidak dikirim API sehingga tidak ada default |
RISK-003 |
| INC-09 | Mobile: PIC/karyawan memiliki halaman sendiri | Halaman dipakai ulang lewat untukRuangan; PIC tanpa aksi terima |
Perilaku seragam |
| INC-10 | Dokumen KRY04 versi lama menyebut log di tabel lama | Kode KRY04 dan RGN06 sama-sama menulis asset_trx_log; dokumen KRY04 sudah dikoreksi pada versi 1.8 |
✅ Selaras |
40Edge Case
| Kasus | Perilaku |
|---|---|
| Satu PIC memegang beberapa ruangan | Form memilih ruangan; mengganti ruangan membuang asset terpilih dan memuat ulang opsi. |
| PIC dengan satu ruangan | Dipilih otomatis; bila opsi gagal dimuat tidak ada retry (RISK-001). |
| Ganti ruangan cepat berturut-turut | Respons opsi bisa tiba tidak berurutan; API menolak asset yang tidak cocok (RISK-002). |
| Asset yang sama diajukan dua kali | Ditolak "sedang diproses pada retur lain" selama retur belum ditolak atau selesai. |
| Retur ditolak seluruhnya | Asset bebas lagi dan dapat diajukan ulang. |
| Sebagian asset ditolak | Yang ditolak tetap di ruangan; hanya yang disetujui diterima dan diberi gudang. |
| Asset pengganti dipakai flow lain sebelum Terima | Pengganti proses; Terima gagal "Asset pengganti sudah tidak siap diserahterimakan." bila status berubah. |
| Asset pindah tangan sebelum Terima | 422; transaksi tetap proses; tidak ada log. |
| Admin Terima dua kali / dua admin bersamaan | Header dikunci; yang kedua "Retur asset belum dikonfirmasi atau sudah selesai." |
| Alasan rusak lalu diterima | Asset asal rusak + ket_rusak dari keterangan retur; berbeda dari alasan lain (tersedia, ket_rusak kosong). |
| Rusak "tanpa pengganti" | Tidak ada pengganti; asset ruangan berkurang. |
| PIC ruangan diganti saat transaksi berjalan | Daftar/detail pindah ke PIC baru; notifikasi tetap ke PIC pengaju (RISK-005). |
| Ruangan dinonaktifkan setelah pengajuan | Konfirmasi dan Terima tidak memeriksa status ruangan; ⚠️ Asumsi perilaku dibiarkan. |
| Daftar kosong | Teks "Belum ada data retur asset." |
| Ruangan tanpa asset yang dapat diretur | Form menampilkan "Ruangan ini belum memiliki asset yang dapat diretur." |
41Potential Bug / Technical Risk
| ID | Tingkat | Risiko | Saran | Status |
|---|---|---|---|---|
| RISK-001 | RENDAH | Form PIC dengan satu ruangan buntu bila GET options gagal: tidak ada tombol coba lagi (HalamanTambahAssetRetur.dart ±122–129, 403–416). |
Tambahkan retry untuk opsi ruangan terpilih. | Terbuka / dibiarkan |
| RISK-002 | RENDAH | Balapan respons saat ganti ruangan cepat (LoadAssetReturOptions konkuren di AssetReturBloc). API menolak asset yang tidak cocok, tidak ada risiko data. |
Gunakan restartable() atau abaikan respons lama. |
Terbuka / dibiarkan |
| RISK-003 | RENDAH | Payload ruangan tidak memuat id_cabang, sehingga gudang default per cabang tidak pernah terpilih pada Terima Asset (beda dari KRY04). |
Sertakan id_cabang pada ruanganPayload atau gunakan join cabang. |
Terbuka / dibiarkan |
| RISK-004 | RENDAH | Riwayat log asset belum mengenali jenis_log = retur_ruangan (AssetAdmin_m::LOG_TRANSAKSI_TABLES, HalamanAssetAdminData._logJenisLabel). Pola sama dengan onboarding/request ruangan. |
Daftarkan tabel dan label jenis log. | Terbuka / dibiarkan |
| RISK-005 | RENDAH | Notifikasi hanya ke PIC pengaju (snapshot id_karyawan_pic), sedangkan akses daftar/detail memakai PIC ruangan saat ini. |
Kirim ke PIC ruangan saat ini atau semua PIC. | Terbuka / dibiarkan |
| RISK-006 | RENDAH | Tidak ada jalur batal/edit setelah diajukan atau disetujui (sama dengan KRY04). Pengganti hanya divalidasi tersedia @gudang tanpa cek kunci flow lain (sama dengan KRY04). |
Tambahkan aksi batal dan cek kunci pengganti bila diperlukan. | Terbuka / dibiarkan |
| RISK-007 | RENDAH | RGN04 masih memakai unique global uq_req_rgn_no_trx (risiko serupa, di luar cakupan RGN06). |
Selaraskan menjadi per company. | Terbuka / di luar cakupan |
| RISK-008 | RENDAH | Retur ruangan tidak memeriksa kunci dari flow karyawan lain dan sebaliknya hanya lewat hasActiveReturRuanganAsset; kerusakan/pemeliharaan ruangan belum ada model/API sehingga belum dicek. |
Tambahkan pengecekan saat modul tersebut dibuat. | Terbuka / dibiarkan |
| RISK-009 | SEDANG | ⚠️ Skrip sql-alk-rgn06-no-trx-unik-per-company.sql belum dikonfirmasi dijalankan. Selama unique global uq_retur_rgn_no_trx masih ada, dua company dengan urutan sama pada bulan yang sama gagal menyimpan. |
Jalankan skrip dan verifikasi SHOW INDEX. |
Perlu dikonfirmasi |
| RISK-010 | SEDANG | Belum diuji di server, emulator, atau MariaDB nyata, termasuk push notifikasi dan rute notifikasi. | Uji alur penuh PIC → Admin/HR. | Terbuka |
42Dependency
- Data master:
asset_lokasi_m(tiperuangandangudang,status_aktif),asset_lokasi_pic_m,master_cabang,master_departemen,data_karyawan,asset_admin,asset_kategori_m. - Modul lain:
asset_data,asset_trx_log, notifikasi (notif_data, FCM); pengecekan kunci terhadap Onboarding Ruangan (RGN01) dan Penambahan Asset Ruangan (RGN04); opsi pengganti dariAssetReturKaryawan_m::getAdminOptions(KRY04). - Berbagi komponen mobile: halaman retur karyawan (
untukRuangan),AssetReturAdminConfirmationSheet, dialog Lokasi Tujuan Asset,AssetAlurStepper.retur,AssetReturTimelineCards. - SQL manual: urutan setelah
sql-pml04-disposal.sql(enumjenis_logmengasumsikan sampaidisposal_gudang).
43Source Code Reference
| Area | File |
|---|---|
| Model API | API/app/Models/Asset/AssetReturRuangan_m.php |
| Controller | API/app/Http/Controllers/Asset/Admin/AssetAdminReturRuangan_c.php, .../Karyawan/AssetKaryawanReturRuangan_c.php |
| Route | API/routes/modules/asset/RouteAsset.php |
| Notifikasi | API/app/Support/Asset/AssetNotification.php |
| Ringkasan | API/app/Models/Asset/AssetAdmin_m.php, AssetLokasiPic_m.php |
| Skema | API/diagram-asset.dbml, Claude outputs/sql-alk-rgn06-retur-ruangan.sql, Claude outputs/sql-alk-rgn06-no-trx-unik-per-company.sql |
| Test API | API/tests/Asset/AssetReturRuanganTest.php |
| Mobile model | MOBILE/lib/Repository/Model/Asset/mAssetRetur.dart, mAssetSummary.dart, mAssetPicRuangan.dart |
| Mobile API | MOBILE/lib/Repository/Service/assetReturApi.dart, assetReturAdminApi.dart |
| Mobile bloc | MOBILE/lib/Bloc/Asset/AssetReturBloc.dart (admin: AssetReturAdminBloc.dart) |
| Mobile UI | MOBILE/lib/UI/Modul/Asset/AssetSaya/Retur/*, .../AssetAdmin/AssetAdminKaryawan/Retur/*, .../Shared/Widgets/AssetReturRuanganCard.dart, .../PicRuangan/HalamanAssetPicRuangan.dart, .../AssetAdmin/AssetAdminRuangan/HalamanAssetAdminRuangan.dart |
| Navigasi | MOBILE/lib/Bloc/NavigatorBloc.dart, MOBILE/lib/Setting/router.dart (rute retur ruangan + assetNotifRoutes) |
| Teks | MOBILE/assets/id/asset.json, assets/en/asset.json (grup menu_asset.pic_ruangan, key retur_* dan adm_retur_*) |
| Test mobile | MOBILE/test/asset/asset_retur_ruangan_test.dart |
44Developer Notes
- Alur dan kontrak sengaja dibuat sama dengan KRY04 supaya halaman mobile dapat dipakai ulang; perbedaan utama ada di §39.
- Tanggal/jam NULL (bukan
0000-00-00);nullableDateValue/nullableTimeValuemengubah NULL dan nilai nol menjadi""untuk mobile. id_asset_lokasi_m_tujuandanparent_idNULL pada item asal; kode memeriksaparent_idNULL atau''untuk keamanan.asset_datatidak dikunci saat pengajuan; konsistensi dijaga lewathasActiveReturRuanganAssetdan penyaringan opsi. Flow lain yang ingin menghormati retur ruangan harus memanggil helper tersebut.- Log memakai
asset_trx_log(jenis_log='retur_ruangan'). Dokumen KRY04 sudah dikoreksi (v1.8) dan selaras dengan kode. - Mengubah enum
jenis_logharus mempertahankan seluruh nilai lama (lihat blok 3 SQL). tests/Routingmemuat angka jumlah route yang ditulis keras (506); hasil nyata 537 sehingga 4 test gagal. Penyebab selisih tidak diselidiki di sini.- Pemformatan: hanya
dart formatpada file Dart yang diubah.
45End-to-End Traceability
| Kebutuhan | Aturan | API | Mobile | Test |
|---|---|---|---|---|
| PIC mengajukan retur asset ruangan | BR-01 sampai BR-07 | API-06, API-08 | Form Ajukan Retur | TC-01 sampai TC-05, TC-19 |
| Admin memutuskan per asset | BR-08, BR-12 | API-04 | Detail Admin + sheet konfirmasi | TC-06, TC-12, TC-20 |
| Asset pengganti | BR-10 | API-01, API-04 | Ganti Asset | TC-07, TC-08 |
| Metode serah terima | BR-11 | API-04 | Sheet konfirmasi | TC-08, TC-09 |
| Terima Asset + gudang per asset | BR-09, BR-13, BR-14 | API-05 | Dialog Lokasi Tujuan Asset | TC-10, TC-11, TC-20 |
| PIC memantau | BR-16 | API-07, API-09 | Daftar, detail, banner | TC-13, TC-14, TC-19 |
| Notifikasi | – | semua | Rute notifikasi | TC-15, TC-16, TC-21 |
| Keunikan nomor transaksi per company | BR-15 | API-08 | – | TC-02, TC-23 |
45.4 Walkthrough Lengkap dengan Contoh Data
Walkthrough berikut mengikuti satu retur dari awal sampai selesai: PIC Ruangan mengajukan tiga asset dengan alasan berbeda, Admin/HR memutuskan per asset (dua disetujui, satu ditolak, satu mendapat pengganti), lalu Admin/HR menerima asset dengan memilih gudang tujuan per asset. Setiap langkah menjelaskan apa yang dilakukan di mobile, apa yang dikirim, apa yang diproses API, apa yang tersimpan di database, dan apa yang diterima mobile. Data bersifat sintetis, tetapi nama kolom, urutan proses, dan pesan diambil dari kode (AssetReturRuangan_m, AssetKaryawanReturRuangan_c, AssetAdminReturRuangan_c, AssetNotification).
| Langkah | Pelaku | Aksi | Endpoint |
|---|---|---|---|
| 0 | PIC Ruangan | Memilih ruangan, memuat asset yang boleh diretur | API-06 GET /asset/karyawan/pic-ruangan/retur/options?id_asset_lokasi_m=… |
| 1 | PIC Ruangan | Ajukan Retur 3 asset | API-08 POST /asset/karyawan/pic-ruangan/retur |
| 2 | Admin/HR | Membuka detail dan opsi pengganti | API-01 GET /asset/retur-ruangan/options, API-03 GET /asset/retur-ruangan/{id} |
| 3 | Admin/HR | Konfirmasi per asset (setujui, tolak, pengganti, metode) | API-04 POST /asset/retur-ruangan/{id}/konfirmasi |
| 4 | Admin/HR | Terima Asset dan pilih gudang per asset | API-05 POST /asset/retur-ruangan/{id}/terima |
| 5 | PIC Ruangan | Memantau status di tab Diterima | API-07/09 |
Data awal
Company CMP001. Tanggal 22 Oktober 2026, periode ID 202610.
data_karyawan (semua aktif)
| id_karyawan | nik | nama_lengkap | Peran |
|---|---|---|---|
| KRY0099 | ADM-001 | Siti Admin | Admin Asset |
| KRY0030 | EMP-030 | Dewi PIC | PIC Ruang Meeting Lt.2 |
asset_lokasi_m: LOK0007 "Ruang Meeting Lt.2" (tipe = ruangan, status_aktif = 1, cabang "Jakarta", departemen "Operasional"), asset_lokasi_pic_m menunjuk KRY0030. Gudang: LOK0001 "Gudang Pusat" dan LOK0002 "Gudang Cadangan" (tipe = gudang).
asset_data
| id_asset_data | nama_asset | status / pemegang / lokasi | Peran dalam skenario |
|---|---|---|---|
| AST0401 | Laptop Dell 14 | terpakai / ruangan / LOK0007 | Rusak, minta pengganti |
| AST0402 | Proyektor Epson | terpakai / ruangan / LOK0007 | Tidak digunakan, disetujui |
| AST0403 | Printer HP | terpakai / ruangan / LOK0007 | Tidak digunakan, ditolak Admin |
| AST0501 | Laptop Lenovo 14 | tersedia / gudang / LOK0001 | Asset pengganti AST0401 |
Penomoran: header dan item memakai id_company + YYYYMM + 3 digit (urut per tabel); no_trx = RGN06-YYYYMMNNN (unik per company); asset_trx_log dan notifikasi memakai 5 digit. Pengajuan tidak mengunci asset_data: asset tetap terpakai sampai Terima Asset; penjagaannya lewat pengecekan retur aktif.
Langkah 0 — PIC memilih ruangan dan asset
Mobile: halaman PIC Ruangan → Retur Asset → Ajukan Retur Asset Ruangan. Setelah ruangan dipilih, layar memanggil API-06.
Dikirim
GET /asset/karyawan/pic-ruangan/retur/options?id_asset_lokasi_m=LOK0007
Authorization: Bearer <access_token_dewi>
Diproses API: getOptionsPic memastikan ruangan aktif dan milik PIC login (selain itu 403 FORBIDDEN: "Ruangan tidak ditemukan, tidak aktif, atau Anda bukan PIC ruangan ini."), lalu menampilkan asset terpakai di ruangan itu kecuali yang sedang terkunci di retur aktif, onboarding proses, penambahan proses, atau upgrade aktif. Database: hanya SELECT.
Diterima mobile: AST0401, AST0402, AST0403 dengan nama dan kategori, ditambah pilihan alasan (rusak, tidak_digunakan, tukar_unit).
Langkah 1 — PIC mengajukan retur tiga asset
Mobile: Dewi menambahkan tiga asset, memilih alasan per asset (untuk Laptop: Asset Rusak → Dengan Pengganti + keterangan), mengisi catatan tambahan, lalu Ajukan Retur.
Dikirim
POST /asset/karyawan/pic-ruangan/retur
Authorization: Bearer <access_token_dewi>
{ "id_asset_lokasi_m": "LOK0007",
"keterangan": "Penataan ulang ruang meeting",
"items": [
{ "id_asset_data": "AST0401", "alasan_retur": "rusak", "rusak_dengan_pengganti": 2, "ket_retur": "Layar mati total" },
{ "id_asset_data": "AST0402", "alasan_retur": "tidak_digunakan", "ket_retur": "Tidak dipakai lagi" },
{ "id_asset_data": "AST0403", "alasan_retur": "tidak_digunakan" } ] }
Diproses API (AssetKaryawanReturRuangan_c::storeReturRuangan → AssetReturRuangan_m::store, satu DB::transaction(…, 3))
1. Validasi controller: id_asset_lokasi_m ("Ruangan wajib dipilih."), items minimal satu ("Minimal satu asset wajib dipilih."), id_asset_data wajib dan tidak kembar ("Asset retur wajib dipilih." / "Asset yang sama tidak boleh dipilih lebih dari sekali."), alasan_retur ("Alasan retur setiap asset wajib dipilih." / "Alasan retur asset tidak valid."), rusak_dengan_pengganti ∈ 0/1/2 ("Pilihan pengganti asset rusak tidak valid."), ket_retur maksimal 255.
2. lockCompany; karyawan aktif ("Karyawan tidak ditemukan atau tidak aktif.").
3. findRuanganPic: ruangan aktif dan PIC-nya pengaju ("Ruangan tidak ditemukan, tidak aktif, atau Anda bukan PIC ruangan ini.").
4. normalizeItems menerapkan aturan per alasan: rusak wajib memilih tanpa/dengan pengganti ("Asset rusak wajib memilih tanpa atau dengan pengganti.") dan keterangan ("Keterangan kerusakan asset wajib diisi."); tukar unit wajib keterangan ("Alasan tukar unit wajib diisi."); selain rusak, rusak_dengan_pengganti dipaksa 0.
5. Asset dibaca FOR UPDATE dengan syarat pemegang_asset = ruangan dan lokasi = ruangan ini ("Satu atau beberapa asset bukan milik ruangan atau sudah tidak dapat diretur.").
6. Cek lintas flow: retur aktif lain ("Satu atau beberapa asset sedang diproses pada retur lain."), onboarding ruangan ("…pada onboarding ruangan."), penambahan ruangan ("…pada penambahan asset ruangan."), upgrade ruangan ("…pada upgrade ruangan."); status harus terpakai ("Satu atau beberapa asset tidak berstatus terpakai dan tidak dapat diretur.").
7. Nomor: CMP001202610001, RGN06-202610001. INSERT header dan tiga item. Tidak ada UPDATE asset_data.
8. Controller mengirim retur_ruangan.diajukan ke semua admin asset.
Hasil database
asset_trx_retur_ruangan (1 baris)
| Kolom | Nilai |
|---|---|
| id_asset_trx_retur_ruangan / no_trx | CMP001202610001 / RGN06-202610001 |
| id_asset_lokasi_m / nama_asset_ruangan | LOK0007 / Ruang Meeting Lt.2 |
| nama_cabang / nama_departemen | Jakarta / Operasional |
| id_karyawan_pic / nama_pic / nik_pic | KRY0030 / Dewi PIC / EMP-030 |
| id_karyawan / nik_karyawan / nama_karyawan | KRY0030 / EMP-030 / Dewi PIC (pengaju) |
| keterangan | Penataan ulang ruang meeting |
| tgl_pengajuan | 2026-10-22 |
| status_approval / status_transaksi | pengajuan / proses |
| flag_jadwalkan | 0 |
| tgl_pengambilan / jam_pengambilan / tgl_serah_terima / jam_serah_terima | NULL |
| ket_tolak / user_approval / tgl_approval | '' / '' / NULL |
| tgl_input / user_input | 2026-10-22 08:30:00 / EMP-030 - Dewi PIC |
asset_trx_retur_ruangan_item (3 item asal; parent_id NULL)
| id_asset_trx_retur_item | id_asset_data | alasan_retur | rusak_dengan_pengganti | ket_retur |
|---|---|---|---|---|
| CMP001202610001 | AST0401 | rusak | 2 | Layar mati total |
| CMP001202610002 | AST0402 | tidak_digunakan | 0 | Tidak dipakai lagi |
| CMP001202610003 | AST0403 | tidak_digunakan | 0 | '' |
asset_data: tidak berubah (ketiganya tetap terpakai di LOK0007).
notif_data: satu baris per admin asset, rute asset_admin_detail_retur_ruangan, judul "Pengajuan Retur Asset Ruangan", isi "Dewi PIC mengajukan retur asset untuk Ruang Meeting Lt.2 (RGN06-202610001).". Push FCM ikut dikirim; kegagalan push hanya dicatat di log.
Diterima mobile (HTTP 201)
{ "code": "CREATED", "message": "Pengajuan retur asset ruangan berhasil dikirim.",
"data": { "id": "CMP001202610001", "no_trx": "RGN06-202610001",
"status_approval": "pengajuan", "status_transaksi": "proses",
"status_label": "Menunggu Konfirmasi", "tab": "pengajuan" } }
Retur muncul di tab Pengajuan milik PIC.
Langkah 2 — Admin membuka detail dan opsi pengganti
Mobile: Ruangan → Retur → tab Pengajuan → detail. Detail menampilkan tiap asset dengan alasannya; AssetAdminReturRuangan memuat opsi pengganti.
Dikirim
GET /asset/retur-ruangan/options
GET /asset/retur-ruangan/CMP001202610001
Authorization: Bearer <access_token_admin>
Diproses API: hanya admin asset (selain itu 403 FORBIDDEN). getAdminOptions mengembalikan asset tersedia di gudang dan kategori; getDetailAdmin menggabungkan header, item, pasangan asal–pengganti lewat parent_id, dan lokasi_gudang[]. Database: hanya SELECT.
Diterima mobile: AST0501 muncul sebagai kandidat pengganti. Tombol aksi: Tolak, Setujui, Ganti Asset per asset.
Langkah 3 — Admin memutuskan per asset
Mobile: Siti menyetujui AST0401 dan memilih AST0501 sebagai pengganti, menyetujui AST0402, menolak AST0403 dengan alasan, memilih metode Dijadwalkan (24 Okt 2026 pukul 10:00), lalu mengonfirmasi.
Dikirim
POST /asset/retur-ruangan/CMP001202610001/konfirmasi
{ "items": [
{ "id_item": "CMP001202610001", "status_item": "disetujui", "id_asset_pengganti": "AST0501" },
{ "id_item": "CMP001202610002", "status_item": "disetujui" },
{ "id_item": "CMP001202610003", "status_item": "ditolak", "ket_tolak": "Masih dipakai rapat mingguan" } ],
"metode_serah_terima": "dijadwalkan",
"tgl_pengambilan": "2026-10-24", "jam_pengambilan": "10:00",
"keterangan_serah_terima": "Bawa troli" }
Diproses API (konfirmasiReturRuangan → konfirmasiAdmin)
1. Validasi controller: items ("Keputusan asset retur wajib diisi."), status_item hanya disetujui/ditolak, ket_tolak wajib bila ditolak ("Alasan penolakan asset wajib diisi."), metode_serah_terima ∈ dijadwalkan|langsung ("Metode serah terima retur tidak valid."), tanggal Y-m-d dan jam H:i wajib bila dijadwalkan.
2. lockCompany; aktor harus admin asset ("Pengguna tidak terdaftar sebagai admin asset."); header FOR UPDATE ("Retur asset tidak ditemukan."); harus pengajuan/proses ("Retur asset sudah tidak menunggu konfirmasi.").
3. Keputusan harus mencakup seluruh item asal, tidak kurang dan tidak lebih ("Seluruh asset retur wajib dikonfirmasi."). Asset asal harus masih dipegang ruangan ini ("Satu atau beberapa asset sudah tidak dipegang oleh ruangan.").
4. Per item: keputusan bukan disetujui/ditolak → "Keputusan asset harus disetujui atau ditolak."; ditolak tanpa alasan ditolak; ditolak dengan pengganti → "Asset yang ditolak tidak boleh memiliki asset pengganti."; disetujui yang membutuhkan pengganti (tukar unit, atau rusak dengan pengganti = 2) tanpa pengganti → "Asset pengganti wajib dipilih untuk retur ini."; disetujui yang tidak membutuhkan pengganti tetapi diberi → "Alasan retur ini tidak memerlukan asset pengganti."; pengganti kembar → "Asset pengganti yang sama tidak boleh dipilih lebih dari sekali.".
5. Pengganti dikunci lewat getLockedAssets (harus tersedia di gudang; "Satu atau beberapa asset pengganti tidak tersedia.").
6. Bila ada yang disetujui, metode wajib valid; dijadwalkan butuh tanggal dan jam ("Tanggal dan jam serah terima wajib diisi."), format benar ("Format jadwal serah terima tidak valid."), tanggal tidak sebelum tgl_pengajuan ("Tanggal serah terima tidak boleh sebelum tanggal pengajuan.").
7. UPDATE keputusan tiap item asal; INSERT item pengganti (parent_id = item asal); UPDATE pengganti → proses; UPDATE header.
8. Controller mengirim retur_ruangan.dikonfirmasi (atau ditolak bila semua ditolak) ke PIC.
Hasil database
asset_trx_retur_ruangan_item
| id_asset_trx_retur_item | id_asset_data | parent_id | status_approval | ket_tolak | rusak_dengan_pengganti |
|---|---|---|---|---|---|
| CMP001202610001 | AST0401 | NULL | disetujui | '' |
2 |
| CMP001202610002 | AST0402 | NULL | disetujui | '' |
0 |
| CMP001202610003 | AST0403 | NULL | ditolak | Masih dipakai rapat mingguan | 0 |
| CMP001202610004 (baru) | AST0501 | CMP001202610001 | disetujui | '' |
0 |
Baris pengganti menyalin alasan_retur item asal (rusak), rusak_dengan_pengganti = 0, ket_retur = '', user_input = admin.
asset_data: hanya AST0501 berubah, status_asset tersedia → proses (user_update = ADM-001 - Siti Admin). AST0401–AST0403 tetap terpakai di ruangan.
asset_trx_retur_ruangan (header)
| Kolom | Sebelum | Sesudah |
|---|---|---|
| status_approval | pengajuan | disetujui |
| status_transaksi | proses | proses (tidak berubah) |
| flag_jadwalkan | 0 | 1 |
| tgl_pengambilan / jam_pengambilan | NULL / NULL | 2026-10-24 / 10:00:00 |
| keterangan_serah_terima | '' |
Bawa troli |
| ket_tolak | '' |
'' (hanya diisi "Seluruh asset retur ditolak." bila semua ditolak) |
| user_approval / tgl_approval | '' / NULL |
ADM-001 - Siti Admin / 2026-10-22 10:00:00 |
| tgl_update / user_update | NULL / '' |
2026-10-22 10:00:00 / ADM-001 - Siti Admin |
notif_data: baris untuk KRY0030, rute asset_pic_ruangan_retur, judul "Retur Asset Ruangan Dikonfirmasi", isi "Retur asset RGN06-202610001 untuk Ruang Meeting Lt.2 telah dikonfirmasi admin.".
Diterima mobile (HTTP 200; pesan "Retur asset ruangan berhasil dikonfirmasi.")
{ "code": "UPDATED", "message": "Retur asset ruangan berhasil dikonfirmasi.",
"data": { "id": "CMP001202610001", "no_trx": "RGN06-202610001",
"status_approval": "disetujui", "status_transaksi": "proses",
"status_label": "Dikonfirmasi", "tab": "dikonfirmasi",
"jumlah_disetujui": 2, "jumlah_ditolak": 1, "jumlah_pengganti": 1 } }
Transaksi pindah ke tab Dikonfirmasi; tombol Terima Asset muncul untuk Admin/HR.
Langkah 4 — Admin menerima asset dan memilih gudang
Pada 24 Okt, laptop rusak dan proyektor tiba. Siti memilih gudang tujuan per asset asal yang disetujui: Laptop rusak ke LOK0001, proyektor ke LOK0002.
Dikirim
POST /asset/retur-ruangan/CMP001202610001/terima
{ "items": [
{ "id_asset_trx_retur_item": "CMP001202610001", "id_asset_lokasi_m": "LOK0001" },
{ "id_asset_trx_retur_item": "CMP001202610002", "id_asset_lokasi_m": "LOK0002" } ] }
Diproses API (terimaReturRuangan → terimaAdmin)
1. Validasi: items wajib, id tidak kembar, gudang wajib ("Lokasi gudang tujuan setiap asset wajib dipilih." / "Lokasi gudang tujuan wajib dipilih.").
2. lockCompany, admin asset, header FOR UPDATE; harus disetujui/proses ("Retur asset belum dikonfirmasi atau sudah selesai.").
3. Item asal yang disetujui dipetakan ke gudang lewat resolveItemLocations: setiap item tepat satu kali ("Lokasi gudang tujuan asset tidak sesuai."), semua item terisi, dan lokasi harus bertipe gudang pada company yang sama ("Lokasi gudang tujuan tidak valid."). Item yang ditolak tidak dimasukkan. Tidak ada item disetujui → "Tidak ada asset retur yang dapat diterima.".
4. Semua asset terkait dikunci FOR UPDATE; asset asal harus masih dipegang ruangan ("Satu atau beberapa asset sudah tidak dipegang oleh ruangan.") dan pengganti harus masih proses di gudang ("Asset pengganti sudah tidak siap diserahterimakan.").
5. Per asset asal: UPDATE asset_data ke gudang (rusak → rusak + ket_rusak, selain itu tersedia), simpan id_asset_lokasi_m_tujuan di item, siapkan log. Bila ada pengganti: UPDATE asset_data → terpakai di ruangan, siapkan log.
6. INSERT semua baris asset_trx_log sekaligus, lalu UPDATE header → status_transaksi = selesai.
7. Controller mengirim retur_ruangan.diterima_admin ke PIC.
Hasil database
asset_data
| id_asset_data | Kolom | Sebelum | Sesudah |
|---|---|---|---|
| AST0401 | status_asset | terpakai | rusak |
| pemegang_asset / id_pemegang_asset | ruangan / LOK0007 | gudang / '' |
|
| id_asset_lokasi_m | LOK0007 | LOK0001 | |
| ket_rusak | '' |
Layar mati total | |
| AST0402 | status_asset | terpakai | tersedia |
| pemegang_asset / id_asset_lokasi_m | ruangan / LOK0007 | gudang / LOK0002 | |
| AST0501 (pengganti) | status_asset | proses | terpakai |
| pemegang_asset / id_pemegang_asset | gudang / '' |
ruangan / LOK0007 | |
| id_asset_lokasi_m | LOK0001 | LOK0007 | |
| AST0403 (ditolak) | semua kolom | terpakai @ LOK0007 | tidak berubah |
asset_trx_retur_ruangan_item: kolom id_asset_lokasi_m_tujuan terisi LOK0001 pada CMP001202610001 dan LOK0002 pada CMP001202610002.
asset_trx_log (3 baris; nomor contoh dengan asumsi log pertama periode 202610)
| id_asset_trx_log | id_asset_data | kondisi awal → akhir | posisi awal → akhir | id_pemegang / nama_pemegang |
|---|---|---|---|---|
| CMP00120261000001 | AST0401 | (kondisi_awal asset) → rusak | ruangan → gudang | '' / '' |
| CMP00120261000002 | AST0501 | normal → normal | gudang → ruangan | LOK0007 / Ruang Meeting Lt.2 |
| CMP00120261000003 | AST0402 | (kondisi_awal asset) → normal | ruangan → gudang | '' / '' |
Semua baris memakai jenis_log = retur_ruangan, id_transaksi = CMP001202610001, nama_tabel_transaksi = asset_trx_retur_ruangan. Urutan log mengikuti urutan item: asset asal, pengganti miliknya, lalu item berikutnya.
asset_trx_retur_ruangan (header): status_transaksi proses → selesai; tgl_serah_terima = 2026-10-24, jam_serah_terima = waktu penerimaan (misalnya 10:05:00); tgl_update/user_update diperbarui. status_approval tetap disetujui.
notif_data: baris untuk KRY0030, judul "Retur Asset Ruangan Diterima", isi "Asset retur RGN06-202610001 untuk Ruang Meeting Lt.2 telah diterima admin.".
Diterima mobile (HTTP 200)
{ "code": "UPDATED", "message": "Retur asset ruangan berhasil diterima.",
"data": { "id": "CMP001202610001", "no_trx": "RGN06-202610001",
"status_approval": "disetujui", "status_transaksi": "selesai",
"status_label": "Selesai", "tab": "diterima",
"jumlah_diterima": 2, "jumlah_pengganti": 1 } }
Langkah 5 — PIC memantau
PIC tidak memiliki aksi terima, ubah, atau batal. Dewi hanya membuka daftar GET /asset/karyawan/pic-ruangan/retur?tab=diterima dan detail GET …/retur/CMP001202610001. Akses dibatasi ke ruangan yang saat ini dipegang PIC (join asset_lokasi_pic_m); PIC lain mendapat 404 NOT_FOUND ("Retur asset ruangan tidak ditemukan."). Detail PIC tidak menyertakan lokasi_gudang[]. Banner pada halaman PIC Ruangan hilang setelah transaksi selesai.
Kondisi akhir
| Objek | Keadaan |
|---|---|
Header CMP001202610001 |
disetujui + selesai, tab Diterima |
| Item | 4 baris: AST0401 (disetujui), AST0402 (disetujui), AST0403 (ditolak), AST0501 (pengganti AST0401) |
| AST0401 | rusak di LOK0001 |
| AST0402 | tersedia di LOK0002 |
| AST0501 | terpakai di LOK0007 |
| AST0403 | tetap terpakai di LOK0007 |
asset_trx_log |
3 baris retur_ruangan |
Variasi
- Semua asset ditolak: tidak ada metode serah terima yang diperlukan; header menjadi
status_approval = ditolak,status_transaksi = selesai,ket_tolak = "Seluruh asset retur ditolak."; tidak ada pengganti,asset_datatidak berubah, PIC dinotifikasiretur_ruangan.ditolak(judul "Retur Asset Ruangan Ditolak"). Respons: "Retur asset ruangan berhasil ditolak.", tabpengajuan, label "Ditolak". Terima Asset tidak dapat dijalankan karena status bukandisetujui. - Metode Langsung:
metode_serah_terima = "langsung", tanpa tanggal dan jam;flag_jadwalkan = 0,tgl_pengambilan/jam_pengambilantetap NULL. Terima Asset dapat dilakukan kapan saja setelah konfirmasi. - Tukar unit:
alasan_retur = tukar_unit(keterangan wajib) selalu membutuhkan pengganti saat disetujui. Asal kembali ke gudang sebagaitersedia(bukanrusak) dan pengganti masuk ke ruangan. Tidak ada syarat kategori yang sama. - Rusak tanpa pengganti:
rusak_dengan_pengganti = 1; Admin tidak boleh memberi pengganti ("Alasan retur ini tidak memerlukan asset pengganti."). Asal kembali ke gudang sebagairusak. - Tidak digunakan: asal kembali sebagai
tersedia; keterangan opsional; pengganti dilarang.
Kasus gagal
Semua kesalahan aturan bisnis dilempar sebagai InvalidArgumentException di dalam transaksi, sehingga seluruh perubahan di-rollback dan controller membalas BUSINESS_RULE_VIOLATION dengan pesan di bawah.
| Situasi | Langkah | Pesan |
|---|---|---|
| Ruangan nonaktif atau bukan milik PIC | 0, 1 | Ruangan tidak ditemukan, tidak aktif, atau Anda bukan PIC ruangan ini. |
| Asset bukan milik ruangan atau sudah berpindah | 1 | Satu atau beberapa asset bukan milik ruangan atau sudah tidak dapat diretur. |
| Asset sudah ada di retur aktif lain | 1 | Satu atau beberapa asset sedang diproses pada retur lain. |
| Asset sedang di onboarding / penambahan / upgrade ruangan | 1 | Satu atau beberapa asset sedang diproses pada onboarding ruangan. / …penambahan asset ruangan. / …upgrade ruangan. |
Asset tidak berstatus terpakai |
1 | Satu atau beberapa asset tidak berstatus terpakai dan tidak dapat diretur. |
| Rusak tanpa pilihan pengganti / tanpa keterangan | 1 | Asset rusak wajib memilih tanpa atau dengan pengganti. / Keterangan kerusakan asset wajib diisi. |
| Tukar unit tanpa alasan | 1 | Alasan tukar unit wajib diisi. |
| Admin tidak memutuskan semua item | 3 | Seluruh asset retur wajib dikonfirmasi. |
| Ditolak tanpa alasan / ditolak dengan pengganti | 3 | Alasan penolakan asset wajib diisi. / Asset yang ditolak tidak boleh memiliki asset pengganti. |
| Pengganti wajib tidak dipilih / dipilih padahal tidak perlu | 3 | Asset pengganti wajib dipilih untuk retur ini. / Alasan retur ini tidak memerlukan asset pengganti. |
| Pengganti kembar atau tidak tersedia | 3 | Asset pengganti yang sama tidak boleh dipilih lebih dari sekali. / Satu atau beberapa asset pengganti tidak tersedia. |
| Metode atau jadwal salah | 3 | Metode serah terima retur tidak valid. / Tanggal dan jam serah terima wajib diisi. / Tanggal serah terima tidak boleh sebelum tanggal pengajuan. |
| Konfirmasi ulang | 3 | Retur asset sudah tidak menunggu konfirmasi. |
| Terima sebelum dikonfirmasi atau sesudah selesai | 4 | Retur asset belum dikonfirmasi atau sudah selesai. |
| Gudang tidak lengkap atau bukan gudang | 4 | Lokasi gudang tujuan setiap asset wajib dipilih. / Lokasi gudang tujuan asset tidak sesuai. / Lokasi gudang tujuan tidak valid. |
Pengganti sudah tidak proses di gudang |
4 | Asset pengganti sudah tidak siap diserahterimakan. |
| Bukan admin asset | 3, 4 | Pengguna tidak terdaftar sebagai admin asset. |
Ringkasan siapa mengerjakan apa
| Pihak | Tugas dalam alur ini |
|---|---|
| Mobile PIC | Memilih ruangan dan asset, mengisi alasan per asset, mengirim pengajuan, memantau status (tanpa aksi lain) |
| Mobile Admin/HR | Memutuskan per asset, memilih pengganti dan metode serah terima, memilih gudang per asset saat Terima Asset |
| API | Validasi, pembatasan ruangan milik PIC, pengecekan flow lain, penguncian asset dan pengganti, penomoran, pemetaan gudang, perubahan asset_data, asset_trx_log, notifikasi bilingual |
| Database | Menyimpan header, item asal dan pengganti (parent_id), keputusan per item, gudang tujuan, status asset, log, dan notifikasi |
46Ringkasan Database Impact
| Tabel | Operasi |
|---|---|
asset_trx_retur_ruangan |
INSERT (ajukan), UPDATE (konfirmasi: jadwal, status, approval; terima: tgl/jam_serah_terima, status_transaksi) |
asset_trx_retur_ruangan_item |
INSERT (item asal, item pengganti), UPDATE (status_approval, ket_tolak, id_asset_lokasi_m_tujuan) |
asset_data |
UPDATE saat konfirmasi (pengganti → proses) dan Terima Asset (asal → gudang, pengganti → ruangan) |
asset_trx_log |
INSERT saat Terima Asset; enum jenis_log + retur_ruangan |
notif_data |
INSERT per notifikasi |
47Checklist Implementasi
- DBML diperbarui (
[NEW ALK-RGN06]) - SQL manual dibuat (
sql-alk-rgn06-retur-ruangan.sql) dan sudah dijalankan pengguna - SQL
sql-alk-rgn06-no-trx-unik-per-company.sqldijalankan dan diverifikasi (perlu dikonfirmasi) - Model, controller, route, notifikasi API
- Ringkasan admin dan beranda PIC (
ruangan_retur_*,retur_ruangan) - Test API (16) lolos dan seluruh
tests/Asset(224) lolos - Model, API client, bloc, halaman, route, teks mobile
- Test mobile retur ruangan (25) lolos
- Uji di server/emulator dan MariaDB nyata, termasuk push notifikasi
- Jalur batal/edit pengajuan (belum ada)
- Pengenalan
retur_ruanganpada riwayat log asset (belum ada)
48Changelog
| Versi | Tanggal | Perubahan |
|---|---|---|
| 1.0 | 15 Oktober 2026 | Dokumen awal ALK-RGN06: skema 2 tabel, API admin (5) dan PIC (4), mobile (pemakaian ulang halaman KRY04), notifikasi, ringkasan admin dan beranda PIC, test. |
| 1.1 | 22 Okt 2026 | Menambah §45.4 walkthrough lengkap (pengajuan PIC, keputusan per asset dengan pengganti, Terima Asset dengan gudang per asset), variasi, dan tabel kasus gagal. |