# Design — Saldo de aplicacao em aberto no card do cliente

> Data: **2026-07-14**. Branch: `feat/loan-product-required-cosif-accounts`.
> Status: **aprovado no brainstorming**. Area: credito/investimentos (`SimulationDialog` card +
> `/cooperative/members/:id` serializer + `Investments` context). Extensao do card (contas/saldo/risco/saldo
> em aberto de emprestimos).

## Problema

O card do cliente na criacao de emprestimo mostra saldo em conta, risco e saldo em aberto de emprestimos,
mas nao o **saldo de aplicacao em aberto** (quanto o cliente tem investido em aplicacoes ativas).

## Achados (exploracao)
- Ha **dois** modelos: (1) **`Investments`** (produtos do cooperado — CDB/RDC/poupanca), com **`member_id`**
  FK direto, `current_balance` e status `[:active, :matured, :redeemed, :rolled_over]`; ja tem
  `Investments.member_portfolio/2` cujo `total_balance` = `sum(current_balance)` das `:active`. (2) RF desk
  (`rf_positions`/MtM) — mesa de tesouraria, mark-to-market. **"Aplicacao do cliente" = o modelo `Investments`.**
- **Unidade:** `Investment.current_balance` esta em **CENTAVOS** (front `InvestmentProductsView` formata
  `v/100`; var `memberBalanceCents` no `InvestmentPositionsView`) → mesmo padrao do saldo em conta.
- **Dados locais:** tabela `investments` VAZIA (0 registros) → na tela local aparece **R$ 0,00** pra todos;
  a soma real so aparece no dev/prod com aplicacoes.

## Decisao (dono)
- **"Aplicacao"** = modelo `Investments` (nao a mesa RF).
- **"Em aberto"** = **`status == :active`** (igual ao `member_portfolio.total_balance`; exclui matured/redeemed/rolled_over).

## Backend
- `Investments.member_open_balance/1` — `sum(current_balance)` (centavos) WHERE `member_id == ^id AND
  status == :active`; retorna 0 se nil. Guard `nil` explicito (Ecto rejeita `== ^nil`). Single-tenant (sem
  filtro de institution, como o default nil de `member_portfolio`).
- `members_controller.show`: busca `Investments.member_open_balance(id)`; `serialize_member_full` adiciona
  **`open_investments_balance`** (centavos).

## Frontend (`SimulationDialog.vue`)
- No card, linha **"Saldo de aplicacao (em aberto): R$ X"** (logo abaixo do saldo em aberto de emprestimos),
  via `formatBRLLocal(foundMember.open_investments_balance ?? 0)`.

## Testes
- **Backend:** `member_open_balance` soma so `:active` (exclui matured/redeemed/rolled_over); 0 se sem
  aplicacao. Validacao viva via rpc (aqui = 0, tabela vazia; logica confirmada pela query).
- **Front:** type-check.

## Fora de escopo (YAGNI)
- Mesa RF (`rf_positions`/MtM) — nao e a aplicacao do cliente.
- Detalhar aplicacao a aplicacao (so o total agregado).
- Incluir matured/redeemed/rolled_over (so :active, por decisao do dono).

## Arquivos afetados
| Arquivo | Mudanca |
|---|---|
| `investments.ex` | `member_open_balance/1` (soma current_balance das :active) |
| `members_controller.ex` | `show` + `serialize_member_full`: `open_investments_balance` |
| `SimulationDialog.vue` | linha "Saldo de aplicacao (em aberto)" no card |
