Vendas e Comercial
Painel Comercial de uma rede varejista com 42 lojas
Contexto
Rede de varejo alimentar com 42 lojas em 6 estados, 18 mil SKUs e 210 vendedores. A diretoria comercial recebia hoje três planilhas diferentes por e-mail toda segunda-feira, com números que não fechavam entre si. O objetivo é uma fonte única, atualizada diariamente, que responda em 10 segundos se o mês está no ritmo da meta e onde está o problema.
Perguntas que o painel deve responder
- Estamos no ritmo da meta do mês, por loja e por vendedor?
- O crescimento vem de mais clientes, mais itens ou preço maior?
- Quais produtos estão puxando a margem para baixo?
- Quais lojas caíram em relação ao mesmo período do ano passado, com dias comparáveis?
- Quais clientes de atacado reduziram compra nos últimos 90 dias?
Indicadores e metas
| Indicador | Meta | Por que importa |
|---|---|---|
| Receita Líquida | R$ 48 mi/mês | Indicador-mestre; exclui devoluções e impostos sobre venda |
| Atingimento Proporcional | ≥ 100% | Mede o ritmo dentro do mês, sem falso alarme no dia 5 |
| Margem Bruta % | ≥ 24% | Crescer vendendo com prejuízo não é crescimento |
| Ticket Médio | R$ 87 | Mostra se o crescimento vem de mais clientes ou de compras maiores |
| Itens por Cupom | ≥ 5,2 | Mede a eficácia de cross-sell e da exposição em loja |
| Ruptura de Gôndola % | ≤ 3% | Venda perdida não aparece no faturamento — só aqui |
| Curva ABC de Produtos | A ≤ 20% dos SKUs | Concentra atenção no que realmente gera resultado |
Modelo de dados
Esquema estrela clássico: uma fato de vendas na granularidade item do cupom, e dimensões de calendário, loja, produto, vendedor e cliente. A tabela de metas fica separada, em granularidade mês × loja × vendedor.
| Tabela | Tipo | Granularidade | Volume |
|---|---|---|---|
| fVendas | Fato | Item do cupom | ~ 62 mi linhas/ano |
| fMetas | Fato | Mês × Loja × Vendedor | ~ 2,5 mil linhas/mês |
| fEstoque | Fato | Dia × Loja × SKU | ~ 9 mi linhas/mês |
| dCalendario | Dimensão | Dia | 1.826 linhas (5 anos) |
| dLoja | Dimensão | Loja | 42 |
| dProduto | Dimensão | SKU | 18.000 |
| dVendedor | Dimensão | Vendedor | 210 |
| dCliente | Dimensão | Cliente (atacado) | 3.400 |
- Data e hora do cupom separadas em duas colunas (reduz ~35% do modelo).
- Coluna CustoUnitario trazida para fVendas na carga (custo vigente na data da venda).
- IDs textuais convertidos em chaves inteiras no Power Query.
- Atualização incremental: últimos 7 dias recarregados diariamente, histórico congelado.
Medidas DAX
Receita Líquida
Receita Líquida =SUMX( fVendas, fVendas[Quantidade] * fVendas[PrecoUnitario] * ( 1 - fVendas[PercDesconto] )) - [Devoluções]Nasce linha a linha porque o desconto é percentual por item; devoluções entram como medida separada para poder ser analisada sozinha.
Margem Bruta %
Margem Bruta =SUMX( fVendas, fVendas[Quantidade] * ( fVendas[PrecoUnitario] * ( 1 - fVendas[PercDesconto] ) - fVendas[CustoUnitario] )) Margem Bruta % = DIVIDE( [Margem Bruta], [Receita Líquida] )Usa o custo gravado na venda, não o custo atual do cadastro — isso evita reescrever o histórico a cada mudança de preço de compra.
Ticket Médio e Itens por Cupom
Qtd Cupons = DISTINCTCOUNT( fVendas[NumeroCupom] ) Ticket Médio = DIVIDE( [Receita Líquida], [Qtd Cupons] ) Itens por Cupom = DIVIDE( SUM( fVendas[Quantidade] ), [Qtd Cupons] )DISTINCTCOUNT sobre cupom é a operação mais cara do painel; em modelos muito grandes, substitua por uma coluna de cupom já deduplicada em uma tabela auxiliar.
Comparação justa com o ano anterior
Receita LY =CALCULATE( [Receita Líquida], SAMEPERIODLASTYEAR( dCalendario[Date] ) ) Receita LY Comparável =-- Só os mesmos dias já decorridos, para não comparar 12 dias com 30VAR UltimoDiaComVenda = CALCULATE( MAX( fVendas[Data] ), REMOVEFILTERS( dCalendario ) )VAR DiaCorte = MIN( MAX( dCalendario[Date] ), UltimoDiaComVenda )RETURNCALCULATE( [Receita Líquida], SAMEPERIODLASTYEAR( FILTER( VALUES( dCalendario[Date] ), dCalendario[Date] <= DiaCorte ) )) Var. YoY % = DIVIDE( [Receita Líquida] - [Receita LY Comparável], [Receita LY Comparável] )A medida comparável é a que evita a reunião mensal mais desgastante do varejo: explicar por que o mês 'caiu 60%' no dia 12.
Meta proporcional e semáforo
Meta =CALCULATE( SUM( fMetas[ValorMeta] ), TREATAS( VALUES( dCalendario[AnoMes] ), fMetas[AnoMes] ), TREATAS( VALUES( dLoja[Loja] ), fMetas[Loja] )) Meta Proporcional =VAR DiasDecorridos = CALCULATE( COUNTROWS( dCalendario ), dCalendario[Date] <= TODAY() )VAR DiasTotais = COUNTROWS( dCalendario )RETURN [Meta] * DIVIDE( DiasDecorridos, DiasTotais ) Atingimento % = DIVIDE( [Receita Líquida], [Meta Proporcional] ) Cor Atingimento =SWITCH( TRUE(), ISBLANK( [Meta] ), "#8A8F98", [Atingimento %] >= 1, "#2E9E6B", [Atingimento %] >= 0.9, "#E0A526", "#D0453B")TREATAS conecta metas sem relacionamento físico, evitando ambiguidade no modelo quando a meta existe em granularidades diferentes.
Ruptura de gôndola
SKUs Ativos por Loja =CALCULATE( DISTINCTCOUNT( fEstoque[IdProduto] ), REMOVEFILTERS( fEstoque[Saldo] ) ) SKUs em Ruptura =CALCULATE( DISTINCTCOUNT( fEstoque[IdProduto] ), fEstoque[Saldo] <= 0, dProduto[CurvaABC] IN { "A", "B" }) Ruptura % = DIVIDE( [SKUs em Ruptura], [SKUs Ativos por Loja] ) Venda Perdida Estimada =SUMX( FILTER( SUMMARIZE( fEstoque, dProduto[Produto], dLoja[Loja] ), [SKUs em Ruptura] > 0 ), [Venda Média Diária do SKU])Ruptura é o indicador que mais impacta o resultado e menos aparece nos painéis: a venda que não aconteceu não está em nenhuma tabela de faturamento.
Curva ABC dinâmica
Receita Acumulada % =VAR Base = ADDCOLUMNS( ALLSELECTED( dProduto[Produto] ), "@Rec", [Receita Líquida] )VAR RecAtual = [Receita Líquida]VAR Acumulado = SUMX( FILTER( Base, [@Rec] >= RecAtual ), [@Rec] )RETURN DIVIDE( Acumulado, SUMX( Base, [@Rec] ) ) Classe ABC Dinâmica =SWITCH( TRUE(), [Receita Acumulada %] <= 0.8, "A", [Receita Acumulada %] <= 0.95, "B", "C")Classe calculada por medida respeita o filtro do usuário: a curva ABC de uma loja específica é diferente da curva da rede.
Clientes de atacado em risco
Clientes em Queda =VAR Base = ADDCOLUMNS( VALUES( dCliente[Cliente] ), "@Atual", CALCULATE( [Receita Líquida], DATESINPERIOD( dCalendario[Date], TODAY(), -90, DAY ) ), "@Anterior", CALCULATE( [Receita Líquida], DATESINPERIOD( dCalendario[Date], TODAY() - 90, -90, DAY ) ) )RETURNCOUNTROWS( FILTER( Base, [@Anterior] > 0 && DIVIDE( [@Atual], [@Anterior] ) < 0.7 ))Queda de 30% em 90 dias é o gatilho combinado com o time comercial — ajuste o limiar ao seu ciclo de compra.
Layout do painel
- 1Faixa superior: título, seletor de período, filtros ativos em texto dinâmico e carimbo de atualização.
- 2Linha de KPIs (6 cartões): Receita Líquida, Atingimento Proporcional, Margem %, Ticket Médio, Itens/Cupom, Ruptura % — todos com variação YoY e cor semântica.
- 3Bloco central esquerdo: linha de receita diária acumulada do mês contra a linha de meta acumulada (mostra o ritmo, não só o total).
- 4Bloco central direito: cascata explicando a variação YoY por categoria.
- 5Bloco inferior esquerdo: matriz por loja com barras de dados na receita, ícone de status e cor por margem.
- 6Bloco inferior direito: Top 10 produtos por contribuição de margem e Bottom 10 por margem negativa.
- 7Tooltip de página em toda a matriz: tendência de 12 meses da loja ou do produto.
- 8Drill-through por loja: página com detalhe de vendedores, ruptura e mix.
- 9Página oculta 'Dicionário': definição escrita de cada indicador e a origem do dado.
Desafio final
Implemente o painel completo e, depois, responda com ele: se a rede cresceu 8% no ano, quanto desse crescimento veio de mais cupons, quanto de ticket maior e quanto de inflação de preço? Crie a decomposição em cascata que separa efeito volume, efeito mix e efeito preço.