# PLAN — Homeroom Finances (Kas & Keuangan Kelas)

**Tujuan**: Menyediakan sistem pembukuan kas kelas yang terkelola oleh Wali Kelas di UtiClass, mencakup pos dana insidental/keperluan khusus, kas rutin harian/mingguan, serah terima setoran siswa (bulk settlement), pencatatan pengeluaran dengan bukti nota, dan akuntansi talangan (piutang wali kelas) jika saldo kas tidak mencukupi.

**Status**: Implementasi dasar tersedia; diperluas dengan pengurus kelas dan pencatatan bendahara
**Tanggal**: 3 Oktober 2026

> Alur siswa mengumpulkan uang hanya secara offline pada rancangan awal di bawah sudah diperluas. Bendahara kelas sekarang mendapat akses pencatatan pembayaran, sementara pengaturan dana, penerimaan uang fisik, pengeluaran, dan koreksi tetap di wali kelas. Kontrak dan panduan terkini ada di [docs/HOMEROOM-TREASURER-FINANCE.md](docs/HOMEROOM-TREASURER-FINANCE.md).

---

## 0. Ringkasan Kebutuhan & Aturan Bisnis

1. **Aktor Utama**: Wali Kelas (Modul `Homeroom/*`). Siswa hanya mengumpulkan uang secara fisik di kelas (offline).
2. **Karakteristik Pos Dana (`HomeroomFund`)**:
   - Pos Khusus / Insidental: Keperluan tertentu (misal: "Beli Perlengkapan Kebersihan", "Jenguk Ahmad Sakit").
   - Pos Kas Rutin: Berulang (misal: "Uang Kas Harian/Mingguan").
   - Partisipan: Wali kelas dapat memilih seluruh siswa di kelas atau memilih siswa tertentu.
3. **Mekanisme Setoran Siswa (Bulk Settlement)**:
   - Wali kelas menerima uang fisik dari bendahara siswa.
   - Di aplikasi, wali kelas mencentang siswa yang sudah setor (multi-select), lalu mengonfirmasi setoran sekaligus.
   - Siswa yang belum bayar tetap berstatus *unpaid* / tertunggak tanpa memblokir alur operasional kelas.
4. **Mekanisme Pengeluaran & Talangan (Strict Zero-Floor with Liability Advance)**:
   - Saldo kas pos dana wajib cukup untuk belanja.
   - Jika saldo kas fisik tidak cukup dan pengeluaran mendesak harus dilakukan:
     - Sistem memvalidasi kekurangan saldo.
     - Wali kelas mengonfirmasi bahwa kekurangan ditalangi menggunakan uang pribadi.
     - Sistem mencatat:
       a. `advance_teacher_in`: Dana talangan masuk dari uang pribadi wali kelas (tercatat sebagai hutang kelas ke wali kelas).
       b. `outflow_expense`: Pengeluaran belanja tercatat lunas dengan saldo kas tetap akurat non-negatif.
     - Ketika ada siswa menyusul membayar iuran, sistem menyediakan aksi **Reimbursement** untuk mengembalikan uang talangan ke wali kelas.
5. **Konteks Periode & Akses**:
   - Selalu terikat ke `SyncedClass`, `AcademicYear`, dan `Semester` melalui `HomeroomAccessService`.
   - Hanya wali kelas yang ditugaskan pada kelas dan periode tersebut yang berhak mengakses dan memodifikasi data.

---

## 1. Skema Database & Migrasi

### File Migrasi: `database/migrations/2026_10_03_000001_create_homeroom_finances_tables.php`

#### A. Tabel `homeroom_funds`
| Kolom | Tipe | Keterangan |
|---|---|---|
| `id` | id | Primary Key |
| `class_id` | foreignId | FK ke `synced_classes` (cascadeOnDelete) |
| `academic_year_id` | foreignId | FK ke `academic_years` (cascadeOnDelete) |
| `semester_id` | foreignId, nullable | FK ke `semesters` (nullOnDelete) |
| `name` | varchar(160) | Nama pos dana (misal: Kas Rutin, Beli Spidol & Cat) |
| `type` | string(30) | `routine` (kas berkala) / `incidental` (kegiatan/keperluan) |
| `frequency` | string(30) | `one_time`, `daily`, `weekly`, `monthly` |
| `target_amount` | unsignedBigInteger, nullable | Target iuran per siswa (null jika donasi sukarela) |
| `description` | text, nullable | Keterangan / tujuan pengumpulan dana |
| `status` | string(20) | `active`, `closed` |
| `created_by` | foreignId | FK ke `users` |
| `created_at`, `updated_at` | timestamps | |

Index: `['class_id', 'academic_year_id', 'semester_id']`, `status`.

