Cantidad con letra: convierte un importe a texto para facturas, cheques y contratos

Escribe un importe y obtén la cantidad con letra lista para copiar, con el formato de las facturas y los cheques en México: MIL DOSCIENTOS CINCUENTA PESOS 50/100 M.N. También en dólares y euros, con los centavos en número o en palabras. Todo se calcula en tu navegador.

Ejemplos:

Comprueba siempre que el texto coincide con la cifra. En un cheque o un pagaré, si la cantidad en número y la cantidad con letra no coinciden, la ley mexicana da por buena la escrita con letra.

Cómo se escribe una cantidad con letra

  • Los centavos van como fracción de 100: 1,250.50 es «MIL DOSCIENTOS CINCUENTA PESOS 50/100 M.N.». M.N. quiere decir «moneda nacional». Si no hay centavos, se escribe 00/100.
  • «Un», no «uno», delante de pesos, mil y millones: «veintiún pesos», «treinta y un mil pesos», «ciento un millones». Es una regla de la gramática: el «uno» pierde la o delante de un sustantivo masculino.
  • «Mil», no «un mil»: 1,000 es «MIL PESOS» y 1,000,000 es «UN MILLÓN DE PESOS».
  • «De» solo con millones redondos: «UN MILLÓN DE PESOS», «DOS MILLONES DE PESOS», pero «UN MILLÓN QUINIENTOS MIL PESOS».
  • Mil millones, no un billón: en español, un billón es un millón de millones. 1,000,000,000 se escribe «MIL MILLONES».
  • Del 16 al 29, en una sola palabra: dieciséis, veintidós, veintitrés, veintiséis… con acento. En mayúsculas también se acentúa; si un sistema no acepta acentos, quita la marca de «Con acentos».

La herramienta acepta la cantidad con o sin signo de pesos y con comas de miles, y también con coma decimal (1.250,50). Si tiene más de dos decimales, la redondea al centavo y te avisa. Llega hasta 999,999,999,999.99.

Cantidad con letra en Python

La misma conversión, sin dependencias. Usa Decimal para que los centavos no se pierdan por redondeos de coma flotante:

from decimal import Decimal, ROUND_HALF_UP

UNIDADES = ["", "uno", "dos", "tres", "cuatro", "cinco", "seis", "siete", "ocho", "nueve",
            "diez", "once", "doce", "trece", "catorce", "quince", "dieciséis", "diecisiete",
            "dieciocho", "diecinueve", "veinte", "veintiuno", "veintidós", "veintitrés",
            "veinticuatro", "veinticinco", "veintiséis", "veintisiete", "veintiocho", "veintinueve"]
DECENAS = ["", "", "", "treinta", "cuarenta", "cincuenta", "sesenta", "setenta", "ochenta", "noventa"]
CENTENAS = ["", "ciento", "doscientos", "trescientos", "cuatrocientos", "quinientos",
            "seiscientos", "setecientos", "ochocientos", "novecientos"]


def hasta_999(n, corto):
    """Del 1 al 999. Con corto=True, «uno» pasa a «un» (veintiún pesos, treinta y un mil)."""
    if n == 100:
        return "cien"
    c, r = divmod(n, 100)
    partes = [CENTENAS[c]] if c else []
    if r:
        t = UNIDADES[r] if r < 30 else DECENAS[r // 10] + (" y " + UNIDADES[r % 10] if r % 10 else "")
        if corto and t.endswith("uno"):
            t = t[:-3] + ("ún" if t == "veintiuno" else "un")
        partes.append(t)
    return " ".join(partes)


def hasta_999999(n, corto):
    miles, resto = divmod(n, 1000)
    partes = []
    if miles == 1:
        partes.append("mil")
    elif miles:
        partes.append(hasta_999(miles, True) + " mil")
    if resto:
        partes.append(hasta_999(resto, corto))
    return " ".join(partes)


def cantidad_con_letra(cantidad, moneda=("peso", "pesos"), sufijo="M.N."):
    cantidad = Decimal(str(cantidad)).quantize(Decimal("0.01"), ROUND_HALF_UP)
    if not 0 <= cantidad < 10 ** 12:
        raise ValueError("La cantidad va de 0 a 999,999,999,999.99")
    enteros, centavos = divmod(int(cantidad * 100), 100)
    millones, resto = divmod(enteros, 10 ** 6)
    partes = []
    if millones:
        partes.append("un millón" if millones == 1 else hasta_999999(millones, True) + " millones")
    if resto:
        partes.append(hasta_999999(resto, True))
    texto = " ".join(partes) or "cero"
    if millones and not resto:
        texto += " de"                      # un millón de pesos
    texto += f" {moneda[enteros != 1]} {centavos:02d}/100 {sufijo}"
    return texto.upper()


print(cantidad_con_letra("1250.50"))
print(cantidad_con_letra(21000000))
print(cantidad_con_letra("99.99", ("dólar", "dólares"), "USD"))
$ python3 letra.pyMIL DOSCIENTOS CINCUENTA PESOS 50/100 M.N.
VEINTIÚN MILLONES DE PESOS 00/100 M.N.
NOVENTA Y NUEVE DÓLARES 99/100 USD

Pásale la cantidad como texto o como Decimal, no como float: 0.29 en coma flotante es en realidad 0.28999…, y con otros redondeos se perdería un centavo.

Otras herramientas para México: validadores de RFC, CURP, CLABE y NSS, y el validador de CFDI 4.0. Todas, en decodigo.com/herramientas.

Reglas de escritura de los numerales: Diccionario panhispánico de dudas, «uno» y «millón». Prevalencia de la cantidad escrita con letra: Ley General de Títulos y Operaciones de Crédito, artículo 16. La herramienta y la función de Python se compararon con la biblioteca num2words en más de 11,000 cantidades: coinciden en todas salvo donde num2words se salta la gramática («veintiuno mil», «un millón pesos»).