Skip to main content
BlogERP

Desain Approval Matrix ERP: Berhenti Hardcode Rantai Approval

Cara mendesain approval matrix ERP yang configurable — band nominal, role, departemen — agar finance bisa mengubah approver tanpa deployment. Dari proyek nyata.

Matthews Wong

Software Engineer

April 202610 menit baca
Desain Approval Matrix ERP: Berhenti Hardcode Rantai Approval

Foto oleh RDNE Stock project

Ringkasan Utama

Fitur persetujuan pertama yang dirilis untuk ANCoraPRO menuliskan rantainya langsung di dalam kode service: supervisor, lalu manajer, lalu finance. Setiap aturan baru kemudian menuntut deployment. Memindahkan rantai itu ke matriks yang bisa dikonfigurasi mengubah perubahan alur persetujuan dari pekerjaan engineering menjadi pekerjaan administratif.

Fitur approval pertama yang saya rilis untuk ANCoraPRO, ERP yang saya bangun untuk klien nyata dengan React, NestJS, dan PostgreSQL, rantai approval-nya tertulis langsung di kode service: supervisor, lalu finance manager, lalu direktur. Berjalan sempurna selama tepat empat bulan — sampai klien melakukan restrukturisasi, menambah departemen procurement, dan ingin invoice di atas nominal tertentu butuh dua tanda tangan finance. Setiap perubahan itu berarti deployment kode.

Penulisan ulangnya mengajarkan pelajaran yang menjadi inti artikel ini: logika approval adalah konfigurasi bisnis, bukan kode aplikasi. Deliverable-nya adalah approval matrix — aturan yang disimpan sebagai data, dikunci pada band nominal, jenis dokumen, role, dan departemen — yang bisa diedit admin dari layar. Berikut cara saya mendesainnya sekarang, lengkap dengan skema, plus edge case yang pasti akan menemukan Anda di production, didesain atau tidak.

Kenapa Rantai Hardcoded Selalu Kalah

Hardcode terasa lebih cepat di minggu pertama, dan untuk rantai dua langkah memang benar. Tapi organisasi mengganti approver jauh lebih sering daripada perkiraan developer — promosi, resign, restrukturisasi, temuan audit, kebijakan pengeluaran baru. Bandingkan kedua pendekatan secara jujur dan matrix menang di mana-mana kecuali effort awal:

AspekRantai hardcodedMatrix configurable
Mengganti approver atau thresholdPerubahan kode, review, deployment — berhari-hari, dan selamanya butuh developer.Admin mengedit baris rule di layar — hitungan menit, finance yang memegang kendali.
Restrukturisasi organisasiTulis ulang semua branch terdampak; risiko regresi tinggi di alur yang tidak disentuh.Nonaktifkan rule lama, masukkan rule baru dengan tanggal berlaku; dokumen lama tetap menyimpan historinya.
Menjawab audit — siapa boleh approve apa, kapanMembaca git history dan berdoa tanggal deploy cocok dengan pertanyaannya.Query baris rule dengan valid_from dan valid_until; jawabannya adalah data.
Effort build awalRendah — rantai if dan dua cek role.Lebih tinggi — tabel rule, engine resolusi, UI admin. Balik modal di reorg pertama.

Empat Dimensi Approval Matrix yang Sesungguhnya

Setiap requirement approval yang pernah saya kumpulkan dari tim finance terurai menjadi empat dimensi. Modelkan keempatnya secara eksplisit dan hampir semua kebijakan muat tanpa perubahan skema:

Jenis dokumen

Purchase order, invoice AP, klaim biaya, pengajuan cuti. Tiap jenis punya rule set sendiri — matrix PO dan matrix expense jarang berbagi threshold.

Band nominal

Rentang half-open dalam IDR: nol sampai 10 juta, 10 sampai 100 juta, 100 juta ke atas. Interval half-open (min inklusif, max eksklusif) mencegah double-match klasik di angka batas.

Departemen atau cost center

Procurement approve dengan cara berbeda dari marketing. Departemen NULL pada rule berarti berlaku se-perusahaan, dan rule departemen spesifik meng-override-nya.

Definisi step approver

Setiap rule yang cocok menghasilkan step berurutan. Step menunjuk ke role, user bernama, atau relasi seperti manager-dari-pemohon, plus quorum untuk approval N-dari-M.

Dokumentasi workflow Dynamics 365 dari Microsoft menggambarkan bentuk persis ini di contoh expense report-nya: laporan 7.000 dolar melewati dua approver, yang 11.000 dolar menambah approver ketiga. Threshold dan partisipannya adalah konfigurasi workflow, bukan logika ter-compile — itu standar yang dipasang sistem besar, dan ERP custom tidak boleh lebih buruk.

Skema yang Tahan Lama: Rule, Step, dan Resolusi

