Pembubaran Yayasan & Perkumpulan — Implementation Plan
Status: draft for review · Branch: dev/2 · Author: Claude session 2026-08-14
Scope decision (Efran, 2026-08-14): follow the real SABH pembubaran logic, entry model is akta-first, Yayasan first then clone to Perkumpulan.
1. The reconciliation that drives everything
SABH's PembubaranController (yayasanbaru) is a registry-first wizard: it searches an existing BadanHukum, pulls that entity's Pembina/Pengurus/Pengawas roster from the master record, and validates a 2/3 meeting quorum against that roster. This repo has no nirlaba master registry — pendirian creates entities and only does a nama-availability check.
The faithful adaptation: everything SABH reads from the master, we extract from the Akta Pembubaran instead, then run the identical SABH validation formulas against the extracted data. Same rules, different source of truth (the deed, not a DB row).
2. SABH step → repo mapping (Yayasan)
| SABH step | SABH captures | Where it lives here |
|---|---|---|
| Alasan | grounds enum: rapat_pembina, jangka_waktu, tujuan, putusan |
extracted from deed (tipe_perubahan) + surfaced on review; rule PEMBUBARAN_ALASAN_PRESENT |
| Cari (search) | entity lookup + voucher | replaced by deed extraction of entity identity (nama, kedudukan, npwp) — no voucher (out of scope, billing not in PoC) |
| Syarat | jenis_rapat (rapat_pembina), 5 mandatory doc checkboxes, disclaimer |
doc checklist becomes review attestation statements; jenis_rapat derived from deed |
| Input | nomor_akta, tanggal_akta, tanggal_rapat, koran[], kuorum[] (attendance + is_setuju) | akta meta + rapat date + koran[] + organ roster with hadir/setuju flags — all extracted |
| Likuidasi | sisa_kekayaan, koran_likuidasi[], pasal-66 disclaimer | extracted sisa_kekayaan + koran_likuidasi[] + attestation |
| Tambah (save) | writes koran, koran_likuidasi, kehadiran_rups, tanggal_rups, tipe_perubahan, kekayaan_akhir, steps JSON |
maps onto Submission columns + Document.rawExtractionJson blob |
Exact SABH validation formulas (ported verbatim)
- Quorum attendance (
SyaratForm/InputForm):2 * total_organ <= 3 * jumlah_hadir→ at least 2/3 of the eligible organ present. - Quorum approval:
2 * jumlah_hadir <= 3 * jumlah_setuju→ at least 2/3 of attendees approve (is_setuju == 1). - For Rapat Pembina,
total_organ= count of Pembina. (SABHjumlahPerPengurus: pembina-only for rapat_pembina; pengurus+pengawas for rapat_gabungan — gabungan is commented-out in SABH, so we implement rapat_pembina only.) - Koran pengumuman window: each
tanggal_cetakwithin[tanggal_akta, tanggal_akta + 5 days]. - Absence reasons enum:
Meninggal,Mengundurkan Diri(with optionalnomor_surat). - Each of pembina/pengurus/pengawas must be non-empty (organ completeness).
3. Data model
New SubmissionType: PEMBUBARAN_YAYASAN (+ later PEMBUBARAN_PERKUMPULAN).
New DocumentType: AKTA_PEMBUBARAN_YAYASAN (+ later AKTA_PEMBUBARAN_PERKUMPULAN).
Following the pendirian-nirlaba precedent, the rich deed data (organ roster with hadir/setuju, koran arrays, grounds, sisa kekayaan) lives in Document.rawExtractionJson; scalar fields (akta nomor/tanggal, rapat date, entity identity, sisa kekayaan) also land as ExtractedField rows for the review field-list. No new tables — mirrors how PENDIRIAN_YAYASAN avoids nirlaba-exclusive columns.
Prisma discipline (learned 2026-08-14): schema change needs a migration file under prisma/migrations/, then prisma db push + prisma generate on each dev DB, because the generated client at backend/src/generated/prisma rejects unknown enum values before Postgres sees them.
4. Extension points (the flow-engine is a total registry — all are compile-forced)
Backend:
1. prisma/schema.prisma — enum values + migration *_pembubaran_nirlaba_enums.
2. src/schema/akta-pembubaran-yayasan.ts — extraction shape + LLM prompt (organ carries hadir/setuju/alasan_ketidakhadiran; koran[]; koran_likuidasi[]; sisa_kekayaan; alasan; akta+rapat dates).
3. src/services/nirlaba-akta-classifier.ts — add title regex AKTA PEMBUBARAN YAYASAN.
4. src/services/nirlaba-akta-extract.ts — add pembubaran-yayasan variant + field map.
5. src/services/submission-type-inference.ts — AKTA_PEMBUBARAN_YAYASAN → PEMBUBARAN_YAYASAN; mapToDocumentType.
6. src/services/submission-dispatch.ts — new case (v2-generic).
7. src/flow-engine/flows/yayasan-pembubaran.ts — defineFlow.
8. src/flow-engine/context/yayasan-pembubaran-context.ts — build ctx from deed blob.
9. src/flow-engine/rules/yayasan-pembubaran/index.ts — rule set (alasan present, organ completeness, quorum attendance 2/3, quorum approval 2/3, koran window, sisa kekayaan present). Shared quorum math extracted to rules/nirlaba-pembubaran-shared/ for later Perkumpulan reuse.
10. src/flow-engine/registry.ts — register.
11. src/review/projectors/yayasan-pembubaran-review.ts + review/review-registry.ts — review sections (identitas, alasan, organ+kuorum roster, koran, likuidasi, pernyataan, validasi).
12. src/routes/ — organ/kuorum edit path if needed (mirror yayasan-pendirian.ts); finalize rides generic engine routes.
Frontend (mirror PP pembubaran + yayasan pendirian pages):
13. pages/yayasan/YayasanPembubaranUploadPage.tsx (akta upload + OCR poll).
14. pages/yayasan/YayasanPembubaranReviewPage.tsx (ReviewEngine sections + attestation).
15. routes.tsx entries; dashboard CTA in NotarisDashboardPage nirlaba tab.
5. Phasing
- Phase 1 — backend foundation (compile-forced skeleton): enums + migration, schema types file, flow def + context + rules + registry + dispatch + type-inference + review projector/registry. Goal:
tsc --noEmitgreen, a forced-classification create reaches review with the rules firing (honest "missing field" state until the prompt is tuned). - Phase 2 — extraction: classifier regex + extractor variant + prompt, validated against a real Akta Pembubaran Yayasan sample.
- Phase 3 — frontend: upload + review pages, routing, dashboard CTA.
- Phase 4 — Perkumpulan clone: extract
nirlaba-pembubaran-shared, addPEMBUBARAN_PERKUMPULAN(Rapat Anggota; SABH stores pembubaran as text onTransaksiPerkumpulan, lighter — no likuidasi wizard).
6. Open questions
- Is the koran pengumuman expected inside the deed text, or as separate uploaded clippings (as PT pembubaran allows both)? Assumption: extract from deed if present; add upload slot in Phase 3 if samples show separate clippings.
- Billing/voucher — confirmed out of scope for the PoC (SABH gates on it; we don't).
- Attestation statements: reuse SABH's 5 syarat checkboxes + pasal-66 likuidasi disclaimer as the review attestation text?