Skip to main content
BlogERP

Membangun Dashboard KPI Eksekutif untuk ERP Kustom

Cara merancang dan membangun dashboard KPI eksekutif real-time di ERP kustom — memilih metrik yang tepat, arsitektur data, visualisasi, dan optimasi performa.

Matthews Wong

Software Engineer

Juni 202510 menit baca
ERP

Ringkasan Utama

Dashboard KPI eksekutif sering menjadi satu-satunya bagian ERP yang benar-benar dibuka pimpinan, sehingga pemilihan metriknya adalah keseluruhan desainnya. Menurunkan tiap angka dari transaksi langsung, bukan dari ekspor semalam, adalah yang memungkinkan keputusan diambil berdasarkan angka itu pada hari yang sama.

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.

Memilih KPI yang Tepat

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.

Model Data Dashboard

Untuk performa, dashboard eksekutif tidak boleh langsung mengkueri tabel transaksi mentah. Saya 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 sebagai konsultan ERP freelance: 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.

Arsitektur Dashboard dan Performa

API dashboard mengembalikan semua 12 KPI dalam satu panggilan endpoint — bukan 12 panggilan API terpisah. Saya 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.

Drill-Down dari KPI ke Transaksi

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
  }
}

Bagaimana Saya Mendekati Ini

Backend dashboard yang saya bangun 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. Saya 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.

Dashboard Mobile untuk Eksekutif

Eksekutif Indonesia lebih mengutamakan mobile. CEO meninjau dashboard di ponsel mereka saat bepergian antar rapat. Saya 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. Saya juga membangun bot WhatsApp yang mengirimkan ringkasan KPI harian pukul 07.00 — fitur favorit CEO.

Alerting dan Manajemen Pengecualian

Dashboard bersifat reaktif — eksekutif melihatnya ketika mereka melihat. Alert bersifat proaktif. Saya 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.

FAQ

Pertanyaan Umum

Jawaban singkat untuk pertanyaan yang paling sering diajukan pembaca tentang topik ini.

Berapa banyak KPI yang sebaiknya ditampilkan di dashboard eksekutif ERP?

Dashboard eksekutif idealnya menampilkan maksimal 8–12 KPI — cukup untuk mencerminkan kesehatan bisnis secara keseluruhan dan memungkinkan pengambilan keputusan tanpa membebani pengguna. Menampilkan terlalu banyak metrik adalah kesalahan paling umum. Untuk perusahaan dagang, KPI inti meliputi pendapatan vs. target, margin laba kotor %, DSO, DPO, perputaran inventaris, posisi kas, pesanan terbuka, dan faktur jatuh tempo.

Mengapa dashboard tidak boleh langsung mengkueri tabel transaksi mentah?

Mengkueri tabel transaksi mentah setiap kali halaman dimuat terlalu lambat untuk dashboard eksekutif yang harus merespons dalam milidetik. Sebagai gantinya, tabel daily_kpi_snapshot dihitung terlebih dahulu setiap malam oleh job cron NestJS pukul 01.00 WIB, menyimpan nilai penutupan tiap hari bisnis. Dashboard kemudian menjalankan SELECT sederhana tanpa join pada tabel snapshot ini, sementara metrik yang benar-benar real-time seperti posisi kas menggunakan materialized view yang diperbarui setiap 15 menit.

Informasi apa yang harus ditampilkan setiap kartu KPI agar dapat ditindaklanjuti?

Angka saja tidak pernah cukup sebagai konteks pengambilan keputusan. Setiap kartu KPI harus menampilkan nilai saat ini, target atau tolok ukur, varians (absolut dan persentase), serta sparkline 12 bulan terakhir. Misalnya, 'Rp 2,4M vs. target Rp 3M (80%) — turun 12% vs. bulan yang sama tahun lalu' memungkinkan keputusan, sedangkan angka pendapatan tanpa konteks tidak memberi makna apa pun.

Bagaimana perselisihan definisi metrik ditangani sebelum dashboard dibangun?

Pemangku kepentingan yang berbeda sering mendefinisikan KPI yang sama secara berbeda — misalnya, 'pendapatan' bisa berarti penjualan bruto bagi direktur penjualan dan bersih setelah pengembalian bagi CFO. Solusinya adalah mendokumentasikan formula tepat untuk setiap KPI sebelum membangun, meminta persetujuan dari CFO dan pemilik bisnis, lalu menampilkan definisi formula di tooltip dashboard. Dokumentasi yang eksplisit ini — bukan data yang lebih baik — yang menyelesaikan perselisihan ketika eksekutif menghitung angka secara berbeda.

Bagaimana dashboard mobile dan sistem alerting bekerja untuk eksekutif Indonesia?

Eksekutif Indonesia lebih mengutamakan mobile, sehingga route mobile terpisah menciutkan 12 KPI penuh menjadi 5 yang paling kritis, menampilkan angka besar dengan label minimal, dan menggunakan kode warna hijau/kuning/merah untuk status instan. Bot WhatsApp mengirimkan ringkasan KPI harian pukul 07.00 sebelum eksekutif tiba di kantor. Alert berbasis threshold — yang dapat dikonfigurasi admin tanpa deployment kode — mengirimkan notifikasi email, WhatsApp, atau SMS ketika KPI melampaui batas yang ditentukan, seperti DSO AR melebihi 60 hari atau faktur jatuh tempo melampaui Rp 500M.