Código Python: converter valores por extenso (para notas e recibos)

Código Python: converter valores por extenso (para notas e recibos)
Uma nota, um recibo ou uma promissória carrega o mesmo valor duas vezes: em algarismos e por extenso. Quando as duas versões não batem, o documento volta, o pagamento espera e alguém precisa explicar a diferença. Por isso a linha do extenso é lida duas vezes, e por isso ela não deveria ser escrita por uma pessoa cansada no fim do dia.
Este programa escreve essa linha por você. Ele lê uma lista de valores de um arquivo de texto, converte cada um por extenso com os centavos e salva o resultado em um segundo arquivo, pronto para colar no documento. Serve igual para o dono de um negócio pequeno que ainda emite nota à mão, para o contador que prepara recibos em série e para o almoxarife que entrega mercadoria com um comprovante assinado.
⬇ Baixar o código (ZIP)O ZIP traz o programa comentado, os dados de exemplo e a saída exata que você deve obter ao executar. Roda com Python 3.11 ou superior e não pede para instalar mais nada: tudo o que ele usa vem com a linguagem.
O que vem na descarga
| Arquivo | Para que serve |
|---|---|
| numero_a_letras.py | O programa, comentado linha por linha |
| datos/importes.csv | Os dez valores de exemplo, cada um com a sua moeda |
| salida_ejemplo.txt | A saída que você deve obter ao executar |
| LEIAME.md | O guia do pacote, com os passos e o aviso de uso |
O que o programa faz
O programa segue sempre os mesmos passos e não precisa que você explique nada sobre o negócio:
- Lê o arquivo
datos/importes.csv, que traz quatro colunas: o documento, o valor, a moeda no singular e a moeda no plural. - Separa cada valor em parte inteira e centavos. A parte inteira é convertida com as tabelas do idioma; os centavos ficam como a fração sobre cem que se escreve no papel.
- Percorre o número em grupos de três algarismos e monta o texto completo: unidades, dezenas, centenas, milhares e milhões.
- Aplica a forma curta dos números terminados em um, para o texto sair do jeito que se fala: "um real" no singular e "vinte e um mil" sem nenhuma palavra sobrando.
- Coloca a moeda no singular quando há uma unidade e no plural quando há várias, e fecha com os centavos.
- Mostra a tabela na tela e salva o CSV de saída para você colar onde precisar.
Nada aqui depende da data do sistema nem de um serviço de internet: o mesmo arquivo de entrada produz sempre a mesma saída, e é exatamente isso que se quer quando um documento é revisado meses depois e alguém precisa saber como ele foi gerado.
Como executar
Instale o Python 3.11 ou superior do site oficial se você ainda não tem. Depois descompacte o ZIP em qualquer pasta e abra o terminal ali mesmo: os arquivos ficam soltos nessa pasta, não existe subpasta para criar nem para lembrar.
python numero_a_letras.py
Não há pacotes para instalar, nem ambiente virtual para montar, nem variáveis do sistema para mexer. O programa usa somente a biblioteca padrão: leitura de arquivos, números decimais e caminhos. Roda igual no Windows, no Linux e no Mac, porque os caminhos são montados a partir da pasta do próprio arquivo e não de um endereço escrito à mão.
Se o terminal responder que não reconhece o comando, no Windows tente py em vez de python, e confira se o Python entrou no PATH durante a instalação. É o tropeço número um de todo mundo, e se corrige uma única vez.
O código, explicado
O programa inteiro tem cerca de cento e cinquenta linhas e se lê de cima para baixo. Estes são os trechos que importam, copiados exatamente como viajam dentro do ZIP em português.
# ==========================================================================
# VALORES POR EXTENSO (PARA NOTAS E RECIBOS)
# Código Python didático para contabilidade · Kardex Tauro · kardex-tauro.muisca.co
# O que faz: lê os valores de datos/importes.csv, escreve cada um por extenso com os
# centavos e salva em salida/importes_en_letras.csv
# Testado com Python 3.11. Somente biblioteca padrão: nada para instalar.
O cabeçalho diz o que o arquivo faz, com que versão foi testado e o que ele gera. A pasta base é calculada a partir da localização do próprio programa, e dela saem o caminho de entrada e o caminho de saída. Você move a pasta inteira de lugar e o programa continua achando os seus arquivos, sem caminhos fixos que quebram quando se troca de computador ou quando o pacote vai para um servidor.
UNIDADES = ["", "um", "dois", "três", "quatro", "cinco", "seis", "sete", "oito", "nove"]
ESPECIALES = {10: "dez", 11: "onze", 12: "doze", 13: "treze", 14: "catorze", 15: "quinze", 16: "dezesseis", 17: "dezessete", 18: "dezoito", 19: "dezenove", 20: "vinte", 21: "vinte e um", 22: "vinte e dois", 23: "vinte e três", 24: "vinte e quatro", 25: "vinte e cinco", 26: "vinte e seis", 27: "vinte e sete", 28: "vinte e oito", 29: "vinte e nove"}
DECENAS = {30: "trinta", 40: "quarenta", 50: "cinquenta", 60: "sessenta", 70: "setenta", 80: "oitenta", 90: "noventa"}
CENTENAS = {1: "cento", 2: "duzentos", 3: "trezentos", 4: "quatrocentos", 5: "quinhentos", 6: "seiscentos", 7: "setecentos", 8: "oitocentos", 9: "novecentos"}
ESCALAS = {1: {"uno": "mil", "muchos": "mil"}, 2: {"uno": "um milhão", "muchos": "milhões"}}
CIEN = "cem"
CERO = "zero"
UNIR_DECENA = " e "
UNIR_CENTENA = " e "
APOCOPE = {1: "um", 21: "um"}
ETIQUETA = "VALOR POR EXTENSO"
UNIR_CENTAVOS = "E"
Aqui está a resposta à pergunta que todo mundo faz: de onde saem as palavras? De tabelas. As unidades, as palavras exatas do dez ao vinte e nove, as dezenas, as centenas e os nomes das escalas ficam em estruturas separadas, e o algoritmo que percorre tudo isso é o mesmo nos três idiomas. As tabelas de palavras do programa em português não são as do programa em espanhol nem as do inglês: cada idioma traz as suas, e é por isso que o mesmo motor serve para três idiomas sem duplicar a lógica. Os separadores e o rótulo da linha moram aqui também, então mudar a forma de imprimir a frase é mudar uma palavra nesta parte e nada mais.
| Idioma | Une a dezena com | Usa a forma curta |
|---|---|---|
| Espanhol | a letra "y" | Sim: diz un peso e veintiún mil |
| Inglês | um hífen | Não precisa: suas palavras já vêm montadas assim |
| Português | a letra "e" | Sim: diz um real e vinte e um |
A tabela acima resume o ponto: o motor é idêntico, as palavras mudam. Se um dia você precisar de outro idioma, acrescenta as tabelas dele e não toca em uma linha sequer do cálculo.
def hasta_999(numero: int) -> str:
"""Escreve por extenso do zero ao novecentos e noventa e nove."""
if numero == 0:
return ""
if numero < 10:
return UNIDADES[numero]
if numero <= 29:
return ESPECIALES[numero]
if numero < 100:
decena, unidad = (numero // 10) * 10, numero % 10
return DECENAS[decena] + (UNIR_DECENA + UNIDADES[unidad] if unidad else "")
if numero == 100:
return CIEN
centena, resto = numero // 100, numero % 100
return CENTENAS[centena] + (UNIR_CENTENA + hasta_999(resto) if resto else "")
Esta é a função que converte do zero ao novecentos e noventa e nove. Ela resolve três casos: a unidade solta, o trecho do dez ao vinte e nove -que em português são palavras próprias e não se montam por partes- e o resto, que se compõe com a dezena, a palavra que une e a unidade. O cem exato tem a sua própria linha, porque em português não se escreve "cento" quando o número é redondo, e o zero devolve texto vazio porque quem monta a frase completa decide se ele faz falta.
def apocopar(texto: str, numero: int) -> str:
"""Aplica a forma curta do número terminado em um (mantido para os idiomas que precisam)."""
if numero % 10 != 1 or numero % 100 == 11:
return texto
corta = APOCOPE.get(numero % 100, APOCOPE.get(1))
partes = texto.split(" ")
partes[-1] = corta
return " ".join(partes)
Esta função cuida dos detalhes que entregam um programa feito às pressas. Quando o número termina em um, a última palavra é encurtada ou trocada pela forma curta: é por isso que o resultado diz "um real" e não uma forma mais pesada, e "vinte e um mil" do jeito que se fala. O programa não adivinha pelo texto: ele olha o número e decide.
def entero_a_letras(numero: int) -> str:
"""Percorre o número em grupos de três algarismos e monta o texto completo."""
if numero == 0:
return CERO
if numero > 999999999:
raise ValueError("Este programa vai até novecentos e noventa e nove milhões")
partes = []
escala = 0
resto = numero
while resto > 0:
grupo = resto % 1000
resto //= 1000
if grupo == 0:
escala += 1
continue
if escala == 0:
texto = hasta_999(grupo)
elif grupo == 1:
texto = ESCALAS[escala]["uno"]
else:
texto = apocopar(hasta_999(grupo), grupo) + " " + ESCALAS[escala]["muchos"]
partes.insert(0, texto)
escala += 1
return " ".join(partes)
O coração do assunto. Percorre o número da direita para a esquerda em grupos de três algarismos, decide se cada grupo leva o nome de uma escala -mil, milhões- e guarda os trechos em ordem para devolvê-los como uma única frase. O limite do programa também mora aqui: se o número passa de novecentos e noventa e nove milhões, ele levanta um erro em vez de escrever um absurdo. Um programa que avisa quando não consegue fazer o trabalho vale mais do que um que escreve errado e ninguém percebe.
def convertir(valor: Decimal, moneda: str, moneda_plural: str) -> str:
"""Converte um valor com centavos: parte inteira, moeda e centavos sobre cem."""
entero = int(valor)
centavos = int((valor - entero).quantize(CENTAVO, rounding=ROUND_HALF_UP) * 100)
palabra = moneda if entero == 1 else moneda_plural
# # Antes da moeda também vale a forma curta: um real, trinta e um reais
letras = apocopar(entero_a_letras(entero), entero) + " " + palabra + " " + UNIR_CENTAVOS
return f"{letras} {centavos:02d}/100".upper()
Esta é a função que olha o documento como olha um contador: separa a parte inteira dos centavos, escolhe a moeda no singular ou no plural, monta a frase e a deixa em maiúsculas, que é como essa linha se imprime. Os centavos são calculados com números decimais e arredondamento comercial, então um valor como 1.234.567,89 não perde um centavo por causa da aritmética binária dos computadores.
O que você vai ver na tela
Ao executar, é isto que aparece. A primeira coluna é o documento, a segunda é o valor e a terceira é a linha que você vai colar:
VALORES POR EXTENSO Código Python didático · Kardex Tauro · kardex-tauro.muisca.co Documento Valor Por extenso ------------------------------------------------------------------------------------------------ RECIBO 001 0,75 VALOR POR EXTENSO: ZERO REAIS E 75/100 RECIBO 002 1,00 VALOR POR EXTENSO: UM REAL E 00/100 NOTA FISCAL 001 15,50 VALOR POR EXTENSO: QUINZE REAIS E 50/100 NOTA FISCAL 002 21,00 VALOR POR EXTENSO: VINTE E UM REAIS E 00/100 NOTA FISCAL 003 100,00 VALOR POR EXTENSO: CEM REAIS E 00/100 NOTA FISCAL 004 101,40 VALOR POR EXTENSO: CENTO E UM REAIS E 40/100 NOTA FISCAL 005 1.000,00 VALOR POR EXTENSO: MIL REAIS E 00/100 NOTA FISCAL 006 1.001,00 VALOR POR EXTENSO: MIL UM REAIS E 00/100 NOTA FISCAL 007 21.000,00 VALOR POR EXTENSO: VINTE E UM MIL REAIS E 00/100 NOTA FISCAL 008 1.234.567,89 VALOR POR EXTENSO: UM MILHÃO DUZENTOS E TRINTA E QUATRO MIL QUINHENTOS E SESSENTA E SETE REAIS E 89/100 ------------------------------------------------------------------------------------------------ Documentos convertidos: 10 Arquivo gerado: salida/importes_en_letras.csv
A linha que você vai consultar mais vezes é a última: um valor de 1.234.567,89 sai como UM MILHÃO DUZENTOS E TRINTA E QUATRO MIL QUINHENTOS E SESSENTA E SETE REAIS E 89/100. Repare em três detalhes que valem a descarga. O programa escreve "MIL" e não uma forma mais longa. Escreve "VINTE E UM MIL" com a ligação que o idioma pede, e não uma palavra colada. E fecha com os centavos sobre cem, exatamente como se escreve no papel.
No fim a tabela diz quantos documentos foram convertidos e em que arquivo o resultado ficou. Abra esse arquivo no Excel, no LibreOffice ou em qualquer editor de texto e copie a coluna do extenso direto para o documento que você está preparando.
Erros comuns e dicas
- Deixe apenas o número na coluna do valor: se você colar o símbolo da moeda ou os separadores de milhar, o programa para com um erro de conversão. É de propósito, para nunca converter um dado mal escrito.
- A moeda se troca no arquivo, não no código: as colunas da moeda e do plural decidem se o texto diz "real" ou "reais".
- Os centavos são calculados com decimais e arredondamento comercial. Se o seu documento exige outra regra, muda-se em uma linha e se executa de novo.
- O teto são novecentos e noventa e nove milhões. Acima disso o programa avisa com um erro em vez de inventar palavras.
- Guarde o arquivo de saída junto do documento. Quando alguém revisar a nota seis meses depois, você vai querer a mesma lista que saiu do programa.
- Teste primeiro com os dados de exemplo e compare com a saída esperada. Se bater, você já sabe que o seu Python está bem instalado e que qualquer problema depois está nos seus dados.
Quando isso não basta
Um programa de console resolve a conversão e mais nada. Ele não carrega o seu estoque, não emite a nota, não diz qual item está perto de acabar e não registra quem autorizou cada saída. Se a sua operação ainda cabe em uma planilha e em alguns scripts, este ZIP é o que você precisa e vai servir por anos.
Quando o negócio cresce, a linha do extenso deixa de ser um problema e vira um detalhe de um problema maior: emitir nota, dar baixa na ficha de estoque, controlar as compras e saber o que realmente saiu. É aí que o Kardex Tauro faz sentido: é o programa que organiza o estoque e os documentos do negócio, e este código gratuito é o degrau anterior, o que você usa enquanto o volume ainda permite.
⬇ Baixar o código (ZIP)Baixe o ZIP, execute com os seus próprios valores e compare a primeira saída com a que você fez à mão. Se servir, passe o arquivo para o contador conferir duas ou três linhas: é a melhor maneira de confirmar que a conversão ficou certa antes de ela chegar aos documentos reais.