RGF Anexo 1 - Despesa com Pessoal (Consolidado)

Material Suplementar - Documento Quarto Executável

Sobre este documento

Este é um documento Quarto executável: o mesmo arquivo que gera o relatório contém o código que produz os resultados. Faz parte de um projeto experimental / piloto de transparência fiscal.

A Despesa com Pessoal é um dos cálculos mais detalhados da Lei de Responsabilidade Fiscal, com várias regras de inclusão e exclusão. A intenção deste documento é abrir cada regra e explicá-la em linguagem natural, para que a área de contabilidade reconheça os critérios do Manual de Demonstrativos Fiscais (MDF-STN) e do art. 19–20 da LRF.

Para reproduzir: instale R e Quarto, coloque o arquivo de dados na mesma pasta e rode quarto render rgf_a01_pessoal.qmd.

O que este demonstrativo mede

O RGF Anexo 1 apura a Despesa Total com Pessoal (DTP) — a base para verificar se a União respeita o limite da LRF (que é um percentual da RCL). A lógica geral é:

DTP Líquida = Despesa Bruta com Pessoal − Despesas Não Computadas

Ou seja: somamos tudo que é pessoal (vencimentos, encargos, aposentadorias, pensões, terceirização) e depois subtraímos as parcelas que a própria LRF permite excluir do limite (como certas aposentadorias custeadas por fontes específicas). O resultado — a DTP Líquida — é o número que se compara ao teto legal.

Este cálculo é consolidado (soma dos três Poderes). A abertura por Poder depende de uma segmentação institucional que a base pública do Siga Brasil não fornece; por isso trabalhamos com o total consolidado, que valida exatamente.

Escopo desta prova de conceito: apuramos aqui exclusivamente a despesa liquidada. Os Restos a Pagar Não Processados não entram no cálculo, e não se apresenta a razão DTP/RCL nem a apuração do cumprimento dos limites legais. A justificativa dessa escolha está na seção Nota metodológica, ao final.

Fonte de dados

Os dados vêm do Siga Brasil (Senado Federal): https://www12.senado.leg.br/orcamento/sigabrasil

Arquivo: despesa_unificada.csv.gz

Coluna no arquivo Papel no cálculo
GND (Cod) DESP Grupo de Natureza da Despesa. Grupo 1 = Pessoal; grupo 3 = Outras Despesas Correntes (entra só para a terceirização).
Elemento Despesa (Cod) DESP Distingue vencimentos (11), obrigações (13), aposentadorias (01), pensões (03), terceirização (34) etc.
Sub-elemento Despesa (Cod) DESP Detalhe de 8 dígitos (a “natureza detalhada”). Usado para capturar exceções específicas.
Sub-elemento Despesa DESP Nome do sub-elemento — usado para identificar aposentadorias/pensões por palavras-chave.
Modalidade Aplicação (Cod) DESP 90 = aplicação direta; 91 = intra-orçamentária. Separa casos de obrigações patronais.
Fonte (Cod) DESP Identifica as fontes de recursos que permitem a exclusão de aposentadorias do limite.
Liquidado (R$) O RGF usa a despesa liquidada como métrica.

Onde estão os dados (leia antes de executar)

Este documento lê os arquivos de dados a partir da pasta dados/ do repositório. Como o .qmd está em standalones/, o caminho padrão é ../dados (a pasta dados que fica um nível acima).

Se você renderizar o documento a partir da própria pasta standalones/, não precisa mudar nada. Se os seus arquivos estiverem em outro lugar, basta ajustar uma única linha no bloco de configuração abaixo — a variável pasta_dados. Exemplos:

  • Arquivos na mesma pasta do .qmd: pasta_dados <- "."
  • Arquivos em dados/ um nível acima (padrão): pasta_dados <- "../dados"
  • Um caminho completo no seu computador: pasta_dados <- "C:/Users/voce/Desktop/dados" (use barras normais /, mesmo no Windows)

