Código python para validar dígito del RIF

1,974 views
Skip to first unread message

Juan Marquez

unread,
Oct 7, 2014, 5:24:30 PM10/7/14
to Python-Venezuela
Tengo tiempo queriendo implementar la validación para el último dígito del RIF (Seniat-Venezuela) encontré una formula Excel y la pasé a Python:

def digito_rif(ci):
    '''
    toma un nro de cedula o rif y verifica el digito validador
    ej.  V12345678 ó J12345678

    '''
    base = {'V': 1 * 4, 'E': 2 * 4, 'J': 3 * 4}
    oper = [0, 3, 2, 7, 6, 5, 4, 3, 2]
    for i in range(len(ci[:9])):
        if i == 0:
            val = base.get(ci[0], 0)
        else:
            val += oper[i] * int(ci[i])
    digit = 11 - (val % 11)
    return '%s%s' % (ci[:9], str(digit)[-1])



Leonardo Caballero

unread,
Oct 7, 2014, 5:27:35 PM10/7/14
to python-v...@googlegroups.com
Hola Juan

Gracias por compartirla 

una sugerencia por que no la pegas en un gist https://gist.github.com/ alli puede quedar para el acceso de todos los programadores python que no esten aca ademas la puedes tratar como repo git y embeber en tu HTML o blog 

Saludos

--
Este es un mensaje del foro Python de Venezuela - http://www.python.org.ve
Para suscripciones y retiros: http://goo.gl/ug9by
---
Has recibido este mensaje porque estás suscrito al grupo "Python Venezuela" de Grupos de Google.
Para anular la suscripción a este grupo y dejar de recibir sus mensajes, envía un correo electrónico a python-venezue...@googlegroups.com.
Para acceder a más opciones, visita https://groups.google.com/d/optout.



--
Atentamente

T.S.U. Leonardo Caballero
Linux Counter ID = https://linuxcounter.net/user/369081.html
Key fingerprint = 9FD2 DC71 38E7 A1D1 57F5  1D29 04DE 43BC 8A27 424A

/me Corriendo Debian Wheezy y Canaina GNU/Linux 3
/me Cree "El Conocimiento Humano le Pertenece al Mundo"

Juan Marquez

unread,
Oct 7, 2014, 5:29:37 PM10/7/14
to Python-Venezuela
Lo mande antes de terminar la explicación.

