Código Python: convertir importes a letras (para facturas y recibos)

Código Python: convertir importes a letras (para facturas y recibos)
Un recibo, una factura o un pagaré llevan la misma cantidad dos veces: en números y en letras. Cuando las dos versiones no dicen lo mismo, el documento se devuelve, el pago se aplaza y alguien tiene que explicar la diferencia. Por eso el renglón de las letras se revisa con lupa, y por eso conviene que no lo escriba una persona cansada al final de la jornada.
Este programa escribe ese renglón por ti. Lee una lista de importes desde un archivo de texto, convierte cada uno a letras con sus centavos y guarda el resultado en otro archivo, listo para copiar y pegar en el documento. Sirve igual para el dueño de un negocio pequeño que todavía factura a mano, para el contador que prepara recibos en serie y para el almacenista que entrega mercancía con un comprobante firmado.
⬇ Descargar el código (ZIP)El ZIP trae el programa comentado, los datos de ejemplo y la salida exacta que debes obtener cuando lo ejecutes. Corre con Python 3.11 o superior y no pide instalar nada más: todo lo que usa viene con el lenguaje.
Lo que encuentras en la descarga
| Archivo | Para qué sirve |
|---|---|
| numero_a_letras.py | El programa, comentado línea por línea |
| datos/importes.csv | Los diez importes de ejemplo, cada uno con su moneda |
| salida_ejemplo.txt | La salida que debes obtener al ejecutarlo |
| LEEME.md | La guía del paquete, con los pasos y el aviso de uso |
Qué hace el programa
El programa sigue siempre los mismos pasos y no necesita que le expliques nada del negocio:
- Lee el archivo
datos/importes.csv, que trae cuatro columnas: el documento, el valor, la moneda en singular y la moneda en plural. - Separa cada importe en parte entera y centavos. La parte entera se convierte con las tablas del idioma; los centavos quedan como la fracción sobre cien que se escribe en el papel.
- Recorre el número en grupos de tres cifras y arma el texto completo: unidades, decenas, centenas, miles y millones.
- Aplica la forma corta de los números que terminan en uno, porque en español no se escribe "uno mil" ni "veintiuno mil".
- Pone la moneda en singular si hay una sola unidad y en plural si hay varias, y remata con los centavos.
- Imprime la tabla en pantalla y guarda el CSV de salida para que lo pegues donde lo necesites.
Nada depende de la fecha del sistema ni de un servicio de internet: el mismo archivo de entrada produce siempre la misma salida, y eso es justo lo que se quiere cuando un documento se revisa meses después y hay que saber con qué se generó.
Cómo ejecutarlo
Instala Python 3.11 o superior desde el sitio oficial si todavía no lo tienes. Después descomprime el ZIP en cualquier carpeta y abre la terminal justo ahí: los archivos quedan sueltos en esa misma carpeta, no hay ninguna subcarpeta que crear ni que recordar.
python numero_a_letras.py
No hay que instalar paquetes, ni crear un entorno virtual, ni tocar variables del sistema. El programa usa solo la biblioteca estándar: lectura de archivos, números decimales y rutas. Corre igual en Windows, en Linux y en Mac, porque las rutas se arman a partir de la carpeta del propio archivo y no de una dirección escrita a mano.
Si la terminal te responde que no reconoce el comando, en Windows prueba con py en lugar de python, y revisa que Python haya quedado en el PATH durante la instalación. Es el tropiezo número uno de todo el mundo, y se corrige una sola vez.
El código, explicado
El programa completo ronda los ciento cincuenta renglones y se lee de arriba abajo. Estos son los tramos que importan, copiados tal como viajan dentro del ZIP en español.
# ==========================================================================
# IMPORTES A LETRAS (PARA FACTURAS Y RECIBOS)
# Código Python didáctico para contabilidad · Kardex Tauro · kardex-tauro.muisca.co
# Qué hace: lee los importes de datos/importes.csv, escribe cada uno en letras con sus
# centavos y lo guarda en salida/importes_en_letras.csv
# Probado con Python 3.11. Solo biblioteca estándar: no hay que instalar nada.
El encabezado dice qué hace el archivo, con qué versión se probó y qué genera. La carpeta base se calcula a partir de la ubicación del propio programa, y de ahí salen la ruta de entrada y la ruta de salida. Mueves la carpeta completa de sitio y el programa sigue encontrando sus archivos, sin rutas fijas que se rompan al cambiar de computador o al pasar el paquete a un servidor.
UNIDADES = ["", "uno", "dos", "tres", "cuatro", "cinco", "seis", "siete", "ocho", "nueve"]
ESPECIALES = {10: "diez", 11: "once", 12: "doce", 13: "trece", 14: "catorce", 15: "quince", 16: "dieciséis", 17: "diecisiete", 18: "dieciocho", 19: "diecinueve", 20: "veinte", 21: "veintiuno", 22: "veintidós", 23: "veintitrés", 24: "veinticuatro", 25: "veinticinco", 26: "veintiséis", 27: "veintisiete", 28: "veintiocho", 29: "veintinueve"}
DECENAS = {30: "treinta", 40: "cuarenta", 50: "cincuenta", 60: "sesenta", 70: "setenta", 80: "ochenta", 90: "noventa"}
CENTENAS = {1: "ciento", 2: "doscientos", 3: "trescientos", 4: "cuatrocientos", 5: "quinientos", 6: "seiscientos", 7: "setecientos", 8: "ochocientos", 9: "novecientos"}
ESCALAS = {1: {"uno": "mil", "muchos": "mil"}, 2: {"uno": "un millón", "muchos": "millones"}}
CIEN = "cien"
CERO = "cero"
UNIR_DECENA = " y "
UNIR_CENTENA = " "
APOCOPE = {1: "un", 21: "veintiún"}
ETIQUETA = "SON"
UNIR_CENTAVOS = "CON"
Aquí está la respuesta a la pregunta que más se repite: ¿de dónde saca las palabras? De tablas. Las unidades, las palabras exactas del diez al veintinueve, las decenas, las centenas y los nombres de las escalas viven en estructuras separadas, y el algoritmo que las recorre es el mismo en los tres idiomas. Las tablas de palabras de este programa en español no son las del programa en inglés ni las del portugués: cada idioma trae las suyas, y por eso el mismo motor sirve para tres idiomas sin duplicar la lógica. También viven aquí los separadores y las etiquetas del renglón, así que cambiar cómo se imprime la frase es cambiar una palabra de esta parte y nada más.
| Idioma | Une la decena con | Usa forma corta |
|---|---|---|
| Español | la letra "y" | Sí: dice un peso y veintiún mil |
| Inglés | un guion | No la necesita: sus palabras ya vienen armadas |
| Portugués | la letra "e" | Sí: dice um real y vinte e um |
La tabla anterior resume el punto: el motor es idéntico, las palabras cambian. Si mañana necesitas otro idioma, agregas sus tablas y no tocas una sola línea de cálculo.
def hasta_999(numero: int) -> str:
"""Escribe en letras del cero al novecientos noventa y nueve."""
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 es la función que convierte del cero al novecientos noventa y nueve. Resuelve tres casos: la unidad suelta, el tramo del diez al veintinueve -que en español son palabras propias y no se pueden armar por partes- y el resto, que se compone con la decena, la palabra que une y la unidad. El cien exacto tiene su propio renglón, porque en español no se dice "ciento" cuando la cifra es redonda, y el cero devuelve texto vacío porque quien arma la frase completa decide si hace falta.
def apocopar(texto: str, numero: int) -> str:
"""Aplica la forma corta del número que termina en uno (un peso, veintiún mil)."""
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 función cuida los detalles que delatan a un programa hecho a la carrera. Cuando el número termina en uno, la última palabra se acorta: por eso el resultado dice "un peso" y no "uno peso", y "veintiún mil" y no "veintiuno mil". El programa no adivina por el texto: mira el número y decide.
def entero_a_letras(numero: int) -> str:
"""Recorre el número en grupos de tres cifras y arma el texto completo."""
if numero == 0:
return CERO
if numero > 999999999:
raise ValueError("Este programa llega hasta novecientos noventa y nueve millones")
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)
El corazón del asunto. Recorre el número de derecha a izquierda en grupos de tres cifras, decide si cada grupo lleva el nombre de una escala -mil, millones- y guarda los tramos en orden para devolverlos como una sola frase. Aquí está también el límite del programa: si el número pasa de novecientos noventa y nueve millones, levanta un error en lugar de escribir un disparate. Un programa que avisa cuando no puede hacer el trabajo vale más que uno que escribe mal y nadie lo nota.
def convertir(valor: Decimal, moneda: str, moneda_plural: str) -> str:
"""Convierte un importe con centavos: parte entera, moneda y centavos sobre cien."""
entero = int(valor)
centavos = int((valor - entero).quantize(CENTAVO, rounding=ROUND_HALF_UP) * 100)
palabra = moneda if entero == 1 else moneda_plural
# # Delante de la moneda también hay forma corta: un peso, treinta y un pesos
letras = apocopar(entero_a_letras(entero), entero) + " " + palabra + " " + UNIR_CENTAVOS
return f"{letras} {centavos:02d}/100".upper()
Esta es la función que mira el documento como lo mira un contador: separa la parte entera de los centavos, elige la moneda en singular o en plural, arma la frase y la deja en mayúsculas, que es como se imprime este renglón. Los centavos se calculan con números decimales y redondeo comercial, así que un importe como 1.234.567,89 no pierde un centavo por culpa de la aritmética binaria de las computadoras.
Qué vas a ver en pantalla
Al ejecutarlo, esto es lo que aparece. La primera columna es el documento, la segunda el importe y la tercera el renglón que vas a pegar:
IMPORTES A LETRAS Código Python didáctico · Kardex Tauro · kardex-tauro.muisca.co Documento Valor En letras ------------------------------------------------------------------------------------------------ RECIBO 001 0,75 SON: CERO PESOS CON 75/100 RECIBO 002 1,00 SON: UN PESO CON 00/100 FACTURA 001 15,50 SON: QUINCE PESOS CON 50/100 FACTURA 002 21,00 SON: VEINTIÚN PESOS CON 00/100 FACTURA 003 100,00 SON: CIEN PESOS CON 00/100 FACTURA 004 101,40 SON: CIENTO UN PESOS CON 40/100 FACTURA 005 1.000,00 SON: MIL PESOS CON 00/100 FACTURA 006 1.001,00 SON: MIL UN PESOS CON 00/100 FACTURA 007 21.000,00 SON: VEINTIÚN MIL PESOS CON 00/100 FACTURA 008 1.234.567,89 SON: UN MILLÓN DOSCIENTOS TREINTA Y CUATRO MIL QUINIENTOS SESENTA Y SIETE PESOS CON 89/100 ------------------------------------------------------------------------------------------------ Documentos convertidos: 10 Archivo generado: salida/importes_en_letras.csv
El renglón que más vas a consultar es el último: un importe de 1.234.567,89 sale como UN MILLÓN DOSCIENTOS TREINTA Y CUATRO MIL QUINIENTOS SESENTA Y SIETE PESOS CON 89/100. Fíjate en tres detalles que valen la pena. El programa escribe "MIL" y no "UNO MIL". Escribe "VEINTIÚN MIL" con la forma corta, porque así se dice. Y remata con los centavos sobre cien, que es exactamente como se escriben en el papel.
Al final la tabla te dice cuántos documentos convirtió y en qué archivo quedó el resultado. Ese archivo lo abres con Excel, con LibreOffice o con cualquier editor de texto, y puedes copiar la columna de las letras directamente al documento que estés armando.
Errores comunes y consejos
- En la columna del valor deja solo el número: si copias el símbolo de la moneda o los separadores de miles, el programa se detiene con un error de conversión. Es a propósito, para que no convierta un dato mal escrito.
- La moneda se cambia en el archivo, no en el código: las columnas de moneda y plural son las que deciden si el texto dice "peso" o "pesos".
- Los centavos se calculan con decimales y redondeo comercial. Si tu documento exige otra regla, se cambia en una línea y se vuelve a ejecutar.
- El tope son novecientos noventa y nueve millones. Para cifras mayores el programa avisa con un error en vez de inventar palabras.
- Guarda el archivo de salida junto al documento. Cuando alguien revise la factura seis meses después, vas a querer la misma lista que salió del programa.
- Prueba primero con los datos de ejemplo y compara contra la salida esperada. Si coincide, ya sabes que tu Python está bien instalado y que el problema, si aparece, está en tus datos.
Cuándo esto no alcanza
Un programa de consola resuelve la conversión y nada más. No lleva tu inventario, no emite la factura, no te dice qué producto está por agotarse y no guarda quién autorizó cada salida. Si tu operación todavía cabe en una hoja de cálculo y en un par de scripts, este ZIP es lo que necesitas y te va a servir por años.
Cuando el negocio crece, el renglón de las letras deja de ser un problema y se convierte en un detalle de un problema mayor: facturar, descontar del inventario, controlar las compras y saber qué se vendió. Ahí es donde Kardex Tauro tiene sentido: es el programa que ordena el inventario y los documentos del negocio, y este código gratuito es el peldaño anterior, el que usas mientras el volumen todavía lo permite.
⬇ Descargar el código (ZIP)Descarga el ZIP, ejecútalo con tus propios importes y compara la primera salida con la que hiciste a mano. Si te sirve, pásale el archivo al contador para que revise dos o tres renglones: es la mejor manera de confirmar que la conversión quedó bien antes de llevarla a los documentos reales.