Admin › Financeiro — UI
Status: produção (Fase D do roadmap de billing). Rota interna: admin-financeiro (sidebar do admin). Backend: edge function admin-financeiro (13 actions agrupadas por tab). Permissão: admin_financeiro (papel administrador tem bypass total). Feature flag: admin_financeiro (gate backend simétrico).
Estrutura
src/components/admin-financeiro/
├── index.tsx # Tabs orchestrator + toggle "incluir teste"
├── kpi-cards.tsx # 6 cards (Visão Geral)
├── vencidas-table.tsx # Tabela de vencidas + bulk send (Visão Geral)
├── cobrancas-tab.tsx # Visão por escola + drawer (Cobranças)
├── templates-tab.tsx # CRUD de templates (Templates)
├── automacao-tab.tsx # Status read-only dos cron jobs (Automação)
├── configuracoes-tab.tsx # Toggle excluir_financeiro por escola (Configurações)
├── escola-drawer.tsx # Drawer compartilhado: gestor + faturas + histórico
├── enviar-cobranca-dialog.tsx # Envio individual (templates fixos + manuais)
├── historico-cobrancas-modal.tsx # Histórico por fatura (sem PII)
└── use-template-options.ts # Helper que mescla fixos + manuais ativosHook único: src/hooks/useAdminFinanceiro.ts — wrappers React Query para todas as actions.
Tabs
1. Visão Geral
- 6 KPIs (
dashboard_kpis) + tabela paginada de faturas vencidas (list_vencidas). - Toggle global "Incluir escolas de teste" (default OFF) propaga
incluir_testepara queryKey. - Ações por linha: Histórico, Enviar cobrança individual, seleção em lote → bulk.
2. Cobranças
- Agregação por escola (
list_cobrancas_overview) com filtro por status (vencida / a vencer / todas) e busca por nome/CNPJ. - Click na linha → abre
EscolaDrawer(lateral) com:- Header: nome, CNPJ, status_assinatura, badge "excluída do financeiro" se aplicável.
- Bloco Gestor: nome / WhatsApp / e-mail (papel
escola). - Bloco Faturas: últimas 20 com ações (histórico + enviar).
- Bloco Cobranças: últimas 50 (canal, slug, origem, status).
3. Faturas
- Tab dedicada renderizando
<AdminFaturas />(consolidação 2026-05: a antiga seção de menuadmin-faturasfoi unificada aqui). - Listagem paginada com filtros (status, escola, plano), criação manual, edição, cancelamento.
- Backend: edge function
admin-faturas(mantida; feature flagadmin_faturassegue gateando server-side). - Deep-links antigos
activeSection === 'admin-faturas'redirecionam paraadmin-financeirona abafaturas(viasessionStorage.admin_financeiro_return).
4. Templates
- Lista os 4 slugs fixos (
fixo_regua) e os manuais (manual_*). - Editor permite alterar nome + corpo (e
ativoapenas para manuais). - Trigger
fn_cobranca_templates_protect_fixosimpede archive/rename/inativação dos fixos. - Variáveis suportadas:
{{escola_nome}},{{numero_fatura}},{{valor}},{{vencimento}},{{dias_atraso}}. Vercobrancas-templates.md.
5. Emails
- Aba dedicada para gestão de templates de e-mail transacional (Resend).
6. Configurações
- Lista de escolas (
list_escolas_financeiro) com toggleexcluir_financeiro. - Escolas com
status_assinatura='trial'aparecem com toggle desabilitado (auto-excluídas). - Toggle manual logado em
faturamento.escola_excluir_financeiro.
KPIs (Visão Geral)
| Card | Fonte | Fórmula |
|---|---|---|
| A receber (próx. 30d) | a_receber_30d | Σ(valor − desconto + taxas) onde status=pendente e vencimento ∈ [hoje, hoje+30d] |
| Recebido (últ. 30d) | recebido_ultimos_30d | Σ(...) onde status=paga e pago_em ≥ hoje−30d |
| Vencidas | vencidas_total + vencidas_valor | count e Σ onde status=vencido |
| Ticket médio | ticket_medio | recebido_30d / nº faturas pagas no período |
| Taxa inadimplência | taxa_inadimplencia | vencidas / (vencidas + recebidas_30d) |
| Saúde da carteira | derivada | <5% Boa · <15% Atenção · ≥15% Crítica |
Todas as queries respeitam incluir_teste: quando false, escolas com status_assinatura='trial' OU excluir_financeiro=true são removidas da agregação.
Envio de cobrança
- Individual (
enviar_cobranca_manual):skipDelay=true, resposta síncrona comcobranca_id. - Bulk (
enviar_cobranca_bulk, ≥1 selecionada): retorna202 + jobId, processa emEdgeRuntime.waitUntilcom 6s entre mensagens (Wasender). Resultado consolidado emfaturamento.cobranca_envio_bulk. - Slugs disponíveis: 4 fixos da régua + manuais ativos (
useTemplateOptions).
Régua automática (referência cruzada)
Cron regua-cobranca-diario (09:00 BRT) e marcar-vencidas (03:00 BRT). Ver docs/operations/CRON_JOBS.md.
Logs (LOG_ACTION_OPTIONS — SSOT)
faturamento.cobranca_envio_manualfaturamento.cobranca_envio_bulkfaturamento.cobranca_falhafaturamento.template_criado/template_atualizado/template_arquivadofaturamento.escola_excluir_financeiro
Out of scope (próximas fases)
- Fase E: gate de bloqueio por inadimplência (exige ADR).
- Botão "Forçar execução agora" na tab Automação.
- Deep-link
?tab=para tabs. - Polling de progresso do bulk (hoje: usar Logs).
- Agendamento de envio futuro / segmentos.
Integração com o AdminDashboard
A tela inicial do admin (AdminDashboard) renderiza a seção Saúde Financeira (src/components/admin-dashboard/financial-health-section.tsx) reaproveitando os mesmos hooks (useDashboardKpis, useListVencidas):
- 4 mini-cards: A receber (30d), Vencidas (qtd + valor), Ticket médio, Inadimplência (com badge tonal: <5% saudável, <15% atenção, ≥15% crítica).
- Mini-tabela "Top 5 vencidas" com badge de tier (D+1 / D+7 / D+15).
- Cards e linhas são clicáveis e levam para o módulo
admin-financeiro.
Sem chamadas extras: o cache de 60s no dashboard_kpis é compartilhado entre dashboard e tela detalhada.
Contrato taxa_inadimplencia (CRÍTICO)
dashboard_kpis.taxa_inadimplencia é retornado como fração 0–1 (ex.: 1.0 = 100%, 0.05 = 5%).
Todo consumidor DEVE multiplicar por 100 antes de:
- Exibir como percentual na UI (
(v * 100).toFixed(1) + '%'). - Comparar contra os limites de tier (
<5saudável,<15atenção,≥15crítica — em pontos percentuais).
Bug histórico: financial-health-section.tsx exibia 1.0% Saudável enquanto kpi-cards.tsx exibia 100% Crítica sobre o mesmo dado, porque o primeiro consumidor esquecia o *100. Não introduzir formatação de inadimplência sem aplicar essa regra.
UI/UX de produção (entrega 2026-05)
Drawer da escola (escola-drawer.tsx)
- Header em card (
bg-muted/30 rounded-lg p-4) com nome / CNPJ / status. - Grid 3-col com contatos do gestor (nome, WhatsApp, e-mail).
- Botão "Histórico de cobranças da escola" no canto superior direito → abre
Sheet(não modal). - Faturas ordenadas por
vencimento_em ASC(backend). - Sem rodapé de "últimas cobranças": substituído pelo Sheet.
Histórico (historico-cobrancas-modal.tsx)
Sheet(não modal). Lista de cards (1 por envio) com badge de status, canal, slug, origem e mensagem de erro quando aplicável.- Datas exibidas via
src/lib/format-datetime-brt.ts(Intl +America/Sao_Paulo).
Aba Cobranças (cobrancas-tab.tsx)
- Substitui lista de escolas por grid 2×2 de KPIs: Faturas, Vencidas, Atraso máximo (dias), Valor em aberto. Cores por severidade.
- Busca debounced (350ms), paginação server-side via
q/page/pageSize.
Aba Templates (templates-tab.tsx)
- Ordem fixa: Manuais primeiro, Régua depois (regras fixas abaixo dos manuais).
Paginação e busca server-side
- Edge
admin-financeiroaceitaq(ilike) +page+pageSizeemlist_vencidas,list_cobrancas_overview,list_escolas_financeiro. - Frontend usa
src/hooks/use-debounced-value.ts(350ms) e propaga em queryKeys.
Helper de data BRT (frontend)
src/lib/format-datetime-brt.tsusaIntl.DateTimeFormat('pt-BR', { timeZone: 'America/Sao_Paulo' })para exibirtimestamptzUTC sem o bug "21h vira amanhã".- Não duplica
_shared/date-brt.ts(que permanece edge-only para comparações de dia).