View Categories

SQLSync – Resultado de integración

9 minutos de lectura

Guardado en tabla de resultados #

De forma predeterminada el resultado de la integración y consulta de estado y eventos se guarda en una tabla de resultados (facturasend_result) diseñada específicamente para el efecto.

Ver estructura de la (Tabla de Resultados)

Esta tabla sigue una estructura de campos con clave (name) y valor (value) de forma que cualquier información adicional sobre la integración puede ser agregada simplemente agregando más registros, ya que los nombres de las columnas no están prefijadas, ganando así más flexibilidad.

IMPORTANTE

Los datos que se guardan en la tabla facturasend_result, en cuanto a los valores de ERROR, ERROR_EVENTO, PAUSADO, CDC, ESTADO, ESTADO_EVENTO deben estar disponibles inmediatamente en la relación principal (transacciones_fe_view), luego de que éstas hayan sido actualizadas en la tabla de resultados, para que todo el proceso ocurra con normalidad.

Para lo comentado anteriormente, existen 2 opciones principales:

Opción A-Mecanismo de sub-select #

Se puede utilizar mecanismos de SUB-SELECTs sobre dichos campos (ERROR, ERROR_EVENTO, PAUSADO, CDC, ESTADO, ESTADO_EVENTO) al consultar la tabla/vista transacciones_fe_view para recuperar dichos campos de la tabla facturasend_result y no de la relación principal aunque en la relación principal existan estos campos.

Si la estructura principal se trata de una vista, el creador de la vista puede especificar en la definición DDL de la vista para que dichos sub-selects sean realizados internamente cuando se realiza el SQL-SELECT sobre dicha vista, como se muestra abajo en el siguiente ejemplo realizado con SQL Estándar.

CREATE VIEW transacciones_fe_view AS 
    SELECT campos,
        (SELECT value FROM factura_send fs WHERE fs.transaccion_id = ve.venta_numero 
                                           AND name='ERROR' LIMIT 1)         AS error,
        (SELECT value FROM factura_send fs WHERE fs.transaccion_id = ve.venta_numero 
                                           AND name='ERROR_EVENTO' LIMIT 1)  AS error_evento,
        (SELECT value FROM factura_send fs WHERE fs.transaccion_id = ve.venta_numero 
                                           AND name='PAUSADO' LIMIT 1)       AS pausado,
        (SELECT value FROM factura_send fs WHERE fs.transaccion_id = ve.venta_numero 
                                           AND name='CDC' LIMIT 1)           AS cdc,
        (SELECT value FROM factura_send fs WHERE fs.transaccion_id = ve.venta_numero 
                                           AND name='ESTADO' LIMIT 1)        AS estado,
        (SELECT value FROM factura_send fs WHERE fs.transaccion_id = ve.venta_numero 
                                           AND name='ESTADO_EVENTO' LIMIT 1) AS estado_evento,
        FROM ventas ve
	WHERE condicion = 'filtros'

        UNION

        SELECT campos FROM notas_creditos WHERE...
;
-- Para una mejor tolerancia a fallos y por si el transaccion_id pudiera repetirse
-- entre varios tipos de documentos, podría añadirse más una condición en el WHERE 
-- de tipo de documento, quedando la condición asi:
-- WHERE fs.transaccion_id = ve.venta_numero AND fs.tipo_documento = 1

Pero si la estructura principal se trata de una tabla, la opción de sub-select, puede ser más compleja de realizar, por ese motivo SQLSync ya incluye una opción que puede activarla por configuración, para que el Sub-select sea realizado por el propio software.

Para configurar la lectura con sub-select en el config.properties, por ejemplo para la base de datos mysql, entonces debe especificar:

database.mysql.read_all_status_from_facturasend_table=Y

Tenga en cuenta que deberá cambiar .mysql. por la base de datos que está utilizando.

Esta opción de SUB-SELECT ya sea por vista o por sistema es más práctica para aquellos casos no existirán muchos documentos a integrar (registros en las tablas), o se estará realizando una limpieza cada cierto tiempo, ya que como sabemos los sub-select aplicados sobre dichos campos puede tener su peso en la performance.

También es la única opción que se dispone para los casos de tablas DBF, ya que en dicha estructura no se puede aplicar la opción B, es decir, la siguiente opción existe de forma implícita en la configuración de SQLSync y no se puede cambiar para el tipo DBF.

#seteado de forma implícita y predeterminada para DBF
database.dbf.read_all_status_from_facturasend_table=Y

Opción B-Mecanismo de Triggers #

Puede utilizar TRIGGERS de base de datos para que cuando se Inserte/Actualice/Elimine regsitros con uno de los siguientes valores en el campo name (ERROR, ERROR_EVENTO, PAUSADO, CDC, ESTADO, ESTADO_EVENTO) de la tabla facturasend_result, estos datos sean automáticamente actualizados en la tabla transacciones_fe_view del registro específico al cual corresponde.

