Los Informes en vDevelop son objetos del proyecto que como otros tipos de Vista de datos, tienen una única entrada de datos en forma de Lista de registros y nos devuelven otra Lista de registros ordenada, agrupada y paginada según un formato de impresión prefijado. Los Informes pueden ser elementos imprescindibles en muchas aplicaciones de gestión y por lo tanto necesitamos tener un profundo conocimiento de ellos.

Informes personalizables Existen 2 tipos de Informes con sus respectivos comandos para previsualizar o imprimir directamente la salida:

  • Informe nativo. Dispone de un editor propio para determinar la agrupación y paginación de los registros. Se inserta en los formularios de la misma manera que otras Vistas de datos como la Rejilla, con la particularidad de que la salida en pantalla se puede imprimir o guardar en disco.
  • Informe personalizable. El diseño de la salida agrupada y paginada de los registros se realiza mediante un Editor de la empresa NociSoft Software Solutions, que ha sido adaptado e integrado tanto en vDevelop como en vClient. Se llama Informe personalizable porque el diseño del Informe se guarda en un archivo XML que se puede crear y editar en tiempo de ejecución. A diferencia de los Informes nativos los Informes personalizables no se pueden incrustar directamente en los formularios como un control, en su lugar disponen de una ventana modal de previsualización desde la que se puede imprimir.

Una de las diferencias más importantes entre los dos tipos de Informes, además de la personalización, es la forma de gestionar el Origen de datos que dará una gran ventaja a los Informes personalizables en muchos escenarios.

En este artículo voy a intentar profundizar en algunos aspectos relacionados con los Orígenes de datos de los informes personalizables, con el objetivo de entender y ampliar nuestro conocimiento sobre este control y en consecuencia poder aplicarlo en nuestros desarrollos.

Hasta la versión Velneo 7.15 los Informes personalizables se gestionaban a través de la Open App Velneo vReport, la cual había que heredar para disponer del editor de Informes. Por esta razón se hablaba indistintamente de Informes externos o de Informes vReport. A partir de Velneo 7.16 el editor de Informes personalizables se integró tanto en vDevelop como en vClient. Ya no es necesario heredar el módulo Velneo vReport aunque se puede seguir usando y aprovechar el magnífico repositorio de definiciones XML. Es cierto que ha habido graves problemas con el repositorio relacionados con un bug aleatorio en el guardado de campos de tipo Objeto (VELNEO-4458). Este bug finalmente se ha identificado y resuelto en la versión 25.
Por lo tanto, a partir de ahora hablaremos por un lado de Informes personalizables como de un objeto independiente de Velneo y de la Open App Velneo vReport como un módulo que podemos integrar en nuestras aplicaciones para gestionar el repositorio de definiciones XML.

Si llevas poco tiempo con Velneo o todavía no has tenido necesidad de crear Informes, échale un vistazo a la siguiente documentación para tener claros los conceptos básicos sobre secciones, orígenes, detalles, zonas, expresiones y fórmulas de scripts, etc … que aparecerán en este artículo.

¿Quién está detrás del diseñador de Informes personalizables en Velneo?

El Editor de Informes personalizables es una excelente adaptación e integración del producto NCReport Qt/C++ Report Generator, al que se han añadido las funcionalidades de conexión a tablas del proyecto de datos de Velneo y el uso del editor de fórmulas también de Velneo.

Desde vDevelop accedemos al Editor de Informes personalizables a través del menú Objetos – Editar informe personalizable. Desde vClient, en tiempo de ejecución, también podemos crear y editar Informes personalizables, teniendo disponibles además todos los objetos públicos del proyecto de datos. Por ejemplo, las variables globales y las funciones de nuestro proyecto de datos se podrán usar desde los controles del Informe personalizable.

Hay alguna funcionalidad, como el generador de la Tabla de contenidos, que no está documentada, ni siquiera en la versión original de NCReport, y sería interesante su puesta en marcha.

