Pular para o conteúdo principal

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):

TabelaGrãoPra quê
belite_totaismes × fabricante × loja × grupo × tipoTotais crus. O front faz as contas e agrega conforme o filtro.
belite_benchmarkmes × marca_benchmark × metricaA 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).

ColunaTipoO que é
mesdate1º dia do mês
fabricantetextmarca real do PDV (pode ser null = sem marca)
lojatextponto de venda
grupotextgrupo econômico
tipotextN (novos) ou S (seminovos)
marca_benchmarktextchave pra cruzar com a régua: = fabricante se N; = 'Multimarca Seminovos' se S
sum_vrfloatΣ rentabilidade (numerador do RPUF)
sum_qtdintΣ contratos financiados faturados
sum_finfloatΣ valor financiado
sum_retornofloatΣ retorno (RetornoFinanceira)
qtd_com_produtointcontratos com produto agregado (seguro/garantia)
entraramintclientes que entraram (id_inicial_agrupamento)
aprovadosintclientes com pelo menos 1 aprovação
faturadosintclientes com pelo menos 1 faturamento
perdidosintaprovados 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. faturados conta clientes (agrupamentos), não é a mesma coisa. No funil, "Clientes"/"Aprovados" usam entraram/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).

ColunaO que é
mes, marca_benchmarkchave (cruza com belite_totais.marca_benchmark)
metricaum de: rpuf, ticket, retorno_pct, penetracao_pct, conversao_pct, taxa_perdidas_pct
p10 … p99percentis da distribuição nacional de grupos
p50mediana = referência "Brasil"
p90referência "Elite"
mediamédia simples entre grupos
maximomaior valor de grupo
qtd_gruposquantos 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_benchmark onde marca_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 pela marca_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

ComponenteDe onde vemObservação
Hero — RPUF do grupobelite_totais (Σvr/Σqtd)ok
Hero — posição (percentil) + P10-P90belite_benchmark (rpuf)régua por marca_benchmark; Fabricante=Todas usa '(Todas)' (ver §3)
KPI Ticket Médiototais + benchmark ticket (Brasil/Elite)ok
KPI Money Losttotais: 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_pcta métrica virou retorno médio % (não R$ absoluto)
KPI "Rentab. Produto" → Penetração %totais (Σcom_produto/Σqtd) + benchmark penetracao_pctvirou penetração produto %
Funil — Clientes/Aprovados/Contratostotais: entraram / aprovados / sum_qtdClientes/Aprovados = clientes (agrupamentos); Contratos = sum_qtd (fichas), pra bater com o Monitor F&I — ver Pendência #2
Funil — taxasderivadas (aprovação, conversão, fechamento)conversão = Σ sum_qtd / Σ entraram
Funil — Elite das taxasbenchmark 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çãosérie dos 7 meses (totais por mês) + benchmark por mêsGrupo × 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 o benchmark_todos_grupos.xlsx cell-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 mes convivem as duas — o money_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)

  1. Região: não existe no Fandi ainda. Hero "% vs Região", e as séries mediaRegiao/eliteRegiao da Visão Temporal não têm dado — usar só Brasil/Elite por enquanto.
  2. 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 usa count(DISTINCT cliente_sk) na fandi_fat_envio (quem teve envio). Jul/2026 Caminho: 730 vs 846. São universos diferentes — ou o time de dados repopula o entraram com a definição do Monitor, ou o funil passa a ler a fandi_fat_envio.
  3. Funil — "Contratos" diverge em 1 contrato FORD. sum_qtd (349) vs Monitor F&I (350). Todas as marcas batem exato, exceto FORD: belite_totais 87 (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.
  4. Taxa de perdidas — denominador: hoje = perdidos/aprovados. Se o negócio quiser perdidos/entraram, é trivial recomputar a régua.
  5. 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.
  6. 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.