# Handoff — ✅ FIX do bug de unidade 100x no bloqueio judicial SISBAJUD

**Data:** 2026-07-09
**Status:** CORRIGIDO e provado (TDD: 6 testes puros + 2 de integração com TigerBeetle real; regressão 406/0). Local, sem push. Segue o diagnóstico de `docs/handoff/2026-07-08-sisbajud-bug-unidade-bloqueio-100x.md`.
**Branch:** `feat/monetarie_improviments`.

---

## 1. O que era o bug

O caminho judicial misturava CENTAVOS (valor da ordem, do `F.parse_v`) com SUBCENTAVOS (base_units do TigerBeetle, BRL×10.000) sem converter. Uma ordem de R$ 50,00 congelava **R$ 0,50** no TB (100x menos) e reportava "executado" ao BCB; desbloqueio deixava dinheiro preso; valores no 5302 saíam 100x inflados. Ver o diagnóstico de 2026-07-08 para o raio de alcance completo.

## 2. Decisão de design (validada com o dono)

**Manter os valores judiciais internamente em CENTAVOS** (a unidade da ordem judicial e da contabilidade/COSIF/wire 5302), convertendo para subcentavos **só na fronteira do TigerBeetle**.

Racional do dono: o TB usa subcentavos por precisão operacional (fee_split de PIX com mais casas), mas a **contabilidade/balancete é em centavos** e não se quer mexer na lógica contábil. Para ordens judiciais, bloqueia-se **o valor da ordem** (centavos): cliente com R$ 35,0032 e ordem de R$ 35,00 → congela R$ 35,00, os R$ 0,0032 ficam no TB.

Por que CENTAVOS e não base_units (subcentavos): a maioria esmagadora dos consumidores de `blocked_amount` espera centavos (wire 5302 via `pad_v`, COSIF via `PostingEngine`, colunas de valor da UI via `/100`, respostas ao BCB). Guardar centavos = **2 conversões** (fronteira do TB) contra ~15 (abordagem base_units, que exigiria `from_cents` na entrada dos 7 pontos + `to_cents` em todo o wire/COSIF/UI). Precedente no código: `cosif_journal_entries.amount` já é uma exceção documentada em centavos.

**Exceção documentada** em `Monetarie.Util.MoneyUnit` (tabela de unidades) + `@moduledoc` do schema `JudicialBlock`: `judicial_blocks`/`judicial_orders.blocked_amount`/`requested_amount` estão em CENTAVOS, não base_units. NÃO assumir base_units ao ler essas colunas.

## 3. Mudanças (código)

| Arquivo | Mudança |
|---|---|
| `use_cases/judicial/processor.ex` | Nova função pura `block_amount_cents/2` (saldo subcentavos + valor centavos → quanto bloquear em centavos). `do_block_account` usa ela + `from_cents` no `hold_funds` + `blocked_amount` em centavos + rollback release em subcentavos. `cancel_one_block` e `release_block` liberam `from_cents(block.blocked_amount)`. |
| `regulatory/sisbajud/file_processor.ex` | Preview: `total_balance` do TB (subcentavos) → `to_cents` para exibir na escala do `valor_solicitado` (centavos). |
| `accounts/blocked_breakdown.ex` | Item judicial: `from_cents(blocked_amount)` — o módulo trabalha em subcentavos (igual MED/PIX/tb_pending); cascata conserta `judicial_total` e o serializador do controller. |
| `controllers/v2/judicial_blocks_controller.ex` | Endpoint IB `/judicial-blocks`: `from_cents` no total e por-bloco (contrato subcentavos, igual ao endpoint irmão MED + `formatCents` do banking). |
| `util/money_unit.ex` + `schemas/judicial/judicial_block.ex` | Documentam a exceção de unidade (centavos). |

Fronteiras do TB no caminho judicial (as únicas que precisam converter): `hold_funds` (bloqueio), `release_funds` (desbloqueio/cancelamento/rollback). `collect_funds/1` (transferência) não recebe valor — libera o hold inteiro, é unit-safe. Tudo a jusante (comparações de status, `select_unblock_blocks`, `amount_exceeds_blocked?`, wire, COSIF, UI) fica correto porque `requested_amount` (centavos) e `blocked_amount` (agora centavos) são a mesma unidade.

## 4. Testes (TDD)

