Bisnis yang memanfaatkan KPI real-time di lingkungan ERP mereka memiliki keunggulan kompetitif melalui kemampuan untuk melihat, memahami, dan bertindak berdasarkan data secara real-time. Untuk tim eksekutif di perusahaan Indonesia, dashboard ERP sering menjadi deliverable paling terlihat dari seluruh proyek ERP — yang dilihat CEO setiap pagi. Mendapatkannya dengan benar berarti memilih metrik yang tepat (bukan setiap metrik), merancang untuk kecepatan, dan membuatnya actionable.
Kesalahan paling umum dalam dashboard eksekutif adalah menampilkan terlalu banyak metrik. Dashboard eksekutif harus memiliki maksimal 8-12 KPI — yang mencerminkan kesehatan bisnis dan memungkinkan keputusan. Untuk perusahaan dagang di Indonesia, KPI intinya adalah: Pendapatan (bulan ini vs. target, vs. bulan yang sama tahun lalu), Margin Laba Kotor %, DSO AR, DPO AP, Perputaran Inventaris, Posisi Kas, Pesanan Terbuka, dan Faktur Jatuh Tempo.
Untuk performa, dashboard eksekutif tidak boleh langsung mengkueri tabel transaksi mentah. Kami pra-menghitung tabel daily_kpi_snapshot yang menyimpan nilai metrik utama per hari bisnis. Job cron NestJS berjalan pukul 01.00 WIB setiap malam dan mengisi tabel ini dengan nilai penutupan kemarin. Dashboard kemudian mengkueri tabel snapshot — SELECT sederhana tanpa join — kembali dalam milidetik.
Executive KPI Dashboard Architecture
Nightly cron (01:00 WIB)
→ Queries raw tables: journal_lines, ar_invoices, stock_movements, ...
→ Computes 12 KPIs for yesterday
→ Writes to daily_kpi_snapshot table
→ Invalidates Redis cache for dashboard
Dashboard API endpoint: GET /dashboard/kpis
→ Reads daily_kpi_snapshot (fast: single row lookup)
→ Merges with real-time queries for cash/orders (15min cache)
→ Returns single JSON payload (not 12 separate calls)
KPI Card structure (React + Recharts):
┌─────────────────────────────────────┐
│ Revenue (Month-to-Date) │
│ │
│ Rp 2.4B ▲ +8% vs target │
│ Target: Rp 3B ▼ -12% vs LY │
│ │
│ [sparkline: last 12 months] │
│ ▁▂▃▃▄▄▅▆▆▇▇▇ │
└─────────────────────────────────────┘
12 KPI Cards:
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Revenue │ │ GP Margin│ │ AR DSO │ │ AP DPO │
│ 🟢 80% │ │ 🟡 36% │ │ 🔴 68d │ │ 🟢 42d │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Inv Turn │ │ Cash Pos │ │Open Ord │ │ Overdue │
│ 🟢 4.2x │ │ 🟢 2.1B │ │ 🟡 45 │ │ 🔴 23 │
└──────────┘ └──────────┘ └──────────┘ └──────────┘Dari pengalaman saya membangun sistem ERP di Commsult: selalu tampilkan metrik, target, dan tren bersama-sama. Angka pendapatan tidak berarti apa-apa tanpa konteks. 'Rp 2,4M pendapatan bulan ini vs. Rp 3M target (80%) — turun 12% vs. bulan yang sama tahun lalu' memungkinkan keputusan. Rancang setiap kartu KPI dengan: nilai saat ini, target atau tolok ukur, varians (absolut dan %), dan sparkline yang menunjukkan 12 bulan terakhir.
API dashboard mengembalikan semua 12 KPI dalam satu panggilan endpoint — bukan 12 panggilan API terpisah. Kami membatch komputasi KPI dan mengembalikan satu payload JSON. Ini mengurangi jumlah round-trip dan mencegah efek 'waterfall loading' di mana setiap kartu muncul pada waktu yang berbeda.
Setiap kartu KPI harus dapat diklik dan menelusuri ke transaksi yang mendasarinya. Klik 'Faktur Jatuh Tempo: 23' dan Anda mendapatkan daftar 23 faktur dengan nama pelanggan, jumlah, hari jatuh tempo, dan pemilik AR. Kapabilitas drill-down ini yang membedakan dashboard yang berguna dari layar perhiasan.
// NestJS: KPI service with Redis caching
@Injectable()
export class KpiService {
constructor(
private financeService: FinanceService,
private arService: ArService,
private inventoryService: InventoryService,
@InjectRedis() private redis: Redis,
) {}
async getDashboardKpis(): Promise<DashboardKpis> {
const cacheKey = 'dashboard:kpis:' + new Date().toISOString().slice(0, 13);
const cached = await this.redis.get(cacheKey);
if (cached) return JSON.parse(cached);
// Fetch all KPIs in parallel
const [revenue, margin, dso, dpo, cashPos, overdueInvoices] =
await Promise.all([
this.financeService.getRevenueKpi(),
this.financeService.getGrossMarginKpi(),
this.arService.getDsoKpi(),
this.financeService.getDpoKpi(),
this.financeService.getCashPositionKpi(),
this.arService.getOverdueKpi(),
]);
const kpis = { revenue, margin, dso, dpo, cashPos, overdueInvoices };
// Cache for 15 minutes
await this.redis.setex(cacheKey, 900, JSON.stringify(kpis));
return kpis;
}
// Threshold alert job — runs Mon morning 07:00 WIB
@Cron('0 7 * * 1', { timeZone: 'Asia/Jakarta' })
async checkKpiAlerts() {
const kpis = await this.getDashboardKpis();
const alerts = this.alertConfig;
if (kpis.dso.value > alerts.dso.threshold) {
await this.notifier.sendEmail(alerts.dso.recipients, {
subject: `⚠️ AR DSO Alert: ${kpis.dso.value} days`,
body: `DSO has exceeded ${alerts.dso.threshold} day threshold.`,
});
}
// ... additional alert checks
}
}Backend dashboard kami adalah NestJS DashboardModule dengan KpiService tunggal. Service mengorkestrasikan query di seluruh FinanceService, InventoryService, ARService, dan APService, merakit payload KPI, dan meng-cache-nya di Redis dengan TTL 15 menit. Frontend adalah dashboard React menggunakan Recharts untuk sparkline.
Pemangku kepentingan yang berbeda sering mendefinisikan KPI yang sama secara berbeda. 'Pendapatan' mungkin berarti penjualan bruto bagi direktur penjualan dan bersih setelah pengembalian bagi CFO. Sebelum membangun dashboard, dokumentasikan formula tepat untuk setiap KPI, minta CFO dan pemilik bisnis menyetujuinya, dan tampilkan definisi formula dalam tooltip dashboard. Kami memiliki dua situasi di mana eksekutif memperdebatkan angka dashboard karena mereka menghitung secara berbeda. Solusinya bukan data yang lebih baik — ini dokumentasi formula yang eksplisit.
Eksekutif Indonesia lebih mengutamakan mobile. CEO meninjau dashboard di ponsel mereka saat bepergian antar rapat. Kami membangun tampilan dashboard yang dioptimalkan untuk mobile yang menciutkan 12 KPI menjadi 5 yang paling kritis, menampilkan angka besar dengan label minimal, dan menggunakan kode warna hijau/kuning/merah untuk status instan. Kami juga membangun bot WhatsApp yang mengirimkan ringkasan KPI harian pukul 07.00 — fitur favorit CEO.
Dashboard bersifat reaktif — eksekutif melihatnya ketika mereka melihat. Alert bersifat proaktif. Kami menambahkan alert berbasis threshold: jika DSO AR melebihi 60 hari, email ke CFO. Jika posisi kas turun di bawah minimum yang dikonfigurasi, WhatsApp ke direktur. Jika faktur jatuh tempo melebihi Rp 500M, SMS ke manajer penagihan. Alert ini berjalan sebagai job cron NestJS yang memeriksa nilai KPI setiap hari.