Extendiendo OutSystems con .Net. Ciclo OutSystems II

En este artículo hablaremos de OutSystems, una plataforma low-code que permite crear desarrollos complejos de forma ágil en menor tiempo comparado con los desarrollos tradicionales.

Una de las ventajas de OutSystems en comparación a otras plataformas low-code, es la flexibilidad al desarrollar, permite crear extensiones de .Net por ejemplo. Esto abre un abanico de posibilidades a la hora de desarrollar como la reutilización de código, uso de paquetes nuget y etc.

Para demostrar el potencial, vamos a implementar desde 0 una extensión .Net de OutSystems para validar sí un DNI, CIF o NIE es correcto según un algoritmo.

Lo primero que tenemos que hacer, es crear una extensión dentro de Service Studio, la voy a llamar de Validación.

Validación

 

El siguiente paso será crear una acción que reciba un parámetro de entrada y que devuelva si el documento es correcto o no. Está acción se crea dentro de la extensión que acabamos de crear, mediante la herramienta Integration Studio.

acciones de OutSystem

El proximo paso será abrir Visual Studio desde Integration Service, lo podemos hacer clicando sobre el icono de editar C#, destaco en la captura abajo:

Integration Servic

Una vez abierto, podemos apreciar que tenemos una clase con el mismo nombre de nuestro proyecto, que se llama Validación.

Validation Console OutSystem

Podemos apreciar tambien, que el método en Visual Studio tiene el mismo nombre que hemos definido, así como los parametros de entrada y salida, con la pequeña diferencia que contiene un prefijo ss.

Ahora que tenemos nuestra Extensión creada, lo único que tenemos que hacer, es escribir nuestro código en .Net. Como las validaciones de NIF, CIF y DNI son muy comunes, buscaré en Internet, estoy seguro que alguien ya implemento este algoritmo antes en .Net.

El código que he encontrado como el primero resultado es el de abajo, lo implementaremos dentro de nuestra extensión.

using System;
using System.Text.RegularExpressions;

/*
 * NumeroNif.cs
 * Servicios de validación de los números NIF
 * 
 * älskare, Ene/11
 */

namespace JnSoftware.Validaciones
{

    /// <summary>
    /// Representa un número. En la clase se desglosan las distintas opciones que se puedan
    /// encontrar
    /// </summary>
    public class NumeroNif
    {
        /// <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>
        private enum TiposCodigosEnum { NIF, NIE, CIF }

        // Número tal cual lo introduce el usuario
        private string numero;
        private TiposCodigosEnum tipo;

        /// <summary>
        /// Parte de Nif: En caso de ser un Nif intracomunitario, permite obtener el cógido del país
        /// </summary>
        public string CodigoIntracomunitario { get; internal set; }
        internal bool EsIntraComunitario { get; set; }

        /// <summary>
        /// Parte de Nif: Letra inicial del Nif, en caso de tenerla
        /// </summary>
        public string LetraInicial { get; internal set; }

        /// <summary>
        /// Parte de Nif: Bloque numérico del NIF. En el caso de un NIF de persona física,
        /// corresponderá al DNI
        /// </summary>
        public int Numero { get; internal set; }

        /// <summary>
        /// Parte de Nif: Dígito de control. Puede ser número o letra
        /// </summary>
        public string DigitoControl { get; internal set; }

        /// <summary>
        /// Valor que representa si el Nif introducido es correcto
        /// </summary>
        public bool EsCorrecto { get; internal set; }

        /// <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 string TipoNif { get { return tipo.ToString(); } }

        /// <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>
        private NumeroNif(string numero)
        {
            // Se eliminan los carácteres sobrantes
            numero = EliminaCaracteres(numero);

            // Todo en maýusculas
            numero = numero.ToUpper();

            // Comprobación básica de la cadena introducida por el usuario
            if (numero.Length != 9 && numero.Length != 11)
                throw new ArgumentException("El NIF no tiene un número de caracteres válidos");
           
            this.numero = numero;
            Desglosa();

            switch (tipo)
            {
                case TiposCodigosEnum.NIF:
                case TiposCodigosEnum.NIE:
                    this.EsCorrecto = CompruebaNif();
                    break;
                case TiposCodigosEnum.CIF:
                    this.EsCorrecto = CompruebaCif();
                    break;
            }
        }

        #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 void Desglosa()
        {
            Int32 n;
            if (numero.Length == 11)
            {
                // Nif Intracomunitario
                EsIntraComunitario = true;
                CodigoIntracomunitario = numero.Substring(0, 2);
                LetraInicial = numero.Substring(2, 1);
                Int32.TryParse(numero.Substring(3, 7), out n);
                DigitoControl = numero.Substring(10, 1);
                tipo = GetTipoDocumento(LetraInicial[0]);
            }
            else
            {
                // Nif español
                tipo = GetTipoDocumento(numero[0]);
                EsIntraComunitario = false;
                if (tipo == TiposCodigosEnum.NIF)
                {
                    LetraInicial = string.Empty;
                    Int32.TryParse(numero.Substring(0, 8), out n);
                }
                else
                {
                    LetraInicial = numero.Substring(0, 1);
                    Int32.TryParse(numero.Substring(1, 7), out  n);
                }
                DigitoControl = numero.Substring(8, 1);
            }
            Numero = n;
        }

        /// <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 TiposCodigosEnum GetTipoDocumento(char letra)
        {
            Regex regexNumeros = new Regex("[0-9]");
            if (regexNumeros.IsMatch(letra.ToString()))
                return TiposCodigosEnum.NIF;

            Regex regexLetrasNIE = new Regex("[XYZ]");
            if (regexLetrasNIE.IsMatch(letra.ToString()))
                return TiposCodigosEnum.NIE;

            Regex regexLetrasCIF = new Regex("[ABCDEFGHJPQRSUVNW]");
            if (regexLetrasCIF.IsMatch(letra.ToString()))
                return TiposCodigosEnum.CIF;

            throw new ApplicationException("El código no es reconocible");
        }



