jueves, 1 de febrero de 2018

¿Qué PAC debo elegir?



Desde la entrada de la facturación electrónica a México, ha ido creciendo el número de proveedores certificados que ofrecen sus servicios para poder realizar el proceso de timbrado y/o cancelación de tus comprobantes fiscales.

La elección del proveedor te puede significar una enorme diferencia entre detener la operación de embarques de tu empresa y tener fluidez en dicha tarea, entre tener dolores de cabeza interminables y pagar un poco más pero tener tranquilidad.

Lo más probable es que te inclines por buscar a uno económico, que por la cantidad de timbres que quieras comprar te puedan dar un precio bajo y así te ahorres una buena lana. Antes de elegir un proveedor, debes preguntarte lo siguiente:
  1. ¿Puedo soportar todo un fin de semana sin respuesta de soporte?
  2. ¿Estoy dispuesto a modificar los precios de mis productos para que el proveedor me timbre mi comprobante?
  3. ¿Significaría un problema para la operación que no se pueda facturar?

La facturación no siempre significa un proceso restrictivo en las empresas, algunas pueden esperar horas o días para poder emitir o cancelar su factura y otras dependen de la emisión para poder continuar con su operación.

Es importante señalar que he colaborado en muchos sistemas diferentes que están relacionados con la facturación electrónica, desde procesamiento de archivos planos, lectura de bases de datos del ERP, obtención de los datos desde Web Services, recepción de datos por Webhooks, captura en sistemas, etc.   Me ha tocado convivir (y sufrir) a algunos PAC, de los cuales en esta entrada platicaré de 4 en particular: Edicom, SolucionFactible.com, Expide tu Factura y Profact.

¿Por dónde comenzamos?, ¿por las buenas noticias o por las malas?   Para no terminar con un mal sabor de boca esta entrada, comenzaremos con los peores y terminaremos con el mejor (desde mi punto de vista)

Expide tu factura
El compromiso en este PAC es inexistente y su personal de soporte se ve desbordado por la cantidad de problemas que tienen (o tenían hasta que tuve la lamentable experiencia de utilizar sus servicios), lo cual se traduce en respuestas al vuelo y promesas que escribieron en el hielo.

El año pasado se había comenzado con la implementación del CFDI v3.3 con meses de anticipación, esto con el fin de evitar las sorpresas que se pudieran recibir durante las validaciones ejecutadas por el PAC.

En agosto de 2017 se había obtenido el dato de que su servicio web de timbrado quedaría en productivo durante la primera semana de septiembre. En cada comunicación que se tenía con ellos al llegar la fecha comprometida, lo único que se recibía era un "el departamento de sistemas está trabajando duro", al final tiraron la toalla y el compromiso ya fue imposible de obtener (porque no lo iban a cumplir)

Llegó el Día D, ¡y qué crees!, lo que ya sabíamos que iba a pasar pero nos negábamos a creer, ¡no servía el servicio web!, ya en fechas de productivo obligatorias por el SAT, su servicio seguía inundado de problemas y el personal de soporte estaba que se arrancaba las pestañas de la cantidad de trabajo que les cayó encima.

¿Los utilizaría?, no, en definitiva perdieron el rumbo desde la versión 3.3 del CFDI. Antes estaban bien, funcionaba en niveles muy aceptables pero se vinieron abajo con la nueva versión.

Profact
Este proveedor se compromete a tener el servicio activo el 99.9% del tiempo, te comentan que utilizarán 8 horas al mes para mantenimiento y éstas no deberán considerarse dentro del 99.9%, es decir que 96 horas al año no estarán disponibles por cuestiones de mantenimiento.

Por el mantenimiento no le veo mayor problema, todo sistema necesita mantenimiento y lo más probable es que tenga que estar fuera de línea para recibirlo.   Tomando en cuenta esto, el 99.9% se deberá calcular sobre las 8664 horas restantes del año.

