08 β Konsolidasi Pusat, Identitas & Penomoran¶
Dibaca oleh: Database Engineer & Backend Engineer. Tujuan: menjawab tiga keraguan arsitektur tersulit pemilik secara utuh β (a) bagaimana laporan konsolidasi ke pusat tanpa repot mapping, (b) bagaimana identitas login vs identitas orang, (c) bagaimana penomoran transaksi anti-bentrok tanpa "kode perangkat".
Dokumen ini lahir dari sesi konsultasi 2026-05-17. Keputusan di sini sudah disepakati pemilik dan menjadi acuan; jika berubah, perbarui dokumen ini dan memori proyek.
Konteks penting yang menjelaskan banyak hal di sistem lama: database ini sudah dua kali bermigrasi (Paradox/Firebird β MySQL β akan ke PostgreSQL) dan tumbuh dari 1 user β multi-user, dikerjakan sendirian tanpa tim selama Β±20 tahun oleh orang yang bukan akuntan & bukan IT formal. Banyak "keanehan" sistem lama adalah solusi pragmatis yang benar untuk zamannya. Kita rapikan, bukan menghakimi.
1. Penomoran Transaksi β Pensiun kdpc (kode perangkat)¶
Kenapa dulu ada (dan kenapa itu cerdas)¶
Dulu beberapa aplikasi/perangkat membuat transaksi bersamaan. Tanpa server
pembuat-ID terpusat, dua perangkat bisa menebak max(id)+1 yang sama β bentrok
nomor. Solusi pemilik: beri tiap perangkat satu kode (kdpc) sehingga nomor
tidak mungkin bentrok.
Ini persis pola "node/machine ID" pada distributed ID β dipakai Twitter Snowflake, MongoDB ObjectId, dll. Ditemukan sendiri tanpa tim. Bukan aib; itu insting rekayasa yang benar.
Kenapa sekarang tidak diperlukan lagi¶
Di PostgreSQL + satu backend API, ID dibuat server secara atomik. Tidak ada lagi banyak perangkat menebak ID sendiri.
| Kebutuhan lama | Pengganti baru |
|---|---|
| ID transaksi unik anti-bentrok | id bigint GENERATED ALWAYS AS IDENTITY |
nobukti urut per jenis, anti-bentrok |
logika.ambil_nomor_bukti(jenis) dengan lock transaksi; berjalan dalam schema tenant aktif |
notrans = kdpc.kdtrans.nobukti |
nomor_transaksi = jenis_kode.nomor_bukti (tanpa kode perangkat) |
| Tahu transaksi dibuat dari perangkat apa | Metadata opsional sumber_perangkat / kanal (desktop/mobile/web) β untuk audit, BUKAN bagian kunci |
Aturan¶
kdpcdibuang dari pembentukan ID & nomor. Keunikan dijamin sequence DB.- Jika riwayat "transaksi dari perangkat mana" masih berguna β simpan sebagai
kolom metadata
sumber_perangkat, bukan kunci. - Menyelesaikan utang K1 (Dokumen 01).
Migrasi:
notranslama (mengandungkdpc) tetap disimpan apa adanya di kolomnomor_transaksi_lamauntuk penelusuran histori; transaksi baru memakai format baru tanpakdpc.
2. Bagan Akun Terpusat & Konsolidasi¶
Masalah nyata (kata pemilik)¶
Toko A & Pabrik B punya CoA masing-masing. Satu rekening bank fisik (BCA
082842834) bisa berkode 120.00 di tenant A dan 120.01 di tenant B. Untuk
menjumlahkan saldo BCA itu lintas tenant β harus mapping manual tiap bikin
laporan. Repot & rawan salah.
Keputusan: Bagan Akun standar grup, dikelola pusat¶
βββββββββββββββββββββββββββββββββββββββββββ
β schema pusat β
β akun_master (CoA STANDAR seluruh grup) β β satu sumber kebenaran
βββββββββββββββββ¬ββββββββββββββββββββββββββ
β seed / sync (runner, Dokumen 04 Β§5)
βββββββββββββββββββββΌββββββββββββββββββββ
βΌ βΌ βΌ
tenant_A.akun tenant_B.akun tenant_C.akun β salinan LOKAL identik
(FK lokal valid) (FK lokal valid) (FK lokal valid)
pusat.akun_master= satu skema kode akun untuk semua tenant. BCA 082842834 = kode yang sama di mana pun (mis.1.10.120).- CoA didistribusikan (seed/sync) sebagai salinan lokal ke tiap schema tenant β
FK
transaksi.akun_kode β akun(kode)tetap lokal & valid. Ini adalah pilihan desain agar tenant bisa punya status/ekstensi lokal. - Konsolidasi jadi sepele: kode seragam β jumlah saldo BCA lintas tenant =
SUM(...) GROUP BY akun_kode. Tidak perlu mapping lagi, selamanya. - Tenant boleh punya sub-akun khusus, tetapi dalam rentang yang diatur pusat
(governed) dan terdaftar di
pusat.akun_master. - Opsional: registry rekening bank fisik global
pusat.bank_fisik(bank, no_rekening, akun_kode)untuk: cocokkan ke rekening koran, dan tangani kasus 1 rekening fisik dipakai > 1 tenant.
Biaya: pemetaan satu kali saat migrasi¶
Saat big-bang, petakan kode lama tiap tenant β kode standar sekali. Sesudah
itu tidak pernah mapping lagi. Jauh lebih murah daripada mapping seumur hidup
setiap kali bikin laporan. Pemetaan ini didokumentasikan (tabel
map_akun(tenant, kode_lama, kode_standar)), masuk pipeline ETL (Dokumen 06).
Lapisan Pusat (konsolidasi)¶
- Schema
pusat/gudang = data turunan untuk laporan grup (ringkasan jurnal perakun_kodeper tenant per periode). Bukan DB transaksional. - Diisi via ETL/sync terjadwal dari tiap tenant. Karena CoA seragam, agregasi langsung jalan.
- Lanjutan (belum perlu sekarang, ditandai): laporan konsolidasi "benar" secara akuntansi butuh eliminasi transaksi antar-entitas (mis. A jual ke B). Catat sebagai backlog, jangan dikerjakan di fase awal.
3. Identitas: User (login) vs ktk (orang)¶
Prinsip pemisahan¶
| Konsep | Apa | Di mana |
|---|---|---|
| Autentikasi | Membuktikan "siapa kamu" (password, Google, MFA) | Firebase Auth (eksternal) β lihat Dokumen 10 |
| User / otorisasi | Boleh akses tenant & peran apa; dikunci firebase_uid |
Global di pusat.pengguna (TANPA password) |
ktk / kontak |
Identitas manusia / ketenagakerjaan (pegawai, sales) | Per tenant di kontak pada schema tenant |
Satu orang bisa: punya 1 login global, tetapi menjadi "pegawai" di > 1 tenant dengan peran berbeda. Karena itu ketiganya wajib dipisah.
Keamanan (memperbaiki utang nyata):
inetlama menyimpan password plaintext + dynamic SQL rawan injeksi. Dengan Firebase Auth, password tak pernah lagi disimpan di DB kita β utang itu hilang sepenuhnya.
Penghubung (bridge) β kunci stabil antar-schema¶
-- di schema pusat
pusat.pengguna_tenant (
pengguna_id bigint REFERENCES pusat.pengguna(id),
tenant_kode varchar REFERENCES pusat.tenant(kode),
peran varchar, -- admin/kasir/akunting/...
kontak_id_lokal bigint, -- id kontak di schema tenant tsb (kunci stabil)
kunci_stabil varchar, -- email / kode pegawai (cadangan pencocokan)
PRIMARY KEY (pengguna_id, tenant_kode)
)
kontak_id_lokal/ email / kode pegawai = kunci stabil untuk menghubungkan login global ke kontak tenant.- FK lokal
transaksi.pegawai_id β kontak(id)di schema tenant tetap ditegakkan di dalam tenant (integritas tidak dilonggarkan). - Yang sengaja tidak dibuat FK langsung adalah
pusat.pengguna_tenant.kontak_id_lokalke semua schema tenant, karena target schema bergantungtenant_kode.
Catatan:
inet.idkontakdi sistem lama membuktikan pemilik sudah setengah jalan memikirkan jembatan ini. Instingnya benar; kita formalkan.
Satukan dua pintu login (opr desktop + inet mobile)¶
Saat ini desktop login via tabel opr, mobile via inet β dua sumber kebenaran
identitas. Keputusan:
- Satu identitas global: Firebase Auth (autentikasi) +
pusat.pengguna(otorisasi), via satu backend API untuk semua klien (lihat Dokumen 10). opr+inetdilebur (dedup per orang) kepusat.penggunasaat migrasi; saat onboarding, user dipetakan ke akun Firebase (by email) β isifirebase_uid.- Delphi (transisi) tetap pakai
operatorlokal apa adanya; tidak di-API-kan (rumit, sia-sia). Delphi dipensiunkan, diganti klien web/Flutter yang API-first β bukan dimodernkan (lihat Dokumen 10 Β§strategi sunset Delphi).
4. Foreign Key ke Master Pusat β Prinsip Umum¶
β οΈ UPDATE (2026-05-17): model jadi schema-per-tenant dalam SATU DB
erp. Karenapusatdantenant_<kode>kini satu database, FK lintas-schema BISA ditegakkan langsung β mis.tenant_x.transaksi.akun_kode β pusat.akun_master(kode). Ini lebih baik dari rencana lama (DB-per-tenant) yang memaksa "kunci stabil tanpa FK". Pilihan desain sekarang: - (A) FK langsung kepusat.*untuk master yang benar-benar global & read-only bagi tenant (mis.akun_master,tenant). Paling sederhana, integritas terkuat. - (B) Salinan lokal per schema tenant + FK lokal bila tenant perlu meng-extend/menonaktifkan baris CoA secara mandiri, atau untuk isolasi tegas. Pola di bawah (salinan lokal) tetap valid sebagai Opsi B; bukan lagi keharusan teknis. Identitas userβkontak lintas-schema tetap pakai kunci stabil (pengguna global dipusat, kontak per schema tenant).
Pertanyaan pemilik (konteks historis, saat model masih DB-per-tenant): "rekening dipindah ke pusat, bagaimana FK?" Pada model final schema-per-tenant, FK lintas-schema bisa. Namun pola salinan-lokal berikut tetap dipilih untuk CoA tenant agar setiap tenant bisa punya status/ekstensi lokal dengan FK lokal:
Master di pusat.akun_master ββ(seed/sync, runner)βββΊ salinan LOKAL di tiap schema tenant
FK ditegakkan LOKAL di tenant
- Pusat = sumber kebenaran (tempat ubah).
- Tenant = salinan baca yang ikut tervalidasi FK lokalnya.
- Sinkronisasi ikut runner migrasi/seed (Dokumen 04 Β§5βΒ§6); idempoten, bernomor.
- FK tidak dibuang. Justru menegakkan FK adalah salah satu target "benar" di
sistem baru (utang lama: integritas longgar). Yang relaks hanya relasi
yang target schema-nya dinamis, seperti
pusat.pengguna_tenant.kontak_id_lokal.
5. devisi β cabang¶
Sejarah (kenapa iddevisi ada)¶
iddevisi dulu dipakai saat pemilik menggabungkan beberapa perusahaan dalam 1
DB, dibedakan per devisi. Seiring bertambahnya pengetahuan, sistem baru
dipisah menjadi schema-per-tenant. Tapi karena banyak data lama terlanjur butuh
iddevisi, nilainya di-hardcode β praktis iddevisi hardcode = penanda
"pemilik tunggal DB ini".
Keputusan baru¶
- Semantik lama (multi-perusahaan via devisi dalam 1 DB) dibuang.
devisimurni menjadicabang: sub-entitas di dalam satu tenant (1 tenant = 1 perusahaan;cabang= cabang-cabangnya).- Skema baru: kolom
iddevisiβcabang_id(FK lokal kecabang). - Migrasi:
iddevisilama yang bermakna βcabangyang sesuai; sisanya β satucabangdefault per tenant. Logika hardcodeiddevisidihapus.
6. Ringkasan Keputusan & Dampak Migrasi¶
| Topik | Keputusan | Dampak migrasi |
|---|---|---|
kdpc |
Dibuang dari ID/nomor; opsional jadi metadata sumber_perangkat |
Format nomor_transaksi baru; simpan nomor_transaksi_lama |
| CoA | Master standar di pusat, distribusi salinan lokal | Pemetaan kode lamaβstandar sekali (map_akun) |
| Konsolidasi | Agregasi by akun_kode seragam di pusat/gudang |
Bangun lapisan pusat (data turunan) |
| Master pusat | pusat.akun_master; tenant memakai salinan lokal untuk CoA |
Proses seed/sync di runner |
User vs ktk |
Login global pusat.pengguna; kontak per tenant; bridge kunci stabil |
Lebur opr+inet, dedup per orang |
| Login | Satu pintu API untuk klien baru | Delphi lama dibiarkan saat transisi lalu dipensiunkan |
devisi |
Jadi cabang (sub-entitas dalam tenant); buang semantik lama |
iddevisiβcabang_id; hapus hardcode |
Dokumen terkait yang ikut diselaraskan: 01 (framing stok), 02 (kamus
klien/devisi), 03 (skema), 04 (multi-tenant & sync), 06 (ETLmap_akun).