Requisitos previos

Al inicio del proyecto es esencial asegurarse del cumplimiento de una serie de requisitos previos de hardware & software de terceras partes, relativos a las aplicaciones a instalar. Estos requisitos dependerán del tipo de instalación que se pretende hacer, que puede ser de 2 tipos: instalación en la nube y instalación on premise:

Requisitos generales

  1. Navegador Google Chrome (v69 aprox.)
  2. MICROSOFT EXCEL (versión 2010 en adelante): Es imprescindible tener instalado y activado en el PC para las exportaciones de informes, así como las importaciones de datos maestros (cargas desde excel).
  3. Sistema operativo de la estación de trabajo o terminal: Windows 8 o Windows 10.

Tipo de instalación

INSTALACIÓN GT EN LA NUBE (Cloud)

  1. No requiere instalación previa.

INSTALACIÓN GT ON PREMISE

  1. La instalación en el servidor seleccionado por el cliente se realizará por parte de técnicos cualificados (ver Instalación en Servidor).
  2. Requisitos básicos: Windows Server 2016 R2 o superior con 8GB de RAM.
  3. Mínimo 20GB libres de disco.
  4. Para recibir actualizaciones será necesario un acceso a Internet
  5. Configuración de la App . Consulta los detalles en Instalación App

SISTEMA DE FICHAJES

En general, se recomienda que el sistema de fichadas esté en funcionamiento con anterioridad a la implantación de GT y se tiene que saber en qué directorio guardará el fichero para que se programe la tarea de lectura de dicho fichero.

Hay 2 Opciones:

  • Opción 1: lectura de fichajes desde una página web de GT o bien desde la App para móviles. Es parte del producto, y sólo hay que asegurarse de que se han creado los usuarios adecuados.
  • Opción 2: fichajes mediante relojes de lectura instalados por el cliente (de tercera partes). En este caso se deben cumplir los siguiente requisitos previos adicionales:
    • Relojes instalados. los relojes ya deben estar ya instalados en la red del cliente y funcionando.
    • Empleados y huellas ya configurados: los empleados deben haber sido ya registrados en el software de control de dichos relojes (ya sea por huella o tarjeta) y haber sido asociados a cada marcaje registrado mediante algún código.
    • MUY IMPORTANTE – exportación de marcajes: el software de control debe ser capaz de volcar los datos de fichajes en un fichero de texto de forma periódica (5 minutos, 10, 30, 60, ó 1 vez al día) sobre una carpeta de la red que será accesible desde GT .Por ejemplo: \\Servidor_A\marcajes o bien una carpeta local del servidor. Nota: se recomienda enviar una muestra de este fichero, indicando también el fabricante de los relojes, antes de la configuración de la aplicación GT, para verificar su compatibilidad, aunque en general se puede configurar una amplia variedad de formatos diferentes.
    • Listado de nombre y códigos asociados: un volcado en texto/excel de todos los empleados registrados incluyendo: nombre completo, código asociado en el reloj, y (si tiene nómina integrado) si es posible se usará el mismo código asignado por la nómina al empleado como código de fichaje.

Si la instalación es cloud: en el caso que GT vaya a usarse directamente en la nube se precisará también la instalación de un programa en la red del cliente o clienta para poder transferir los datos contenidos en ese fichero a la nube. Será necesario proporcionar un acceso remoto a un servidor de la red para efectuar esta configuración. En el apartado Envío de marcajes en instalaciones cloud se detalla toda la información.

Atención! Importante: el software responsable de gestionar las huellas de las empleadas y empleados y registrar todos los relojes conectados a la red es el propio software de control suministrado con los mismos, quedando esta configuración fuera del alcance de GT.

INTERFACE CON NÓMINA

El proveedor de Nómina (A3) debe haber instalado el programa de sincronización (interface) entre NÓMINA y GT. En tal caso se tiene que tener preparado una serie de datos para poder configurar correctamente el GT al iniciarlo por primera vez (por ej. Codigo Cliente, Usuario & Clave que da acceso al sistema de nominas para la extracción de datos, etc.). Estos datos seran necesarios para configurar el Asistente de GT.

Si se integran datos de nómina, antes de empezar la implantación, ya tendríamos algunos datos en BOLD (Absentismos, contratos, Empresa, etc…) Que podremos aprovechar para la implantación de GT.

Formato XML del objeto informe de tabla dinámica

Este informe tiene el código EnumAdvancedReport (también denominado a veces informe avanzado).

Campos principales