La rutina funciona pero hay algunos RIF que no se validan correctamente y no se como calcular los dígitos para RIF del gobierno (G-#########-#) y otros casos

Este RIF no se calcula correctamente: J001535810

Los RIF calculados se pueden verificar en: http://contribuyente.seniat.gob.ve/getContribuyente/getrif?rif=J001535810

No esta listo pero es un comienzo.

Saludos

Juan Marquez

unread,
Oct 7, 2014, 5:34:56 PM10/7/14
to Python-Venezuela
Leonardo: Eventualmente la subiré pero al menos quiero estar seguro de que funciona bien.

Juan Marquez

unread,
Oct 7, 2014, 5:36:25 PM10/7/14
to Python-Venezuela

Leonardo Caballero

unread,
Oct 7, 2014, 5:47:02 PM10/7/14
to python-v...@googlegroups.com
Juan y no tienes cuenta github.com?

Asi queda asociado a tu cuenta en github.com por ejemplo https://gist.github.com/macagua

--
Este es un mensaje del foro Python de Venezuela - http://www.python.org.ve
Para suscripciones y retiros: http://goo.gl/ug9by
---
Has recibido este mensaje porque estás suscrito al grupo "Python Venezuela" de Grupos de Google.
Para anular la suscripción a este grupo y dejar de recibir sus mensajes, envía un correo electrónico a python-venezue...@googlegroups.com.
Para acceder a más opciones, visita https://groups.google.com/d/optout.

Francisco Palm

unread,
Oct 7, 2014, 10:57:13 PM10/7/14
to python-venezuela
Parece que la formula en general funciona

Sería mas o menos lo que sigue:

def ultimo_digito_rif(cod):
    """
    (str) -> int
    cod es el rif sin digito de verificación en formato L12345678
    donde L es V, E o J, y el resto tiene un máximo de 8 dígitos numéricos
    """
    base = {'V': 4, 'E': 8, 'J': 12}
    oper = [3, 2, 7, 6, 5, 4, 3, 2]
    let = cod[0]
    num = cod[1:]
    let = let.upper()
    # validar entradas
    assert num.isdigit() and len(num) <= 8
    assert let in base.keys()
    # añadir ceros a la derecha
    num = '0' * (8 - len(num)) + num
    num = [int(d) for d in num]
    aux = sum([a * b for (a, b) in zip(num, oper)])
    value = 11 - (base[let] + aux) % 11
    return value if value < 10 else 0

El tema es que tomas el resto de 11, y puede dar 1 o 0 en cuyo caso al restar al 11 daría 10 u 11, en estos casos el código de verificación sería 0.

validar_digito_rif(rif) debe ser otra formula que utilice la anterior y devuelva True o False.

Para introducir "G" yo empezaría por probar con algún múltiplo de 4, empezando por 16 :-D en "base"

Saludos

F. Palm


--
Este es un mensaje del foro Python de Venezuela - http://www.python.org.ve
Para suscripciones y retiros: http://goo.gl/ug9by
---
Has recibido este mensaje porque estás suscrito al grupo "Python Venezuela" de Grupos de Google.
Para anular la suscripción a este grupo y dejar de recibir sus mensajes, envía un correo electrónico a python-venezue...@googlegroups.com.
Para acceder a más opciones, visita https://groups.google.com/d/optout.



--
--------------------------------------
fp...@mapologo.org.ve
francis...@gmail.com

cel: +58 +424 7228252
tel: +58 +274 6352001

----
Debemos ser libres, no para hacer lo que nos plazca, sino libres para comprender muy profundamente nuestros propios instintos e impulsos. K

Francisco Palm

unread,
Oct 8, 2014, 10:21:34 AM10/8/14
to python-venezuela

Mejorando lo anterior :-D

El RIF utiliza el algoritmo de módulo verificador de módulo 11
http://es.wikipedia.org/wiki/C%C3%B3digo_de_control#C.C3.A1lculo_del_d.C3.ADgito_verificador
Es decir, que los dígitos se multiplican por 2, 3, 4, 5, 6, 7, y si hay mas dígitos se repite el ciclo
Con un poco de ensayo y error encontré que el valor de la "base" para los RIF "G" es 20

La cosa quedaría así:

import itertools


def ultimo_digito_rif(cod):
    """
    (str) -> int
    cod es el rif sin digito de verificación en formato L12345678
    donde L es V, E, J o G, y el resto son dígitos numéricos
    Emplea el algoritmo de módulo verificador de módulo 11 con
    algunas modificaciones para diferenciar entre tipos de RIF
    Asigna 0 a los dígitos mayores que 10
    http://es.wikipedia.org/wiki/C%C3%B3digo_de_control#C.C3.A1lculo_del_d.C3.ADgito_verificador   
    """
    base = {'V': 4, 'E': 8, 'J': 12, 'G': 20}
    factor = itertools.cycle(xrange(2, 8))

    let = cod[0]
    num = cod[1:]
    let = let.upper()
    # validar entradas
    assert num.isdigit()
    assert let in base.keys()
    # calculos
    oper = [factor.next() for i in range(len(num))]
    oper.reverse()

    num = [int(d) for d in num]
    aux = sum([a * b for (a, b) in zip(num, oper)])
    value = 11 - (base[let] + aux) % 11
    return value if value < 10 else 0


Hay un detalle, lo usual del método como se utiliza en otros sistemas/países es que en lugar del "0" para los mayores de 10 se debería utilizar "value % 10", o mejor aún utilizar una letra para el 10 y otra para el 11. Así el código de verificación tendría una distribución uniforme.

Con el "macheteo" que le dan en Venezuela hay 3 veces mas posibilidades de tener un 0 que cualquier otro dígito.
Este código de verificación debería además ser único para que sea realmente útil para capturar errores de transcripción.
No estaría de más enviar una sugerencia al SENIAT.

F. Palm

Juan Marquez

unread,
Oct 8, 2014, 3:08:48 PM10/8/14
to Python-Venezuela, Humberto Arocha
Ya esta listo: https://gist.github.com/juanvmarquezl/a299049a3acc5d201924 me faltaba comprobar el caso del resto = 11 digito = 0

Francisco: con mucho respeto: usar itertools no es como demasiado? un simple oper = [0, 3, 2, 7, 6, 5, 4, 3, 2] sin depencencias ni nada me parece más limpio y legible. "Mejor explicito que implícito"

No incluyo validaciones porque solo me interesa mostrar el algoritmo de calculo del dígito de validación del RIF en implementaciones si estarán las validaciones.

Código:
def calcular_rif(data):
    '''
    Toma un nro de cédula o rif y calcula el dígito validador
    data: string con número de CI o RIF sin espacios ni guiones ej.
        V12345678
        E12345678
        J123456789
    devuelve el rif con el dígito calculado
    no se validan los datos de entrada
    para validar: if data == calcular_rif(data):
    '''

    base = {'V': 4, 'E': 8, 'J': 12, 'G': 20}
    oper = [0, 3, 2, 7, 6, 5, 4, 3, 2]
    val = 0
    for i in range(len(data[:9])):
        val += base.get(data[0], 0) if i == 0 else oper[i] * int(data[i])

    digit = 11 - (val % 11)
    digit = digit if digit < 10 else 0
    return '%s%s' % (data[:9], digit)



En cuanto a notificar al SENIAT requeriría al menos re-tramitar todos los RIF terminados en 0 así que veo pocas posibilidades (recuerdan el NIT?)

Juancarlo Añez

unread,
Oct 8, 2014, 3:30:33 PM10/8/14
to python-venezuela, Humberto Arocha

Eso no es nada phytónico....

--
Juancarlo Añez

Nhomar Hernández

unread,
Oct 8, 2014, 5:11:44 PM10/8/14
to python-v...@googlegroups.com, Humberto Arocha

El 8 de octubre de 2014, 14:38, Juan Marquez <juanvm...@gmail.com> escribió:

Francisco: con mucho respeto: usar itertools no es como demasiado? un simple oper = [0, 3, 2, 7, 6, 5, 4, 3, 2] sin depencencias ni nada me parece más limpio y legible. "Mejor explicito que implícito"

itertools es la forma "explícita " de hacer eso mi pana ;-)


