think
16px
820px

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-apostilleahu-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 JenisLayananEnum di ahu-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) vs Elektronik (TTE, tidak pernah kena jalur spesimen).
  • jenis_permohonan: diri_sendiri vs orang_lain (pakai kuasa/POA — butuh berkas surat kuasa).
  • kewarganegaraan: WNI vs WNA (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 via GET /api/apostille/utils/get-template/:filename?type=format_surat_kuasa.docx (router/index.ts, handler utils.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_lain saja, 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)

stateDiagram-v2 [*] --> Draft Draft --> dikirim: submit (spesimen pejabat DITEMUKAN) Draft --> ditangguhkan: submit (spesimen TIDAK ditemukan + Konvensional) ditangguhkan --> dikirim: user upload spesimen (dalam 7 hari) ditangguhkan --> dibatalkan: lewat 7 hari (auto) dikirim --> verifikasi_verifikator: auto-assign verifikator verifikasi_verifikator --> verifikasi_kasi: verified = true verifikasi_verifikator --> ditolak: verified = false verifikasi_kasi --> pencetakan: verified = true (generate voucher + QR) verifikasi_kasi --> ditolak: verified = false ditolak --> verifikasi_verifikator: verifikasi ulang pencetakan --> [*]: bayar → cetak sertifikat/stiker

Urutan langkah + referensi kode

# Tahap Legacy (file:line) Rebuild (file:line)
1 Ambil jenis dokumen apostille.ts:7-10apostille-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)
    → set document.status = 2 ("Menunggu Spesimen Terbaru"), generate surat pengantar.
  • Rebuild (permohonan.service.ts:302-350): resolve lewat nama_pejabat + jabatan_pejabat ke SpesimenPejabat. Kalau tidak ada id_spesimen_pejabat tapi ada nama_pejabat → buat SpesimenTangguhan; kalau tipe_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/editeditDocument() di apostille-service.ts:1041-1253.
    Menerima file spesimenFile + data pejabat (jabatan, NIP, instansi, alamat, tahun jabatan). Membuat Spesimen + SpesimenPosition status=2 (pending), lalu ubah document.status 2→1 dan kirim ke verifikasi (Kafka 1158).
  • Rebuild: uploadSpesimenTangguhan() di permohonan.service.ts:3781-3959.
    Upload ke S3 prefix spesimen-pejabat-tangguhan/, simpan BerkasSpesimenTangguhan, lalu ubah status_permohonan ditangguhkan → dikirim, baru assign no_permohonan, dan buat 2 record VerifikasiPermohonan (Verifikator + Kasi).
    Ada enforcement deadline 7 hari di kode (3803-3815): kalau lewat, permohonan auto dibatalkan (410).

Diagram sub-flow spesimen

flowchart TD A[Submit permohonan Konvensional] --> B{Spesimen pejabat
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 (template covering-letter-spesimen.html). Disimpan di MinIO spesimen-pengantar/{YYYYMM}/.
  • Rebuild: tidak membuat PDF; membuat QR berisi ID SpesimenTangguhan terenkripsi → 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: SpesimenPosition dibuat dengan status=2 (pending), diverifikasi admin saat approve step2 → status=1 (aktif), baru bisa dipakai permohonan lain.
  • Rebuild: SpesimenTangguhan TIDAK otomatis dipromosikan jadi SpesimenPejabat; permohonan hanya lanjut ke verifikasi. (Promosi ke master menjadi keputusan verifikasi terpisah.)

Admin UI master spesimen

  • Legacy: ap-fe-admin route /master/spesimen/...
  • Rebuild: module-apostille route /apostille/master-data/spesimen (DynamicMenu "Daftar Spesimen", apostilleRoute.jsx:146, DynamicMenu/config.js:130). CRUD SpesimenPejabat + upload file ke S3 spesimen-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.