# De→para corrigido do legado SPB (AB_MGI) — por operação, não por mensagem

Data: 2026-07-08. Status: **desenho para aprovação. Nada gravado em HML/PRD.** O SPB legado foi
removido por completo de PRD e HML (rollback cirúrgico por marcador; operações nativas intactas —
8 em produção, 38 em HML). Este documento substitui a seção SPB do de→para anterior, que estava errada.

## 1. O que estava errado antes

A carga anterior tratava **cada mensagem como uma operação** e usava a direção crua de cada leg. Resultado:
uma STR0008R1 (recibo de uma TED que **nós enviamos**) aparecia como operação "recebida"; a STR0008 enviada
ficava sem os detalhes consolidados; e a STR0008R2 (que é **TED recebida**, com nós na posição de credor)
era tratada como uma terceira leg da saída. Somado a isso, o status vinha genérico ("Cancelada") e o
`created_at` era a data do import (hoje), não a data original do BACEN.

## 2. Estrutura real do backup AB_MGI

- `CORPO_TRANS_INTERF_PASS`: XML de cada leg, por `NUMORIGEM` (`SEQXML=0` sempre; XML não é fatiado).
- `TRANS_INTERF_PASS`: 1 linha por leg, com as colunas de ligação. Chaves:
  - `NUMORIGEM` — identificador do leg. No leg-base tem formato `STR<AAAAMMDD><seq>` (ex. `STR20251203000074888`).
  - `NUMORIGEMOR` — nos legs de **resposta recebida** aponta para o `NUMORIGEM` do leg-base (é o que amarra a operação).
  - `NUMCTRLSPB` — número de controle atribuído pelo BACEN (= NumCtrlSTR).
  - `CODSISTEMA_O` — sistema de origem: `EB`/`GI`/`SGR` = **nós originamos**; `SPB` = **recebido** do clearing.
  - `STATUSSTR`, `SITUACAO`, `DTHRINC` (carimbo real do leg).
- `TRANS_SISTEMAS` (130 linhas): lançamentos internos, subconjunto — **não** é a tabela de operação.

**Chave de agrupamento da operação:** `COALESCE(NULLIF(RTRIM(NUMORIGEMOR),''), RTRIM(NUMORIGEM))`.
Todos os legs com a mesma chave = uma operação. 10.662 legs → ~7.016 operações.

## 3. Tabela-verdade por tipo de mensagem (contagens empíricas do backup)

Direção provada pela posição no XML (nosso ISPB `46026562` como `ISPBIFDebtd` vs `ISPBIFCredtd`).

| Tipo | Origem | Qtde | Nós devedor | Nós credor | VlrLanc | SitLancSTR | Papel |
|------|--------|------|-------------|------------|---------|------------|-------|
| STR0008 | EB | 2740 | 2740 | 0 | sim | – | **Saída** (base) |
| STR0008R1 | SPB | 2722 | 2722 | 0 | não | sim | Recibo da nossa saída → **dobra na saída** |
| STR0008R2 | SPB | 2707 | 0 | 2707 | sim | – | **TED recebida (entrada)** — operação própria |
| STR0008E | SPB | 8 | 8 | 0 | não | – | Entrada/erro da nossa saída (dobra) |
| STR0008C | SGR | 15 | 15 | 0 | sim | – | Cancelamento da saída (dobra) |
| STR0006R2 | SPB | 281 | 0 | 281 | sim | – | **Entrada recebida** |
| STR0007R2 | SPB | 40 | 0 | 40 | sim | – | **Entrada recebida** |
| STR0010 | EB/SGR | 95 | 95 | 0 | sim | – | **Saída** (base) |
| STR0010R1 | SPB | 92 | 92 | 0 | não | sim | Recibo → **dobra na saída** |
| STR0010R2 | SPB | 138 | 0 | 138 | sim | – | **Entrada recebida** |
| STR0010E | SPB | 1 | 1 | 0 | sim | – | Entrada/erro (dobra) |
| STR0004 | SGR | 13 | 13 | 0 | sim | – | **Saída** |
| STR0025 | EB/SGR | 11 | 11 | 0 | sim | – | **Saída** |
| STR0013 | SGR | 1 | 0 | 0 | não | – | Administrativa (saída) |
| LPI0001..0005 | GI/SGR | 810 | – | – | sim | – | **Saída** (mov. Conta PI; ISPBIF=ISPBPSPI=nós) |
| LPI000xR1 | SPB | 670 | – | – | não | sim | Recibo → **dobra** |
| LPI0002/0003/0004 C/E | SGR/SPB | ~9 | – | – | – | – | Consulta/erro (dobra) |
| LPI0006 | SPB | 280 | – | – | 0 | sim | Espelho Conta PI recebido → **entrada** |
| GEN0001/0019/0020 | SGR | 14 | – | – | não | – | Administrativa (saída) |
| SME0003 | SGR | 15 | – | – | não | – | Administrativa (saída) |