La última versión de NCReport a fecha de hoy es la  2.23.2 de 02 de mayo de 2019. Desde el editor de Velneo obtenemos la siguiente información a través de las variables de sistema:

Por la tanto, en Velneo 25 estamos trabajando con la versión NCReport 2.20.1 de 3 de enero de 2017.

La versión 2.13 de NCReport introdujo una nueva forma de definir los Orígenes de datos que permitía la impresión de informes maestro-detalle. A eso se añadió la posibilidad de imprimir varios detalles recorriendo un maestro principal. Hagamos entonces un repaso con más detalle.

El Origen de datos y las relaciones maestro-detalle

Ya hemos dicho que el Informe personalizable funciona como una Vista de datos de Velneo y necesita obligatoriamente una Entrada de tipo Lista.
Por lo tanto siempre habrá por lo menos un Origen de datos asociado directamente a una tabla del proyecto de datos de Velneo.

Nuestras aplicaciones normalmente gestionan bases de datos que guardan relaciones maestro-detalle, como los clientes con sus facturas y albaranes y las líneas de detalle correspondientes. Los Informes nativos de Velneo no están preparados para mostrar directamente estas relaciones y lo solucionamos alimentando la Vista de datos con la tabla que está más abajo en el esquema maestro-detalle, es decir, la tabla con las líneas de facturas o albaranes. Desde la tabla de detalle se accede a todos los niveles superiores del esquema (facturas, albaranes y clientes) mediante los punteros a maestro.

En la figura siguiente mostramos un esquema típico de relaciones maestro-detalle. Si alimentamos la Vista de datos del Informe nativo con la tabla Lineas facturas del nivel inferior del esquema, podemos acceder al resto de datos de las tablas mediante los punteros a maestro.

Como ya habrás observado, hay un problema con este planteamiento, las posibles facturas sin líneas y los clientes sin factura no tienen registro en la tabla Lineas facturas y por lo tanto no se imprimen en el Informe.

El editor de Informes personalizables soluciona este problema implementando la relación maestro-detalle a la hora de definir los Orígenes de datos.

El Origen de datos principal es la tabla Maestra

El primer Origen de datos que definamos en el Informe personalizable será siempre la lista de registros maestros a partir de la cual, mediante procesos de Velneo, obtendremos las otras Listas con los detalles que conforman el resto del Informe.

El diseñador de Informes personalizables solo contempla esquemas de tablas maestro-detalle tal como estamos acostumbrados en la Base de datos de Velneo.

En la ventana de Configuración de Orígenes de datos añadimos las tablas de Velneo y los procesos para obtener los registros del Informe.
Los Orígenes de datos se identifican en los elementos del Informe con el ID.

El Origen de datos principal que alimenta el Informe puede comportarse de 2 maneras diferentes dependiendo del valor seleccionado en la opción «Abriendo/ejecutando rol«:

  • Origen de datos principal como Comienzo del informe. El motor del generador de Informes recorre la tabla maestra secuencialmente. Por cada registro de la tabla maestra tenemos la opción de alimentar un segundo Origen de datos de tipo Subconsulta mediante un proceso de Velneo. De la subconsulta resultante podemos alimentar un tercer Origen de datos de tipo Subconsulta y así sucesivamente.

En el esquema siguiente tenemos el ejemplo para diseñar un Informe personalizable con la lista de todos los Clientes con sus Facturas y las Líneas correspondientes. La Lista de Entrada del formulario es de la tabla Clientes que alimenta el Origen de datos principal de tipo Comienzo del informe. El Informe recorre secuencialmente los Clientes y por cada registro ejecuta un proceso Velneo con entrada de Ficha que devuelve la Lista de Facturas alimentando un segundo Origen de datos de tipo Subconsulta. Por cada registro de la Lista de Facturas se ejecuta un proceso Velneo con entrada de Ficha que devuelve la Lista de Líneas facturas alimentando un tercer Origen de datos de tipo Subconsulta.

  • Origen de datos principal como Iteración del subinforme. En este caso el Origen de datos principal es una tabla maestra de maestros. Por cada registro de la tabla maestra principal se ejecutan uno o varios procesos de Velneo que alimentan otros tantos Orígenes de datos de tipo Comienzo del informe. Cada uno de estos Orígenes de datos son subinformes independientes.