--
--------------------
Saludos Cordiales
 
 

javieredm

unread,
Oct 9, 2014, 10:08:24 AM10/9/14
to python-v...@googlegroups.com, humbert...@gmail.com
Buenos días a tod@s,
Aportando algo, yo lo dejaría así:


# -*- coding: utf-8 -*-
import itertools


def calcular_rif(data):
    '''
    Toma un nro de cédula o rif y calcula el dígito validador
    data: string con número de CI o RIF sin espacios ni guiones ej.
        V12345678
        E12345678
        J12345678
        G12345678
    devuelve el rif con el dígito calculado
    no se validan los datos de entrada
    para validar: if data == calcular_rif(data):
    '''
    base = {'V': 4, 'E': 8, 'J': 12, 'G': 20}
    factor = itertools.cycle(xrange(2, 8))
    let = data[0]
    num = data[1:]
    let = let.upper()
    # validar entradas
    assert num.isdigit()
    assert let in base.keys()

    #buscando operadores
    oper = [factor.next() for i in range(len(num))]
    oper.reverse()
    #convirtiendo a entero
    num = [int(d) for d in num]
    # calculos
    par = zip(num, oper)
    numXope = map(lambda x: x[0]*x[1], par)
    s_numXope = reduce(lambda x, y: x + y, numXope)
    value = 11 - (base[let] + s_numXope) % 11
    return value if value < 10 else 0

Juan Marquez

unread,
Oct 9, 2014, 11:18:56 AM10/9/14
to Python-Venezuela
y así?

    base = '*VEJ*G'  # los * corresponden a otras letras por definir
    oper = [4, 3, 2, 7, 6, 5, 4, 3, 2]  # o
#~ oper = (range(2, 8) * 2)[:9]
    #~ oper.reverse()
    items = [base.find(data[0])]
    items.extend(map(lambda x: int(x), data[1:]))
    val = sum(map(lambda x: x[0] * x[1], zip(items, oper)))

    digit = 11 - (val % 11)
    digit = digit if digit < 10 else 0
    return '%s%s' % (data[:9], digit)

Carlos Guerrero

unread,
Oct 9, 2014, 11:50:51 AM10/9/14
to python-v...@googlegroups.com, Humberto Arocha
Creo que pueden aprovechar para explicar qué es algo explícito y que no lo es :)

A manera de aprendizaje para todos.

Saludos.

--

Jesús Gómez

unread,
Oct 9, 2014, 12:17:04 PM10/9/14
to python-venezuela
El 9 de octubre de 2014, 11:20, Carlos Guerrero <guerrer...@gmail.com> escribió:
Creo que pueden aprovechar para explicar qué es algo explícito y que no lo es :)


En este caso usaron, a mi parecer, el término al revés.

Tomemos por ejemplo las listas por comprensión (list comprehension). Es una forma de describir una lista. El término viene de las matemáticas donde los conjuntos puede describirlos de dos maneras:

