# Padronização de unidade monetária — CENTAVOS Implementation Plan

> **For Claude:** REQUIRED SUB-SKILL: usar superpowers:executing-plans / subagent-driven-development. Mudança backend+front é ATÔMICA por app (deploy junto), senão quebra a tela.

**Goal:** Toda resposta de API que carrega dinheiro devolve CENTAVOS (BRL×100); todo formatador de moeda do front espera centavos (÷100). Acaba com os 37 bugs unit_100x e a inconsistência de formatters.

**Architecture:** Centavos é a unidade canônica na fronteira da API. (1) Front: o `moneyUnit.ts` (vendado nos 3 apps + shared) passa a dividir por 100, não 10000; os `formatCurrency` locais (admin `lib/format.ts`, banking `lib/formatters.ts`) já dividem por 100 (ficam canônicos). (2) Backend: remover `MoneySerializer.to_base()` (×100) das respostas v2/partner (passam a devolver centavos direto), e converter os controllers que devolvem TigerBeetle cru (subcentavos) para centavos (÷100). Mudança por app, backend+front juntos.

**Tech Stack:** Elixir/Phoenix (controllers), Vue 3 (formatters), Vitest/ExUnit (testes), buildx ARM64 + ECS deploy.

**Fonte:** `docs/reports/2026-06-26-core-money-bug-sweep.md` (94 achados) + work-list da varredura.

---

## Invariantes
- TigerBeetle (`Wallet.get_balance`, `lookup_account` credits-debits) e `account_entries.amount` = SUBCENTAVOS (×10000). Converter p/ centavos = `div(x, 100)` (`subcent_to_cents_signed`).
- cosif_journal_entries / cosif_account_balances = CENTAVOS já.
- Front canônico: `formatCurrency(centavos) => centavos/100`.

## Fase 1 — Backend: tudo devolve centavos

### Task 1.1: account_controller.ex (admin /v1/accounts)
- Modify: `controllers/account_controller.ex:487-505` (`fetch_tigerbeetle_balance`): converter `balance/credits_posted/debits_posted/credits_pending/debits_pending` com `div(_, 100)`.
- Teste: `GET /api/v1/accounts/:id` → balance em centavos (bate com account_entries/100).

### Task 1.2: wallet_controller.ex
- Modify: `controllers/wallet_controller.ex` (`balance`, `serialize_account`): TB subcent → `div(_, 100)`.

### Task 1.3: v2/account_controller, v2/transfer_controller, v2/pix_controller, v2/merchant_portal_controller
- Remover `MoneySerializer.to_base(...)` das RESPOSTAS (devolver centavos direto). Garantir que a fonte (fetch_tigerbeetle_balance) já está em centavos.
- ATENÇÃO: hoje esses pareiam `to_base` (×100) com `formatBrl` (÷10000) no front e ACERTAM por acaso. Remover o `to_base` SÓ junto da Fase 2 do app correspondente (banking/merchant), no mesmo deploy.

### Task 1.4: partner_v1/accounts_controller, partner_v1/transfers_controller
- Idem: remover `to_base`, devolver centavos. (Partner API — confirmar consumidor.)

### Task 1.5: transactions_controller / netting_controller / fees_controller
- `empty_transactions`: trocar leitura de `transactions` por `account_entries` onde for extrato/saldo; netting/fees são métricas operacionais (legitimamente vazias p/ base migrada — documentar, não forçar). Onde devolver dinheiro: centavos.

## Fase 2 — Front: todo formatter espera centavos

### Task 2.1: moneyUnit.ts (shared + 3 cópias vendadas)
- Modify: `core/packages/shared/src/utils/money.ts` e `core/apps/{admin,banking,merchant}/src/shared/utils/moneyUnit.ts`:
  - `formatBrl(v) = v/100` (era `v/(100*SCALE)` = /10000). `formatBalance/formatAuto` idem. `fromCents(c) = formatBrl(c)` (sem ×SCALE).
- Teste Vitest: `formatBrl(100000) === "R$ 1.000,00"`.

### Task 2.2: admin app
- `lib/format.ts:formatCurrency` já ÷100 (canônico). Substituir usos de `moneyUnit.formatBrl/Balance/Auto` por `formatCurrency` OU confiar na Fase 2.1.
- Backend admin já em centavos (Fase 1.1/1.2 + os já corrigidos). Deploy admin-ui + core-api.

### Task 2.3: banking app (CRÍTICO — atômico)
- `lib/formatters.ts:formatCurrency` já ÷100. Aplicar Fase 2.1 (moneyUnit ÷100).
- No MESMO deploy: remover `to_base` do v2/account+transfer (Fase 1.3). Testar saldo + extrato + transferência na tela.

### Task 2.4: merchant app (atômico)
- moneyUnit ÷100 (Fase 2.1) + remover `to_base` do v2/merchant_portal (Fase 1.3) no mesmo deploy. Testar dashboard + saldo.

## Fase 3 — Limpeza
- Remover `MoneySerializer` se ninguém mais usa `to_base` em resposta. Manter `from_base` (entrada). Lint/teste: nenhum campo de dinheiro em subcentavos cru na resposta.

## Fase 4 — Validação por tela (print)
- Para cada app: saldo, extrato, transferência/movimento, dashboard. Conferir valor real (sem 100x p/ mais ou p/ menos).

---

## Sequência de deploy (segura)
1. Backend admin (centavos) + admin-ui (Fase 2.2) — admin já está quase tudo centavos.
2. Banking: core-api (remove to_base v2/account+transfer) + banking-ui (moneyUnit ÷100) JUNTOS.
3. Merchant: core-api (remove to_base v2/merchant) + merchant-ui JUNTOS.
4. Partner API à parte (confirmar consumidor antes).

## Risco
- Nunca mudar só um lado (backend OU front) de banking/merchant — os dois pareiam hoje. Mudar atômico ou a tela fica 100x errada.
