Untuk bisnis Indonesia yang berdagang secara internasional — mengimpor bahan baku, mengekspor produk, atau menagih klien dalam USD atau SGD — penanganan multi-mata uang dalam ERP bukan pilihan. Produsen yang mengimplementasikan kapabilitas ERP multi-mata uang biasanya melihat pengurangan 60-80% dalam waktu penutupan keuangan melalui konsolidasi otomatis. Dalam praktiknya, saya telah melihat perusahaan dagang Indonesia menghabiskan 3-4 hari per bulan secara manual mengonversi transaksi mata uang asing ke Rupiah untuk pembukuan mereka.
Sistem kami memiliki satu mata uang fungsional (IDR — Rupiah Indonesia) sebagai basis. Semua laporan keuangan dalam IDR. Transaksi mata uang asing disimpan dengan jumlah mata uang asing asli dan ekuivalen IDR pada tanggal transaksi. Pendekatan dual-amount ini adalah standar dalam sistem akuntansi dan menyederhanakan pelaporan.
Kami mempertahankan tabel exchange_rates dengan kolom: currency_pair (misalnya 'USD/IDR'), rate_date, buy_rate, sell_rate, dan mid_rate. Kurs diambil setiap hari dari API Bank Indonesia (sumber resmi untuk bisnis Indonesia) melalui job cron NestJS pukul 08.00 WIB. Bisnis juga dapat memasukkan kurs kustom secara manual untuk transaksi tertentu.
Multi-Currency ERP Architecture (IDR as Functional Currency)
Exchange Rate Source
├── Bank Indonesia API (daily, 08:00 WIB) ← official source
│ └── exchange_rates table (historical, never overwritten)
└── Manual override (for negotiated bank rates)
Foreign Currency Transaction Flow:
┌─────────────────────────────────────────────────────────┐
│ AP Invoice: USD 10,000 │
│ │
│ ERP looks up USD/IDR rate: 15,850 (from BI API) │
│ │
│ Journal Entry posted: │
│ DEBIT Expense Account IDR 158,500,000 │
│ CREDIT AP Payable — USD USD 10,000 / IDR 158,500K │
└──────────────────────────┬──────────────────────────────┘
│ Payment date: rate 15,920
▼
┌─────────────────────────────────────────────────────────┐
│ Payment: USD 10,000 × 15,920 = IDR 159,200,000 │
│ │
│ DEBIT AP Payable — USD IDR 158,500,000 │
│ DEBIT Forex Loss Account IDR 700,000 ← diff │
│ CREDIT Bank — USD IDR 159,200,000 │
└─────────────────────────────────────────────────────────┘
Month-end Revaluation:
Open FC balances × closing rate − original IDR = unrealized P/L
Posted as reversing journal (reverses on day 1 of next period)Dari pengalaman saya membangun sistem ERP di Commsult: gunakan kurs Bank Indonesia sebagai default, tetapi selalu izinkan pengguna mengganti untuk transaksi tertentu. Standar akuntansi Indonesia (PSAK 10) mengharuskan penggunaan kurs tanggal transaksi, tetapi 'kurs tanggal transaksi' sering kali adalah kurs yang diberikan bank Anda, yang sedikit berbeda dari kurs BI yang dipublikasikan. Simpan kurs BI dan kurs yang diterapkan pada setiap transaksi.
Ketika faktur AP datang dalam USD, layar entri menampilkan jumlah USD, dan sistem mencari kurs USD/IDR saat ini untuk menghitung ekuivalen IDR. Entri jurnal mencatat: debit Beban (ekuivalen IDR), kredit Utang Usaha — USD (jumlah USD asli + ekuivalen IDR). Ketika pembayaran dilakukan, jika kurs IDR/USD telah berubah, pembayaran menghasilkan entri laba atau rugi selisih kurs.
Pada akhir bulan, saldo mata uang asing terbuka (faktur yang belum dibayar, rekening bank dalam mata uang asing) harus dinilai ulang pada kurs penutup. Job revaluasi membaca semua saldo FC terbuka, menghitung perbedaan antara ekuivalen IDR asli dan ekuivalen IDR saat ini pada kurs penutup, dan memposting entri jurnal laba/rugi belum terealisasi. Ini adalah praktik akuntansi standar per PSAK 10 dan IAS 21.
// NestJS: Bank Indonesia exchange rate fetcher
@Cron('0 8 * * 1-5', { timeZone: 'Asia/Jakarta' })
async fetchBankIndonesiaRates() {
const pairs = ['USD/IDR', 'EUR/IDR', 'SGD/IDR', 'CNY/IDR'];
const today = new Date().toISOString().split('T')[0];
const res = await fetch(
`https://www.bi.go.id/biwebservice/wskursbi.asmx/...`
);
// Parse BI XML response and insert rates
for (const { pair, buy, sell, mid } of parsedRates) {
await this.exchangeRateRepo.upsert({
currencyPair: pair,
rateDate: today,
buyRate: buy,
sellRate: sell,
midRate: mid,
source: 'bank_indonesia',
}, ['currencyPair', 'rateDate']);
}
this.logger.log(`Updated ${pairs.length} exchange rates from BI`);
}
// PostgreSQL: transaction schema with dual amounts
ALTER TABLE journal_lines ADD COLUMN foreign_currency VARCHAR(3);
ALTER TABLE journal_lines ADD COLUMN foreign_amount NUMERIC(15,2);
ALTER TABLE journal_lines ADD COLUMN exchange_rate NUMERIC(12,6);
-- Computed IDR amount check (6 decimal precision)
ALTER TABLE journal_lines ADD CONSTRAINT chk_idr_consistency
CHECK (
foreign_amount IS NULL
OR ABS((debit - credit) - (foreign_amount * exchange_rate)) < 1
);Implementasi multi-mata uang menambahkan tiga bidang ke setiap transaksi keuangan: foreign_currency, foreign_amount, dan exchange_rate. Jumlah IDR selalu dihitung sebagai foreign_amount × exchange_rate. Fungsi PostgreSQL memvalidasi bahwa jumlah IDR pada baris jurnal cocok dengan perhitungan yang tersimpan. Kami menggunakan 6 desimal untuk kurs tukar.
Jika klien Anda memiliki beberapa entitas yang bertransaksi satu sama lain dalam mata uang berbeda, eliminasi antar-perusahaan dalam akun konsolidasi menjadi jauh lebih kompleks. Kedua sisi transaksi antar-perusahaan mungkin mencatat jumlah IDR yang berbeda. Ini menciptakan perbedaan rekonsiliasi antar-perusahaan yang harus dieliminasi secara eksplisit dalam konsolidasi.
Rekonsiliasi bank lebih kompleks dengan beberapa mata uang: Anda merekonsiliasi laporan bank USD terhadap transaksi USD di ERP, kemudian juga merekonsiliasi ekuivalen IDR. Kami menyediakan layar rekonsiliasi per rekening bank per mata uang, menampilkan transaksi outstanding dalam mata uang asing diurutkan berdasarkan jumlah dan tanggal.
Semua laporan manajemen disajikan dalam IDR, dengan kolom sekunder opsional yang menampilkan jumlah mata uang asing asli untuk bisnis yang banyak bertransaksi dalam FC. L/R menampilkan total pendapatan dan biaya dalam IDR, dengan baris Forex untuk laba/rugi selisih kurs yang terealisasi dan belum terealisasi. Menampilkan komponen Forex secara terpisah membantu manajemen memahami kinerja operasional sebenarnya versus dampak mata uang.