Menurut Deloitte, 79% pemimpin bisnis melaporkan bahwa produktivitas tim mereka terhambat oleh ketidakselarasan antara sistem. Tidak ada yang lebih benar dari manajemen dokumen: faktur yang dikirim via email sebagai PDF, kontrak tersimpan di hard drive seseorang, gambar teknis tersebar di folder Google Drive. Ketika dokumen hidup di luar ERP, mereka tidak dapat ditautkan ke transaksi, tidak dapat memicu workflow, dan tidak dapat ditemukan dengan cepat selama audit.
Setiap dokumen dalam DMS kami memiliki: document_id, entity_type (vendor, produk, faktur, proyek, karyawan), entity_id (rekord ERP yang ditautkan), document_type (kontrak, faktur, sertifikat, gambar, foto), filename, storage_path (kunci Google Cloud Storage), file_size, mime_type, version, uploaded_by, uploaded_at, dan expires_at opsional.
Semua file dokumen disimpan di Google Cloud Storage, bukan di database. Kami hanya menyimpan kunci GCS (path) di PostgreSQL. File diorganisir berdasarkan struktur folder: {entity_type}/{entity_id}/{document_type}/{version}/{filename}. Akses melalui GCS signed URL dengan TTL yang dapat dikonfigurasi: 1 jam untuk tampilan dalam aplikasi, 24 jam untuk tautan unduhan yang dikirim via email.
ERP Document Management Architecture
ERP Entity (vendor, invoice, project, employee)
│ linked via entity_type + entity_id
▼
┌─────────────────────────────────────────────────────┐
│ documents table (PostgreSQL) │
│ │
│ id │ UUID │
│ entity_type │ 'vendor' | 'invoice' | 'project' │
│ entity_id │ UUID (FK to entity) │
│ document_type│ 'contract' | 'invoice' | 'cert' │
│ storage_path │ GCS key (not the file itself) │
│ version │ INT (increments on replacement) │
│ expires_at │ TIMESTAMPTZ (nullable) │
│ text_content │ tsvector (for full-text search) │
└──────────────────────┬──────────────────────────────┘
│
┌──────────────┼──────────────┐
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────────┐
│ GCS Storage │ │ OCR Extract │ │ Expiry Monitor │
│ (files) │ │ Vision API │ │ → alerts 60/30/ │
│ Signed URLs │ │ → tsvector │ │ 7 days before │
└──────────────┘ └──────────────┘ └──────────────────┘
Full-text search:
SELECT * FROM documents
WHERE text_content @@ to_tsquery('english', 'Sumber & Makmur')
AND uploaded_at BETWEEN '2025-01-01' AND '2025-03-31';Dari pengalaman saya membangun sistem ERP di Commsult: implementasikan versioning dokumen dari awal. Permintaan manajemen dokumen paling umum setelah go-live adalah 'Saya mengunggah versi kontrak yang salah — bisakah saya menggantinya tanpa kehilangan yang lama?' Jika Anda tidak memiliki versioning, jawabannya menyakitkan. Sistem kami membuat baris versi baru untuk setiap penggantian, mempertahankan semua versi sebelumnya. 'Versi aktif' adalah nomor versi tertinggi.
Dokumen harus memicu dan berpartisipasi dalam workflow. Ketika vendor mengunggah sertifikat NPWP melalui portal vendor, dokumen muncul di antrian tinjauan tim pengadaan ERP. Ketika faktur AP disetujui, PDF faktur yang dilampirkan secara otomatis diarsipkan ke folder dokumen vendor. Integrasi workflow ini mengubah DMS dari lemari arsip menjadi peserta aktif dalam proses bisnis.
File PDF mentah tidak dapat dicari kecuali Anda mengekstrak teksnya. Kami menggunakan Google Cloud Vision API untuk OCR pada PDF dan gambar yang diunggah: konten teks diekstrak dan disimpan dalam tabel document_search sebagai PostgreSQL tsvector. Pencarian full-text menggunakan ts_query PostgreSQL, yang menangani teks Indonesia dan Inggris.
// NestJS: Document upload with GCS storage + OCR
@Post('/documents/upload')
@UseInterceptors(FileInterceptor('file'))
async uploadDocument(
@UploadedFile() file: Express.Multer.File,
@Body() dto: UploadDocumentDto,
@GetUser() user: User,
) {
// 1. Determine storage path and version
const existing = await this.docRepo.findLatestVersion(
dto.entityType, dto.entityId, dto.documentType
);
const version = (existing?.version ?? 0) + 1;
const storagePath = [
dto.entityType, dto.entityId, dto.documentType,
`v${version}`, file.originalname
].join('/');
// 2. Upload to GCS
await this.storageService.upload(storagePath, file.buffer, file.mimetype);
// 3. Extract text via Google Cloud Vision (async)
const doc = await this.docRepo.save({
entityType: dto.entityType,
entityId: dto.entityId,
documentType: dto.documentType,
storagePath, version,
fileSize: file.size,
mimeType: file.mimetype,
uploadedBy: user.id,
expiresAt: dto.expiresAt,
});
// Trigger OCR extraction asynchronously
await this.ocrQueue.add('extract-text', { documentId: doc.id, storagePath });
return { documentId: doc.id, version, storagePath };
}
// OCR processor: updates tsvector for full-text search
@Process('extract-text')
async extractText(job: Job<{ documentId: string; storagePath: string }>) {
const imageBytes = await this.storageService.download(job.data.storagePath);
const [result] = await this.visionClient.textDetection({
image: { content: imageBytes },
});
const text = result.fullTextAnnotation?.text ?? '';
await this.docRepo.update(job.data.documentId, {
textContent: () => `to_tsvector('english', ${JSON.stringify(text)})`,
});
}Backend DMS adalah NestJS DocumentModule dengan StorageService (membungkus GCS), DocumentService (CRUD metadata), dan SearchService (full-text PostgreSQL). Frontend adalah penampil dokumen React menggunakan react-pdf untuk rendering PDF dan tampilan pohon folder yang dibangun dengan react-arborist. Dokumen terbuka di panel samping tanpa meninggalkan halaman ERP saat ini.
Biaya penyimpanan dokumen menumpuk lebih cepat dari yang diharapkan. Perusahaan yang menyimpan semua faktur, kontrak, foto, dan laporan selama 5 tahun dapat dengan mudah mengumpulkan 500GB-1TB file. Biaya Google Cloud Storage untuk 1TB di wilayah asia-southeast2 (Jakarta) sekitar $20/bulan untuk penyimpanan ditambah biaya egress untuk unduhan. Implementasikan kebijakan siklus hidup dokumen: pindahkan dokumen yang lebih dari 1 tahun ke penyimpanan Coldline (10x lebih murah untuk arsip yang jarang diakses).
Kepatuhan regulasi Indonesia mengharuskan pelacakan dokumen dengan tanggal kedaluwarsa: sertifikat PKP, SIUP, API (lisensi impor), sertifikasi SNI, sertifikat ISO, dan sertifikat kualifikasi staf. DMS kami mengirimkan email peringatan 60 hari, 30 hari, dan 7 hari sebelum dokumen kepatuhan kedaluwarsa. Modul pengadaan memeriksa validitas dokumen sebelum mengeluarkan PO kepada vendor.
Selama audit (audit pajak oleh DJP, atau audit kepatuhan yang diperlukan klien), auditor meminta dokumen tertentu. DMS kami mendukung: ekspor massal berdasarkan rentang tanggal, jenis entitas, dan jenis dokumen; audit trail yang menunjukkan siapa yang mengakses setiap dokumen dan kapan; dan tingkat akses auditor read-only yang memberikan akses tampilan saja ke set dokumen tertentu.