#### B. Tabel `homeroom_fund_obligations`
| Kolom | Tipe | Keterangan |
|---|---|---|
| `id` | id | Primary Key |
| `fund_id` | foreignId | FK ke `homeroom_funds` (cascadeOnDelete) |
| `student_id` | foreignId | FK ke `synced_students` (cascadeOnDelete) |
| `period_label` | varchar(60), nullable | Label periode (misal: "Minggu 1", atau null jika one-time) |
| `amount_expected` | unsignedBigInteger | Target nominal yang harus disetor |
| `amount_paid` | unsignedBigInteger | Nominal yang sudah diserahkan ke wali kelas (default 0) |
| `status` | string(20) | `unpaid`, `paid`, `waived` (dibebaskan) |
| `paid_at` | timestamp, nullable | Waktu pelunasan/setor |
| `recorded_by` | foreignId, nullable | FK ke `users` |
| `created_at`, `updated_at` | timestamps | |

Index: `['fund_id', 'student_id']`, `['fund_id', 'status']`.

#### C. Tabel `homeroom_fund_entries`
| Kolom | Tipe | Keterangan |
|---|---|---|
| `id` | id | Primary Key |
| `class_id` | foreignId | FK ke `synced_classes` (cascadeOnDelete) |
| `fund_id` | foreignId, nullable | FK ke `homeroom_funds` (nullOnDelete) |
| `entry_type` | string(30) | `inflow_student`, `outflow_expense`, `advance_teacher_in`, `reimburse_teacher_out` |
| `amount` | unsignedBigInteger | Nominal mutasi (selalu positif) |
| `transaction_date` | date | Tanggal transaksi |
| `title` | varchar(160) | Judul transaksi |
| `description` | text, nullable | Rincian keperluan/transaksi |
| `evidence_path` | varchar(255), nullable | File nota/struk belanja di disk storage |
| `recorded_by` | foreignId | FK ke `users` |
| `created_at`, `updated_at` | timestamps | |

Index: `['class_id', 'transaction_date']`, `['fund_id', 'entry_type']`.

---

## 2. Eloquent Models & Relasi

1. `App\Models\HomeroomFund`:
   - `belongsTo(SyncedClass::class, 'class_id')`
   - `belongsTo(AcademicYear::class)`
   - `belongsTo(Semester::class)`
   - `hasMany(HomeroomFundObligation::class, 'fund_id')`
   - `hasMany(HomeroomFundEntry::class, 'fund_id')`
   - Scopes: `forClassAndPeriod($classId, $period)`
   - Helper methods: `getCurrentBalanceAttribute()`, `getTotalInflowAttribute()`, `getTotalOutflowAttribute()`, `getPendingAdvanceAttribute()`

2. `App\Models\HomeroomFundObligation`:
   - `belongsTo(HomeroomFund::class, 'fund_id')`
   - `belongsTo(SyncedStudent::class, 'student_id')`

3. `App\Models\HomeroomFundEntry`:
   - `belongsTo(SyncedClass::class, 'class_id')`
   - `belongsTo(HomeroomFund::class, 'fund_id')`
   - `belongsTo(User::class, 'recorded_by')`

---

## 3. Service Layer (`HomeroomFinanceService`)

Lokasi: `app/Services/HomeroomFinanceService.php`

Metode Utama:
- `getSummary(SyncedClass $class, array $period): array`
  Menghitung:
  - Saldo Kas Tersedia (Total Inflows - Total Outflows)
  - Total Iuran Terkumpul
  - Total Belanja/Pengeluaran
  - Total Hutang Talangan Wali Kelas yang belum di-reimburse
- `createFund(User $actor, SyncedClass $class, array $period, array $data): HomeroomFund`
  Membuat pos dana baru sekaligus mendaftarkan siswa partisipan (`obligations`).
- `recordBulkPayment(User $actor, HomeroomFund $fund, array $studentIds, ?int $amount): void`
  Mencatat pelunasan setoran untuk siswa-siswa terpilih, sekaligus membuat `HomeroomFundEntry` tipe `inflow_student`.
- `recordExpense(User $actor, SyncedClass $class, array $period, array $data): HomeroomFundEntry`
  Memvalidasi saldo kas pos dana.
  - Jika saldo cukup: potong langsung (`outflow_expense`).
  - Jika saldo kurang dan `allow_advance = true`:
    1. Buat entry `advance_teacher_in` sebesar nominal kekurangan.
    2. Buat entry `outflow_expense` sebesar total belanja.
    3. Simpan bukti nota jika diunggah.
- `reimburseAdvance(User $actor, SyncedClass $class, array $period, int $amount, ?int $fundId): HomeroomFundEntry`
  Mencairkan kembali kas kelas untuk membayar hutang talangan wali kelas (`reimburse_teacher_out`).