Configuração inicial

library(dplyr)
library(stringr)
library(knitr)

# ---------------------------------------------------------------------------
# CAMINHO DAS BASES DE DADOS  <-- ajuste aqui se necessário (veja seção acima)
# ---------------------------------------------------------------------------
pasta_dados <- "../dados"

# Monta o caminho completo de um arquivo dentro da pasta de dados.
# Ex.: caminho_dado("despesa_unificada.csv.gz") -> "../dados/despesa_unificada.csv.gz"
caminho_dado <- function(arquivo) file.path(pasta_dados, arquivo)

ler_csv_gz <- function(caminho) {
  df <- read.csv(gzfile(caminho), check.names = FALSE, stringsAsFactors = FALSE)
  Encoding(names(df)) <- "UTF-8"
  df
}

# Seleciona uma coluna pelo NOME. Normaliza acentos, espaços extras e
# caracteres invisíveis (BOM) que costumam vir no cabeçalho exportado — assim
# a leitura é legível e ao mesmo tempo tolerante a variações do arquivo.
# Se o nome não existir, para com mensagem clara em vez de somar a coluna errada.
coluna <- function(dados, nome) {
  normalizar <- function(x) {
    x <- iconv(x, to = "ASCII//TRANSLIT")
    tolower(trimws(gsub("[^A-Za-z0-9]+", " ", x)))
  }
  i <- match(normalizar(nome), normalizar(names(dados)))
  if (is.na(i)) {
    stop("Coluna nao encontrada no arquivo: '", nome, "'.\nColunas disponiveis: ",
         paste(names(dados), collapse = " | "), call. = FALSE)
  }
  dados[[i]]
}

em_mil <- function(reais) {
  format(round(reais / 1000), big.mark = ".", decimal.mark = ",", scientific = FALSE)
}

# A "fonte" vem com 4 dígitos; comparamos pelos 3 últimos.
fonte_3digitos <- function(x) str_sub(str_pad(as.character(x), 4, pad = "0"), -3, -1)

Listas de referência (critérios do MDF-STN)

Antes de calcular, definimos as listas de códigos que o Manual de Demonstrativos Fiscais estabelece. Nomeamos cada uma para deixar claro o que representa.

# Sub-elementos "ressalvados": despesas que, apesar de estarem no grupo Pessoal,
# NÃO compõem a despesa com pessoal (ex.: sentenças, indenizações específicas).
# São removidas logo na entrada.
ndds_ressalvadas <- c(
  "31900503","31900504","31900505","31900506","31900507","31900508",
  "31900501","31900502","31900402","31900421","31900819","31900901",
  "31900902","31900903","31900904","31900905","31900906","31909209",
  "31900809","31900810","31900811","31900812","31900815","31900816"
)

# Naturezas de OBRIGAÇÕES PATRONAIS (encargos do empregador)
nat_obrigacoes <- c("31909213","31919213","31909207")

# Naturezas de APOSENTADORIA (além do elemento 01)
nat_aposentadoria <- c(
  "31901702","31909109","31909112","31909115","31909118","31909123",
  "31909124","31909128","31909129","31909201","31909403","31909404","31909414"
)

# Naturezas de PENSÕES (além do elemento 03)
nat_pensoes <- c(
  "31909110","31909113","31909116","31909119","31909131","31909136",
  "31909137","31909130","31909203","31909220","31909221","31909406","31909413"
)

elem_obrigacoes  <- c("13","07")                       # elementos de obrigações patronais
elem_apos_pens   <- c("01","03")                       # aposentadoria e pensão
elem_diversos_inat <- c("05","08","09","17","91","92","94")  # elementos que podem conter inativos

# Fontes que permitem EXCLUIR aposentadorias/pensões do limite (art. 19 LRF)
fontes_exclusao <- c("023","024","055","056")

