Klien adalah vendor/partner resmi sebuah ISP fiber-to-the-home (FTTH) yang mengelola jaringan di beberapa titik layanan dengan ribuan pelanggan aktif. Mereka punya akses ke dashboard billing milik ISP induk — tapi dashboard itu bukan punya mereka, dan tidak dirancang untuk kebutuhan operasional harian mereka sebagai vendor lapangan.
Dashboard internal ini dibangun untuk menjawab satu pertanyaan yang selama ini tidak bisa dijawab siapa pun di perusahaan: port mana yang masih kosong, dan siapa yang boleh dijual ke sana?
Latar Belakang Masalah
- Tidak ada visibilitas kapasitas jaringan. Setiap titik distribusi optik (ODP) punya 8 slot koneksi — slot kosong berarti peluang jualan. Tapi tidak ada satu pun tempat untuk melihat ODP mana yang masih ada sisa port; tim sales jalan tanpa peta, kadang menjanjikan slot yang sebenarnya sudah penuh.
- Tidak ada manajemen gudang sama sekali. Modem, kabel, adaptor dicatat manual di Excel oleh tim gudang — tidak ada riwayat transaksi, tidak ada cara memverifikasi stok fisik vs catatan, dan modem dengan serial number unik tidak dilacak per unit.
Saat data pelanggan pertama kali dianalisis untuk migrasi, ditemukan belasan kasus pelanggan aktif yang tercatat memegang port fisik yang sama — sesuatu yang mustahil terjadi di dunia nyata, tapi sudah bertahun-tahun tidak terdeteksi karena tidak ada sistem yang bisa mengeceknya. Dashboard billing dari ISP induk sendiri lengkap untuk urusan tagihan, tapi nol untuk inventory — celah itulah yang jadi scope proyek ini.
Solusi yang Dibangun
Sebuah dashboard internal berbasis peran (gudang, admin, owner) yang memosisikan dirinya sebagai pencatat, bukan eksekutor — sistem tidak pernah menyentuh perangkat jaringan pelanggan, tidak memutus koneksi siapa pun, dan tidak memindahkan uang. Kalau ada bug, akibatnya paling buruk adalah angka yang salah tampil di layar, bukan pelanggan kehilangan internet.
- Inventory bertingkat. Gudang sentral mengalokasikan barang ke tiap titik layanan, dengan riwayat mutasi penuh — bukan kolom stok yang ditimpa.
- Peta kapasitas jaringan. Setiap ODP dan port-nya terlacak: terpakai, kosong, atau rusak — plus visualisasi di peta untuk tim sales.
- Data pelanggan tergabung. Satu pandangan yang menyatukan pelanggan, port yang dipegang, dan perangkat yang terpasang.
- Cermin tagihan, bukan generator. Status tagihan diimpor dari sistem billing resmi, digabung dengan konteks lapangan dan pemicu follow-up ke WhatsApp/CRM.
Keputusan Desain & Teknis Penting
Stok selalu dihitung, tidak pernah disimpan. Tidak ada kolom stok di tabel barang — angka stok selalu dihitung ulang dari seluruh riwayat transaksi (masuk, keluar, transfer, rusak, hilang). Kolom stok yang ditimpa langsung cepat atau lambat menyimpang dari kenyataan, dan begitu itu terjadi tidak ada cara membuktikan mana yang benar.
Import melaporkan konflik, bukan menolak seluruh file. Data pelanggan datang dari export manual sistem billing pihak ketiga yang kualitasnya di luar kendali tim — port yang diklaim lebih dari satu pelanggan, nomor port di luar rentang valid, kode lokasi tak dikenal. Alih-alih menolak file, importer mencatat tiap anomali sebagai konflik yang direview manusia, dan tidak pernah mengoreksi data itu sendiri secara otomatis.
Pengaman anti-silent-failure pada import. Sumber data eksternal pernah mengembalikan file kosong tanpa pesan error apa pun. Karena itu, file dengan 0 baris valid ditolak total, dan penurunan jumlah baris lebih dari 20% dibanding import sukses terakhir ditahan untuk konfirmasi eksplisit.
Tidak ada tombol hapus di seluruh aplikasi. Data pelanggan adalah cerminan dari sistem billing pihak ketiga, bukan milik sistem ini — menghapus baris di sini tidak menghapusnya di sumber aslinya. Pelanggan yang berhenti diubah statusnya, bukan dihapus; portnya otomatis dilepas dan riwayat tetap bisa ditelusuri.
Setiap perubahan stok dikunci di dalam transaksi database. Pemeriksaan ketersediaan barang dan port selalu memakai row-level locking di dalam transaksi, bukan diperiksa dulu baru dieksekusi terpisah — mencegah dua petugas gudang sama-sama lolos pemeriksaan lalu sama-sama mengurangi stok yang sama.
Keputusan lain yang berpengaruh: integrasi ke CRM pihak ketiga (untuk tombol chat WhatsApp dari halaman pelanggan) sengaja dibuat read-only — sistem ini tidak pernah membuat atau mengubah data di CRM tersebut. Dan permintaan sinkronisasi otomatis ke sistem billing induk ditolak secara sadar, karena tim IT pihak ketiga tidak pernah mengonfirmasi izinnya.
Fitur Utama
| Modul | Fungsi |
|---|---|
| Dashboard | Ringkasan port siap jual, port terpakai, jumlah ODP, konflik data menunggu review, dan peringatan stok menipis per lokasi |
| Barang & Stok | Master data barang (modem serialized, kabel per-satuan, aksesoris qty-based) dengan riwayat mutasi penuh sebagai satu-satunya sumber angka stok |
| Transfer Stok | Perpindahan gudang sentral → titik layanan dengan alur dua langkah supaya barang "di jalan" tetap punya jejak kalau hilang |
| Instalasi | Pencatatan pemasangan, penggantian, dan pencabutan — tiap aksi mengunci/melepas port dan mengurangi/menambah stok dalam satu transaksi |
| Pelanggan | Data pelanggan tergabung dengan port & perangkat terpasang, termasuk koreksi manual per kolom yang tidak tertimpa saat data terbaru diimpor |
| ODP / ODC | Manajemen titik distribusi optik dan kabinet distribusi, termasuk status tiap slot koneksi (kosong/terpakai/rusak) |
| Laporan Port Kosong | Tabel, export, dan peta interaktif untuk tim sales — port dipecah tiga kategori: komersial, fasilitas umum, dan belum dipastikan |
| Pencapaian Penjualan | Persentase penetrasi pelanggan aktif per wilayah, dihitung otomatis dari kapasitas titik distribusi |
| Tagihan & Pembayaran | Cerminan status tagihan dari sistem billing resmi, plus catatan pembayaran manual dan pemicu follow-up WhatsApp/CRM |
| Project | Pengelolaan material per proyek/PO terpisah dari stok material sisi pelanggan |
| Import Excel | Tiga jenis importer (aset jaringan, data pelanggan, opname barang) dengan preview wajib dan pencatatan konflik otomatis |
| User Management | Tiga peran (gudang, admin, owner) dengan hak akses berbeda per modul; akun dinonaktifkan, tidak pernah dihapus |
Tech Stack
| Layer | Teknologi |
|---|---|
| Bahasa & Framework | PHP 8.3, Laravel 12 |
| Database | MySQL 8 |
| Frontend | Blade, Alpine.js, Tailwind (CDN) |
| Peta | Leaflet + OpenStreetMap |
| Import Excel | maatwebsite/excel |
| Antrian job | Laravel Queue |
Stack dipilih untuk kecepatan pengembangan dan kemudahan maintenance jangka panjang oleh tim kecil — tanpa toolchain frontend terpisah (tidak ada npm/build step), dan tanpa ketergantungan API berbayar untuk peta (OpenStreetMap gratis, cukup untuk kebutuhan visualisasi internal).
Tantangan Teknis
- Kualitas data sumber yang tidak bisa diandalkan. Data pelanggan datang dari export manual sistem pihak ketiga: koordinat GPS dengan encoding yang kadang rusak, tanggal yang kadang berupa serial number mentah, kode lokasi dengan pola penomoran tidak konsisten antar cabang, dan struktur sheet yang bisa berubah tanpa pemberitahuan.
- Konsistensi angka lintas modul. "Berapa port yang masih kosong" harus konsisten di dashboard, laporan sales, dan proses import — termasuk aturan status pelanggan mana yang benar-benar mengunci port secara fisik. Perubahan aturan ini sempat bikin angka berbeda tergantung dari layar mana dilihat, sebelum disatukan jadi satu sumber kebenaran tunggal.
- Race condition pada sumber daya terbatas. Port jaringan, stok barang, dan serial number modem semuanya bisa diperebutkan dua petugas di waktu bersamaan — semua pemeriksaan ketersediaan dikunci di level database, di dalam transaksi.
- Import idempotent untuk data yang datang berkala. Data pelanggan dikirim ulang secara berkala dengan campuran baris baru, baris berubah, dan baris yang koreksinya sudah dilakukan manual. Importer harus membedakan ketiganya, melindungi koreksi manual, dan tetap mendeteksi anomali baru — tanpa duplikasi data setiap kali file yang sama diimpor ulang.
- Rate limit pada integrasi CRM pihak ketiga. Sinkronisasi kontak ke CRM eksternal punya batas permintaan per detik yang ketat. Solusinya lewat job queue dengan penjadwalan yang disebar, plus penanganan eksplisit untuk membedakan "gagal menghubungi API" dari "data memang tidak ditemukan".

















