# `ChargeController` plan

## Scope
- **Controller**: `app/Http/Controllers/Api/Finance/ChargeController.php`
- **Purpose**: list charges for a parent and manage extra/event charges (create/cancel/apply/mark paid).
- **Primary dependency**: `App\Services\Billing\ChargeService`
- **Resources**:
  - `ChargeListResource` (for list responses)
  - `ChargeActionResource` (for mutation responses)

## Routes
All routes are under:
- Base prefix: `/api/v1/finance/fees/charges`

Endpoints:
- `GET /api/v1/finance/fees/charges/` → `index`
- `POST /api/v1/finance/fees/charges/extra` → `storeExtra`
- `POST /api/v1/finance/fees/charges/event` → `storeEvent`
- `POST /api/v1/finance/fees/charges/extra/{id}/cancel` → `cancelExtra`
- `POST /api/v1/finance/fees/charges/extra/{id}/apply` → `applyExtra`
- `POST /api/v1/finance/fees/charges/event/{id}/cancel` → `cancelEvent`
- `POST /api/v1/finance/fees/charges/event/{id}/mark-paid` → `markEventPaid`

## Authentication & authorization
- `storeExtra` / `storeEvent` require an authenticated user id (checked by `authenticatedUserIdOrUnauthorized()`).
- Other endpoints currently do not call the helper; authorization is expected to be enforced by surrounding route middleware and/or service-layer checks.

## Requests/validation
- `index`: `ChargeListRequest` (`parent_id` required; optional `school_year`)
- `storeExtra`: `StoreExtraChargeRequest` (+ adds `created_by` from authenticated user)
- `storeEvent`: `StoreEventChargeRequest` (+ adds `created_by`)
- `applyExtra`: `ApplyExtraChargeRequest` (`invoice_id`)
- `markEventPaid`: `MarkEventChargePaidRequest` (`paid`, optional `payment_id`)
- `cancelEvent`: uses raw `Request` with query boolean `issue_credit` (default true)

## Response shapes
Legacy `{ ok: ... }` shape:
- `index`:
  - `{ ok:true, charges: ChargeListResource }`
- Mutations:
  - `{ ok:(bool), charge: ChargeActionResource }`
  - HTTP status:
    - Create success: `201`
    - Duplicate charge success-case: `409` (when `error === "duplicate_charge"`)
    - Validation/business failure: `422`
    - Cancel/apply/mark-paid: `200` when ok, else `422`

## Side effects
- `storeExtra` / `storeEvent` attach `created_by` for auditing.
- `cancelEvent` optionally issues credits (controlled via `issue_credit` query flag).
- `markEventPaid` can link to a `payment_id` when supplied.

## Error handling expectations
- `storeExtra` / `storeEvent` catch unexpected exceptions:
  - return `500` `{ ok:false, message:"Unable to create ... charge." }`
- Unauthorized (store endpoints) returns:
  - `401` `{ ok:false, message:"Unauthorized." }`

## Test plan (feature)
- Listing:
  - Returns `ChargeListResource` payload for a parent with charges.
- Create extra/event:
  - Unauthenticated → 401.
  - Valid payload → 201 and ok true.
  - Duplicate charge → 409.
  - Service exception → 500.
- Apply extra:
  - Valid invoice id applies charge → 200.
  - Invalid invoice/charge → 422.
- Cancel:
  - Extra cancel → 200/422 depending on service result.
  - Event cancel with `issue_credit=false` behaves accordingly.
- Mark paid:
  - Toggle paid true/false; supports payment_id link.

