# Dossiê de paridade profundo — LPI0001-0006 (Conta PI / SPB)

Auditoria READ-ONLY. Zero inferência: cada afirmação traz prova (arquivo:linha, tabela.coluna
ou XSD do catálogo oficial BACEN v5.12). Onde não houve prova, o veredito é INCONCLUSIVO.

## 0. Escopo e semântica oficial (catálogo BACEN v5.12)

Fonte: `/Users/luizpenha/cecresa/md/XSDDOCV512/LPI/*.XSD` (cat:InfEvento).

| Código | Evento (cat:Evento) | Emissor→Destinatário | Efeito na Conta PI |
|--------|---------------------|----------------------|--------------------|
| LPI0001 | IF requisita transferência de conta RB ou CL para depósito em Conta PI | IF → STR | Crédito (aporte, direto/liquidante) |
| LPI0002 | IF requisita transferência de conta CCME para depósito em Conta PI | IEME → STR | Crédito (aporte, moeda eletrônica / SME) |
| LPI0003 | PSPI requisita transferência para saque em Conta PI e depósito em conta RB ou CL | PSPI → STR | Débito (saque, direto/liquidante) |
| LPI0004 | PSPI requisita transferência para saque em Conta PI e depósito em CCME | PSPI → STR | Débito (saque, SME) |
| LPI0005 | PSPI informa configuração de transferência automática de valores para depósito em Conta PI | PSPI → STR | Config (percentuais RB/CL/CCME p/ auto-aporte) |
| LPI0006 | STR informa transferência automática de valores para depósito em Conta PI | STR → PSPI | Notificação inbound (espelho do aporte automático) |

Campos por XSD (confirmados via `iconv ISO-8859-1` + parse dos ComplexType):
- LPI0001: CodMsg, NumCtrlIF, ISPBIF, ISPBPSPI, VlrLanc, DtMovto
- LPI0002: CodMsg, NumCtrlIEME, ISPBIEME, VlrLanc, DtMovto
- LPI0003: CodMsg, NumCtrlPSPI, ISPBPSPI, ISPBIFCredtd, **Grupo_LPI0003_CliCredtd** (AgCredtd?, CtCredtd, CtPgtoCredtd, CNPJCliCredtd), FinlddLPI, **NumCtrlSTROr?**, VlrLanc, DtMovto
- LPI0004: CodMsg, NumCtrlPSPI, ISPBPSPI, VlrLanc, DtMovto
- LPI0006 (inbound): CodMsg, ISPBPSPI, NumCtrlSTR, TpCtBC, PercSldRB_CL?, VlrRB_CL?, PercSldCCME?, VlrCCME?, VlrLanc?, SitLancSTR, DtHrSit, DtMovto

Observação 3 do catálogo LPI0006: "Quando não houver saldo suficiente na conta determinada, a
transferência não será efetuada e a mensagem será enviada com Situação de Lançamento STR
'Rejeitado sem Saldo'".

---

## 1. LEGADO (SPI.Core / .NET) — comportamento com prova

### 1.1 Onde vive o LPI no legado PIX
O núcleo PIX (`SPI.Core.SPB.Application`) constrói **apenas LPI0001-0004** e delega a transmissão
ao LegadoSPB (Evolution/Topaz ou "JD") por integração HTTP. **NÃO** assina nem envia ao BACEN, e
**NÃO** constrói LPI0005 nem processa LPI0006** (essas ficam no LegadoSPB, fora do decompilado do PIX;
grep por `LPI0005`/`LPI0006` nos `.decompiled.cs` = zero ocorrências).

### 1.2 Derivação do código por Crédito/Débito × Própria/Liquidante/SME
`MontaMsg.ConverteXML` — `SPI.Core.SPB.Application.decompiled.cs:49-64`:
```
Crédito (enumMsgSaldo.SolicitacaoCredito) + (Propria|Liquidante)  -> LPI0001 (NumCtrlIF, ISPBIF, ISPBPSPI, VlrLanc, DtMovto)
Crédito + SME                                                     -> LPI0002 (NumCtrlIEME, ISPBIEME, VlrLanc, DtMovto)
Débito (SolicitacaoDebito) + (Propria|Liquidante)                -> LPI0003 (NumCtrlPSPI, ISPBPSPI, ISPBIFCredtd, FinlddLPI, VlrLanc, DtMovto)
Débito + SME                                                     -> LPI0004 (NumCtrlPSPI, ISPBPSPI, VlrLanc, DtMovto)
```
Enums: `enumMsgSaldo{SolicitacaoCredito=1,SolicitacaoDebito=2,Aporte=3,MetaSaldo=4}` e
`enumTipoMsgSaldo{Propria=1,Liquidante=2,SME=3}` — `SPI.Core.General.decompiled.cs:7649-7668`.
**Própria e Liquidante caem no MESMO código** (0001 no crédito, 0003 no débito); só SME muda para
0002/0004. Nota: o construtor cobre só Crédito/Débito — `Aporte`/`MetaSaldo` produziriam XML vazio
(gap latente do próprio legado, não exercido).