Con la caída que tuvieron apenas ya andan en 99.9569%, lo cual los deja con poco margen para lo que resta del año, pero no se les puede reclamar aún porque siguen dentro del parámetro anunciado. Es curioso que en su SLA le hacen pensar al usuario que lograrán un 99.9999%, pero con inteligencia únicamente se comprometen al 99.9%

Tienen errores en su codificación, errores que no reconocen aunque se les demuestre que sí existen y que son latentes... así que si eres programador y te gira un poco el coco, te causarán muchos disgustos estos personajes (si es que encuentras los errores)

El personal con el que se tuvo intercambio de información demostró un conocimiento bastante cortito, tan corto que no sabían lo que es el timbrado del Sector Primario, se quedaron con la mente en blanco y no supieron qué contestar.

¿Los utilizaría?, solamente si la facturación fuera algo casi sin importancia en la operación y vendiera en precios que tengan una posición decimal como máximo.

SolucionFactible.com
Tiene un nivel de servicio muy aceptable, al menos en el tiempo que se ha estado utilizando. Su personal de soporte atiende las solicitudes de información y se esfuerzan en apoyarte en todo lo posible.

Timbran todos los complementos y también el sector primario, cuentan con librerías para todos los lenguajes o conectividad por Web Service si quieres establecer la comunicación por esa vía.

¿Por qué no lo coloco como el mejor?, porque el que le sigue solamente me ha tocado verlo caer una vez en los años que llevo en esto.

¿Lo utilizaría y lo recomendaría?, seguro que sí, la experiencia ha sido muy grata.

Edicom
Como lo he mencionado antes, solamente me ha tocado verlo caer una vez en años. La caída fue por un error en sus bases de datos, error que se corrigió después de unas horas, pero cumplieron con su SLA al final del año.

Hay poco qué decir de este proveedor, porque casi todo es positivo. La única mosca en el arroz es que el soporte lo prefieren telefónico, y hay algunas personas que preferimos la comunicación vía correo elctrónico.

¿Lo utilizaría y lo recomendaría?, ¡por supuesto!, ciertamente tendrá un costo más elevado, pero si tu operación está muy ligada a tu proceso de facturación, este es el proveedor por excelencia.

Comentarios finales
Es importante notar que cada quién habla de cómo le fue en la fiesta, estos comentarios son expuestos a partir de la experiencia que he tenido con cada uno de ellos. Es muy probable que tú no tengas la misma percepción de alguno de ellos si es que ya te tocó que te causaran un problema.

La persona que se dedica al desarrollo sabe que todos los sistemas fallan, lo importante es que:

  1. El impacto sea tan mínimo como sea posible.
  2. El tiempo de respuesta para solucionarlo sea reducido.
  3. Se acepten los errores y se corrijan.
Espero te haya resultado de utilidad esta entrada, ¡saludos!


viernes, 25 de agosto de 2017

cross database ownership chaining



En esta entrada exploraremos una característica muy interesante de SQL Server que permite brincar de una base de datos a otra con permisos mínimos para el usuario.

Para que esto sea posible será necesario que ambas bases de datos tengan el mismo propietario.

Vamos a crear dos bases de datos, cada una con una tabla muy sencilla.

USE master
GO
CREATE DATABASE DatabaseA
GO
CREATE DATABASE DatabaseB
GO
USE DatabaseA
GO
CREATE TABLE TableA(
    Valor VARCHAR(10)
)
GO
USE DatabaseB
GO
CREATE TABLE TableB(
    Valor VARCHAR(10)
)



Ahora vamos a crear un procedimiento almacenado que nos permita insertar un registro en TableA de DatabaseA y que en el mismo procedimiento se inserte un registro en TableB de DatabaseB.