- `test/monetarie/judicial/block_amount_unit_test.exs` (6, tag `:sisbajud_unit`): a decisão pura, incluindo o exemplo R$ 35,00 / R$ 35,0032 e os casos parcial/total/sub-centavo.
- `test/monetarie/judicial/block_hold_unit_integration_test.exs` (2, tag `:sisbajud_integration`, TB real):
  1. Ordem R$ 50,00 com saldo R$ 100 → congela **500_000 subcentavos** (não 5_000) e `blocked_amount` em centavos. Discriminador direto do bug (pré-fix deixava saldo 995_000).
  2. Round-trip bloqueio→desbloqueio total → saldo inteiro de volta (release não deixa resíduo preso).
- Regressão: `test/monetarie/{judicial,regulatory/sisbajud,use_cases/regulatory/sisbajud}` + `blocked_breakdown_test` = **406 testes, 0 falhas**. As 20 falhas sob `--only sisbajud_integration` são placeholders pré-existentes (lookup/transfer/cancellation _integration — `tb_transfer_id: nil`, colisão de índice, fixtures), provadas idênticas com e sem minhas mudanças via `git stash`.

Como rodar (imagem `monetarie-core-backend-test:latest`, TB local em `192.168.155.3:3001`):
```
TB_ADDRESSES=192.168.155.3:3001 docker run --rm --network monetarie_default \
  --security-opt seccomp=unconfined \
  -v "$PWD/core/backend/lib:/app/lib" -v "$PWD/core/backend/test:/app/test" \
  -v "$PWD/core/backend/priv:/app/priv" -v "$PWD/core/backend/config:/app/config" \
  -v monetarie-test-build:/app/_build -v monetarie-test-deps:/app/deps \
  -e DB_HOST=monetarie-pg -e DB_PORT=5432 -e DB_USER=monetarie -e DB_PASS=monetarie \
  -e TB_ADDRESSES=192.168.155.3:3001 -e MIX_ENV=test \
  monetarie-core-backend-test:latest \
  mix test test/monetarie/judicial/block_hold_unit_integration_test.exs --only sisbajud_integration
```

## 5. 🔴 PRÉ-CONDIÇÃO DE DEPLOY (revisão adversarial) — over-release de blocos legados

O código pré-fix gravava `blocked_amount` na unidade que congelava (o hold no TB batia com o `blocked_amount` gravado). O release novo faz `from_cents(blocked_amount)`. Para um bloco **criado antes do fix** e ainda `active`, `from_cents(blocked_amount)` = ~100x o valor realmente travado → o `release_funds` pagaria o excedente (~99x) da conta cash-asset da instituição (dinheiro saindo do banco).

- **`mon_core` local: 0 `judicial_blocks` ativos** (só 2 ordens BLOCK `not_found`, sem hold) → sem risco local.
- **ANTES de deployar em HML/prod:** contar `judicial_blocks WHERE status='active'` e reconciliar cada `blocked_amount` gravado contra o hold real no TB (`lookup_transfers`). Se houver blocos legados: migrar/reinterpretar `blocked_amount` (ou liberar/re-bloquear sob o código novo) ANTES de rodar o caminho de release novo. O caminho TRANSFER (`collect_funds`, sem valor) é seguro para legados, mas sub-transferiria vs. o `total_blocked` reportado (inconsistência legada, não introduzida por este diff).

## 6. Itens em aberto (fora do escopo deste fix)

- **Colisão de índice único no DESBLOQUEIO (pré-existente):** `judicial_orders_protocolo_seq_reit_idx` (parcial, `WHERE protocolo IS NOT NULL AND sequencial_bloqueio IS NOT NULL`, sem `order_type`) impede a ordem de desbloqueio de compartilhar `(protocolo, seq, reit)` com o bloqueio. O teste de round-trip contorna deixando `seq/reit` nil no desbloqueio (o `find_prior_block_order` coage para 0). Fix real: incluir `order_type` no índice ou usar `sequencial_desbloqueio` como identidade própria. Isso BLOQUEIA o fluxo de desbloqueio real em produção.
- **TRANSFER sem teste de integração:** provado por raciocínio (unit-safe: `collect_funds` sem valor + comparações em centavos) + testes struct-level, mas sem teste TB real fim-a-fim. Candidato a um `:sisbajud_integration` futuro.
- **`Formatter.encode_latin1` (UTF-8, não ISO-8859-1)** no builder do 5302 — separado, já anotado.
- Contatos (5306) e Varas (5305) — pendentes desde 2026-07-08.

## 7. Ponteiros

- Diagnóstico: `docs/handoff/2026-07-08-sisbajud-bug-unidade-bloqueio-100x.md`.
- Memória: `sisbajud-leiautes-e-fatos-locais`.
- Revisões adversariais desta sessão: mapa de consumidores de `blocked_amount` (unidade por consumidor) + revisão de correção do diff (7 vetores OK + o achado de over-release legado da seção 5).