Con el mismo esquema anterior queremos diseñar un Informe que nos imprima la lista de todos los Clientes con sus Facturas y Líneas facturas junto con los Albaranes y Líneas albaranes. La Lista de Entrada del formulario es de la tabla Clientes que alimenta el Origen de datos principal de tipo Iteración del subinforme. El Informe recorre secuencialmente los Clientes y por cada registro ejecuta un proceso Velneo con entrada de Ficha que devuelve la Lista de Facturas alimentando un Origen de datos de tipo Comienzo del Informe. Al mismo tiempo se ejecuta otro proceso Velneo que devuelve la Lista de Albaranes alimentando otro Origen de tipo Comienzo del Informe.

Aunque los subinformes son de tipo Comienzo del informe, no podemos ejecutar subconsultas para obtener Orígenes de datos de tipo Subconsulta. Es una limitación del generador de informes. Desde la tabla principal Clientes podemos obtener los maestros de Facturas y Albaranes y si necesitamos las Líneas tendremos que ejecutar procesos que devuelven dichas Líneas directamente desde el Cliente y agrupar por Factura o Albarán.

Ahora ya podemos sacar algunas conclusiones y tener claro qué podemos lograr con el diseñador de Informes personalizables de Velneo:

  • Siempre partimos de una tabla maestra que se recorre secuencialmente.
  • Cuando el maestro principal es de tipo Comienzo del informe el resto de Origenes serán todos de tipo Subconsulta.
  • Por cada registro maestro solo se puede ejecutar una subconsulta para obtener el siguiente nivel de detalle.
    Por esta razón desde Facturas solo es posible obtener las Lineas facturas o los Vencimientos, pero no los dos en el mismo Informe.
  • Partiendo de la tabla maestra obtenemos todos los registros maestros aunque no tengan detalle.
    Si partimos de la tabla Clientes siempre obtenemos todos en el Informe, aunque no tengan Facturas ni Albaranes.
  • El modo Iteración del subinforme solo admite un nivel de profundidad, es decir, solo puede haber Orígenes de tipo Comienzo del informe.
  • El orden de los registros deberá determinarse desde el proceso que alimenta el Origen de datos.
    Desde la versión 21 existe la posibilidad de ordenar el Origen de datos por uno o varios campos, aunque solo de manera ascendente.
  • ¿Qué hacemos entonces si con nuestro esquema de tablas no podemos obtener todos los Origenes de datos mediante subconsultas o iteración de subinformes?
    Existen varias soluciones. Una muy utilizada y sencilla de implementar es rellenar una tabla temporal desde las tablas del esquema y alimentar con ella el Origen de datos principal. Otra solución consiste en añadir un campo fórmula a la tabla maestra que devuelva dos o más detalles listos para mostrar en el Informe, por ejemplo en formato HTML. En nuestro esquema añadimos un campo fórmula a la tabla Facturas para que tengamos en el informe una tabla HTML lista para mostrar las Lineas factura y los Vencimientos de forma conjunta.

Del Origen de datos a la sección Detalle

Con la definición de los Orígenes de datos le estamos indicando al generador de Informes cómo debe recorrer los maestros de la tabla principal y cómo obtener los detalles a través de las subconsultas. Para mostrar los registros en el Informe necesitamos una sección Detalle que vaya leyendo los registros y colocando los diferentes elementos según el tipo de dato.

No basta con añadir la sección Detalle al informe, hay que asignarle un Origen de datos de tipo Comienzo del informe. El generador de Informes recorre secuencialmente los maestros del Origen de datos principal. Si hay una Subconsulta en el nivel inferior, ésta se ejecutará por cada registro del maestro y así en cada nivel de subconsulta. Cuando se llega al nivel inferior del esquema se pinta finalmente el Detalle con los elementos que contiene.