USE DatabaseA
GO
CREATE PROC pAgregarValor(
    @Valor VARCHAR(10)
)
AS
BEGIN
    BEGIN TRAN
    BEGIN TRY
        INSERT INTO TableA (Valor) VALUES (@Valor)
        INSERT INTO DatabaseB.dbo.TableB (Valor) VALUES (@Valor)

        COMMIT
    END TRY
    BEGIN CATCH
        ROLLBACK

        DECLARE @Error VARCHAR(4000) = ERROR_MESSAGE()
        RAISERROR (@Error, 16, 1)
    END CATCH
END


Crearemos un login y le daremos acceso a DatabaseA, también dándole permiso de ejecución sobre el procedimiento almacenado que acabamos de crear.

USE master
GO
CREATE LOGIN LoginA WITH PASSWORD = 'password'
GO
USE DatabaseA
GO
CREATE USER LoginA FOR LOGIN LoginA
GO
GRANT EXEC ON pAgregarValor TO LoginA


Si nos conectamos con el login que acabamos de crear e intentamos ejecutar el procedimiento almacenado en DatabaseA nos va a marcar el siguiente error:



El login por lo menos debe tener acceso a DatabaseB.   Vamos a darle un usuario al login en esa base de datos para que pueda ingresar.    Aquí hay que notar que lo único que tendrá permitido es entrar a la base de datos y ejecutar las tareas que tenga permitidas el rol "public".

USE DatabaseB
GO
CREATE USER LoginA FOR LOGIN LoginA


Si volvemos a ejecutar el procedimiento almacenado en DatabaseA, el mensaje de error cambiará debido a que el usuario ya puede entrar a DatabaseB pero no tiene permiso de inserción sobre TableB.


He aquí donde utilizaremos la opción "cross database ownership chaining" de SQL Server.    Al darle permiso de ejecución en un procedimiento almacenado de DatabaseA que realice una modificación en DatabaseB, el usuario tendrá la autorización porque ambas bases de datos tienen el mismo propietario.

Vamos a habilitar la opción cross database ownership chaining.

sp_configure 'cross db ownership chaining', 1
GO
RECONFIGURE
GO
sp_configure 'cross db ownership chaining'


Si ejecutamos nuestro procedimiento almacenado de nuevo, veremos que la inserción resultó exitosa.



Notarás que no es necesario darle permiso de inserción a LoginA sobre TableB de DatabaseB, tuvo permiso porque el propietario de ambas bases de datos es el mismo y cross database ownership chaining está habilitado.

Espero esta entrada te haya gustado y te ayude a mejorar en tu trabajo.   Te espero en la siguiente, ¡saludos!


miércoles, 12 de julio de 2017

ORDER BY



Durante la semana pasada estaba de visita con un cliente y me encontré con un tema que resulta muy interesante, los conjuntos de resultados ordenados.

Cuando uno ejecuta una sentencia en SQL Server sin utilizar ORDER BY, los resultados son contemplados como conjuntos de datos, éstos no tienen un orden bien definido, son simplemente conjuntos donde sus datos satisfacen las características de los predicados utilizados en las sentencias.

Si utilizas el ORDER BY, entonces lo que estás solicitando es conocido como CURSOR.   Es importante no confundirlos con los objetos que nos permiten lecturas fila a fila.

Cuando la combinación de las columnas que aparecen en el criterio de ordenamiento no aseguran una combinación única, el orden del resultado no está totalmente asegurado, esto se debe a que varias formas de ordenar el mismo resultado cumplirían con los criterios de ordenamiento.

Para explicar mejor este punto vamos a realizar un ejercicio bastante sencillo pero que ayudará a entender mejor este tema tan interesante.

Vamos a conectarnos a una base de datos que tengas de pruebas (yo usaré AdventureWorks2014), crearemos una tabla de prueba y la llenaremos con datos aleatorios con el siguiente query.

IF NOT OBJECT_ID('dbo.TestOrderTORAB') IS NULL
    DROP TABLE dbo.TestOrderTORAB
