Files
tsUtilidades/ValidarDocumentoIdentidad.vb

407 lines
14 KiB
VB.net

Imports System.Linq.Expressions
Imports System.Text.RegularExpressions
''' <summary>
''' Representa un número. En la clase se desglosan las distintas opciones que se puedan
''' encontrar
''' </summary>
Public Class ValidarDocumentoIdentidad
''' <summary>
''' Tipos de Códigos.
''' </summary>
''' <remarks>Aunque actualmente no se utilice el término CIF, se usa en la enumeración
''' por comodidad</remarks>
Public Enum TiposDocumentosEnum
NIF
NIE
CIF
End Enum
' Número tal cual lo introduce el usuario
Private m_numero As String
Private tipo As TiposDocumentosEnum
Public ReadOnly Property TipoDocumento As TiposDocumentosEnum
Get
Return tipo
End Get
End Property
''' <summary>
''' Parte de Nif: En caso de ser un Nif intracomunitario, permite obtener el cógido del país
''' </summary>
Public Property CodigoIntracomunitario() As String
Get
Return m_CodigoIntracomunitario
End Get
Friend Set(value As String)
m_CodigoIntracomunitario = value
End Set
End Property
Private m_CodigoIntracomunitario As String
Friend Property EsIntraComunitario() As Boolean
Get
Return m_EsIntraComunitario
End Get
Set(value As Boolean)
m_EsIntraComunitario = value
End Set
End Property
Private m_EsIntraComunitario As Boolean
''' <summary>
''' Parte de Nif: Letra inicial del Nif, en caso de tenerla
''' </summary>
Public Property LetraInicial() As String
Get
Return m_LetraInicial
End Get
Friend Set(value As String)
m_LetraInicial = value
End Set
End Property
Private m_LetraInicial As String
''' <summary>
''' Parte de Nif: Bloque numérico del NIF. En el caso de un NIF de persona física,
''' corresponderá al DNI
''' </summary>
Public Property Identificador() As Integer
Get
Return m_numero
End Get
Friend Set(value As Integer)
m_numero = value
End Set
End Property
Private m_Identificador As Integer
''' <summary>
''' Parte de Nif: Dígito de control. Puede ser número o letra
''' </summary>
Public Property DigitoControl() As String
Get
Return m_DigitoControl
End Get
Friend Set(value As String)
m_DigitoControl = value
End Set
End Property
Private m_DigitoControl As String
''' <summary>
''' Valor que representa si el Nif introducido es correcto
''' </summary>
Public Property EsCorrecto() As Boolean
Get
Return m_EsCorrecto
End Get
Friend Set(value As Boolean)
m_EsCorrecto = value
End Set
End Property
Private m_EsCorrecto As Boolean
''' <summary>
''' Cadena que representa el tipo de Nif comprobado:
''' - NIF : Número de identificación fiscal de persona física
''' - NIE : Número de identificación fiscal extranjería
''' - CIF : Código de identificación fiscal (Entidad jurídica)
''' </summary>
Public ReadOnly Property TipoNif() As String
Get
Return tipo.ToString()
End Get
End Property
''' <summary>
''' Constructor. Al instanciar la clase se realizan todos los cálculos
''' </summary>
''' <param name="numero">Cadena de 9 u 11 caracteres que contiene el DNI/NIF
''' tal cual lo ha introducido el usuario para su verificación</param>
Public Sub New(numero As String)
' Se eliminan los carácteres sobrantes
Try
' numero = EliminaCaracteres(numero)
' numero = numero.ToUpper()
' Comprobación básica de la cadena introducida por el usuario
If numero.Length <> 9 AndAlso numero.Length <> 11 Then
Me.EsCorrecto = False ' Throw New ArgumentException("El NIF no tiene un número de caracteres válidos")
Else
Me.m_numero = numero
Desglosa()
Select Case tipo
Case TiposDocumentosEnum.NIF, TiposDocumentosEnum.NIE
Me.EsCorrecto = CompruebaNif()
Exit Select
Case TiposDocumentosEnum.CIF
Me.EsCorrecto = validateCif(numero)
' Me.EsCorrecto = CompruebaCif()
Exit Select
End Select
End If
Catch ex As Exception
Me.EsCorrecto = False
End Try
End Sub
#Region "Preparación del número (desglose)"
''' <summary>
''' Realiza un desglose del número introducido por el usuario en las propiedades
''' de la clase
''' </summary>
Private Sub Desglosa()
Dim n As Int32
If m_numero.Length = 11 Then
' Nif Intracomunitario
EsIntraComunitario = True
CodigoIntracomunitario = m_numero.Substring(0, 2)
LetraInicial = m_numero.Substring(2, 1)
Int32.TryParse(m_numero.Substring(3, 7), n)
DigitoControl = m_numero.Substring(10, 1)
tipo = GetTipoDocumento(LetraInicial(0))
Else
' Nif español
tipo = GetTipoDocumento(m_numero(0))
EsIntraComunitario = False
If tipo = TiposDocumentosEnum.NIF Then
LetraInicial = String.Empty
Int32.TryParse(m_numero.Substring(0, 8), n)
Else
LetraInicial = m_numero.Substring(0, 1)
Dim listaLetrasNIE As Char() = {"X", "Y", "Z"}
If listaLetrasNIE.Contains(LetraInicial) Then
'// Las letras por las que comienza el NIE deben ser reemplazadas por números antes de realizar
'// la operación Int32.TryParse, que además deberá incluir ese número reemplazado.
'// X = 0
'// Y = 1
'// Z = 2
Select Case LetraInicial
Case "X"
m_numero = 0 & m_numero.Substring(1, m_numero.Length - 1)
Case "Y"
m_numero = 1 & m_numero.Substring(1, m_numero.Length - 1)
Case "Z"
m_numero = 2 & m_numero.Substring(1, m_numero.Length - 1)
End Select
Int32.TryParse(m_numero.Substring(0, 8), n)
Else
'// El curso normal, cuando la letra inicial no es X, Y o Z.
Int32.TryParse(m_numero.Substring(1, 7), n)
End If
End If
DigitoControl = m_numero.Substring(8, 1)
End If
Identificador = n
End Sub
''' <summary>
''' En base al primer carácter del código, se obtiene el tipo de documento que se intenta
''' comprobar
''' </summary>
''' <param name="letra">Primer carácter del número pasado</param>
''' <returns>Tipo de documento</returns>
Private Function GetTipoDocumento(letra As Char) As TiposDocumentosEnum
Dim regexIdentificadors As New Regex("[0-9]")
If regexIdentificadors.IsMatch(letra.ToString()) Then
Return TiposDocumentosEnum.NIF
End If
Dim regexLetrasNIE As New Regex("[LKMXYZ]")
If regexLetrasNIE.IsMatch(letra.ToString()) Then
Return TiposDocumentosEnum.NIE
End If
Dim regexLetrasCIF As New Regex("[ABCDEFGHJPQRSUVNW]")
If regexLetrasCIF.IsMatch(letra.ToString()) Then
Return TiposDocumentosEnum.CIF
End If
Throw New ApplicationException("El código no es reconocible")
End Function
''' <summary>
''' Eliminación de todos los carácteres no numéricos o de texto de la cadena
''' </summary>
''' <param name="numero">Número tal cual lo escribe el usuario</param>
''' <returns>Cadena de 9 u 11 carácteres sin signos</returns>
Private Function EliminaCaracteres(numero As String) As String
' Todos los carácteres que no sean números o letras
Dim caracteres As String = "[^\w]"
Dim regex As New Regex(caracteres)
Return regex.Replace(numero, "")
End Function
#End Region
#Region "Cálculos"
Private Function CompruebaNif() As Boolean
Return DigitoControl = GetLetraNif()
End Function
Public Shared Function validateCif(ByVal cif As String) As Boolean
If String.IsNullOrEmpty(cif) Then Return False
cif = cif.Trim().ToUpper()
If cif.Length <> 9 Then Return False
Dim firstChar As String = cif.Substring(0, 1)
Dim cadena As String = "ABCDEFGHJNPQRSUVW"
If cadena.IndexOf(firstChar) = -1 Then Return False
Try
Dim sumaPar As Int32 = Nothing
Dim sumaImpar As Int32 = Nothing
Dim cif_sinControl As String = cif.Substring(0, 8)
Dim digits As String = cif_sinControl.Substring(1, 7)
For n As Int32 = 0 To digits.Length - 1 Step 2
If n < 6 Then
sumaPar += Convert.ToInt32(digits(n + 1).ToString())
End If
Dim dobleImpar As Int32 = 2 * Convert.ToInt32(digits(n).ToString())
sumaImpar += (dobleImpar Mod 10) + (dobleImpar \ 10)
Next
Dim sumaTotal As Int32 = sumaPar + sumaImpar
sumaTotal = (10 - (sumaTotal Mod 10)) Mod 10
Dim digitoControl As String = ""
Select Case firstChar
Case "N", "P", "Q", "R", "S", "W"
Dim characters As Char() = {"J"c, "A"c, "B"c, "C"c, "D"c, "E"c, "F"c, "G"c, "H"c, "I"c}
digitoControl = characters(sumaTotal).ToString()
Case Else
digitoControl = sumaTotal.ToString()
End Select
Return digitoControl.Equals(cif.Substring(8, 1))
Catch __unusedException1__ As Exception
Return False
End Try
End Function
''' <summary>
''' Cálculos para la comprobación del Cif (Entidad jurídica)
''' </summary>
'Private Function CompruebaCif() As Boolean
' Dim letrasCodigo As String() = {"J", "A", "B", "C", "D", "E", _
' "F", "G", "H", "I"}
' Dim n As String = Identificador.ToString()
' Dim sumaPares As Int32 = 0
' Dim sumaImpares As Int32 = 0
' Dim sumaTotal As Int32 = 0
' Dim i As Int32 = 0
' Dim digitoCalculado As String
' Dim retVal As Boolean = False
' ' Recorrido por todos los dígitos del número
' For i = 0 To n.Length - 1
' Dim aux As Int32
' Int32.TryParse(n(i).ToString(), aux)
' If (i + 1) Mod 2 = 0 Then
' ' Si es una posición par, se suman los dígitos
' sumaPares += aux
' Else
' ' Si es una posición impar, se multiplican los dígitos por 2
' aux = aux * 2
' ' se suman los dígitos de la suma
' sumaImpares += SumaDigitos(aux)
' End If
' Next
' ' Se suman los resultados de los números pares e impares
' sumaTotal += sumaPares + sumaImpares
' ' Se obtiene el dígito de las unidades
' Dim unidades As Int32 = sumaTotal Mod 10
' ' Si las unidades son distintas de 0, se restan de 10
' If unidades <> 0 Then
' unidades = 10 - unidades
' End If
' Select Case LetraInicial
' ' Sólo números
' Case "A", "B", "E", "H"
' retVal = DigitoControl = unidades.ToString()
' Exit Select
' ' Sólo letras
' Case "K", "P", "Q", "S"
' retVal = DigitoControl = letrasCodigo(unidades)
' Exit Select
' Case Else
' retVal = (DigitoControl = unidades.ToString()) OrElse (DigitoControl = letrasCodigo(unidades))
' Exit Select
' End Select
' Return retVal
'End Function
''' <summary>
''' Obtiene la suma de todos los dígitos
''' </summary>
''' <returns>de 23, devuelve la suma de 2 + 3</returns>
Private Function SumaDigitos(digitos As Int32) As Int32
Dim sIdentificador As String = digitos.ToString()
Dim suma As Int32 = 0
For i As Int32 = 0 To sIdentificador.Length - 1
Dim aux As Int32
Int32.TryParse(sIdentificador(i).ToString(), aux)
suma += aux
Next
Return suma
End Function
''' <summary>
''' Obtiene la letra correspondiente al Dni
''' </summary>
Private Function GetLetraNif() As String
Dim indice As Integer = Identificador Mod 23
Return "TRWAGMYFPDXBNJZSQVHLCKET"(indice).ToString()
End Function
''' <summary>
''' Obtiene una cadena con el número de identificación completo
''' </summary>
Public Overrides Function ToString() As String
Dim nif As String
nif = If(EsIntraComunitario, CodigoIntracomunitario, Convert.ToString(String.Empty + LetraInicial & Identificador) & DigitoControl)
Return nif
End Function
#End Region
''' <summary>
''' Comprobación de un número de identificación fiscal español
''' </summary>
''' <param name="numero">Identificador a analizar</param>
''' <returns>Instancia de <see cref="IdentificadorNif"/> con los datos del número.
''' Destacable la propiedad <seealso cref="IdentificadorNif.EsCorrecto"/>, que contiene la verificación
''' </returns>
Public Shared Function CompruebaNif(numero As String) As ValidarDocumentoIdentidad
Return New ValidarDocumentoIdentidad(numero)
End Function
End Class