# Design: extrato com contraparte íntegra + aba Comprovante no coreadmin

Data: 2026-07-15
Status: IMPLEMENTADO (validação viva local em 2026-07-15, screenshots em `docs/reports/screenshots/2026-07-15-coreadmin-extrato-comprovante/`). Pendente: deploy HML/PROD com autorização do dono e backfill do acervo em HML/PROD via rpc.
Escopo: coreadmin (tela Contas Correntes) + write-path de extrato no Core + backfill do acervo

## Commits da implementação (branch `feat/coreadmin-extrato-comprovante`, 21 commits)

Fase 0 (diagnóstico com evidência PROD read-only): `1a02b868`.

Fase 1 (write-path do extrato, todos os trilhos): `27e3081c` (PIX recebido persiste CPF/CNPJ do pagador), `f6d556ba` (caminho TB-first idem), `e793170c` (TED enviada gera lançamento — gap estrutural do spb_handler), `355b98eb` (TEF debita com contraparte), `d08ebe3b` (recebedor de TEF vê o crédito), `89b956c5` (hardening da perna de crédito TEF), `b86a2dbe` (PIX interno grava as duas pernas), `631ee525` (envio manual propaga recebedor da cabine ao Core), `1d99056c` (débito PIX persiste recebedor na outbound_request), `e8d37a2f` (uma query por evento settled na cabine).

Fase 2 (backfill do acervo): `44763f5c` (Release.StatementBackfill dry-run/apply/reconcile), `d81dcf4f` (par reference/source das pernas internas), `d5e34f04` (reconcile inclui entries reversed), `40f3116d` (execução validada local).

Fase 3 (aba Comprovante): `2245c23e` (serializer compartilhado UseCases.Receipts.Payload), `1953b609` (endpoints por conta), `f979218b` (ordenação determinística), `821fdba1` (rótulo TEF + liquidação interna provada), `c4b1f829` (aba na tela Contas Correntes), `1aa64b11` (comprovante no desenho do ib-front com impressão).

Validação viva local (Task 16, 2026-07-15): banco isolado `mon_core_livecheck` com as 303 migrations do worktree, fixtures pelos caminhos reais (`StatementEntries.record_outbound_settled/record_inbound_settled`, `Tef.create_receiver_transaction`), core-api dev + admin vite, Playwright. Provado na tela: Extrato com Pagador E Recebedor em todas as linhas (PIX enviado, PIX recebido, TED enviada, TEF), extrato do recebedor com o crédito da TEF ("Transferência recebida - <pagador>"), aba Comprovante com os 3 débitos elegíveis e Dialog do comprovante emitido (marca, valor, Concluído, recebedor, pagador, E2E, autenticação, rodapé Lei 14.063/2020). Regressões: backend (pix/payments/nats/release/admin/v2) e vitest admin 350/350.

## Objetivo

Duas melhorias na tela Contas Correntes do coreadmin (`/dashboard/accounts/overview`):

1. O extrato deve popular Pagador e Recebedor corretamente em todos os trilhos (PIX enviado/recebido, TED enviada/recebida, TEF e PIX interno entre contas da própria instituição), investigando por que a integração com a cabine PIX não populou o caso vivo observado em PROD (PIX enviado de R$ 2.500 da conta 000000891, M2 Serviços Médicos, com Recebedor "-").
2. Nova aba "Comprovante" entre Extrato e Dados da Conta, listando os pagamentos (débitos) liquidados onde o pagador é conta Monetarie (ISPB 46026562) e permitindo emitir o comprovante no mesmo desenho e fluxo do ib-front.

## Diagnóstico (mapeado no código em 2026-07-15)

Como o extrato monta as partes: a aba Extrato (`core/apps/admin/src/views/accounts/tabs/AccountExtratoTab.vue`) chama `GET /admin/bank-accounts/:id/statement` (`core/backend/lib/monetarie_web/controllers/admin/bank_accounts_controller.ex:496`), que lê exclusivamente `account_entries`. O titular é sempre uma das partes; a contraparte vem do JSONB `metadata` (`recipient_name/document`, `payer_name/document`, `counterparty_*`, resolvido em `statement_counterparty/2`, linhas 616-631). Chave ausente no metadata = "-" na UI.

Estado por trilho:

| Trilho | Grava `account_entries`? | Contraparte |
|---|---|---|
| PIX enviado | Sim (`statement_entries.ex` `record_outbound_settled`) | Nome e documento SÓ se `recipient_name/document` veio na origem (resolução DICT). Caso vivo de PROD veio vazio: investigar na Fase 0. |
| PIX recebido | Sim (`record_inbound_settled`) | Nome sim; documento NÃO (o handler tem `debtor_cpf_cnpj` em mãos e descarta, `pix_handler.ex:702` vs `statement_entries.ex:154-163`). |
| TED recebida | Sim (`inbound_credits.ex:317` `persist_member_credit`) | Completa (nome, documento, banco). Acervo antigo por caminho legado pode estar incompleto: verificar e backfillar. |
| TED enviada | NÃO. O ramo `settled` outbound do `spb_handler.ex:141-159` só atualiza status/tarifa/webhook, nunca grava extrato. | n/a (gap estrutural) |
| TEF débito | Sim (via Máquina A, `atomic_payment_handler.ex:309`) | NÃO: `tef.ex:62-69` resolve só o id da conta destino, nunca o titular. Sai "Transferência enviada" sem nome. |
| TEF crédito | NÃO. `tef.ex:173-205` só insere em `transactions`. O recebedor não vê a entrada no extrato. | n/a |
| PIX interno (book-transfer) | NÃO em nenhuma perna. `internal_transfer.ex:192` `persist_records` só insere em `transactions`. | n/a |

Defeitos correlatos no serializer de comprovante (`v2/transaction_controller.ex`):
- `transaction_description/1` (linhas 566-579) não tem cláusula para `"tef"`: comprovante de TEF sai "Transacao" genérica.
- `pix_settlement_metadata_proven?/1` (linhas 381-398) não reconhece `settlement_source: "internal"`: o comprovante do pagador de PIX interno exibe "processing" mesmo liquidado.

## Decisões validadas com o dono

1. Corrigir write-path E backfillar o acervo (não só daqui pra frente; não enriquecer na leitura).
2. Comprovante cobre PIX + TED enviados, e o dono acrescentou TEF (transferências internas entre contas da mesma instituição).
3. TED recebida entra na verificação e no backfill mesmo com write-path atual correto.
4. Componente de comprovante do admin: cópia adaptada do `ReceiptView.vue` do banking agora; unificação dos três apps (banking, merchant, admin) fica como follow-up.

## Seção 1: investigação + correção do write-path

### Fase 0 (investigação, read-only em PROD)

Confirmar no dado vivo a causa do PIX de R$ 2.500 sem recebedor: `account_entries.metadata`, `transactions.metadata`, `outbound_requests.metadata` e o registro na cabine PIX pelo E2E. Hipóteses ranqueadas: (a) pagamento iniciado por caminho sem resolução DICT (envio manual/partner), o dado nunca entrou; (b) dado entrou no `OutboundRequest` e se perdeu na materialização para `transactions`; (c) o evento da cabine trazia `creditor_name` e o handler descartou. Amostrar também TED recebida no acervo. Somente leitura; nada de escrita em PROD nesta fase.

### Correções (todas com TDD, RED antes de GREEN)

| # | Trilho | Fix |
|---|---|---|
| 1 | PIX enviado | `record_outbound_settled` usa `creditor_name/document` do payload da cabine como fallback quando o metadata da transação vier vazio; mais o fix de origem apontado pela Fase 0. |
| 2 | PIX recebido | Persistir `debtor_cpf_cnpj` como documento do pagador no `record_inbound_settled`. |
| 3 | TED enviada | Ramo `settled` outbound do `spb_handler` grava o lançamento via `StatementEntries.record_outbound_settled` (rótulo "TED enviada" já existe; idempotente por `reference` + `metadata.source`). Toque mínimo, sem reestruturar o consumer SPB. |
| 4 | TED recebida | Sem fix de código; verificação viva + backfill. |
| 5 | TEF débito | Resolver nome/documento do titular da conta destino na origem (`tef.ex`) e propagar como contraparte. |
| 6 | TEF crédito | Criar lançamento de extrato do recebedor ("Transferência recebida", contraparte = pagador). Novo rótulo inbound. |
| 7 | PIX interno | Gravar as duas pernas em `account_entries` ("PIX enviado"/"PIX recebido", contraparte cruzada), no `settle`/`persist_records` do `internal_transfer.ex`. |
| 8 | Serializer receipt | Cláusula `"tef"` com rótulo "Transferência interna"; `settlement_source: "internal"` reconhecido como liquidado. Beneficia IB e admin. |

