sábado, 11 de octubre de 2014

IBAN y BIC, validación con SQL Server y .NET

Recientemente en la zona SEPA (Single Euro Payments Area)  se han establecido nuevos formatos para las cuentas bancarias. El antiguo código  de cuenta corriente CCC ha sido sustituido por el nuevo International Bank Account Number, IBAN. Además se ha creado un nuevo código Bank Identification Code BIC.

Voy  a describir cómo implementar dos funciones de SQL Server para que validen ambos códigos y luego estas funciones se puedan utilizar desde .Net, para los ejemplos de .Net voy  a utilizar Visual Basic.

Validador de IBAN y BIC
Imagen de gophoto.


IBAN


El Código Internacional de Cuenta Bancaria o International Bank Account Number (IBAN) es un código alfanumérico que identifica una cuenta bancaria en cualquier lugar del mundo (siempre que el país esté adherido al sistema IBAN). A cada cuenta le corresponderá un único IBAN. El objetivo del IBAN es facilitar el tratamiento automático de pagos y cobros transfronterizos. El IBAN consta de un máximo de 34 caracteres alfanuméricos. Los dos primeros son de carácter alfabético e identifican el país. Los dos siguientes son dígitos de control y constituyen el elemento de validación de la totalidad del IBAN. Los restantes son el número de cuenta, que, en la mayoría de los casos, identifica además la entidad y la oficina.
El IBAN es el estándar creado por el Comité Europeo de Estándares Bancarios (ECBS) con el objetivo de facilitar la identificación homogénea de las cuentas bancarias a todos los países. El IBAN (estándar ISO 13616) consta de un máximo de 34 caracteres alfanuméricos, en función del país.
En España el IBAN se compone de 24 caracteres con la siguiente estructura:
Los dos primeros caracteres identifican al país de la cuenta. En el caso de España sería: ES.
Los dos caracteres siguientes corresponden a los dígitos de control, calculados según el algoritmo utilizado para validar el IBAN.
Los veinte caracteres restantes corresponden al Código de Cuenta Cliente que se viene utilizando y que identifican la Entidad financiera (4 dígitos), Oficina de la cuenta (4 dígitos), Dígitos de control (2 dígitos) y Número de cuenta (10 dígitos).
El IBAN se presenta en dos formatos diferentes:
Ejemplo:
CCC   9000 0001 21 0123456789
IBAN (electrónico)   ES0690000001210123456789
IBAN (impreso)        IBAN ES06 9000 0001 2101 2345 6789

BIC


El código de identificación bancaria (BIC en su acrónimo en inglés) es el estándar internacional utilizado para identificar a las entidades que operan en el sector financiero. El Organismo Internacional de Estandarización (ISO, en su acrónimo en inglés) ha designado a SWIFT como la entidad responsable de asignar y registrar los BICs.

Más información BOE.

Funciones de SQL SERVER.


Validar IBAN


A continuación presento las dos funciones que validan si un código IBAN o un código BIC son correctos.
La primera función valida el IBAN, toma un IBAN como parámetro y devuelve un Bit de SQL Server como salida, 1 si es correcto y un 0 si es incorrecto.