FinlddLPI (finalidade, só no débito LPI0003): `enumFinalidadeLPI{MovimentacaoPropria=1
("Movimentação própria ou para conta em Liquidante no STR"), DevolucaoAporte=2 ("Devolução de aporte
recebido indevidamente")}` — `SPI.Core.General.decompiled.cs:7786-7792`.

Legacy LPI0003 **NÃO** emite o grupo `Grupo_LPI0003_CliCredtd` nem `NumCtrlSTROr` (opcionais do XSD)
— `SPI.Core.SPB.Application.decompiled.cs:59`.

### 1.3 Origem: tela "Solicitação de Saldo" (operador PIX)
- UI Angular `incluir_req_saldo.component.html:20-22` (Própria/Liquidante/SME) + `:38-39` (finalidade
  1 "Movimentação para Liquidante no STR" / 2 "Devolução de aporte recebido indevidamente").
- `incluir_req_saldo.component.ts:36-70` (form: dtMovto, cboTipo→IdTipoRequisicao, txtValor→Vlr,
  cboFinalidade→FinlddLPI; tpMsg = crédito(1)/débito(2)).
- `solicitacao_saldo.service.ts:17-38` POST `/api/ReqSaldo/add`.

### 1.4 Validações de negócio na origem
`RequisicaoSaldoUseCase.InsereReqSaldo` — `SPI.Core.Angular.Application.decompiled.cs:1987-2037`:
1. `Vlr < 0` → "Valor deve ser > 0" (linha 1992; nota: aceita 0)
2. IdInstFinanc existe (1997)
3. IspbIF informado deve bater com o ISPB da IF; senão preenche do cadastro (2001-2008)
4. IdFilialInst existe (2009)
5. Débito sem FinlddLPI → "É preciso informar a finalidade quando a mensagem é uma solicitação de
   débito" (2013)
6. IspbVinculado obrigatório e numérico (2017)
7. `PodeIncluirNovaRequisicao` — bloqueia nova requisição se existir qualquer requisição do mesmo
   IF+DtMovto+IdTipoMsg em status não-final: "Existem solicitações pendentes para essa combinação..."
   (`SPI.Core.Angular.Infrastructure.decompiled.cs:3044-3050`).

### 1.5 Fluxo de envio e retorno (sem alçada)
`ReqPagtoUseCase` — `SPI.Core.SPB.Application.decompiled.cs:182-509`:
- `InsereReqSaldo` gera `NumCtrlIF = "SPI{yyyyMMdd}{seq:000000000}"` (277-278), integra via
  TOPAZ (`IntegraTopaz`, 291-339) ou JD (`IntegraJD`, 341-387), grava a mensagem espelho.
- `TrataRetornoIntegracao` (68-164): status = `AguardandoRetorno` se OK, `ErroNaIntegracaoComSPB`,
  `Rejeitada` ou `PendenteEnvio`; **`Alcada = null`** (linha 134) → movimento de Conta PI NÃO passa
  por aprovação (alçada) no legado; salta direto para AguardandoRetorno.
- `BackgroundService EnvioBacen` varre IFs e chama `EnviaReqSaldo` sobre `ObtemPendentesEnvio`
  (status PendenteEnvio) — `SPI.Web.Angular.Saldos.Api.decompiled.cs:299-315` +
  `SPI.Core.SPB.Infrastructure.decompiled.cs:241-248`.
- `RetornoBacen`/`UpdateReqSaldo` → `VerificaRequisicao` + `VerificaRetornoMensagem` (poll de
  protocolo no LegadoSPB) → atualiza status por `NumCtrlIF/NumCtrlIEME/NumCtrlPSPI`
  (`ProcessaProtocolos`, 471-508).

### 1.6 Prova viva no DB legado (crk_spi)
- `dbo.SpiReqSaldo` = **4084 linhas** (colunas: IdRequisicao, IdInstFinanc, IdFilialInst, Ispb,
  IspbDestino, DtMovto, DtHrEntrada, IdTipoMsg, IdStatus, IdTipoRequisicao, Vlr, DtHrEnvio,
  DtHrRetorno, IdUsuario, NumCtrlIF(varchar 20), FinlddLPI, Protocolo, IdOperacao).
