sábado, abril 20, 2024

Número Consecutivo y Clave en la Factura Electrónica en Costa Rica

Uno de los problemas iniciales al comenzar a crear el XML de la Factura Electrónica es la estructura de los campos Clave y Número Consecutivo, ya que pueden crear confusión. En este artículo explicamos como crear estos dos valores y también como crear el código de seguridad que forma parte de la clave.

Número Consecutivo

Este número se compone de 4 partes, código de casa matriz o código de sucursal, código de punto de venta, tipo de comprobante o tipo de documento, y número de comprobante. Ahora que significa cada uno:

Ejemplos de Documentos Electrónicos 4.3 - En este artículo vamos a brindar varios ejemplos de documentos electrónicos para la versión 4.3…

Código de casa matriz o código de sucursal: Este es un número que identifica el lugar donde se emiten las facturas, si la factura corresponde a la casa Matriz debe ser 001, este es un número reservado para casa matriz. Del 002 en adelante se utilizan para identificar cada una de las sucursales.

Código de punto de venta: Este es un numero secuencial que identifica cada uno de los puntos de venta de una sucursal, deben de comenzar en 1 por cada sucursal. Esto quiere decir que la sucursal 002 y 003 ambas deben de tener el punto de venta 1.

Tipo de documento: Este valor identifica que tipo de comprobante estamos generando, 01. Factura Electrónica, 02. Nota de Débito, 03. Nota de Crédito, 04. Tiquete Electrónico, 05. Confirmación de aceptación de documento electrónico, 06. Aceptación Parcial, 07. Rechazo de documento electrónico. (Ver encabezados de estos documentos XML)

Número de comprobante: Este es un valor consecutivo que se debe de generar por tipo de documento. Es un valor que se debe administrar propiamente en el punto de venta. Siempre comienza desde 1 por cada punto de venta y por cada tipo de documento. Por ejemplo el punto de venta código 1 comienza una factura electrónica con el número de comprobante 1, el punto de venta 2 comienza con el número de comprobante en 1 y este mismo punto de venta realiza una nota de crédito que también comienza en 1. Por lo tanto, cada punto de venta y cada tipo de documento tienen un numero de consecutivo individual.

Factura Electrónica Costa Rica Encabezados del XML Factura Electrónica Costa Rica 4.3 - Para generar el XML de la factura electrónica de Costa Rica se debe de seguir…

Clave

Este es el valor que más confusión ha generado, principalmente por el código de seguridad. En este artículo te brindamos una sugerencia de como generar este código y que tenga lógica con respecto a los mismos datos de la factura.

Se compone de 8 partes, código de país (3 caracteres), día (2 caracteres), mes (2 caracteres), año (2 caracteres), número de identificación (12 caracteres), numeración consecutiva (20 caracteres), situación del comprobante (1 carácter), código de seguridad (8 caracteres). Ahora que significa cada uno:

Código de país: Es un valor internacional de 3 caracteres que identifica el país, en este caso Costa Rica es 506.

Día: Valor de 2 caracteres que representa el día, ejemplo 01, 10, 24.

Mes: Valor de 2 caracteres que representa el mes, ejemplo 01, 08, 12.

Año: Valor de 2 caracteres que representa el año, en este caso los últimos 2 dígitos del año.

Número de identificación: Este valor debe ser el mismo que está definido como numero de identificación del emisor en la factura electrónica. En caso contrarió Hacienda va a rechazar la factura. Tiene una longitud de 12 caracteres y por ejemplo si el numero de identificación es de 9 los otros caracteres a la izquierda se llenan con 0.

Numeración consecutiva: Este es el valor de 20 caracteres generado arriba, es el número de consecutivo de la factura electrónica.

Situación de comprobante electrónica: Valor de 1 carácter que identifica la situación actual cuando se genera el comprobante, 1. Situación Normal, 2. Contingencia, 3. Sin Internet.

Código de seguridad: Este valor tiene una longitud de 8 caracteres y debe ser generado diferente para cada comprobante. Ahora si bien puede ser un número aleatorio lo ideal es que cuente con un formato estandarizado. Después de haber realizado sistemas de facturas electrónicas para países como Brasil, Perú, Ecuador, Guatemala, Chile, en algunos de estos utilizan un valor similar pero si le han definido un formato, para Costa Rica yo adapté de estos uno propio modificado.