Existe un bug que provoca el cierre brusco del generador de Informes y de vClient. Se produce cuando dejamos en blanco el combo Origen de datos en la ventana de Configuración de detalle. El problema es que si cambiamos el ID de un Origen de datos, el combo vuelve a quedar vacío y se reproduce el bug nuevamente.

En la imagen siguiente tenemos la sección de Detalle que nos imprimirá el generador de Informes. Hemos colocado 6 elementos de tipo texto con la siguiente información leyendo de izquierda a derecha:

  • Columna gris con la variable de sistema currentrow que enumera las líneas de Detalle resultantes de recorrer la tabla de maestros CLI y las Subconsultas del esquema maestro-detalle. Tenemos 17 líneas de Detalle.
  • Columna verde con el maestro principal desde el Origen de datos de tipo Comienzo del informe. Tenemos 5 registros en la tabla maestra Clientes. A la derecha se muestra el Nº de Facturas de cada Cliente usando la función rowCount() aplicada al Origen de datos FAC después de ejecutar la 1ª Subconsulta.
  • Columna naranja con el Origen de datos FAC resultado de ejecutar la Subconsulta a los registros de la tabla maestra CLI. A la derecha se muestra el Nº de Líneas facturas para cada Factura aplicando función rowCount() al Origen de datos FAC_LIN después de ejecutar la 2ª Subconsulta.
  • Columna azul con el Origen de datos FAN_LIN resultado de ejecutar la Subconsulta. Para las Facturas que no tienen Líneas condicionamos usando la función isEmpty() aplicada al Origen de datos FAC_LIN.

Hay un problema cuando tenemos más de un nivel de Subconsulta como en este ejemplo de Facturas y Líneas facturas y hay un Cliente sin Facturas. Un Cliente sin Facturas devolverá un Origen de datos FAC vacío en la 1ª Subconsulta, pero el Origen de Líneas facturas FAC_LIN no se reinicia porque no existe un registro de Facturas que ejecute la 2ª Subconsulta. El efecto es que en el Detalle aparece un Cliente sin Facturas pero con las Líneas facturas de la Factura anterior.

Por ejemplo, si asignamos la FACTURA 006 a otro cliente, el CLIENTE 04 tendrá la columna naranja vacía pero aparecerá con 5 Líneas en la columna azul que corresponden a las Líneas de la FACTURA 005 cuyo Origen de datos no se ha reiniciado. Además el valor de currentrow llegará hasta el valor erróneo de 22.
En este caso habrá que usar la función $D{facturas.isEmpty()} para ocultar las Líneas de Detalle extra que aparecen e ignorar el valor que devuelva currentrow.

Si nos fijamos en la estructura del Detalle, nos daremos cuenta que es igual a la tabla temporal que tendríamos que construir si no tuviéramos disponible el generador de Informes personalizables. Recordar que la gran ventaja del Informe personalizable es la posibilidad de generar Informes directamente de un esquema maestro-detalle, recorriendo la tabla maestra de nivel superior y permitiendo Subconsultas con resultado vacío.

Varios Detalles en los Informes de tipo Iteración del subinforme

Cuando el Origen de datos principal es de tipo Iteración del subinforme se pueden mostrar tantos Detalles independientes como Orígenes de tipo Comienzo del informe hayamos definido.

A diferencia del Informe nativo, donde por cada registro del Origen de entrada se imprime una línea de cada Detalle, en el Informe personalizable se muestran primero todas las líneas del primer Detalle y a continuación todas las líneas del segundo Detalle.

Modo de pasada doble

Ya sabemos que el Modo de pasada doble hay que activarlo cuando hacemos uso de la variable de sistema pagecount.

Con el Modo de pasada doble activado, el generador de Informes carga el Origen de datos principal una única vez, pero recorre dos veces el esquema maestro-detalle ejecutando las Subconsultas, la primera vez para obtener el valor de pagecount y la segunda vez para mostrar o imprimir el Informe.

Organizando los registros de los Orígenes de datos

