# Fitur: Hitung Kas Fisik Harian

Fitur ini memungkinkan teller menghitung dan mencatat uang fisik harian berdasarkan
pecahan IDR, lalu membandingkannya dengan **saldo transaksi harian sistem** (kas masuk
dikurangi kas keluar hari itu). Data bersifat audit-ready dan tidak dapat diedit setelah
submit.

---

## Keputusan Desain Penting

> [!IMPORTANT]
> **Sumber `system_balance`**: Dihitung dari transaksi kas hari itu —
> **Kas masuk** (angsuran pembiayaan + setoran simpanan) dikurangi **Kas keluar**
> (penarikan simpanan + pencairan pembiayaan). Ini merepresentasikan uang fisik yang
> seharusnya ada di tangan teller, bukan saldo akun buku besar.

> [!NOTE]
> **Tidak ada integrasi jurnal** untuk saat ini. Hitung kas cukup sebagai data audit.
> Journal adjustment dapat ditambahkan di fase berikutnya jika diperlukan.

---

## Proposed Changes

### 1. Database Migrations

#### [NEW] `create_cash_counts_table`
- [id](file:///d:/Web%20Development%20Reyhan/btm-koperasi/app/Services/AccountingService.php#181-218), [date](file:///d:/Web%20Development%20Reyhan/btm-koperasi/app/Models/User.php#92-99) (date, unique per teller), `teller_id` (FK → users), `system_balance` (decimal 15,2), `physical_total` (decimal 15,2), `difference` (decimal 15,2), `status` (enum: balanced/over/short), `note` (text, nullable), `created_at`, `updated_at`
- Unique constraint: [(date, teller_id)](file:///d:/Web%20Development%20Reyhan/btm-koperasi/resources/js/pages/Savings/Index.tsx#114-122)

#### [NEW] `create_cash_count_details_table`
- [id](file:///d:/Web%20Development%20Reyhan/btm-koperasi/app/Services/AccountingService.php#181-218), `cash_count_id` (FK → cash_counts, cascade delete), `denomination` (integer: 1000/2000/5000/10000/20000/50000/100000), `quantity` (unsigned integer), `subtotal` (decimal 15,2 = denomination × quantity)

---

### 2. Backend

#### [NEW] `app/Models/CashCount.php`
- `$fillable`: date, teller_id, system_balance, physical_total, difference, status, note
- [casts()](file:///d:/Web%20Development%20Reyhan/btm-koperasi/app/Models/Account.php#35-48): date → 'date', system_balance/physical_total/difference → 'decimal:2'
- Relationships: `teller()` → BelongsTo User, `details()` → HasMany CashCountDetail
- Scope: `scopeForTeller($query, $tellerId)`, `scopeForDate($query, $date)`

#### [NEW] `app/Models/CashCountDetail.php`
- `$fillable`: cash_count_id, denomination, quantity, subtotal
- Relationship: `cashCount()` → BelongsTo CashCount

#### [NEW] `app/Policies/CashCountPolicy.php`
- `viewAny`: Administrator, Manager (lihat semua history)
- [create](file:///d:/Web%20Development%20Reyhan/btm-koperasi/app/Models/User.php#76-83): Administrator, Manager, Teller (input kas hari ini)
- `view`: Administrator, Manager — atau — Teller yang memiliki record tersebut

#### [NEW] `app/Http/Requests/StoreCashCountRequest.php`
- `authorize`: user bisa create CashCount
- `rules`:
  - [date](file:///d:/Web%20Development%20Reyhan/btm-koperasi/app/Models/User.php#92-99): required, date, max today
  - `details`: required, array (size: 7)
  - `details.*.denomination`: required, integer, in:[1000,2000,5000,10000,20000,50000,100000]
  - `details.*.quantity`: required, integer, min:0
  - `note`: nullable, string, max:500
- Custom `withValidator`: pastikan total quantity > 0; jika ada selisih (dihitung ulang di backend), note wajib diisi

#### [NEW] `app/Services/CashCountService.php`
- `getSystemBalance(Carbon $date, int $tellerId): float`
  - Sum kas masuk: `financing_payments.total_amount` (by `payment_date` = $date)
  - Sum kas masuk: `savings_transactions.amount` WHERE `type='deposit'` AND `transaction_date` = $date
  - Sum kas keluar: `savings_transactions.amount` WHERE `type='withdrawal'` AND `transaction_date` = $date
  - *(Pencairan pembiayaan sementara diabaikan karena tidak ada teller_id di disbursement)*
  - Return: total_in - total_out
- `storeCashCount(array $data, User $teller): CashCount`
  - Gunakan `DB::transaction()`
  - Cek duplikasi (date + teller_id)
  - Hitung physical_total, difference, status
  - Simpan CashCount + CashCountDetail
  - AuditLog::log()

#### [NEW] `app/Http/Controllers/CashCountController.php`
- Inject: `CashCountService`
- [index()](file:///d:/Web%20Development%20Reyhan/btm-koperasi/app/Http/Controllers/DashboardController.php#23-123): untuk Admin/Manager → list semua dengan filter tanggal & teller
  - Pass: `cashCounts` (paginated), `filters`, `tellers` (list User dengan role Teller)
  - Render: `CashCounts/Index`
- [create()](file:///d:/Web%20Development%20Reyhan/btm-koperasi/app/Models/User.php#76-83): untuk Teller/Admin/Manager
  - Cek apakah sudah ada hitung kas hari ini milik user ini → pass `alreadySubmitted`
  - Hitung `systemBalance` dari service
  - Pass: [date](file:///d:/Web%20Development%20Reyhan/btm-koperasi/app/Models/User.php#92-99), `systemBalance`, `denominations` (array), `alreadySubmitted`
  - Render: `CashCounts/Create`
- `store(StoreCashCountRequest $request)`: simpan via service → redirect ke show
- `show(CashCount $cashCount)`: detail hitung kas + breakdown per pecahan
  - Render: `CashCounts/Show`

#### [MODIFY] [routes/web.php](file:///d:/Web%20Development%20Reyhan/btm-koperasi/routes/web.php)
Tambahkan route group baru:
```php
Route::prefix('cash-counts')
    ->name('cash-counts.')
    ->controller(CashCountController::class)
    ->group(function () {
        Route::get('/', 'index')->name('index');        // Admin/Manager
        Route::get('/create', 'create')->name('create'); // Teller/Admin/Manager
        Route::post('/', 'store')->name('store');
        Route::get('/{cashCount}', 'show')->name('show');
    });
```

---

### 3. Frontend

#### [NEW] `resources/js/pages/CashCounts/Create.tsx`
- Tabel input 7 baris pecahan (1rb–100rb):
  - Kolom: Pecahan | Input Lembar | Subtotal (auto-calc)
- Summary section (real-time update saat qty berubah):
  - Total Kas Fisik
  - Saldo Kas Sistem (read-only)
  - Selisih = fisik - sistem
  - Status badge: Balanced / Over / Short
- Field keterangan (muncul + required jika selisih ≠ 0)
- Submit button: disabled jika semua qty = 0, atau selisih ≠ 0 & note kosong
- Jika `alreadySubmitted === true`: tampilkan pesan sudah disubmit + link ke show

#### [NEW] `resources/js/pages/CashCounts/Index.tsx`
- Hanya untuk Admin/Manager
- Tabel: Tanggal | Teller | Kas Fisik | Saldo Sistem | Selisih | Status | Aksi
- Filter: date range (tanggal dari–sampai), filter by teller
- Status badge: balanced (hijau), over (kuning), short (merah)
- Link ke Show detail

#### [NEW] `resources/js/pages/CashCounts/Show.tsx`
- Detail header: Tanggal, Teller, Status badge, Waktu submit
- Tabel breakdown per pecahan: Pecahan | Qty Lembar | Subtotal
- Summary: Total Fisik, Saldo Sistem, Selisih
- Keterangan (jika ada)

#### [MODIFY] [resources/js/layouts/app-layout.tsx](file:///d:/Web%20Development%20Reyhan/btm-koperasi/resources/js/layouts/app-layout.tsx) (atau sidebar component)
- Tambah menu baru **"Kas & Teller"** dengan sub-item:
  - Hitung Kas Harian → `/cash-counts/create`
  - History Hitung Kas → `/cash-counts` (hanya tampil untuk Admin/Manager)

#### [NEW] `resources/js/routes/cash-counts.ts`
- Route helpers sesuai pola existing (`savings.ts`, dll.)

#### [NEW] `resources/js/types/cash-count.ts`
- TypeScript interfaces: `CashCount`, `CashCountDetail`, `CashCountFilters`

---

## Verification Plan

### Automated Tests (Pest)

File baru: `tests/Feature/CashCountTest.php`

Test cases yang akan ditulis:

**Authorization:**
- Teller bisa akses `GET /cash-counts/create`
- Admin & Manager bisa akses `GET /cash-counts` (index)
- Collector tidak bisa akses (403)
- Unauthenticated redirect ke login

**Store (POST /cash-counts):**
- Submit valid → `cash_counts` & `cash_count_details` tersimpan
- Submit dengan semua qty = 0 → validation error
- Submit dengan selisih ≠ 0 tanpa note → validation error
- Teller mencoba submit dua kali di hari yang sama → error duplikasi
- Status `balanced`, `over`, `short` ditentukan dengan benar

**Show:**
- Admin/Manager bisa lihat hitung kas teller lain
- Teller hanya bisa lihat miliknya sendiri

**Jalankan:**
```bash
php artisan test --filter=CashCount
```

### Manual Verification

1. Login sebagai **Teller** → buka menu "Kas & Teller" → "Hitung Kas Harian"
2. Input jumlah lembar tiap pecahan → pastikan subtotal dan total auto-update
3. Pastikan field **Keterangan** muncul dan menjadi required jika selisih ≠ 0
4. Submit → redirect ke halaman Show dengan data yang benar
5. Coba akses form lagi di hari yang sama → harus muncul pesan "sudah disubmit"
6. Login sebagai **Admin/Manager** → buka "History Hitung Kas" → pastikan list tampil dengan filter
7. Klik detail dari history → pastikan breakdown pecahan benar
