Logística e Supply Chain
Torre de controle logística: OTIF, estoque e custo de frete
Contexto
Operador logístico com 4 centros de distribuição, 180 rotas semanais e 12 mil SKUs armazenados para 30 embarcadores. Os indicadores existiam, mas cada CD calculava OTIF de um jeito. O objetivo é uma definição única, um painel diário para a operação e um painel mensal para a diretoria e para os clientes.
Perguntas que o painel deve responder
- Quais clientes estão abaixo do OTIF contratado e por qual causa?
- Quais SKUs estão em ruptura e quais estão obsoletos?
- Qual rota tem o maior custo por entrega e por quê?
- O atraso é do CD, da transportadora ou do cliente que não recebeu?
- Quanto de estoque parado eu tenho hoje, em reais?
Indicadores e metas
| Indicador | Meta | Por que importa |
|---|---|---|
| OTIF (On Time In Full) | ≥ 95% | O indicador que o cliente enxerga; combina prazo e completude |
| On Time % | ≥ 97% | Separa o problema de prazo do problema de disponibilidade |
| In Full % | ≥ 98% | Mede ruptura e erro de separação |
| Giro de Estoque | ≥ 9x/ano | Estoque parado é capital imobilizado e risco de obsolescência |
| Cobertura em Dias | 30 a 45 dias | Traduz o giro em linguagem de planejamento |
| Acuracidade de Inventário | ≥ 99,5% | Sem acuracidade, todo o resto do painel é ficção |
| Custo de Frete sobre Receita | ≤ 6,5% | Principal linha de custo variável da operação |
| Ocupação de Veículo | ≥ 85% | Frete pago por espaço vazio é perda pura |
Modelo de dados
Logística tem múltiplas fatos com granularidades diferentes: pedido, item do pedido, embarque e posição de estoque. Cada uma se relaciona com as dimensões compartilhadas, nunca entre si.
| Tabela | Granularidade | Datas relevantes |
|---|---|---|
| fPedidos | Pedido | Data do pedido, prometida, expedição, entrega |
| fItensPedido | Item do pedido | Quantidade pedida x separada x entregue |
| fEmbarques | Conhecimento de transporte | Coleta, entrega, peso, cubagem, valor do frete |
| fEstoquePosicao | Dia × CD × SKU | Saldo, custo médio, data da última movimentação |
| fInventario | Contagem × SKU | Saldo sistema x saldo contado |
| dCliente / dCD / dProduto / dTransportadora / dRota | Dimensões | — |
| dCalendario | Dia | Com marcação de dia útil e feriado por estado |
Medidas DAX
OTIF e seus componentes
Pedidos Entregues = DISTINCTCOUNT( fPedidos[NumeroPedido] ) On Time =CALCULATE( [Pedidos Entregues], FILTER( fPedidos, fPedidos[DataEntrega] <= fPedidos[DataPrometida] )) In Full =CALCULATE( [Pedidos Entregues], FILTER( fPedidos, NOT EXISTS( FILTER( RELATEDTABLE( fItensPedido ), fItensPedido[QtdEntregue] < fItensPedido[QtdPedida] ) ) )) OTIF =CALCULATE( [Pedidos Entregues], FILTER( fPedidos, fPedidos[DataEntrega] <= fPedidos[DataPrometida] && fPedidos[QtdItensDivergentes] = 0 )) OTIF % = DIVIDE( [OTIF], [Pedidos Entregues] )On Time % = DIVIDE( [On Time], [Pedidos Entregues] )In Full % = DIVIDE( [In Full], [Pedidos Entregues] )OTIF não é On Time × In Full. É o pedido que cumpriu as duas condições simultaneamente — por isso é sempre menor ou igual ao menor dos dois.
Atraso e sua causa
Dias de Atraso Médio =AVERAGEX( FILTER( fPedidos, fPedidos[DataEntrega] > fPedidos[DataPrometida] ), DATEDIFF( fPedidos[DataPrometida], fPedidos[DataEntrega], DAY )) Causa do Atraso =VAR AtrasoSeparacao = CALCULATE( COUNTROWS( fPedidos ), FILTER( fPedidos, fPedidos[DataExpedicao] > fPedidos[DataPrometidaExpedicao] ) )VAR AtrasoTransporte = CALCULATE( COUNTROWS( fPedidos ), FILTER( fPedidos, fPedidos[DataExpedicao] <= fPedidos[DataPrometidaExpedicao] && fPedidos[DataEntrega] > fPedidos[DataPrometida] ) )RETURNSWITCH( TRUE(), AtrasoSeparacao > AtrasoTransporte, "CD / Separação", AtrasoTransporte > 0, "Transporte", "Sem atraso")Separar a causa é o que transforma o OTIF de indicador de cobrança em ferramenta de melhoria: o CD e a transportadora param de culpar um ao outro.
Giro, cobertura e estoque parado
Estoque Médio =AVERAGEX( VALUES( dCalendario[Date] ), CALCULATE( SUM( fEstoquePosicao[ValorEstoque] ) ) ) CMV Período = CALCULATE( SUM( fItensPedido[CustoTotal] ) ) Giro de Estoque =VAR DiasPeriodo = COUNTROWS( dCalendario )RETURN DIVIDE( [CMV Período], [Estoque Médio] ) * DIVIDE( 365, DiasPeriodo ) Cobertura em Dias = DIVIDE( 365, [Giro de Estoque] ) Estoque Parado =CALCULATE( SUM( fEstoquePosicao[ValorEstoque] ), FILTER( fEstoquePosicao, DATEDIFF( fEstoquePosicao[DataUltimaSaida], TODAY(), DAY ) > 120 )) Estoque Parado % = DIVIDE( [Estoque Parado], [Valor Total em Estoque] )Giro precisa ser anualizado para ser comparável entre períodos de tamanhos diferentes. Cobertura é o mesmo número em linguagem de planejador.
Acuracidade de inventário
Acuracidade % =VAR SKUsContados = DISTINCTCOUNT( fInventario[IdProduto] )VAR SKUsCorretos = CALCULATE( DISTINCTCOUNT( fInventario[IdProduto] ), FILTER( fInventario, fInventario[SaldoSistema] = fInventario[SaldoContado] ) )RETURN DIVIDE( SKUsCorretos, SKUsContados ) Acuracidade Financeira % =VAR DivergenciaAbs = SUMX( fInventario, ABS( fInventario[SaldoSistema] - fInventario[SaldoContado] ) * fInventario[CustoUnitario] )RETURN 1 - DIVIDE( DivergenciaAbs, [Valor Total em Estoque] )Acuracidade por SKU e acuracidade financeira contam histórias diferentes: errar 200 parafusos não é o mesmo que errar 2 motores.
Custo de frete e ocupação
Custo de Frete = SUM( fEmbarques[ValorFrete] ) Frete sobre Receita % = DIVIDE( [Custo de Frete], [Receita de Vendas] ) Custo por Entrega = DIVIDE( [Custo de Frete], DISTINCTCOUNT( fEmbarques[IdEntrega] ) ) Custo por Kg = DIVIDE( [Custo de Frete], SUM( fEmbarques[PesoKg] ) ) Ocupação de Veículo % =AVERAGEX( VALUES( fEmbarques[IdViagem] ), VAR PesoUtil = CALCULATE( SUM( fEmbarques[PesoKg] ) ) VAR Capacidade = CALCULATE( MAX( dVeiculo[CapacidadeKg] ) ) VAR CubUtil = CALCULATE( SUM( fEmbarques[CubagemM3] ) ) VAR CapCub = CALCULATE( MAX( dVeiculo[CapacidadeM3] ) ) RETURN MAX( DIVIDE( PesoUtil, Capacidade ), DIVIDE( CubUtil, CapCub ) ))Ocupação é o maior entre peso e cubagem: carga leve e volumosa lota o caminhão sem atingir o peso máximo.
Classificação XYZ (previsibilidade de demanda)
Coef. Variação Demanda =VAR Serie = ADDCOLUMNS( VALUES( dCalendario[AnoMes] ), "@Qtd", [Quantidade Vendida] )VAR Media = AVERAGEX( Serie, [@Qtd] )VAR Desvio = SQRT( DIVIDE( SUMX( Serie, ( [@Qtd] - Media ) ^ 2 ), COUNTROWS( Serie ) - 1 ) )RETURN DIVIDE( Desvio, Media ) Classe XYZ =SWITCH( TRUE(), [Coef. Variação Demanda] <= 0.25, "X - Previsível", [Coef. Variação Demanda] <= 0.60, "Y - Variável", "Z - Errático")Cruzar ABC (valor) com XYZ (previsibilidade) gera 9 estratégias de estoque distintas — é a análise de maior retorno em supply chain.
Layout do painel
- 1Página 1 — Torre de Controle (operação, atualizada de hora em hora): cartões de OTIF do dia, pedidos em atraso, pedidos a expedir, veículos em rota; tabela de pedidos críticos com semáforo e link para o WMS.
- 2Página 2 — OTIF Gerencial: linha de OTIF mensal com meta; decomposição em On Time e In Full; barras por causa de atraso; matriz por cliente com OTIF contratado x realizado.
- 3Página 3 — Estoque: matriz ABC × XYZ com valor em estoque em cada célula; barras de estoque parado por faixa de dias; giro e cobertura por CD; acuracidade por contagem.
- 4Página 4 — Custo: custo por entrega e por kg por rota; ocupação média por transportadora; dispersão distância × custo revelando rotas fora da curva.
- 5Tooltip de página em toda matriz de cliente: evolução de 12 meses do OTIF daquele cliente.
- 6Drill-through por rota: detalhe de cada viagem com peso, cubagem, ocupação e custo.
Desafio final
Implemente OTIF com a separação de causa e valide a definição com a operação dos 4 CDs. Depois construa a matriz ABC × XYZ e proponha uma política de estoque distinta para cada uma das 9 células.