# ETL Core (monbank) 2026-07-08 — matriz de>para 100%, coluna a coluna, batida no centavo

Backup: `~/Desktop/monbank_backup_2026-07-08.sql` (pg_dump plain, servidor origem 15.3, dump 18.4, 2026-07-08 13:40).
Destino: Core Monetarie, banco `mon_core` (HML), reconciliado ao centavo.
Método: carga byte-fiel do dump inteiro num Postgres 18 isolado (container `monbank-etl-0708`, porta 5545, volume próprio) e análise empírica read-only. Nada aqui é inferido: cada número tem consulta SQL de origem.

## RESULTADO — EXECUTADO E RECONCILIADO EM HML (2026-07-08 18:04)

Re-migração destrutiva rodada no `mon_core` vivo (Aurora `monetarie-core-homolog-45`; produção intocada). Snapshot pré-ETL `moncore-pre-etl-0708`. Relatório final de `Remigrate.run(reset: true)`:

- **to_the_cent = TRUE**, error_count = 0, mismatch_count = 0.
- **1.606 contas, 1.542 usuários** (idêntico ao backup).
- **total account_entries = R$ 719.303,44** = exatamente o total do relatório oficial (batido ao centavo, conta a conta).

Verificado no vivo: cadastro no fill-rate exato da projeção (birth_date 781, doc/RG 584, marital_status 424, father_name 399, annual_revenue 538, monthly_income 949, mother_name 426, occupation 145, bairro 1592, is_pep 0); IBAN 1.606/1.606 + número de conta legado preservado (000000003…, agência 0001); extrato 216.471/231.415 lançamentos com contraparte; status BLQ → 7 contas blocked. Partner API keys re-provisionadas do Secrets Manager (Herbeth `cli_716d68efa0b3156deaf799dc` TOKEN_OK http=200 + time-integração).