- Distribuição IdTipoMsg×IdTipoRequisicao: {Crédito×Propria=5, Crédito×Liquidante=2, Crédito×SME=1,
  Débito×Propria=4073, Débito×Liquidante=2, Aporte×Liquidante=1} → 99,7% são Débito Própria (LPI0003).
- Status: 7 AguardandoRetorno=3688, 4 PendenteEnvio=297, 10 Rejeitada=90, 17 ErroNaIntegracaoComSPB=7,
  9 Efetivada=2 (mapa: `enumStatusOperacao`, `SPI.Core.General.decompiled.cs:7114+`, 0-indexado).
- FinlddLPI: 1=4082, 2=1, 10=1.
- `dbo.SpiSaldoSpb` = **1016 linhas** (IdInstFinanc, DtMovto, VlrSaldo, DtAtualizacao) — snapshot do
  saldo de reserva/Conta PI que o PIX legado mantém sincronizado a partir da integração SPB.
- `dbo.SpiCadParamReqSaldo` (IdInstFinanc, DtInicioVigencia, IdTipoMsg, Vlr, IdUsuario) — valor
  DEFAULT por IF/tipo para pré-preencher a tela (não gera aporte automático).

---

## 2. NOSSO sistema (Elixir/Vue) — comportamento com prova

### 2.1 Onde vive o LPI hoje
Diferente do legado: **a cabine PIX (`pix/backend`) NÃO origina LPI** (grep `LPI000` no source não-teste
= 1 única ocorrência, um comentário em `apps/spi_service/lib/spi_service/statements/statement_file.ex:128`).
Todo o LPI vive na **cabine SPB (`spb/services/bacen_gateway`)**, que assina e envia direto ao BACEN.

### 2.2 Builders LPI0001-0005 (XSD-compliant, iguais ou superiores ao legado)
`spb/.../messages/lpi/lpi000{1..5}.ex`:
- LPI0001 (`lpi0001.ex:73-82`): CodMsg, NumCtrlIF, ISPBIF, ISPBPSPI, VlrLanc, DtMovto — idêntico ao
  legado e ao XSD; `validate/1` exige campos + `validate_money_format` fail-closed (140-198).
- LPI0002 (`lpi0002.ex:70-74`): CodMsg, NumCtrlIEME, ISPBIEME, VlrLanc, DtMovto — idêntico.
- LPI0003 (`lpi0003.ex:70-85`): CodMsg, NumCtrlPSPI, ISPBPSPI, ISPBIFCredtd, **Grupo_LPI0003_CliCredtd
  (AgCredtd, CtCredtd, CtPgtoCredtd, CNPJCliCredtd), NumCtrlSTROr (opcional)**, FinlddLPI, VlrLanc,
  DtMovto — **mais completo que o legado** (que omite o grupo cliente-creditado e NumCtrlSTROr).
- LPI0004 (`lpi0004.ex`): CodMsg, NumCtrlPSPI, ISPBPSPI, VlrLanc, DtMovto — idêntico.
- LPI0005 (`lpi0005.ex:70-83`): CodMsg, NumCtrlPSPI, ISPBPSPI, PercSldRB_CL|VlrRB_CL,
  PercSldCCME|VlrCCME, DtMovto — presente (o PIX legado nunca construiu LPI0005).
- Resolução por código: `messages/registry.ex:113-177` (`get_module`/`build_message`).

