# Design — Contas COSIF padrão dos produtos de sistema + vínculo (seed `036`)

- **Data:** 2026-07-02
- **Autor:** Claude Code (brainstorming com Bruno)
- **Status:** Aprovado (aguardando revisão do spec escrito)
- **Repo:** `monetarie` (core banking) · branch de trabalho: `feat/loan-cosif-binding`

---

## 1. Contexto e problema

Os produtos de empréstimo **de sistema** (Cheque Especial/Limite e Adiantamento a
Depositantes PF/PJ) contabilizam automaticamente via `AdiantamentoTrigger` e
`OverdraftJournal`. Hoje há dois furos:

1. **Contas COSIF ausentes em alguns ambientes.** O `production.exs` roda
   `cosif_accounts.exs` + `033_overdraft_cosif_accounts.exs` + `035_…`, mas **não**
   roda `cosif_seed.exs`. Contas de renda/IOF do CE e a patrimonial do Adiantamento
   não estão no caminho de produção. Quando a conta não existe:
   - concessão de limite é *fail-closed* (bloqueia a facility);
   - utilização/encargos e o JE de Adiantamento são *fail-soft* (JE some, buraco
     contábil silencioso — só um `Logger.warning`).

2. **Produtos não vinculados às contas.** `loan_products.exs` cria os produtos mas
   **nunca** preenche `loan_products.cosif_account` nem cria `loan_products_historic`.
   O vínculo é implícito (códigos hardcoded no `OverdraftJournal`); para o
   **Adiantamento**, que só posta via `historic.liberation` (sem fallback hardcoded),
   o resultado é que **nenhum JE de Adiantamento é postado hoje**.

O `historic` é a fonte real do vínculo por evento contábil: campos
`liberation`, `liquidation`, `fees`, `fine`, `fees_delay`, `provision_incorrida`,
`provision_adicional`, `provision_esperada`, `despesa_provisao` (todos strings de
código COSIF). O motor de provisionamento lê `historic.provision_*` e normaliza
(`161200160213` → `1.6.1.20.01.60.213`).

## 2. Objetivo

Garantir (create-if-missing) as contas COSIF padrão dos produtos de sistema **e**
vinculá-las aos produtos, de forma idempotente, em todo ambiente — via seed rodado
no deploy.

### Escopo (in)
- Produtos: **Cheque Especial** (pai `limite`), **Uso de Limite** (submodelo),
  **Adiantamento PF** (`2001`), **Adiantamento PJ** (`2002`).
- Criar contas COSIF faltantes + vincular `historic` (contábil completo) +
  `cosif_account`.

### Escopo (out — follow-ups)
- **Não** reconcilia o `Plano_de_Contas_Interno_SCD_v2.md` (subtítulo `.10`) nem o
  De/Para do CADOC 4010 — o runtime usa `.01` e é a fonte autoritativa aqui; a
  divergência `.01` × `.10` é um follow-up separado já sinalizado.
- **Não** mexe nos produtos operados manualmente (PESSOAL/CONSIGNADO/GARANTIDO) nem
  no `Loans.COSIFJournaling` (que usa códigos hardcoded `-0`).

## 3. Decisões (brainstorming)

| # | Decisão | Escolha |
|---|---------|---------|
| Escopo | quais produtos | Sistema: CE + Adiantamento PF/PJ |
| Mecanismo | como rodar | Seed idempotente no deploy (padrão do `033`) |
| Profundidade | até onde vincular | Contábil completo (operacional + provisões 4.966), **exceto o CE-pai que não tem provisão — só o submodelo Uso de Limite** |
| Códigos | fonte autoritativa | Conjunto `.01` do runtime |
| Multa/mora | `fine`/`fees_delay` | Incluídos, vinculados à renda da própria operação (multa/mora são rendas da operação) — sem fabricar subgrupo `7.1.2` novo |

## 4. Arquitetura

Novo seed **`priv/repo/seeds/036_loan_products_cosif_binding.exs`**, wired no
`production.exs` **depois** de `loan_products.exs` (produtos precisam existir) e de
`cosif_accounts.exs`/`033` (contas base), guardado por `File.exists?` como os demais.

Duas fases no mesmo arquivo:

- **Fase 1 — garantir contas COSIF.** `Repo.insert(on_conflict: :nothing,
  conflict_target: :code)`, offset de ID **`60_000–69_999`** (disjunto: owem 1–1060,
  cosif_accounts 10k, cosif_seed 20k, judicial 30k, overdraft 40k, scd_plan 50k).
  Cria folhas grau 7 + ancestrais grau 2–6 que faltarem.
