Ringkasan Utama
Panduan ini menunjukkan cara menambahkan dukungan multi-perusahaan ke ERP custom: bagikan master data seperti chart of accounts dan item master antar entitas, batasi pengaturan pajak dan mata uang per perusahaan, hubungkan invoice intercompany dengan id pasangan, lalu buat jurnal eliminasi otomatis agar laporan konsolidasi bersaldo nol tanpa rekonsiliasi manual.
Sebagian besar sistem ERP dirancang untuk satu perusahaan saja. Begitu bisnis mendirikan entitas hukum kedua, entah unit trading, anak perusahaan manufaktur, atau cabang regional, asumsi yang tertanam dalam model data satu perusahaan mulai bermasalah: data pelanggan yang sama terduplikasi di berbagai database, penjualan antar perusahaan terlihat seperti pendapatan riil, dan tim finance akhirnya merekonsiliasi entitas secara manual lewat spreadsheet setiap akhir bulan.
Artikel ini membahas cara merancang dukungan multi-perusahaan pada ERP custom: master data mana yang dibagi bersama versus tetap lokal per entitas, cara memodelkan penagihan intercompany agar kedua sisi transaksi tetap terhubung, cara menghasilkan jurnal eliminasi secara otomatis alih-alih manual, dan apa yang berubah saat semuanya digabung menjadi satu laporan konsolidasi.
Masalah master data bersama
Sebelum menulis satu pun aturan posting intercompany, tentukan data mana yang berada di atas level perusahaan dan mana yang tetap terbatas pada satu entitas. Jika keliru, pelanggan yang sama bisa terduplikasi lima kali dengan lima kode berbeda, atau setup pajak lokal satu entitas malah bocor ke pembukuan entitas lain.
- Dibagi bersama di semua perusahaan: struktur chart of accounts, item master, tabel unit of measure, dan direktori karyawan, semuanya sebaiknya punya satu sumber kebenaran dengan identifier yang tidak terikat perusahaan tertentu.
- Lokal untuk tiap perusahaan: kode pajak, format pelaporan statutori, mata uang fungsional, dan subset chart of accounts yang benar-benar diaktifkan untuk posting, entitas PT di Indonesia dan Pte Ltd saudaranya di Singapura jarang punya setup pajak yang sama.
- Identitas bersama, perilaku lokal: pelanggan dan vendor perlu satu master record agar lawan transaksi intercompany bisa diresolusi dengan benar, tetapi tiap perusahaan tetap punya termin kredit, price list, dan buku besar open-item sendiri terhadap pelanggan yang sama itu.
Solusi praktisnya adalah menambahkan dimensi perusahaan pada setiap tabel transaksional, ditambah beberapa tabel master yang sengaja tidak diberi dimensi itu. Logika pelaporan dan posting kemudian memfilter berdasarkan perusahaan; lookup master data tidak.
Merancang dimensi perusahaan
Setelah master data bersama disepakati, pertanyaan skema berikutnya adalah bagaimana satu perusahaan membatasi lingkup segalanya. Pendekatan paling bersih adalah entitas Company sebagai first-class citizen dengan mata uang basis, kalender fiskal, dan subset chart of accounts aktif sendiri, yang dirujuk lewat kolom company id di setiap tabel ledger, invoice, dan jurnal.
- Tambahkan tabel pasangan intercompany yang mencatat dua perusahaan mana saja yang diizinkan bertransaksi satu sama lain, serta kode pelanggan atau vendor mana yang mewakili entitas lawan di masing-masing sisi.
- Beri flag pada setiap akun yang layak untuk intercompany, jangan hardcode nomor akun ke dalam job eliminasi, karena nomor akun bisa berubah per klien sedangkan flag tidak.
- Simpan mata uang fungsional dan sumber kurs per perusahaan, karena invoice intercompany rutin melintasi batas mata uang meski masih dalam satu grup yang sama.
Di sinilah dukungan multi-perusahaan terbayar secara operasional: mengonboarding anak perusahaan baru cukup dengan menambah satu baris Company dan beberapa record pasangan intercompany, bukan migrasi skema.
Jika Anda menambahkan dukungan multi-perusahaan ke skema satu perusahaan yang sudah berjalan, tambahkan kolom company id dengan nilai default sama dengan id perusahaan tunggal yang sudah ada terlebih dahulu, lakukan backfill, baru kemudian jadikan wajib. Cara ini membuat migrasi tetap bisa dibalik jika ada yang gagal di tengah jalan.
Alur kerja penagihan intercompany
Penjualan intercompany harus menghasilkan dua transaksi independen yang seimbang, invoice AR di entitas penjual dan bill AP di entitas pembeli, yang tetap terhubung agar job eliminasi bisa menemukan kedua sisinya nanti. Perlakukan ini sebagai satu operasi bisnis dengan dua efek ledger, bukan dua posting terpisah yang kebetulan saling mereferensi.
- Cari pasangan intercompany untuk dua perusahaan yang terlibat guna menentukan kode pelanggan mana yang mewakili pembeli dan kode vendor mana yang mewakili penjual.
- Posting invoice AR di entitas penjual dalam mata uang fungsionalnya sendiri, ditandai dengan id pasangan intercompany.
- Konversi nilai invoice menggunakan kurs yang berlaku pada tanggal posting, lalu posting bill AP cermin di entitas pembeli, ditandai dengan id pasangan intercompany yang sama.
- Hubungkan kedua dokumen satu sama lain sehingga satu lookup mengembalikan pasangan lengkapnya, tautan inilah, bukan pencocokan nomor akun, yang menjadi andalan job eliminasi.
Kode di bawah menunjukkan bentuk operasi tersebut: satu pemanggilan fungsi yang membuat kedua sisi secara atomik, sehingga kegagalan di tengah proses tidak pernah menyisakan satu perusahaan dengan invoice terposting sementara yang lain tidak mendapat apa-apa.
// Posting an intercompany sale: one call creates both legs
async function postIntercompanySale(input: {
sellingCompanyId: string;
buyingCompanyId: string;
itemId: string;
qty: number;
unitPrice: number; // in sellingCompany's currency
postingDate: string;
}) {
const icPair = await getIntercompanyPair(
input.sellingCompanyId,
input.buyingCompanyId
);
// 1. AR invoice on the selling entity
const arInvoice = await createInvoice({
companyId: input.sellingCompanyId,
customerId: icPair.counterpartyCustomerId, // maps to buying company
lines: [{ itemId: input.itemId, qty: input.qty, price: input.unitPrice }],
intercompanyPairId: icPair.id,
postingDate: input.postingDate,
});
// 2. AP bill on the buying entity, converted to its base currency
const fxRate = await getRate(icPair.sellCurrency, icPair.buyCurrency, input.postingDate);
const apBill = await createBill({
companyId: input.buyingCompanyId,
vendorId: icPair.counterpartySupplierId, // maps to selling company
lines: [{
itemId: input.itemId,
qty: input.qty,
price: round(input.unitPrice * fxRate, 2),
}],
intercompanyPairId: icPair.id,
postingDate: input.postingDate,
});
// 3. Link both legs so the elimination job can find the pair later
await linkIntercompanyDocuments(arInvoice.id, apBill.id);
return { arInvoice, apBill };
}Bungkus seluruh fungsi ini dalam satu transaksi database. Posting intercompany yang setengah jalan adalah salah satu masalah rekonsiliasi paling menyita waktu untuk diurai kembali, karena begitu ada yang menyadarinya, periode di salah satu sisi mungkin sudah ditutup.
Jurnal eliminasi otomatis
Pada saat tutup periode, setiap pasangan AR dan AP intercompany, serta setiap pasangan pendapatan dan cost of goods sold intercompany, perlu dibalik untuk tampilan konsolidasi, sambil tetap tidak tersentuh di pembukuan masing-masing entitas. Job eliminasi menyusuri tautan pasangan intercompany yang dibuat saat posting, mengelompokkannya berdasarkan grup konsolidasi, dan menghasilkan satu jurnal eliminasi per pasangan.
| Baris eliminasi | Akun | Debit (IDR) | Kredit (IDR) |
|---|---|---|---|
| Eliminasi konsolidasi | Pendapatan penjualan intercompany | 100,000,000 | — |
| Eliminasi konsolidasi | Cost of goods sold (intercompany) | — | 100,000,000 |
| Eliminasi konsolidasi | Utang usaha intercompany | — | 100,000,000 |
| Eliminasi konsolidasi | Piutang usaha intercompany | 100,000,000 | — |
Dua baris eliminasi membalik pasangan penjualan versus biaya yang tercatat di akun pendapatan dan cost of goods sold; dua lainnya membalik pasangan AR dan AP yang tercatat di balance sheet. Semua ini tidak pernah menyentuh pembukuan asli Perusahaan A atau Perusahaan B, jurnal eliminasi hanya diposting di entitas konsolidasi, sehingga jejak audit di level entitas tetap sama seperti aslinya.
Eliminasi hanya berfungsi jika nilai debit dan kredit cocok persis, termasuk setelah konversi mata uang. Selisih satu hari antara waktu entitas penjual posting dan waktu entitas pembeli posting, atau kurs yang sudah kedaluwarsa di salah satu sisi, akan menghasilkan eliminasi yang tidak bersaldo nol, rekonsiliasi tanggal posting dan kurs sebelum menjalankan job, bukan sesudahnya.
Pelaporan konsolidasi: mata uang, kepemilikan, dan minority interest
Eliminasi menangani noise intercompany, tetapi menggabungkan beberapa entitas menjadi satu laporan konsolidasi memunculkan dua pertanyaan lagi: dalam mata uang apa Anda melaporkan, dan apa yang terjadi bila induk perusahaan memiliki kurang dari seratus persen saham anak perusahaan.
- Translasikan trial balance setiap anak perusahaan ke mata uang penyajian grup menggunakan kurs akhir periode untuk akun balance sheet dan kurs rata-rata untuk akun laba rugi, lalu simpan selisih translasi di akun ekuitas terpisah alih-alih memaksanya masuk ke laba rugi.
- Simpan persentase kepemilikan per relasi perusahaan agar mesin konsolidasi tahu berapa bagian dari ekuitas dan laba bersih anak perusahaan yang dimiliki sebagian itu menjadi milik grup versus menjadi minority interest.
- Jaga agar langkah eliminasi dan translasi tetap terpisah dan bisa dijalankan ulang secara independen, sehingga mengoreksi kurs mata uang tidak mengharuskan penurunan ulang seluruh entri eliminasi dari awal.
Di sinilah dimensi perusahaan dari bagian rancangan sebelumnya kembali terbayar: laporan konsolidasi sebenarnya hanyalah tampilan yang difilter, ditranslasi, dan dinetkan dari tabel transaksional yang sama yang sudah dibaca oleh laporan-laporan lain.
Jebakan praktis saat membangunnya sendiri
Tim yang membangun dukungan multi-perusahaan dari awal cenderung menemui beberapa masalah yang sama berulang kali. Antisipasi ini sebelum muncul di production.
- Perlakukan tautan pasangan intercompany sebagai sumber kebenaran untuk eliminasi, bukan pencocokan nomor akun, nomor akun bisa dipakai ulang untuk posting yang tidak berkaitan, tetapi id pasangan tidak bisa.
- Jangan pernah membiarkan satu entitas menutup periode sementara pasangan intercompany-nya masih terbuka, tanggal tutup harus dikoordinasikan lintas pasangan, atau job eliminasi akan menemukan entri sepihak.
- Bangun job eliminasi agar idempoten dan bisa dijalankan ulang untuk periode tertentu, karena pada akhirnya akan ada yang merestatement bulan sebelumnya dan perlu menjalankannya lagi tanpa menggandakan entri eliminasi sebelumnya.
- Catat setiap jurnal eliminasi dengan referensi balik ke pasangan AR dan AP sumbernya, sehingga auditor bisa menelusuri satu angka konsolidasi sampai ke dua posting level entitas yang menghasilkannya.
Semua ini tidak memerlukan arsitektur yang eksotis. Dimensi perusahaan, tabel pasangan intercompany, flag pada akun yang layak dieliminasi, dan job eliminasi yang bisa dijalankan ulang sudah mencakup sebagian besar kasus grup multi-entitas di dunia nyata, termasuk kasus mata uang dan kepemilikan sebagian yang akhirnya dihadapi kebanyakan ERP custom.