# Palavras que identificam despesa de inativos no nome do sub-elemento
padrao_inativos <- "\\bAPOSENT|\\bINAT|\\bREFORMA|\\bPENS|LEI 7\\.963/1989"

Passo 1 — Carregar e preparar a base

Ficamos apenas com os grupos relevantes (1 = Pessoal e 3 = Outras Despesas Correntes, este só para terceirização) e removemos, de saída, os sub-elementos ressalvados.

despesa <- ler_csv_gz(caminho_dado("despesa_unificada.csv.gz"))

d <- data.frame(
  grupo      = as.character(coluna(despesa, "GND (Cod) DESP")),
  elemento   = as.character(coluna(despesa, "Elemento Despesa (Cod) DESP")),
  subelem    = as.character(coluna(despesa, "Sub-elemento Despesa (Cod) DESP")),
  subnome    = as.character(coluna(despesa, "Sub-elemento Despesa DESP")),
  modalidade = as.character(coluna(despesa, "Modalidade Aplicação (Cod) DESP")),
  fonte      = fonte_3digitos(coluna(despesa, "Fonte (Cod) DESP")),
  liquidado  = as.numeric(coluna(despesa, "Liquidado (R$)")),
  stringsAsFactors = FALSE
)

# Remove os sub-elementos ressalvados e mantém só grupos 1 e 3
d <- d[!d$subelem %in% ndds_ressalvadas & d$grupo %in% c("1","3"), ]

# Atalho: soma o "liquidado" das linhas que satisfazem uma condição
somar_se <- function(condicao) sum(d$liquidado[condicao], na.rm = TRUE)

cat("Linhas de despesa consideradas:", nrow(d), "\n")
Linhas de despesa consideradas: 100587 

Passo 2 — Despesa Bruta com Pessoal (I)

A despesa bruta soma cinco componentes. Cada um é uma linha do demonstrativo.

11 — Vencimentos e Vantagens

É o “corpo” da folha: tudo do grupo Pessoal que não seja aposentadoria/pensão (01, 03), terceirização (34) ou benefício assistencial (86), e que não caia em uma série de exceções específicas (obrigações patronais destacadas, ressalvas de natureza 0599, nomes de inativos, etc.). Essas exclusões evitam contar duas vezes o que será somado em outras linhas.

# Exclusão A: obrigações patronais que serão contadas na linha 12
exclusao_obrig <-
  (substr(d$subelem,1,1)=="3" & d$elemento=="92" &
     ((d$modalidade=="90" & d$subelem %in% c("31909213","31909207")) |
      (d$modalidade=="91" & d$subelem=="31919213"))) |
  d$elemento %in% elem_obrigacoes

# Exclusão B: ressalvas específicas de natureza (0599 e um subelemento 92 especial)
exclusao_ressalva <-
  (substr(d$subelem,1,1)=="3" & d$elemento=="92" &
     d$subelem=="31909205" & d$modalidade %in% c("90","91")) |
  str_detect(d$subelem, "0599")

# Exclusão C: qualquer despesa cujo NOME indique inativos (vai para 13/14)
exclusao_inativos <- str_detect(d$subnome, padrao_inativos)

# Exclusão D: elemento 05 com sub-elementos de inativos
exclusao_elem05 <- d$elemento=="05" & str_detect(d$subelem, "0505|0506|0507|0508")

c11 <- d$grupo=="1" &
  !d$elemento %in% c("01","03","34","86") &
  !exclusao_obrig & !exclusao_ressalva & !exclusao_inativos & !exclusao_elem05

vencimentos <- somar_se(c11)
cat("11 Vencimentos e Vantagens:  R$", em_mil(vencimentos), "mil\n")
11 Vencimentos e Vantagens:  R$ 210.390.865 mil

12 — Obrigações Patronais

Os encargos que o empregador (a União) paga sobre a folha — identificados pelos elementos de obrigações (13, 07) ou pelas naturezas específicas de obrigação patronal.