Ejemplo se puede descargar en https://github.com/royrojas/FacturaElectronicaCR

Ejemplos de Documentos Electrónicos 4.3 - En este artículo vamos a brindar varios ejemplos de documentos electrónicos para la versión 4.3…

Generar el código de seguridad

Este ejemplo es una propuesta de como se debería de hacer, pero usted puede tener su propio generador, lo que si recomiendo es que no sea un número random y que tu clave de seguridad tenga una estructura lógica.

Ejemplo C# .Net

        public static string CreaCodigoSeguridad(string TipoComprobante, 
                                                 string Localidad, 
                                                 string CodigoPuntoVenta, 
                                                 DateTime Fecha, 
                                                 string NumeroFactura)
        {
            // 'Los parametros TipoComprobante, Localidad y CodigoPuntoVenta 
            //  pueden modificarse por otros valores, siempre manteniendo la longitud
            // 'Tipo Comprobante debe tener 2 caracteres máximo
            // 'Localidad debe tener 3 caracteres máximo
            // 'Punto de Venta debe de tener 5 caracteres máximo
            // 'Fecha es un campo datetime, debe ser la fecha de la factura
            // 'Número Factura debe tener máximo 10 caracteres 
            //  y debe ser el mismo parámetro que se usa en la 
            //  funcion GeneraNumeroSecuencia
            try
            {
                if (Anno.Trim().Length == 4)
                    Anno = Anno.Substring(2, 2);

                if ((TipoComprobante.Trim().Length > 2))
                {
                    throw new Exception("Tipo Comprobante debe tener 2 caracteres");
                }

                if ((Localidad.Trim().Length > 3))
                {
                    throw new Exception("Localidad no debe de superar los 3 caracteres");
                }

                if ((CodigoPuntoVenta.Trim().Length > 5))
                {
                    throw new Exception("Codigo Punto Venta no debe de superar los 5 caracteres");
                }

                if ((NumeroFactura.Trim().Length > 10))
                {
                    throw new Exception("Numero Factura no de superar los 10 caracteres");
                }

                string concatenado = "";
                concatenado = (concatenado + TipoComprobante.PadLeft(2, '0'));
                concatenado = (concatenado + Localidad.PadLeft(3, '0'));
                concatenado = (concatenado + CodigoPuntoVenta.PadLeft(5, '0'));
                concatenado = (concatenado + Fecha.ToString("yyyyMMddHHmmss"));
                concatenado = (concatenado + NumeroFactura.PadLeft(10, '0'));
                if ((concatenado.Length != 34))
                {
                    throw new Exception("El concatenado debe de ser de 34 caracteres para poder calcular el código de seguridad");
                }

                int calculo = 0;
                calculo = (calculo
                            + (int.Parse(concatenado.Substring(0, 1)) * 3));
                calculo = (calculo
                            + (int.Parse(concatenado.Substring(1, 1)) * 2));
                calculo = (calculo
                            + (int.Parse(concatenado.Substring(2, 1)) * 9));
                calculo = (calculo
                            + (int.Parse(concatenado.Substring(3, 1)) * 8));
                calculo = (calculo
                            + (int.Parse(concatenado.Substring(4, 1)) * 7));
                calculo = (calculo
                            + (int.Parse(concatenado.Substring(5, 1)) * 6));
                calculo = (calculo
                            + (int.Parse(concatenado.Substring(6, 1)) * 5));
                calculo = (calculo
                            + (int.Parse(concatenado.Substring(7, 1)) * 4));
                calculo = (calculo
                            + (int.Parse(concatenado.Substring(8, 1)) * 3));
                calculo = (calculo
                            + (int.Parse(concatenado.Substring(9, 1)) * 2));
                calculo = (calculo
                            + (int.Parse(concatenado.Substring(10, 1)) * 9));
                calculo = (calculo
                            + (int.Parse(concatenado.Substring(11, 1)) * 8));
                calculo = (calculo
                            + (int.Parse(concatenado.Substring(12, 1)) * 7));
                calculo = (calculo
                            + (int.Parse(concatenado.Substring(13, 1)) * 6));
                calculo = (calculo
                            + (int.Parse(concatenado.Substring(14, 1)) * 5));
                calculo = (calculo
                            + (int.Parse(concatenado.Substring(15, 1)) * 4));
                calculo = (calculo
                            + (int.Parse(concatenado.Substring(16, 1)) * 3));
                calculo = (calculo
                            + (int.Parse(concatenado.Substring(17, 1)) * 2));
                calculo = (calculo
                            + (int.Parse(concatenado.Substring(18, 1)) * 9));
                calculo = (calculo
                            + (int.Parse(concatenado.Substring(19, 1)) * 8));
                calculo = (calculo
                            + (int.Parse(concatenado.Substring(20, 1)) * 7));
                calculo = (calculo
                            + (int.Parse(concatenado.Substring(21, 1)) * 6));
                calculo = (calculo
                            + (int.Parse(concatenado.Substring(22, 1)) * 5));
                calculo = (calculo
                            + (int.Parse(concatenado.Substring(23, 1)) * 4));
                calculo = (calculo
                            + (int.Parse(concatenado.Substring(24, 1)) * 3));
                calculo = (calculo
                            + (int.Parse(concatenado.Substring(25, 1)) * 2));
                calculo = (calculo
                            + (int.Parse(concatenado.Substring(26, 1)) * 9));
                calculo = (calculo
                            + (int.Parse(concatenado.Substring(27, 1)) * 8));
                calculo = (calculo
                            + (int.Parse(concatenado.Substring(28, 1)) * 7));
                calculo = (calculo
                            + (int.Parse(concatenado.Substring(29, 1)) * 6));
                calculo = (calculo
                            + (int.Parse(concatenado.Substring(30, 1)) * 5));
                calculo = (calculo
                            + (int.Parse(concatenado.Substring(31, 1)) * 4));
                calculo = (calculo
                            + (int.Parse(concatenado.Substring(32, 1)) * 3));
                calculo = (calculo
                            + (int.Parse(concatenado.Substring(33, 1)) * 2));
                int mDV = 0;
                int digitoMod = 0;
                digitoMod = (calculo % 11);
                if (((digitoMod == 0)
                            || (digitoMod == 1)))
                {
                    mDV = 0;
                }
                else
                {
                    mDV = (11 - digitoMod);
                }

                return (TipoComprobante.PadLeft(2, '0')
                            + (calculo.ToString().PadLeft(5, '0') + mDV.ToString()));
            }
            catch (Exception ex)
            {
                throw ex;
            }

        }