Una vez que hemos definido los Origenes de datos y sabemos el número total de filas de Detalle obtenidas al recorrer el esquema maestro-detalle (valor final de currentrow), solo nos queda indicar al editor cómo queremos agrupar esas filas de Detalle. Los Agrupamientos y sus secciones de Cabecera y Pie facilitan la impresión de datos de los registros maestros, así como totalizar columnas del Detalle en las Variables definidas en el Informe.

Recordar siempre que el número de filas que componen la sección de Detalle es fija y determinada por el generador de Informes cuando abre el maestro principal y ejecuta las Subconsultas en los n-niveles del esquema maestro-detalle. Con los Agrupamientos, cabeceras, pies y fórmulas lo que hacemos es organizar las filas de Detalle para que aparezcan agrupadas, ocultar algunas filas de Detalle mediante condiciones de impresión, mostrar totales, suma y sigue, etc …

Desde las diferentes secciones del Informe podemos obtener información relativa a los Orígenes de datos mediante funciones y variables de sistema:

  • rowCount() : devuelve el número de registros del Origen de datos.
  • isAvailable() : devuelve 1 si el origen de datos tiene al menos un registro.
  • isValid() : el puntero al registro apunta a un registro válido.
  • isEmpty() : no tiene datos de registro.
  • isNotEmpty() : tiene datos de registro.
  • update() : fuerza una actualización (requery).

Estas funciones se pueden usar dentro de los campos del Informe tanto si es tipo Origen de datos CLI.rowCount(), o Script $D{CLI.rowCount()}.

Para referirnos a un Origen de datos se usará el ID que hemos introducido en la ventana de Configuración de Orígenes de datos.

En todas las secciones del Informe existe un combo con los ID’s de los Orígenes de datos. En las expresiones y fórmulas seleccionaremos un ID de la lista para indicar de qué origen queremos obtener el dato.

La variable de sistema currentrow almacena el contador de filas de la sección de Detalle, el primer registro del Informe empieza con currentrow = 0.

En los informes de Iteración del subinforme con varios detalles la variable de sistema currentrow toma valores erróneos. En este artículo la usamos solo a efectos ilustrativos, pero no sirve para llevar el conteo de filas del Detalle.

Informe con un Maestro y varias Subconsultas

Veamos el diseño básico de un Informe personalizable con el esquema de Comienzo del informe y dos Subconsultas.
Este tipo de informes tiene normalmente una sola Sección de Detalle y varios niveles de Agrupamiento.
Desde la sección de Detalle accedemos a los registros de los tres Orígenes de datos: CLI, FAC y FAC_LIN.

Informe con un Maestro y varios Detalles

Cuando el esquema contiene una tabla de maestros con dos detalles es necesario usar un informe de tipo Iteración del subinforme.

Este tipo de informe solo permite un nivel de profundidad en la relación maestro- detalle, es decir, solo puede haber dos tipos de Origen de datos, el principal para iterar y por cada registro un subinforme con Origen de datos de tipo Comienzo del informe.

Como ejemplo veamos el informe siguiente en el que queremos mostrar la lista de todos los Clientes con los detalles de sus Facturas y Albaranes. Tendremos que iterar en la tabla de Clientes y por cada Cliente ejecutar dos procesos que devuelvan las Líneas factura y las Líneas albarán.

Recordar que usar las tablas más bajas en el esquema tiene el inconveniente de que no se imprimen los maestros que no tengan detalle. Aunque en este ejemplo las facturas y albaranes siempre tendrán al menos una línea, en otros escenarios habrá que tenerlo en cuenta.

Original y Copia

En el generador de Informes personalizables no podemos indicar el Número de copias que queremos imprimir. Solo se puede indicar en la ventana de diálogo de la impresora. Podemos recurrir otra vez a los Orígenes de datos para resolver este problema.