/****** Objeto:  UserDefinedFunction [Dbo].[ValidarIBAN]    Fecha de la secuencia de comandos: 09/30/2014 11:52:55 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER OFF
GO

CREATE     FUNCTION [DBO].[ValidarIBAN]
      ( @vcharIban  AS  VARCHAR(34) )
      RETURNS BIT
AS

  BEGIN

      DECLARE @vcharIbanAux AS VARCHAR (40)
      DECLARE @i AS SMALLINT
      DECLARE @vcharIbanTransformado AS VARCHAR(400)
      DECLARE @LEN AS INT 
      DECLARE @modulo AS INT
      DECLARE @bprimera AS INT
      DECLARE @MENOS AS INT
      DECLARE @Caracteres AS VARCHAR (70)

      SET @Caracteres =  'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'

      IF LEN(@vcharIBAN) < 15  --16
      BEGIN
            RETURN 0   
      END
 
      IF  ISNUMERIC (SUBSTRING(@vcharIban,1,2)) = 1
      BEGIN
            RETURN 0   
      END


      IF PATINDEX('%[^' + @Caracteres + ']%', @vcharIban) > 0
      BEGIN
            RETURN
      END 

       
      SET @vcharIbanaux = SUBSTRING(@vcharIban, 5, LEN(RTRIM(@vcharIban)) - 4) +  +  SUBSTRING(@vcharIban, 1,4)  

      SET @vcharIbanaux =  REPLACE(@vcharIbanaux, 'A', '10')
      SET @vcharIbanaux =  REPLACE(@vcharIbanaux, 'B', '11') 
      SET @vcharIbanaux =  REPLACE(@vcharIbanaux, 'C', '12')
      SET @vcharIbanaux =  REPLACE(@vcharIbanaux, 'D', '13')
      SET @vcharIbanaux =  REPLACE(@vcharIbanaux, 'E', '14')
      SET @vcharIbanaux =  REPLACE(@vcharIbanaux, 'F', '15')
      SET @vcharIbanaux =  REPLACE(@vcharIbanaux, 'G', '16')
      SET @vcharIbanaux =  REPLACE(@vcharIbanaux, 'H', '17')
      SET @vcharIbanaux =  REPLACE(@vcharIbanaux, 'I', '18')
      SET @vcharIbanaux =  REPLACE(@vcharIbanaux, 'J', '19')
      SET @vcharIbanaux =  REPLACE(@vcharIbanaux, 'K', '20')
      SET @vcharIbanaux =  REPLACE(@vcharIbanaux, 'L', '21')
      SET @vcharIbanaux =  REPLACE(@vcharIbanaux, 'M', '22')
      SET @vcharIbanaux =  REPLACE(@vcharIbanaux, 'N', '23')
      SET @vcharIbanaux =  REPLACE(@vcharIbanaux, 'O', '24')
      SET @vcharIbanaux =  REPLACE(@vcharIbanaux, 'P', '25')
      SET @vcharIbanaux =  REPLACE(@vcharIbanaux, 'Q', '26')
      SET @vcharIbanaux =  REPLACE(@vcharIbanaux, 'R', '27')
      SET @vcharIbanaux =  REPLACE(@vcharIbanaux, 'S', '28')
      SET @vcharIbanaux =  REPLACE(@vcharIbanaux, 'T', '29')
      SET @vcharIbanaux =  REPLACE(@vcharIbanaux, 'U', '30')
      SET @vcharIbanaux =  REPLACE(@vcharIbanaux, 'V', '31')
      SET @vcharIbanaux =  REPLACE(@vcharIbanaux, 'W', '32')
      SET @vcharIbanaux =  REPLACE(@vcharIbanaux, 'X', '33')
      SET @vcharIbanaux =  REPLACE(@vcharIbanaux, 'Y', '34')
      SET @vcharIbanaux =  REPLACE(@vcharIbanaux, 'Z', '35')
       
 
      SET @vcharIbanTransformado = @vcharIbanAux

      SET @len = 0

BEGIN
      SET @bprimera = 0

      WHILE  LEN(@vcharIbanTransformado) > 0
            BEGIN
                  IF @bprimera = 0
                        BEGIN
                             SET @modulo =  (SELECT  CAST(CAST(SUBSTRING(@vcharIbanTransformado, 1, 9) AS BIGINT ) % 97 as VARCHAR(20) ))
                             SET @LEN = (SELECT LEN(RTRIM(CAST(@modulo AS VARCHAR(20)))))

                             SET @MENOS = LEN((SELECT SUBSTRING(@vcharIbanTransformado, 10, (- @LEN )) ))
             

                             SET @vcharIbanAux =  CAST(@modulo  AS VARCHAR(20))  + (SELECT SUBSTRING(@vcharIbanTransformado, 10, (- @LEN )) )
                             SET @bprimera = 1
                             SET @vcharIbanTransformado = SUBSTRING(@vcharIbanTransformado, 10, len(@vcharIbanTransformado) -9)

                        END
                  ELSE
                        BEGIN
                             SET @modulo =  (SELECT CAST(@vcharIbanAux AS BIGINT ) % 97 ) 

                             SET @MENOS = LEN((SELECT SUBSTRING(@vcharIbanTransformado, 1, (- @LEN )) ))

                             SET @vcharIbanAux = CAST(@modulo  AS VARCHAR(20)) + (SELECT SUBSTRING(@vcharIbanTransformado, @MENOS + 1, (- @LEN )) )
           
                             IF LEN (@vcharIbanTransformado) >= @MENOS 
                                   BEGIN
                                         SET @vcharIbanTransformado = SUBSTRING(@vcharIbanTransformado, @MENOS + 1, len(@vcharIbanTransformado) - @MENOS + 1)  

                                   END
                              ELSE
                                   BEGIN
                                         SET @vcharIbanTransformado = SUBSTRING(@vcharIbanTransformado, @MENOS + 1, LEN (@vcharIbanTransformado) - @MENOS + 1)
                                   END

                             IF LEN(@vcharIbanTransformado) - @MENOS < = 0
                                   BEGIN

                                         IF (SELECT CAST(@vcharIbanaux AS BIGINT ) % 97 ) = 1
                                               BEGIN      
                                                     RETURN 1
                                               END  
                                   END 
                        END 
            END
END


RETURN 0

END


Validar BIC


La siguiente  funcion que valida si un código BIC es correcto,  toma como parámetro el nímero BIC y devuelve un Bigint de SQL SERVER como salida, 1 si es correcto y un 0 si es incorrecto.

/****** Objeto:  UserDefinedFunction [DBO].[ValidarBIC]    Fecha de la secuencia de comandos: 09/30/2014 13:40:26 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER OFF
GO

CREATE FUNCTION [DBO].[ValidarBIC] (@strBIC AS VARCHAR(11))

RETURNS BIGINT

AS


BEGIN
      DECLARE @I AS INT
    DECLARE @X AS BIGINT


      SET @X = 1
      SET @I = 1
      WHILE @I < 7
            BEGIN

                  IF NOT ASCII(SUBSTRING(@strBIC,@I,1)) BETWEEN 65 AND 90
                        BEGIN
                             SET @X = 0
                        END
                        SET @I = @I + 1
            END

      IF  NOT ASCII(SUBSTRING(@strBIC,7,1)) BETWEEN 65 AND 90 AND  not ASCII(SUBSTRING(@strBIC,7,1)) BETWEEN 50 AND 57

                        BEGIN
                             SET @X = 0
                        END
       
      IF  NOT ASCII(SUBSTRING(@strBIC,8,1)) BETWEEN 65 AND 78 AND           
            NOT ASCII(SUBSTRING(@strBIC,8,1)) BETWEEN 80 AND 90 AND
            NOT ASCII(SUBSTRING(@strBIC,8,1)) BETWEEN 48 AND 57

                        BEGIN
                             SET @X = 0
                        END
       

      IF  (SELECT COUNT(*) FROM DBO.tbPaisesISO3166 WHERE strCodPais = SUBSTRING(@strBIC,5,2)) = 0

            BEGIN
                  SET @X = 0
            END

      IF LEN(@strBIC) <> 8 AND LEN(@strBIC) <> 11
            BEGIN
                  SET @X = 0
            END

      IF LEN(@strBIC) = 11
            BEGIN

                  SET @I = 9
                  WHILE @I < 12
                        BEGIN

                                   IF  not ASCII(SUBSTRING(@strBIC,@I,1)) BETWEEN 65 AND 90 AND NOT ASCII(SUBSTRING(@strBIC,@I,1)) between 48 and 57
                                   BEGIN
                                         SET @X = 0
                                   END
                             SET @I = @I + 1
                        END

            END

RETURN (@X)
END

La tabla DBO.tbPaisesISO3166 es una tabla de este estilo:

Tabla Países para IBAN y BIC

Código de .NET



Si queremos utilizar estas funciones en una aplicación de .net tendremos que usar un código similar al que voy a describir. En primer lugar en el evento ServerClick del botón de validación deberemos incluir entre otras las siguientes instrucciones: 
Definimos una conexión, conectamos y llamamos a la función del módulo de clientes RequestDatosBancarios donde estará defindo el código de negocio y cerramos la conexión.

Private Sub aValidacionDatosBancarios_ServerClick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles aValidacionDatosBancarios.ServerClick

Dim objclsSQLConnection As New clsSQLConnection
Dim objclsHTMLTable As New clsHTMLTable
Dim objclsClienteAltas As New clsCliente

objclsSQLConnection.Connect(objclsUser, objclsErrors)
If objclsSQLConnection.Open Then
objclsClienteAltas.RequestDatosBancarios(objclsSQLConnection, Me, objclsHTMLTable, True)
end if
objclsSQLConnection.Close()
End Sub

La definición de RequestDatosBancarios a continuación:


Aquí tomamos el valor de un check de la pantalla que nos indicará si hay que validar el IBAN o no. Hecho esto llamamos a las funciones que gestionan la llamada a las funciones de SQL Server de validación de IBAN y BIC, si el resultado de retorno es "ERROR" entonces muestra los correspondientes mensajes de error en la pantalla.

Public Function RequestDatosBancarios(ByRef objclsSqlConnection As clsSQLConnection, ByRef objPage As System.Web.UI.Page, ByRef objclsHTMLTable As clsHTMLTable, Optional ByRef blnRecogerDatosSiNoOk As Boolean = False) As Boolean

        Dim objclsDatosBanco As clsDatosBanco
        Dim objclsProperties As clsHTMLTable.clsPropertiesCell
        Dim objCollectionValues As New Collections.ArrayList
        Dim objCollectionTmp As New Collections.ArrayList
        Dim strValue As String
        Dim objclsError As clsErrors.clsError
        Dim blnIncompleto As Boolean
        Dim intContFilaBancos As Integer
        Dim valorCheckIBAN As Boolean

'EsIBAN es un check que indica si tiene que validar o no el campo 'introducido como IBAN
           
If objclsDatosBanco.EsIBAN.Value = 1 Then
        valorCheckIBAN = True
Else
        valorCheckIBAN = False
End If

If clsUtilidades.ValidarCuentaIBAN(objclsSqlConnection, clsTools.UserFormat(objclsDatosBanco.NumeroCuenta, "", clsTools.TypeDataCode.Data_String), valorCheckIBAN) = "ERROR" Then
                objclsError = New clsErrors.clsError
                objclsError.Description = " La línea " & intContFilaBancos & " no contiene un número de cuenta IBAN correcto"
                objclsError.typeError = clsErrors.typeErrorCode.Alert
                objclsErrors.ErrorsCollection.Add(objclsError)
                intContFilaBancos = intContFilaBancos + 1
                blnIncompleto = True
            End If
            If clsUtilidades.ValidarBIC(objclsSqlConnection, clsTools.UserFormat(objclsDatosBanco.NumeroBIC, "", clsTools.TypeDataCode.Data_String)) = "ERROR" Then
                objclsError = New clsErrors.clsError
                objclsError.Description = " La línea " & intContFilaBancos & " no contiene un número BIC correcto"
                objclsError.typeError = clsErrors.typeErrorCode.Alert
                objclsErrors.ErrorsCollection.Add(objclsError)
                intContFilaBancos = intContFilaBancos + 1
                blnIncompleto = True
            End If

End Function

Y aquí las definiciones de las funciones que llaman a las funciones de SQL Server para validar el IBAN y el BIC. La función de validación del IBAN trae como parámetro  blnEsIBAN que indica si el check de la pantalla estaba o no marcado y dependiendo de su valor valida o se salta la validación. Si valida, ejecuta la instrucción de SQL Server contenida en strSql y espera el valor de retorno para devolver error o no. Si no es error ambas funciones devuelven el IBAN o el BIC respectivamente, si es error devuelven "ERROR".

'se cambian las validaciones con el nuevo IBAN
Public Shared Function ValidarCuentaIBAN(ByRef objclsSqlConnection As clsSQLConnection, ByRef strIDNumeroCuenta As SqlTypes.SqlString, ByVal blnEsIBAN As Boolean) As String
        '************************************
        Dim strSql As String
        Dim objSqlLiteralIBAN As SqlTypes.SqlBoolean

        ValidarCuentaIBAN = ""
        If blnEsIBAN Then
            strSql = "SELECT DBO.ValidarIBAN (" & clsTools.UserFormatToSqlcommand(strIDNumeroCuenta, "", clsTools.TypeDataCode.Data_String) & ")"
            If objclsSqlConnection.GetSqlValue(strSql, objSqlLiteralIBAN) Then
                If objSqlLiteralIBAN.Value = True Then
                    ValidarCuentaIBAN = CStr(clsTools.UserFormat(strIDNumeroCuenta, 0, clsTools.TypeDataCode.Data_Number))
                Else
                    ValidarCuentaIBAN = "ERROR"
                End If
            Else
                ValidarCuentaIBAN = "ERROR"
            End If
        End If

    End Function

    'se cambian las validaciones con BIC
Public Shared Function ValidarBIC(ByRef objclsSqlConnection As clsSQLConnection, ByRef strIDNumeroBIC As SqlTypes.SqlString) As String
       
        Dim strSql As String
        Dim objSqlLiteralBIC As SqlTypes.SqlInt64  'En vez de retornar 'un bit sql retorna un bigint

        ValidarBIC = ""
        strSql = "SELECT DBO.ValidarBIC (" & clsTools.UserFormatToSqlcommand(strIDNumeroBIC, "", clsTools.TypeDataCode.Data_String) & ")"
        If objclsSqlConnection.GetSqlValue(strSql, objSqlLiteralBIC) Then
            If objSqlLiteralBIC.Value = 1 Then
                ValidarBIC = CStr(clsTools.UserFormat(strIDNumeroBIC, 0, clsTools.TypeDataCode.Data_Number))
            Else
                ValidarBIC = "ERROR"
            End If
        Else
            ValidarBIC = "ERROR"
        End If

    End Function


clsTools.UserFormat es un módulo de herramientas definido por el programador entre las que se incluye el formateo  de campos y tipos, su uso no es obligatorio, se pueden  tratar directamente los resultados de devueltos por las funciones según lo estime el programador 

No hay comentarios:

Publicar un comentario