# Phase 1: Backend Foundation — Core + General Ledger

Membangun seluruh fondasi backend untuk Koperasi Bermadani v2.0. Saat ini project sudah punya **11 halaman frontend (React/Inertia)** dengan mock data, tapi **belum ada backend sama sekali** — hanya ada 3 migrasi default Laravel dan 1 model `User.php`. Semua domain directory (`Accounting`, `Koperasi`, `Minimarket`, `Supplier`) masih kosong.

## Current State

| Layer | Status |
|---|---|
| Frontend (11 pages) | ✅ Done — mock data |
| Database migrations | ❌ Only default `users`, `cache`, `jobs` |
| Models / Services | ❌ Empty domain directories |
| Controllers | ❌ Only base `Controller.php` |
| Routes | ❌ All closures, no controllers |
| Auth / Middleware | ❌ Only `HandleInertiaRequests` |
| Tests | ❌ Empty |

## Proposed Changes

Pekerjaan dibagi menjadi **7 Sprint berurutan** yang bisa dikerjakan secara paralel di beberapa area. Setiap sprint menghasilkan sesuatu yang testable.

---

### Sprint 1: Database Schema & Migrations

Membuat seluruh tabel yang dibutuhkan Phase 1 sesuai FSD.md.

#### [MODIFY] [0001_01_01_000000_create_users_table.php](file:///home/tanesheva/Documents/project/bermadani/database/migrations/0001_01_01_000000_create_users_table.php)
- Tambah kolom: `member_number` (YYNNNNNN), `nik`, `phone`, `address`, `unit_kerja`, `position`, `join_date`, `status` (AKTIF/NONAKTIF/KELUAR/MENINGGAL), `role` (PENGURUS/PENGAWAS/STAF/KASIR/ANGGOTA), `monthly_wajib_amount`, `points`, `member_tier`, `is_member_koperasi`, `photo`, `documents`, `status_note`, `last_balance_sync_at`

#### [NEW] Migrations (urutan sesuai foreign key dependencies):

1. **`create_cooperative_settings_table`** — Pengaturan global koperasi (key-value pair, termasuk `fin_simwa_default`, `fin_loan_admin_fee`, dll)
2. **`create_chart_of_accounts_table`** — CoA dengan hierarki (`code`, `name`, `type`, `normal_balance`, `parent_id`, `level`, `is_system`)
3. **`create_fiscal_periods_table`** — Periode fiskal (`code` YYYYMM, `year`, `month`, `start_date`, `end_date`, `status` OPEN/CLOSED/LOCKED)
4. **`create_journals_table`** — Header jurnal (`journal_number`, `transaction_date`, `source_type/id` morph, `fiscal_period_id`, `is_posted`, `is_reversed`)
5. **`create_journal_entries_table`** — Entri debet/kredit per jurnal (`journal_id`, `account_id`, `debit`, `credit`, `member_id` subledger) + CHECK constraints
6. **`create_category_account_mappings_table`** — Mapping kategori transaksi manual → akun CoA
7. **`create_simpanan_transactions_table`** — Transaksi simpanan (`member_id`, `type` POKOK/WAJIB/SUKARELA, `transaction_type` SETOR/TARIK/TRANSFER, `amount`, `balance_before/after`, `receipt_number`, `billing_month`)
8. **`create_loans_table`** — Data pinjaman (`member_id`, `loan_source`, `base_amount`, `interest_rate`, `tenor`, `monthly_installment`, `remaining_amount`, `status`, `admin_fee`)
9. **`create_loan_payments_table`** — Pembayaran angsuran (`loan_id`, `amount`, `principal_amount`, `interest_amount`, `penalty_amount`, `receipt_number`)
10. **`create_activity_logs_table`** — Audit trail (`user_id`, `action`, `model_type/id`, `old_values`, `new_values`, `ip_address`)

> [!NOTE]
> Database saat ini SQLite (dev). Schema dirancang MySQL-compatible sesuai FSD, tapi kita buat compatible keduanya selama development.

---

### Sprint 2: Eloquent Models, Factories & Seeders

Membuat model Eloquent di bawah domain structure yang sudah disiapkan.

#### [NEW] Accounting Domain Models (`app/Domains/Accounting/Models/`):
- **`ChartOfAccount.php`** — Relasi: `parent()`, `children()`, `journalEntries()`. Scopes: `byType()`, `active()`, `system()`
- **`FiscalPeriod.php`** — Relasi: `journals()`, `closedBy()`. Methods: `isOpen()`, `isClosed()`, `isLocked()`
- **`Journal.php`** — Relasi: `entries()`, `fiscalPeriod()`, `source()` (morph), `createdBy()`. Method: `isBalanced()`
- **`JournalEntry.php`** — Relasi: `journal()`, `account()`, `member()`
- **`CategoryAccountMapping.php`** — Relasi: `account()`

