# Design — Dados do cliente na busca + conta de desembolso selecionável

> Data: **2026-07-14**. Branch: `feat/loan-product-required-cosif-accounts`.
> Status: **aprovado no brainstorming**. Área: crédito (`SimulationDialog` + `/cooperative/members/:id`
> serializer + `MemberAccount` + `disburse_loan`).

## Problema

Ao buscar/selecionar o cliente na criação de proposta, o card mostra pouco (nome, CPF, faixa de renda).
O dono quer ver: **nº da conta corrente, saldo, situação da conta e o último nível de risco do cliente** —
e, quando o cliente tem mais de uma conta corrente ativa, **selecionar** qual recebe o desembolso.

## Achados (exploração)
- **Itens 1-3 já vêm na resposta** de `/cooperative/members/:id` (`serialize_member_full`): `accounts[]`
  com `account_number`, `agency`, `kind`, `status`, `balance_cents` (via `batch_account_balances`). O front
  só não exibe. (Mas o `accounts` traz TODAS as contas, sem filtro de elegibilidade.)
- **Risco:** `risk_classification_result` (por `customer_id`, que **= member.id**, confirmado via rpc — 19
  linhas locais). Campos: `customer_rating` (ex.: "A"), `reference_date`, `risk_level_id`, `portfolio`.
- **Conta de desembolso hoje:** `MemberAccount.active_account_query/1` resolve **1** conta elegível
  (`kind ∈ [1,3]`, `status="active"`, exclui `account_type "capital_shares"`; ordem `asc kind, asc id`,
  `limit 1`) e o `disburse_loan` credita nela (fail-soft: pula se nil). Um cliente pode ter até ~18 contas
  (maioria inativas/legado).

## Decisões (dono)
- **Risco:** mostrar **rating + data** (`customer_rating` + `reference_date` da última classificação).
- **Conta:** mostrar **todas as contas correntes ATIVAS** (elegíveis); se >1, o operador **seleciona** e a
  escolha é **funcional** — é a conta do **desembolso**.

## Comportamento

### 1. Exibir (card de cliente encontrado)
- Contas correntes ativas elegíveis (mesmo critério do `MemberAccount`): **número · situação · saldo**.
- **Risco:** `rating` + `data` (ou "Sem classificação" se não houver).

### 2. Selecionar a conta de desembolso
- 1 conta → auto-selecionada. **>1 → radio** de seleção; default = a que o auto-pick escolheria
  (`asc kind, asc id`) → sem mexer = comportamento atual.
- Vai no metadata da simulação: **`disbursement_account_id`** (mesmo padrão de `periodicity`/`business_day_adjust`).

### 3. Desembolso respeita a seleção (backend, money path)
- No `disburse_loan`, se `loan.metadata["disbursement_account_id"]` presente **E** for conta elegível do
  member (validada pelos mesmos critérios) → credita **nela**; senão → auto-pick atual (fallback idêntico ao
  de hoje). **Sem seleção = zero mudança de comportamento.**

## Backend
- `MemberAccount`:
  - `active_accounts_query/1` — como `active_account_query/1` mas **sem `limit`** (todas elegíveis). Fonte
    única pra listar (card) e validar a seleção.
  - Predicado/consulta de validação da conta escolhida (`account_id` pertence às elegíveis do member).
- Serializer `serialize_member_full`: adicionar
  - `checking_accounts`: as elegíveis (número/agência/situação/saldo) — filtradas pelo critério do
    `MemberAccount` (não replicar a regra no front);
  - `last_risk`: `%{rating, reference_date}` da última `risk_classification_result` por `customer_id ==
    member.id` (nil se não houver).
- `disburse_loan` (`account_entry` Multi): resolver a conta assim — `metadata["disbursement_account_id"]`
  validado > auto-pick (`active_account_query`) > fail-soft skip.

## Frontend (`SimulationDialog.vue`)
- No card de cliente encontrado: bloco de **contas correntes ativas** (número · situação · saldo),
  selecionáveis via `RadioButton` quando >1 (1 → fixa). Estado `selectedAccountId` (default = 1ª da lista,
  que já vem ordenada como o auto-pick).
- **Risco:** linha "Risco: {rating} — {data}" (ou "Sem classificação").
- Submit inclui `disbursement_account_id: selectedAccountId` no `metadata`.
- Reset em `clearMember`/nova busca.

## Testes
- **Backend:** `active_accounts_query` retorna todas as elegíveis (exclui inativa/cotas-parte); `disburse_loan`
  credita a conta selecionada quando válida; **fallback** pro auto-pick quando ausente OU inválida (conta de
  outro member / inativa); serializer traz `checking_accounts` + `last_risk`.
- **Front:** type-check.

## Fora de escopo (YAGNI)
- Reclassificar risco / recalcular saldo (só exibe o existente).
- Contas não-ativas (mostra só as ativas, como pedido).
- Alterar a `active_account_query` de `limit 1` (o auto-pick/fallback continua igual).

## Arquivos afetados (previsão)
| Arquivo | Mudança |
|---|---|
| `member_account.ex` | `active_accounts_query/1` (sem limit) + validação da conta escolhida |
| `members_controller.ex` | `serialize_member_full`: `checking_accounts` + `last_risk` |
| `loans.ex` | `disburse_loan`: honrar `metadata["disbursement_account_id"]` (valida → fallback auto-pick) |
| `SimulationDialog.vue` | card com contas ativas (radio se >1) + risco; `disbursement_account_id` no submit |
| testes (`member_account`/`loan_lifecycle`/`members`) | elegíveis, desembolso honra/fallback, serializer |