### 2.3 Origem no nosso sistema: apenas o "Send Message" genérico
Não há tela dedicada de aporte/saque de Conta PI. A origem possível é o construtor genérico
(`POST /messages/send`, `MessagesController`) — `router.ex:107-108`, com fluxo de aprovação
`/messages/:id/approve|reject` (`router.ex:116-117`). O operador escolhe o CÓDIGO e preenche campos;
NÃO há derivação automática LPI0001/0002/0003/0004 a partir de crédito/débito × Própria/Liquidante/SME.
Prova de que LPI0001 é exercido em PRD: `post_integration/state_guard.ex:9` ("PRD: 4 LPI0001 +
1 SME0003 r1_confirmed") e moduledoc `lpi_handler.ex:16` ("LPI0001 round-trip homolog").

### 2.4 Handler de resposta (status-only) e mapa de status
`post_integration/specific_handlers/lpi_handler.ex`:
- `handle/4` (72-86): R1→`handle_r1`, R2→`handle_r2`, R3→`handle_r3`, E→`handle_error`; envio
  LPI0001→"lpi_registration", LPI0003→"lpi_settlement_sent", LPI0004→"lpi_cancel_sent",
  LPI0005→"lpi_extension".
- R1 via `SitLancSTR` numérico + `StatusDePara.map_status("LPI", n)` (254-276): 1-3 confirmado,
  9/999 rejeitado, resto processing/pendente.
- Direção de saldo pelo catálogo (`debit_credit_flag`), não heurística — `lifecycle_engine.ex:2947-2967`.

### 2.5 LPI0006 — espelho da Conta PI (grupo de saldo 43)
`lpi_handler.ex:92-234` + wiring `consumers/inbound_consumer.ex:414-471, 700-712, 806-818`:
- LPI0006 chega como `:inbound_notification` (direction_flag='R'); `mirror_conta_pi/1` credita o
  grupo de saldo 43 com o valor efetivamente transferido.
- Valor por `TpCtBC`: RL→VlrRB_CL, ME→VlrCCME, com fallback + VlrLanc (`conta_pi_amount/1`, 196-207);
  nunca inventa (0 se nenhum campo tem valor).
- Só credita se `SitLancSTR` confirmado (1-3) — "Rejeitado sem Saldo" NÃO move o espelho (148-168),
  respeitando a Observação 3 do catálogo.
- Idempotente por `NumCtrlSTR` (`dedup_uuid/1`, 220-232) — redelivery não credita em dobro.
Esse processamento de LPI0006 **NÃO existe no PIX legado** (é uma capacidade da cabine SPB).

### 2.6 Saldo da Conta PI na cabine PIX (leitura, replicação cruzada)
`apps/spi_service/lib/spi_service/nats/balance_responder.ex:96-119` responde `spi.balance.request`
com `Balances.get_balance/1` (fonte camt.053/060, tabela `monetarie_spi.balances`, REAIS), read-only.
`settlement_service/.../gateway/spb_balance_controller.ex:3-9` faz o inverso (a cabine PIX exibe a
reserva SPB via `spb.balance.request`). A cabine PIX NÃO mantém um ledger LPI de Conta PI — o ledger
grupo-43 vive na cabine SPB (§2.5).

### 2.7 Config de auto-aporte (stub, sem worker)
`settlement_service/.../gateway/balance_parameters_controller.ex:5,33-51` guarda
`auto_request_enabled/threshold/amount/target_account` (análogo do `SpiCadParamReqSaldo` legado e da
config LPI0005). Grep por `auto_request` no source não-teste = só o próprio controller: NENHUM worker
consome esses parâmetros para emitir LPI0001. É armazenamento/exibição, sem automação.

---

## 3. GAPS (legado × nosso, com prova)

Ver objeto estruturado. Resumo:
- g1 divergencia (info): arquitetura de origem — legado PIX constrói e delega ao LegadoSPB
  (TOPAZ/JD); nós não originamos LPI no PIX, a cabine SPB constrói+assina+envia ao BACEN.
- g2 ausente (medio): sem tela de aporte/saque Conta PI com Própria/Liquidante/SME e derivação
  automática do código LPI a partir de crédito/débito × tipo de participante.
- g3 divergencia (baixo): rótulos/semântica CDI no `lpi_handler` (registro/liquidação/prorrogação)
  vs semântica Conta PI do catálogo BACEN; efeito só de status, mas confunde analytics/operação.
- g4 parcial (medio): validações de negócio da origem ausentes (duplicata pendente, IspbIF==IF,
  filial, finalidade obrigatória no débito).
- g5 divergencia (medio): campos do formulário LPI genérico fabricados (SitLancLPI Normal/Prioritario/
  Emergencial; codFinalidadeLPI 01/02/03) — não batem com o XSD/FinlddLPI oficial.
- g6 divergencia (info): fonte do saldo de Conta PI diverge (legado SpiSaldoSpb via integração SPB;
  nós camt na cabine PIX + ledger grupo-43 na cabine SPB) — risco de reconciliação.
- g7 parcial (baixo): config de auto-aporte (auto_request_*) sem worker que a consuma.
- g8 divergencia (info): legado NÃO aplica alçada ao movimento de Conta PI (Alcada=null); nós
  temos rota genérica approve/reject cuja aplicação ao LPI é INCONCLUSIVA (não provada).

## 4. COBERTOS relevantes
- c1: builders LPI0001-0004 XSD-compliant (iguais/superiores ao legado; LPI0003 com Grupo_CliCredtd
  + NumCtrlSTROr que o legado omite).
- c2: LPI0006 espelho Conta PI implementado, idempotente por NumCtrlSTR, respeita "Rejeitado sem
  Saldo" — capacidade AUSENTE no PIX legado.
- c3: LPI0005 (config auto-transferência) presente como builder — o PIX legado nunca construiu.
- c4: mapa de resposta R1/R2/R3/E via SitLancSTR + StatusDePara (paridade com VerificaRetornoMensagem
  do legado) e direção de saldo pelo catálogo (não invertida por heurística).