- **Fase 2 — vincular produtos.** Resolve os produtos de sistema por
  `model_type`/`pessoa_tipo`/`parent_product_id`; upsert de `historic` (get_by
  `product_id` + `insert_or_update`) e update de `cosif_account`.

## 5. Fase 1 — Inventário de contas a garantir (conjunto `.01`)

| Código | Descrição | Hoje |
|---|---|---|
| `7.1.1.05.30.01.001` / `7.1.1.05.35.01.001` | Rendas de Cheque Especial — PF / PJ | criar |
| `4.9.1.10.10.01.001` / `4.9.1.10.10.01.002` | IOF a recolher — PF / PJ | criar se faltar¹ |
| `1.6.1.10.01.10.001` / `1.6.1.10.01.10.002` | Adiantamento a Depositantes patrimonial — PF / PJ | criar (só existe em `cosif_seed.exs`) |
| `1.6.1.20.01.40.213` / `.50.213` / `.60.213` | Retificadoras 4.966 (Perda Incorrida / Provisão Adicional / Perda Esperada) — Uso de Limite | criar |
| `1.6.1.10.01.40.001` / `.50.001` / `.60.001` (PF) e `…002` (PJ) | Retificadoras 4.966 — Adiantamento | criar |
| `7.1.1.10.01.10.001` / `7.1.1.10.01.10.002` | Rendas de op. de crédito — Adiantamento PF / PJ² | criar (confirm) |
| `8.1.9.92.16.01.002` | Despesa de provisão (global, todos os produtos) | **reutilizar** — verificar se já existe; não criar por produto |

¹ `4.9.1.10.10.*` é compartilhado com `AccountingBridge`; verificar na implementação se já existe antes de criar.
² Folha de renda do Adiantamento é item a confirmar (§9); default proposto sob a subárvore de rendas do plano (`7.1.1.10.01.10.NNN`).

## 6. Fase 2 — Binding por produto

### 6.1 Campos lidos operacionalmente (precisos)

| Produto | Resolução | `cosif_account` | `historic.liberation` | Provisões (`incorrida`/`adicional`/`esperada`) | `despesa_provisao` |
|---|---|---|---|---|---|
| **Cheque Especial** (pai) | `model_type=limite` | `9.3.4.10.01.01.002` | `3.3.4.20.20.01.000` (nó neutro) | **nenhuma** | — |
| **Uso de Limite** (sub) | `parent_product_id`=CE-pai | `1.6.1.20.01.10.213` | `1.6.1.20.01.10.213` | `1.6.1.20.01.40.213` / `.50.213` / `.60.213` | `8.1.9.92.16.01.002` (global) |
| **Adiantamento PF** (2001) | `adiantamento`/`pf` | `1.6.1.10.01.10.001` | `1.6.1.10.01.10.001` | `1.6.1.10.01.40.001` / `.50.001` / `.60.001` | `8.1.9.92.16.01.002` (global) |
| **Adiantamento PJ** (2002) | `adiantamento`/`pj` | `1.6.1.10.01.10.002` | `1.6.1.10.01.10.002` | `1.6.1.10.01.40.002` / `.50.002` / `.60.002` | `8.1.9.92.16.01.002` (global) |

- O CE-pai fica **sem** provisão (é pura compensação): `liberation` = comp. ativa no nó
  neutro `3.3.4.20.20.01.000` ("Crédito a Liberar", sem viés PF/PJ) e `cosif_account` =
  comp. passiva `9.3.4.10.01.01.002`. **Decisão do dono (2026-07-02, validação no admin):**
  fixar o nó neutro em vez de deixar `liberation` nil, para o produto aparecer configurado
  na tela. Trade-off: o JE de concessão lança no nó grau-6 em vez das folhas PF/PJ
  (`3.3.4.20.20.01.005`/`.001`), que seguem como fallback quando `liberation` está vazio.
- As folhas de provisão são gravadas **direto no `historic`** → corretas
  independentemente do `modality_code=0299`.
- ⚠️ **Alias enganoso no código:** as queries do `AdiantamentoTrigger` selecionam
  `cosif_account: h.liberation` — o campo operativo do Uso de Limite/ADEP é o
  **`historic.liberation`**, não o `loan_products.cosif_account`. O `cosif_account`
  do produto é gravado por completude e é lido pelo `OverdraftJournal` apenas no
  **produto pai** (comp. passiva `9.3.4.10.01.01.002`).