Regra derivada:
- **Saída (`direction=outbound`)**: leg-base com `CODSISTEMA_O ∈ {EB,GI,SGR}`. STR de transferência: nós devedor.
  R1/E/C correspondentes (mesma chave de operação) são **dobrados**, não viram operação.
- **Entrada (`direction=inbound`)**: leg com `CODSISTEMA_O=SPB` em que somos credor (STR *R2*) ou notificação
  recebida (LPI0006). É operação própria; `NUMORIGEMOR` aponta para o controle do **remetente** (não casa com envio nosso).

## 4. Status (SitLancSTR → estado da cabine)

Valores presentes no backup: **1 (Efetivado) = 3747, 5 (Rejeitado sem saldo) = 10, 17 (Pendente) = 5**.

| SitLancSTR | Estado cabine | status_code |
|------------|---------------|-------------|
| 1 (Efetivado) | `settled` / confirmado | liquidado |
| 5 (Rejeitado s/ saldo) | `rejected` | rejeitado |
| 17 (Pendente) | `pending` | pendente |
| TED recebida (R2, sem SitLancSTR, crédito ocorreu) | `r2_confirmed` / settled | 10 |

Sem uso de "Cancelada" para sucesso.

## 5. Datas

`created_at` (eixo de ordenação/exibição da cabine) = `TRANS_INTERF_PASS.DTHRINC` do leg correspondente
(intervalo real 02/12/2025 → 01/07/2026). Timestamps de ciclo: `sent_at`/`received_at` = DTHRINC do leg;
`r1_received_at` = `DtHrSit` do R1. `inserted_at/updated_at` podem ser `now()` (auditoria do import),
mas **nunca** `created_at`.

## 6. De→para para o modelo da cabine (`mon_spb`)

### 6.1 `spb_operations` (1 por operação)
| Coluna alvo | Origem |
|-------------|--------|
| `direction` | regra §3 (outbound/inbound) |
| `message_type` | `CodMsg` do leg-base (STR0008, STR0008R2, STR0006R2, STR0010, LPI0001, …) |
| `amount` | `VlrLanc` do leg-base, em reais (sem /100) |
| `debtor_ispb`/`debtor_*` | `ISPBIFDebtd`/`AgDebtd`/`CtDebtd`/`CNPJ_CPFCliDebtd`/`NomCliDebtd` |
| `creditor_ispb`/`creditor_*` | `ISPBIFCredtd`/`AgCredtd`/`CtCredtd`/`CNPJ_CPFCliCredtd`/`NomCliCredtd` |
| `control_number` | `NumCtrlIF` do R1 (o base costuma vir vazio) / do leg de entrada |
| `control_number_clearing` | `NumCtrlSTR` (= `NUMCTRLSPB`) do R1/R2 |
| `state`/`status_code` | mapa SitLancSTR §4 (entrada recebida → r2_confirmed / 10) |
| `created_at` | `DTHRINC` do leg-base (LEGADO) |
| origem/marcador | `source='legacy_import'` + marcador para rollback |

### 6.2 `bacen_messages` (1 primária por operação, R1 dobrado)
| Coluna alvo | Origem |
|-------------|--------|
| `operation_id` | FK da operação |
| `message_type` | `CodMsg` do base |
| `direction` | direção física do primário (saída=outbound; entrada=inbound) |
| `content`/`xml` | XML do leg-base |
| `response_xml` | XML do R1 (dobrado), se houver |
| `sent_at`/`received_at` | `DTHRINC` (saída: sent; entrada: received) |
| `r1_received_at` | `DtHrSit`/`DTHRINC` do R1 |
| `status_code`, control numbers | como §6.1 |
| `created_at` | `DTHRINC` do base (LEGADO) |
| `seq` | 1 |

### 6.3 `operation_events` (trilha de auditoria)
Uma linha por transição, `created_at` = `DTHRINC` do leg correspondente (LEGADO):
`message_sent` (base saída) / `credit_received` (base entrada) → `r1_received` (SitLancSTR) →
`settled` | `rejected` | `pending`.

Sem gravar cópias ocultas de R1/R2 como linhas de negócio (o folding cobre a exibição correta).

## 7. Validação em HML antes de qualquer PRD (gate de aprovação)

1. Reescrever `21_project_spb.sql` + `00_deltas_spb.sql` + staging conforme §3–§6.
2. Rodar em HML (task Fargate one-off, mesmo caminho do PIX). Conferir empiricamente:
   - contagem de operações por direção e por tipo bate com a tabela-verdade §3;
   - nenhuma STR0008R1 como operação isolada; STR0008R2 como entrada com nós credor;
   - `created_at` = datas legadas (min/max 12/2025–07/2026), zero linha com data de hoje;
   - status distribuído em settled/rejected/pending conforme §4 (zero "Cancelada");
   - soma de `amount` de entradas e saídas reconciliada; amostras abrindo a tela.
3. Apresentar as evidências ao dono. **Só após o "ok" explícito**, reimportar em PRD no mesmo formato.
4. Tudo marcado como legado → rollback cirúrgico possível; **nada tocado no que está ativo hoje**.
