# WP3 — Expose per-document approval state on the API (BE)

## Goal
The FE needs to know, for any sales/purchase document, its approval state so it can show the right buttons (Submit / Pending / Approve / Reject / Post) uniformly — without the FE juggling raw ApprovalLog ids. Expose a compact `approval` block on the document API resources, and add a per-document approve/reject that drives the engine (so the FE acts on the document, not a log id).

## Exact files (BE)
- Resources: `Modules/Sales/app/Http/Resources/SalesInvoiceResource.php` (+ Order/Quotation/Return resources), `Modules/Purchases/app/Http/Resources/*` (Bill/Order/Request/Return). Add an `approval` block (see contract).
- A small read helper on the existing trait `Modules/Core/app/Support/DrivesApprovalWorkflow.php` OR the `ApprovalWorkflowService` — reuse, don't duplicate: expose `approvalStateFor(Model $doc, ApprovalDocumentType $type, ?User $user): array` returning the block below. (Reads `approval_logs` — the WP1 index covers it.)
- Per-document approve/reject: EITHER reuse the controllers' existing `approve()`/`reject()` (WP1/WP2 already route approve through the engine) — confirm they return the updated approval state — OR add thin `POST /{module}/{doc}/{id}/approve|reject` if missing. Prefer reusing what WP1/WP2 built; just ensure the response includes the new `approval` block.

## Contract — the `approval` block on a document resource
```json
"approval": {
  "required": true,            // an active workflow gates this doc type
  "state": "pending|approved|none",  // none = auto path / no workflow
  "current_level": 2,          // the level awaiting action (null if none/approved)
  "total_levels": 3,
  "can_act": true,             // the CURRENT user is the approver for current_level (canUserApprove)
  "my_pending_log_id": 123,    // the log this user can act on now (null otherwise)
  "history": [ {level, approver_name, status, acted_at} ]   // optional, for a drawer
}
```
- `state: "none"` whenever the doc has zero engine logs (auto path) → FE shows today's behavior (no approval UI).
- `can_act` uses the engine's `canUserApprove` (assigned user OR role + it's the lowest pending level).
- Keep it cheap: one query per doc for the logs; do not N+1 across a list (guard: only compute the full block on the single-doc `show`, and a lightweight `{required, state}` on list rows if easy — or compute lazily).

## Interfaces (for FE WP5/WP6)
- Document `show` response carries `approval` (full block). List rows carry at least `approval.state` + `approval.required` (so the list can badge "pending approval").
- Approve/reject a document: the FE calls the document's own approve/reject (already wired in WP1/WP2) and re-reads `approval`. The generic `/core/approval-logs/{id}/approve` still exists for the inbox (WP6) but document screens use the doc endpoint.
- WP6 inbox uses the existing `GET /core/approval-logs` (pending-for-user) — confirm it returns document_type + document_id + amount + a link-able reference so the inbox can deep-link.

## Acceptance criteria
- A gated invoice/bill's `show` returns `approval.state="pending"`, correct `current_level`/`total_levels`, and `can_act=true` ONLY for the assigned approver of the current level.
- An auto-path (no workflow) doc returns `approval.state="none"` and the FE can treat it exactly as today.
- No N+1 regression on list endpoints (verify the list query count didn't explode).
- Approve via the document endpoint advances the engine and the re-read `approval` reflects the new level / approved.

## Tests (Pest, sqlite, file-by-file)
Extend the wiring tests (or add `ApprovalStateApiTest`): assert the `approval` block shape for none/pending/approved, `can_act` true only for the right approver, and that approving via the doc endpoint moves `current_level`. Baseline: WP1/WP2 suites green. Zero new failures.

## Flags
- Not schema-changing (reads only). Not [FIN] itself (no posting logic change — WP1/WP2 own that), but touches shared resources → Codex review.

## Out of scope
- FE (WP4-6). Changing the engine or the WP1/WP2 gating. Production/other modules.
- Do NOT commit-to-main / deploy.