Ejemplo VB .net

    Public Function CreaCodigoSeguridad(TipoComprobante As String, Localidad As String,
                                        CodigoPuntoVenta As String, Fecha As Date,
                                        NumeroFactura As String) As String

        ''Los parametros TipoComprobante, Localidad y CodigoPuntoVenta pueden modificarse por otros valores, siempre manteniendo la longitud
        ''Tipo Comprobante debe tener 2 caracteres máximo
        ''Localidad debe tener 3 caracteres máximo
        ''Punto de Venta debe de tener 5 caracteres máximo
        ''Fecha es un campo datetime, debe ser la fecha de la factura
        ''Numero Factura debe tener máximo 10 caracteres y debe ser el mismo parámetro que se usa en la funcion CreaNumeroSecuencia
        Try
            If TipoComprobante.Trim.Length > 2 Then Throw New Exception("Tipo Comprobante debe tener 2 caracteres")
            If Localidad.Trim.Length > 3 Then Throw New Exception("Localidad no debe de superar los 3 caracteres")
            If CodigoPuntoVenta.Trim.Length > 5 Then Throw New Exception("Codigo Punto Venta no debe de superar los 5 caracteres")
            If NumeroFactura.Trim.Length > 10 Then Throw New Exception("Numero Factura no de superar los 10 caracteres")

            ''Concatenamos los valores para poder calcular el valor con la fórmula más adelante.
            Dim concatenado As String = ""
            concatenado += TipoComprobante.PadLeft(2, "0")
            concatenado += Localidad.PadLeft(3, "0")
            concatenado += CodigoPuntoVenta.PadLeft(5, "0")
            concatenado += Fecha.ToString("yyyyMMddHHmmss")
            concatenado += NumeroFactura.PadLeft(10, "10")

            If concatenado.Length <> 34 Then Throw New Exception("El concatenado debe de ser de 34 caracteres para poder calcular el código de seguridad")

            Dim calculo As Integer = 0

            calculo += CInt(concatenado.Substring(0, 1)) * 3
            calculo += CInt(concatenado.Substring(1, 1)) * 2
            calculo += CInt(concatenado.Substring(2, 1)) * 9
            calculo += CInt(concatenado.Substring(3, 1)) * 8
            calculo += CInt(concatenado.Substring(4, 1)) * 7
            calculo += CInt(concatenado.Substring(5, 1)) * 6
            calculo += CInt(concatenado.Substring(6, 1)) * 5
            calculo += CInt(concatenado.Substring(7, 1)) * 4
            calculo += CInt(concatenado.Substring(8, 1)) * 3
            calculo += CInt(concatenado.Substring(9, 1)) * 2
            calculo += CInt(concatenado.Substring(10, 1)) * 9
            calculo += CInt(concatenado.Substring(11, 1)) * 8
            calculo += CInt(concatenado.Substring(12, 1)) * 7
            calculo += CInt(concatenado.Substring(13, 1)) * 6
            calculo += CInt(concatenado.Substring(14, 1)) * 5
            calculo += CInt(concatenado.Substring(15, 1)) * 4
            calculo += CInt(concatenado.Substring(16, 1)) * 3
            calculo += CInt(concatenado.Substring(17, 1)) * 2
            calculo += CInt(concatenado.Substring(18, 1)) * 9
            calculo += CInt(concatenado.Substring(19, 1)) * 8
            calculo += CInt(concatenado.Substring(20, 1)) * 7
            calculo += CInt(concatenado.Substring(21, 1)) * 6
            calculo += CInt(concatenado.Substring(22, 1)) * 5
            calculo += CInt(concatenado.Substring(23, 1)) * 4
            calculo += CInt(concatenado.Substring(24, 1)) * 3
            calculo += CInt(concatenado.Substring(25, 1)) * 2
            calculo += CInt(concatenado.Substring(26, 1)) * 9
            calculo += CInt(concatenado.Substring(27, 1)) * 8
            calculo += CInt(concatenado.Substring(28, 1)) * 7
            calculo += CInt(concatenado.Substring(29, 1)) * 6
            calculo += CInt(concatenado.Substring(30, 1)) * 5
            calculo += CInt(concatenado.Substring(31, 1)) * 4
            calculo += CInt(concatenado.Substring(32, 1)) * 3
            calculo += CInt(concatenado.Substring(33, 1)) * 2

            ''Después de calcular el valor, obtenemos el dígito verificador.
            Dim mDV As Integer = 0
            Dim digitoMod As Integer = 0

            digitoMod = calculo Mod 11

            If digitoMod = 0 Or digitoMod = 1 Then
                mDV = 0
            Else
                mDV = 11 - digitoMod
            End If

            ''Retornamos el valor compuesto del tipo de documento, el valor calculado y el digito verificador.
            ''Esto nos retorna un valor congruente y estandarizado.
            Return TipoComprobante.PadLeft(2, "0") & calculo.ToString.PadLeft(5, "0") & mDV.ToString
        Catch ex As Exception
            Throw ex
        End Try
    End Function

Si desea más información del API no dude en contactarnos.

Información: contacto@dotnetcr.com

Roy Rojas
Roy Rojashttp://www.dotnetcr.com
Con más de 20 años de experiencia en programación, experto en lenguajes .NET, VB, C#, ASP.NET, Xamarin, XCode, DBA en SQL Server. Creador de dotnetcr.com, sitio web para programadores en español. royrojas.com | dotnetcr.com | GitHub
Roy Rojas
Roy Rojashttp://www.dotnetcr.com
Con más de 20 años de experiencia en programación, experto en lenguajes .NET, VB, C#, ASP.NET, Xamarin, XCode, DBA en SQL Server. Creador de dotnetcr.com, sitio web para programadores en español. royrojas.com | dotnetcr.com | GitHub