c12 <- (d$grupo=="1" & d$elemento %in% elem_obrigacoes) |
        d$subelem %in% nat_obrigacoes

obrigacoes <- somar_se(c12)
cat("12 Obrigações Patronais:  R$", em_mil(obrigacoes), "mil\n")
12 Obrigações Patronais:  R$ 33.313.510 mil

13 — Aposentadorias e Reformas

Inativos: pelo elemento 01 (ou 86) ou pelas naturezas específicas de aposentadoria.

c13 <- d$grupo=="1" & (d$elemento %in% c("01","86") | d$subelem %in% nat_aposentadoria)

aposentadorias <- somar_se(c13)
cat("13 Aposentadorias e Reformas:  R$", em_mil(aposentadorias), "mil\n")
13 Aposentadorias e Reformas:  R$ 127.518.286 mil

14 — Pensões

Pelo elemento 03 ou pelas naturezas específicas de pensão.

c14 <- d$grupo=="1" & (d$elemento=="03" | d$subelem %in% nat_pensoes)

pensoes <- somar_se(c14)
cat("14 Pensões:  R$", em_mil(pensoes), "mil\n")
14 Pensões:  R$ 58.894.096 mil

16 — Terceirização (substituição de mão de obra)

Contratos que substituem servidores contam como despesa de pessoal, mesmo estando no grupo 3 (Outras Despesas Correntes). É o elemento 34.

c16 <- d$grupo %in% c("1","3") & d$elemento=="34"

terceirizacao <- somar_se(c16)
cat("16 Terceirização:  R$", em_mil(terceirizacao), "mil\n")
16 Terceirização:  R$ 1.007.663 mil

Soma da Despesa Bruta (I)

despesa_bruta <- vencimentos + obrigacoes + aposentadorias + pensoes + terceirizacao
cat("= DESPESA BRUTA (I):  R$", em_mil(despesa_bruta), "mil\n")
= DESPESA BRUTA (I):  R$ 431.124.419 mil

Passo 3 — Despesas Não Computadas (II)

A LRF permite excluir do limite certas despesas — sobretudo aposentadorias e pensões custeadas por fontes específicas (fundos previdenciários próprios), que não devem pesar no teto de pessoal ativo. Identificamos essas parcelas para subtraí-las.

# 21/22/23: elementos 94, 91 e 92 do grupo Pessoal, EXCETO quando são inativos
#           custeados pelas fontes de exclusão (esses ficam para a linha 24)
nao_inativo_fonte <- !(d$fonte %in% fontes_exclusao & str_detect(d$subnome, padrao_inativos))

c21 <- d$grupo=="1" & d$elemento=="94" & nao_inativo_fonte
c22 <- d$grupo=="1" & d$elemento=="91" & nao_inativo_fonte
c23 <- d$grupo=="1" & d$elemento=="92" & nao_inativo_fonte

# 24: aposentadorias/pensões custeadas pelas fontes de exclusão (o núcleo da dedução)
c24 <- d$grupo=="1" & d$fonte %in% fontes_exclusao &
  (d$elemento %in% elem_apos_pens |
   (d$elemento %in% elem_diversos_inat & str_detect(d$subnome, padrao_inativos)))

nao_computadas <- somar_se(c21) + somar_se(c22) + somar_se(c23) + somar_se(c24)
cat("= NÃO COMPUTADAS (II):  R$", em_mil(nao_computadas), "mil\n")
= NÃO COMPUTADAS (II):  R$ 66.924.442 mil

Passo 4 — Despesa Líquida com Pessoal (III)

despesa_liquida <- despesa_bruta - nao_computadas
cat("= DESPESA LÍQUIDA (III):  R$", em_mil(despesa_liquida), "mil\n")
= DESPESA LÍQUIDA (III):  R$ 364.199.978 mil

Demonstrativo completo e validação

