Apostille / Legalisasi — Flow Lengkap + Titik Upload Spesimen
Hasil telusur read-only atas dua codebase (tidak ada yang diubah):
- Legacy:
ahu-codebase/ahu-apostille(Express + TypeORM) - Rebuild:
ahu-codebase/ahu-rebuild/ahu-rebuild-apostille—ahu-apostille-api(NestJS + Prisma) +module-apostille(frontend)
1. Apostille vs Legalisasi — apa bedanya di flow?
Keduanya satu alur permohonan yang sama, hanya beda jenis_layanan / applicationType. Bukan dua flow terpisah.
| Apostille | Legalisasi | |
|---|---|---|
Legacy applicationType |
1 → kode AP |
2 → kode LGL |
| Rebuild enum | JenisLayananEnum.Apostille |
JenisLayananEnum.Legalisasi |
| Output akhir | Sertifikat Apostille + QR | Stiker Legalisasi + QR |
- Legacy: penentuan kode di
apostille-service/src/service/apostille-service.ts:655-656
(const appType = applicationType === 1 ? 'AP' : 'LGL') - Rebuild: enum
JenisLayananEnumdiahu-apostille-api/prisma/main/schema.prisma:99-104
Yang membedakan dua layanan hanya: nomor urut/kode, template output (sertifikat vs stiker), dan sebagian aturan negara tujuan. Tahap verifikasi, spesimen, pembayaran identik.
Dimensi lain yang justru lebih menentukan cabang flow:
tipe_dokumen:Konvensional(kertas, butuh spesimen tanda tangan) vsElektronik(TTE, tidak pernah kena jalur spesimen).jenis_permohonan:diri_sendirivsorang_lain(pakai kuasa/POA — butuh berkas surat kuasa).kewarganegaraan:WNIvsWNA(memengaruhi format identitas & kelengkapan alamat).
1.1 Pilihan awal pemohon — yang menentukan berkas & field
Di awal permohonan, pemohon menentukan tiga hal yang mengubah data/berkas wajib. Negara tujuan sudah dibahas di atas (menentukan Apostille vs Legalisasi). Tiga sumbu berikut menentukan input:
A. Dokumen Konvensional vs Elektronik (tipe_dokumen)
Ini soal cara dokumen ditandatangani, bukan format file.
| Konvensional | Elektronik | |
|---|---|---|
| Tanda tangan | Basah (pena + cap/stempel di kertas) | Digital / TTE (biasanya ber-QR / sertifikat elektronik) |
| Verifikasi keaslian | Dicocokkan ke spesimen tanda tangan pejabat | Dipercaya sudah sah digital; tidak dicek ke spesimen |
| Jalur spesimen | Aktif — jika pejabat belum terdaftar → wajib upload spesimen (status ditangguhkan) |
Tidak pernah — langsung ke verifikasi |
| Enum | Legacy isElectronic == 2 · Rebuild tipe_dokumen = 'Konvensional' |
Legacy isElectronic == 1 · Rebuild 'Elektronik' |
Detail jalur spesimen ada di Bagian 3. Untuk Elektronik dengan pejabat tak ditemukan: permohonan tetap lanjut (dikirim / status=1), tak pernah diminta spesimen — keabsahan TTE tidak divalidasi otomatis, hanya diperiksa verifikator secara manual.
B. Untuk diri sendiri vs orang lain (jenis_permohonan)
Menentukan berkas wajib (permohonan.service.ts:204-225; legacy pakai flag isPoa):
| Berkas | diri_sendiri |
orang_lain (kuasa) |
|---|---|---|
ktp_pemohon |
✅ wajib | ✅ wajib |
ktp_penerima_kuasa |
— | ✅ wajib (:211-217) |
surat_kuasa |
— | ✅ wajib (:218-225) |
| file dokumen sumber | ✅ wajib (≥1/dokumen) | ✅ wajib |
Kalau orang_lain, sistem juga menyimpan data penerima kuasa (nama + identitas + kewarganegaraan kuasa) secara terpisah dari pemohon.
Template surat kuasa untuk diunduh. Karena surat kuasa harus bermaterai + tanda tangan basah, sistem tidak meng-generate otomatis — pemohon mengunduh template kosong, mengisinya, lalu meng-upload kembali. Fitur ini sudah ada di kedua referensi dan hanya muncul saat orang_lain, tepat di dekat field upload surat_kuasa:
- Legacy: tombol muncul saat
isPoa === '1'(ap-fe/.../ApplicantForm.component.tsx:175-178), file di-serve backend dari MinIO viaGET /api/apostille/utils/get-template/:filename?type=format_surat_kuasa.docx(router/index.ts, handlerutils.ts:19-41). - Rebuild: tombol muncul saat
jenis_permohonan == 'orang_lain'(module-apostille/.../PemilikUsaha/index.jsx:238-275), tapi menaut ke Google Docs eksternal (.../export?format=docx).
Untuk permohonan OCR baru kita → pakai opsi host-sendiri (cara legacy) agar konsisten dengan prinsip full on-prem: simpan format_surat_kuasa.docx sebagai asset/served-file kita sendiri, jangan link eksternal.
⚠️ Pemicu template =
orang_lainsaja, BUKAN WNI/WNA. Tombol download + field upload surat kuasa tampil kapan pun "orang lain" dipilih, terlepas pemohon/kuasa WNI atau WNA. Sumbu WNI/WNA (lihat C) hanya mengubah format nomor identitas & kelengkapan alamat, tidak memicu surat kuasa. Dua kondisi ini independen.
C. Penerima legalisasi WNI vs WNA (kewarganegaraan)
Rebuild punya field eksplisit kewarganegaraan_pemohon enum { wni, wna } (schema.prisma:24); legacy menyimpulkannya dari country (99 = Indonesia). WNI/WNA tidak mengubah alur, tapi mengubah format & kelengkapan input:
| WNI | WNA | |
|---|---|---|
| Nomor identitas | NIK 16 digit (/^\d{16}$/) |
Paspor/KITAS alfanumerik 6–18 (/^[A-Za-z0-9]{6,18}$/) |
| Alamat rinci (prov/kab/kec/kel) | Wajib bila tinggal di Indonesia | Opsional (cukup alamat + negara tinggal) |
| Berkas identitas | ktp_pemohon |
ktp_pemohon (jenis berkas sama — tak ada tipe file paspor terpisah) |
Referensi: format identitas & alamat di module-apostille/.../Pendaftaran/validationScehma.js:107-118, 156-206; alamat legacy hard-coded country === 99 di apostille-service.ts:1514-1528.
Berlaku untuk siapa: field kewarganegaraan ada untuk pemohon dan penerima kuasa (masing-masing kewarganegaraan_pemohon & kewarganegaraan_penerima_kuasa), sehingga tiap pihak divalidasi format identitasnya sendiri. Jadi WNI/WNA bisa bersilang dengan diri-sendiri/kuasa (mis. pemohon WNA diwakili kuasa WNI, dst.) — masing-masing mengikuti aturan format di atas.
Catatan untuk OCR: ketiga sumbu ini + negara adalah satu-satunya hal yang mengubah kebutuhan input. Jenis dokumen (13 macam) tidak mengubah berkas wajib; ia hanya metadata + flag kelayakan apostille.
2. Lifecycle Permohonan (end-to-end)
Status Permohonan
Legacy — field tunggal CustomerDocuments.status
(customer-document-approval-service.ts:332-350):
| status | arti |
|---|---|
| 1 | Menunggu Verifikasi |
| 2 | Menunggu Spesimen Terbaru (jalur spesimen) |
| 3 | Permohonan Ditolak |
| 4 | Selesai |
| 5 | Selesai Pembayaran |
| 6 | Sudah Dicetak |
plus verificationStep (1 = verifikator, 2 = kasi).
Rebuild — enum StatusPermohonanEnum (schema.prisma:127-138):
Draft → dikirim → verifikasi_verifikator → verifikasi_kasi → pencetakan
dengan cabang ditangguhkan (jalur spesimen), ditolak, dibatalkan (kedaluwarsa/batal).
Verifikasi dipisah ke tabel VerifikasiPermohonan (tipe_verifikator = Verifikator | Kasi, verified boolean).
Diagram lifecycle (rebuild)
Urutan langkah + referensi kode
| # | Tahap | Legacy (file:line) | Rebuild (file:line) |
|---|---|---|---|
| 1 | Ambil jenis dokumen | apostille.ts:7-10 → apostille-service.ts:105-134 |
master data |
| 2 | Submit permohonan | apostille-service.ts:624-970 (submitDocument) |
permohonan.service.ts:150-550 (createPermohonan) |
| 3 | Cek spesimen (set status) | apostille-service.ts:733,759-760 |
permohonan.service.ts:302-350 |
| 4 | Verifikasi verifikator (step 1) | customer-document-approval-service.ts:78-175 (approve) |
verifikasi.service.ts:527-900 (verifikasiPermohonan) |
| 5 | Verifikasi kasi (step 2) | idem, step2 | idem, tipe Kasi |
| 6 | Tolak / verifikasi ulang | reject 177-244 / reverify 246-307 |
verified=false + is_verifikasi_ulang |
| 7 | Voucher / PNBP | via Kafka setelah step2 (getDocumentVoucher 442-495) |
dibuat saat verifikasi kasi (verifikasi.service.ts:547-569) |
| 8 | Cetak sertifikat/stiker + QR | job service terpisah | tabel Cetak (status_bayar, status_cetak) |
Aktor: Pemohon (submit + upload spesimen) → Verifikator (cek awal) → Kasi (approve akhir) → pembayaran → cetak. Auto-assign verifikator berdasar beban kerja (legacy via Kafka; rebuild permohonan.service.ts:459-474).
3. DI MANA upload spesimen terjadi
Jalur spesimen bukan langkah wajib untuk semua permohonan. Ia hanya menyala saat:
spesimen pejabat penanda tangan TIDAK ketemu di master DAN dokumen
Konvensional.
Dokumen elektronik tidak pernah masuk jalur ini.
Kondisi pemicu (rangkuman jawaban sebelumnya)
- Legacy (
apostille-service.ts:733):
if ((!doc.spesimen || !doc.spesimenPositionId) && doc.isElectronic == 2)
→ setdocument.status = 2("Menunggu Spesimen Terbaru"), generate surat pengantar. - Rebuild (
permohonan.service.ts:302-350): resolve lewat nama_pejabat + jabatan_pejabat keSpesimenPejabat. Kalau tidak adaid_spesimen_pejabattapi adanama_pejabat→ buatSpesimenTangguhan; kalautipe_dokumen === 'Konvensional'→status_permohonan = 'ditangguhkan',no_permohonan = null.
Siapa yang upload, dan di endpoint mana
Yang upload adalah PEMOHON (bukan admin) — ia mengambil spesimen tanda tangan dari pejabat terkait lalu meng-upload balik.
- Legacy: endpoint
POST /document/edit→editDocument()diapostille-service.ts:1041-1253.
Menerima filespesimenFile+ data pejabat (jabatan, NIP, instansi, alamat, tahun jabatan). MembuatSpesimen+SpesimenPositionstatus=2 (pending), lalu ubahdocument.status2→1 dan kirim ke verifikasi (Kafka1158). - Rebuild:
uploadSpesimenTangguhan()dipermohonan.service.ts:3781-3959.
Upload ke S3 prefixspesimen-pejabat-tangguhan/, simpanBerkasSpesimenTangguhan, lalu ubahstatus_permohonanditangguhkan → dikirim, baru assignno_permohonan, dan buat 2 recordVerifikasiPermohonan(Verifikator + Kasi).
Ada enforcement deadline 7 hari di kode (3803-3815): kalau lewat, permohonan autodibatalkan(410).
Diagram sub-flow spesimen
ada di master?} B -- Ya --> C[status = dikirim / verifikasi normal] B -- Tidak --> D[Buat SpesimenTangguhan
status = ditangguhkan
no_permohonan = null] D --> E[Generate surat pengantar + QR] E --> F[Pemohon ambil spesimen
dari pejabat] F --> G{Upload dalam 7 hari?} G -- Tidak --> H[Auto dibatalkan] G -- Ya --> I[uploadSpesimenTangguhan
→ file ke S3] I --> J[status: ditangguhkan → dikirim
assign no_permohonan
buat Verifikasi Verifikator+Kasi] J --> C
Surat pengantar / QR
- Legacy: generate PDF 3 halaman (
pdf.ts:991-1421,generateSpesimenLetter) berisi surat permintaan + Lampiran 01 (TTD & cap pejabat) + Lampiran 02 (verifikasi instansi), di-email ke pemohon (templatecovering-letter-spesimen.html). Disimpan di MinIOspesimen-pengantar/{YYYYMM}/. - Rebuild: tidak membuat PDF; membuat QR berisi ID
SpesimenTangguhanterenkripsi → mengarah ke halaman web form/apostille/surat-pengantar?q=...(permohonan.service.ts:352-411). Pemohon isi form + upload file di situ.
Rekonsiliasi (setelah upload)
- Legacy:
SpesimenPositiondibuat denganstatus=2(pending), diverifikasi admin saat approve step2 →status=1(aktif), baru bisa dipakai permohonan lain. - Rebuild:
SpesimenTangguhanTIDAK otomatis dipromosikan jadiSpesimenPejabat; permohonan hanya lanjut ke verifikasi. (Promosi ke master menjadi keputusan verifikasi terpisah.)
Admin UI master spesimen
- Legacy:
ap-fe-adminroute/master/spesimen/... - Rebuild:
module-apostilleroute/apostille/master-data/spesimen(DynamicMenu "Daftar Spesimen",apostilleRoute.jsx:146,DynamicMenu/config.js:130). CRUDSpesimenPejabat+ upload file ke S3spesimen-pejabat/.
4. Perbedaan Legacy vs Rebuild — CATATAN yang perlu di-keep
| Aspek | Legacy | Rebuild | Catatan penting |
|---|---|---|---|
| Model status | 1 field status (1–6) di dokumen |
enum status_permohonan + tabel VerifikasiPermohonan terpisah |
Rebuild memecah verifikasi jadi tabel sendiri (Verifikator/Kasi) |
| Spesimen pending | pakai SpesimenPosition.status=2 (tak ada tabel khusus) |
tabel khusus SpesimenTangguhan |
Konsep berubah: pending punya entitas sendiri |
| Pemicu not-found | !spesimen \|\| !spesimenPositionId (bergantung nilai kiriman FE, tak ada search aktif) |
search aktif findByNamaAndJabatan (nama + jabatan, case-insensitive) |
Rebuild lebih deterministik; kunci = nama_pejabat + jabatan_pejabat |
| Surat pengantar | PDF 3 halaman + email | QR → web form, tanpa PDF | Perubahan UX signifikan; tak ada lagi lampiran cetak |
| Deadline 7 hari | hanya disebut di email | di-enforce di kode (auto dibatalkan) |
Rebuild menambah aturan bisnis yang dulu manual |
| Nomor permohonan | dibuat saat submit (kecuali status 2) | ditunda sampai spesimen di-upload | Di rebuild, permohonan ditangguhkan belum punya no_permohonan |
| Storage file | MinIO (spesimen-file/, spesimen-pengantar/) |
AWS S3 (spesimen-pejabat/, spesimen-pejabat-tangguhan/) |
Beda prefix & backend object storage |
| Promosi ke master | SpesimenPosition diverifikasi → aktif, reusable |
SpesimenTangguhan tidak auto jadi master |
Perlu diverifikasi apakah ada langkah promosi eksplisit di rebuild |
| Voucher/PNBP | via Kafka setelah approve step2 | dibuat inline saat verifikasi kasi (Cetak.status_bayar) |
|
| Applicant type | field isPoa (0/1) |
enum jenis_permohonan (diri_sendiri/orang_lain) |
|
| Elektronik vs Konvensional | isElectronic (1/2) |
tipe_dokumen (Elektronik/Konvensional) |
Elektronik selalu bypass jalur spesimen di keduanya |
Poin tindak lanjut untuk PoC:
1. Kunci deteksi "spesimen tidak ditemukan" untuk OCR sebaiknya ikut rebuild = nama_pejabat + jabatan_pejabat (+ instansi opsional), bukan hanya keberadaan ID.
2. Ingat: jalur spesimen hanya untuk dokumen Konvensional.
3. Rebuild menunda no_permohonan → jangan asumsikan permohonan selalu bernomor sejak awal.
4. Deadline 7 hari sudah jadi aturan kode di rebuild — relevan kalau kita replikasi state machine.