---

## 4. Endpoints & Routes (`routes/web.php`)

Grup rute di bawah `Route::middleware(['auth', 'verified', 'role:guru,admin'])`:
```php
// Wali Kelas - Kas & Keuangan Kelas
Route::get('/homeroom/finances', [HomeroomFinanceController::class, 'index'])->name('homeroom.finances.index');
Route::post('/homeroom/finances/funds', [HomeroomFinanceController::class, 'storeFund'])->name('homeroom.finances.funds.store');
Route::get('/homeroom/finances/funds/{fund}', [HomeroomFinanceController::class, 'showFund'])->name('homeroom.finances.funds.show');
Route::put('/homeroom/finances/funds/{fund}', [HomeroomFinanceController::class, 'updateFund'])->name('homeroom.finances.funds.update');
Route::delete('/homeroom/finances/funds/{fund}', [HomeroomFinanceController::class, 'destroyFund'])->name('homeroom.finances.funds.destroy');

// Setoran Siswa (Bulk / Single)
Route::post('/homeroom/finances/funds/{fund}/settle', [HomeroomFinanceController::class, 'settleObligations'])->name('homeroom.finances.funds.settle');

// Pengeluaran & Talangan
Route::post('/homeroom/finances/expenses', [HomeroomFinanceController::class, 'storeExpense'])->name('homeroom.finances.expenses.store');
Route::post('/homeroom/finances/reimburse', [HomeroomFinanceController::class, 'reimburseAdvance'])->name('homeroom.finances.reimburse');
Route::delete('/homeroom/finances/entries/{entry}', [HomeroomFinanceController::class, 'destroyEntry'])->name('homeroom.finances.entries.destroy');
```

---

## 5. Antarmuka Pengguna (Frontend React + TypeScript)

Lokasi Halaman: `resources/js/Pages/Homeroom/Finance/`
1. `Index.tsx`:
   - Header KPI: Saldo Kas Bersih, Total Terkumpul, Total Pengeluaran, Hutang Talangan Belum Diganti.
   - Banner Peringatan jika ada talangan wali kelas yang siap di-reimburse saat saldo kas sudah masuk.
   - Tabs:
     - **Tab 1: Pos Dana (Khusus & Rutin)**: Grid kartu pos dana, progres nominal, tombol "Terima Setoran" & "Rincian".
     - **Tab 2: Buku Kas & Log Pengeluaran**: Riwayat mutasi, filter masuk/keluar/talangan, preview nota struk.
2. `Show.tsx` (Rincian Pos Dana & Checklist Siswa):
   - Daftar seluruh siswa di kelas.
   - Filter status: Semua / Sudah Bayar / Belum Bayar.
   - Multi-select checkbox: Centang nama-nama siswa yang uangnya sudah diterima wali kelas -> Tombol "Konfirmasi Terima Setoran (X Siswa)".
   - Tombol "Catat Pengeluaran Pos Ini".
3. **Modal Dialogs**:
   - `CreateFundModal.tsx`: Form pos baru + selector siswa partisipan.
   - `RecordExpenseModal.tsx`: Form nominal belanja, nota file, dan toggle otomatis "Talangi dengan dana pribadi jika saldo kurang".
   - `ReimburseModal.tsx`: Form pencairan kembali uang talangan wali kelas.

---

## 6. Navigasi

Di `resources/js/Layouts/AuthenticatedLayout.tsx`:
Tambahkan item navigasi di bawah grup `Wali Kelas`:
```tsx
{ 
    href: '/homeroom/finances', 
    label: 'Uang Kas & Keuangan', 
    icon: WalletCards, // Lucide icon
    homeroomOnly: true 
}
```

---

## 7. Rencana Eksekusi Bertahap

- [ ] **Langkah 1**: Migrasi Database (`2026_10_03_000001_create_homeroom_finances_tables.php`) & jalankan `php artisan migrate`.
- [ ] **Langkah 2**: Eloquent Models (`HomeroomFund`, `HomeroomFundObligation`, `HomeroomFundEntry`).
- [ ] **Langkah 3**: Service Layer (`HomeroomFinanceService`) dengan business logic talangan & bulk payment.
- [ ] **Langkah 4**: Request Validation & Controller (`HomeroomFinanceController`).
- [ ] **Langkah 5**: Registrasi Routes di `routes/web.php` dan link menu di `AuthenticatedLayout.tsx`.
- [ ] **Langkah 6**: Frontend UI React (`Index.tsx`, `Show.tsx`, komponen Modals).
- [ ] **Langkah 7**: Feature Test (`tests/Feature/HomeroomFinanceTest.php`) untuk memverifikasi isolasi akses antar wali kelas, kalkulasi saldo, bulk settlement, dan mekanisme talangan.