Dua tabel menanggung seluruh desain: approval_rule menyimpan dimensi pencocokan, approval_rule_step menyimpan approver berurutan untuk tiap rule. Resolusi memilih rule aktif paling spesifik — departemen spesifik mengalahkan se-perusahaan, lalu priority eksplisit memutus seri:

-- Rules are data, not code
CREATE TABLE approval_rule (
  id             BIGSERIAL PRIMARY KEY,
  document_type  TEXT NOT NULL,      -- 'PO' | 'AP_INVOICE' | 'EXPENSE'
  department_id  BIGINT REFERENCES department(id),  -- NULL = any
  amount_min     NUMERIC(18,0) NOT NULL DEFAULT 0,  -- IDR, inclusive
  amount_max     NUMERIC(18,0),                     -- NULL = no cap
  priority       INT NOT NULL DEFAULT 100, -- lower wins on overlap
  is_active      BOOLEAN NOT NULL DEFAULT true,
  valid_from     DATE NOT NULL,
  valid_until    DATE                       -- NULL = open ended
);

CREATE TABLE approval_rule_step (
  id              BIGSERIAL PRIMARY KEY,
  rule_id         BIGINT NOT NULL REFERENCES approval_rule(id),
  step_order      INT NOT NULL,            -- 1, 2, 3...
  approver_type   TEXT NOT NULL,  -- 'ROLE' | 'USER' | 'MANAGER_OF'
  approver_ref    BIGINT NOT NULL,
  quorum          INT NOT NULL DEFAULT 1,  -- N-of-M approvers
  UNIQUE (rule_id, step_order)
);

-- Resolution: most specific active rule wins
SELECT r.* FROM approval_rule r
WHERE r.document_type = $1
  AND (r.department_id = $2 OR r.department_id IS NULL)
  AND $3 >= r.amount_min
  AND ($3 < r.amount_max OR r.amount_max IS NULL)
  AND r.is_active
  AND CURRENT_DATE BETWEEN r.valid_from
      AND COALESCE(r.valid_until, CURRENT_DATE)
ORDER BY (r.department_id IS NULL), r.priority
LIMIT 1;

Query resolusi berjalan sekali, saat submission. Momen itu penting: engine men-snapshot step hasil resolusi ke tabel approval_instance yang menempel ke dokumen. Dokumen lalu menjalani salinan rantainya sendiri yang dibekukan. Kalau admin mengedit matrix besok, dokumen yang sedang berjalan tidak tersentuh — hanya submission baru yang melihat rule baru.

Pro tip: snapshot-saat-submission adalah perilaku tunggal terpenting yang harus benar. Alternatifnya — me-resolve ulang rule di setiap klik approve — berarti editan admin bisa diam-diam membelokkan dokumen yang sedang di tengah rantai, dan audit trail Anda tidak lagi bisa menjelaskan kenapa dokumen pergi ke siapa.

Edge Case yang Pasti Menemukan Anda

Lima skenario ini tidak eksotis. Semuanya muncul dalam tahun pertama ERP saya berjalan di production, dan masing-masing butuh keputusan desain eksplisit:

  • Approver sekaligus pemohon — manajer yang mengajukan purchase request sendiri tidak boleh self-approve. Lompat ke step berikutnya atau eskalasi satu level ke atas; pilih satu kebijakan dan tegakkan di engine.
  • Approver berhalangan — orang cuti. Anda butuh delegasi dengan tanggal kedaluwarsa, diatur approver atau admin, dan audit trail harus mencatat bertindak-sebagai-delegasi, bukan impersonasi.
  • Rule tumpang tindih — dua rule cocok dengan dokumen yang sama. Pengurutan deterministik (spesifisitas, lalu priority, lalu id terkecil) plus peringatan di layar admin saat rule baru tumpang tindih dengan yang ada.
  • Tidak ada rule yang cocok — kombinasi jenis dokumen dan nominal yang tidak dikonfigurasi siapa pun. Fail closed: blokir submission dengan pesan jelas, dan beri alert ke admin. Auto-approve karena celah konfigurasi adalah asal mula pengeluaran tak terotorisasi.
  • Nominal diedit di tengah jalan — invoice yang dikoreksi naik mungkin kini masuk band lebih tinggi. Kebijakan amannya: perubahan nominal apa pun membatalkan approval berjalan dan me-resolve ulang dari step satu.

Apa pun keputusan Anda untuk tiap edge case, tuliskan ke engine sebagai kode dan ke SOP klien sebagai satu kalimat per kasus. Mode kegagalan yang mahal bukan memilih kebijakan yang salah — melainkan dua layar yang diam-diam mengimplementasikan kebijakan berbeda.

Rollout Matrix Tanpa Kekacauan