Además de los campos habituales en la importación de objetos del sistema (ver Campos XML std., los campos mas destacados son los siguientes:

ExtCode: este campo es imprescindible rellenarlo con un nombre que sea un identificador valido (comienza por una letra, sin espacios ni caracteres especiales). Se utiliza para etiquetar los nodos del documento XML de salida.

DataInputType: origen de los datos de entrada con los que se alimentará el informe. Los tipos posibles son los siguientes:

0 - Otro informe estándard del sistema
1 - Una consulta SQL
2 - Iterator_Task_ForPlanTasks Períodos planificados individuales (cada una de las tareas del plan). Objetos de tipo TaskContextObject con todas sus propiedades asociadas.
3 - IterBased_ByAccumulatedByNeedIncidence Períodos planificados agrupados por días (períodos del plan agrupados por jornadas y tipo de actividad realiza en ellas. También son objetos de tipo TaskContextObject.
4 - Sobre el iterador Iterator_ByDay_ForPlanTasks
5 - Sobre el iterador Iterator_TasksAndEmptyDays_ForPlanTasks
6 - Sobre el iterador Iterator_ByDayAndEmptyDays_ForPlanTasks

ReportClass: (reservado) cuando el origen de datos sea un informe nativo del sistema, indicará el tipo de informe

EnableAggregationStep: indica si esta definición de informe contempla la fase de agregación (lo normal) o bien únicamente incluye el procesado de los atributos calculados que se hayan podido añadir a los datos de entrada y eso será directamente la salida, sin agregación (como un informe de los nativos, pero configurado desde un objeto de informe en lugar de a través del querycustom.xml). Utilizando Input_Axis.

Parent: reservado

X_Axis: eje X u horizontal de agregación. Los resultados del informe se mostrarán en formato tabla agrupando los resultados según dos ejes X e Y en los que se definen campos clave de agregación de tal forma que a todos los registros que cumplan las condiciones de la clave se les aplicará la misma función de agregación.

Y_Axis: eje Y o vertical.

Cells: eje de agregación o cálculo

Los tres nodos o campos anteriores definen los siguientes subcampos:

  • Name: nombre interno del campo
  • ShowName: nombre a mostrar para el campo (título de la columna o fila)
  • Type: tipo de datos del campo. Los valores posibles se pueden consultar en la tabla de Tipos de datos.
  • Expression: expresión que determina el valor que le corresponde al objeto fila actual
  • ShowFormat: expresión que configura cómo se mostrará en valor interno en pantalla.
  • ShowType: tipo del valor a mostrar en pantalla
  • ReverseShowFormat: función opcional para recuperar el valor interno a partir del valor mostrado en pantalla (inversa de Expression). Debe existir una correspondencia 1-1 entre ambos valores. Esta función es necesaria para recuperar de nuevo las filas de detalle a partir de los datos mostrados en el resultado del informe.

Además, los nodos de tipo Cells añaden los siguiente atributos:

  • EnumAggrFunc: función de agregación a aplicar sobre cada valor. Es un valor enumerado. Ver tabla anterior.
    • 1 Suma
    • 2 Máximo
    • 3 Mínimo
    • 4 Cuenta
  • Input_Axis: permite definir columnas adicionales sobre los datos de entrada que podrán reutilizarse como variables e las expresiones de agregación.
  • Output_Axis: permite definir columnas adicionales que formarán parte de los datos agregados y que pueden utilizar estos datos agregados como variables para su cálculo. El contexto sería de evaluación de estas expresiones sería la clave de agregación y las dimensiones de agregación que se hayan definido.

Ejemplo XML de Informe de tabla dinámica

Un ejemplo de documento XML de un objeto report completo y preparado para su importación sería el siguiente:

 <?xml version="1.0" encoding="ISO-8859-1" ?>
 <IntContainerVector Count="1" >
  <UpdateFields UpdateAll="1"/>
  <BIDatacubeModel>
    <Name value="Ejemplo1 de informe" />
    <ExtCode value="Ejemplo1_interno" />
    <DataInputType value="2" />
    <ReportClass value="0" />
    <EnableAggregationStep value="1" />
    <Parent value="0" valueRef="" />
    <Definition>
      <BeforeExpression>
      <Expression>
    <![CDATA[ 
      LogMessage("Report starts")
    ]]>
      </Expression>
      </BeforeExpression>
      <AfterExpression>
      <Expression>
    <![CDATA[ 
      LogMessage("Report ends")
    ]]>
      </Expression>
      </AfterExpression>
    <X_Axis >
      <Axis Count="2" >
        <Rows>
          <BIDimension>
            <Name value="Year" />
            <ShowName value="" />
            <Description value="" />
            <Visible value="True" />
            <Type value="3" />
            <Default value="0" />
            <EnumFamily value="" />
            <RefClass value="0" />
            <Expression>
              <![CDATA[Year(TimePeriod.StartTime)]]>
            </Expression>
          </BIDimension>
          <BIDimension>
            <Name value="Month" />
            <ShowName value="" />
            <Description value="" />
            <Visible value="True" />
            <Type value="3" />
            <Default value="0" />
            <EnumFamily value="" />
            <RefClass value="0" />
            <ShowFormat value="MonthNumber2Name(@value)" />
            <ShowType value="19" />              
            <ReverseShowFormat value="MonthName2Number(@value)" />
            <Expression>
              <![CDATA[Month(TimePeriod.StartTime)]]>
            </Expression>
          </BIDimension>
      </Axis>
    </X_Axis>
    <Y_Axis >
      <Axis Count="1" >
        <Rows>
          <BIDimension>
            <Name value="Incidence" />
            <ShowName value="Incidencia" />
            <Description value="" />
            <Visible value="True" />
            <Type value="9" />
            <Default value="0" />
            <EnumFamily value="" />
            <RefClass value="7" />
            <Expression>
              <![CDATA[TimePeriod.Incidence]]>
            </Expression>
          </BIDimension>
        </Rows>
      </Axis>
    </Y_Axis>
    <Cells >
      <DataCells Count="2" >
        <Rows>
          <BIDataCellDimension>
            <Name value="Count" />
            <ShowName value="" />
            <Description value="" />
            <Visible value="True" />
            <Type value="3" />
            <Default value="0" />
            <EnumFamily value="" />
            <RefClass value="0" />
            <EnumAggrFunc value="4" />
            <Expression>
              <![CDATA[TimePeriod.Incidence!=0]]>
            </Expression>
          </BIDataCellDimension>
          <BIDataCellDimension>
            <Name value="CountMondays" />
            <ShowName value="" />
            <Description value="" />
            <Visible value="True" />
            <Type value="3" />
            <Default value="0" />
            <EnumFamily value="" />
            <RefClass value="0" />
            <EnumAggrFunc value="1" />
            <Expression>
              <![CDATA[CIsOnMonday]]>
            </Expression>
          </BIDataCellDimension>
        </Rows>
      </DataCells>
    </Cells>
    <Input_Axis >
      <Axis Count="2" >
        <Rows>
          <BIDimension>
            <Name value="IsOnMonday" />
            <ShowName value="" />
            <Description value="" />
            <Visible value="True" />
            <Type value="0" />
            <Default value="false" />
            <EnumFamily value="" />
            <RefClass value="0" />
            <Expression>  
              <![CDATA[DayOfTheWeek(TimePeriod.StartTime)==1 && TimePeriod.Incidence==0]]>
            </Expression>
          </BIDimension>
          <BIDimension>
            <Name value="CIsOnMonday" />
            <ShowName value="" />
            <Description value="" />
            <Visible value="True" />
            <Type value="3" />
            <Default value="0" />
            <EnumFamily value="" />
            <RefClass value="0" />
            <Expression>
              <![CDATA[IfElse(IsOnMonday, 1, 0)]]>
            </Expression>
          </BIDimension>
        </Rows>
      </Axis>
    </Input_Axis>
    <Output_Axis >
      <Axis Count="1" >
        <Rows>
          <BIDimension>
            <Name value="MeanMonth" />
            <ShowName value="" />
            <Description value="" />
            <Visible value="True" />
            <Type value="3" />
            <Default value="0" />
            <EnumFamily value="" />
            <RefClass value="0" />
            <Expression>
              <![CDATA[Count*10]]>
            </Expression>
          </BIDimension>
        </Rows>
      </Axis>
    </Output_Axis>
    </Definition>
  </BIDatacubeModel>    
 </IntContainerVector>

Script de sincronización con BOLDXML2

Un script de sincronización también se denomina “sincro” y lo que hace es transportar datos entre un sistema y otro, efectuando alguna transformación por en medio. Por ejemplo puede utilizarse para cargar los datos de empleadas o empleados, contratos, ausencias, necesidades, horarios, etc…

Existen scripts “estándar” que implementan tareas de sincronización entre algunos sistemas conocidos como a3EQUIPO o cierto formato de relojes. Si el sistema es diferente, habrá que implementar una serie de scripts para personalizar la integración entre BOLD y los otros sistemas.

Prerrequisitos

Es necesario tener preinstalado y configurado el módulo BOLDXML2.

Los scripts se implementan en javascript y se utiliza nodejs para su ejecución.

Formato del script

El contenido típico de un script sería similar a éste (en este caso implementa la carga de un fichero maestro de empleados proveniente de un sistema externo y lo introduce en BOLD):

var assert = require("assert");
var fs = require("fs");
var path = require("path");
var util = require("util");
var logger = require("../../../../../../BOLDXML2/scripts/core/util/logger.js");
var gpsdates = require("../../../../../../BOLDXML2/scripts/core/util/gps_dates_commons.js");
var BOLDXML_tools = require("../../../../../../BOLDXML2/scripts/BOLDXML_tools.js");
var SyncWait = require("../../../../../../BOLDXML2/scripts/core/util/SyncWait.js");

var API_Custom = require("./API_Custom.js");
var api = new API_Custom();


//--------------------------------------------------------------------------------
// logger.setLevel(logger.TRACE); // uncomment to TRACE HTTP messages
logger.setLevel(logger.DEBUG); // uncomment to TRACE HTTP messages
//--------------------------------------------------------------------------------


exports.sincro = function(taskController) {
  // Returns a "sincroObj"
  return {
    name: "Empleados",
    description: "",
    
    beforeRunTask : function(sincroObj) {
      sincroObj.description = sincroObj.BOLDXML.param(0);
      return true;
    },

    initFunc : function(sincroObj) {
      if (sincroObj.BOLDXML.paramCount() < 1) {
        throw new Error("Debe indicar el tipo de cambio que quiere procesar: CATEGORIA, CONVENIO, POSICION, UNIDAD_ORGANIZATIVA, REDUCCIONES_DE_JORNADA, CENTRO_DE_COSTE o TODOS");
      }
    },

    extract: {
      xmlFile: "Empleados.json",
      extractFunction: function(sincroObj) {    
        var idPersona, FechaInicio, FechaFin;
        var FechaInicio = "2017-01-01T00:00:00";
        var FechaFin = "2019-01-01T00:00:00";
        var sessionID = api.login();
        var data = api.getEmpleados(FechaInicio, FechaFin);
        api.logout();
        return data;
      }
    },
    transform: {
      queryFunction: function(sincroObj, json_input) {
        var list = BOLDXML_tools.toArray(json_input); // cuando solo retorna 1 elemento, lo normaliza en un array
        logger.debug(JSON.stringify(list.slice(0,2), null, 2));
        return { 
          list: list,
          ConvFecha: ConvFecha
        };
      },
      transformFile: "Empleados.xml"
    },
    process: {
      importXML: "Worker"
    },
  };
};


/**
 * @param f example: 4000-01-01T00:00:00.000Z
 */
function ConvFecha(f) {  
  if (f.indexOf("4000") !== -1)
    return gpsdates.maxDate2ISO;
  else
    return f.substring(0, 10); // trim time zone
}

Secciones del script

extract: sección que configura la extracción del sistema nativo (por ejemplo)

transform: sección que configura la transformación de las datos de entrada en los de salida. En las primeras versiones se utilizaba el lenguaje XSLT, pero luego ha sido sustituido por un scripting de plantillas mucho más flexible y simple. Ver el artículo Documento de definición de una transformación XML.

process: sección para configurar el destino de los datos transformados. Normalmente se indica la clase de objetos resultante y se activa su importación en el servidor de bold. En el caso anterior el resultado son objetos de tipo Worker (empleados/as)

initFunc (función opcional): se ejecuta para efectuar inicializaciones globales útiles para el resto de etapas o también para comprobar los parámetros adicionales, como en este ejemplo.

beforeRunTask (función opcional): si se especifica esta función, se ejecuta antes de la ejecución del resto de etapas del script. Si la función está implementada, un valor de retorno false indicará que se debe cancelar la ejecución de la tarea y esta ejecución no dejará ninguna traza en la pantalla de resumen de las interfaces. En el caso anterior se utiliza para personalizar la etiqueta de la descripción de la tarea, resulta útil cuando el script se le pasan parámetros que cambian el funcionamiento de la tareas y de esta forma se puede distinguir en la pantalla de resumen cada una de las diferentes modalidades del mismo script.

Otros detalles del script

SyncWait: como en nodejs casi todas las llamadas son asíncronas, esta clase nos permite convertir un código asíncrono en síncrono lo cual facilita mucho la implementación de las interfaces puesto que son siempre muy secuenciales.

API_Custom: en esta clase implementaremos los accesos al sistema externo (mediante webservices, u otros mecanismos…)

sincroObj: este objeto que reciben como primer parámetro muchas funciones proporciona acceso a una serie de objetos de contexto y métodos muy útiles. Son los siguientes:

Depuración

  • La forma más rápida de depurar los scripts consiste casi siempre en añadir una traza dentro del código.
  • Si el script ni siquiera arranca por errores sintácticos lo mejor es pasarlo por un validador de javascript como éste: http://esprima.org/demo/validate.html
  • Si lo que está mal sintácticamente es un archivo JSON de configuración o de entrada de datos usar este validador JSON como éste: https://jsonlint.com/

Sin embargo, también es posible preparar el entorno para debugar scripts de nodejs…el último usado fue node-inspector (hay un readme en la carpeta de BOLDXML2/docs)…TBD

Configuración del entorno de trabajo del script

La primera tarea que debe realizar un script es leer las variables de entorno de trabajo. Para ello ejecuta el archivo BOLDXMLConfig.bat preparado para cada instalación (utilizar la herramienta de configuración boldxmlcfg.exe).

Variables de entorno de trabajo para scripts de servidor

Una forma de inicializar las variables de entorno std. para que estén disponibles desde un script de servidor (por ejemplo para pasarlas a una tarea enlazada con algún evento de servidor) es implementar dicho script como en el siguiente ejemplo:

set START_DIR=%CD%
cd %~dp0
set SCRIPT_DIR=%CD%
cd ..\sincro
echo Ejecutando import IT....
call "90 Sincro_IncidenciasYAbsentismos.bat" 
echo Comprobando resultados...analizando el fichero %BOLDXML_LOG%...
cd %START_DIR%
echo "%BOLD_CSCRIPT%"
echo "%SCRIPT_DIR%\Sincro_CheckIT.wsf"
call "%BOLD_CSCRIPT%" "%SCRIPT_DIR%\Sincro_CheckIT.wsf"
echo Final...

El ejemplo anterior implementa un script de servidor (por ejemplo: que se ejecuta escogiendo una opción en menú) que lo que hace es lanzar primero una tarea de sincro global 90 Sincro_IncidenciasYAbsentismos.bat (que inicializará las variables de entorno) y posteriormente ejecuta el script Sincro_CheckIT.wsf. Desde dentro del código de Sincro_CheckIT se podrá acceder a las variables de entorno como la ubicación de la carpeta de ficheros BOLDXML de trabajo (variable de entorno DATA_BOLDXML).

Funciones de gestión de tareas del log

Gestionan la apertura y cierre de las tareas que aparecen en la página web del log de seguimiento del sistema BOLDXML.

Importante! las etiquetas de tareas y subtareas no pueden contener caracteres especiales, aunque sí espacios.

TASK_New

TASK_NewSubtask(task_name, “Aplicando transformacion 1”)

TASK_SaveSubtask

TASK_SaveResults

TASK_Save

Funciones de log

Log_Info Envía mensajes tipo Info a la salida standard (que también son capturados por el log de seguimiento (ver BOLDXML)

Log_Debug Envía mensajes tipo Debug a la salida standard (que también son capturados por el log de seguimiento (ver BOLDXML)

Importante! Las funciones anteriores sólo puede usarse dentro del contexto de una tarea o subtarea. No puede usarse después de TASK_Save o TASK_SaveSubtask ya que la salida estándar ya habrá sido cerrada.

Snippets de código útiles

Visualización correcta de caracteres especiales en la consola

Para visualizar correctamente los caracteres especiales como acentos o diéresis en la ventana de cmd.exe es necesario añadir al inicio del script la siguiente instrucción:

chcp 65001

Acceso a la carpeta NativeXML

Obtener el directorio (sin la \) donde se dejan los ficheros nativos del sistema origen o propietario):

const nativeXMLDir = sincroObj.BOLDXML.getNativeML()

Ejemplos de scripts

  • Ejemplo 1: Script BOLDXML implementado con javascript
  • Ejemplo 2: Script BOLDXML implementado con batch file (.bat)

Comandos BOLDXML

Preparación del entorno de ejecución

Los comandos se ejecutan normalmente desde un shell o ventana de comandos cmd del sistema operativo. Para acceder a los comandos BOLDXML debe ejecutar las siguientes acciones:

  • Situarse en una carpeta cliente “Integration” ( ej.: C:\Program Files (x86)\Global Planning Solutions\Custom_Files\Integration\configuration\production )
  • Abrir un intérprete de comandos (ej.: cmd.exe) allí
  • Ejecutar el comando: BOLDXMLConfig.bat
  • A continuación ya podremos escribir los comandos de integración detallados más abajo.

Generación del archivo de inicialización BOLDXMLConfig.bat

Si este archivo no existiera, debe generarse automáticamente con el siguiente procedimiento:

  1. Abrir un cmd.exe o un powershell en la carpeta BOLDXML2.
  2. Ejecutar el comando (cambiar por la carpeta que corresponda en cada caso):
.\generate_BOLDXMLConfig.bat "C:\Program Files (x86)\Global Planning Solutions\Custom_Files\Integration\configuration\production\boldserver"

El comando BOLDXMLConfig.bat debe estar ahora disponible en la carpeta ..\production

Comandos de sincro

sincro: para ejecutar tareas relacionadas con la sincronización e integración de sistemas. Ver Comando sincro para más información.

sincroa3: para ejecutar tareas relacionadas con la integración específica con la solución a3EQUIPO. Ver Interface con a3EQUIPO para más información.

sincroics: para ejecutar tareas relacionadas con la integración con el SAP ICS. Ver Comando sincroics para más información.

Para ver un listado de las opciones de cada comandos escriba: <comando>

Comando de reinicio de los servidores RestartWorkPlanner

Habitualmente se suele programar una tarea de reinicio de los servidores o instancias de WorkPlanner 1 vez al día. En entornos con IIS lo que hace es ejecutar un iisreset.bat. sin embargo existe un comando que efecuta otras tareas y además deja rastro en los logs del BOLDXML

RestartWorkPlanner: ejecuta un reinicio de todos los servidores con BOLD Cluster o sin él y los arranca de nuevo cargando la lista de planes en su caso.

Es necesario ejecutar el comando anterior con el usuario WP_USER=administrador, en caso contrario el comando generará un error.

Lo normal es crear un script de sincro (ej.: en la carpeta integration/sincro/RESETWP.bat) como el siguiente:

cd..
call boldxmlconfig.bat
set WP_USER=administrador
set WP_PWD=administrador
restartworkplanner

El script anterior puede lanzarse como tarea programada igual que el resto. En entornos donde esté instalado un BOLD Cluster habrá que definir una entrada en el fichero de configuración BOLDServer.json como la siguiente:

"CLUSTER": "http://xxlocalhost/bold/BOLDCluster.dll/soap"

Ver también Reiniciar WorkPlanner

Otros comandos

Para obtener una lista de todos los comandos documentados de BOLDXML basta con ejecutar, desde la línea de comandos preparada para ello (BOLDXMLConfig.bat) el comando “sincro” sin parámetros.

Añadir nuevos comandos al BOLDXML

Los comandos se implementan en su mayoría dentro del fichero .\scripts\BOLDXML.js. Es relativamente fácil añadir nuevos fijándonos en cualquiera de los actuales.

Ficheros de configuración de BOLDXML2

Los ficheros de configuración del módulo de integración BOLDXML2 se encuentra normalmente en la carpeta <cliente>/integration y son los siguientes:

BOLDXML.json

Contiene la configuración general del módulo. Las variables disponibles son:

  • “HTTPSAllowAll”: true / false. Cuando se utilizan conexiones https, indica si se permitirá crear los canales de comunicación SSL aunque los certificados proporcionados por los servidores no permitan realizar una verificación completa. Normalmente debe ponerse a true cuando se utilizan certificados temporales o que no permiten su validación a través de internet.
  • “BOLDXML_INSTALL” : “C:\\Proyectos\\Clientes\\BOLDXML2”. Indica la ubicación de la instalación del módulo.
  • “bUseSOAP_API” : true. Permite seleccionar si en las llamadas se importación de objetos contra el servidor BOLD se utilizará el protocolo SOAP o REST. Existe una limitación actualmente que obliga usar SOAP al enviar documentos XML grandes al servidor (ej.: aprox. más de 30KB)
  • TBD

Los ficheros de transformación se encuentran en la carpeta por defecto (ej.: BOLDXML2\a3), pero pueden especializarse de forma particular copiando la plantilla estándar en la cartpeta de configuración de la instancia (ver personalizar XSLT)

BOLDServer.json

Contiene la configuración de acceso al servidor BOLD. Las variables disponibles son:

  • “CLUSTER”: indica la URL completa del cluster, si está configurado (ej.: “http://localhost/bold/BOLDCluster.dll“)
  • “HOST”: “localhost” (indica la máquina que tiene instalado el servidor de WP)
  • “SYSTEM”: “/Servers/a3_local/ISAPIBoldWP.dll/soap” (indica la URL donde está instalado el servidor de WP)
  • “user”: “adm” (indica el usuario que se utilizará para las tareas de integración e interfaces)
  • “pwd”: “adm” (pwd del usuario anterior)
  • “timeout”: 180 (tiempo de espera máximo -segundos- a usar por defecto esperando una respuesta del servidor en las interfaces)
  • “SQLServer”: indica el hostname que tiene el servidor de base de datos (SQL Server) utilizado por el servidor de BOLD. Ejemplo: “sgps01”.
  • “SQLServer_database”: indica la base de datos utilizada por el servidor BOLD. Ejemplo: “OHSJD_ZAR_TEST”
  • “SQLServer_username”: usuario de acceso al SQL Server. Ejemplo: “usrdba”
  • “SQLServer_pwd”: password de acceso.

Ficheros de configuración específicos de una integración

Existen algunas integraciones preconfiguradas “estándard” contra los siguientes sistemas: a3EQUIPO, SAP ICS y SPEC. Cada una de estas integraciones dispone de su propio archivo de configuración particular.

Integración con a3EQUIPO: A3Server.json

Ver también el artículo principal: Interface con a3EQUIPO.

Integración con SAP ICS: SAPICS.json

  • “enableSFTP”: false/true. Indica si se accede o no al servidor SFTP para recoger los ficheros de intercambio de datos.
  • “SFTP_PROD” : “SFTP_server.dominio.intranet”
  • “sft_user_PROD” : “usuario”
  • “sft_pwd_PROD” : “password”
  • TBD

Integración con relojes de marcajes: SPECServer.json

Permite configurar la conexión con los Relojes SPEC (u otros):

  • “Clockings_file”: “C:/ImportExport/ohsjd/NativeXML/clockings/marcajes.csv”. Ubicación del fichero donde van apareciendo los marcajes.
  • “Export_workers_dir”: “C:/ImportExport/ohsjd/NativeXML”. Ubicación donde WP irá dejando los ficheros para el alta/baja de empleados en los relojes.
  • “ClockNames”: { “Planta Principal”: -1 }. JSON map con la lista de nombres de los relojes y su nº identificador. Atención! si no se da de alta en la lista de terminales de la aplicación este identificador no aparecerán los marcajes en pantalla.
  • TBD

Definición de una transformación XML

Los documentos de transformación determinan el comportamiento de las etapas de transformación dentro de un script de “sincro” (sincronización) entre sistemas. Se utiliza habitualmente para transformar un documento desde el formato del sistema origen al formato BOLDXML. Guarda cierta similitud con las transformaciones que se implementan habitualmente con XSLT.

El documento de transformación determina el formato final del documento BOLDXML que realmente se podrá enviar al servidor para su proceso. Los pasos entonces son los siguientes:

Documento XML/JSON nativo --> Documento de transformación --> Documento XML en formato BOLDXML

El documento generado a través del documento de transformación deberá tener un formato válido XML: ver Estructura de un documento XML en formato BOLDXML.

Localización de los ficheros de transformación

Con el módulo BOLDXML se distribuyen una serie de ficheros de transformación estándar. Por ejemplo: los de la interface de a3EQUIPO están en la carpeta BOLDXML2\a3 y tienen extensión .xml

Cuando se quiere personalizar alguno de estos ficheros (o bien realizar una transformación nueva, por ejemplo al implementar una nueva sincro) es necesario copiar el fichero .xml en la carpeta de nombre xslt base de configuración de la instancia, por ejemplo:

C:\Program Files (x86)\Global Planning Solutions\Custom_Files\Integration\configuration\development\xslt

Los scripts de integración miran primero si existe el fichero en esta carpeta, y sólo en el caso de que no exista, van a mirar luego a la carpeta dentro de BOLDXML2.

Ejemplo de documento XML de transformación

A continuación se muestra un documento de ejemplo de una transformación XML real:

<?xml version="1.0"?>
<IntContainerVector>
  {%- if CECOS_asPlanArea -%} {# Acerinox #}
    <UpdateFields UpdateOnly="1" COLNAME0="LabourAgreement" COLNAME1="ProfessionalCategory" COLNAME2="SystemProps.Grupo" COLNAME3="SystemProps.Nivel" ImportType="PropertyIntervalSet" UseLastContractWorkerDataForNewCut="1" MergeCutDuplicates="1"/>
  {%- else -%}
    <UpdateFields UpdateOnly="1" COLNAME0="LabourAgreement" COLNAME1="PlanArea" ImportType="PropertyIntervalSet" UseLastContractWorkerDataForNewCut="1" MergeCutDuplicates="1"/>
  {%- endif -%}  
  <CustomParams Control="A3"></CustomParams>

<!-- jsonquery=QueryResult.Record -->

{% for o in list %}
  <!-- {% set dummy=JSON.stringify(o) %} {{ dummy|safe }} -->
  <ContractWorker>
    <Worker valueExt="{{ o.EmployeeID }}"/>
    <PropertyStart value="{{ o.DateLaborLife|parseA3Date(1) }}"/>
    <PropertyEnd value="{{ o.DateEndLaborLife|parseA3Date }}"/>
    {%- if !CECOS_asPlanArea -%}
      <PlanArea valueExt="{{ o.WorkplaceID }}"/>
    {%- endif -%}
    <LabourAgreement valueRef="{{ o.AgreementCode }}"/>
    {%- if o.JobTitle == "" -%} 
      {% set msg = "Atención! Empleado sin JobTitle: " + o.CompanyCode + "-" + o.EmployeeCode %}
      {% set dummy=logger.warn(msg) %} {% set dummy=sincroObj.logging.incWarningCount() %}
      <ProfessionalCategory valueRef="MODIFICAR"/>
    {%- else -%}
      <ProfessionalCategory valueRef="{{ o.JobTitle }}"/> <!-- {{ o.WorkplaceCode }} {{ o.WorkplaceName }} -->
    {%- endif -%}
    {%- if CECOS_asPlanArea -%}
    <SystemProps>
      <Grupo value="{{ o.ClassificationCode }}"/> {% if grupoPorEmpleado[o.CompanyCode + "-" + o.EmployeeCode].length > 1 %} <!-- EMPLEADO CON CAMBIOS DE GRUPO --> {% endif %}
      <Nivel value="{{ o.SubClassificationCode }}"/>  {% if nivelPorEmpleado[o.CompanyCode + "-" + o.EmployeeCode].length > 1 %} <!-- EMPLEADO CON CAMBIOS DE NIVEL --> {% endif %}
    </SystemProps>
    {%- endif -%}
    <BOLDXML_info>
        <WorkerName>{{ o.CompanyCode + '-' + o.EmployeeCode}}</WorkerName
        <CompanyName>{{ o.CompanyCode }}</CompanyName>
        <PlanArea>{{ o.WorkplaceCode }} {{ o.WorkplaceName }}</PlanArea>
        <LabourAgreement>{{ o.AgreementDescription }}</LabourAgreement> <!-- {{ o.AgreementDescription | xml_c }} -->
    </BOLDXML_info>
  </ContractWorker>
{% endfor %}

</IntContainerVector>


Como se puede ver, la plantilla define el formato del documento destino (en este caso, un documento de importación de contratos, ver Formato XML para contratos. Ver también Categoría:Esquema XML)

Templating engine

La transformación se realiza utilizando el lenguaje de plantillas SWIG (originalmente era: https://github.com/paularmstrong/swig, pero dejó de ser mantenido y actualmente debería ser este otro: https://github.com/node-swig/swig-templates).

El documento se modela utilizando las variables que dan acceso a los datos en el documento origen que suele ser una lista de registros. Los datos de cada registro se inyectan en la plantilla en formato JSON.

Funciones pipe de transformación

A menudo resulta útil poder realizar pequeñas transformaciones directamente desde dentro de la propia plantilla de transformación.

Por ejemplo, se puede ver cómo se aplica la función transformación xml_c a un valor obtenido del documento de entrada (campo LabourAgreement). Las funciones de transformación internas reciben como parámetro un valor y retornan otro diferente. Las funciones pipe disponibles son las siguientes:

Sobre valores strings

  • xml_c: convierte el valor en un valor válido que siempre será válido dentro de un comentario XML. Por ejemplo dentro de un comentario XML no es válido incluir doble caracter “–“. Esta función lo sustituye por el caracter “-“.
  • padWithZeros(lenght): alarga el valor hasta la longitud indicada añadiendo los ceros delante que sean necesarios. Ej.: “67” | padWithZeros(5) –> “00067”

Convertir fechas a diferentes formatos

  • parseA3Date: Decodifica una fecha-hora en el formato “a3”. Ej.: “31/05/2014 9:30:00” y la pasa a formato ISO: “2014-05-31T09:30:00”
  • parseA3DateOnly: Decodifica una fecha-hora en el formato “a3”. Ej.: “31/05/2014 9:30:00” y la pasa a formato ISO (pero descartando las horas): “2014-05-31T00:00:00”
  • parseMDYY2ISODateTime: Converts a string in in format dd/MM/YY into a javascript date
  • parse_MDYY_2_YYYYMMDD: Transforms a string in in format dd/MM/YY into YYYY-MM-DD

Sobre valores numéricos

  • incNumber: convierte el valor a un nº entero y luego lo incrementa en 1. Ej.: “1” | incNumber –> “2”

Otras

  • today: retorna la fecha actual en formato ISO. Ej.: “” | today –> “2018-04-12T00:00:00”
  • now: retorna la fecha-hora actual en formato ISO. Ej.: “” | today –> “2018-04-12T12:38:54”

Implementación de transformaciones custom

Sin embargo a veces no existe ninguna función de transformación que satisfaga los requerimientos. Entonces es necesario implementar una nueva. Para ello podemos utilizar toda la potencia del lenguaje javascript. Los pasos a seguir son los siguientes:

  1. Implementar la función en el script de transformación
  2. Publicar la función para que sea accesible desde la plantilla de transformación pseudo “xslt”.
  3. Modificar la plantilla de transformación para utilizarla

Objetos disponibles desde la plantilla de transformación

  • list: contiene la lista de registros obtenidos del documento de entrada. Ejemplo list[0] accedería al primer elemento.
  • sincroObj: objeto Sincro que encapsula la tarea de sincronización. Desde él se puede acceder a otros objetos de contexto como son:
    • sincroObj.logging: acceso al controlador de los logs de resumen
    • sincroObj.BOLDXML: acceso al controlador BOLDXML
    • sincroObj.bold: acceso a la API SOAP del servidor
    • sincroObj.boldREST: acceso a la API REST del servidor

Acceder a los archivos de configuración desde dentro de la plantilla de transformación

A veces puede ser necesario acceder a variables o métodos desde la propia plantilla. El siguiente ejemplo muestra cómo hacerlo:

 <CustomParams UseExtCodeAsKeyField="1" Control="A3" KeepWPData="{{ sincroObj.BOLDXML.getKeepWPData() }}">

En este ejemplo el atributo KeepWPData se decide en función de lo que se haya configurado en el fichero BOLDXML.json (pero encapsulado con el método getKeeyWPData)

Control de errores y log desde dentro de la plantilla de transformación

Anotar el objeto JSON de entrada a transformar en el mismo resultado XML

Una opción bastante útil es poder ver directamente dentro del resultado de la transformación los datos del objeto original a transformar. Esto se realiza con la línea (opcional) siguiente:

 <!-- {% set dummy=JSON.stringify(o) %} {{ dummy|safe }} -->

El resultado en el fichero de salida será algo similar a los siguiente:

<!--  {"CompanyID":"a66844ce-d271-49cf-ba91-ad6864429e22","CompanyCode":"1722","EmployeeID":"95764b3e-e12b-4143-aefa-1117d7dbc139","EmployeeCode":"002128","DateLaborLife":"","DateEndLaborLife":"31/10/2015 0:00:00","SecondName1":"LOPEZ","SecondName2":"DIAZ","GivenName":"DOLORES","IdentifierNumber":"0900000Z","PhoneNumber1":"99999999","PhoneNumber2":"9999999","JobTitle":"STRUCTURE CD","WorkplaceID":"6d605342-beb8-40ed-be6f-b0360b66e9f7","WorkplaceCode":"306","WorkplaceName":"TARRAGONA 1","WorkplaceLocation":"MA1","SeniorityCompanyDate":"16/04/2009 0:00:00","AgreementID":"88ab035e-23b7-49eb-9c8c-e8b504ae860b"} -->
  <ContractWorker>
    <Worker valueExt="95764b3e-e12b-4143-aefa-1117d7dbc139"/>
    <PropertyStart value="1950-01-01T00:00:00"/>
    <PropertyEnd value="2015-10-31T00:00:00"/><LabourAgreement valueRef="00000172200305"/>
  ...
  <ContractWorker>

Con lo cual podremos ver más fácilmente el efecto de la transformación en función de los datos de entrada.

Uso de comandos de log dentro de la plantilla de transformación

En el ejemplo anterior la sección siguiente genera un aviso en el log e incrementa el contador de avisos correspondiente a la etapa de transformación que se está ejecutando:

    {%- if o.JobTitle == "" -%} 
      {% set msg = "Atención! Empleado sin JobTitle: " + o.CompanyCode + "-" + o.EmployeeCode %}
      {% set dummy=logger.warn(msg) %} {% set dummy=sincroObj.logging.incWarningCount() %}
      <ProfessionalCategory valueRef="MODIFICAR"/>
    {%- else -%}
      <ProfessionalCategory valueRef="{{ o.JobTitle }}"/> <!-- {{ o.WorkplaceCode }} {{ o.WorkplaceName }} -->
    {%- endif -%}

En este caso cuando el registro que se está procesando (objeto o) contiene la propiedad JobTitle en blanco, se genera un mensaje en el log: set dummy=logger.warn(msg) y se incrementa el contador de avisos: set dummy=sincroObj.logging.incWarningCount() que aparecerá luego en la página de resumen de la interface.

Acceso a la configuración del BOLDXML para controlar qué transformaciones se realizan

Otro ejemplo posible sería cuando no se quiere incluir una parte de la transformación en función de si alguna de las sincros ha sido desactivada. En el ejemplo siguiente, el campo LabourAgreement sólo se rellenerá si la sincro STD_ObtenerConvenios está habilitada:

 {%- if sincroObj.BOLDXML.isSincroEnabled("STD_ObtenerConvenios") %}
 <LabourAgreement valueRef="Plantilla:O.Laborales.CodigoConvenio"/>
 {% endif -%}

Estructura de un documento XML en formato BOLDXML

Formato general

La estructura de cualquier documento de importación/exportación de entidades de WorkPlanner es la siguiente:

<?xml version="1.0" encoding="ISO-8859-1"?>
<IntContainerVector>
<UpdateFields UpdateOnly="0" COLNAME0="nombre_de_campo_0_a_actualizar" .COLNAME1=" nombre_de_campo_1_a_actualizar " …/>
<CustomParams MergeCustomProperties="1" />
<NombreEntidad>
	… campos específicos de la entidad …
</NombreEntidad >
</IntContainerVector>

Paramétros de configuración

Estos parámetros determinan el tratamiento que se hará en el servidor del documento.

IntContainerVector: este nodo indica el inicio de un documento de importación de WorkPlanner y es el mismo con independencia de la entidad a tratar.

UpdateFields: este nodo es opcional y se utiliza para indicar los campos que se actualizarán en el caso de que la entidad que se esté importando ya existiera en WorkPlanner. Por ejemplo, si la profesional con código “12345” ya existe, si el proceso de importación se la encuentra sólo trataría de actualizar los campos que aquí se indicaran, ignorando el resto de ellos.

UpdateOnly: este atributo booleano es opcional. Indica si el único objetivo del documento de importación es la actualización de los datos (1-true) o bien tanto la creación como la actualización (0-false). Este último es el valor por defecto.

COLNAMEi: para el caso de una actualización indica si el valor de este campo se tendrá en cuenta o no.

UpdateAll: este atributo booleano indica si, cuando el objeto ya exista, se han de actualizar todos sus atributos según los nuevos valores del documento XML actual o no. Por defecto es “0” o false. Sólo se actualizan los que se especifiquen exactamente.

DebugLevel: regula el número de mensajes que genera una operación de importación de objetos. Si es 0 tan sólo salen los errores. Si es 1 aparecen mensajes informativos. Superior a 1 todo tipo de información adicional.

Opciones de configuración dentro del nodo CustomParams

Este nodo es opcional y se utiliza para pasar parámetros adicionales al servidor. Las propiedades específicas adicionales dependen de la entidad a sincronizar. De forma general existen las siguientes:

  • MergeCustomProperties: boolean. Cuando se sincronizan propiedades auxiliares (ver CustomProperties) indica si se respetarán las propiedades auxiliares ya existentes en las entidades a actualizar –valor 1- o bien se reemplazarán totalmente por las que se definan en el documento de importación –valor 0- (no confundir con las propiedades definidas como CustomProps que definen una estructura más rígida)
  • UseIDAsKeyField: boolean. Indica que los objetos a actualizar dentro de BOLD se identificarán en función del campo ID que se proporcione en el documento de carga.
  • UseExtCodeAsKeyField: boolean. Indica que los objetos a actualizar se identificarán en función del campo ExtCode que se proporcione en el documento de carga.
  • TotalImport: boolean. 0 Indica que la importación es incremental. Es el valor por defecto. 1 Indica que la importación es total. Una importación total lo que hace es borrar primero todo lo que hay en la base de datos que no aparece en el documento XML de entrada para esa clase de objetos (CUIDADO!).
  • MaxPercentToDelete: integer. Si no se indica este valor por defecto se toma un valor del 40%. Como TotalImport es una opción muy peligrosa, se abortará la importación si se intenta borrar una cantidad de datos superior a MaxPercentToDelete. Por defecto este valor es del 40%.
  • GlobalTransaction: boolean. 0 Indica que la importación se hará con transacciones objeto por objeto. Es el valor por defecto. 1 Indica que la importación se ejecuta con una transacción global.

Nota: en algunas entidades es posible identificar el objeto o entidad a actualizar utilizando otros atributos diferentes de los anteriores (ID, Name o ExtCode), dependiendo de la versión de la aplicación. Por ejemplo, en el caso de la entidad ContractWorker, es posible identificar el contrato a actualizar utilizando los campos código de empleado/a y una fecha cualquiera (Worker y ContractStart): el módulo de importación tratará de localizar en el sistema un contrato vigente de esa profesional durante la fecha indicada y ese será el que se actualizará según lo nuevos datos del documento de importación. Para más detalles ver la sección correspondiente.

Entidad a importar: nodo NombreEntidad

Cada entidad a importar debe encerrar su definición dentro de este nodo. En un mismo documento de importación todos los nodos NombreEntidad deben corresponder a la misma entidad. Es decir: no se admitiría que en un mismo documento coexistieran, por ejemplo, una entidad horario (Schedule) junto a un/a empleado/a (Worker).

Para ver el detalle del formato de cada tipo de objetos XML disponible ver Categoría:Esquema XML.

Otros parámetros menos frecuentes

  • NoExactIdentify: boolean. Esta opción sirve solo para la importación de las clasificaciones ( WP_Clasificaciones) . Si este modo está activo, la importación intentará hacer un matching lo más fiel posible entre el fichero XML que se importa y lo que ya existe en la base de datos. Este modo sirve para pasar de un sistema de clasificación no fechado a un sistema de clasificación fechado. Por defecto vale 0.
  • SearchValidPath: boolean. Cuando en el documento de importación no llega información suficiente para identificar el path completo donde va colgado un objeto, se ha definido este modo. En este caso el programa de importación identifica los nodos a partir del nombre y las fechas. Por defecto vale 0.
  • TotalImporByNeed: boolean. Si está activo indica que se importarán en un modo especial las necesidades. Este modo borrará todas las necesidades cuyo código externo aparezca en el documento de entrada excepto aquellas que estén en el documento. Es decir:

Llega un documento con 3 needs.

Need A1. Código externo A
Need A2. Código externo A
Need A3. Código externo A

En la BD existen las siguientes Needs:

Need A1. Código externo A
Need A2. Código externo A
Need A3. Código externo A
Need A4. Código externo A
Need A5. Código externo A

Need B1. Código externo B
Need B2. Código externo B
Need C4. Código externo C

Lo que se ha de hacer es, de aquellos códigos externos que vienen en el documento, borramos todas menos las que están en el documento. En el documento se habla de needs con código externo A. Por tanto, borramos A4, A5 (son las únicas con código externo mencionado en el documento, y que a la vez, no vienen en el mismo) Las necesidades B1, B2 y C4 se dejan tal cual pues sus códigos externos no figuran en el documento.

Uso de variables de sesión y tablas externas

Cuando dentro de una fórmula (de contadores por ejemplo) es necesario recuperar algún valor de una tabla externa a la aplicación la solución puede ser efectuar un comando SQL SELECT contra la tabla en cuestión (utilizando la función SQLCount, cuando se trata del valor a extraer de una única columna cualquiera, por ejemplo).

Sin embargo, este procedimiento deja de ser ágil cuando el nº de consultas que se han de lanzar empieza a ser elevado (varios miles). Para solucionar el problema anterior se puede utilizar la siguiente técnica que consiste en precargar los datos en una tabla en memoria, guardar la tabla en una variable y luego acceder a ella mediante otra función. Los pasos son los siguientes:

Precargar los datos en la tabla ad-hoc: un script

Programar un script externo que efectúe la carga de los datos en una tabla auxiliar de la base de datos de BOLD, creada para este fin.

A modo de ejemplo consultar alguno de los scripts de la interfases de ejemplo existentes. Por ejemplo:

95 Sincro_DatosComedor.bat / Sincro_DatosComedor.wsf / Sincro_DatosComedor.js

El script anterior crea la tabla dadesmenjador, que contiene datos de fichajes de comedor que luego serán utilizados en las fórmulas de contadores de nómina para determinar si a cada empleado le corresponde o no un descuento en su nómina para cada uno de los días en que haya utilizados los servicios de comedor. Para ello, las fórmulas de cálculo correspondientes se apoyan en las siguientes funciones dinámicas adicionales:

Precarga de la tabla: DictionarySQL

DictionarySQL La función dinámica que se ocupa cargar tablas en memoria a partir de un comando SQL genérico, donde se definen los campos clave de búsqueda.

Esta función debe tener siempre 5 parámetros (SQL, filtro1, filtro2,filtro3, filtro4). En caso de no ser necesarios alguno de los filtros, se debe especificar “”.

Búsqueda rápida en memoria de un registro: DictionaryLookup

DictionaryLookup Función dinámica para la búsqueda rápida de datos en las tablas en memoria.

Gestión de la tabla precargada en caché: variables de sesión

SetSessionVar Función para almacenamiento temporal de variables en sesion, que serán habitualmente variables tipo tabla retornadas por DictionarySQL.

GetSessionVar Función de recuperacion de variables de la sesión.

DelSessionVar Función de eliminación de variables de la sesión.

NOTA: Si la variable de sesión se utiliza en un informe que a su vez tiene definidas variables calculadas a traves del método CustomWorkerCounter, es IMPERATIVO que el nombre de dicha variable de sesión lleve como prefijo la palabra CustomWorkerCounter. El motivo es que usar CustomWorkerCounter implica recursividad, lo cual conlleva pasar más de una vez por el mismo SetSessionVar, y ello da lugar a errores diversos.

Ejemplo de uso combinado

Por ejemplo en el informe de contadores, donde se necesita un acceso eficaz a estos datos se configura de la siguiente forma en la sección de inicialización (BeforeExpression) y finalización (AfterExpresion) del informe correspondiente (ExtAttributes, nodo <Class>):

<BeforeExpression>
  <Expression>
    <![CDATA[
      SetSessionVar("var_datos_comedor", DictionarySQL(
        "select max(id_persona) as id_persona, max(id_empresa) as id_empresa, max(data) as fecha, count(*) as repeticiones, max(tipus) as tipo_comida 
         from dadesmenjador group by id_persona, id_empresa, tipus",
        "id_persona", "id_empresa", "fecha", "tipo_comida"))
    ]]>
 </Expression>
</BeforeExpression>
<AfterExpression>
  <Expression>
    <![CDATA[
      DelSessionVar("var_datos_comedor")
    ]]>
 </Expression>
</AfterExpression>


Y dentro de la configuración de los datos del informe aparecerá una definición similar a esta para obtener el nº de usos del pase de comedor realizadas de tipo 1 (comidas), para un empleado X, de la empresa Y, en la fecha Z:

<ExtAttribute Name="Dieta_comidas" Type="int" Default="0">
<Expression>
  <![CDATA[
    DictionaryLookup(GetSessionVar("var_datos_comedor"), IdCW.Worker.ExtCode, IdCW.Company.ExtCode, StartDate, 1).repeticiones
 ]]>
</Expression>
</ExtAttribute>


Nótese como se accede al valor del registro extraido, usando el nombre de la columna del resultado de la consulta: DictionaryLookup(…).repeticiones como si se tratara de una propiedad estándar de un objeto del sistema. Esto es así porque la función DictionaryLookup retorna un Objeto automático.

Por supuesto, también se podría almacenar el valor así extraído en otra variable (del informe o de la sesión) para evitar efectuar más búsquedas de las estrictamente necesarias.

Informes de tabla dinámica

ATENCIÓN: El siguiente contenido es de uso avanzado y su utilización puede ocasionar daños irreparables en la configuración del sistema

Introducción

La principal diferencia de los informes de tabla dinámica (EnumAdvancedReport) respecto de los habituales es que son capaces de agregar los datos directamente en el servidor y por tanto pueden procesar un número muy superior de filas sin los problemas generados por la descarga de grandes documentos en el cliente. Tan sólo los datos agregados tras efectuar algún tipo de suma o cálculo es lo que realmente se retorna al cliente. De esta forma se evita el problema que suponía enviar un gran volúmen de datos al cliente para mostrar totales, subtotales, etc…

Además, mediante estos informes es posible partir de un informe ya existente y crear una nueva instancia del mismo simplificando su número de columnas, cambiando la ordenación por defecto, etc… de forma que siempre aparezca y se genere tal como nos interesa sin necesidad de ninguna acción adicional por parte del usuario o usuaria.

Antecedentes

Los informes en BOLD se pueden definir ahora de dos formas: en el querycustom.xml partiendo de un informe “nativo” o bien generando un objeto de tipo BIDatacubeModel. Estos objetos contienen una definición similar a la que existía hasta ahora en el querycustom pero con la ventaja de que se pueden construir dinámicamente mientras la aplicación está en ejecución y son más fáciles de actualizar (por ahora sólo vía importación).

Además estos objetos ahora se gestionan de la misma forma que el resto de objetos del sistema: tienen un maestro, control de versiones, import/export, etc… Y los resultados de los informes se pueden almacenar de forma persistente como el resto.

Descripción

Los informes de tabla dinámica pueden funcionar de forma muy similar a como se configuran las tablas dinámicas en una hoja de cálculo:

  1. Se parte de una tabla base virtual interna en memoria donde residen los datos a agregar
  2. Se definen las claves de agregación
  3. Se define la función de agregación
  4. Se efectúa el cálculo de la tabla resultante
  5. Sobre los datos agregados es posible extraer la información que los originó fácilmente (tipo drill-down)

Por otro lado, existen algunos aspectos diferentes:

Origen de los datos a agregar

La tabla virtual de los datos a agregar puede tener diferentes orígenes y puede que no exista realmente como tal sino que los datos origen podrían ser:

  • un iterador sobre los objetos en memoria del sistema (ej.: las tareas del plan en un intervalo, los contratos del plan, las personas empleadas en el plan, ver las funciones de iterador)
  • los resultados de un informe preexistente base (por ejemplo uno de estos).
  • el resultado de una consulta SQL (que no es más que otro tipo de informe).

Además es posible anexar nuevos campos calculados a los campos de la tabla origen (al estilo de los ExtAttribute). Estas columnas adicionales se pueden utilizar luego como variables en el siguiente paso de agregación. Ver Input_Axis. También es posible utilizar expresions BeforeExpression y AfterExpression para ejecutar ciertas acciones únicamente al inicio y al final del cálculo del informe.

Nota: El tipo de origen de los informes de tabla dinámica se especifica a través del atributo DataInputType. Los valores posibles son:

0: Sobre un informe estándar.

1: Sobre un informe SQL (Pendiente de implementar)

2: Sobre el iterador Iterator_Task_ForPlanTasks

3: Sobre el iterador IterBased_ByAccumulatedByNeedIncidence

4: Sobre el iterador Iterator_ByDay_ForPlanTasks

5: Sobre el iterador Iterator_TasksAndEmptyDays_ForPlanTasks

6: Sobre el iterador Iterator_ByDayAndEmptyDays_ForPlanTasks

Definición de las claves de agrupación o agregación de los datos

La agrupación de los datos se configura a lo largo de dos ejes: X e Y. Por ejemplo los datos se podrían obtener agregados/agrupados según [Año, Mes] y [Categoría, Actividad] de tal forma que se obtuviera el tiempo asignado correspondiente a cada presencia según dichas coordenadas. Otra configuración de ejemplo podría ser [Necesidad/Puesto] para obtener una relación de la última asignación efectuada en el intervalo de cálculo, etc…

La definición de las claves de agrupación de los datos se realiza de la misma forma que para las columnas calculadas de los informes evaluadas en el contexto de cada fila de datos de entrada y retornan el valor de coordenada que corresponde a esa fila de datos. Por ejemplo, si queremos agrupar por años y meses para determinar el año al que asignar los datos de una fila, la fórmula de una de las columnas de agrupación sería:

Year(TimePeriod.StartTime)

La evaluación sobre cada fila de datos de entrada de la función anterior podría generar una clave como la siguiente:

2009, 2010, 2011

Las variables disponibles dependerán del contexto generado por el objeto y los atributos calculados sobre la fila de datos de entrada.

Definición de la función de agregación

Es posible definir múltiples campos de agregación, pero todos ellos compartirán la misma clave de agregación. Por ejemplo podríamos acumular los tiempos de incidencia por mes y a la vez obtener las duraciones asignadas a una área en concreto.

Las funciones de agregación disponibles actualmente son:

  • Def. de usuario: (pendiente) Una expresión definida por la propia persona usuaria
  • Suma: suma el resultado de la evaluación de la dimensión de agregación al valor ya existente (para las claves de agregación actuales)
  • Max: selecciona el máximo entre el valor ya existente y el nuevo valor retornado por la expresión
  • Min: selecciona el mínimo
  • Contar: cuenta el nº de veces que la expresión es cierta

Alguns ejemplos de funciones de agregación posibles serían (recordar: la función se evalúa para cada fila de datos de entrada y su resultado se agrega sobre los cálculos sobre filas anteriores que cumplan la misma clave de agregación):

Contar el nº de incidencias que aparecen

TimePeriod.Incidence!=0

Definición en formato XML

Como cualquier objeto del sistema, un objeto informe de tabla dinámica también puede importarse a partir de un documento XML con un ejemplo clarificador.

Acceso a la lista de informes definidos

Se puede acceder a la lista de definiciones cargadas en el sistema desde el menú Maestros–>Informes de tabla dinámica

Lamentablemente no existe por el momento otra forma de editar las definiciones que no sea modificando manualmente la definición XML desde un editor de texto e importándola de nuevo.

Importar o actualizar una definición de informe de tabla dinámica

Cargando el fichero de definición XML desde el menú Integración–>Importar documento XML o desde el subsistema externo de integración BOLDXML.

Exportar definición de informe de tabla dinámica

Desde el cliente windows, Maestros->Configuración->Informes de tabla dinámica.

Una vez dentro, ir al menú herramientas, -> Exportación BOLDXML. Te pedirá un nombre de fichero y tras seleccionarlo te volcará todos los informes en ese fichero.

Ejemplos

Algunos ejemplos de definiciones de informes de tabla dinámica genéricos que se pueden importar en la herramienta y que no dependen de ninguna parametrización específica se pueden descargar de la página Biblioteca de informes de tabla dinámica genéricos

Filtrado de datos de un informe de tabla dinámica

Estos informes disponen de dos secciones especiales para filtrar tareas o personas trabajadoras.

  • InputFilterByWorker: Expresión para filtrar qué profesionales quieres que se incluyan en el informe. El contexto es WorkerPlan.
  • InputFilter: Expresión para decidir que cajas/días se incluirán en el informe. El contexto es TaskContextObject.

Truco para paginar informes especiales

<InputFilterByWorker>
    <Expression>
        <![CDATA[
               IfElse(!IsInvalid(GetSessionVar("PAGINA")) AND !IsInvalid(GetSessionVar("PAGINASTOTALES")) , 
Mod(ContractWorker.Worker.ID,GetSessionVar("PAGINASTOTALES"))==GetSessionVar("PAGINA")-1 , true)
         ]]>
    </Expression>
     </InputFilterByWorker>

Ver también

TaskContextObject Para ver en los informes de iteradores de plan, sobre qué propiedades nos podemos apoyar en la etapa de agregación.

Expresión

Una expresión en BOLD funciona de forma similar a como funciona en el Excel: se trata de una fórmula que se evalúa retornando uno o más valores.

Las fórmulas o expresiones se utilizan para personalizar todo tipo de información dentro de la aplicación.

1 Utilización de expresiones o fórmulas

Por ejemplo para efectuar las siguientes cosas:

  • Personalizar columnas de informes (ExtAttribute)
  • Personalizar la información que aparece al pulsar en diferentes objetos de la aplicación (ej.: en el diagrama de Gantt)
  • Personalizar los contadores anuales y de cálculo de nómina
  • Personalizar alertas en la aplicación y en el portal (KPI)
  • Personalizar el comportamiento antes determinadas acciones del usuario
  • etc…

2 Sintaxis de una expresión

Un ejemplo de expresión es el siguiente código:

IfElse(PreferentNeed.32DatosEvaluacion1.IdJefe!=0, "SI", "NO")

Una expresión se evalúa siempre dentro de un contexto. El contexto significa simplemente que existen una seria de variables predefinidas cuyo valor es directamente accessible.

Dentro de una expresión pueden aparecer:

  • Referencias a atributos de los objetos del sistema (ej.: CustomProps.M30DatosEmpleado.IdCW.PreferentNeed2.ID accedería a la propiedad ID del objeto IdCW)
  • Otras funciones (ver funciones)
  • Atributos implícitos del objeto de contexto (ej.: Name o Description)
  • Variables globales publicadas el contexto de evaluación (ej.: @From o @To)
  • Comentarios: los comentarios empiezan con /* y terminan con */. En expresiones del tipo IfElse anterior con varios niveles o condiciones lógicas complejas vale la pena utilizar la sintaxis de este ejemplo.

3 Tipo devuelto por una expresión

Debe ser uno de los definidos en Tipos de datos (Type).

4 Acceso a los atributos de los objetos del sistema

Una expresión puede referenciar atributos de los objetos del sistema asociadas a los objetos de contexto. El acceso a estos atributos se efectúa ‘navegando’ por las referencias de los objetos disponibles en el contexto de evaluación de la expresión utilizando el operador de “.” En el ejemplo anterior, suponiendo que el contexto fuera un objeto de tipo contrato (ContractWorker) se está accediendo al atributo necesidad preferente del contrato y se comprueba si existe, y en ese caso se retorna el valor “SI”.

5 Tipo Link y acceso a atributos cuando la referencia no es válida

Una referencia a un objeto del sistema internamente se representa mediante el tipo de datos Link.

Si no estamos seguros de que la referencia al objeto sea válida y se intenta acceder a alguna de sus propiedades la expresión generará un error. Para controlar los errores es necesario verificarlo mediante una condición como la siguiente:

CustomProps.M30DatosEmpleado.IdCW.PreferentNeed2.ID != 0

La condición anterior será verdadera cuando PreferentNeed2 apunte a un objeto inexistente (sea NULL).

6 Otros enlaces de interés

Prueba de expresiones

Configuración de columnas en informes

Lista de funciones