Imaginemos un listado de facturas en el que por cada factura deben imprimirse dos copias. Lo que hacemos es recorrer la lista de facturas como Iteración del subinforme y por cada registro definimos dos Orígenes de datos de tipo Comienzo de informe que ejecutan el mismo proceso con entrada y salida la misma factura. De esta forma por cada Subinforme tenemos dos secciones de Detalle en la que imprimimos la factura por duplicado una detrás de otra.

La ventaja de este método es que podemos personalizar la segunda Copia modificando el diseño del Detalle e incluso cancelar la impresión de la segunda copia mediante un Parámetro en la Condición de impresión.

Informe con 2 Orígenes de datos en el mismo Detalle

Estamos viendo que en los informes de tipo Iteración del subinforme las distintas secciones Detalle se imprimen una detrás de otra según el orden que hayamos establecido en la ventana de Configuración de detalle.

Sin embargo, el diseñador de Informes personalizables nos permite juntar hasta 2 Orígenes de datos en un solo Detalle e imprimir los registros de ambos en la misma sección. Para ello indicamos en la pestaña Especial de la ventana Configuración de detalle el Origen de datos secundario que queremos mostrar en la misma sección Detalle. Recordar que debemos usar la constante @DS2 en la fórmula de Condición de impresión para los elementos del Origen secundario.

Para que este tipo de Informe funcione se debe cumplir que el número de filas del Detalle secundario debe ser igual o inferior que el del Detalle primario.

Veamos un ejemplo en el que queremos imprimir las 10 últimas facturas de cada Cliente. La lista de facturas debe mostrarse en dos columnas de 5 filas cada una, es decir, la sección Detalle imprime siempre los primeros 5 registros en la columna de la izquierda y el resto hasta 10, en la columna de la derecha. De esta forma la columna de la izquierda tiene siempre más filas o las mismas que la columna de la derecha y el informe funciona correctamente.

Preparamos 2 procesos de Velneo, uno que devuelva las 5 primeras facturas de cada Cliente y otro las 5 siguientes. Estos procesos alimentan los Orígenes de datos de tipo Comienzo del informe CLI_FAC_LIS1 y CLI_FAC_LIS2 en cada iteración del Cliente en el Origen de datos principal CLI.

El Origen de datos primario se adapta a los registros del Origen secundario

En el ejemplo anterior tenemos controlado el número de filas de los Orígenes de datos primario y secundario de la sección Detalle. Sin embargo, en la mayoría de escenarios no sabemos de antemano el número de Filas. El Tutor de vReport muestra un ejemplo con la opción de añadir a la tabla maestra de Clientes un campo fórmula que devuelve un texto HTML con la lista de Albaranes y Facturas en 2 columnas. Esta opción tiene el problema de que imprimir imágenes desde los registros se vuelve lento porque habría que convertirlas a Base64 y el tamaño de los datos devueltos por la fórmula crecerá bastante.

Veamos el mismo ejemplo del Tutor, pero esta vez usando Orígenes de datos y controlando que el Origen primario tenga los mismos o más registros que el Origen secundario.

El Origen primario es la lista de Albaranes del Cliente y el Origen secundario la lista de Facturas. Usaremos una tabla temporal para determinar el número de Albaranes. La tabla temporal contendrá siempre como mínimo el mismo número de registros que el Origen de datos de las Facturas.

Este es el proceso de Velneo que devuelve la lista de Albaranes con el mismo número de registros que la lista de Facturas.

Rem ( Obtenemos una tabla temporal con los mismos **Albaranes** que **Facturas** )
Libre
Rem ( Vaciamos la **tabla temporal** )
Cargar lista ( **ALB\_TEMP**@0PS_ARTIPACO_dat, ID, , , , )
   Recorrer lista eliminando fichas
Libre
Libre
Rem ( Nº de Facturas para determinar el tamaño mínimo de la tabla temporal )
Cargar plurales ( **FACTURAS\_CLIENTES** )
  Set ( NNUM_FAC, sysListSize )
Libre
Rem ( Añadimos los Albaranes a la tabla temporal )
Cargar plurales ( **ALBARANES\_CLIENTES** )
  Tubo de lista ( **TUB\_ALB\_TEMP**@0PS_ARTIPACO_dat )
    Libre
