think
16px
820px

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).

flowchart LR subgraph SABH[SABH legacy - registry first] A1[Search BadanHukum] --> A2[Alasan] A2 --> A3[Syarat + quorum vs master roster] A3 --> A4[Input: akta + koran + kuorum attendance] A4 --> A5[Likuidasi: sisa kekayaan + koran likuidasi] A5 --> A6[Save TransaksiYayasan] end subgraph OURS[This repo - akta first] B1[Upload Akta Pembubaran] --> B2[Classify + extract deed] B2 --> B3[Review: identitas, alasan, organ+kuorum, koran, likuidasi] B3 --> B4[Validate: 2/3 quorum, koran window, alasan present] B4 --> B5[Attest + finalize -> COMPLETED] end

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. (SABH jumlahPerPengurus: 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_cetak within [tanggal_akta, tanggal_akta + 5 days].
  • Absence reasons enum: Meninggal, Mengundurkan Diri (with optional nomor_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.tsAKTA_PEMBUBARAN_YAYASANPEMBUBARAN_YAYASAN; mapToDocumentType.
6. src/services/submission-dispatch.ts — new case (v2-generic).
7. src/flow-engine/flows/yayasan-pembubaran.tsdefineFlow.
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 --noEmit green, 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, add PEMBUBARAN_PERKUMPULAN (Rapat Anggota; SABH stores pembubaran as text on TransaksiPerkumpulan, lighter — no likuidasi wizard).

6. Open questions

  1. 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.
  2. Billing/voucher — confirmed out of scope for the PoC (SABH gates on it; we don't).
  3. Attestation statements: reuse SABH's 5 syarat checkboxes + pasal-66 likuidasi disclaimer as the review attestation text?