Un ejemplo de trigger para actualizar los datos en la base de datos mysql desde la tabla facturasend_result hacia la tabla transacciones_fe_view, que incluye los campos CDC, ERROR, PAUSADO, ESTADO y QR, se muestra a continuación:

CREATE DEFINER = 'root'@'localhost' TRIGGER `facturasend_result_after_ins_tr` AFTER INSERT ON `facturasend_result`
  FOR EACH ROW
BEGIN
    if (new.name='CDC') and (new.value<>'') then
	update transacciones_fe_view set cdc=new.value where transaccion_id=new.transaccion_id;
    end if;

    if (new.name='ERROR') and (new.value<>'') then
	update transacciones_fe_view set error=new.value where transaccion_id=new.transaccion_id;
    end if;

    if (new.name='PAUSADO') and (new.value<>'') then
	update transacciones_fe_view set pausado=new.value  where transaccion_id=new.transaccion_id;
    end if;

    if (new.name='ESTADO') and (new.value<>'') then
	update transacciones_fe_view set estado=new.value where transaccion_id=new.transaccion_id;
    end if;

    if (new.name='QR') and (new.value<>'') then
	update transacciones_fe_view set qr=new.value where transaccion_id=new.transaccion_id;
    end if;
END;

CREATE DEFINER = 'root'@'localhost' TRIGGER `facturasend_result_after_del_tr` AFTER DELETE ON `facturasend_result`
  FOR EACH ROW
BEGIN
    if (old.name='ERROR') then
	update transacciones_fe_view set error=null where transaccion_id=old.transaccion_id;
    end if;

    if (old.name='PAUSADO')  then
	update transacciones_fe_view set pausado=null where transaccion_id=old.transaccion_id;
    end if;
END;

Note que para el ejemplo de mysql de arriba el WHERE sólo es realizado comparando las columnas transaccion_id de ambas tablas. No se añade más nada al WHERE debido a que para ese caso no se consideró que las columnas transaccion_id se van a repetir por tipo de documento. Esta decisión la dejamos abierta para que el implementador decida cómo comparar.

Mas abajo, en el ejemplo de trigger para actualizar los datos en la base de datos postgres desde la tabla facturasend_result hacia la tabla transacciones_fe_view, que se muestra a continuación, ya se puede ver que el WHERE se realiza con las columnas transaccion_id y tipo_documento de ambas tablas, siendo suficiente si por acaso se repitan valores de un mismo transaccion_id para varios tipos de tipo_documento:

-- Function: public.trigger_proc_facturasend_result()
-- DROP FUNCTION public.trigger_proc_facturasend_result();

CREATE OR REPLACE FUNCTION public.trigger_proc_facturasend_result()
  RETURNS trigger AS
$BODY$
DECLARE
	
BEGIN
IF (TG_OP = 'DELETE') THEN
	IF TRIM(OLD.name)='ERROR' THEN
		UPDATE transacciones_fe_view t SET ERROR=NULL WHERE t.transaccion_id=OLD.transaccion_id and t.tipo_documento=OLD.tipo_documento ;
	ELSIF TRIM(OLD.name)='ERROR_EVENTO' THEN
		UPDATE transacciones_fe_view t SET ERROR_EVENTO=NULL WHERE t.transaccion_id=OLD.transaccion_id and t.tipo_documento=OLD.tipo_documento ;
	ELSIF TRIM(OLD.name)='PAUSADO' THEN
		UPDATE transacciones_fe_view t SET PAUSADO=NULL WHERE t.transaccion_id=OLD.transaccion_id and t.tipo_documento=OLD.tipo_documento ;
--	ELSIF TRIM(OLD.name)='CDC' THEN
--		UPDATE transacciones_fe_view t SET CDC=NEW.value WHERE t.transaccion_id=OLD.transaccion_id and t.tipo_documento=OLD.tipo_documento ;
	ELSIF TRIM(OLD.name)='ESTADO' THEN
		UPDATE transacciones_fe_view t SET ESTADO=NULL WHERE t.transaccion_id=OLD.transaccion_id and t.tipo_documento=OLD.tipo_documento ;
	ELSIF TRIM(OLD.name)='ESTADO_EVENTO' THEN
		UPDATE transacciones_fe_view t SET ESTADO_EVENTO=NULL WHERE t.transaccion_id=OLD.transaccion_id and t.tipo_documento=OLD.tipo_documento ;
	END IF ;

	RETURN OLD;
ELSIF (TG_OP = 'UPDATE') THEN
	RETURN NEW;