demonstrativo <- data.frame(
  Item = c(
    "11 Vencimentos e Vantagens",
    "12 Obrigações Patronais",
    "13 Aposentadorias e Reformas",
    "14 Pensões",
    "16 Terceirização",
    "= Despesa Bruta (I)",
    "= (-) Não Computadas (II)",
    "= Despesa Líquida (III)"
  ),
  `R$ mil` = c(
    em_mil(vencimentos), em_mil(obrigacoes), em_mil(aposentadorias),
    em_mil(pensoes), em_mil(terceirizacao),
    em_mil(despesa_bruta), em_mil(nao_computadas), em_mil(despesa_liquida)
  ),
  check.names = FALSE
)

kable(demonstrativo, align = c("l", "r"))
Item R$ mil
11 Vencimentos e Vantagens 210.390.865
12 Obrigações Patronais 33.313.510
13 Aposentadorias e Reformas 127.518.286
14 Pensões 58.894.096
16 Terceirização 1.007.663
= Despesa Bruta (I) 431.124.419
= (-) Não Computadas (II) 66.924.442
= Despesa Líquida (III) 364.199.978
# ESCOPO: apura somente despesa liquidada. Os Restos a Pagar Não Processados
# existem na base pública, mas exigiriam extração adicional — ficaram fora
# desta prova de conceito. Ver a seção "Nota metodológica", logo abaixo.

oficial_mil <- 364199978   # Despesa liquidada com pessoal — DOU (RGF 3º quadr./2025)
nosso_mil   <- round(despesa_liquida / 1000)
diferenca   <- nosso_mil - oficial_mil

cat("Nossa Despesa Líquida (somente liquidada): R$", format(nosso_mil,   big.mark=".", decimal.mark=","), "mil\n")
Nossa Despesa Líquida (somente liquidada): R$ 364.199.978 mil
cat("Oficial (mesma base):                      R$", format(oficial_mil, big.mark=".", decimal.mark=","), "mil\n")
Oficial (mesma base):                      R$ 364.199.978 mil
cat("Diferença:                                 R$", format(diferenca,   big.mark=".", decimal.mark=","), "mil\n\n")
Diferença:                                 R$ 0 mil
if (diferenca == 0) {
  cat("VALIDADO: coincide com o valor publicado no Diário Oficial (R$ mil).\n")
} else {
  cat("Diferença encontrada — investigar.\n")
}
VALIDADO: coincide com o valor publicado no Diário Oficial (R$ mil).

Nota metodológica

Este demonstrativo apura exclusivamente a despesa liquidada. Os Restos a Pagar Não Processados estão disponíveis na base pública, mas sua incorporação exigiria uma extração adicional e ampliaria a complexidade desta prova de conceito, cujo objetivo é demonstrar que o demonstrativo é replicável com dados públicos — não reproduzir seu valor integral para fins de apuração de limite.

Pela mesma razão, não se apresenta a relação Despesa Total com Pessoal / Receita Corrente Líquida nem a apuração do cumprimento dos limites dos arts. 19 e 20 da LRF. A comparação acima é apenas de valores, contra a parcela liquidada publicada no Diário Oficial.

A extensão para o valor integral, com os Restos a Pagar Não Processados, é imediata a partir do mesmo pipeline: basta acrescentar a extração correspondente e somá-la às linhas já calculadas — a estrutura de critérios permanece a mesma.

Conclusão

A Despesa com Pessoal consolidada de 2025, na sua parcela liquidada, foi reproduzida a partir de dados exclusivamente públicos e coincide com o valor oficial. Apesar da complexidade das regras — várias inclusões e exclusões previstas na LRF — cada critério está aqui explícito e nomeado, permitindo que qualquer pessoa confira ou questione uma regra específica.

É esse o valor do literate programming para a transparência: a metodologia é texto legível ao lado do código que a executa. E o mesmo vale para os limites do trabalho — o que ficou de fora está dito com todas as letras, no lugar em que o leitor vai procurar.