Ayuda sobre productos BOLD:
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:
- Implementar la función en el script de transformación
- Publicar la función para que sea accesible desde la plantilla de transformación pseudo «xslt».
- 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 -%}