Skip to main content
BlogERP

Implementasi Multi-Mata Uang di ERP Kustom: Kurs, Revaluasi, dan Pelaporan

Panduan teknis mendalam membangun kapabilitas multi-mata uang di ERP kustom — dari penyimpanan kurs dan penanganan transaksi hingga revaluasi selisih kurs dan pelaporan konsolidasi.

Matthews Wong

Software Engineer

Juni 202510 menit baca
ERP

Ringkasan Utama

Multi-mata uang di ERP Indonesia berarti satu mata uang fungsional, yaitu IDR, dengan setiap transaksi valas disimpan dalam nominal aslinya dan nilai Rupiahnya, ditambah kurs pajak mingguan Kementerian Keuangan untuk PPN di faktur pajak. Revaluasi akhir periode kemudian menyatakan ulang saldo terbuka dengan kurs penutup, sesuai PSAK 221 (sebelumnya PSAK 10), sehingga buku besar tetap konsisten setelah kurs bergerak.

Bagi bisnis Indonesia yang berdagang lintas negara — mengimpor bahan baku, mengekspor produk, atau menagih klien dalam USD atau SGD — penanganan multi-mata uang di ERP bukan pilihan. Artinya juga harus hidup dengan dua set kurs resmi: kurs harian Bank Indonesia, yang dipakai modul ini untuk pembukuan, dan kurs pajak mingguan dari Kementerian Keuangan, yang wajib dipakai untuk PPN pada faktur pajak bermata uang asing. Di proyek-proyek yang pernah saya kerjakan, tim keuangan perusahaan dagang menghabiskan beberapa hari setiap bulan untuk mengonversi transaksi valuta asing ke Rupiah secara manual. Sebagai konsultan ERP lepas, saya membangun modul multi-mata uang untuk salah satu klien yang menangani seluruh siklusnya: input transaksi dalam mata uang apa pun, perhitungan ekuivalen IDR otomatis, revaluasi akhir periode, dan pelaporan konsolidasi dalam Rupiah.

Arsitektur Mata Uang

Sistem yang saya bangun 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.

Penyimpanan Kurs dan Pembaruan

Saya mengelola tabel exchange_rates dengan kolom currency_pair (misalnya 'USD/IDR'), rate_date, buy_rate, sell_rate, dan mid_rate. Setiap hari kerja, job cron NestJS pukul 08.30 WIB mengambil Kurs Transaksi BI dari layanan web wsKursBI milik Bank Indonesia: kurs jual dan kurs beli yang diumumkan pukul 08.00 WIB, dengan titik tengah USD yang mengacu pada JISDOR hari kerja sebelumnya (kurs referensi USD/IDR BI, dipublikasikan pukul 15.15 WIB). Bisnis juga dapat memasukkan kurs kustom secara manual bila memakai kurs hasil negosiasi dengan bank — hal yang lazim untuk transaksi USD bernilai besar. Semua kurs historis disimpan (tidak pernah ditimpa) sehingga nilai IDR untuk transaksi apa pun di masa lalu dapat direkonstruksi dengan tepat.

Multi-Currency ERP Architecture (IDR as Functional Currency)

  Exchange Rate Source
  ├── Bank Indonesia wsKursBI (working days, 08:30 WIB)  ← Kurs Transaksi BI
  │   └── 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: 17,880 (example rate)      │
  │                                                         │
  │  Journal Entry posted:                                  │
  │  DEBIT  Expense Account     IDR 178,800,000            │
  │  CREDIT AP Payable — USD    USD 10,000 / IDR 178,800K  │
  └──────────────────────────┬──────────────────────────────┘
                             │  Payment date: rate 17,950
                             ▼
  ┌─────────────────────────────────────────────────────────┐
  │  Payment: USD 10,000 × 17,950 = IDR 179,500,000        │
  │                                                         │
  │  DEBIT  AP Payable — USD    IDR 178,800,000             │
  │  DEBIT  Forex Loss Account  IDR     700,000   ← diff   │
  │  CREDIT Bank — USD          IDR 179,500,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 sebagai konsultan ERP lepas: jadikan kurs Bank Indonesia sebagai default, tetapi selalu izinkan pengguna menggantinya untuk transaksi tertentu. PSAK 221 (penomoran baru IAI untuk PSAK 10 sejak 1 Januari 2024) mengharuskan kurs spot pada tanggal transaksi, dan kurs yang benar-benar diterapkan bank sering sedikit berbeda dari kurs BI, jadi simpan keduanya di setiap transaksi. Pajak punya aturan sendiri: Pasal 21 PP 44/2022 mewajibkan PPN atas penjualan dalam mata uang asing dikonversi dengan kurs yang ditetapkan Menteri Keuangan yang berlaku saat Faktur Pajak seharusnya dibuat, yaitu kurs pajak mingguan, jadi simpan juga kurs tersebut di faktur.

