# Plano de Implementação — Seed `036` (contas COSIF padrão + vínculo)

- **Spec:** `docs/superpowers/specs/2026-07-02-loan-products-cosif-binding-design.md`
- **Branch:** `feat/loan-cosif-binding`
- **Data:** 2026-07-02

Entregável: `priv/repo/seeds/036_loan_products_cosif_binding.exs` (idempotente) + wire no
`production.exs` + testes. **Sem alterar código de runtime.**

---

## Regra crítica — formato dos códigos

Dois formatos convivem no sistema. Gravar cada campo no formato que o consumidor espera:

| Onde | Formato | Exemplo | Por quê |
|---|---|---|---|
| `cosif_accounts.code` (contas criadas) | **dotted** | `1.6.1.20.01.60.213` | todas as contas COSIF são dotted |
| `historic.liberation/liquidation/fees/fine/fees_delay` | **dotted** | `1.6.1.10.01.10.001` | `AdiantamentoTrigger`/`OverdraftJournal` fazem `String.replace("-",".")` e resolvem por código dotted |
| `historic.provision_incorrida/adicional/esperada` | **12 dígitos** | `161200160213` | casa com linhas existentes; `provision_journal.normalize/1` converte 12-díg→dotted no lookup |
| `historic.despesa_provisao` | **12 dígitos, valor exato** | `819921601002` | **precisa ser idêntico** ao das linhas existentes — o `GROUP BY provision_esperada, despesa_provisao` agrupa pela string crua |

---

## Fase 0 — Verificações de aterrissagem (read-only, antes de codar)

- **P0.1 — Guarda de ambiguidade (maior risco).** Ler `provision_journal.ex:120-175`
  inteiro. Entender exatamente o que dispara `{:error, {:ambiguous_despesa_provisao}}`.
  Confirmar que adicionar os pares abaixo (todos com despesa `819921601002`) não quebra:
  `(161200160213, 819921601002)`, `(161101160001, 819921601002)`, `(161101160002, 819921601002)`.
  Se a guarda exigir 1 despesa por carteira, avaliar despesa distinta por família ou
  ajuste no engine (decidir com Bruno).
- **P0.2 — IOF.** `grep 4.9.1.10.10 cosif_accounts.exs` + `AccountingBridge`. Se ausente,
  incluir `4.9.1.10.10.01.001/002` no inventário; se presente, remover da criação.
- **P0.3 — `category`/`subcategory`.** Conferir strings para grupos 4 (passivo/IOF),
  7 (receita) e 8 (despesa) em `cosif_accounts.exs`/`scd_cosif_plan.exs` (já visto
  `DESPESA` no grupo 8). Reusar as mesmas.
- **P0.4 — Ancestrais grau 2–6.** Para cada folha nova, enumerar ancestrais faltantes
  (inserir com `on_conflict: :nothing`, como o `033`):
  - `7.1.1.05.30/35.01.001` → `7.1.1.05.00.00.000`, `7.1.1.05.30/35.00.000`, `…01.000`.
  - `1.6.1.10.01.10.001/002` → `1.6.1.10.00.00.000`, `1.6.1.10.01.00.000`, `1.6.1.10.01.10.000` (existem em `cosif_seed.exs`, **não** em prod → criar).
  - `1.6.1.20.01.40/50/60.213` → `1.6.1.20.01.40/50/60.000` (`1.6.1.20.01.00.000` já vem do `033`).
  - `1.6.1.10.01.40/50/60.001/002` → `1.6.1.10.01.40/50/60.000`.
  - `7.1.1.10.01.10.001/002` → `7.1.1.10.00.00.000`, `7.1.1.10.01.00.000`, `7.1.1.10.01.10.000`.
  - `8.1.9.92.16.01.002` (global) → **verificar se já existe**; se faltar, criar + ancestrais.
- **P0.5 — `CosifAccount.changeset`.** Ler o schema: campos obrigatórios + categorias
  válidas, para o account-map passar na validação.
- **P0.6 — Resolução dos produtos.** CE-pai: `model_type="limite"` (código auto-alocado,
  não fixo). Uso de Limite: `parent_product_id`=CE-pai. ADEP: `code IN ('2001','2002')`
  (ou `model_type="adiantamento"` + `pessoa_tipo`).

**Saída da Fase 0:** inventário de contas finalizado (folhas + ancestrais), strings de
category, decisão sobre despesa/ambiguidade, e IOF confirmado.