Bug latente corrigido no run (PR #6): `AccountNumberGenerator.generate_iban` não convertia a letra do tipo de conta no mod-97. Pendente (refinamento): hold em TB do `blocked_amount` (5 contas) e modalidade fina via CSV.

## 0. Sumário do backup (provado)

10 tabelas, 262 colunas, 694.162 linhas (soma exata das INSERTs = 694.162, zero linha órfã).

| Tabela | Linhas | Papel |
|---|---:|---|
| account_information | 1.606 | Cadastro/KYC do titular |
| account_information_addresses | 1.606 | Endereço (1:1 com titular) |
| account_restrictions | 9 | Bloqueio administrativo de conta (BLQ) |
| account_wallets | 1.606 | Saldo (autoridade) |
| accounts | 1.606 | Conta |
| autbank_legacy_transactions | 231.350 | Razão legado com saldo corrido + history_code + contraparte |
| autbank_transaction_import_items | 10.500 | Log de auditoria da importação legada |
| transactions | 231.350 | Razão do app (1:1 com o legado) |
| transfer_transactions | 212.987 | Detalhe de PIX/TED/interna (contraparte, tarifa, juros) |
| users | 1.542 | Usuário (login por CPF) |

Carga: só 10 statements pulados, todos idênticos — as 10 foreign keys órfãs para `public.tenants` (tabela-pai que o cliente não exportou). Zero linha de dado perdida. No Core não há multi-tenant (instituição única SCD), então `tenant_id` e essas FKs não têm efeito e viram rastro em metadata.

## 1. Unidades monetárias e conversão (verificado no código)

Origem: todo dinheiro é `numeric(_,2)` em reais (centavos de precisão).
Core:
- `account_entries.amount` = subcentavos, com sinal (+ crédito, − débito). Subcentavo = BRL × 10.000.
- `cosif_journal_entries.amount` = centavos. Centavo = BRL × 100.
- Saldo TigerBeetle = subcentavos.
- `cooperative_members.monthly_income` e `capital_social` = `:decimal` em reais (armazenado como está).

Funções (core/backend/lib/monetarie/etl/autbank/money.ex + util/money_unit.ex):
```
to_centavos(reais)    = round(reais * 100)                 # BRL x 100
to_base_units(reais)  = to_centavos(reais) * scale(=100)   # BRL x 10.000 (subcentavos)
```
Preservam o sinal (débito negativo). Regra de conversão da matriz:
- extrato/saldo/TB: `to_base_units` (× 10.000).
- COSIF: `to_centavos` (× 100).
- renda/capital do cadastro: reais decimal direto.

## 2. Mapa descritivo do extrato — 34 history_codes (o "batido no descritivo")

`autbank_legacy_transactions.history_code` está 100% preenchido e a `description` já vem com rótulo humano limpo. Cada código cai numa `category` do `account_entries` e numa família COSIF (a classificação COSIF já existe e cobre 100% dos 34 códigos deste backup — nenhum `:unmapped`).

| history_code | descrição legada | n | sentido | category (account_entries) | família COSIF (EventMap) |
|---|---|---:|---|---|---|
| 01000 | Pix - Recebimento | 191.391 | credit | transfer | cash_credit |
| 01012 | Pix - Pagamento | 14.147 | debit | transfer | passthrough_debit |
| 00903 | Tarifa PIX | 12.230 | debit | fee | tarifa |
| 00725 | TITULO PROPRIO BANCO | 3.798 | debit | transfer | passthrough_debit |
| 00732 | CREDITO VIA TED | 2.128 | credit | transfer | cash_credit |
| 00709 | Transf. a Débito | 1.945 | debit | transfer | passthrough_debit |
| 00708 | Transf. a crédito | 1.493 | credit | transfer | cash_credit |
| 00724 | TED DIFERENTE TITULARIDADE | 1.324 | debit | transfer | passthrough_debit |
| 00902 | Tarifa TED | 1.211 | debit | fee | tarifa |
| 00912 | Tarifa Manut Cta PJ | 641 | debit | fee | tarifa |
| 00723 | TED MESMA TITULARIDADE | 425 | debit | transfer | passthrough_debit |
| 00727 | PAGAMENTO DE CONCESSIONARIAS | 270 | debit | transfer | passthrough_debit |
| 00505 | Devolução de TED Emitida | 77 | credit | reversal | cash_credit |
| 00906 | Tarifa Manut Cta PF | 64 | debit | fee | tarifa |
| 00817 | Estorno débito indevido | 41 | credit | reversal | cash_credit |
| 00813 | IOF Adicional | 37 | debit | fee | iof_adicional |
| 01002 | Pix - Devolução | 17 | credit | reversal | cash_credit |
| 01030 | TRANSFERENCIA OJ | 14 | debit | transfer | passthrough_debit |
| 00802 | Juros ADP PJ | 14 | debit | interest | juros |
| 00818 | Estorno Pagto de Título | 13 | credit | reversal | cash_credit |
| 00736 | TARIFA SERV NEGOCIADO | 13 | debit | fee | tarifa |
| 01014 | Pix - Débito Devolução | 11 | debit | reversal | passthrough_debit |
| 00816 | Estorno crédito indevido | 10 | debit | reversal | passthrough_debit |
| 00808 | IOF ADP PJ | 10 | debit | fee | iof_credito |
| 00738 | LIQ TITULO A VENCER / VENCIDO | 5 | debit | transfer | passthrough_debit |
| 00734 | MIGRAÇAO SALDO | 5 | credit | adjustment | cash_credit |
| 01006 | Pix - Recebimento Fr. | 5 | credit | transfer | cash_credit |
| 01023 | Pix - Dev. Falha Op | 4 | debit | reversal | passthrough_debit |
| 01004 | Pix - Recebimento Openbanking | 2 | credit | transfer | cash_credit |
| 00507 | Devolução de TED Recebida | 1 | debit | reversal | passthrough_debit |
| 00905 | Tarifa Atualização Cadastro PJ | 1 | debit | fee | tarifa |
| 00733 | CRÉDITO VIA TED MESMA TIT | 1 | credit | transfer | cash_credit |
| 01007 | Pix - Dev. Falha Op | 1 | credit | reversal | cash_credit |
| 00722 | CREDITO DE DIFERENTE TITULARID | 1 | credit | transfer | cash_credit |

Enriquecimento do descritivo (o ETL antigo descartava): `autbank_legacy_transactions.counterparty` (100%) traz `{bank, agency, account, document, name}` da contraparte, e `transfer_transactions.details.autbank_classification` traz method/category/direction do próprio legado. O rótulo do extrato passa a ser, por exemplo, "Pix - Recebimento — MAISA VITORIA PEIXOTO (Banco 0290)" em vez de "Movimentacao".

## 3. Reconciliação de dinheiro — batida contra o relatório oficial (SALDO CONTAS GERAL)

Autoridade de batimento: `~/Desktop/SALDO CONTAS GERAL.csv` (relatório oficial emitido, 1.612 linhas = 1.611 contas + rodapé de total). Carregado no Postgres, parseado (formato brasileiro), chave de junção **`accounts.number || accounts.digit` = conta do CSV (10 dígitos)**.

Resultado (prova SQL):

| Métrica | Backup | CSV oficial | Diferença |
|---|---|---|---|
| Contas casadas | 1.606 | 1.606 (de 1.611) | — |
| Saldo total | R$ 719.303,44 | R$ 719.303,44 | **0,00** |
| Saldo exato por conta | 1.606/1.606 | — | **0 divergência** |
| Bloqueado total | R$ 523.174,21 | R$ 523.174,21 | **0,00** |
| Bloqueado exato por conta | 1.606/1.606 | — | **0 divergência** |
| Rodapé do CSV | — | R$ 719303.44 | confirma o total |

- `balance = disponível + bloqueado`; disponível backup R$ 196.129,23 (o `SLD.DISP` do CSV R$ 190.859,59 usa definição levemente diferente; a autoridade saldo+bloqueado bate exato).
- 2 contas com saldo negativo (soma −R$ 14,25); 291 de 1.606 têm razão; 286 batem `balance_after`=`wallet.balance` ao centavo (5 encerradas/varridas onde a carteira é autoridade).
- **5 contas do CSV não estão no backup**: todas PF, Ativas, saldo 0,00 (0000009883/9921/9891/9913/9905 — abertas depois do backup). 0 conta do backup fora do CSV.

Bloqueado por conta (todas batem exato):

| conta | nome | modalidade | situação | bloqueado |
|---|---|---|---|---|
| 0000005063 | KAY CASS GLOBAL LTDA | DEP VISTA P.JURIDICA | Ativa | R$ 373.446,51 |
| 0000003710 | LIDER TRATORES LTDA | CONTA DE PAGAMENTO PJ | Ativa | R$ 136.570,65 |
| 0000007511 | RJ RICK ELETRONICOS | CONTA DE PAGAMENTO PJ | Bloqueada | R$ 12.854,93 |
| 0000004415 | CARGOPAY INSTITUIÇÃO DE PAGAMENTO | CONTA DE PAGAMENTO PJ | Ativa | R$ 210,00 |
| 0000005705 | CATAN COMERCIO DE CARNES | CONTA DE PAGAMENTO PJ | Ativa | R$ 92,12 |

## 4. Matriz de>para por tabela (todas as colunas)

Legenda de status: POPULAR (destino existe, popular) · JÁ-OK (o ETL já fazia) · NOVA-COLUNA (dado real sem casa → coluna nova + tela) · METADATA (rastro legado preservado em JSONB) · VAZIA (0% na origem, nada a migrar, documentado) · N/A-TENANT (multi-tenant não se aplica).

### 4.1 users (1.542) → `users` (login por CPF)
| origem | fill | destino | transform |
|---|---:|---|---|
| id | 100% | metadata.autbank.legacy_user_id | rastro |
| name | 100% | users.name | direto |
| email | 98,2% | users.email | direto |
| document | 100% | users.login + users.tax_id | CPF/CNPJ limpo; login ≤20 chars |
| password | 100% ($argon2) | — | Argon2 não verificável pelo Core; status pending_activation (reativação) |
| activation_status | 100% (pending_activation) | users.status/first_access | mapear reativação |
| phone | 1,2% | users.phone | direto quando houver |
| pin_attempts | 100% | VAZIA-op | operacional legado, não migra |
| address, email_verified_at, remember_token, deleted_at, transaction_pin, pin_locked_until, profile_photo_url, activated_at, activation_token_hash/expires_at, scopes | 0%/op | VAZIA/METADATA | sem dado ou operacional |
| tenant_id | 100% | N/A-TENANT | — |
| created_at/updated_at | 100% | rastro | admission/metadata |

### 4.2 account_information (1.606) → `cooperative_members` + `users`
Titular/KYC. É aqui que mora a maior perda do ETL antigo.
| origem | fill | destino | transform |
|---|---:|---|---|
| id | 100% | metadata.autbank.account_information_id | rastro |
| user_id | 100% | (join) | liga a users |
| name | 100% | members.name | direto (PJ: razão social) |
| document | 100% | members.cpf_cnpj | limpo (não único) |
| email | 98,4% | members.email | direto |
| phone | 1,2% | members.phone | direto quando houver |
| person_type 1/2 | 100% | members.member_type | 1→pessoa_fisica, 2→pessoa_juridica |
| status (=3) | 100% | METADATA | status interno legado |
| birth_date | 48,6% (781) | members.birth_date | **POPULAR** (era nil) |
| rg_number | 36,4% (584) | members.doc_type='RG'+doc_number | **POPULAR** |
| rg_issuer | 36,4% (584) | members.doc_issuer | **POPULAR** |
| rg_issuing_state | 36,4% (584) | members.doc_issuer_state | **NOVA-COLUNA** (UF do RG) |
| rg_issue_date | 0% | members.doc_issued_at | VAZIA |
| politically_exposed (=2) | 100% | members.is_pep=false | **POPULAR** (2=não; nenhum PEP no lote) |
| occupation | 9,0% (145) | members.occupation | **POPULAR** |
| marital_status | 26,4% (424) | members.marital_status | **NOVA-COLUNA** (SOLTEIRO/CASADO/…) |
| father_name | 24,8% (399) | members.father_name | **NOVA-COLUNA** |
| mother_name | 26,5% (426) | members.mother_name | **POPULAR** |
| document_type | 36,4% (584) | members.doc_type | consistente com RG |
| monthly_income | 59,1% (949) | members.monthly_income | **POPULAR** (decimal reais) |
| company_name | 55,9% (897) | = members.name | idêntico a name em 100% dos PJ → sem coluna |
| company_representative_name | 55,9% (897) | VAZIA (auto-duplicata) | == name em 897/897 → NÃO é representante real |
| company_representative_cpf | 55,9% (897) | VAZIA (auto-duplicata) | == document (CNPJ) em 897/897 → NÃO gera legal_representatives |
| company_representative_rg | 9,6% (154) | METADATA | preservado; sem representante real associado |
| annual_revenue | 33,5% (538) | members.annual_revenue | **NOVA-COLUNA** (faturamento anual PJ) |
| data (json) | 100% | (minerado) | ver 4.2.1 |
| user_provision_status, account_provision_status, provision_* | 100% | METADATA | rastro de provisionamento legado |
| spouse_* (6), nationality, birth_city, birth_state, education_level, trade_name, state_registration, municipal_registration, company_opening_date, contract_file*, share_capital, company_type, cnae_code, account_type, document_front/back/selfie, webhook_message, location_event_id, metadata, biometric_enrollment_id | 0% | VAZIA | nada a migrar (documentado) |
| tenant_id | 100% | N/A-TENANT | — |

Observação: `nationality` é 0% na coluna mas o default do Core já é "Brasileira"; `data_constituicao`/`cnae_principal`/`capital_social` existem no Core mas estão 0% na origem (a abertura PJ vem do `data.autbank`, não da coluna) — ver 4.2.1.

#### 4.2.1 account_information.data (JSON) — chaves enumeradas
Top-level: autbank, migration, technical_state. Sub-chaves de `autbank`: account_situation, cod_agencia, cod_cliente, cod_coligada, data_atualizacao, data_cadastro, documents[], last_refreshed_at, legacy, monthly_income, monthly_income_column_persisted, source, status, tipo_pessoa.
| chave | destino | transform |
|---|---|---|
| account_situation ATI/ENC/BLQ | accounts.status + members.status | ATI→active, ENC→inactive, BLQ→suspended+restrição |
| data_cadastro | members.admission_date | data real de cadastro |
| cod_cliente, cod_agencia, cod_coligada | members.metadata.autbank + accounts.metadata | rastro |
| monthly_income | members.monthly_income (fallback da coluna) | reais decimal |
| documents[] (lista de docs entregues) | members.metadata.autbank.documents | rastro KYC |
| tipo_pessoa, status, source, last_refreshed_at, data_atualizacao, legacy, monthly_income_column_persisted | METADATA | rastro |
| migration.*, technical_state.* | METADATA | rastro de importação |

### 4.3 account_information_addresses (1.606) → `cooperative_members.address_*`
| origem | fill | destino | transform |
|---|---:|---|---|
| line1 | 100% | members.address_street | logradouro |
| line2 | 65,1% | members.address_complement | complemento |
| line3 | 99,1% | members.address_neighborhood | **BAIRRO** (o ETL antigo ignorava → neighborhood nil) |
| number | 99,9% | members.address_number | 0→"S/N" |
| city | 100% | members.address_city | direto |
| state | 100% | members.address_state | UF (2) |
| postal_code | 100% | members.address_zip | left-pad 8 |
| country | 100% | METADATA | quase sempre BRASIL |
| type, residence_type, is_default, is_billing | 100% | METADATA | tipo de endereço/residência |
| address_verified_at, deleted_at | 0% | VAZIA | — |
| tenant_id | 100% | N/A-TENANT | — |

### 4.4 accounts (1.606) → `accounts`
| origem | fill | destino | transform |
|---|---:|---|---|
| id | 100% | metadata.autbank.source_account_id | rastro |
| user_id, account_information_id | 100% | (join) | liga user/member |
| number | 100% | **DECISÃO** (ver 6.3) | legada 000000003… |
| agency (=19) | 100% | **DECISÃO** / metadata | agência legada única 19 |
| digit | 100% | **DECISÃO** / metadata | DV legado |
| name | 100% | accounts.friendly_name/member.name | direto |
| document | 100% | (=member cpf_cnpj) | consistência |
| type (=1) | 100% | accounts.account_type='payment' | conta de pagamento SCD (kind=1) |
| status active/inactive | 100% | accounts.status | active/inactive; BLQ→ver restrições |
| currency (BRL) | 100% | fixo BRL | — |
| transfer_approval_mode/required_approvals | 100% | accounts.transfer_* | direto |
| metadata (json, 11 chaves autbank_*) | 100% | accounts.metadata | rastro (autbank_account_number/situation/cod_*/wallet_snapshot/migration_*) |
| external_id, transfer_approval_amount_threshold, deleted_at | 0% | VAZIA | — |
| tenant_id | 100% | N/A-TENANT | — |

### 4.5 account_wallets (1.606) → TigerBeetle + `account_entries`
| origem | fill | destino | transform |
|---|---:|---|---|
| balance | 100% | saldo TB (autoridade) | to_base_units; abertura-plug no extrato |
| balance_available | 100% | derivado (available = balance − blocked) | ver 6.2 |
| blocked_amount | 100% (R$523.174,21) | **DECISÃO** hold/retenção | ver 6.2 |
| scheduled_amount (0) | 100% | VAZIA | tudo zero |
| account_id | 100% | (join) | — |
| name, type(1), currency(BRL), status(true) | 100% | METADATA/fixo | uniforme |
| id | 100% | metadata.autbank.wallet_id | rastro |
| deleted_at | 0% | VAZIA | — |
| tenant_id | 100% | N/A-TENANT | — |

### 4.6 autbank_legacy_transactions (231.350) → `account_entries` (extrato) + COSIF
| origem | fill | destino | transform |
|---|---:|---|---|
| posted_at | 100% | account_entries.entry_date/value_date | data real do lançamento |
| type credit/debit | 100% | sinal do amount | + / − |
| amount | 100% | account_entries.amount | to_base_units, com sinal |
| description | 100% | account_entries.description | rótulo humano (+ contraparte) |
| complement | 93,9% | account_entries.notes | complemento |
| history_code | 100% | account_entries.category + COSIF | tabela seção 2 |
| counterparty (json) | 100% | account_entries.metadata.counterparty | nome/banco/agência/conta/doc |
| document_number | 100% | account_entries.reference (parte) | nº documento legado |
| legacy_transaction_id | 100% | account_entries.reference | idempotência |
| balance_before/after | 100% | account_entries.metadata (auditoria) + reconciliação | prova do saldo corrido |
| transfer_transaction_id | 92,1% | (join transfer_transactions) | enriquecimento |
| metadata, raw_payload (json) | 100% | account_entries.metadata.raw | rastro fiel |
| source(autbank), batch_id, account_number, created_at/updated_at | 100% | METADATA | rastro |
| tenant_id | 100% | N/A-TENANT | — |

### 4.7 transfer_transactions (212.987) → enriquecimento de `account_entries`
Fonte da contraparte e da classificação para PIX/TED/interna. Colunas preenchidas: transaction_type (0 pix/1 ted/3 interna), type, amount, description, client_description (100%), name/document/bank_number/account_number/account_digit/account_branch da contraparte (91-99,8%), nsu (100%), fee/interest_amount/penalty_amount (100%), details (json, com autbank_classification), status (approved).
| origem | destino | transform |
|---|---|---|
| transaction_type, type | account_entries.metadata.method (pix/ted/internal) | classificação |
| name, document, bank_number, account_*, account_branch | account_entries.metadata.counterparty | contraparte do extrato |
| fee, interest_amount, penalty_amount | lançamentos próprios (00903/00902/00802) | já vêm como movimento no razão; aqui só cross-check |
| nsu, external_id, transaction_code, client_description | account_entries.metadata | rastro |
| details (json) | account_entries.metadata.transfer_details | autbank_classification + raw |
| pix_key (0%), movement_code (0%), operation_number (0%), control_number (0%), ticket (0%), identifier (0%), refunded_* (0%), idempotency_* (0%), scheduled_at (0%), approval_* (quase 0), bank_ispb (28) | VAZIA/METADATA | sem dado (chaves PIX não vêm no backup) |
| tenant_id | N/A-TENANT | — |

### 4.8 account_restrictions (9) → **DECISÃO** (ver 6.1)
Todas: type=`block_outgoing_transactions`, reason="Autbank account situation BLQ", created_by=autbank-migration, metadata com account_situation=BLQ. São bloqueios administrativos de saída (não judiciais). Colunas expires_at/revoked_at = 0%.

### 4.9 autbank_transaction_import_items (10.500) → METADATA/auditoria
Log de auditoria da importação legada (batch_id, account_id, date_from/to, mode dry-run/apply, status, transactions_found/valid/imported/failed, started/finished_at). Não é dado de cliente nem de dinheiro. Destino: preservar como auditoria (tabela de auditoria de ETL ou arquivo de evidência), sem entrar no razão. error_message só 1 linha preenchida.

## 5. Cobertura 100% — resumo por status

| status | colunas | observação |
|---|---:|---|
| Migrada (destino existente, JÁ-OK ou POPULAR) | núcleo de cadastro, conta, saldo, extrato, COSIF | inclui os campos que o ETL antigo deixava nil |
| Preservada em metadata (rastro fiel) | JSON data/metadata/counterparty/details + campos operacionais legados | nada some |
| Nova coluna (dado real, sem casa) | doc_issuer_state, marital_status, father_name, annual_revenue | + ajuste da tela de cadastro |
| Vazia na origem (0%, nada a migrar) | cônjuge, naturalidade, escolaridade, inscrições, arquivos de doc, chaves PIX, campos de aprovação/idempotência | documentado, provado 0% |
| N/A multi-tenant | tenant_id (10 tabelas) + FKs órfãs de tenants | instituição única |

## 6. Decisões (respostas do dono em 2026-07-08)

### 6.1 + 6.2 Bloqueios (DECIDIDO: TB hold + status + metadata)
Decisão do dono: (a) os R$ 523.174,21 de fundos retidos (5 contas) viram **hold pending no TigerBeetle**, reduzindo o disponível (é como o `BlockedBreakdown` do Core lê bloqueio) — o saldo total (autoridade) é migrado inalterado, disponível = saldo − bloqueado; (b) os 9 bloqueios administrativos BLQ viram **accounts.status bloqueada + registro fiel em metadata**. Sem tabela nova. A natureza escrow/dep-vista é refletida pela modalidade (6.4). Rastreabilidade: cada hold em TB carrega `user_data` da conta; o registro administrativo fica em `accounts.metadata.autbank` (situação BLQ, reason, created_by, data).

### 6.3 Numeração de conta — manter sequencial + IBAN, agência 0001
Decisão do dono: em SCD só existe **agência 0001**. **Manter o número de conta sequencial legado como está** (o backup já traz 946 contas 0000… e 660 contas 0526…, com dígito) e **aplicar o padrão IBAN Monetarie junto na migração**, com a **mesma lógica para contas novas**. Ou seja: agency=0001, account_number = número legado preservado, digit = dígito legado, e gerar o IBAN. (Correção da recomendação anterior de "numeração nova".)

### 6.4 Modalidade da conta (escrow / dep à vista / conta pagamento) — só no CSV
O backup traz `accounts.type`=1 para todas; a modalidade real (CONTA DE PAGAMENTO PF/PJ, CTA ESCROW VINCULADA PJ, DEP VISTA) só existe no relatório oficial. Proposta: derivar a modalidade do CSV para `accounts.account_type`/`product_id` (escrow vinculada, dep à vista, pagamento PF/PJ), para a conta nascer com a natureza certa. Confirmar o mapeamento de produto.

### 6.5 5 contas do CSV fora do backup (saldo 0)
Contas PF ativas abertas após o backup (saldo 0). Proposta: criar as 5 como contas ativas de saldo zero (a partir do CSV) para paridade total com o relatório oficial, ou deixar de fora (sem dado no backup). Confirmar.

## 7. Status de implementação (branch `etl/core-monbank-0708`)

FEITO (código, compila limpo, sem warning nos arquivos alterados):
1. Migration aditiva das 4 colunas com dado real sem casa: `marital_status`, `father_name`, `doc_issuer_state`, `annual_revenue` (`20260708200000_add_autbank_kyc_fields_to_members.exs`) + schema `Member` (cast).
2. `Source.src_holders` estendido: KYC completo, endereço com `line3`, numeração legada número+dígito, split de saldo, `data.autbank`.
3. `Loader.Member` populando TODOS os campos (birth_date, mother_name, father_name, marital_status, occupation, is_pep de politically_exposed, monthly_income coluna/JSON, RG->doc_*, bairro line3, cnae/capital). Prova de projeção no fill-rate real.
4. Numeração: `OpenAccount` agora preserva número+dígito legado e gera IBAN (`AccountNumberGenerator`), com agência 0001; passa a gerar IBAN também para contas NOVAS (mesma lógica). `Loader.Customer` e `Remigrate.add_account` (via `open_additional_account`) usam a numeração legada.
5. Extrato: descritivo enriquecido com a contraparte (`entry_label`) + metadata fiel (contraparte/banco/agência/conta/documento, complemento, history_code) em `account_entries.metadata`.
6. Tela de cadastro (admin `ClientProfileTab.vue` + `useClients.ts`): expõe os 4 campos novos (Select de estado civil e UF do RG, faturamento). Persistência real pelo endpoint de cooperative members (`Member.update_changeset`, já com os 4 campos); a aba de perfil herda um gap pré-existente de round-trip que vale endereçar à parte.

NÃO migrar como representante legal: `company_representative_*` provou-se auto-duplicata do titular PJ (name==name, cpf==document em 897/897) — documentado, sem criar `member_legal_representatives`.

PENDENTE (a executar no run em HML, com verificação):
1. Bloqueio de fundos (`blocked_amount` R$ 523.174,21, 5 contas): hold pending no TigerBeetle para refletir disponível = saldo − bloqueado. Requer mecanismo de hold no boundary do Wallet (hoje só deposit/withdraw) + verificação contra o TB — por isso é passo verificado no run, não código não testado. BLQ administrativo já sai como `accounts.status` bloqueada + metadata.
2. Modalidade fina (escrow/dep-vista/pagamento) em `account_type`: vem do relatório oficial (CSV), não do backup — carregar o CSV como fonte auxiliar do ETL.
3. As 5 contas do CSV sem backup (saldo 0): criar para paridade, se o dono quiser.
4. Snapshot pré-ETL do mon_core + `Remigrate.run(reset: true)` + reconciliação ao centavo contra o CSV oficial (R$ 719.303,44; bloqueado 523.174,21 conta a conta) com prova SQL == código == telas.
5. Re-provisionar Partner API keys (toda re-migração do Core zera `api_keys`) — `scripts/partner_api_reprovision.sh`.
