XPrivate Education — Business Flowcharts

Iter 1 (Admin-MVP) · Visual sync untuk diskusi konsep · ← Back to scope proposal

Admin Daily Ops Dashboard

Apa yang admin lihat & lakukan sehari-hari di landing dashboard.

M1 + M3 + M4 + M7 Iter 1 active

① Dashboard Landing — 3 Kategori Utama

flowchart LR
    Login(["🔑 Admin login
Google OAuth"]) --> Dash[📋 Daily Ops
Dashboard] Dash --> S1["⚡ Yang perlu
segera ditangani"] Dash --> S2["📊 Yang perlu
di-monitor"] Dash --> S3["📅 Operasi
akhir bulan"] style S1 fill:#fbbf24,color:#422006 style S2 fill:#38bdf8,color:#0c2c4a style S3 fill:#a855f7,color:#2e1065

② Detail per Kategori

⚡ Yang perlu segera ditangani

  • Approval Queue
    Sesi baru menunggu di-approve admin. Sesi tidak jalan sampai disetujui.
  • Anomaly Queue
    Sesi mencurigakan: double-booking tutor, atau item yang lewat SLA tanggap (mis. REQUESTED >24 jam).
  • FM Claim Review
    Klaim Force Majeure (sakit/duka/bencana) dari tutor. Review L1 + opsi banding L2 dalam 14 hari.

📊 Yang perlu di-monitor

  • Calendar View
    Lihat jadwal sesi per tutor / per siswa / global. Drag-friendly reschedule.
  • Outstanding Aging
    Tagihan siswa yang belum dibayar, dikelompokkan: 0-30 / 31-60 / 61-90 / 90+ hari.
  • Honor Unpaid Reconciliation
    Honor tutor yang belum di-transfer. Tool track per sesi + bulk mark paid setelah transfer manual.

📅 Operasi akhir bulan

  • Generate Tagihan + Honorarium
    Sekali per bulan: sistem aggregate sesi yang sudah COMPLETED → jadi Tagihan siswa + Honorarium tutor.
  • Mark paid bulk
    Tag Settlement.PAID untuk siswa yang sudah upload bukti bayar, atau honor tutor yang sudah ditransfer.
  • Period Close Checklist
    Lock pembukuan bulan ini. Setelah lock: tidak boleh ada transaksi backdated. Re-open butuh approval senior.

② Saat Admin Approve / Reject Sesi (dari Approval Queue)

flowchart TD
    Open["Admin klik sesi di Approval Queue"] --> Decide{Approve
atau
Reject?} Decide -->|✓ Approve| Snap["🔒 Sistem freeze pricing + cancellation policy
(harga + policy sesi ini di-kunci,
tidak berubah meski admin edit master data nanti)"] Decide -->|✗ Reject| Audit["Reason di-audit
(REJECTED = final, tidak bisa di-undo)"] Snap --> Notify["Admin notif tutor + siswa
via WhatsApp manual"] style Snap fill:#fef3c7,color:#422006 style Notify fill:#dbeafe,color:#1e3a8a

Catatan Penting (Bahasa Awam)

  • Approval Queue = antrian sesi baru yang menunggu admin review. Sesi tidak akan dijalankan sampai admin approve.
  • SLA flag = badge peringatan kalau sesi sudah di antrian >24 jam tanpa direspons admin. Mengingatkan supaya tidak terlewat.
  • Snapshot (freeze) = saat di-approve, sistem mencatat harga + cancellation policy yang berlaku pada saat itu ke sesi tersebut. Walaupun admin nanti ubah harga di master data, sesi ini tetap pakai harga lama.
  • Notify WhatsApp manual = di iter 1, admin yang chat tutor + siswa langsung. Iter 2 → auto-notif sistem.
  • Honor Unpaid Reconciliation = tool untuk admin yang membantu track honor tutor mana yang sudah / belum di-transfer manual ke rekening tutor.
  • Istilah teknis: lihat tab 📖 Glossary di paling akhir.

Sesi State Machine (7 states)

Lifecycle sesi dari created sampai terminal state.

M3 State machine LOCKED
stateDiagram-v2
    [*] --> REQUESTED: Admin create sesi
    REQUESTED --> APPROVED: Admin approve (snapshot pricing + policy)
    REQUESTED --> REJECTED: Admin reject (reason audited)
    APPROVED --> IN_PROGRESS: Tutor Start (admin proxy iter 1)
    IN_PROGRESS --> COMPLETED: 2-step gate (tutor confirm + admin validate)
    APPROVED --> CANCELLED: Cancel flow (siswa/tutor/admin)
    APPROVED --> RESCHEDULED: Reschedule flow
    RESCHEDULED --> REQUESTED: New linked sesi created
    REJECTED --> [*]
    CANCELLED --> [*]
    COMPLETED --> [*]: Settlement UNPAID auto-created

    note right of COMPLETED
      Sub-states (COMPLETED variants):
      NO_SHOW_STUDENT (charge full),
      NO_SHOW_TUTOR (escalate cancellation)
    end note
    

State Properties

StateMeaningTrigger by
REQUESTEDSesi diminta, nunggu admin approveAuto on create
APPROVEDAdmin approve, slot di-lock, pricing+policy snapshotAdmin only
REJECTEDAdmin tolak (terminal)Admin only
IN_PROGRESSSesi mulai (timestamp logged)Admin proxy iter 1
COMPLETEDSelesai + materi confirmed; 2-step gateTutor confirm + admin validate
CANCELLEDDibatalin post-APPROVED w/ punishment calcSiswa/tutor/admin
RESCHEDULEDOld sesi closed, new linked sesi createdSiswa/tutor/admin

Booking Happy Path

End-to-end sesi sukses → settlement → invoice.

M3 → M4 → M7 Iter 1 admin-proxy
flowchart TD
    Create["Admin create sesi
(siswa + tutor + datetime + mode + Tempat Les)"] --> Req[REQUESTED] Req --> AdminAppr["Admin approve
(via Approval Queue)"] AdminAppr --> Snap["Snapshot:
pricingMatrixId
amountStudent / amountTutor
transportFee
cancellationPolicyConfigId"] Snap --> Apr[APPROVED] Apr --> Start["Tutor mark Start
(admin proxy iter 1)"] Start --> InProg[IN_PROGRESS] InProg --> Materi["Mark attendance +
materi log + foto report (R2)"] Materi --> AdmVal["Admin validate completion
(2-step COMPLETED gate)"] AdmVal --> Done[COMPLETED] Done --> Settl["Settlement auto-created
(siswa + tutor side
UNPAID, snapshot-based)"] Settl --> EOM[End of month] EOM --> Tagihan[Tagihan generated] EOM --> Honor[Honorarium generated] style Snap fill:#fef3c7,color:#422006 style Req fill:#fbbf24,color:#422006 style Apr fill:#38bdf8,color:#0c2c4a style InProg fill:#a855f7,color:#2e1065 style Done fill:#34d399,color:#052e1f

Catatan Penting

  • Snapshot at APPROVED: 4 hal di-freeze — pricing matrix ID (STUDENT + TUTOR), amount actual, transport fee tier, cancellation policy config ID. Immune dari later change.
  • 2-step COMPLETED gate: tutor confirm (admin proxy iter 1) → admin validate. Mencegah accidental complete tanpa verify materi/foto.
  • Settlement auto-created: dual entries (siswa-side untuk Tagihan, tutor-side untuk Honorarium).
  • Iter 2+: tutor login langsung mark Start / attendance / materi / foto (tanpa admin proxy).

Cancellation: Tutor Cancel (Option b Locked)

Tutor cancel ≤4h: self-sub clean atau escalate kena denda graduated + strike.

M3 (cancellation calc engine) Option (b) LOCKED
flowchart TD
    Cancel["TUTOR cancel ≤ 4 jam
via WhatsApp ke admin"] --> Decide{"Option α:
Tutor cari pengganti
SENDIRI dari komunitas?"} Decide -->|Yes try self-sub| SelfSub["Admin record self-sub attempt"] SelfSub --> SubOk{Pengganti
accept?} SubOk -->|SUCCESS| Clean["✓ Tutor original: CLEAN
(no penalty, no strike)
✓ Pengganti: full Honorarium
✓ Siswa: bayar normal
(sesi jalan dengan pengganti)"] SubOk -->|FAIL| Escalate["Escalate ke XPrivate
(Option β — transfer liability)"] Decide -->|No / fail| Escalate Escalate --> XDispatch{XPrivate
dispatch sub?} XDispatch -->|Yes| SesiJadi["Sesi jadi w/ pengganti
(via Substitute Matching)"] XDispatch -->|No| SesiCancel["Sesi cancelled
(siswa charge 0)"] SesiJadi --> Penalty SesiCancel --> Penalty Penalty["Tutor original penalty:
• Zero pay sesi (lost opp)
• Denda graduated 100/50/25%
• +1 strike
Denda = REVENUE XPrivate"] style Clean fill:#34d399,color:#052e1f style Penalty fill:#f87171,color:#420c0c

Denda Bracket (Configurable Runtime — Default)

BracketThresholdDenda %Strike
1≤ 1 jam100%+1
2≤ 2 jam50%+1
3≤ 4 jam25%+1
4> 4 jam0%0
FMForce majeure (any)0%0

Bracket runtime configurable via Cancellation Policy Editor (M2). Snapshot at APPROVED — sesi tetap pakai bracket yang berlaku saat di-approve.

Cancellation: Siswa Cancel (Bracket)

Siswa cancel charge + honor proporsional terhadap lead time.

M3 (cancellation calc engine) Bracket configurable
flowchart TD
    Cancel["SISWA cancel
via WhatsApp ke admin"] --> AdminEntry["Admin entry cancel di backoffice
+ reason + initiator: SISWA"] AdminEntry --> CalcEngine["Cancellation calc engine:
cek lead time vs bracket"] CalcEngine --> Lt1{Lead time
≥ 4 jam?} Lt1 -->|Yes| NoCharge["Charge siswa: 0
Honor tutor: 0
(sesi free cancel)"] Lt1 -->|No| Lt2{≥ 2 jam?} Lt2 -->|Yes| B3["Bracket: charge 25%
Honor 25%"] Lt2 -->|No| Lt3{≥ 1 jam?} Lt3 -->|Yes| B2["Bracket: charge 50%
Honor 50%"] Lt3 -->|No| B1["Bracket: charge 100%
Honor 100%"] NoCharge --> Logged["Audited + sesi CANCELLED"] B3 --> Logged B2 --> Logged B1 --> Logged Logged --> Override{Admin
override?} Override -->|Yes| Manual["Manual override w/ alasan
(audited)"] Override -->|No| Final[Final settlement entries] style NoCharge fill:#34d399,color:#052e1f style B1 fill:#f87171,color:#420c0c

Bracket Default

Lead timeCharge siswaHonor tutor (no transport)
< 1 jam100%100%
< 2 jam50%50%
< 4 jam25%25%
≥ 4 jam00

Transport fee tidak di-bayarkan ke tutor untuk siswa cancel (tutor tidak jadi berangkat). Admin override tetap available dengan audit.

Force Majeure Workflow

L1 review + L2 banding 14d window. Category enum (SAKIT/DUKA/DARURAT/BENCANA/LAINNYA).

M3 + cancellation policy exception 2-level + 14d appeal LOCKED
flowchart TD
    Claim["Tutor claim FM via WhatsApp
(category + evidence)"] --> Entry["Admin entry claim di backoffice
+ evidence upload (R2)"] Entry --> Status1["Status: PENDING_L1"] Status1 --> L1{L1 Operator
review} L1 -->|APPROVE| NoPenalty["Status: APPROVED_L1
✓ No penalty for tutor
✓ Audit log"] L1 -->|REJECT| Status2["Status: REJECTED_L1
+ normal penalty escalate"] Status2 --> Banding{Tutor banding
dalam 14 hari?} Banding -->|No| Final["Penalty stays final
(REJECTED_L1 terminal)"] Banding -->|Yes| Status3["Status: PENDING_L2"] Status3 --> L2{L2 Admin
senior review} L2 -->|UPHOLD| Stayed["Status: UPHELD
Penalty stays final
+ audit"] L2 -->|OVERTURN| Refund["Status: OVERTURNED
✓ Refund penalty
✓ Audit
✓ Optional comp_notes
(finance off-system condolence)"] style NoPenalty fill:#34d399,color:#052e1f style Refund fill:#34d399,color:#052e1f style Final fill:#f87171,color:#420c0c style Stayed fill:#f87171,color:#420c0c

FM Categories & Properties

  • Categories: SAKIT · DUKA · DARURAT · BENCANA · LAINNYA
  • Evidence requirement: surat dokter / surat keterangan instansi resmi (untuk sakit + bencana)
  • L1 approver: operator (admin) — case-by-case judgment
  • L2 approver: senior admin — banding only
  • Appeal window: 14 hari rolling dari REJECTED_L1 timestamp
  • comp_notes field: optional — finance bayar belasungkawa off-system bila ada (tidak otomatis)
  • Termination blocker: bila ada FM PENDING_L1 atau PENDING_L2 → termination tutor di-block sampai resolved

Substitute Matching (Admin-side)

Admin dispatch via WhatsApp setelah tutor cancel & gagal self-sub.

M3 Iter 1 manual dispatch
flowchart TD
    XCancel["Tutor X cancel via WhatsApp
→ admin info"] --> CheckSelf{"Tutor X coba
self-sub dulu?"} CheckSelf -->|Berhasil| Direct["Sesi jalan dengan tutor self-sub
(no penalty for X)"] CheckSelf -->|Gagal / skip| OpenDetail["Admin open schedule X
klik [Cari Sub]"] OpenDetail --> ListCand["List candidates sorted by distance:
Filter:
• subject match (Subject + SubjectLevel)
• Slot bebas (tidak overlap sesi)
• status ACTIVE
Display: nama, distance km,
Golongan, last_active"] ListCand --> Pick["Admin pilih candidate Z"] Pick --> Dispatch["Dispatch via WhatsApp manual
(template message)"] Dispatch --> Accept{Z accept?} Accept -->|Yes| Reassign["Admin update sesi assignment ke Z
(audit log capture)"] Reassign --> NewSnap["Re-snapshot Honorarium
(berdasarkan Z's Golongan)"] NewSnap --> Run["Sesi jalan dengan Z
(X kena penalty Option β)"] Accept -->|No| ListCand Accept -->|Habis kandidat| Cancel["Sesi cancelled
(siswa charge 0, X kena penalty)"] style Direct fill:#34d399,color:#052e1f style Run fill:#34d399,color:#052e1f style Cancel fill:#f87171,color:#420c0c

Iter 1 Constraints

  • No service radius MVP — sortir by distance saja, tanpa hard cutoff (admin judgment)
  • Manual dispatch via WhatsApp — tidak ada auto-accept/decline. Admin chat manual, Z respond, admin update.
  • Re-snapshot Honorarium — kalau Z's Golongan beda dari X, Honorarium di-recalc. Tagihan siswa tetap sama (STUDENT matrix tidak dipengaruhi).
  • Iter 2+: tutor punya app/portal → notif push langsung ke Z, accept/decline in-app, audit lebih lengkap.

Monthly Billing & Honor Cycle

End of month — Tagihan ke siswa + Honorarium ke tutor (snapshot-based).

M4 + M7 Iter 1 manual transfer
flowchart TD
    EOM[End of month trigger] --> GenT["Generate Tagihan per siswa
(aggregate semua sesi COMPLETED
+ NO_SHOW_STUDENT + cancellation
charges + adjustment lines)"] EOM --> GenH["Generate Honorarium per tutor
(aggregate sesi by Golongan
+ transport reimburse
+ adjustments + PPh placeholder)"] GenT --> Share["Tagihan dishare ke siswa
(welcome email + admin WA manual)"] Share --> Bayar["Siswa bayar offline
(bank transfer / e-wallet)
upload bukti via WA"] Bayar --> AdmVer["Admin verify bukti
+ mark Settlement.PAID bulk"] AdmVer --> Aging["Outstanding aging update
(0-30 / 31-60 / 61-90 / 90+)"] GenH --> Slip["Honor slip PDF generated
(breakdown per tutor per bulan)"] Slip --> Trf["Finance batch transfer off-system
(bank/e-wallet)"] Trf --> MarkH["Admin mark
SettlementTeacher.PAID bulk"] Aging --> WriteOff{Bad debt
review?} WriteOff -->|Yes| WO["Senior approval +
tax compliance flag +
audit"] WriteOff -->|No| OK[Move to next cycle] AdmVer --> Close[Ready for Period Close] MarkH --> Close style Close fill:#fef3c7,color:#422006

Key Notes

  • Tagihan = STUDENT matrix amount snapshot + transport tier + cancellation charges + adjustment lines. Per-sesi line items visible ke siswa.
  • Honorarium = TUTOR matrix amount snapshot (by Golongan) + actual transport reimburse (XPrivate cover delta) + adjustments - PPh withholding. SISWA NEVER SEES.
  • Manual reminder broadcast: admin bisa trigger WhatsApp template untuk late-payment siswa atau low-completion tutor.
  • Honor reconciliation tool: per-session granular + bulk mark paid (untuk track session yang honor-nya belum di-transfer).

Financial Period Close (M7)

Monthly close checklist + period lock + re-open option.

M7 Audit-heavy
flowchart TD
    Trigger["Finance trigger monthly close"] --> CheckIP{Ada sesi
IN_PROGRESS?} CheckIP -->|Yes| BlockIP["BLOCK: finalize sesi dulu
(tutor confirm + admin validate)"] CheckIP -->|No| Checklist["Monthly close checklist UI
(step-through)"] Checklist --> S1["✓ Bills (Tagihan) generated"] Checklist --> S2["✓ Payments reconciled
(siswa bayar bukti verified)"] Checklist --> S3["✓ Honor batch ready
(Honorarium generated)"] Checklist --> S4["✓ FM cases reviewed
(no PENDING_L1/L2)"] Checklist --> S5["✓ Adjustments processed
(credit notes / refunds)"] S1 --> AllGood{Semua
checked?} S2 --> AllGood S3 --> AllGood S4 --> AllGood S5 --> AllGood AllGood -->|Yes| Lock["Period LOCK flag set
+ Lock audit (who/when)"] AllGood -->|No| Wait[Address pending items] Lock --> Effect["Effect:
• No backdated bookings
• No retroactive mark-paid
• No new adjustments"] Effect --> ReOpen{Need
re-open?} ReOpen -->|Yes| Senior["Senior admin approval
+ reason audited
+ unlock"] ReOpen -->|No| Done[Period closed final] style BlockIP fill:#f87171,color:#420c0c style Lock fill:#34d399,color:#052e1f style Done fill:#34d399,color:#052e1f

Period Close Properties

  • IN_PROGRESS blocker: hard guard — period tidak bisa di-close kalau ada sesi belum finalized (mencegah data integrity issue).
  • Lock flag: setelah lock, attempt operasi backdated / retroactive akan rejected dengan error UI.
  • Re-open option: senior admin only, with reason audit. Untuk correction case yang ditemukan setelah close.
  • Reconciliation export: Excel template finance-familiar dengan adjustments column, dipakai untuk validation final sebelum lock.

Tutor Termination Flow (M6)

HR-driven termination dengan FM blocker, termination checklist, archive.

M6 HR Discipline
flowchart TD
    Reason["Termination reason:
• Strike 3 (auto-trigger)
• HR judgment (manual)
• Tutor resign"] --> Trigger["HR trigger termination via UI"] Trigger --> CheckFM{FM claim status
PENDING_L1 atau
PENDING_L2?} CheckFM -->|Yes| Block["⚠ BLOCKED:
Resolve FM claim dulu
(no termination during FM review)"] CheckFM -->|No| Checklist["Termination Checklist UI
(step-through)"] Checklist --> S1["1. Final settlement payout
(auto-trigger calc untuk
remaining unpaid sesi)"] Checklist --> S2["2. Document return tracking"] Checklist --> S3["3. Access revoke immediate
(revoke OAuth tokens +
login disabled atomic txn)"] Checklist --> S4["4. Archive flag
(tutor.status=ARCHIVED)"] S1 --> Final[Final terminated] S2 --> Final S3 --> Final S4 --> Final Final --> Archive["Tutor data preserved
(read-only via ARCHIVED status)
Historic sesi/settlement tetap query-able"] Archive --> Reactivate{Reactivation
request?} Reactivate -->|Yes| Senior2["Senior admin approval
+ audit log
+ ARCHIVED → ACTIVE"] Reactivate -->|No| End[Termination final] style Block fill:#f87171,color:#420c0c style Final fill:#fbbf24,color:#422006 style End fill:#94a3b8,color:#1e293b

Strike Sanction Reference (Triggers Termination)

  • Strike 1: Warning + audit log entry
  • Strike 2: Final warning + temporary suspend 7 hari
  • Strike 3: Termination kerjasama (auto-trigger)
  • Strike decay: 90 hari rolling window (oldest expires)

Critical Properties

  • FM blocker: legal/fairness protection — jangan terminate tutor yang masih punya pending FM claim (mungkin claim valid → no penalty → no Strike 3).
  • Final settlement auto-trigger: legal obligation — semua unpaid sesi yg sudah COMPLETED harus di-payout.
  • Access revoke immediate + atomic: same DB transaction dengan termination flag set, untuk konsistensi.
  • Archive (bukan delete): data preservation untuk audit trail historical disputes / regulator inquiry.

5D Pricing Matrix Structure

Dua matrix versioned per Tahun Ajaran. Snapshot at APPROVED.

M2 5D Matrix LOCKED

① Matrix Structure (dua matrix terpisah)

flowchart LR
    subgraph SM["STUDENT Matrix → Tagihan / sesi"]
      direction TB
      S1[Subject]
      S2["SubjectLevel ?
(optional child)"] S3[Tingkat] S4[Segmentasi] S1 --> SA[("amount/sesi")] S2 -.-> SA S3 --> SA S4 --> SA end subgraph TM["TUTOR Matrix → Honorarium / sesi"] direction TB T1[Subject] T2["SubjectLevel ?
(optional child)"] T3[Tingkat] T4[Segmentasi] T5["Golongan
(tutor tier)"] T1 --> TA[("amount/sesi")] T2 -.-> TA T3 --> TA T4 --> TA T5 --> TA end style SA fill:#38bdf8,color:#0c2c4a style TA fill:#a855f7,color:#2e1065

② Snapshot Composition at APPROVED (vertical flow)

flowchart TD
    SA["amountStudent
← lookup STUDENT matrix"] --> Snap TA["amountTutor
← lookup TUTOR matrix"] --> Snap TR["transportFee
← app config bracket per km
(default <20km=Rp10k, >20km=Rp12k)"] --> Snap MS["mode_surcharge
(optional per-sesi escape hatch)"] --> Snap Snap["🔒 Snapshot at APPROVED
frozen ke Schedule record:
pricingMatrixIdStudent + pricingMatrixIdTutor
+ amountStudent + amountTutor
+ transportFee + mode_surcharge
+ cancellationPolicyConfigId"] Snap --> Margin["💵 XPrivate margin (derived per sesi)
= amountStudent − amountTutor − (XPrivate transport share)"] style SA fill:#38bdf8,color:#0c2c4a style TA fill:#a855f7,color:#2e1065 style Snap fill:#fef3c7,color:#422006 style Margin fill:#34d399,color:#052e1f

③ Contoh Concrete Rows

MatrixSubjectSubjLevelTingkatSegmentasiGolonganAmount/sesi
STUDENTMandarinHSK 3SMA 10-12REGULERRp 150.000
STUDENTMandarinHSK 3SMA 10-12INTERNASIONALRp 250.000
STUDENTPianoSD 1-6REGULERRp 120.000
TUTORMandarinHSK 3SMA 10-12REGULERGOL_1Rp 120.000
TUTORMandarinHSK 3SMA 10-12REGULERGOL_3Rp 80.000
TUTORPianoSD 1-6REGULERGOL_2Rp 90.000

Margin XPrivate per sesi = STUDENT amount − TUTOR amount (untuk row Mandarin/HSK3/SMA/REGULER/GOL_1: Rp 150k − Rp 120k = Rp 30k).

Versioning & Operations

  • Versioned per Tahun Ajaran (Jul-Jun cycle) dengan effective_from / effective_until.
  • Sparse allowed: admin bisa kosongkan combo. Coverage gap detector warn "Subject X has 60% combos populated".
  • Mid-flight override OK: admin bisa edit current matrix (audit-logged).
  • Atomic Clone Tahun Ajaran: one-click — set effective_until on old + insert new rows w/ %-bump. Audit-tracked.
  • Hierarchical drill-down UX: Subject → SubjectLevel → matrix per audience (NOT flat 6-column entry).
  • CSV import + bulk-edit + per-row validation.
  • Snapshot at APPROVED: sesi capture pricingMatrixId + frozen amount. Immune dari later config change.

Auth & Identity Landscape (Future-Facing)

Iter 1 admin only ACTIVE. Multi-identity schema DB-READY sejak awal (user constraint: auth = nightmare to change later).

M1 + cross-cutting User constraint LOCKED
flowchart LR
    subgraph A1["Iter 1 ACTIVE"]
      AdminLogin["Admin Login
Google OAuth +
Workspace domain restrict
@xprivate.education"] Session[Session mgmt
timeout + remember-me] RBAC["Custom flexible RBAC
Role + Permission +
RolePermission + UserRole"] end subgraph DR["Iter 1 DB-READY (schema present, deferred activation)"] UserType["UserType enum:
• ADMIN ✓ (active)
• TUTOR (ready)
• SISWA (ready)
• PARENT (ready)"] IdProvider["IdentityProvider:
• Google OAuth ✓ (active)
• email/password (ready)
• social login (ready)"] UserIdentity["UserIdentity
(many-to-many link)"] TFA["TwoFactorMethod
(2FA TOTP / SMS ready)"] LoginAudit["LoginAuditEvent
(login events ready)"] PwdReset["PasswordReset
EmailVerification"] AccState["AccountState machine:
PENDING_INTERVIEW →
APPROVED → ACTIVE →
SUSPENDED → ARCHIVED"] end subgraph A2["Iter 2+ ACTIVATION"] TutorPortal["Tutor portal login
(basic email/pwd
or magic link)"] SiswaPortal["Siswa portal login"] Activate2FA["2FA TOTP activate
(opt-in or mandatory)"] SelfReg["Self-register flow
(apply → interview →
approved → active)"] end AdminLogin --> Session Session --> RBAC UserType -.-> TutorPortal UserType -.-> SiswaPortal IdProvider -.-> TutorPortal TFA -.-> Activate2FA AccState -.-> SelfReg style AdminLogin fill:#34d399,color:#052e1f style Session fill:#34d399,color:#052e1f style RBAC fill:#34d399,color:#052e1f style UserType fill:#fbbf24,color:#422006 style IdProvider fill:#fbbf24,color:#422006 style UserIdentity fill:#fbbf24,color:#422006 style TFA fill:#fbbf24,color:#422006 style LoginAudit fill:#fbbf24,color:#422006 style PwdReset fill:#fbbf24,color:#422006 style AccState fill:#fbbf24,color:#422006 style TutorPortal fill:#38bdf8,color:#0c2c4a style SiswaPortal fill:#38bdf8,color:#0c2c4a style Activate2FA fill:#38bdf8,color:#0c2c4a style SelfReg fill:#38bdf8,color:#0c2c4a

Why Future-Facing dari Iter 1

  • Auth menyentuh setiap entity — siapa user, role, session, permission, identity, MFA, audit. Migration auth pasca-launch = pain.
  • Schema cost rendah: tambah tabel/kolom upfront ≪ migrate auth model dengan data sensitif + downtime + retraining.
  • Iter 1 admin-only ACTIVE: cuma Google OAuth + RBAC + Session yang dipakai. Sisanya schema dormant.
  • Iter 2 ACTIVATION: tutor portal login (basic) supaya tutor mark Start/attendance/materi sendiri (genuine identity audit, bukan admin proxy).

📖 Glossary — Istilah XPrivate Education

Kamus istilah yang dipakai di dokumen ini, scope.md, dan UI sistem. Untuk pembaca non-teknis.

Reference Term LOCKED

👥 Aktor / Entitas

IstilahArtinya (bahasa awam)
Sesi1 unit pengajaran (1 lesson). Default durasi 90 menit, format 1 tutor & 1 siswa.
SiswaPelanggan / murid yang les. (Term formal "siswa", bukan "murid".)
TutorGuru yang mengajar. (Term formal "tutor", bukan "guru".)
Admin / OperatorStaff XPrivate yang mengelola sistem di iter 1 (1-5 orang). Di iter 1, semua interaksi dengan sistem dilakukan admin.

📚 Catalog Dimensions (kategori untuk pricing & filtering)

IstilahArtinya
SubjectMata pelajaran konkret. Contoh: "Bahasa Mandarin", "Piano", "CPNS", "Matematika".
SubjectLevelTingkatan dalam satu Subject. Contoh: HSK 1-6 di Mandarin; Grade 1-8 di Piano. Opsional (tidak semua Subject punya level).
TingkatTingkat pendidikan siswa. Pilihan: SD 1-6, SMP 7-9, SMA 10-12, Mahasiswa, Umum, Profesional.
SegmentasiTier layanan yang dipilih siswa: REGULER, REGULER_PLUS, INTERNASIONAL. Mempengaruhi harga.
KategoriGrouping/kelompok Subject (REGULER_KELAS, PROGRAM_KHUSUS, SERTIFIKASI, BAHASA_ASING, MUSIK, KOMPUTER). Hanya untuk organisasi — TIDAK mempengaruhi harga.
GolonganTier tutor: GOL_1 (paling senior) sampai GOL_4 (paling junior), + NON_GOLONGAN (belum di-tier). Mempengaruhi honor tutor.

📅 Periode & Versioning

IstilahArtinya
Tahun AjaranPeriode Jul-Jun (mengikuti siklus pendidikan Indonesia). Contoh: Tahun Ajaran 2026/2027 = Jul 2026 sampai Jun 2027. Pricing matrix di-versioned per Tahun Ajaran.
SnapshotSistem mencatat data harga + cancellation policy yang berlaku saat sesi di-approve, supaya sesi itu pakai aturan lama walau admin edit master data nanti. Mencegah retroactive price change.
Effective_from / Effective_untilTanggal mulai & berakhir berlakunya satu baris pricing matrix atau cancellation config.
Atomic Clone Tahun AjaranOperasi 1-klik untuk admin: copy semua pricing dari Tahun Ajaran lama ke Tahun Ajaran baru, dengan opsi naik harga (%) sekaligus.

💰 Finansial

IstilahArtinya
TagihanInvoice yang dikirim ke siswa per bulan (post-paid). Berisi list sesi yang dijalankan + transport + adjustment.
HonorariumHonor yang dibayarkan ke tutor per bulan. Siswa TIDAK PERNAH lihat detail Honorarium.
SettlementCatatan transaksi per sesi (di sisi siswa = jadi line di Tagihan; di sisi tutor = jadi line di Honorarium).
STUDENT MatrixTabel harga yang dipakai untuk hitung Tagihan siswa per sesi.
TUTOR MatrixTabel harga yang dipakai untuk hitung Honorarium tutor per sesi (include dimensi Golongan).
Transport bracketTier ongkos transport berdasarkan jarak (default: <20km = Rp10.000, >20km = Rp12.000). Tagihan ke siswa pakai tier; tutor reimburse pakai bukti actual; XPrivate cover delta.
mode_surchargeTambahan harga opsional per sesi — kalau ternyata di kasus tertentu online vs offline perlu beda harga. Escape hatch.
Outstanding AgingLaporan tagihan yang belum dibayar, dikelompokkan by usia: 0-30 hari, 31-60, 61-90, 90+ hari (overdue).
Credit NoteDokumen finansial yang mengurangi Tagihan siswa (untuk refund, dispute resolution, atau koreksi salah charge).
Bad Debt Write-offTagihan yang sudah tidak bisa di-collect ditandai sebagai "kerugian". Butuh approval senior + audit + flag pajak.
PPh withholdingPotongan pajak penghasilan dari honor tutor. Iter 1: field manual input. Iter 2: engine tax full.
Period CloseProses mengunci pembukuan keuangan satu bulan. Setelah locked: tidak bisa ada transaksi backdated atau retroactive mark-paid.

⏱️ Sesi States (state machine 7 status)

StateArtinya
REQUESTEDSesi baru dibuat admin, menunggu approval. Slot belum di-lock.
APPROVEDAdmin sudah approve, slot tutor di-lock, harga + cancellation policy snapshot.
REJECTEDAdmin tolak. Final — tidak bisa di-undo. Reason di-audit.
IN_PROGRESSSesi sedang berjalan (sudah di-mark Start).
COMPLETEDSesi selesai + materi confirmed. Lewat 2-step gate (tutor confirm + admin validate).
CANCELLEDDibatalin setelah APPROVED. Bisa kena penalty (tergantung policy bracket).
RESCHEDULEDSesi lama di-close, sesi baru linked dibuat dengan jadwal baru.
NO_SHOW_STUDENTSub-state COMPLETED: siswa tidak hadir. Siswa kena charge full.
NO_SHOW_TUTORSub-state COMPLETED: tutor tidak hadir. Escalate ke cancellation flow.

⚙️ Operasi & Compliance

IstilahArtinya
Approval QueueAntrian sesi yang status REQUESTED, menunggu admin approve/reject.
Anomaly QueueAntrian sesi/item yang flag oleh sistem sebagai mencurigakan (mis. double-booking, durasi aneh).
SLAService Level Agreement — komitmen waktu tanggap. "SLA flag" = badge warn kalau item lewat threshold tanggap (mis. REQUESTED >24 jam = SLA breach).
SlotWindow waktu availability tutor (kapan dia bisa ngajar).
FM (Force Majeure)Keadaan darurat (sakit, duka, bencana, dll) yang membebaskan tutor dari penalty cancellation. Butuh approval L1 + opsi banding L2 dalam 14 hari.
StrikePelanggaran tutor (mis. cancel mendadak tanpa FM). Akumulasi 3 strike dalam 90 hari → terminate. Strike decay 90 hari rolling.
Self-sub (Option α)Tutor cancel ≤4h tapi cari pengganti sendiri dari komunitas tutor XPrivate. Kalau sukses = clean (no penalty).
Escalate (Option β)Tutor cancel ≤4h dan tidak/gagal cari pengganti sendiri → XPrivate yang dispatch substitute. Tutor original kena penalty graduated + strike.
Audit TrailLog perubahan: siapa lakukan apa, kapan, ke entity mana, sebelum vs sesudah. Immutable (tidak bisa di-edit/delete), retention 7 tahun.
R2Cloudflare object storage (untuk simpan file: foto KTP, kontrak PDF, foto materi/report sesi, bukti bayar).
Coverage gap detectorTool yang warn ke admin: "Subject X cuma 60% combos populated" — supaya admin tahu mana pricing yang masih bolong di matrix.

🧱 Modul (developer-facing reference)

ModulScope
M1User Management — login admin, CRUD admin user, role & privilege
M2Master Data — tutor/siswa profile, catalog dimensions, pricing matrix, cancellation policy editor, app config
M3Schedule Management — create sesi, approval queue, calendar, cancellation engine, FM, substitute matching
M4Reporting — Tagihan, Honorarium, student progress, outstanding aging, export Excel
M6HR Discipline + Termination — strike, suspend, terminate, archive, reactivation
M7Finance Workflow — refund/credit note, period close, reconciliation, PPh placeholder, honor slip PDF
A1Audit Trail (cross-cutting) — middleware logging, immutable, 7y retention
A2Notification Trigger — iter 1 manual welcome email saja, iter 2 multi-channel auto

🚀 Iteration Strategy

IstilahArtinya
Iter 1 (Admin-MVP)Iteration pertama: pure backoffice admin. Customer-facing self-service belum ada. Fokus de-risk foundation.
Iter 2+Iteration berikutnya: customer-facing aktivasi (tutor/siswa self-service signup & login, payment gateway, auto-notif, Zoom, in-app chat, dispute UI).
DB-readySchema database sudah didesain support fitur tersebut, tapi UI/feature belum di-implementasi. Tujuan: tidak perlu migration besar saat fitur di-aktivasi nanti.
Admin proxyDi iter 1, action yang seharusnya dilakukan tutor (mark Start sesi, attendance, upload materi) di-input admin atas nama tutor — karena tutor belum punya login sendiri.
Navigasi: · 1-9 jump tab