#### [NEW] Koperasi Domain Models (`app/Domains/Koperasi/Models/`):
- **`Member.php`** — Extend/alias User model dengan member-specific logic. Scopes: `active()`, `byStatus()`
- **`SimpananTransaction.php`** — Relasi: `member()`, `journal()`. Events: auto-post ke GL
- **`Loan.php`** — Relasi: `member()`, `payments()`, `journal()`, `approvedBy()`
- **`LoanPayment.php`** — Relasi: `loan()`, `journal()`

#### [MODIFY] [User.php](file:///home/tanesheva/Documents/project/bermadani/app/Models/User.php)
- Tambah fillable fields, casts, relasi ke simpanan/loan/journals
- Tambah role-checking methods: `isPengurus()`, `isStaf()`, `isKasir()`, `isAnggota()`, `isPengawas()`
- Tambah encrypted attributes: `nik`, `phone`

#### [NEW] Factories:
- `UserFactory.php` (update) — States: `pengurus()`, `staf()`, `kasir()`, `anggota()`, `pengawas()`
- `ChartOfAccountFactory.php`, `FiscalPeriodFactory.php`, `JournalFactory.php`, `SimpananTransactionFactory.php`, `LoanFactory.php`, `LoanPaymentFactory.php`

#### [NEW] Seeders:
- **`ChartOfAccountSeeder.php`** — Seed 45+ akun standar koperasi sesuai FSD Section 4.1
- **`FiscalPeriodSeeder.php`** — Buat periode fiskal 2026 (Jan-Des) dengan bulan berjalan = OPEN
- **`CooperativeSettingsSeeder.php`** — Default settings (simwa Rp50k, admin fee Rp25k, dll)
- **`DemoDataSeeder.php`** — Data contoh: 5 anggota, riwayat simpanan, 2 pinjaman aktif (untuk development)

---

### Sprint 3: Core Services — Journal & GL Engine

Ini adalah **jantung arsitektur**. Semua transaksi keuangan bermuara di sini.

#### [NEW] `app/Domains/Accounting/Services/JournalService.php`
```php
- createJournal(array $data): Journal
- postJournal(Journal $journal): void
- reverseJournal(Journal $journal, string $reason): Journal
- validateBalance(Journal $journal): bool  // sum(debit) == sum(credit)
- getNextJournalNumber(string $period): string  // JU-YYYYMM-NNNNN
```

#### [NEW] `app/Domains/Accounting/Services/GeneralLedgerService.php`
```php
- getAccountBalance(int $accountId, ?string $asOfDate): float
- getTrialBalance(string $periodCode): array
- getAccountLedger(int $accountId, string $start, string $end): Collection
- getMemberSubLedger(int $memberId, int $accountId): Collection
- getMemberSavingsBalance(int $memberId, string $type): float  // POKOK/WAJIB/SUKARELA
```

#### [NEW] `app/Domains/Accounting/Services/FiscalPeriodService.php`
```php
- getCurrentPeriod(): FiscalPeriod
- getOrCreatePeriod(string $date): FiscalPeriod
- closePeriod(FiscalPeriod $period): void
- lockPeriod(FiscalPeriod $period): void
- ensurePeriodOpen(string $date): void  // throws if closed/locked
```

---

### Sprint 4: Domain Services — Simpanan & Pinjaman

#### [NEW] `app/Domains/Koperasi/Services/SimpananService.php`
```php
- deposit(Member $member, string $type, float $amount, array $meta): SimpananTransaction
  // Auto-post journal: Dr. Kas → Cr. Simpanan (Pokok/Wajib/Sukarela)
- withdraw(Member $member, float $amount): SimpananTransaction
  // Auto-post journal: Dr. Simpanan Sukarela → Cr. Kas
- transfer(Member $from, Member $to, float $amount): SimpananTransaction
  // Auto-post journal: Dr. SimSuk (sender) → Cr. SimSuk (receiver)
- getMemberBalance(Member $member, string $type): float
  // Read from GL, bukan dari kolom cache
- generateBilling(string $billingMonth): Collection
```

#### [NEW] `app/Domains/Koperasi/Services/LoanService.php`
```php
- createLoan(Member $member, array $data): Loan
- approveLoan(Loan $loan, User $approver): void
- disburseLoan(Loan $loan): void
  // Auto-post: Dr. Piutang → Cr. Kas + Dr. Kas → Cr. Pendapatan Admin
- generateInstallmentSchedule(Loan $loan): array
- recordPayment(Loan $loan, float $amount): LoanPayment
  // Auto-post: Dr. Kas → Cr. Piutang (pokok) + Cr. Pendapatan Margin (margin)
- checkOverdue(): Collection
```

---

### Sprint 5: Auth, Middleware & RBAC

#### [MODIFY] Auth configuration
- Setup Laravel authentication (login via email/phone/member_number)
- Konfigurasi Inertia shared data (auth user, flash messages)