## Fase 1 — Seed `036`, Parte A (garantir contas)

1. Criar `priv/repo/seeds/036_loan_products_cosif_binding.exs` espelhando o `033`:
   header com `alias Repo/CosifAccount/ID`, `overdraft`→`loan_binding` offset **60_000**,
   `now`.
2. `accounts = [...]` com todas as folhas (Fase 5 do spec) + ancestrais da P0.4, cada
   uma no shape do `033` (`code, name, category, subcategory, level, parent_code,
   tigerbeetle_account_code: nil, description, is_active`).
3. Loop `Enum.with_index |> Enum.reduce` com `Repo.insert(changeset, on_conflict: :nothing,
   conflict_target: :code)` + logging idêntico ao `033`.

**Aceite:** banco sem essas contas → todas criadas; rerun → 0 novas (log).

## Fase 2 — Seed `036`, Parte B (vincular produtos)

No mesmo arquivo, após a Parte A:

1. `binding_map` declarativo, uma entrada por produto (resolver + `cosif_account` +
   `historic` map), conforme §6 do spec e a Regra Crítica de formato.
2. Para cada entrada:
   - Resolver o produto (`Repo.one`). Se `nil` → `Logger.warning` + skip.
   - `cosif_account`: atualizar **só se nil/vazio** (`update_all` com
     `where: is_nil(p.cosif_account) or p.cosif_account == ""`).
   - `historic`: `get_by(product_id)`; construir attrs preenchendo **só campos nil/vazios**
     do historic atual (não sobrescreve customização); `insert_or_update`.
3. CE-pai: só `cosif_account = 9.3.4.10.01.01.002`; **sem** historic de provisão
   (`liberation` fica nil → fallback PF/PJ do `OverdraftJournal`).
4. Log final: `inseridas/total` + `vinculados/total`.

**Aceite:** cada produto com historic/cosif_account esperado; CE-pai sem provisão;
rerun não altera nada.

## Fase 3 — Wire no `production.exs`

Após o bloco `loan_products_seed_path` (~linha 325), adicionar bloco análogo com guarda
`File.exists?` rodando `036_loan_products_cosif_binding.exs`. Ordem garantida: vem depois
de `cosif_accounts.exs`, `033` e `loan_products.exs`.

## Fase 4 — Testes

`test/monetarie/credit/loan_cosif_binding_test.exs` (sandbox Ecto):

- Semeia base mínima (cosif_accounts + 033 + loan_products) e roda `036` via `Code.eval_file/1`.
- Contas criadas quando ausentes; **rerun idempotente** (0 inserts no 2º run).
- Cada produto de sistema com `historic`/`cosif_account` esperado (checar formato: dotted
  vs 12-díg).
- CE-pai **sem** historic de provisão.
- Binding **não sobrescreve** valor pré-existente.
- `ProvisionJournal` (constituição) **não** retorna `{:error, {:ambiguous_despesa_provisao}}`
  com os produtos vinculados.

**Aceite:** `mix test test/monetarie/credit/loan_cosif_binding_test.exs` verde.

## Fase 5 — Validação local

1. Banco limpo → rodar seeds → `mix run priv/repo/seeds/036_...` **2×**; conferir logs
   (idempotência).
2. `mix test` (suite de credit/provisioning não regride).
3. (Opcional) Simular débito CC que dispara ADEP e conferir JE COSIF postado (fim do
   buraco *fail-soft*).

## Fase 6 — Deploy e follow-ups

- **Deploy:** rodar `036` via release rpc nos ambientes existentes (homolog/local/prod) —
  one-off idempotente — além de já estar no `production.exs` para fresh deploys.
- **Follow-ups (fora deste PR):** reconciliar plano v2 (`.10`) × runtime (`.01`); De/Para
  4010 do CE; alinhar `modality_code` do CE à modalidade SCR de cheque especial.

## Riscos

| Risco | Mitigação |
|---|---|
| Guarda de ambiguidade do provisionamento (P0.1) | despesa global `819921601002` idêntica às linhas existentes; verificar em teste |
| IOF já existente → duplicar | P0.2 (verificar antes de criar; `on_conflict: :nothing` protege) |
| Formato dotted × 12-díg trocado | Regra Crítica + teste que checa formato por campo |
| Ancestrais faltando em fresh deploy | P0.4 enumera; `on_conflict: :nothing` |
