Portal vendor produksi membutuhkan empat kapabilitas: pengiriman faktur (mengunggah PDF dan memetakan item ke PO), pelacakan pembayaran (status setiap faktur dapat dilihat vendor), manajemen dokumen (detail rekening bank, dokumen pajak, sertifikat hukum), serta komunikasi terstruktur yang terhubung ke PO atau faktur tertentu. Keempatnya harus tersinkronisasi dengan ERP — portal yang berdiri sendiri tanpa koneksi ke ERP hanya menciptakan silo data paralel.
Saat vendor mengirimkan faktur, ERP secara otomatis mencocokkan tiga angka: kuantitas yang difakturkan, kuantitas di PO, dan kuantitas penerimaan barang. Jika ketiganya cocok dalam batas toleransi, faktur langsung disetujui dan masuk ke antrian pembayaran. Jika ada selisih, faktur ditandai untuk ditinjau oleh tim AP — proses ini menghilangkan sekitar 80% pekerjaan pencocokan AP secara manual.
Banyak kontak pemasok Indonesia beroperasi terutama melalui ponsel, memotret faktur dan mengirimkannya langsung dari mobile. Dengan membangun portal sebagai PWA — termasuk fitur pengambilan foto faktur dengan konversi otomatis ke PDF menggunakan pdf-lib — portal menjadi praktis bagi pengguna tersebut. Fitur foto faktur terbukti menjadi yang paling banyak digunakan di bulan pertama, mengkonfirmasi bahwa portal yang hanya bisa diakses dari desktop akan ditinggalkan oleh vendor.
Di awal implementasi, endpoint API yang salah konfigurasi mengembalikan status pembayaran semua vendor jika parameter vendor_id dimanipulasi — ini adalah kerentanan Insecure Direct Object Reference (IDOR). Solusinya adalah menerapkan kebijakan row-level security (RLS) PostgreSQL pada semua tabel yang menghadap vendor, serta memberlakukan vendor_id di setiap query melalui guard kustom NestJS yang mengekstrak identitas vendor dari JWT. Pengujian menyeluruh dengan beberapa akun vendor sebelum go-live sangat penting untuk mendeteksi jenis kerentanan ini.
Waktu pemrosesan AP per faktur turun dari 45 menit menjadi 8 menit, panggilan pertanyaan pembayaran vendor turun 65%, tingkat pembayaran tepat waktu meningkat dari 72% menjadi 91%, dan waktu onboarding vendor berkurang dari 5 hari menjadi 1 hari. CFO klien menyebutnya sebagai 'modul tunggal paling berdampak yang kami deploy.'
Pada 2025, 85% perusahaan B2B menawarkan portal pemasok atau vendor online, naik dari 68% setahun sebelumnya. Kasus bisnis sudah jelas: portal vendor mengurangi biaya pemrosesan AP dengan menghilangkan entri faktur manual, mempercepat siklus pembayaran, dan meningkatkan hubungan pemasok. Untuk perusahaan manufaktur dan dagang Indonesia yang mengelola ratusan vendor — banyak yang masih mengirim faktur PDF melalui email dan menindaklanjuti via WhatsApp — portal vendor adalah peningkatan operasional yang signifikan.
Portal vendor produksi membutuhkan empat kapabilitas inti: pengiriman faktur (vendor mengunggah PDF faktur, memasukkan item baris, memetakan ke PO), pelacakan pembayaran (vendor dapat melihat status pembayaran untuk setiap faktur), manajemen dokumen (vendor memperbarui detail bank, dokumen pajak, sertifikat hukum), dan komunikasi (pesan terstruktur yang terhubung ke PO atau faktur tertentu).
Saya menggunakan aplikasi Next.js yang menghadap vendor terpisah dengan autentikasi tersendiri. Vendor mendaftar dengan NPWP (nomor registrasi pajak Indonesia), nama bisnis, dan email. Admin ERP menyetujui pendaftaran dan menghubungkan akun vendor ke vendor master di ERP. Autentikasi menggunakan JWT dengan kedaluwarsa lebih pendek (4 jam) daripada pengguna internal.
Vendor Portal Architecture
┌─────────────────────┐ ┌─────────────────────────┐
│ Vendor Portal │ │ Internal ERP │
│ (Next.js PWA) │ │ (Next.js + NestJS) │
│ │ │ │
│ 📋 Submit Invoice │ │ 📊 AP Module │
│ 💳 Track Payment │◄───►│ 📦 Purchase Orders │
│ 📄 Docs Upload │ │ ✅ 3-Way Matching │
│ 💬 Messages │ │ 💰 Payment Batches │
└──────────┬──────────┘ └──────────┬──────────────┘
│ Vendor-scoped JWT │
▼ ▼
┌────────────────────────────────────────────────────┐
│ NestJS API (shared) │
│ │
│ VendorGuard: extracts vendor from JWT │
│ → injects vendor_id into every query │
│ → PostgreSQL RLS enforces row-level isolation │
└──────────────────────┬─────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────┐
│ PostgreSQL (RLS enabled on vendor-facing tables) │
│ │
│ vendors │ purchase_orders │ ap_invoices │ payments│
└────────────────────────────────────────────────────┘Dari pengalaman saya sebagai konsultan ERP lepas (freelance): berikan portal yang ramah mobile kepada vendor, bukan hanya desktop. Banyak kontak pemasok Indonesia beroperasi terutama di mobile — mereka akan memotret faktur dan mengirimkannya dari ponsel. Saya membangun portal sebagai PWA dengan formulir yang dioptimalkan untuk mobile. Pengambilan foto faktur (dengan konversi PDF otomatis menggunakan pdf-lib) adalah fitur yang paling banyak digunakan di bulan pertama.
Alur pengiriman faktur: vendor memilih PO dari PO terbuka mereka, mengkonfirmasi item baris dan kuantitas yang difakturkan, memasukkan nomor dan tanggal faktur, mengunggah PDF, dan mengirimkan. ERP secara otomatis menjalankan three-way matching: kuantitas faktur vs. kuantitas PO vs. kuantitas penerimaan barang. Jika ketiganya cocok dalam toleransi, faktur disetujui otomatis ke antrian pembayaran. Ini menghilangkan 80% pekerjaan pencocokan AP manual.
Vendor dapat melihat pelacak pembayaran untuk setiap faktur: dikirim, dalam peninjauan, disetujui, pembayaran dijadwalkan (dengan perkiraan tanggal pembayaran), dan dibayar (dengan referensi transfer bank). Visibilitas ini hampir sepenuhnya menghilangkan panggilan 'kapan saya akan dibayar?' ke tim AP.
-- PostgreSQL: Row-Level Security for vendor portal
-- Vendors can ONLY see their own data
ALTER TABLE purchase_orders ENABLE ROW LEVEL SECURITY;
ALTER TABLE ap_invoices ENABLE ROW LEVEL SECURITY;
ALTER TABLE payments ENABLE ROW LEVEL SECURITY;
-- Policy: vendor can only see their own POs
CREATE POLICY vendor_po_isolation ON purchase_orders
USING (vendor_id = current_setting('app.vendor_id')::UUID);
CREATE POLICY vendor_invoice_isolation ON ap_invoices
USING (vendor_id = current_setting('app.vendor_id')::UUID);
-- NestJS: Set vendor context before every query
@Injectable()
export class VendorContextMiddleware implements NestMiddleware {
async use(req: Request, res: Response, next: NextFunction) {
const vendor = req.user as VendorJwtPayload;
if (vendor?.vendorId) {
// Set PostgreSQL session variable for RLS
await this.dataSource.query(
"SELECT set_config('app.vendor_id', $1, true)",
[vendor.vendorId]
);
}
next();
}
}
// 3-way matching on invoice submission
async validateInvoiceAgainstPo(dto: SubmitInvoiceDto) {
const po = await this.poRepo.findOneOrFail(dto.poId);
const gr = await this.grRepo.findByPoId(dto.poId);
const grQty = gr.reduce((sum, r) => sum + r.quantity, 0);
const tolerance = 0.02; // 2% tolerance
for (const line of dto.lines) {
const poLine = po.lines.find(l => l.itemId === line.itemId);
if (!poLine) throw new BadRequestException(`Item ${line.itemId} not on PO`);
if (Math.abs(line.quantity - poLine.quantity) / poLine.quantity > tolerance) {
return { status: 'DISCREPANCY', requiresReview: true };
}
}
return { status: 'MATCHED', autoApprove: true };
}Portal vendor adalah aplikasi Next.js terpisah yang memanggil API NestJS yang sama dengan ERP internal, tetapi dengan kunci API yang dibatasi vendor. Modul vendor NestJS memberlakukan vendor_id pada setiap query menggunakan guard kustom yang mengekstrak vendor dari JWT dan menyuntikkannya ke setiap panggilan service.
Portal vendor berisi data keuangan sensitif — jumlah pembayaran, detail rekening bank, dokumen pajak. Row-level security di PostgreSQL harus memastikan setiap vendor hanya dapat melihat data mereka sendiri. Saya memiliki implementasi awal di mana endpoint API yang salah konfigurasi mengembalikan status pembayaran semua vendor jika parameter vendor_id dimanipulasi. Selalu gunakan kebijakan RLS PostgreSQL pada tabel yang menghadap vendor.
Regulasi pengadaan Indonesia mengharuskan vendor mempertahankan sertifikat PKP (Pengusaha Kena Pajak), NPWP, SIUP (izin usaha), dan untuk beberapa sektor, sertifikasi ISO atau SNI yang masih berlaku. Portal yang saya bangun memungkinkan vendor mengunggah dan memperbarui dokumen-dokumen ini. ERP melacak tanggal kedaluwarsa dan menandai vendor yang dokumennya akan kedaluwarsa dalam 30 atau 60 hari.
Enam bulan setelah meluncurkan portal vendor untuk klien saya, peningkatan terukur meliputi: waktu pemrosesan AP per faktur turun dari 45 menit menjadi 8 menit, panggilan pertanyaan pembayaran vendor turun 65%, tingkat pembayaran tepat waktu meningkat dari 72% menjadi 91%, dan waktu onboarding vendor berkurang dari 5 hari menjadi 1 hari. CFO klien menyebutnya 'modul tunggal paling berdampak yang kami deploy.'