Dashboard Elite — como usar as tabelas do benchmark
Data: 2026-08-08
Para: time que vai montar a tela do Dashboard Elite (apps/web/.../belite/home/[account]/dashboard/).
O que mudou: os dados existem no Supabase e o front já está plugado (o mock foi removido). Este doc descreve as tabelas e as regras de derivação.
TL;DR
Duas tabelas no Supabase, schema dw_concessionarias_mart (mesmo schema das fandi_kpi_*, RLS off por design — leia com a service role/conexão do backend, igual os outros dashboards de mart):
| Tabela | Grão | Pra quê |
|---|---|---|
belite_totais | mes × fabricante × loja × grupo × tipo | Totais crus. O front faz as contas e agrega conforme o filtro. |
belite_benchmark | mes × marca_benchmark × metrica | A régua: percentis entre grupos da marca (Brasil, Elite, P10–P90). |
Regra de ouro: o mart não entrega nada calculado (nem RPUF, nem taxa, nem %). Ele entrega totais (belite_totais) e a distribuição de mercado (belite_benchmark). Todo KPI, taxa, posição e "money lost" o front deriva. Isso é de propósito: só assim o número do grupo bate quando o usuário filtra 1 loja, 3 lojas ou tudo.
Cobertura atual: jan–jul/2026, eixo marca (região ainda não — ver Pendências).
1. belite_totais — os totais crus
Uma linha por mês × fabricante × loja × grupo × tipo (tipo = N novos / S seminovos).
| Coluna | Tipo | O que é |
|---|---|---|
mes | date | 1º dia do mês |
fabricante | text | marca real do PDV (pode ser null = sem marca) |
loja | text | ponto de venda |
grupo | text | grupo econômico |
tipo | text | N (novos) ou S (seminovos) |
marca_benchmark | text | chave pra cruzar com a régua: = fabricante se N; = 'Multimarca Seminovos' se S |
sum_vr | float | Σ rentabilidade (numerador do RPUF) |
sum_qtd | int | Σ contratos financiados faturados |
sum_fin | float | Σ valor financiado |
sum_retorno | float | Σ retorno (RetornoFinanceira) |
qtd_com_produto | int | contratos com produto agregado (seguro/garantia) |
entraram | int | clientes que entraram (id_inicial_agrupamento) |
aprovados | int | clientes com pelo menos 1 aprovação |
faturados | int | clientes com pelo menos 1 faturamento |
perdidos | int | aprovados que não faturaram |
Como derivar cada número do dashboard
Sempre soma os totais do recorte filtrado e só depois divide (nunca faça média de médias):
RPUF = Σ sum_vr / Σ sum_qtd
Ticket médio = Σ sum_fin / Σ sum_qtd
Retorno médio % = Σ sum_retorno / Σ sum_fin × 100
Penetração produto %= Σ qtd_com_produto / Σ sum_qtd × 100
Taxa de conversão % = Σ faturados / Σ entraram × 100
Taxa de perdidas % = Σ perdidos / Σ aprovados × 100
Taxa de aprovação % = Σ aprovados / Σ entraram × 100
Money Lost (R$) = Σ perdidos × RPUF(do mesmo recorte)
Contratos (tabela de benchmark, funil "Contratos" e "fechamento") =
Σ sum_qtd(fichas faturadas) — bate com o Monitor F&I.faturadosconta clientes (agrupamentos), não é a mesma coisa. No funil, "Clientes"/"Aprovados" usamentraram/aprovados; a "conversão" =Σ sum_qtd / Σ entraram.
2. belite_benchmark — a régua (Brasil / Elite / percentis)
Uma linha por mês × marca_benchmark × metrica. É a distribuição do valor da métrica entre os grupos que operam aquela marca no mês (cada grupo pesa 1).
| Coluna | O que é |
|---|---|
mes, marca_benchmark | chave (cruza com belite_totais.marca_benchmark) |
metrica | um de: rpuf, ticket, retorno_pct, penetracao_pct, conversao_pct, taxa_perdidas_pct |
p10 … p99 | percentis da distribuição nacional de grupos |
p50 | mediana = referência "Brasil" |
p90 | referência "Elite" |
media | média simples entre grupos |
maximo | maior valor de grupo |
qtd_grupos | quantos grupos entraram (leia junto: percentil sobre 4-5 grupos é frágil) |
Brasil = p50, Elite = p90. As unidades batem com as fórmulas da seção 1 (rpuf/ticket em R$, _pct em %).
Posição / percentil do grupo (o gauge do Hero e a coluna "Posição")
Calcule no front: pega o valor da métrica do grupo (seção 1) e interpola na curva p10…p90 da marca_benchmark correspondente. A função pronta (percentileForValue em _lib/dashboard-data.ts) faz a interpolação linear.
3. Lógica de filtro (o drill que a tela precisa)
O filtro global (mês, grupo, fabricante, loja, tipo) vira um WHERE na belite_totais. Some as linhas que sobram e aplique as fórmulas da seção 1.
- Default (1 mês, 1 grupo, todos fabricantes/lojas):
WHERE mes=? AND grupo=?→ soma tudo → números do grupo pra preencher os KPIs/funil. - Filtra 1 loja:
WHERE mes=? AND grupo=? AND loja=?→ números daquela loja → e posiciona essa loja na régua da marca (belite_benchmarkondemarca_benchmark= a marca da loja).
A régua (belite_benchmark) não muda com o filtro de loja — ela é sempre a distribuição nacional entre grupos da marca. O que muda é a entidade que você posiciona nela (grupo inteiro ou 1 loja).
Seminovos: para linhas
tipo='S', cruze pelamarca_benchmark = 'Multimarca Seminovos'(todo seminovo do país disputa uma régua única, independente da marca real).
Fabricante = Todas (IMPORTANTE): quando o filtro de fabricante é "Todas", o RPUF do grupo é o pooled (todas as marcas juntas). Nesse caso cruze com a régua
marca_benchmark = '(Todas)'— a distribuição do RPUF pooled entre os grupos. Nunca posicione o pooled contra a régua de uma marca específica (ex.: a dominante) — isso infla a posição (bug jul/2026: Caminho aparecia P75 quando o correto é ~P27). A régua'(Todas)'existe para as 6 métricas.
4. Mapa: componente da tela → dado
| Componente | De onde vem | Observação |
|---|---|---|
| Hero — RPUF do grupo | belite_totais (Σvr/Σqtd) | ok |
| Hero — posição (percentil) + P10-P90 | belite_benchmark (rpuf) | régua por marca_benchmark; Fabricante=Todas usa '(Todas)' (ver §3) |
| KPI Ticket Médio | totais + benchmark ticket (Brasil/Elite) | ok |
| KPI Money Lost | totais: perdidos × RPUF; benchmark = taxa_perdidas_pct (Brasil/Elite) | valor do card é R$; a referência de mercado é a taxa de perdidas % |
| KPI "Rentab. Total" → Retorno médio % | totais (Σretorno/Σfin) + benchmark retorno_pct | a métrica virou retorno médio % (não R$ absoluto) |
| KPI "Rentab. Produto" → Penetração % | totais (Σcom_produto/Σqtd) + benchmark penetracao_pct | virou penetração produto % |
| Funil — Clientes/Aprovados/Contratos | totais: entraram / aprovados / sum_qtd | Clientes/Aprovados = clientes (agrupamentos); Contratos = sum_qtd (fichas), pra bater com o Monitor F&I — ver Pendência #2 |
| Funil — taxas | derivadas (aprovação, conversão, fechamento) | conversão = Σ sum_qtd / Σ entraram |
| Funil — Elite das taxas | benchmark conversao_pct / taxa_perdidas_pct (p90) | aprovação/fechamento não têm régua própria ainda |
| Tabela Benchmark (Grupo→…) | totais somados por nível + benchmark p10-p90 | "Posição" = percentil na régua da marca |
| Visão Temporal — RPUF / Ticket / Posição | série dos 7 meses (totais por mês) + benchmark por mês | só Grupo × Brasil × Elite; região não (Pendência #2) |
5. Queries de exemplo
KPIs do grupo (default, todos fabricantes/lojas):
SELECT
sum(sum_vr)/nullif(sum(sum_qtd),0) AS rpuf,
sum(sum_fin)/nullif(sum(sum_qtd),0) AS ticket_medio,
100*sum(sum_retorno)/nullif(sum(sum_fin),0) AS retorno_pct,
100*sum(qtd_com_produto)/nullif(sum(sum_qtd),0) AS penetracao_pct,
100*sum(faturados)/nullif(sum(entraram),0) AS conversao_pct,
100*sum(perdidos)/nullif(sum(aprovados),0) AS taxa_perdidas_pct,
sum(perdidos) * (sum(sum_vr)/nullif(sum(sum_qtd),0)) AS money_lost
FROM dw_concessionarias_mart.belite_totais
WHERE mes = '2026-05-01' AND grupo = 'GRUPO CAMINHO';
Régua (Brasil/Elite) de uma marca no mês:
SELECT metrica, p10, p25, p50 AS brasil, p75, p90 AS elite, media, qtd_grupos
FROM dw_concessionarias_mart.belite_benchmark
WHERE mes = '2026-05-01' AND marca_benchmark = 'GWM';
Tabela de benchmark — RPUF por loja de um grupo, com a régua da marca:
SELECT t.loja, t.marca_benchmark,
sum(t.sum_vr)/nullif(sum(t.sum_qtd),0) AS rpuf,
sum(t.sum_qtd) AS contratos,
b.p10, b.p50, b.p90
FROM dw_concessionarias_mart.belite_totais t
JOIN dw_concessionarias_mart.belite_benchmark b
ON b.mes = t.mes AND b.marca_benchmark = t.marca_benchmark AND b.metrica = 'rpuf'
WHERE t.mes = '2026-05-01' AND t.grupo = 'GRUPO CAMINHO'
GROUP BY t.loja, t.marca_benchmark, b.p10, b.p50, b.p90
ORDER BY rpuf DESC;
6. Definições que o front precisa conhecer
- Cliente =
id_inicial_agrupamento(dedup por banco). 10 fichas aprovadas no mesmo agrupamento = 1 aprovado. - Aprovado = agrupamento com ≥1 ficha em situação grupo 60 ou 80. Faturado = ≥1 ficha 80. Perdido = tem 60 e nenhuma 80. Sempre bate:
aprovados = faturados + perdidos. - RPUF usa
valor_rentabilidade(rentabilidade com produto), universo faturado (situação 80, financiadas, novos+seminovos, grupo/loja ativos). Reproduz obenchmark_todos_grupos.xlsxcell-for-cell. - Base de data: métricas de faturamento (RPUF/ticket/retorno/penetração) são por mês de faturamento; funil (entraram/aprovados/faturados/perdidos) é por mês de entrada. No mesmo
mesconvivem as duas — omoney_losté uma aproximação (perdidos do mês de entrada × RPUF do mês de faturamento).
7. Pendências (o que ainda NÃO dá pra fazer)
- Região: não existe no Fandi ainda. Hero "% vs Região", e as séries
mediaRegiao/eliteRegiaoda Visão Temporal não têm dado — usar só Brasil/Elite por enquanto. - Funil — "Clientes" não bate com o Monitor F&I. O funil usa
entraram(id_inicial_agrupamento, dedup por banco), mas o Monitor F&I usacount(DISTINCT cliente_sk)nafandi_fat_envio(quem teve envio). Jul/2026 Caminho: 730 vs 846. São universos diferentes — ou o time de dados repopula oentraramcom a definição do Monitor, ou o funil passa a ler afandi_fat_envio. - Funil — "Contratos" diverge em 1 contrato FORD.
sum_qtd(349) vs Monitor F&I (350). Todas as marcas batem exato, exceto FORD:belite_totais87 (74 N + 13 S) vs FANDI 88 (57 NOVOS + 13 SEMINOVOS + 18 VENDAS DIRETA). Um contrato FORD caiu em classificação/tipo diferente entre as marts — reconciliação de ETL. - Taxa de perdidas — denominador: hoje =
perdidos/aprovados. Se o negócio quiserperdidos/entraram, é trivial recomputar a régua. - Aprovação/Fechamento do funil não têm régua Elite própria — só conversão e taxa de perdidas têm benchmark. Se precisar, dá pra adicionar.
- Histórico só jan–jul/2026. A Visão Temporal tinha 17 meses no mock (removido); hoje temos 7. Backfill de 2025 é possível quando pedirem.
8. Conexão
Schema dw_concessionarias_mart (RLS off, padrão dos marts). Ler com a mesma conexão de backend que já lê fandi_kpi_*/kpi_onepage_dia. As tabelas são recarregadas por mês (idempotente) — não cachear valores de meses fechados por muito tempo se o pipeline reprocessar.
Nota de arquitetura: esta é a carga direta (Postgres→Supabase) pra destravar a tela. A carga definitiva (medallion: Silver/Gold → mart → sync) vai repopular as mesmas tabelas/colunas — o front não muda nada quando isso acontecer.