Memigrasikan ERP yang sudah live dari rantai hardcoded ke matrix adalah migrasi data plus perubahan perilaku, dan urutannya penting:

  1. Transkripsikan dulu perilaku hardcoded saat ini menjadi baris rule, apa adanya — approver sama, threshold sama. Rilis engine yang menjalankan kebijakan persis seperti sebelumnya.
  2. Jalankan resolusi ganda di staging: untuk dua minggu dokumen nyata, catat keputusan kode lama dan engine baru, lalu diff output-nya sampai nol.
  3. Cutover dengan scope feature flag: satu jenis dokumen dulu (expense paling rendah risikonya), lalu PO, lalu invoice AP.
  4. Serahkan layar admin ke finance hanya setelah cutover stabil, dengan perubahan rule butuh admin kedua untuk aktivasi — four-eyes pada matrix-nya sendiri.
  5. Jadwalkan review matrix kuartalan bersama finance. Rule basi yang menunjuk karyawan yang sudah resign adalah dead code versi matrix.

Intinya

Approval matrix adalah pembeda antara ERP yang dioperasikan klien dan ERP yang ditunggui developer. Rule sebagai data, snapshot saat submission, resolusi fail-closed, dan delegasi dengan kedaluwarsa — empat keputusan itu mencakup sembilan puluh persen permintaan finance, dan mengubah gejolak struktur organisasi dari deployment menjadi edit form.

Kalau Anda memulai build NestJS hari ini: tahan godaan if-statement. Skema dua tabel di atas berbiaya mungkin seminggu termasuk layar admin. Versi hardcoded juga berbiaya seminggu — hanya saja dicicil di setiap reorganisasi sepanjang umur sistem.

FAQ

Pertanyaan Umum

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

Mengapa hardcode rantai approval di kode service menjadi masalah?

Rantai yang di-hardcode mengharuskan perubahan kode, review, dan deployment setiap kali approver atau threshold berubah — proses yang bisa memakan berhari-hari dan selamanya melibatkan developer. Organisasi mengganti approver jauh lebih sering dari perkiraan, akibat promosi, resign, restrukturisasi, dan kebijakan pengeluaran baru. Matrix yang configurable menyimpan aturan sebagai data sehingga admin finance bisa melakukan perubahan dalam hitungan menit tanpa deployment.

Apa empat dimensi yang harus dimodelkan di setiap rule approval?

Empat dimensinya adalah jenis dokumen (PO, invoice AP, klaim biaya, dll.), band nominal (rentang half-open dalam IDR untuk mencegah double-match di angka batas), departemen atau cost center (departemen NULL berarti berlaku se-perusahaan), dan definisi step approver (step berurutan yang menunjuk ke role, user bernama, atau relasi seperti manager-dari-pemohon, dengan quorum N-dari-M opsional). Memodelkan keempatnya secara eksplisit membuat hampir semua kebijakan finance muat tanpa perubahan skema.

Mengapa rantai approval harus di-snapshot saat submission, bukan di-resolve ulang di setiap klik approve?

Snapshot saat submission membekukan step approval tiap dokumen ke tabel approval_instance sehingga dokumen yang sedang berjalan tidak pernah terpengaruh oleh editan matrix berikutnya. Me-resolve ulang di setiap klik berarti perubahan admin bisa diam-diam membelokkan dokumen yang sedang di tengah rantai, dan audit trail tidak lagi bisa menjelaskan rule mana yang berlaku saat tiap step di-approve. Snapshot-saat-submission disebut dalam artikel sebagai perilaku tunggal terpenting yang harus benar.

Bagaimana sistem harus menangani dokumen yang nominalnya diedit di tengah proses approval?

Perubahan nominal dokumen bisa memindahkannya ke band yang berbeda, sehingga kebijakan amannya adalah membatalkan semua approval yang berjalan dan me-resolve ulang rantai dari step satu. Ini memastikan approver yang tepat untuk nominal baru selalu diterapkan dan mencegah dokumen bernilai lebih tinggi lolos lewat rantai threshold yang lebih rendah. Artikel merekomendasikan memilih satu kebijakan eksplisit, mengkodekannya ke engine, dan mendokumentasikannya di SOP klien.

Apa urutan rollout yang disarankan saat memigrasikan ERP live dari rantai hardcoded ke matrix yang configurable?

Pertama, transkripsikan perilaku hardcoded yang ada menjadi baris rule apa adanya dan rilis engine baru yang menjalankan kebijakan persis seperti sebelumnya. Kemudian jalankan resolusi ganda di staging selama dua minggu dengan dokumen nyata, lalu diff output lama dan baru sampai nol sebelum cutover. Lakukan cutover satu jenis dokumen per waktu di balik feature flag — mulai dari expense sebagai yang paling rendah risikonya — dan serahkan layar admin ke finance hanya setelah cutover stabil, dengan perubahan rule butuh admin kedua untuk aktivasi.