        /// <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 string EliminaCaracteres(string numero)
        {
            // Todos los carácteres que no sean números o letras
            string caracteres = @"[//^w]";
            Regex regex = new Regex(caracteres);
            return regex.Replace(numero, "");
        }

        #endregion

        #region Cálculos

        private bool CompruebaNif()
        {
            return DigitoControl == GetLetraNif();
        }

        /// <summary>
        /// Cálculos para la comprobación del Cif (Entidad jurídica)
        /// </summary>
        private bool CompruebaCif()
        {
            string[] letrasCodigo = { "J", "A", "B", "C", "D", "E", "F", "G", "H", "I" };

            string n = Numero.ToString("0000000");
            Int32 sumaPares = 0;
            Int32 sumaImpares = 0;
            Int32 sumaTotal = 0;
            Int32 i = 0;
            bool retVal = false;

            // Recorrido por todos los dígitos del número
            for (i = 0; i < n.Length; i++)
            {
                Int32 aux;
                Int32.TryParse(n[i].ToString(), out aux);

                if ((i + 1) % 2 == 0)
                {
                    // 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);
                }
            }
            // Se suman los resultados de los números pares e impares
            sumaTotal += sumaPares + sumaImpares;

            // Se obtiene el dígito de las unidades
            Int32 unidades = sumaTotal % 10;

            // Si las unidades son distintas de 0, se restan de 10
            if (unidades != 0)
                unidades = 10 - unidades;

            switch (LetraInicial)
            {
                // Sólo números
                case "A":
                case "B":
                case "E":
                case "H":
                    retVal = DigitoControl == unidades.ToString();
                    break;

                // Sólo letras
                case "K":
                case "P":
                case "Q":
                case "S":
                    retVal = DigitoControl == letrasCodigo[unidades];
                    break;

                default:
                    retVal = (DigitoControl == unidades.ToString())
                            || (DigitoControl == letrasCodigo[unidades]);
                    break;
            }

            return retVal;

        }

        /// <summary>
        /// Obtiene la suma de todos los dígitos
        /// </summary>
        /// <returns>de 23, devuelve la suma de 2 + 3</returns>
        private Int32 SumaDigitos(Int32 digitos)
        {
            string sNumero = digitos.ToString();
            Int32 suma = 0;

            for (Int32 i = 0; i < sNumero.Length; i++)
            {
                Int32 aux;
                Int32.TryParse(sNumero[i].ToString(), out aux);
                suma += aux;
            }
            return suma;
        }

        /// <summary>
        /// Obtiene la letra correspondiente al Dni
        /// </summary>
        private string GetLetraNif()
        {
            int indice = Numero % 23;
            return "TRWAGMYFPDXBNJZSQVHLCKET"[indice].ToString();
        }

        /// <summary>
        /// Obtiene una cadena con el número de identificación completo
        /// </summary>
        public override string ToString()
        {
            string nif;
            string formato = "{0:0000000}";

            if (tipo == TiposCodigosEnum.CIF && LetraInicial == "")
                formato = "{0:00000000}";
            if (tipo == TiposCodigosEnum.NIF)
                formato = "{0:00000000}";
            
            nif = EsIntraComunitario ? CodigoIntracomunitario :
                string.Empty + LetraInicial + string.Format(formato,Numero ) + DigitoControl;
            return nif;
        }

        #endregion

        /// <summary>
        /// Comprobación de un número de identificación fiscal español
        /// </summary>
        /// <param name="numero">Numero a analizar</param>
        /// <returns>Instancia de <see cref="NumeroNif"/> con los datos del número.
        /// Destacable la propiedad <seealso cref="NumeroNif.EsCorrecto"/>, que contiene la verificación
        /// </returns>
        public static NumeroNif CompruebaNif(string numero)
        {
            return new NumeroNif(numero);
        }

    }

}

 

Lo primero que hacemos, es crear una clase con el mismo nombre y pegar el código del algoritmo dentro, tal cual.

Outsystem

 

El siguiente paso, es invocar el método que necesitamos, dentro de nuestra clase Validación, en este caso es CompruebaNif que está dentro de clase NumeroNif.

OutSystem

 

Listo, lo publicamos y ya podemos utilizar. Hacemos clic en guardar en Visual Studio y luego debemos hacer clic en el icono de publicar en Integration Service, que está representado con un botón que contiene el número 1.

OutSystems

 

Lo que tenemos que hacer ahora es utilizar nuestra función, para esto agregamos la referencia de nuestra extensión en el modulo que queremos utilizar, arrastramos nuestra función y pasamos el valor de entrada que será una variable que contiene el número del documento que queremos validar.

OutSystem

 

Como respuesta retorna si es valido o no el documento.

El algoritmo que hemos encontrado, nos puede retorna mucho más que esto, si queremos que retorne todo, podemos crear una estructura que retorne todo está información en lugar de un booleano.

Otro punto positivo es que podemos mostrar las excepciones directamente desde .Net, ejemplo:

OutSystems

OutSystems directamente nos mostrar la excepción de la siguiente forma:

OutSystems

 

De forma que no es necesario retornar el error en el método, lo podemos mostrar directamente.

En resumen, por más que este algoritmo se puede hacer perfectamente desde OutSystems, tenemos la posibilidad de reutilizar código .Net de una forma simples.

Referencias y recursos

Plugin OutSystems