Auditoria do benefício de alimentação em campo
Caso fictício. A Fiorena Redes Elétricas S.A., suas bases, pessoas e lançamentos não existem. Os dados foram gerados do zero por um script em R, com regras de benefício inventadas e defeitos de tamanho conhecido. O método é real.
Baixar o relatório (PDF) · Baixar os casos para revisar (CSV)
A pergunta
A controladoria da Fiorena viu o valor pago em alimentação por dia de equipe em campo subir de R$ 219 no primeiro semestre de 2025 para R$ 239 no segundo, 9,5% a mais, sem reajuste de valor e sem mudança no tipo de serviço. A pergunta era simples: quanto disso é gasto legítimo e quanto foi pago fora da regra?
A queda de julho tem causa nos dados: julho e janeiro são os meses com mais gente de férias (em julho, 81 eletricistas por dia útil, contra 37 na mediana do ano), e cada equipe saiu a campo com 3,5 pessoas, contra 3,8 na mediana. O valor por pessoa em campo não caiu: ficou entre R$ 58 e R$ 60 de janeiro a julho.
Cada eletricista recebe no cartão de alimentação uma recarga por dia em campo. A recarga é lançada pela base, aprovada pelo supervisor e paga sem cruzamento automático com o diário de campo nem com o cadastro de férias e afastamentos. Ninguém tinha olhado os 12 meses de lançamentos juntos.
A auditoria respondeu três perguntas, nessa ordem:
- Quanto foi pago fora de uma regra objetiva (duplicidade, acima do teto, pessoa que não estava trabalhando)?
- Quanto foi pago sem lastro: sem registro de equipe em campo naquele dia, ou com justificativa que não explica nada?
- Onde isso se concentra, e por quê?
A regra e os dados
A regra do benefício:
- Almoço de R$ 52 por dia em campo.
- Jantar de R$ 44, só quando o diário de campo registra pernoite da equipe.
- Extra de até R$ 24, só com uma das justificativas da lista: “deslocamento acima de 300 km” ou “jornada estendida em mais de 2 horas”.
- Teto de R$ 120 por pessoa por dia, somando tudo, e no máximo uma recarga de cada tipo por dia.
- Só para quem está ativo, fora de férias e afastamentos, e numa equipe que esteve em campo naquele dia.
Foram auditadas 155.909 recargas de 2025, no total de R$ 7,63 milhões, pagas a 649 pessoas de 8 bases, cruzadas com 33.254 registros de equipe em campo no diário, com o cadastro e com as férias e afastamentos do RH.
Método
Cada recarga passa pelas mesmas verificações e termina numa de quatro conclusões. A auditoria separa o que é erro objetivo, que pode ser cobrado, do que só merece uma segunda olhada da base; ela não tira conclusão sobre a intenção de ninguém.
- Preparar. Usar o dia trabalhado, e não a data em que a recarga foi lançada, e ligar cada recarga ao cadastro, às ausências e à equipe da pessoa naquele dia.
- Conferir as regras objetivas. Duplicidade, pessoa fora do trabalho, jantar sem pernoite, extra sem justificativa e soma do dia acima do teto. No teto, só o excedente conta como indevido.
- Conferir o lastro. A equipe estava em campo naquele dia? A justificativa do extra é válida, vaga ou ausente? Um dicionário de termos aceita erros de digitação, para “deslocamneto acima de 300km” contar como válida.
- Comparar com o esperado. Para cada base e mês, o valor por dia em campo é comparado com o das outras bases no mesmo mês. Fica fora da faixa esperada o grupo que se afasta muito mais do que as bases costumam se afastar umas das outras (o critério de Tukey, no código).
- Pontuar o risco. Um ponto para cada sinal: regra violada, sem lastro de campo, justificativa vaga ou ausente, base e mês acima da faixa.
- Concluir. Cada recarga recebe uma das quatro conclusões abaixo.
| Conclusão | Quando | O que fazer |
|---|---|---|
| Indevido | Violou uma regra objetiva | Estornar ou recuperar, conforme a política da empresa e com apoio do RH e do jurídico |
| Revisar | Nenhuma regra violada, mas sem lastro de campo ou com dois sinais ou mais | A base confirma ou corrige em até 15 dias |
| Atenção | Um sinal só, sem regra violada | Entra na amostra do próximo mês |
| Regular | Nenhum sinal | Nada a fazer |
Indevido
Quando: Violou uma regra objetiva
O que fazer: Estornar ou recuperar, conforme a política da empresa e com apoio do RH e do jurídico
Revisar
Quando: Nenhuma regra violada, mas sem lastro de campo ou com dois sinais ou mais
O que fazer: A base confirma ou corrige em até 15 dias
Atenção
Quando: Um sinal só, sem regra violada
O que fazer: Entra na amostra do próximo mês
Regular
Quando: Nenhum sinal
O que fazer: Nada a fazer
Ver o código R da etapa 1: preparar
# O que vale é o dia de referência (o dia trabalhado), e não a data em que a
# recarga foi lançada. Cada recarga é ligada ao cadastro, às ausências e à
# equipe da pessoa naquele dia no diário de campo.
recargas <- read_csv("dados/recargas.csv.gz", show_col_types = FALSE, na = "",
col_types = cols(justificativa = "c"))
diario <- read_csv("dados/diario_campo.csv", show_col_types = FALSE)
cadastro <- read_csv("dados/cadastro.csv", show_col_types = FALSE, na = "")
ausencias <- read_csv("dados/ausencias.csv", show_col_types = FALSE)
base_auditoria <- recargas |>
mutate(dia = dia_referencia) |>
left_join(cadastro |> select(matricula, equipe, admissao, desligamento), "matricula") |>
left_join(ausencias |> select(matricula, tipo_ausencia = tipo, inicio, fim),
join_by(matricula, between(dia, inicio, fim))) |>
left_join(diario |> transmute(equipe, dia = data, em_campo = TRUE, pernoite = pernoite == "sim"),
c("equipe", "dia")) |>
mutate(em_campo = coalesce(em_campo, FALSE))Ver o código R da etapa 2: regras objetivas
# Regras objetivas: cada violação pode ser cobrada. No teto, só o excedente
# conta como indevido; ele é calculado depois de tirar o que já é indevido por
# outra regra, para não contar o mesmo real duas vezes.
normalizar <- function(x) {
x <- tolower(iconv(coalesce(x, ""), to = "ASCII//TRANSLIT"))
x <- gsub("([0-9])\\s*km", "\\1 km", x)
x <- gsub("([0-9])\\s*h\\b", "\\1 horas", x)
trimws(gsub("[^a-z0-9 ]", " ", gsub("\\s+", " ", x)))
}
# Dicionário com tolerância a erro de digitação: a justificativa vale se
# estiver a até 15% de distância (em letras) de uma das frases da regra.
classificar_justificativa <- function(x) {
n <- normalizar(x)
validas <- normalizar(JUSTIFICATIVAS_VALIDAS)
dist <- adist(n, validas, partial = FALSE) / outer(nchar(n), nchar(validas), pmax)
case_when(n == "" ~ "ausente",
apply(dist, 1, min) <= 0.15 ~ "válida",
TRUE ~ "vaga")
}
# Cada texto diferente é classificado uma vez só.
textos <- unique(coalesce(base_auditoria$justificativa[base_auditoria$tipo == "Extra"], ""))
classes <- classificar_justificativa(textos)
auditada <- base_auditoria |>
mutate(justificativa_classe = if_else(tipo == "Extra", classes[match(coalesce(justificativa, ""), textos)], NA)) |>
arrange(matricula, dia, tipo, data_recarga, id) |>
group_by(matricula, dia, tipo) |>
mutate(duplicidade = row_number() > 1) |>
ungroup() |>
mutate(
fora_do_trabalho = dia < admissao | (!is.na(desligamento) & dia > desligamento) | !is.na(tipo_ausencia),
jantar_sem_pernoite = tipo == "Jantar" & !coalesce(pernoite, FALSE),
extra_sem_justificativa = tipo == "Extra" & justificativa_classe == "ausente",
motivo_regra = case_when(duplicidade ~ "Duplicidade",
fora_do_trabalho ~ "Pessoa fora do trabalho",
jantar_sem_pernoite ~ "Jantar sem pernoite",
extra_sem_justificativa ~ "Extra sem justificativa"),
valor_indevido = if_else(is.na(motivo_regra), 0, valor)
)
# Teto diário: soma do que não é indevido por outra regra; o excedente sai
# primeiro do extra, depois do jantar.
ordem_corte <- c("Extra" = 1, "Jantar" = 2, "Almoço" = 3)
auditada <- auditada |>
mutate(restante = valor - valor_indevido, corte = ordem_corte[tipo]) |>
group_by(matricula, dia) |>
mutate(excedente_dia = pmax(sum(restante) - TETO, 0)) |>
arrange(corte, .by_group = TRUE) |>
mutate(antes = cumsum(restante) - restante,
acima_do_teto = pmin(restante, pmax(excedente_dia - antes, 0))) |>
ungroup() |>
mutate(valor_indevido = valor_indevido + acima_do_teto,
motivo_regra = if_else(is.na(motivo_regra) & acima_do_teto > 0, "Acima do teto", motivo_regra),
regra = !is.na(motivo_regra)) |>
select(-restante, -corte, -excedente_dia, -antes)Ver o código R da etapa 3: lastro
# A equipe estava em campo naquele dia? A justificativa do extra é válida,
# vaga ou ausente?
auditada <- auditada |>
mutate(sem_lastro = !em_campo,
justificativa_fraca = tipo == "Extra" & justificativa_classe %in% c("vaga", "ausente"))Ver o código R da etapa 4: faixa esperada
# Valor pago por dia de equipe em campo, por base e mês. Como o nível geral
# muda de um mês para outro (mais pernoites no segundo semestre), cada base é
# comparada com a mediana das bases no mesmo mês; a faixa esperada desse
# índice vem do critério de Tukey sobre todos os grupos com pelo menos 15 dias
# de equipe em campo. Como a auditoria procura pagamento a mais, só ficar
# acima da faixa conta como sinal de risco; abaixo dela, o grupo só é anotado.
dias_campo <- diario |>
mutate(mes = format(data, "%Y-%m")) |>
count(base, mes, name = "dias_equipe")
base_mes <- auditada |>
mutate(mes = format(dia, "%Y-%m")) |>
group_by(base, mes) |>
summarise(valor = sum(valor), .groups = "drop") |>
left_join(dias_campo, c("base", "mes")) |>
mutate(valor_dia = valor / dias_equipe) |>
group_by(mes) |>
mutate(mediana_mes = median(valor_dia), indice = valor_dia / mediana_mes) |>
ungroup()
validos <- base_mes |> filter(dias_equipe >= 15)
q <- quantile(validos$indice, c(0.25, 0.75))
faixa <- c(q[[1]] - 1.5 * diff(q), q[[2]] + 1.5 * diff(q))
base_mes <- base_mes |>
mutate(faixa_min = faixa[1] * mediana_mes, faixa_max = faixa[2] * mediana_mes,
abaixo_da_faixa = dias_equipe >= 15 & indice < faixa[1],
fora_da_faixa = dias_equipe >= 15 & indice > faixa[2])
auditada <- auditada |>
mutate(mes = format(dia, "%Y-%m")) |>
left_join(base_mes |> select(base, mes, fora_da_faixa), c("base", "mes")) |>
mutate(fora_da_faixa = coalesce(fora_da_faixa, FALSE))Ver o código R da etapa 5: score de risco
# Um ponto para cada sinal, de 0 a 4.
auditada <- auditada |>
mutate(score = regra + sem_lastro + justificativa_fraca + fora_da_faixa,
score_sem_regra = score - regra)Ver o código R da etapa 6: conclusão
# Indevido: violou uma regra objetiva. Revisar: sem regra violada, mas sem
# lastro de campo ou com score de 2 ou mais. Atenção: score 1. Regular: 0.
# Numa recarga que passou do teto só em parte, o excedente é Indevido e o
# resto recebe a conclusão que teria sem essa regra.
concluir <- function(sem_lastro, score) {
case_when(sem_lastro | score >= 2 ~ "Revisar", score == 1 ~ "Atenção", TRUE ~ "Regular")
}
auditada <- auditada |>
mutate(conclusao = if_else(regra, "Indevido", concluir(sem_lastro, score)),
conclusao_resto = concluir(sem_lastro, score_sem_regra),
valor_resto = valor - valor_indevido)
NIVEIS <- c("Indevido", "Revisar", "Atenção", "Regular")Resultado
Do valor pago no ano, R$ 305 mil (4,0%) foram indevidos, e outros R$ 235 mil (3,1%) precisam de confirmação da base.
- Indevido é o que violou uma regra objetiva e pode ser cobrado.
- Revisar é quase todo formado por recargas sem lastro de campo: R$ 234 mil em 4.500 recargas lançadas em dias em que a equipe não aparece no diário. Pode ser falha do diário, e não da recarga; por isso a base confirma antes de qualquer cobrança.
- Atenção não é acusação. Das 8.686 recargas em Atenção, 7.925 estão lá só porque a base e o mês ficaram acima da faixa esperada; a recarga em si cumpre a regra. Entre elas estão 1.072 lançamentos que só pareciam erro (lançados um ou dois dias depois, pernoite registrado com atraso, justificativa com erro de digitação). As outras 872 têm justificativa vaga, como “ajuste” ou “conforme combinado”.
O indevido por tipo de defeito
O que explica o salto do segundo semestre
O valor por dia em campo subiu R$ 20,86. Desse aumento, R$ 10,21 (49%) vieram de pagamentos indevidos. Outros R$ 8,51 (41%) vieram de mais jantares legítimos, porque os pernoites passaram de 11,5% para 16,8% dos dias em campo. O resto, R$ 2,14, acompanha os pernoites, nos extras.
Onde e por quê
- A troca de sistema em agosto criou duplicidades. Até julho, elas somam R$ 6 mil; de agosto a dezembro, R$ 112 mil. O sistema novo aceita duas recargas do mesmo tipo para a mesma pessoa no mesmo dia.
- O cadastro chega atrasado. 100% das recargas pagas a quem estava de férias caíram nos cinco primeiros dias do período: a base só fica sabendo das férias depois que elas começaram.
- Duas bases pagam jantar sem pernoite. A Base 3 e a Base 6 concentram 100% do jantar sem pernoite, e são as únicas com meses acima da faixa esperada: 3 meses na primeira e 3 na segunda.
Casos para revisar
A lista completa tem 12.033 recargas classificadas como Indevido ou Revisar, ordenadas por conclusão, risco e valor, e pode ser baixada no topo da página. Abaixo, primeiro as 5 pessoas com maior valor indevido (só a parte indevida de cada recarga) e depois as 5 com maior valor para revisar.
| Conclusão | Matrícula | Base | Período | Recargas | Valor | Motivo |
|---|---|---|---|---|---|---|
| Indevido | F6561 | Base 3 | 06/01/2025 a 17/12/2025 | 40 | R$ 1.692 | Jantar sem pernoite |
| Indevido | F5109 | Base 3 | 24/02/2025 a 11/12/2025 | 40 | R$ 1.684 | Jantar sem pernoite |
| Indevido | F5730 | Base 6 | 27/03/2025 a 31/12/2025 | 37 | R$ 1.616 | Jantar sem pernoite |
| Indevido | F4119 | Base 6 | 22/04/2025 a 26/12/2025 | 37 | R$ 1.580 | Jantar sem pernoite |
| Indevido | F5573 | Base 3 | 23/01/2025 a 29/12/2025 | 37 | R$ 1.568 | Jantar sem pernoite |
| Revisar | F3012 | Base 2 | 13/01/2025 a 29/12/2025 | 18 | R$ 936 | Sem lastro de campo |
| Revisar | F8211 | Base 6 | 23/01/2025 a 31/12/2025 | 18 | R$ 936 | Sem lastro de campo |
| Revisar | F1355 | Base 6 | 08/01/2025 a 23/12/2025 | 18 | R$ 872 | Sem lastro de campo |
| Revisar | F6862 | Base 6 | 15/01/2025 a 16/12/2025 | 17 | R$ 852 | Sem lastro de campo |
| Revisar | F1035 | Base 2 | 27/01/2025 a 29/12/2025 | 16 | R$ 832 | Sem lastro de campo |
Indevido
Matrícula: F6561
Base: Base 3
Período: 06/01/2025 a 17/12/2025
Recargas: 40
Valor: R$ 1.692
Motivo: Jantar sem pernoite
Indevido
Matrícula: F5109
Base: Base 3
Período: 24/02/2025 a 11/12/2025
Recargas: 40
Valor: R$ 1.684
Motivo: Jantar sem pernoite
Indevido
Matrícula: F5730
Base: Base 6
Período: 27/03/2025 a 31/12/2025
Recargas: 37
Valor: R$ 1.616
Motivo: Jantar sem pernoite
Indevido
Matrícula: F4119
Base: Base 6
Período: 22/04/2025 a 26/12/2025
Recargas: 37
Valor: R$ 1.580
Motivo: Jantar sem pernoite
Indevido
Matrícula: F5573
Base: Base 3
Período: 23/01/2025 a 29/12/2025
Recargas: 37
Valor: R$ 1.568
Motivo: Jantar sem pernoite
Revisar
Matrícula: F3012
Base: Base 2
Período: 13/01/2025 a 29/12/2025
Recargas: 18
Valor: R$ 936
Motivo: Sem lastro de campo
Revisar
Matrícula: F8211
Base: Base 6
Período: 23/01/2025 a 31/12/2025
Recargas: 18
Valor: R$ 936
Motivo: Sem lastro de campo
Revisar
Matrícula: F1355
Base: Base 6
Período: 08/01/2025 a 23/12/2025
Recargas: 18
Valor: R$ 872
Motivo: Sem lastro de campo
Revisar
Matrícula: F6862
Base: Base 6
Período: 15/01/2025 a 16/12/2025
Recargas: 17
Valor: R$ 852
Motivo: Sem lastro de campo
Revisar
Matrícula: F1035
Base: Base 2
Período: 27/01/2025 a 29/12/2025
Recargas: 16
Valor: R$ 832
Motivo: Sem lastro de campo
Como saber que a auditoria acertou
Como os dados deste caso foram gerados por script, foi possível conferir a auditoria contra o que o gerador plantou: ela encontrou os 12.905 defeitos plantados, sem exceção, e nenhum dos 19.885 lançamentos que só pareciam erro virou Indevido ou Revisar. A auditoria não lê o gabarito; um script separado compara os dois no fim. Com dados reais não existe gabarito, e essa conferência é feita com a base: uma amostra de casos de cada conclusão é revisada com quem conhece o processo antes de o relatório ser fechado.
Recomendações
Cada achado vira uma correção no processo, para o problema não voltar no ano seguinte.
| Achado | Correção recomendada | Responsável |
|---|---|---|
| Duplicidade depois da troca de sistema | Bloqueio no sistema de uma recarga por tipo, pessoa e dia | TI e fornecedor do cartão |
| Cadastro atrasado | Lista diária de férias, afastamentos e desligamentos enviada ao sistema de recarga | RH |
| Jantar sem pernoite | Jantar só liberado quando o diário registra pernoite | Operação e supervisores das bases |
| Acima do teto | Teto diário aplicado pelo próprio sistema | TI |
| Justificativa vaga | Justificativa escolhida numa lista fechada, não digitada | Controladoria |
| Valores já pagos como indevido | Estorno ou recuperação conforme a política da empresa | RH, com apoio do jurídico |
Duplicidade depois da troca de sistema
Correção recomendada: Bloqueio no sistema de uma recarga por tipo, pessoa e dia
Responsável: TI e fornecedor do cartão
Cadastro atrasado
Correção recomendada: Lista diária de férias, afastamentos e desligamentos enviada ao sistema de recarga
Responsável: RH
Jantar sem pernoite
Correção recomendada: Jantar só liberado quando o diário registra pernoite
Responsável: Operação e supervisores das bases
Acima do teto
Correção recomendada: Teto diário aplicado pelo próprio sistema
Responsável: TI
Justificativa vaga
Correção recomendada: Justificativa escolhida numa lista fechada, não digitada
Responsável: Controladoria
Valores já pagos como indevido
Correção recomendada: Estorno ou recuperação conforme a política da empresa
Responsável: RH, com apoio do jurídico
A auditoria também deixa três indicadores para acompanhar todo mês:
- Taxa de recargas indevidas: valor indevido ÷ valor pago, com meta abaixo de 0,5%.
- Cobertura de lastro: recargas com equipe registrada em campo ÷ total de recargas, com meta acima de 99%.
- Valor por dia em campo: por base, contra a faixa esperada.
O que o caso demonstra
- Cruzar bases que ninguém cruzava. O erro não estava em nenhuma base sozinha, e sim na falta de conferência entre o cartão, o diário de campo e o RH.
- Separar erro objetivo de suspeita. Só o que viola uma regra vai para cobrança; o resto vai para a base confirmar, sem acusar ninguém.
- Um número que explica o problema. O salto do segundo semestre foi dividido entre o que é indevido e o que é legítimo, em vez de virar uma ordem genérica de corte de gastos.
- Um relatório que se repete. O código fica com a empresa, e o mesmo relatório roda no ano seguinte com os dados novos.
Sua empresa paga algum benefício, reembolso ou adicional sem cruzar com outra base?
Me conte como funciona hoje e eu digo o que dá para conferir.