Rem ( Rellenamos la tabla temporal hasta alcanzar el mismo número que Facturas )
Cargar lista ( **ALB\_TEMP**@0PS_ARTIPACO_dat, ID, , , , )
  Set ( NNUM_ALB, sysListSize )
  If ( NNUM_ALB < NNUM_FAC )
    For ( NCON, 0, NCON < (NNUM_FAC - NNUM_ALB), 1 )
      Crear nueva ficha en memoria ( hFic, **ALB\_TEMP**@0PS_ARTIPACO_dat )
        Libre
      Alta de ficha ( hFic )
        Libre
Libre
Cargar lista ( **ALB\_TEMP**@0PS_ARTIPACO_dat,ID, , , , )
  Añadir lista a la salida

La tabla en memoria ALB_TEMP alimenta el Origen de datos principal ALB de la sección Detalle. El Origen de datos secundario es FAC que siempre tendrá el mismo número de filas que el Origen primario ALB.

Si no hay más remedio, usaremos una tabla en Memoria

Ya hemos indicado que el motor del generador de Informes personalizables está limitado a los esquemas de maestro principal con subconsultas o a la iteración del subinforme de un solo nivel. Cuando tenemos multi detalle en más de un nivel, tendremos que recurrir a campos fórmula que generan HTML personalizado o rellenar una tabla temporal en memoria para crear la lista de filas de la sección Detalle. Veamos un ejemplo usando una tabla temporal.

En el siguiente esquema tenemos los dos niveles de maestro-detalle.

Este es el Informe que queremos obtener.

Escribimos un proceso que vaya recorriendo el esquema desde el maestro principal hasta el segundo nivel de detalle.
El resultado es la siguiente tabla temporal que alimentará el Origen de datos principal del Informe:

Tenemos 24 filas de Detalle que apuntan a registros de las 5 tablas del esquema maestro-detalle. Para definir las Agrupaciones del Informe usamos el valor de las columnas PZA, CHK, PRO, MAT y FOT.

Como solo tenemos una sección Detalle en el Informe, tenemos que condicionar la impresión de los elementos y distribuirlos en Zonas.

El diseño del Informe personalizable queda como se muestra a continuación.

En un próximo artículo veremos a fondo éste y otros tipos de Informe.

Conclusiones

En este artículo hemos explorado las distintas opciones que tenemos para preparar los Orígenes de datos según el esquema maestro-detalle de nuestras tablas. Hemos visto las limitaciones y cómo acudir a la tabla temporal si no hay más remedio.

En cualquier caso, tanto los Informes por Subconsultas como los Informes por Iteración del subinforme nos resolverán la mayoría de las situaciones.

En un próximo artículo profundizaremos en las múltiples posibilidades de integración de los Orígenes de datos en las secciones del Informe, así como en los diferentes formatos de salida.

Algo de Código

Los Informes personalizables se denominan así porque guardan su definición en un archivo XML que se puede editar en tiempo de ejecución, proporcionando al usuario la posibilidad de personalizar sus propios Informes.

Hagamos pues una práctica que consista en abrir el archivo XML y analizar su estructura sin tener que abrir el editor de informes. Nos centraremos de momento en leer la configuración de los Orígenes de datos que es de lo que va este artículo.

Para leer la estructura de un XML necesitamos un Parser que pueda leer este tipo de documentos.

En Velneo disponemos de un parseador simple de documentos XML, pero lamentablemente no nos sirve con las definiciones de los Informes porque necesitamos acceder a los Atributos de un Nodo y eso no es posible.

Ya que Velneo no nos da solución a un tema tan simple y a la vez tan necesario, habrá que recurrir al mercado de componentes para Velneo. Afortunadamente nuestro amigo Cristian ha desarrollado un plugin muy completo que transforma el XML en un JSON, lo que convierte la gestión del XML en un juego de niños.