Menangani Transaksi Mata Uang Asing

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.

Revaluasi Akhir Periode

Pada akhir bulan, saldo moneter mata uang asing yang masih terbuka (faktur yang belum dibayar, rekening bank dalam valuta asing) harus dinilai ulang dengan kurs penutup. Job revaluasi membaca semua saldo valas terbuka, menghitung selisih antara ekuivalen IDR awal dan ekuivalen IDR saat ini pada kurs penutup, lalu memposting jurnal laba/rugi selisih kurs yang belum terealisasi. PSAK 221 dan IAS 21 mewajibkan pos moneter dijabarkan dengan kurs penutup dan selisihnya diakui di laba rugi; membalik jurnal tersebut di awal periode berikutnya (ditandai dengan reverse_on_date) adalah pilihan desain saya, agar pembayaran tetap mengukur laba/rugi terealisasinya terhadap kurs awal. Tanpa dukungan ERP, semua ini dikerjakan manual — saya pernah melihat perusahaan melewatkannya dan menghasilkan neraca yang tidak akurat secara material.

// NestJS: Bank Indonesia exchange rate fetcher
// Kurs Transaksi BI is announced at 08:00 WIB on working days
@Cron('30 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/getSubKursLokal2?tgl=${today}`
  );
  // Parse BI XML: nil_subkurslokal is the quote unit (100 for JPY)
  for (const { pair, unit, buy, sell } of parsedRates) {
    await this.exchangeRateRepo.upsert({
      currencyPair: pair,
      rateDate:    today,
      buyRate:     buy / unit,
      sellRate:    sell / unit,
      midRate:     (buy + sell) / 2 / unit,
      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);

-- IDR check: foreign_amount carries the line's sign (negative on credits)
ALTER TABLE journal_lines ADD CONSTRAINT chk_idr_consistency
  CHECK (
    foreign_amount IS NULL
    OR ABS((debit - credit) - (foreign_amount * exchange_rate)) < 1
  );

Bagaimana Saya Mendekati Ini

Implementasi multi-mata uang menambahkan tiga kolom ke setiap transaksi keuangan: foreign_currency, foreign_amount, dan exchange_rate. Jumlah IDR selalu dihitung sebagai foreign_amount × exchange_rate, dan check constraint PostgreSQL memvalidasinya di setiap baris jurnal sehingga masalah pembulatan langsung tertangkap. Saya memakai 6 angka desimal untuk kurs: USD/IDR (JISDOR Rp17.899,00 pada 12 Oktober 2026) tidak membutuhkannya, tetapi Bank Indonesia mengutip JPY per 100 yen dan VND di 0,69, sehingga fetcher membagi kurs dengan satuan kutipannya dan menyimpan kurs per unit.

Jika klien Anda memiliki beberapa entitas yang saling bertransaksi dalam mata uang berbeda, eliminasi antar-perusahaan dalam laporan konsolidasi menjadi jauh lebih rumit. Kedua sisi transaksi antar-perusahaan bisa mencatat jumlah IDR yang berbeda (karena kurs pada tanggal faktur berbeda dari kurs pada tanggal pembayaran). Ini menimbulkan selisih rekonsiliasi antar-perusahaan yang harus dieliminasi secara eksplisit dalam konsolidasi. Saya menanganinya dengan modul eliminasi antar-perusahaan khusus dan mewajibkan semua transaksi antar-perusahaan memakai kurs rata-rata bulanan yang terstandarisasi, bukan kurs spot — pendekatan yang diterima IAS 21 hanya selama kurs tidak berfluktuasi signifikan.

Rekonsiliasi Bank Multi-Mata Uang

Rekonsiliasi bank lebih kompleks dengan beberapa mata uang: Anda merekonsiliasi laporan bank USD terhadap transaksi USD di ERP, kemudian juga merekonsiliasi ekuivalen IDR. Saya menyediakan layar rekonsiliasi per rekening bank per mata uang, menampilkan transaksi outstanding dalam mata uang asing diurutkan berdasarkan jumlah dan tanggal.

Pelaporan Manajemen dalam IDR

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.

FAQ

Pertanyaan Umum

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

Kurs Bank Indonesia mana yang disimpan ERP, dan kapan kurs itu dipublikasikan?

ERP menyimpan Kurs Transaksi BI, yaitu kurs jual dan kurs beli yang diumumkan Bank Indonesia pukul 08.00 WIB setiap hari kerja, yang diambil job cron NestJS pukul 08.30 WIB ke tabel exchange_rates yang tidak pernah ditimpa. Titik tengah USD dari kurs tersebut mengacu pada JISDOR hari kerja sebelumnya, kurs referensi USD/IDR BI yang dipublikasikan pukul 15.15 WIB. Karena semua kurs historis disimpan, ekuivalen IDR untuk transaksi masa lalu mana pun selalu dapat direkonstruksi.

Mengapa kurs Bank Indonesia dan kurs yang diterapkan keduanya disimpan di setiap transaksi?

PSAK 221, penomoran baru IAI untuk PSAK 10 yang berlaku sejak 1 Januari 2024, mengharuskan kurs spot pada tanggal transaksi, tetapi kurs yang benar-benar diterapkan bank untuk transaksi tertentu sering sedikit berbeda dari kurs yang dipublikasikan BI. Menyimpan keduanya membuat pembukuan tetap dapat ditelusuri ke kurs resmi sekaligus mencerminkan ekonomi transaksi yang sebenarnya.

Kurs apa yang dipakai untuk PPN pada faktur pajak dalam mata uang asing?

Kurs yang ditetapkan Menteri Keuangan, bukan kurs Bank Indonesia. Pasal 21 PP 44/2022 mewajibkan PPN atas transaksi dalam mata uang asing dikonversi ke Rupiah dengan kurs Menteri yang berlaku saat Faktur Pajak seharusnya dibuat. Kementerian Keuangan menetapkan kurs pajak ini setiap pekan, berlaku Rabu sampai Selasa, sehingga ERP membutuhkan tabel kurs kedua khusus untuk pajak.

Bagaimana ERP menangani laba/rugi selisih kurs yang terealisasi dan yang belum terealisasi?

Ketika faktur dalam mata uang asing dibayar dengan kurs yang berbeda dari kurs tanggal faktur, pembayaran memposting laba atau rugi terealisasi ke akun Laba Kurs atau Rugi Kurs khusus. Pada akhir bulan, saldo moneter yang masih terbuka seperti faktur belum dibayar dan rekening bank valas dinyatakan ulang dengan kurs penutup, sesuai PSAK 221 dan IAS 21, dan selisihnya diposting sebagai laba atau rugi belum terealisasi. Modul ini membalik jurnal tersebut di awal periode berikutnya, sehingga pembayaran nantinya tetap mengukur hasil terealisasinya terhadap kurs awal.

Bagaimana transaksi antar-perusahaan multi-mata uang ditangani agar tidak menimbulkan kesalahan konsolidasi?

Ketika dua entitas dalam grup bertransaksi dalam mata uang berbeda, masing-masing sisi dapat mencatat jumlah IDR yang berbeda tergantung kurs pada tanggalnya. Sistem memakai modul eliminasi antar-perusahaan khusus dan mewajibkan transaksi antar-perusahaan menggunakan kurs rata-rata bulanan yang terstandarisasi, bukan kurs spot. IAS 21 menerima kurs rata-rata hanya sebagai pendekatan selama kurs tidak berfluktuasi signifikan.