ELSIF (TG_OP = 'INSERT') THEN
	IF TRIM(NEW.name)='ERROR' THEN
		UPDATE transacciones_fe_view t SET ERROR=NEW.value WHERE t.transaccion_id=NEW.transaccion_id and t.tipo_documento=NEW.tipo_documento ;
	ELSIF TRIM(NEW.name)='ERROR_EVENTO' THEN
		UPDATE transacciones_fe_view t SET ERROR_EVENTO=NEW.value WHERE t.transaccion_id=NEW.transaccion_id and t.tipo_documento=NEW.tipo_documento ;
	ELSIF TRIM(NEW.name)='PAUSADO' THEN 
		UPDATE transacciones_fe_view t SET PAUSADO=CAST(NEW.value AS NUMERIC(1)) WHERE t.transaccion_id=NEW.transaccion_id and t.tipo_documento=NEW.tipo_documento ;
	ELSIF TRIM(NEW.name)='CDC' THEN
		UPDATE transacciones_fe_view t SET CDC=NEW.value WHERE t.transaccion_id=NEW.transaccion_id and t.tipo_documento=NEW.tipo_documento ;
	ELSIF TRIM(NEW.name)='QR' THEN
		UPDATE transacciones_fe_view t SET qr=NEW.value WHERE t.transaccion_id=NEW.transaccion_id and t.tipo_documento=NEW.tipo_documento ;
	ELSIF TRIM(NEW.name)='ESTADO' THEN
		UPDATE transacciones_fe_view t SET ESTADO=CAST(NEW.value AS NUMERIC(1)) WHERE t.transaccion_id=NEW.transaccion_id and t.tipo_documento=NEW.tipo_documento ;
	ELSIF TRIM(NEW.name)='ESTADO_EVENTO' THEN
		UPDATE transacciones_fe_view t SET ESTADO_EVENTO=CAST(NEW.value AS NUMERIC(1)) WHERE t.transaccion_id=NEW.transaccion_id and t.tipo_documento=NEW.tipo_documento ;
	END IF ;
	RETURN NEW;

END IF;
RETURN NULL;
END;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
ALTER FUNCTION public.trigger_proc_facturasend_result()
  OWNER TO postgres;

IMPORTANTE

Evite utilizar mecanismos de actualización de datos en diferido desde la tabla facturasend_result hacia la tabla transacciones_fe_view en segundo plano, o cada cierta cantidad de tiempo.

Esta no es una opción válida puesto que si la actualización no es inmediata puede ocurrir que el siguiente SELECT que se realice sobre la tabla transacciones_fe_view aun no tenga el dato actualizado y se vuelva a enviar un documento ya enviado anteriormente, generando así un error en la integración.

La actualización debe estar asegurada por una unicidad de transacción, vía trigger, o por SUB-SELECT como se mencionó más arriba, de forma que para el siguiente ciclo de lectura con SQL-SELECT los datos recuperados ya contengan la última actualización.

Otra opción disponible es configurar el SQLSync para que los datos se almacenen además de en la tabla facturasend_result, también en la tabla principal transacciones_fe_view directamente. Esto es explicado en el siguiente apartado.

Guardar resultados en tabla principal #

Si la estructura principal (transacciones_fe_view) es una tabla y se tiene acceso de escritura en dicha tabla, entonces el resultado de la integración y la actualización de la consulta de estados pueden ser almacenado en la misma tabla principal, para reflejar la integración inmediatamente en el monitor.

Los parámetros del config.properties para que se guarden los resultados en la tabla principal, se muestra a continuación, como ejemplo para la base de datos mysql. Cambié mysql por la base de datos que está utilizando para que le funcione

database.mysql.transaction_table_update=transacciones_fe_view
database.mysql.transaction_table_update.field.cdc=cdc
database.mysql.transaction_table_update.field.error=error
database.mysql.transaction_table_update.field.estado=estado
database.mysql.transaction_table_update.field.pausado=pausado
database.mysql.transaction_table_update.where.tipo_documento=tipo_documento
database.mysql.transaction_table_update.where.transaccion_id=transaccion_id

Hacer que los datos se guarden también en la estructura principal, no impedirá que los mismos datos se guarden también en la tabla de resultados (facturasend_result), motivo por el cual ambas tablas son imprescindibles para el sistema.

ADVERTENCIA

Esta última opción no funcionará con DBF, ya que por el hecho de que la tabla esté compartida, no conviene escribir en la misma aplicación que ya está escribiendo el programador.

Discriminación de la tabla de resultados #

Dependiendo del ERP que utilice puede que requiera que los resultados, que de forma predeterminada se almacenan en la tabla (facturasend_result) se almacenen en tablas diferentes y con diferente estructura, separándolos por tipo de documento.

Es decir, por ejemplo tener una tabla de resultados por cada tipo de documento, como se ejemplifica más abajo:

  • facturasend_result_factura
  • facturasend_result_nota_credito
  • facturasend_result_nota_debito
  • facturasend_result_nota_remision

Esta necesidad está cubierta por el SQLSync, realizando algunos pequeños cambios en los parámetros de configuración, los cuales se muestran en la configuración específica de cada motor de la base de datos