1) Por Extensión. Se dicta cada elemento del conjunto. Por ejemplo, el conjunto {blanco, amarillo, verde} o el conjunto {-1, 100, -999, 4}.
2) Por Comprensión: Se menciona una propiedad común a todos los elementos del conjunto. Por ejemplo: El conjunto de los números enteros positivos menores que 100, o escrito de otra manera: {x | x <- Z, 0 < x < 100}

A mi parecer, si alguna de las dos maneras de describir un conjunto es la manera "Explícita", es por extensión.

Entonces, la lista [0, 1, 2, 3, 4, 5] está escrita de manera explícita y [i for i in range(6)] no lo está.

Ahora ¿Cuál utilizar? la que te de la gana. Cada un tiene su ventaja frente a la otra.

 * Ventaja de la forma explícita (en mis términos). Legibilidad
 * Ventaja de la forma no explícita. Adaptabilidad (imaginen que ahora son los números del 0 al 20).

Claro, si la lista se pone exquisita, por ejemplo: [-123, 5, 99, -3, 7777, 12343], no hay un patrón evidente para describir la lista por comprensión.

Francisco Palm

unread,
Oct 9, 2014, 1:00:28 PM10/9/14
to python-venezuela

No entiendo porque tanto respeto, el orgullo excesivo es una virtud de todo programador :-D

Bueno, yo me contuve de hacer las validaciones como excepciones.

Tal y como dice Juancarlo, recorrer usando for es poco pitónico, hay otros detalles. Tampoco mi código es totalmente pitónico.

Hacer la comprobación "if == 0" en cada iteración no es conveniente. Si tienes que calcular/verificar muchos RIF se va a notar la diferencia.

El tema del itertools es que de antemano no sabes cuantos dígitos tienes, de hecho tu código no sirve si la entrada no tiene los ceros a la derecha para números con menos de 8 dígitos. Y no te servirá cuando haya números por encima de 9 dígitos.

itertools no es una dependencia porque es parte de la librería estándar. Es una línea de Python idéntica a la cabecera de la propia función. Hay que tener cuidado con módulos como tkinter que en algunos entornos depende de paquetes como python-tk.

Usar la interpolación de cadenas estilo C/PHP con % lo evito tanto como puedo. cadena.format() es el bushido.

Mínimo utilizaría tres funciones: una para generar el dígito de control, otra para validar, otra para escribir el RIF completo. Así puedo utilizar estas funciones de manera mas flexible. Cada función debe realizar una tarea claramente distinguible.

----

Respecto a la implantación de javier:

Hacer sum() sobre una comprensión de lista siempre será más pitónico que usar lambdas :-D
Incluso queda en una sola línea.

Aunque veo algo así y me encanta ver como se desmonta eso que en Python hay una única forma de hacer las cosas.

Saludos

F. Palm

Israel

unread,
Oct 9, 2014, 1:01:59 PM10/9/14
to python-v...@googlegroups.com
Vamos a reescribirlo en assembler ahora :D :D :D
Israel Fermín Montilla

Juancarlo Añez

unread,
Oct 9, 2014, 1:19:23 PM10/9/14
to python-venezuela
Va mi versión. Los comentarios son sobre los cambios, y no sobre el código.

# -*- coding: utf-8 -*-
from __future__ import print_function, division, absolute_import, unicode_literals
import itertools

# colocamos las constantes fuera de la función
FACTOR_TIPO = {'V': 4, 'E': 8, 'J': 12, 'G': 20}
FACTOR_DIGITO = [3, 2, 7, 6, 5, 4]


# Usamos dos parámetros ya que en muchas aplicaciones la letra
# para el tipo de RIF se obtiene con un campo aparte.
# En todo caso, es fácil para el cliente hacer codigo[0], codigo[1:]
def ultimo_digito_rif(tipo, digitos):
    tipo = tipo.upper()
    # no usamos assert para validaciones requeridas
    if tipo not in FACTOR_TIPO:
        raise ValueError('%s no es un tipo de RIF válido' % tipo)
    if not digitos.isdigit():
        raise ValueError('esperaba una serie de dígitos: %s' % digitos)

    factores = itertools.cycle(FACTOR_DIGITO)
    suma = sum([int(d) * f for (d, f) in zip(digitos, factores)])
    suma += FACTOR_TIPO[tipo]

    verificacion = 11 - suma % 11
    return verificacion if verificacion < 10 else 0

Juancarlo Añez
tel:+58(414)901-2021
skype:juancarloanez
Reply all
Reply to author
Forward
0 new messages