Princípios: extrato é perna PG de exibição, fail-soft, nunca bloqueia nem toca TB/dinheiro. Nenhuma mudança no money-path.

## Seção 2: backfill do acervo

Fontes por lacuna:

| Lacuna | Fonte | Ação |
|---|---|---|
| PIX enviado sem recebedor | Cabine PIX por E2E (`CabinStatusLookup.by_end_to_end_id`, já existe) | Completa metadata |
| PIX recebido sem documento | Cabine PIX por E2E | Completa metadata |
| TED enviada sem lançamento | `transactions` do Core (TED outbound liquidadas) | Cria lançamento |
| TED recebida incompleta | Acervo SPB local (`transactions.metadata`) | Completa metadata |
| TEF crédito e PIX interno sem lançamento | `transactions` do Core (dado 100% local) | Cria as pernas |
| TEF débito sem contraparte | Titular da conta destino (local) | Completa metadata |

Mecânica: mix task executada via ECS rpc (mesmo mecanismo das migrations), com `dry-run` que só reporta contagens por categoria e depois `apply`. Consultas à cabine em lote com throttle e fail-soft por linha (linha com falha é logada e pulada). Idempotência dupla: só preenche chaves ausentes (nunca sobrescreve valor existente) e usa a mesma chave de idempotência do write-path (`reference` + `metadata.source`); rodar duas vezes resulta em zero mudanças.

Integridade: criar lançamentos faltantes altera o saldo acumulado exibido, e isso é o conserto (hoje o extrato diverge do TB nessas contas). O backfill termina com batimento por conta tocada: soma de `account_entries` versus saldo TB, relatório das divergências restantes. Nada toca TB ou dinheiro.

Ordem: write-path deployado primeiro, backfill depois. Local, depois HML, PROD só com autorização do dono e fora de janela de operação (regra dura: nunca deployar/rodar durante operação de money-path ao vivo).

## Seção 3: aba Comprovante no coreadmin

### Backend (gate `EnsureAdmin`)

1. `GET /admin/bank-accounts/:id/receipts`: lista operações elegíveis da conta (tipos `pix`, `ted`, `tef`, direção outbound, liquidadas), filtro de período, paginação server-side; colunas: data, tipo, recebedor, valor, status.
2. `GET /admin/transactions/:id/receipt`: payload do comprovante. Extrair a serialização do `V2.TransactionController.receipt` para módulo compartilhado (ex.: `Monetarie.UseCases.Receipts.Payload`) usado pelo v2 (IB, contrato inalterado) e pelo admin. O comprovante do admin fica, por construção, idêntico ao do ib-front.

### Frontend (core/apps/admin)

- `AccountDetail.vue` ganha a aba "Comprovante" entre Extrato e Dados da Conta.
- Novo `AccountComprovantesTab.vue`: tabela das operações elegíveis com botão "Emitir comprovante" por linha.
- Emissão abre componente admin novo adaptado do `ReceiptView.vue` do banking: marca Monetarie, valor com breakdown de tarifa, dados do recebedor e do pagador, chave PIX, E2E, autenticação, rodapé legal (Lei 14.063/2020), impressão via iframe com `@media print` (operador salva PDF pela caixa de impressão).

### Validação

TDD nos controllers, vitest na aba nova, validação viva local com Playwright (screenshot da aba e do comprovante emitido), regra #11 (screenshot antes de declarar validado).

## Riscos e cuidados

- Sessões paralelas: há sessões trabalhando PIX e SPB agora. Este trabalho toca `core/backend` (inclusive `spb_handler.ex` e `pix_handler.ex` do Core) e `core/apps/admin`. Coordenar merge; commits pequenos por trilho.
- Push só com autorização explícita do dono a cada vez.
- Nenhum deploy durante operação de money-path ao vivo.
- Backfill em PROD só depois de dry-run com contagens revisadas pelo dono.

## Follow-ups registrados

- Unificar `ReceiptView` (banking, merchant, admin) num componente compartilhado em `core/packages/shared`.
- Avaliar se o status "processing" do comprovante do pagador de PIX interno afeta outros consumidores do serializer.
