10 โ Arsitektur Klien, Autentikasi & Realtime¶
Dibaca oleh: Backend & developer klien (web/Flutter/desktop). Tujuan: menyatukan rencana fitur baru (AI, chat, notifikasi realtime) dan kenyataan klien sekarang (Delphi/DevExpress) ke dalam satu arah arsitektur yang konsisten dengan keputusan yang sudah dibuat (PostgreSQL trigger-first, satu backend API, 1 database
erpdengan schema-per-tenant).Lahir dari konsultasi 2026-05-17. Statusnya rencana/arah yang direkomendasikan; difinalkan saat pemilik setuju.
1. Peta Besar¶
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Firebase (layanan) โ
โ Auth (Google/email/MFA) FCM (push Android)โ
โโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโ
โ verifikasi token โ kirim notif
Web (baru) โโ โผ โฒ
Flutter โโโผโโบ Backend API (Node/TS) โโโโโโโโ
Delphi* โโโ โ authZ: pusat.pengguna โ LISTEN
โผ โ
PostgreSQL erp: pusat + tenant schema (trigger-first)
โ pg_notify saat event penting โโโโโโโโ
* Delphi = transisi, akses DB langsung apa adanya, lalu DIPENSIUNKAN (ยง5)
Prinsip pembatas (penting, sejalan Dokumen 03 ยง7 soal JSONB):
PostgreSQL = sistem pencatat (akuntansi, stok, transaksi). Firebase = layanan komunikasi & identitas (auth, push, chat). Jangan pernah menaruh data keuangan di Firebase. Jangan menaruh chat/notifikasi volatil sebagai beban utama di Postgres.
2. Autentikasi โ Firebase Auth (rencana pemilik)¶
Keputusan terkonfirmasi 2026-05-17: proyek Firebase autentikasi = SATU proyek
luckyjayagroup(Project ID:luckyjayagroup) untuk SEMUA tenant & klien. Otorisasi per-tenant tetap dipusat.pengguna_tenantโ bukan proyek Firebase per tenant. Proyek Firebase lain yang ada di akun (luckyjayagroupleontech/leontech,lele,commands-center) TIDAK dipakai sebagai sumber auth sistem ini. Status: CLI & login siap (knavinkids@gmail.com); workspace belum di-firebase init, Service Account Admin SDK belum dibuat (lihatdocs/TODO.mdยงC &backend/TODO.md).
Pemilik memakai Google Auth / Firebase Auth. Ini cocok dan direkomendasikan, dengan pembagian peran yang tegas:
| Lapis | Tugas | Di mana |
|---|---|---|
| Autentikasi | Buktikan "siapa kamu": password, Google sign-in, MFA, reset password | Firebase Auth |
| Otorisasi | "Boleh akses tenant & peran apa" | pusat.pengguna + pusat.pengguna_tenant (Dokumen 04/08) |
Alur (ringkas; detail di frontend/flutter/erp/AUTH.md):
- Klien login via Firebase โ dapat Firebase ID token.
- Backend verifikasi token (Firebase Admin SDK) โ
POST /api/auth/sesi(HTTP 200 selalu). - Jika
firebase_uidada dipusat.penggunaโterdaftar: true+ JWT aplikasi. Jika tidak ada โterdaftar: false+access_token: nullโ klien tampilkanDashboardUmumPage(bukan error 401). Siapapun bisa login Google. - Pilih tenant โ JWT berisi
tenant_kode+peran(FK kepusat.tipe_pengguna) kontak_idlokal โ routing ke pool/schema tenant.- Response pilih tenant juga membawa context akses yang disimpan lokal:
akses_lokasi[]danakses_kas[].
2.1 Context Lokal Setelah Pilih Tenant¶
Setelah tenant dipilih, secure storage lokal klien minimal harus menyimpan:
tenant_kodeperankontak_id_lokalakses_lokasi[]akses_kas[]
Fungsi data ini:
- default filter UI
- membatasi opsi lokasi/akun kas yang ditampilkan
- menjaga UX tetap konsisten antar screen
Tetapi keamanan akses tetap milik backend. Klien tidak boleh dianggap sebagai sumber kebenaran untuk otorisasi data.
2.2 Mapping Canonical Peran dari Legacy inet.tipe¶
Mapping yang dipakai sistem sekarang:
inet.tipe |
inet_tipe.nama |
tipe_pengguna.kode |
|---|---|---|
0 |
None |
none |
1 |
Sales |
sales |
2 |
Gudang |
gudang |
3 |
Admin |
admin |
4 |
Direksi |
direksi |
5 |
Customer |
customer |
6 |
Umum |
umum |
Keuntungan langsung:
- Hapus utang keamanan nyata:
inetlama simpan password plaintext + dynamic SQL rawan injeksi (temuan Dokumen 08). Dengan Firebase, password tak pernah lagi ada di DB kita. - Satu identitas untuk semua klien (web/Flutter/desktop-baru), menggantikan dua
pintu lama (
operatordesktop +inetmobile). - Fitur berat (MFA, reset, Google) gratis & terkelola โ tak perlu dibangun.
Catatan: pusat.pengguna tidak menyimpan password (lihat skema Dokumen 04
ยง3, kolom firebase_uid). Saat onboarding, user lama dipetakan ke akun Firebase
lewat email.
3. Notifikasi Realtime (Android kuat) โ FCM + LISTEN/NOTIFY¶
Implementasi Flutter:
frontend/flutter/erp/NOTIFIKASI-FCM.md
Pemilik ingin notifikasi realtime yang kuat untuk Android. Rekomendasi: Firebase Cloud Messaging (FCM) โ pasangan alami Firebase Auth, terbaik untuk push Android (jalan walau app background/tertutup, andal).
Bagaimana ia menyatu dengan trigger-first (logika di DB)?
Event penting terjadi (mis. transaksi disetujui, stok kritis)
โโ trigger / fungsi PL/pgSQL โ pg_notify('kanal_notif', payload JSON)
โโ Backend LISTEN 'kanal_notif'
โโ kirim push via FCM ke device token user terkait
- DB tetap jadi sumber peristiwa (konsisten dengan filosofi trigger-first):
trigger memutuskan "ini layak dinotifikasi",
pg_notifymengirim sinyalnya. - Backend = relai: dari
LISTENPostgres โ FCM. - Device token FCM per pengguna disimpan di
pusat(mis.pusat.perangkat(pengguna_id, fcm_token, platform, terakhir_aktif)). - Jangan memanggil FCM langsung dari trigger (jangan ada HTTP di dalam DB).
Trigger hanya
pg_notify; backend yang bicara ke dunia luar.
3.1 Keputusan Proyek Saat Ini¶
Untuk sistem ERP ini, notifikasi user dipakukan ke FCM-only. WebSocket/SSE tidak dipakai sebagai jalur notifikasi operasional.
Alasan:
- push tetap sampai saat app background atau tertutup
- delivery lintas Android lebih andal
- jalur notifikasi lebih sederhana untuk diaudit dan dioperasikan
3.2 Targeting Notifikasi¶
Backend harus mampu memilih target notifikasi dengan mode berikut:
- per email
- per
(tenant_kode, lokasi_id)untuk user aktif yang memiliki[tenant].pengguna_akses_lokasi.is_default = true - per
(tenant_kode, tipe_pengguna)sepertiadmin,direksi,gudang,sales,customer, dan seterusnya - per aturan tenant (
notifikasi_aturan+notifikasi_aturan_penerima) berdasarkankode_eventdan opsionaljenis_kode
Aturan default tenant dibuat di V908__notifikasi_aturan_tenant.sql.
Trigger mengirim target_type=aturan; backend membaca aturan tenant untuk
menentukan penerima FCM. Dengan pola ini target penerima tidak hardcode di
trigger dan bisa diubah per tenant/per jenis transaksi.
3.3 Registrasi Token Device¶
Alur registrasi token yang benar:
- user login ke Firebase
- backend menerbitkan JWT aplikasi
- setelah user memilih tenant, app mengambil token FCM
- app mengirim token itu ke backend
- backend menyimpan ke
pusat.perangkat
FCM bukan sumber data bisnis. FCM hanya jalur pengantaran notifikasi.
3.4 Alur User Login Tapi Belum Punya Tenant¶
Alur operasional yang sekarang dipakai:
- user login berhasil ke Firebase dan terdaftar di ERP
GET /api/tenantsmengembalikan daftar kosong- klien diarahkan ke mode
umum/none - di mode itu user dapat aksi
Request Permission - klien memanggil
POST /api/notifikasi/request-akses-tenant - backend mengirim FCM ke semua user aktif berperan
admindandireksi - payload notifikasi membawa
type=request_permission_tenantdanmodul_kode=PENGATURAN_PENGGUNA - saat notifikasi disentuh, aplikasi membuka screen
Pengaturan Pengguna
3.5 Event Bisnis via pg_notify dan Aturan Tenant¶
Trigger di DB menghasilkan pg_notify untuk event bisnis utama.
Payload yang dikirim backend ke Flutter selalu mengandung field data.modul_kode
sehingga deep-link langsung membuka tab yang tepat, dan data membawa
identifier baris terkait (barang_id, transaksi_id, dll.).
data.type |
Trigger pada | Kondisi | Default penerima | data.modul_kode |
|---|---|---|---|---|
draft_diajukan |
transaksi_draft UPDATE status |
status menjadi diajukan |
admin; SO/PM/WO punya override | TRANSAKSI_DRAFT / dashboard modul |
draft_disetujui |
transaksi_draft UPDATE status |
status menjadi disetujui |
pembuat draft; SO juga gudang | TRANSAKSI_DRAFT |
draft_ditolak / draft_revisi / draft_batal |
transaksi_draft UPDATE status |
status terkait | pembuat draft | TRANSAKSI_DRAFT |
pj_dalam_proses / pj_cetak_nota / pj_ready / pj_selesai |
transaksi INSERT/UPDATE status |
PJ status 1..4 |
gudang/admin/sales sesuai tahap | PENJUALAN_DASHBOARD |
transaksi_final_dibuat |
transaksi INSERT |
PB/PL/BB/HP final | gudang/admin sesuai jenis | modul jenis transaksi |
piutang_jatuh_tempo |
transaksi INSERT |
PJ/PI, jatuh tempo โค 7 hari | direksi, admin, sales transaksi | LAPORAN_PIUTANG |
stok_kritis |
stok UPDATE qty |
qty โ โค0 dari positif | gudang, admin | LAPORAN_STOK |
jurnal_tidak_imbang |
transaksi INSERT (DEFERRED) |
jurnal debit โ kredit | admin, direksi | LAPORAN_JURNAL |
Tabel konfigurasi tenant:
notifikasi_aturan: event, template judul/body,modul_kode, aktif/nonaktif.notifikasi_aturan_penerima: targetperan,pengguna,kontak_field,supervisor_kontak_field, ataulokasi_field_default.notifikasi_log: audit/inbox hasil resolve dan pengiriman.
Deep-link di klien Flutter:
NotificationNavigationService.storeFilter(modulKode, data)menyimpan payload bisnis sebelum tab dibuka.- Screen tujuan membaca filter via
takeFilter(modulKode)diinitState(). - Banner notifikasi muncul di laporan yang dibuka dari notifikasi, menampilkan konteks (nomor_transaksi, jatuh_tempo, barang_id) agar user langsung paham.
4. Chat & AI (fitur baru) โ bangun API-first¶
Fitur baru (chat, asisten AI) tidak boleh mewarisi utang akses-DB-langsung. Dibangun API-first sejak awal:
| Fitur | Rekomendasi | Catatan |
|---|---|---|
| Chat | Pesan via backend API + WebSocket; simpan di Postgres tenant (kerja/sistem) atau Firestore bila perlu cepat & realtime out-of-the-box |
Jangan taruh data keuangan di sini. Chat = data komunikasi |
| Notifikasi | FCM (push) + tabel notifikasi per tenant untuk riwayat/inbox |
Riwayat tetap di Postgres agar bisa diaudit |
| Asisten AI | Backend memanggil model AI; akses data hanya lewat API/fungsi DB bernama, jangan beri AI kredensial DB langsung | Batasi per tenant & peran (JWT) |
Pegangan: fitur baru = percontohan cara benar. Kalau yang baru saja sudah "langsung tembak DB", peremajaan ini kalah sebelum mulai.
5. Klien Desktop: Delphi/DevExpress โ Strategi "Sunset", Bukan "Modernkan"¶
Kenyataan & perasaan pemilik (dihormati)¶
- Suka Delphi karena DevExpress
TdxDBGrid: kolom mudah di-custom, ada lookup. Itu produktivitas nyata, bukan sekadar selera. - Tidak suka: SDM paham Pascal makin langka.
- Sadar bahaya akses DB langsung, tapi merasa "API + Delphi" sangat rumit, jadi merasa tak punya pilihan.
Pembingkaian ulang yang melegakan¶
Pemilik tidak perlu menyelesaikan "API + Delphi" (memang rumit & sia-sia). Yang benar:
Delphi โ BIARKAN apa adanya (akses DB langsung) selama transisi
โ JANGAN investasi meng-API-kan Delphi
โ PENSIUNKAN, diganti klien web/Flutter baru yang API-first
Jadi kerumitan "API+Delphi" tidak pernah perlu dipecahkan โ Delphi disetop, bukan dimodernkan. Energi diarahkan ke klien baru yang bersih.
Pengganti grid DevExpress (agar fitur tak "turun kelas")¶
Kekhawatiran utama: kehilangan kekayaan grid (kolom custom, lookup, edit inline, grouping). Rekomendasi yang mempertahankan paradigma DevExpress:
| Target | Rekomendasi utama | Kenapa | Alternatif |
|---|---|---|---|
| Web | DevExtreme DataGrid atau DevExpress Blazor Grid (vendor sama: DevExpress) | Model mental sama persis: lookup column, column chooser, edit, master-detail, grouping. Kurva belajar paling landai dari TdxDBGrid |
AG Grid (paling kuat, paradigma beda) |
| Android/Flutter | Syncfusion Flutter DataGrid | Sangat kaya: edit, sort, kolom custom, lookup bisa dibangun | PlutoGrid (gratis, ringan) |
Memilih DevExtreme/Blazor = tetap di ekosistem DevExpress yang sudah dikuasai pemilik, hanya pindah platform. Ini jembatan paling halus dari Delphi โ web.
Urutan praktis¶
- Bangun backend API + auth (Firebase) lebih dulu (fondasi semua klien).
- Bangun klien baru (web DevExtreme/Blazor; Android Flutter) fitur per fitur, mulai dari yang sering dipakai (transaksi inti + grid).
- Delphi tetap jalan paralel (direct DB) sampai klien baru menutupi fiturnya.
- Setelah fitur tertutup & teruji โ pensiunkan Delphi modul demi modul.
"API + Delphi" tidak ada di peta jalan. Yang ada: "API + klien baru" โ "Delphi pensiun". Dengan bantuan AI, ini realistis.
6. Ringkasan Keputusan/Arah¶
| Topik | Arah | Dampak |
|---|---|---|
| Autentikasi | Firebase Auth (authN) + pusat.pengguna (authZ) |
Firebase-first: login selalu berhasil; terdaftar flag yang membuka akses ERP |
| Push Android | FCM, dipicu pg_notify dari trigger โ backend relai |
Tabel pusat.perangkat (fcm_token); tak ada HTTP di trigger; tanpa WebSocket |
| Chat/AI | API-first; data komunikasi terpisah dari data keuangan | Postgres = pencatat; Firebase = komunikasi/identitas |
| Klien desktop | Delphi sunset, bukan di-API-kan | Tak ada proyek "API+Delphi"; energi ke klien baru |
| Grid pengganti | Web: DevExtreme/Blazor โข Flutter: Syncfusion | Paradigma DevExpress dipertahankan; kurva belajar landai |
| OLAP / dashboard analitik | CDC PostgreSQL โ ClickHouse via Debezium+Kafka | Pipeline live sejak 2026-05-21 โ lihat database/cdc/ |
Dokumen terkait yang diselaraskan: 00 (stack), 04 (skema
pengguna+ alur login), 08 (identitas, keamanan). Keputusan dicatat di memori proyek.