---
title: SQLSync – Resultado de integración
description: "# 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](https://facturasend.com.py/documentacion/sqlsync-estructuras-de-tablas-y-campos/#estructura-de-resultados))…"
url: "https://facturasend.com.py/documentacion/sqlsync-resultado-de-integracion/"
updated: "2025-08-27"
category: Integración desde una base de datos
---

# SQLSync – Resultado de integración

# 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](https://facturasend.com.py/documentacion/sqlsync-estructuras-de-tablas-y-campos/#estructura-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