#### [NEW] Middleware (`app/Http/Middleware/`):
- **`CheckRole.php`** — RBAC enforcement (`role:PENGURUS,STAF`)
- **`EnsureActiveMember.php`** — Block KELUAR/MENINGGAL dari mutasi
- **`LogActivity.php`** — Audit trail otomatis pada state changes
- **`EnsureAppIsInstalled.php`** — Redirect ke installer jika belum setup

#### [NEW] Auth routes & controllers:
- `LoginController.php` — Multi-identifier login (email/phone/member_number)
- `AuthenticatedSessionController.php` — Session management

---

### Sprint 6: API Controllers & Route Wiring

Menghubungkan backend ke frontend yang sudah ada.

#### [NEW] Controllers (`app/Http/Controllers/`):

**Member Portal:**
- **`DashboardController.php`** — `/dashboard`: summary simpanan, pinjaman, mutasi terakhir
- **`SimpananController.php`** — `/simpanan`: detail 3 jenis simpanan + histori
- **`PinjamanController.php`** — `/pinjaman`: status pinjaman aktif + jadwal angsuran
- **`MutasiController.php`** — `/mutasi`: riwayat transaksi all-in-one dengan filter

**Admin (Phase 1 — basic CRUD):**
- **`Admin/MemberController.php`** — CRUD anggota
- **`Admin/SimpananController.php`** — Input setoran/penarikan
- **`Admin/LoanController.php`** — Manajemen pinjaman (create, approve, disburse, payment)

#### [MODIFY] [web.php](file:///home/tanesheva/Documents/project/bermadani/routes/web.php)
- Ganti semua route closure → controller methods
- Tambah route groups: `auth`, `admin` (role:PENGURUS,STAF), `member`
- Tambah auth routes (login/logout)

---

### Sprint 7: Tests & Verification

#### [NEW] Feature Tests:
- **`JournalServiceTest.php`** — Balance validation, number generation, posting, reversal
- **`SimpananServiceTest.php`** — Deposit → journal created, withdraw → journal created, balance from GL
- **`LoanServiceTest.php`** — Disburse → journal, payment → journal, overdue check
- **`FiscalPeriodTest.php`** — Close/lock period, reject transactions on locked period
- **`AuthenticationTest.php`** — Login multi-identifier, RBAC enforcement
- **`DashboardControllerTest.php`** — Authenticated member sees own data

#### [NEW] Unit Tests:
- **`JournalBalanceTest.php`** — sum(debit) must equal sum(credit), always
- **`LoanCalculationTest.php`** — Flat margin formula accuracy
- **`MemberNumberGeneratorTest.php`** — YYNNNNNN format

---

## Open Questions

> [!IMPORTANT]
> **Q1: Database Engine untuk Development**
> Saat ini pakai SQLite. FSD menargetkan MySQL 8.0+. Mau langsung switch ke MySQL sekarang, atau tetap SQLite dulu untuk development speed? (CHECK constraints di SQLite limited)

> [!IMPORTANT]
> **Q2: Auth Package**
> Mau pakai auth bawaan Laravel (manual) atau pakai package seperti Laravel Breeze/Fortify untuk scaffolding login? Mengingat login-nya multi-identifier (email/phone/member_number), kemungkinan manual lebih fleksibel.

> [!IMPORTANT]
> **Q3: Admin Panel**
> Untuk admin/staf, apakah kita bikin custom admin panel (React/Inertia juga) atau pakai Filament? Ini akan menentukan approach Sprint 6.

> [!IMPORTANT]
> **Q4: Execution Order**
> Mau jalankan semua 7 sprint sekaligus (saya build paralel pakai subagents), atau satu-satu supaya bisa di-review per sprint?

---

## Verification Plan

### Automated Tests
```bash
php artisan test --compact
vendor/bin/pint --dirty --format agent
```

### Manual Verification
- `php artisan migrate` — semua tabel terbuat tanpa error
- `php artisan db:seed` — data demo terisi
- Buka `/dashboard` — data real dari DB muncul (bukan mock)
- Buka `/simpanan` — saldo dihitung dari GL
- Login sebagai STAF → bisa akses admin
- Login sebagai ANGGOTA → hanya bisa lihat data sendiri
- Trial balance dari seeder harus balance (debit = credit)

---

## Estimated Scope

| Sprint | Files | Effort |
|---|---|---|
| 1. Migrations | ~12 files | Medium |
| 2. Models & Seeders | ~20 files | Medium |
| 3. Accounting Services | ~3 files | High (core logic) |
| 4. Domain Services | ~2 files | High (business rules) |
| 5. Auth & Middleware | ~6 files | Medium |
| 6. Controllers & Routes | ~8 files | Medium |
| 7. Tests | ~8 files | Medium |
| **Total** | **~59 files** | **~3-4 working sessions** |
