Código Python: comparar PEPS, UEPS e média ponderada com os mesmos dados

Código Python: comparar PEPS, UEPS e média ponderada com os mesmos dados
Pergunte a três pessoas da mesma empresa quanto custou uma venda específica e é bem possível receber três respostas diferentes sem que ninguém esteja errado. A briga não é de matemática: é sobre qual compra é baixada quando a mercadoria sai. Se no dia 5 de março saíram 90 sacos de cimento, eles vieram da compra de 100 sacos a 18 000 do dia 2 de março, ou da compra de 60 sacos a 20 000 do dia 9, que naquela data ainda nem tinha chegado ao depósito? Na contabilidade as duas respostas existem: o PEPS diz que saiu primeiro o que entrou primeiro, o UEPS diz que saiu primeiro o que entrou por último, e a média ponderada reparte o custo entre tudo o que estava disponível.
Este programa coloca os três métodos para rodar sobre exatamente os mesmos dez movimentos e as mesmas 360 unidades vendidas. No fim ele mostra uma única tabela com o custo das vendas de cada método, o estoque final em unidades e em valor, o lucro bruto e o quanto cada método se afasta da média. Foi feito para o dono da pequena empresa que quer entender por que o relatório do contador não bate com a conta de cabeça; para o contador que precisa mostrar o efeito de uma troca de método sem montar uma planilha do zero; e para o auxiliar de estoque ou de contabilidade que confere notas fiscais contra a ficha de estoque do sistema e desconfia que tem algo fora do lugar.
Não é preciso saber programar. Descompacte a pasta, abra o terminal e rode um comando. O programa usa apenas a biblioteca padrão do Python, então não há nada mais para instalar, e ele se comporta igual no Windows, no Linux e no Mac.
⬇ Baixar o código (ZIP)| O que vem no ZIP | Para que serve |
|---|---|
| comparar_metodos.py | O programa, comentado linha por linha |
| datos/movimientos.csv | Dez movimentos de exemplo: compras e vendas de dois produtos |
| datos/precios.csv | Preço de venda de cada produto, necessário para o lucro bruto |
| salida_ejemplo.txt | A saída que você deve obter ao executar, para comparar |
| LEIAME.md | O guia do pacote e o detalhe de cada arquivo |
| Python 3.11 ou superior | O único requisito: é a própria linguagem |
O que o programa faz
- Lê
datos/movimientos.csv, com as colunas produto, data, tipo, detalhe, quantidade e custo_unitario. - Lê
datos/precios.csv(produto e preco_venda): só precisa dele para calcular o lucro bruto de cada método sobre a mesma venda líquida. - Calcula o PEPS: cada compra abre um lote com sua quantidade e seu custo unitário, e cada venda consome o lote mais antigo.
- Calcula o UEPS: a mesma venda, mas consumindo o lote mais recente.
- Calcula a média ponderada: refaz o custo médio depois de cada compra e avalia cada venda pela média vigente naquele instante.
- Soma os três resultados, obtém o lucro bruto e a diferença de cada método contra a média.
- Mostra a tabela comparativa, o saldo final por método e uma conclusão, e salva a mesma tabela em
salida/comparacion.csvpara abrir no Excel.
Como executar
A única coisa que você precisa ter instalada é o Python 3.11 ou superior. Se não tiver certeza da versão, digite no terminal:
python --version
Depois descompacte o ZIP numa pasta fácil de achar, abra o terminal e entre nessa pasta. No Windows você pode digitar:
cd C:\caminho\para\a\pasta\descompactada
E o programa roda com uma única linha:
python comparar_metodos.py
É tudo. Não tem ambiente virtual para criar, não tem pandas para instalar e não precisa de internet: o programa se apoia só em csv, decimal e pathlib, que já vêm com o Python. Se o arquivo de movimentos estiver no lugar certo, a tabela aparece na tela em menos de um segundo e o CSV fica salvo na pasta salida.
O código, explicado
O programa inteiro cabe em umas duzentas linhas. Estes são os trechos que explicam por que a mesma empresa chega a três custos diferentes.
1. O que ele faz e com o quê. O cabeçalho diz isso sem rodeios:
# ==========================================================================
# PEPS · UEPS · MÉDIA PONDERADA
# Código Python didático para estoque · Kardex Tauro · kardex-tauro.muisca.co
# O que faz: avalia as MESMAS saídas com PEPS (FIFO), UEPS (LIFO) e média
# ponderada, compara os três resultados e salva em salida/comparacion.csv
# Testado com Python 3.11. Somente biblioteca padrão: nada para instalar.
# ==========================================================================
Repare na última linha: somente biblioteca padrão. É por isso que você baixa, executa e funciona, sem briga com instalação, versão ou permissão.
2. Cada compra cria um lote. Aqui está o coração da questão. O programa não guarda um custo único do produto: ele guarda uma lista de lotes, e cada lote é um saco com sua quantidade e seu custo unitário.
def por_lotes(movimientos: list[dict], ultimo_primero: bool) -> dict:
"""Motor de lotes: a venda consome o lote mais antigo (PEPS) ou o mais recente (UEPS)."""
estado = {}
for movimiento in movimientos:
producto = movimiento["produto"]
cuenta = estado.setdefault(producto, {"lotes": [], "cantidad": CERO, "valor": CERO,
"vendidas": CERO, "costo_ventas": CERO})
cantidad = Decimal(movimiento["quantidade"])
if movimiento["tipo"] == TIPO_ENTRADA:
# # Cada compra cria um lote com sua quantidade e seu custo unitário
costo = Decimal(movimiento["custo_unitario"])
cuenta["lotes"].append({"cantidad": cantidad, "costo": costo})
cuenta["cantidad"] += cantidad
cuenta["valor"] += redondear(cantidad * costo)
Essa lista de lotes é a memória que faz o custo de uma venda depender de qual compra foi baixada. Sem lotes não existe PEPS nem UEPS: haveria um custo só e uma resposta só.
3. A venda consome lotes e é aí que tudo se decide. Quando chega uma venda, o programa vai pegando lotes até despachar a quantidade pedida. Uma única venda pode precisar de parte de um lote e de parte de outro, e é por isso que existe um laço while: a venda não para porque um lote acabou, ela segue para o próximo.
elif movimiento["tipo"] == TIPO_SALIDA:
# # Uma venda pode consumir VÁRIOS lotes, com custos diferentes
indice = -1 if ultimo_primero else 0
pendiente, costo_total = cantidad, CERO
while pendiente > 0:
lote = cuenta["lotes"][indice]
toma = min(pendiente, lote["cantidad"])
costo_total += redondear(toma * lote["costo"])
lote["cantidad"] -= toma
pendiente -= toma
if lote["cantidad"] == 0:
cuenta["lotes"].pop(indice)
cuenta["cantidad"] -= cantidad
cuenta["valor"] -= costo_total
Leia com calma a primeira linha desse trecho. Toda a diferença entre PEPS e UEPS mora aí: no índice do lote que é pego. Com o índice zero ele pega o lote mais antigo, ou seja, PEPS. Com o índice menos um ele pega o último que entrou, ou seja, UEPS. Todo o resto é idêntico. Por isso os dois métodos vendem as mesmas 360 unidades e só o custo muda: o que muda é de onde veio a mercadoria, não quanta saiu.
4. Dois nomes para o mesmo motor. As funções públicas de PEPS e UEPS são quase uma piada: chamam o mesmo motor de lotes com um interruptor em falso ou em verdadeiro.
def peps(movimientos: list[dict]) -> dict:
"""Método PEPS (FIFO): primeiro que entra, primeiro que sai."""
# # PEPS: a venda consome primeiro o lote MAIS ANTIGO (fila FIFO)
return por_lotes(movimientos, False)
def ueps(movimientos: list[dict]) -> dict:
"""Método UEPS (LIFO): último que entra, primeiro que sai."""
# # UEPS: a venda consome primeiro o lote MAIS RECENTE (pilha LIFO)
return por_lotes(movimientos, True)
Isso é o que muita gente não vê quando discute métodos de estoque: não são dois programas diferentes, é a mesma mecânica com um critério diferente sobre qual lote sai primeiro.
5. A média ponderada não usa lotes. Ela usa uma gaveta única por produto: soma quantidades e valores, e avalia cada venda pela média vigente antes de baixá-la.
elif movimiento["tipo"] == TIPO_SALIDA:
# Cada venta se valora al promedio vigente ANTES de descontarla.
vigente = redondear(cuenta["valor"] / cuenta["cantidad"]) if cuenta["cantidad"] else CERO
costo = redondear(cantidad * vigente)
cuenta["cantidad"] -= cantidad
cuenta["valor"] -= costo
cuenta["vendidas"] += cantidad
cuenta["costo_ventas"] += costo
Esse detalhe do antes importa: se o programa baixasse primeiro e tirasse a média depois, cada venda mudaria o próprio custo com que é avaliada. E tem uma segunda consequência que vale avisar: esta é uma média móvel, refeita a cada compra. Se o seu contador usa média mensal ou periódica, os números não vão bater centavo por centavo, e não é que um esteja errado: são duas formas diferentes de tirar médias.
6. A conclusão, sem números digitados à mão. O programa não imprime um texto fixo: ele procura entre os resultados o estoque mais caro e o mais barato, e mostra a diferença.
# # Com custos em alta o PEPS deixa o estoque mais caro e o UEPS o mais barato; a média divide.
# # Qual pode ser usado NÃO é o programa que decide: manda a norma contábil local.
caro = max(filas, key=lambda fila: fila["inv_val"])
barato = min(filas, key=lambda fila: fila["inv_val"])
diferencia = redondear(caro["inv_val"] - barato["inv_val"])
O comentário do código explica isso melhor que qualquer manual, e vale repetir: com custos em alta o PEPS deixa o estoque pelo custo mais recente, que é o mais caro, e o menor custo das vendas; o UEPS faz o contrário; a média fica no meio. E fecha com o aviso mais honesto de todo o programa: qual método pode ser usado não é o programa que decide, quem manda é a norma contábil do seu país e a política interna da empresa.
O que você vai ver na tela
Com os dados de exemplo (dois produtos, dez movimentos, 360 unidades vendidas e 190 ainda no depósito) a tabela sai assim:
| Método | Custo das vendas | Estoque final (valor) | Lucro bruto | Diferença vs média |
|---|---|---|---|---|
| PEPS (FIFO) | 3.240.000,00 | 1.042.500,00 | 1.080.000,00 | -36.397,70 |
| UEPS (LIFO) | 3.312.000,00 | 970.500,00 | 1.008.000,00 | 35.602,30 |
| Média ponderada | 3.276.397,70 | 1.006.102,30 | 1.043.602,30 | 0,00 |
O bloco final da tela diz sem enrolação:
CONCLUSÃO Estoque mais caro: PEPS (FIFO) = 1.042.500,00 Estoque mais barato: UEPS (LIFO) = 970.500,00 Diferença (é só de método, não é dinheiro perdido): 72.000,00
Vale parar na última linha. Entre o estoque mais caro e o mais barato existe uma diferença de 72 000, e não é dinheiro perdido nem ganho: quando essas 190 unidades forem vendidas, a diferença se inverte. O que o método decide é em que mês o custo aparece, não quanto a mercadoria custa no total. Por isso convém olhar o lucro de um período longo e não o de um mês isolado quando se compara um método com outro. O mesmo resultado fica em salida/comparacion.csv em formato simples, pronto para a planilha: 3240000.00 de custo das vendas no PEPS, 3312000.00 no UEPS e 3276397.70 na média, com estoques finais de 1042500.00 e 970500.00 e uma diferença de 72000.00.
Erros comuns e dicas
- Cuidado com nome de produto escrito errado. O programa separa os saldos pelo nome do produto. Se numa linha você escreveu Cimento cinza 50 kg e em outra Cimento Cinza 50 KG, para o programa são dois produtos e os saldos ficam partidos.
- As vendas entram sem custo unitário. No arquivo de exemplo a coluna fica vazia nas vendas, e é assim que deve ficar: o custo quem calcula é o programa, conforme o método que está sendo avaliado.
- Se uma venda for maior que o saldo, o programa para com erro. Isso é de propósito: quer dizer que falta registrar uma compra ou que a venda foi lançada duas vezes. Confira o arquivo, não tape o problema.
- Carregue os movimentos em ordem de data. Os três métodos dependem da ordem: PEPS e UEPS porque consomem lotes num sentido ou no outro, e a média porque é refeita a cada movimento. Um arquivo fora de ordem dá um resultado que não significa nada.
- Os centavos da média são normais. A média ponderada quase nunca dá número redondo, por isso o programa trabalha com centavos e os carrega adiante. Se você comparar com um sistema que arredonda, vai achar diferenças pequenas.
- Não compare métodos diferentes entre meses. O efeito do método só aparece com os mesmos movimentos: se você muda o método e muda o período, nunca vai saber o que causou a diferença.
- Guarde o CSV de cada teste. A tabela comparativa é a prova da sua decisão. Se mais tarde alguém perguntar por que se usa um método e não outro, é esse arquivo que responde.
Quando isso não é suficiente
Este programa é um laboratório de mesa: lê um CSV, calcula e termina. Ele não identifica usuários, não registra quem editou um movimento, não controla vários depósitos, não cruza pedidos de compra com notas fiscais e não deixa rastro para uma auditoria. Quando o estoque não cabe mais numa planilha e num script —porque são vários auxiliares, centenas de itens e movimentos todos os dias— o caminho sensato é um software de estoque que mantenha a ficha de estoque sozinho. O Kardex Tauro é um programa gratuito que faz exatamente isso: organiza o estoque e mantém os saldos sem que você precise mexer em arquivos de dados. Este código serve para a etapa anterior: entender a lógica, testar o efeito dos três métodos e saber o que você está olhando quando o sistema mostrar um custo.
⬇ Baixar o código (ZIP)Baixe o ZIP, rode com os dados de exemplo e depois troque o arquivo pelos seus próprios movimentos. Quando você vir os três custos lado a lado vai entender de uma vez por que o método de estoque é uma decisão contábil e não uma simples conta: a mercadoria é a mesma, as unidades vendidas são as mesmas e, mesmo assim, o lucro do mês muda.