Yo para este ejercicio me he decidido por algo intermedio, he usado el DOM XML que tenemos en el Visor HTML de Velneo. No es la solución perfecta ya que solo funciona en 1P, pero como experiencia de aprendizaje no está mal y además todo nativo puro.

Lo que he hecho es lo siguiente:

  • Creo un formulario FRM_INF_XML_PARSER que se ejecute siempre en Cuadro de diálogo (modal) para que pueda devolver un resultado.
  • Incrusto en el formulario anterior un Visor HTML al que le inyecto en el POS_INI código CHTML personalizado y activo la delegación de clicks.
// Obtenemos el control Visor HTML
var oVisorHTML = theRoot.dataView().control("VIS_HTM")
// Fijamos el código HTML del control
oVisorHTML.setSourceCode(theRoot.varToString("CHTML"),theRoot.varToString("CPATH_BASE"))
oVisorHTML.setLinkDelegationPolicy(VCWebView.DelegateAllLinks)
  • El formulario recibe en la variable local CXML el contenido XML que vamos a parsear. En el PRE_INI, el contenido de CXML recibido como parámetro se incrusta dentro del código CHTML entre los marcadores <!– VELNEO_INI –> y <!– VELNEO_FIN –>.
<!DOCTYPE html>
<html>
<!-- Parsear un texto **XML** usando el objeto **DOM** del Visor HTML -->
<meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
<head>
<script>
// **Evento** para ejecutar al cargar la página window.addEventListener(**'load'**, xmlParser, false)
  function **xmlParser()** {
    var oTextoArea_XML, text, parser, xmlDoc
    // Documento XML cargado desde Velneo
    oTextoArea_XML = document.getElementById('xmlArea')
    text = oTextoArea_XML.value.trim().replace(/\\n/g, '')
    // Instanciamos el parseador XML del DOM
    parser = new **DOMParser()**;
    xmlDoc = parser.parseFromString(text,'text/xml')
    // Construimos un JSON con la configuración de los Orígenes de datos
    var aLisDS = xmlDoc.getElementsByTagName(**'datasource'**)
    var oResultado = []
    for (var n = 0; n < aLisDS.length; n++) {
      var oItem = {}
      oItem[**'Id'**] = aLisDS[n].attributes['id'].value
      if (aLisDS[n].attributes['parentID']) {
        oItem[**'parentID'**] = aLisDS[n].attributes['parentID'].value
      }
      oItem[**'openRole'**] = aLisDS[n].attributes['openRole'].value
      oItem[**'modelID'**] = aLisDS[n].attributes['modelID'].value
      oResultado.push(oItem)
    }
    var cResul = JSON.stringify(oResultado)
    document.getElementById('resultado').innerHTML = cResul
    // Enviamos el resultado a Velneo
    simularClick(cResul)
  }
  function simularClick(cDatos) {
    // Envía un evento Click a Velneo
    var a = document.createElement('a')
    a.href = 'file:///' + cDatos
    a.click()
    document.body.removeChild(a)
  }
</script>
</head>
<body>
  <p id='resultado'></p>
  <p><button onclick='xmlParser()'>Ver</button></p>
  <textarea id='xmlArea' cols='55' rows='15'>
  **<!-- VELNEO\_INI --><!-- VELNEO\_FIN -->**
  </textarea>
</body>
</html>
  • El evento Link clicked del Visor HTML recoge el string JSON y lo guarda en la variable local CRESUL con la información de los Orígenes de datos del Informe personalizable. A continuación cierra el formulario.
  • Ya solo tenemos que crear un manejador de objeto de FRM_INF_XML_PARSER, pasarle el XML a la variable CXML y disparar. Nos devolverá el resultado en CRESUL con el JSON que ya podemos procesar y guardar en una tabla.

Este es el resultado de leer la carpeta con los archivos XML de los Informes usados en este artículo.

Como ejercicio puedes leer la tabla de definiciones de Informes de vERP y obtener un listado semejante.

Espero que con este tutorial no te quede ninguna duda sobre informes personalizables en Velneo*.*