- ⚠️ **Sobrescrita intencional do fallback de modalidade.** O backfill genérico
  (migration `20260527100000`) mapeia `modality_code` → folha de provisão, com
  `0299 → 1.6.1.20.01.{40/50/60}.299`. O binding **sobrescreve** isso para manter
  cada produto na folha correta: CE em `.213` (alinhado à patrimonial hardcoded
  `@cheque_especial_code`) e ADEP na família `1.6.1.10` (não `1.6.1.20`). Como o
  provisionamento lê `historic.provision_*` (confirmado, `provision_journal.ex:137-152`),
  gravar no `historic` é suficiente. **Verificar** que a guarda de ambiguidade
  (`GROUP BY provision_esperada, despesa_provisao` → `{:error, {:ambiguous_despesa_provisao}}`)
  tolera os novos pares `(provision_esperada, 8.1.9.92.16.01.002)`.

### 6.2 Campos de completude (rendas / liquidação / multa / mora)

Para "contábil completo". Roteamento PF/PJ das rendas do CE acontece no **posting**
(`OverdraftJournal` escolhe `@rendas_pf/pj` por `pessoa_tipo`); o valor no `historic`
é a referência canônica.

| Produto | `liquidation` | `fees` (rendas) | `fine` (multa) | `fees_delay` (mora) |
|---|---|---|---|---|
| **Uso de Limite** | `1.6.1.20.01.10.213` | `7.1.1.05.30.01.001` (PF canônico) | = rendas da operação | = rendas da operação |
| **Adiantamento PF** | `1.6.1.10.01.10.001` | `7.1.1.10.01.10.001`² | = rendas da operação | = rendas da operação |
| **Adiantamento PJ** | `1.6.1.10.01.10.002` | `7.1.1.10.01.10.002`² | = rendas da operação | = rendas da operação |

Multa e mora são receitas da própria operação de crédito → vinculadas à mesma folha
de renda (sem fabricar subgrupo `7.1.2` novo, que nem consta do plano interno). Se o
contador exigir folha dedicada de multa/mora, vem do elenco oficial (§9).

## 7. Idempotência e segurança

- **Contas:** `on_conflict: :nothing, conflict_target: :code` — rerun não duplica.
- **Binding:** upsert de `historic` por `product_id`; **só preenche campos nil/vazios**
  — não sobrescreve binding que o operador já customizou ("o operador pode ajustar
  depois"). `cosif_account` idem (só quando nil/vazio).
- **Produto ausente** (seed fora de ordem): `Logger.warning` e segue; não cria produto
  (responsabilidade do `loan_products.exs`).
- **Ancestrais:** inseridos com `on_conflict: :nothing` para garantir hierarquia em
  fresh deploy standalone (padrão do `033`).

## 8. Erros e observabilidade

- Cada conta inserida/pré-existente: `Logger.info` (padrão dos seeds numerados).
- Cada produto vinculado / já vinculado / ausente: `Logger.info`/`warning`.
- Contagem final `inseridas/total` + `vinculados/total`.

## 9. Pontos a confirmar na implementação/plano

1. ✅ **Resolvido:** o provisionamento lê `historic.provision_esperada`/`despesa_provisao`
   em runtime (`provision_journal.ex:137-152`). Binding via `historic` é suficiente.
   Falta apenas verificar a guarda de ambiguidade (§6.2) contra os novos pares.
2. IOF CE (`4.9.1.10.10.*`) realmente falta ou já vem do `AccountingBridge`.
3. Folha de **renda do Adiantamento** (`7.1.1.10.01.10.001/002`) e a existência de
   **folha dedicada de multa/mora** — confirmar contra o elenco oficial antes de fixar.
4. Modalidade SCR do CE: `modality_code` hoje é `0299`; a folha patrimonial usa `.213`.
   Confirmar se `.213` é a modalidade correta (cheque especial) e se convém alinhar o
   `modality_code`.

## 10. Testes

ExUnit `test/monetarie/credit/loan_cosif_binding_test.exs` (roda o seed via
`Code.eval_file/1` no sandbox Ecto):

- Contas criadas quando ausentes; **rerun não duplica** (0 inserts no 2º run).
- Cada produto de sistema fica com `historic`/`cosif_account` esperado.
- CE-pai **sem** `historic` de provisão.
- Binding **não sobrescreve** valor pré-existente (customização do operador).
- Adiantamento/Uso de Limite passam a postar JE COSIF (fim do buraco *fail-soft*).

## 11. Arquivos afetados

- **Novo:** `priv/repo/seeds/036_loan_products_cosif_binding.exs`
- **Editar:** `priv/repo/seeds/production.exs` (wire do `036` após `loan_products.exs`)
- **Novo:** `test/monetarie/credit/loan_cosif_binding_test.exs`
- **Sem alteração de código de runtime** (`OverdraftJournal`/`AdiantamentoTrigger` já
  leem os campos; o seed apenas os popula).
