Sistem pajak Indonesia adalah salah satu yang paling kompleks di Asia Tenggara untuk implementor ERP. Pada 2025, pemerintah menetapkan tarif PPN 12% yang hanya diterapkan pada barang dan jasa mewah, dengan tarif efektif 11% untuk sebagian besar barang dan jasa melalui mekanisme DPP Nilai Lain — perbedaan teknis yang membingungkan banyak bisnis. Wajib pajak yang tidak patuh dapat dicabut akses e-Faktur mereka karena gagal menyerahkan SPT Masa PPN selama tiga bulan berturut-turut. Di Commsult, saya telah membangun kepatuhan pajak Indonesia ke dalam ERP kustom kami dari nol.
Pajak Indonesia yang paling relevan untuk sistem ERP adalah: PPN (Pajak Pertambahan Nilai) 11% untuk sebagian besar barang dan jasa; PPh 21 (Pemotongan Pajak Penghasilan Pasal 21 — pemotongan gaji karyawan); PPh 23 (PPh Pasal 23 — pemotongan atas jasa, dividen, bunga sebesar 2-15%); PPh 25 (angsuran pajak penghasilan badan); dan PPh 4(2) (pajak final atas jenis penghasilan tertentu seperti jasa konstruksi sebesar 4%).
Modul PPN melacak pajak masukan (PPN atas pembelian yang dapat dikreditkan) dan pajak keluaran (PPN atas penjualan yang harus disetorkan). Setiap faktur AR ke pelanggan PKP menghasilkan faktur pajak (e-Faktur). Setiap faktur AP dari vendor PKP mengandung pajak masukan. Kewajiban PPN bulanan bersih adalah: pajak keluaran dikurangi pajak masukan yang dapat dikreditkan.
Indonesian Tax Module Architecture (NestJS + PostgreSQL)
AR Invoice (PKP customer)
│ PPN 11% computed: DPP × 11%
▼ (DPP = Nilai Lain if applicable)
┌─────────────────────────────────────────────────────┐
│ e-Faktur Generation │
│ │
│ NSFP (nomor seri faktur pajak) from DJP allocation │
│ Customer NPWP (validated via DJP API) │
│ DPP (Dasar Pengenaan Pajak) │
│ PPN Amount = DPP × 11% │
│ │
│ Export → CSV (DJP format, UTF-8 BOM, 57 columns) │
└─────────────────────────────────────────────────────┘
AP Invoice (PKP vendor)
│ Input Tax: vendor's PPN
▼
┌─────────────────────────────────────────────────────┐
│ Input Tax Register │
│ Credited against Output Tax monthly │
└─────────────────────────────────────────────────────┘
Monthly PPN Closing:
Output Tax (AR e-Faktur total PPN)
− Input Tax (AP creditable PPN)
= Net PPN Payable (or Credit)
→ Filed via DJP e-Filing by last business day of next month
PPh 23 Withholding (on service invoices):
Vendor Invoice: Rp 50,000,000 (consulting fee)
PPh 23: 50,000,000 × 2% = Rp 1,000,000 (withheld by buyer)
Net Payment to vendor: Rp 49,000,000
Bukti Potong issued to vendor ← vendor uses for their own tax creditDari pengalaman saya membangun sistem ERP di Commsult: bangun ekspor e-Faktur terlebih dahulu, bukan perhitungan PPN. Banyak implementor ERP membangun logika perhitungan pajak tetapi kemudian menyadari format file e-Faktur (CSV dengan urutan kolom, encoding, dan aturan validasi tertentu) adalah upaya rekayasa yang sepenuhnya terpisah. Format DJP e-Faktur berubah secara berkala — simpan format ekspor sebagai template yang dapat dikonfigurasi, bukan pemetaan kolom yang dikodekan keras.
Saat membayar faktur jasa tertentu (konsultasi, layanan teknis, biaya manajemen, sewa), pembeli harus memotong PPh 23 sebesar 2% dari biaya jasa bruto (atau 4% jika vendor tidak memiliki NPWP). Jumlah yang dipotong disetorkan ke DJP oleh pembeli dan dikurangi dari pembayaran vendor. Modul AP kami memiliki tanda PPh23 pada setiap baris faktur — ketika dicentang, komputasi pembayaran secara otomatis mengurangi jumlah pemotongan.
Sebelum bertransaksi dengan vendor baru, bisnis Indonesia harus memverifikasi NPWP dan status PKP mereka. Master vendor kami mencakup bidang NPWP dan status PKP. Ketika vendor baru dibuat, ERP memanggil API validasi NPWP DJP untuk memverifikasi nomor tersebut valid dan nama bisnis cocok. Jika vendor mengklaim sebagai PKP tetapi statusnya tidak mengkonfirmasi ini, kami menandainya sebelum pajak masukan PPN dikreditkan.
// NestJS: e-Faktur CSV export (DJP format)
// Reference: PER-03/PJ/2022 (DJP e-Faktur format)
@Injectable()
export class EFakturService {
// NPWP validation: exactly 15 digits, valid check digit
private validateNpwp(npwp: string): void {
const clean = npwp.replace(/[.-]/g, '');
if (!/^d{15}$/.test(clean)) {
throw new BadRequestException(
`Invalid NPWP format: ${npwp} (must be 15 digits)`
);
}
}
async generateExport(periodYear: number, periodMonth: number): Promise<Buffer> {
const invoices = await this.arRepo.findEfakturReadyInvoices(
periodYear, periodMonth
);
// DJP format: UTF-8 with BOM (Windows compatibility)
const rows = [
// Header row (DJP column specification)
'FK,KD_JENIS_TRANSAKSI,FG_PENGGANTI,...',
];
for (const inv of invoices) {
this.validateNpwp(inv.customer.npwp);
const dpp = inv.subtotal; // DPP = nilai barang/jasa (excl. PPN)
const ppn = Math.round(dpp * 0.11); // PPN 11%
rows.push([
'FK', // Kode faktur
inv.transactionType, // 01, 02, ...
inv.isReplacement ? '1' : '0', // FG pengganti
inv.nsfp, // NSFP from DJP
inv.invoiceDate.toISOString().slice(0,10).replace(/-/g,'/'),
inv.npwpSeller,
inv.customer.npwp.replace(/[.-]/g,''),
inv.customer.name.toUpperCase().slice(0, 200),
dpp.toFixed(0),
ppn.toFixed(0),
'0', // PPnBM (0 for non-luxury)
].join(','));
}
const bom = Buffer.from('', 'utf8');
const csv = Buffer.from(rows.join('
'), 'utf8');
return Buffer.concat([bom, csv]); // UTF-8 with BOM
}
}Modul pajak adalah NestJS TaxModule dengan: PpnService (pelacakan pajak keluaran/masukan), PphService (pemotongan 21, 23, dan 4(2)), EFakturService (menghasilkan CSV ekspor), dan BuktiPotongService (menghasilkan sertifikat pemotongan via Puppeteer PDF). Pembuatan CSV e-Faktur menggunakan serializer yang divalidasi secara ketat yang memberlakukan urutan kolom, encoding (UTF-8 dengan BOM untuk kompatibilitas Windows), dan semua format bidang yang diperlukan DJP.
Pengiriman e-Faktur ditolak oleh sistem DJP karena masalah kualitas data: format NPWP yang salah, nama pelanggan yang tidak cocok dengan catatan DJP, tanggal faktur di luar masa pajak, dan penggunaan NSFP (nomor seri faktur pajak) yang duplikat. Kami mengalami penolakan batch 140 e-Faktur dalam satu bulan karena validasi NPWP kami terlalu longgar dan menerima NPWP dengan digit yang hilang. Bangun validasi NPWP yang ketat ke dalam alur pembuatan e-Faktur.
Pada akhir setiap bulan, modul pajak menjalankan daftar periksa penutupan: semua faktur AR dengan PPN harus memiliki nomor referensi e-Faktur yang lengkap (dari DJP), semua faktur AP dengan PPh23 harus memiliki nomor Bukti Potong, buku besar PPN harus seimbang, dan masa harus dikunci sebelum batas waktu pengarsipan DJP.
Untuk persiapan SPT Badan tahunan, ERP menyediakan: L/R tahunan dalam format yang diperlukan DJP, rekonsiliasi PPh 21 tahunan, rekonsiliasi PPh 23 tahunan, dan rekonsiliasi PPN tahunan (12 bulan SPT Masa PPN cocok dengan catatan e-Faktur). Laporan rekonsiliasi ini dijalankan sebelum konsultan pajak mulai menyiapkan SPT Badan.