Lewati ke isi

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

  • kdpc dibuang 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: notrans lama (mengandung kdpc) tetap disimpan apa adanya di kolom nomor_transaksi_lama untuk penelusuran histori; transaksi baru memakai format baru tanpa kdpc.


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)
  1. pusat.akun_master = satu skema kode akun untuk semua tenant. BCA 082842834 = kode yang sama di mana pun (mis. 1.10.120).
  2. 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.
  3. Konsolidasi jadi sepele: kode seragam β†’ jumlah saldo BCA lintas tenant = SUM(...) GROUP BY akun_kode. Tidak perlu mapping lagi, selamanya.
  4. Tenant boleh punya sub-akun khusus, tetapi dalam rentang yang diatur pusat (governed) dan terdaftar di pusat.akun_master.
  5. 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 per akun_kode per 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): inet lama 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_lokal ke semua schema tenant, karena target schema bergantung tenant_kode.

Catatan: inet.idkontak di 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 + inet dilebur (dedup per orang) ke pusat.pengguna saat migrasi; saat onboarding, user dipetakan ke akun Firebase (by email) β†’ isi firebase_uid.
  • Delphi (transisi) tetap pakai operator lokal 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. Karena pusat dan tenant_<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 ke pusat.* 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 di pusat, 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.
  • devisi murni menjadi cabang: sub-entitas di dalam satu tenant (1 tenant = 1 perusahaan; cabang = cabang-cabangnya).
  • Skema baru: kolom iddevisi β†’ cabang_id (FK lokal ke cabang).
  • Migrasi: iddevisi lama yang bermakna β†’ cabang yang sesuai; sisanya β†’ satu cabang default per tenant. Logika hardcode iddevisi dihapus.

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 (ETL map_akun).