GO
CREATE TABLE dbo.TestOrderTORAB(
    Id INT IDENTITY(1, 1),
    Nombre VARCHAR(32) NOT NULL,
    Color VARCHAR(8) NOT NULL,
    CONSTRAINT pkTestOrderTORAB PRIMARY KEY (Id)
)
GO
DECLARE @i INT = 1
WHILE @i <= 500
BEGIN
    INSERT INTO dbo.TestOrderTORAB (Nombre, Color) VALUES (REPLACE(CAST(NEWID() AS VARCHAR(36)), '-', ''), 'Amarillo')
    INSERT INTO dbo.TestOrderTORAB (Nombre, Color) VALUES (REPLACE(CAST(NEWID() AS VARCHAR(36)), '-', ''), 'Blanco')
    INSERT INTO dbo.TestOrderTORAB (Nombre, Color) VALUES (REPLACE(CAST(NEWID() AS VARCHAR(36)), '-', ''), 'Rojo')
    INSERT INTO dbo.TestOrderTORAB (Nombre, Color) VALUES (REPLACE(CAST(NEWID() AS VARCHAR(36)), '-', ''), 'Azul')

    SET @i += 1
END


En una nueva conexión (ventana) ejecuta la siguiente sentencia.    Es importante mencionar que tu resultado y mi resultado serán muy diferentes, esto es debido a que la tabla fue poblada con información aleatoria.   Aquí lo importante es que veas en tu resultado las filas que forman parte de él.

SELECT TOP 10 Id, Nombre, Color FROM dbo.TestOrderTORAB ORDER BY Color


Supongamos que por tareas de optimización se vio la necesidad de crear un índice sobre la tabla TestOrderTORAB.    Ejecuta este query en otra ventana para que no pierdas el resultado de la sentencia que anteriormente ejecutamos.

CREATE INDEX ixTestOrderTORAB ON dbo.TestOrderTORAB (Color, Nombre)

Si abrimos otra ventana y volvemos a ejecutar la misma sentencia que obtiene los 10 primeros productos ordenados por color, ¡veremos que es diferente el resultado!


Esto a pesar de que es el mismo query y que los datos de la tabla no han sido modificados.    ¿Cuál es la razón?, que el plan de ejecución con el que se despachó el query cambió de uno a otro y que la condición de tomar los primeros 10 productos ordenados por color sigue siendo cumplida con un conjunto de resultados diferente.


Ambos resultados son correctos, y la razón es que ambos cumplen con devolver los primeros 10 elementos que SQL Server encontró ordenándolos por nombre.

Si deseamos que esto no suceda, es necesario incluir una columna que ayude a que sea única la combinación de valores de las columnas utilizadas en el ORDER BY, a esta columna se le conoce como tiebreaker.

Vamos a eliminar el índice con la siguiente sentencia:

DROP INDEX ixTestOrderTORAB ON dbo.TestOrderTORAB

Modificamos el query para incluir la columna id como tiebreaker y ejecutamos para observar los resultados:

SELECT TOP 10 Id, Nombre, Color FROM dbo.TestOrderTORAB ORDER BY Color, Id


Creamos de nueva cuenta el índice con la siguiente sentencia:

CREATE INDEX ixTestOrderTORAB ON dbo.TestOrderTORAB (Color, Id, Nombre)

Ejecutamos el mismo query (el que tiene id como tiebreaker) en otra ventana y veremos que el resultado es el mismo.   No importa que se haya creado un índice, el resultado no fue modificado y la razón es que la combinación de valores de las columnas utilizadas en el ORDER BY es única.

Si comparamos los planes de ejecución de ambas sentencias (antes del índice vs después del índice) veremos que cambió, lo cual es totalmente deseable cuando uno genera índices, que éstos sean contemplados por el optimizador de consultas para mejorar el rendimiento.

En conclusión, es muy recomendable incluir una columna tiebreaker en caso de que la combinación de valores de las columnas utilizadas en un ORDER BY no sean únicos.

Espero esta entrada te haya gustado y te ayude a mejorar en tu trabajo.   Te espero en la siguiente, ¡saludos!