01 β Audit Sistem Lama¶
Dibaca oleh: semua developer, terutama Database Engineer. Tujuan: paham bagaimana sistem lama benar-benar bekerja β dan di mana utang teknisnya β sebelum menyentuh apa pun. Jangan migrasi apa yang belum Anda pahami.
Audit ini jujur, bukan menghakimi. Desain ini cerdas untuk zamannya dan menopang bisnis 10 tahun. Tujuan kita: mempertahankan kecerdasannya, membuang kerapuhannya.
1. Gambaran Besar Arsitektur¶
Sistem ini adalah ERP metadata-driven dengan logika di database. Tiga pilar:
Pilar 1 β Satu tabel transaksi untuk semua ("Single Table Inheritance")¶
Semua jenis transaksi (penjualan, pembelian, retur, kas, jurnal, opname, pindah
lokasi, dll.) disimpan di satu tabel header t dan satu tabel detail d.
Pembeda jenisnya: kolom t.kdtrans (3 huruf, mis. PJ = penjualan).
t (header transaksi, Β±60 kolom) ββ kdtrans βββΊ datakode (master jenis transaksi)
β
βββ d (detail transaksi, Β±45 kolom)
Pilar 2 β datakode adalah "mesin konfigurasi"¶
Tabel datakode mendefinisikan perilaku setiap jenis transaksi: akun jurnal mana
yang didebit/dikredit, apakah memengaruhi stok, apakah hitung HPP, nama tabel
master/detail, bahkan potongan SQL yang disimpan sebagai teks (sql_biaya).
Mengubah 1 baris di datakode mengubah perilaku akuntansi seluruh jenis transaksi.
Pilar 3 β Logika hidup di trigger + prosedur¶
Saat baris masuk ke t/d, trigger otomatis: membuat nomor transaksi, menghitung
total, memutakhirkan stok realtime, menghitung HPP secara FIFO, dan memposting jurnal.
Aplikasi (Delphi/Node) "cukup" menulis ke t/d; sisanya database yang kerjakan.
Ini kekuatan sekaligus kelemahan. Kekuatan: konsisten, semua aplikasi dapat perlakuan sama. Kelemahan: logika tak terlihat dari kode aplikasi & tak terdokumentasi. Dokumen 05 membongkar logika ini.
2. Tabel Inti (yang WAJIB dipahami)¶
| Tabel | Peran | Catatan penting |
|---|---|---|
t |
Header semua transaksi | PK id bigint tanpa AUTO_INCREMENT (dibuat manual via fungsi getidtrans()), ~60 kolom, banyak NULL, charset latin1 |
d |
Detail semua transaksi | PK id varchar(60) berisi gabungan idtrans.idbarang β rapuh (lihat Β§4) |
datakode |
Master/konfigurasi jenis transaksi | Inti "mesin"; kode 3 huruf jadi PK |
s |
Stok realtime per (idbarang,idlokasi) |
Diisi ulang oleh prosedur, bukan sumber kebenaran β hasil hitung dari d |
fifo |
Lapisan FIFO masuk (untuk HPP) | idmasuk, masuk, keluar, sisa, harga |
fifokeluar |
Konsumsi lapisan FIFO saat barang keluar | Menghubungkan keluar β lapisan masuk |
j |
Jurnal akuntansi (buku besar) | Double entry: rek,debit,kredit. Tanpa PK, tanpa ID |
kas |
Buku kas / piutang-hutang | |
brg |
Master barang | PK id tanpa AUTO_INCREMENT |
ktk |
Master kontak (pelanggan/supplier/pegawai) | Flag c/s/p = customer/supplier/pegawai |
rekening |
Bagan akun (Chart of Accounts) | PK kode bertipe decimal(12,2) β nomor akun disimpan sebagai angka desimal |
brggolongan |
Pemetaan golongan barang β akun jurnal | rek,rekjual,rekhpp,rekopname,... |
lokasi |
Lokasi/gudang | |
devisi |
Divisi/bidang usaha | |
klien |
Kode klien (kdpc, 3 huruf) |
Dipakai membentuk nomor transaksi |
operator |
User aplikasi |
Daftar Kode Transaksi (datakode) β nyata di sistem¶
| kode | nama | tipe | hitung stok | hitung HPP |
|---|---|---|---|---|
PB |
Pembelian | TRANSAKSI | ya | ya |
PJ |
Penjualan | TRANSAKSI | ya | ya |
RB |
Retur Pembelian | TRANSAKSI | ya | β |
RJ |
Retur Penjualan | TRANSAKSI | ya | ya |
PL |
Pemindahan Lokasi | PINDAH | ya | β |
OS |
Stok Opname / Saldo Persediaan | OPNAME | ya | β |
BB |
Penggunaan Persediaan (produksi) | PRODUKSI | ya | ya |
PO |
Order Pembelian | TRANSAKSI | β | β |
SO |
Order Penjualan | TRANSAKSI | β | β |
KK |
Kas Keluar | KEUANGAN | β | β |
KM |
Kas Masuk | KEUANGAN | β | β |
KB |
Kas & Bank | KEUANGAN | β | β |
PH |
Pelunasan Hutang | KEUANGAN | β | β |
PP |
Pelunasan Piutang | KEUANGAN | β | β |
PI |
Piutang | KEUANGAN | β | β |
HU |
Hutang | KEUANGAN | β | β |
DA |
Penyusutan | KEUANGAN | β | β |
GP |
Bayar Poin / Bonus Sales | β | β | β |
JU |
Jurnal Umum | β | β | β |
OH/OHP/OP |
Saldo Awal / Audit Hutang-Piutang | OPNAME | β | β |
Daftar ini = peta fitur bisnis. Setiap kode harus tetap berfungsi di sistem baru.
3. Aliran Logika Saat Transaksi Dibuat (contoh: Penjualan PJ)¶
Pahami satu alur ini, Anda paham 80% sistem:
Aplikasi INSERT ke t (kdtrans='PJ')
ββ trigger t_tr_bi (BEFORE INSERT):
β’ id = getidtrans() (ID manual = max+1)
β’ nobukti = getnobukti('PJ') (no urut per jenis)
β’ notrans = kdpc.PJ.nobukti
β’ salin setelan terakhir operator (rekening, lokasi, dll)
β’ ambil konfigurasi akun dari datakode
Aplikasi INSERT ke d (per barang)
ββ trigger d_tr_bi (BEFORE INSERT):
β’ id = idtrans.idbarang (PK string gabungan)
β’ urut = max(urut)+1 per transaksi
β’ ambil harga jual (jual1..jual5 sesuai jharga)
β’ cek stok di tabel s; hitung qty & mutasi (mutasi = Β± qty)
β’ HPP via a_getfifo() (rata-rata tertimbang lapisan FIFO)
ββ trigger d_tr_ai (AFTER INSERT):
β’ calc_stok_by_id_il() (hitung ulang stok realtime ke s)
β’ a_setfifo() (catat konsumsi lapisan FIFO)
Saat transaksi diposting:
ββ a_posttrans() β a_setjurnaltransaksi()
β’ DELETE jurnal lama transaksi ini
β’ INSERT pasangan debit/kredit ke j sesuai datakode + brggolongan
Catatan kunci:
- mutasi = pergerakan stok bertanda (+masuk / βkeluar) = barangdebit Γ qty.
Stok realtime = SUM(mutasi) per barang per lokasi. Tabel s hanyalah cache.
- HPP FIFO: a_getfifo membaca lapisan fifo (urut tanggal) dan menghitung biaya
rata-rata tertimbang dari lapisan yang terpakai. a_setfifo mencatat pemakaiannya.
- Kolom cek di t/d dipakai sebagai sinyal kontrol (nilai 56, 66, 7) untuk
memerintahkan trigger melewati/menyesuaikan logika. Pola ini rapuh β lihat Β§4.
4. Katalog Utang Teknis (diurut berdasarkan tingkat bahaya)¶
π΄ Kritis β harus diperbaiki saat migrasi¶
| # | Masalah | Kenapa berbahaya | Solusi di sistem baru |
|---|---|---|---|
| K1 | ID dibuat manual max(id)+1 (getidtrans, getnobukti) |
Dua transaksi bersamaan bisa dapat ID/nobukti sama β data tumpang tindih | BIGINT GENERATED ALWAYS AS IDENTITY + nomor dokumen via sequence/tabel counter ber-lock |
| K2 | PK d = string idtrans.idbarang |
Barang sama tak bisa muncul 2 baris dalam 1 transaksi (beda harga/lokasi); rawan tabrakan | PK BIGINT identity; keunikan bisnis via constraint terpisah bila perlu |
| K3 | j (jurnal) tanpa PK / tanpa ID |
Sulit audit, sulit koreksi 1 baris, mudah duplikat | id BIGINT IDENTITY, FK ke transaksi, index |
| K4 | Nomor akun rekening.kode bertipe decimal(12,2) |
Akun "1.10.01" jadi angka; rawan pembulatan & sulit dibaca | kode INT (kode Γ 100: 110.01 β 11001); hierarki numerik bersih: klas 1β9, subklas 110β800, akun 11001ββ¦ |
| K5 | Kolom cek sebagai sinyal kontrol trigger |
Logika tersembunyi di "angka ajaib" (56/66/7); sangat sulit dibaca tim | Ganti dengan kolom status eksplisit + parameter fungsi yang jelas |
| K6 | Charset latin1 |
Karakter non-latin rusak; tidak siap masa depan | PostgreSQL UTF8 (default) |
π Tinggi β sangat mengganggu pemeliharaan¶
| # | Masalah | Dampak | Solusi |
|---|---|---|---|
| T1 | Β±150 prosedur & Β±100 trigger tak terdokumentasi | Logika tak terlihat; risiko fitur hilang saat migrasi | Inventarisasi & dokumentasikan (mulai Dokumen 05); yang tak terpakai dibuang |
| T2 | Banyak view tampilan menumpuk (lihat Β§5) β bukan duplikasi tabel | Pembaca baru bisa salah sangka itu tabel kembar; sebagian view mungkin tak terpakai lagi | Inventaris view, port yang dipakai ke PostgreSQL, buang yang mati. Tabel dasar TIDAK dikonsolidasi karena memang tidak kembar |
| T3 | Tabel t Β±60 kolom, d Β±45 kolom, mayoritas NULL |
Sulit dipahami; kolom dipakai beda arti tergantung kdtrans |
Pisah kolom umum vs spesifik; pertimbangkan tabel turunan/JSONB untuk yang jarang |
| T4 | SQL disimpan sebagai data (datakode.sql_biaya, datakode.detail/master) |
SQL dinamis = sulit di-review, risiko keamanan, sulit dilacak | Pindahkan logika ke fungsi PL/pgSQL bernama |
| T5 | Variabel sesi @var dipakai lintas statement di prosedur |
Rapuh, sulit ditelusuri, tidak aman di koneksi pool | Variabel lokal DECLARE dalam fungsi PL/pgSQL |
| T6 | Nama tabel/kolom 1β3 huruf | Hanya pembuat yang paham | Penamaan jelas (Dokumen 02 & 03) |
π‘ Sedang β rapikan sambil jalan¶
| # | Masalah | Solusi |
|---|---|---|
| S1 | Penamaan campur (created, dibuat, created_date, updated, aksesakhir) |
Standarkan created_at / updated_at (Dokumen 07) |
| S2 | Banyak tabel temp_* & *_draft & *lite |
Pisahkan: tabel kerja sementara β skema inti |
| S3 | 63 view, sebagian tumpang tindih | Inventaris; pertahankan yang dipakai laporan, buang sisanya |
| S4 | tinyint(1) dipakai sebagai boolean & angka campur |
Tegaskan tipe boolean di skema baru |
| S5 | Tidak ada kolom tenant; multi-tenant lama dipisah secara fisik | Sistem baru memakai schema-per-tenant di database erp, distandarkan lewat template & runner (Dokumen 04) |
5. Tabel vs View β Klarifikasi Penting (BUKAN duplikasi tabel)¶
Catatan dari pemilik (penting untuk semua pembaca): nama-nama seperti
barang,barang_mini,baranglengkap,baranglite,mobile_barang,v_barang*bukan tabel kembar dan bukan kerja yang berantakan. Itu adalah VIEW β "jendela" baca yang sengaja dibuat agar aplikasi mudah menampilkan data lengkap tanpa menulis query gabungan yang rumit berulang kali. Ini justru praktik yang baik. Jangan menyimpulkan ini sebagai utang teknis.
Dari 183 objek di database, sebenarnya:
| Jenis objek | Jumlah | Arti |
|---|---|---|
Tabel dasar (BASE TABLE) |
120 | Tempat data benar-benar disimpan |
β di antaranya tabel kerja (temp_*,*_draft,*_temp) |
21 | Sementara, bukan skema inti |
| β sisanya tabel skema "inti" | Β±99 | Master & transaksi sesungguhnya |
View (VIEW) |
63 | Bukan tabel β hanya cara menampilkan data lebih mudah |
Jadi gambaran sebenarnya jauh lebih sehat dari kesan "183 tabel berantakan".
Pemetaan yang benar: 1 tabel sumber + keluarga view di atasnya¶
| Konsep | Tabel SUMBER (data nyata) | View di atasnya (hanya tampilan, bukan duplikat) |
|---|---|---|
| Barang | brg (tabel; di-FK oleh d) |
barang, barang_mini, barang_pg, barang_v2, baranglengkap, baranglite, brg_with_stok, mobile_barang, v_barang, v_barang_json β semua VIEW |
| Kontak | ktk (tabel; di-FK oleh t) |
kontak, kontak_lengkap, kontakalamatlengkap, kontaklite, minikontak β semua VIEW |
| Transaksi | t (tabel) |
transaksi, transaksilite, transaksiliteperiode, transaksi_draft β semua VIEW |
| Detail | d (tabel) |
detail, detail_draft β VIEW |
| Stok | s (tabel cache; sumber = d.mutasi) |
stok, stoklite, stokperiode, rekapstok, cek_stok, v_stok β semua VIEW |
| Harga | harga, diskon, diskond (tabel terpisah, fungsi beda) |
a_harga, a_harga_master, a_harga_rekomendasi β VIEW |
| Tabel kerja transaksi | t_draft, t_temp, temp_trans, d_draft, d_retur, temp_d (tabel kerja nyata) |
β |
Cara membaca tabel ini: kolom tengah = satu tempat data sebenarnya disimpan (sumber kebenaran). Kolom kanan = view yang membaca dari sumber itu untuk memudahkan tampilan/laporan/aplikasi mobile. Tidak ada konsolidasi data yang perlu dilakukan di sini β datanya sudah tunggal.
Implikasi untuk migrasi (yang benar)¶
- View tidak menyimpan data, jadi tidak perlu di-ETL. Yang perlu: menulis ulang definisi view yang masih dipakai ke sintaks PostgreSQL (Dokumen 06).
- Inventaris view: tandai mana yang masih dipakai aplikasi/laporan vs yang sudah mati β yang mati dibuang, yang hidup di-port.
- Untuk tabel dasar (kolom tengah): buktikan dengan query bahwa memang itu
satu-satunya sumber (cek FK & trigger). Berdasarkan analisis,
brg/ktk/t/d/sjelas kanonik karena dipakai sebagai target FOREIGN KEY oleh tabel inti lain.
Tindakan: fokus inventaris ke view (mana dipakai/mati), bukan ke "konsolidasi tabel kembar" β karena tabelnya memang tidak kembar. Catat hasil di Dokumen 02.
6. Apa yang Sudah BAIK (jangan dirusak saat migrasi)¶
- Model metadata-driven
datakodesangat fleksibel untuk banyak jenis usaha (dagang/produksi/jasa). Konsep ini dipertahankan, hanya dirapikan. - Trigger-first memberi konsistensi: semua aplikasi (Delphi/Flutter) dapat perlakuan akuntansi yang sama. Ini sesuai keinginan pemilik dan tetap dipakai.
- FIFO HPP berlapis (
fifo/fifokeluar) sudah benar secara akuntansi. Logikanya dipindah apa adanya ke PL/pgSQL. - Stok: pola cache + jangkar rekonsiliasi (klarifikasi pemilik) β ini desain yang benar & disengaja, bukan sekadar cache:
s(peridlokasi) &brg.stok(total) = sumber tampilan realtime & cepat yang disukai pengguna; dijaga trigger.- Sumber kebenaran =
SUM(mutasi)dari detail. - Audit cepat: hitung ulang dari mutasi lalu bandingkan dengan
s; jika beda β ada transaksi/logika salah, lalu ditelusuri. Di antara ribuan transaksi, inilah cara menemukan 1 kasus yang menyimpang. - Dipertahankan, dan dinaikkan statusnya jadi fitur terdokumentasi (fungsi rekonsiliasi resmi) di Dokumen 03 & 05 β bukan trik manual.
7. Tindak Lanjut (sebelum desain skema baru difinalkan)¶
- Inventarisasi prosedur & trigger: buat daftar Β±150 prosedur β tandai dipakai / tidak dipakai / duplikat. Tanpa ini, estimasi waktu tak akurat.
- Inventaris view (Β§5): tandai view mana yang masih dipakai aplikasi/laporan vs yang sudah mati. View hidup di-port ke PostgreSQL; yang mati dibuang. (Bukan "konsolidasi tabel" β tabel dasar tidak kembar.)
- Identifikasi semua arti kolom
cek(angka 7/56/66/...) β ini logika tersembunyi. - Tarik daftar laporan yang dipakai (dari 63 view + prosedur
periodic*,omset*,neraca*,rugilaba*) β ini kontrak fungsional yang harus tetap jalan. - Hasil 1β4 dicatat ke Dokumen 02 (Kamus Data) sebagai sumber kebenaran tim.
Perintah bantu (read-only) untuk inventaris ada di Dokumen 06 Β§Lampiran.