Generalidades
El sistema de scripting de Modela, permite crear pequeños scripts para personalizar los formularios o ejecutar actividades automáticas de integración con otros sistemas. Todos los scripts se programan en .Net (C#), permitiendo acceder al modelo de datos del proceso (campos del formulario y otras propiedades).
Existen principalmente 3 tipos de scripts:
- Comportamientos. Son scripts que permiten controlar la visibilidad, Editabilidad, Obligatoriedad y Validación de los controles de los formularios. Estos scripts devuelven un Verdadero o Falso para indicar el funcionamiento del comportamiento al que están vinculados.
- Campo calculado. Son scripts que permiten obtener el valor de un campo calculado en base a otros. Estos scripts devuelven un valor, que será el valor de dicho campo, y que está calculado en base a cualquier otro campo del formulario.
- Actividades automáticas. Estos scripts ejecutan una tarea automática dentro del proceso, normalmente de integración con sistemas externos, como crear una incidencia en JIRA o cambiar algún elemento en una lista de Sharepoint. Estos scripts también deben devolver un valor, pero sólo se utilizará como resultado de la actividad automática para visualizarlo en la consola de Adminsitración.
Debemos tener en cuenta que al abrir una solicitud se ejecutan todos los scripts de comportamiento y de campos calculados, si la solicitud es nueva o si no lo es pero no tiene todos los campos informados, algunos campos estarán vacíos, por lo que su valor interno es “null”. Tenemos que programar los script teniendo en cuenta los posibles valores “null” de todos los campos que usemos en ellos. Por ejemplo de la siguiente manera:
if (Form.GetValueDecimal("FechaInicio").HasValue && Form.GetValueDecimal("HorasIncremento").HasValue)
return Form.GetValueDecimal("FechaInicio").Value.AddHours(Decimal.ToDouble(Form.GetValueDecimal("HorasIncremento").Value));
else
return null;
Editor de script
Independientemente de dónde usemos el script, el editor de scripting es siempre el mismo, y se muestra a continuación:

En la zona de código (abajo a la izquierda) es en dónde podremos escribir el código asociado a nuestro script. En la imagen anterior aparece un script muy simple, que siempre devuelve “true”. De esta manera si lo asignamos a la propiedad de Visibilidad de un control, éste siempre será visible, pues el script siempre devuelve este valor.
A la derecha tenemos el navegador de propiedades, en el que podemos ver las propiedades Internas, del Formulario, variables globales, así como los estados de nuestro proceso. Al pulsar sobre uno de los elementos, en la zona de código se nos incluirá el nombre interno del elemento seleccionado rodeado de comillas dobles.
Existen una serie de funciones para obtener y establecer el valor de los campos del formulario, para obtener el valor de las variables globales además de una serie de funciones enfocadas al usuario. Estas funciones son:
- Variables globales:
- Global.GetValueString(“CodigoVariable”): Obtiene el valor de la variable global (tipo Texto) con código “CodigoVariable”. Devuelve un valor tipo “string”.
- Global.GetValueDecimal(“CodigoVariable”): Obtiene el valor de la variable global (tipo Númerico) con código “CodigoVariable”. Devuelve un valor tipo “decimal?”.
- Global.GetValueDateTimel(“CodigoVariable”): Obtiene el valor de la variable global (tipo Fecha) con código “CodigoVariable”. Devuelve un valor tipo “DateTime?”.
- Campos del Formulario
- Form.GetValueDecimal(“IdInterno”): Obtiene el valor del campo (tipo Numérico) con Id Interno “IdInterno”. Devuelve un valor tipo “decimal?”.
- Form.GetValueTableDecimal(“IdInterno”): Obtiene el valor del campo (tipo Numérico y debe estar contenido dentro de una tabla) con Id Interno “IdInterno”. Devuelve un valor tipo “List<decimal?>”
- Form.GetValueTableRowDecimal(“IdInterno”, CURRENT_ROW): Obtiene el valor del campo (tipo Numérico y debe estar contenido dentro de una tabla) con Id Interno “IdInterno” para una fila concreta, si se utiliza la constante CURRENT_ROW, nos devolverá el valor de la fila actual, también es posible utilizar un entero (por ejemplo, 0, 1, 2, …) para obtener el valor de la fila que deseemos. Devuelve un valor tipo “decimal?”
- Form.GetValueString(“IdInterno”): Obtiene el valor del campo (tipo Texto, Combo, Radio button y listados personalizados) con Id Interno “IdInterno”. Devuelve un valor tipo “string”.
- Form.GetValueTableString(“IdInterno”): Obtiene el valor del campo (tipo Texto, Combo, Radio button y listados personalizados y deben estar contenidos dentro de una tabla) con Id Interno “IdInterno”. Devuelve un valor tipo “List<string>”.
- Form.GetValueTableRowString(“IdInterno”, CURRENT_ROW): Obtiene el valor del campo (tipo Texto, Combo o Radio button que debe estar contenido dentro de una tabla) con Id Interno “IdInterno” para una fila concreta, si se utiliza la constante CURRENT_ROW, nos devolverá el valor de la fila actual, también es posible utilizar un entero (por ejemplo, 0, 1, 2, …) para obtener el valor de la fila que deseemos. Devuelve un valor tipo “string”
- Form.GetValueListString(“IdInterno”): Obtiene el valor del campo (tipo Checkbox y listas de usuarios) con Id Interno “IdInterno”. Devuelve un valor tipo “List<string>”.
- Form.GetValueTableListString(“IdInterno”): Obtiene el valor del campo (tipo Checkbox y listas de usuarios y deben estar contenidos dentro de una tabla) con Id Interno “IdInterno”. Devuelve un valor tipo “List<List<string>>”.
- Form.GetValueTableRowListString(“IdInterno”, CURRENT_ROW): Obtiene el valor del campo (tipo Checkbox y listas de usuarios y deben estar contenidos dentro de una tabla) con Id Interno “IdInterno” para una fila concreta, si se utiliza la constante CURRENT_ROW, nos devolverá el valor de la fila actual, también es posible utilizar un entero (por ejemplo, 0, 1, 2, …) para obtener el valor de la fila que deseemos. Devuelve un valor tipo “List<string>”.
- Form.GetValueDateTime(“IdInterno”): Obtiene el valor del campo (tipo Fecha) con Id Interno “IdInterno”. Devuelve un valor tipo “DateTime?”.
- Form.GetValueTableDateTime(“IdInterno”): Obtiene el valor del campo (tipo Fecha y debe estar contenido dentro de una tabla) con Id Interno “IdInterno”. Devuelve un valor tipo “List<DateTime?>”.
- Form.GetValueTableRowDateTime(“IdInterno”, CURRENT_ROW): Obtiene el valor del campo (tipo Fecha) con Id Interno “IdInterno” para una fila concreta, si se utiliza la constante CURRENT_ROW, nos devolverá el valor de la fila actual, también es posible utilizar un entero (por ejemplo, 0, 1, 2, …) para obtener el valor de la fila que deseemos. Devuelve un valor tipo “DateTime?”.
- Form.GetCustomListPropertyValueString(“IdInterno”, “CódigoPropiedad”): Sólo puede ser usado en campos calculados. Devuelve el valor de la propiedad “CodidoPropiedad” para el valor del listado seleccionado. IdInterno debe ser un campo tipo listado personalizado.
- Form.GetValueObject(“IdInterno”): Devuelve un objecto con las propiedades “Code”, y “Value” con el valor de un campo tipo Combo, Radio button, listado personalizado, check box o listado de usuarios. Estas propiedades contendrán lo siguiente según el tipo de campo:
- Combo, Radio button y Check Box: El código y el valor correspondiente
- Lista de usuarios: El upn (Code) y el nombre del usuario (Value)
- Listado personalizado:
- Form.GetValueTableObject(“IdInterno”): Al igual que la función Form.GetValueObject(“IdInterno”) pero para campos que están dentro de una tabla, devolviendo en este caso un listado de resultados “Code”, “Value”
- Form.GetValueTableRowObject(“IdInterno”, “row”): Al igual que la función Form.GetValueObject(“IdInterno”) pero para campos que están dentro de una tabla y especificando una fila concreta, devolviendo en este caso un único resultado “Code”, “Value”
- Form.SetValueDecimal(“IdInterno”, “nuevoValor”): Guarda el valor “nuevoValor” en el campo (tipo Numérico) con Id Interno “IdInterno”. El valor enviado debe ser del tipo “decimal?”.
- Form.SetValueString(“IdInterno”, “nuevoValor”): Guarda el valor “nuevoValor” en el campo (tipo Texto, Combo, Radio button y listados personalizados) con Id Interno “IdInterno”. El valor enviado debe ser del tipo “string”. En caso de ser un listado personalizado con jerarquía, esta debe ir separada por el carácter /
- Form.SetValueDateTime(“IdInterno”, “nuevoValor”): Guarda el valor “nuevoValor” en el campo (tipo Fecha) con Id Interno “IdInterno”. El valor enviado debe ser del tipo “DateTime?”.
- Form.SetValueListString(“IdInterno”, “nuevoValor”): Guarda el valor “nuevoValor” en el campo (tipo Checkbox y listas de usuarios) con Id Interno “IdInterno”. El valor enviado debe ser del tipo “List<string>”.
- Funciones de usuario:
- User.IsRequester(): Devuelve true si el usuario actual es el solicitante
- User.IsKnower(): Devuelve true si el usuario actual es conocedor en la solicitud
- User.IsApprover(): Devuelve true si el usuario actual es aprobador en la solicitud
- User.BelongsToGroup(“groupName”): Devuelve true si el usuario actual pertenece al grupo “groupName”
- User.BelongsToUserList(“userListCode”): Devuelve true si el usuario actual pertenece al listado de usuarios con Código: “userListCode”
- User.BelongsToPosition(“positionCode”): Devuelve true si el usuario actual pertenece a la posición con Código: “positionCode”
- User.UsersInRequestPosition(“positionCode”): Devuelve una lista de objetos tipo Usuario con los usuarios que se corresponden a la posición con código “positionCode” en la solicitud. Este objeto usuario se compone de las propiedades: “ObjectId”, “Name”, “Upn” y “Email”
Debemos tener en cuenta que Modela validará que el campo o variable global usados en el método sea el correspondiente a dicho método así como el valor enviado y el devuelto, no permitiendo, por ejemplo usar “Form.GetValueDateTime” y pasar como Id Interno el de un campo tipo texto.
Además al ser funciones tipo “dinamyc” de .NET no es posible comprobar directamente la propiedad “HasValue” ya que da error de ejecución, aunque la compilación es correcta. Por lo que en vez de usar, por ejemplo “Form.GetValueDecimal(“CAMPO_FECHA”).HasValue” debemos usar “DateTime? fecha = Form.GetValueDecimal(“CAMPO_FECHA”); if (fecha.HasValue){}”
Librerías
Hay una serie de librerías agregadas por defecto como “using” a los scripts, estas librerías son:
- Scripts asociados a campos (visibilidad, editabilidad, cálculos, . ..)
- System.Collections.Generic
- System.Collections
- System.Linq
- Newtonsoft.Json
- Scripts asociados a un nodo tipo script (además de las anteriores):
- System.Net.Http
- System.Net.Http.Headers
Además debemos tener en cuenta que Modela no nos permite utilizar las siguientes librerías:
- System.IO
- System.Environment
- System.AppDomain
Code Snippets
A continuación se muestran una serie de fragmentos de código útiles que puedes usar para personalizar tus procesos.
Comportamiento. Basado en la opción seleccionada de un “Radio Button”
Devuelve True en el caso de que en el Radio Button indicado hayamos seleccionado la opción con código “CODE_OPTION1”.
return (Form.GetValueString("INTERNAL_ID_RADIO_BUTTON") == "CODE_OPTION1");
Comportamiento. Basado en el estado personalizado de un proceso
Devuelve True en el caso de que el estado personalizado del proceso sea “DEV”.
return (Form.GetValueString("REQUEST_STATUS") == "DEV");
Comportamiento. Basado en las opciones seleccionadas de un Campo “Check box”
Devuelve True si en el campo se ha seleccionado la opción “CODE_OPTION1” o “CODE_OPTION2”.
return (Form.GetValueListString("INTERNAL_ID_CHECK_BOX").Contains("CODE_OPTION1") || Form.GetValueListString("INTERNAL_ID_CHECK_BOX").Contains("CODE_OPTION2"));
Comportamiento. Algún elemento de una tabla es menor que 0
Devuelve True si alguna fila de una columna de tipo numérico en una tabla tiene valor menor que cero.
List<decimal?> elementos = Form.GetValueTableDecimal("ElementosCustomField");
return (elementos.Any(x => x < 0));
Comportamiento. Se repiten dos valores en una tabla
Devuelve True si hay 2 filas en una tabla con exactamente los mismo valores
List<string?> elementos = Form.GetValueTableString("ElementosCustomField");
var repetidos = elementos.GroupBy(x => x).Count(y => y.Count() > 1);
return (repetidos > 0 ? true : false);
Comportamiento. Fecha fin es superior a fecha de inicio
Devuelve True si una fecha es superior a otra dada.
if(Form.GetValueDateTime("DateStart").HasValue && Form.GetValueDateTime("DateEnd").HasValue) {
return Form.GetValueDateTime("DateStart") <= Form.GetValueDateTime("DateEnd");
} else {
return false;
}
Comportamiento. Campo tipo DNI contiene un valor válido
Devuelve True si el campo en cuestión contiene un string que representa un DNI de España válido.
string numeroDNI = Form.GetValueString("EmpleadoNIF");
if (String.IsNullOrEmpty(numeroDNI)) return true;
// Verificar la longitud del DNI
if (numeroDNI.Length != 9)
{
return false;
}
// Extraer el número y la letra del DNI
string numeroStr = numeroDNI.Substring(0, 8);
char letra = char.ToUpper(numeroDNI[8]);
// Verificar que el número sea un entero válido
if (!int.TryParse(numeroStr, out int numero))
{
return false;
}
// Calcular la letra correspondiente al número
string letras = "TRWAGMYFPDXBNJZSQVHLCKE";
int indice = numero % 23;
char letraCalculada = letras[indice];
// Comparar la letra calculada con la letra proporcionada
if (letra == letraCalculada)
{
return true;
}
else
{
return false;
}
Campo calculado. Suma 2 campos sin importar si están rellenos
Suma el valor de dos campos; si alguno de ellos no está relleno no se calculará el resultado y el campo calculado será “vacío”.
return (Form.GetValueDecimal("INTERNAL_ID_NUMERIC_FIELD_1") + Form.GetValueDecimal("INTERNAL_ID_NUMERIC_FIELD_2"));
Campo calculado. Suma 2 campos controlando si están vacíos
Suma el valor de dos campos; si uno de ellos no está relleno, se interpreta como un valor 0, de tal manera que el campo calculado resultante siempre tiene algún valor (aunque sea 0 cuando los dos campos de los que depende no están rellenos).
return ((Form.GetValueDecimal("INTERNAL_ID_NUMERIC_FIELD_1").HasValue ? Form.GetValueDecimal("INTERNAL_ID_NUMERIC_FIELD_1") : 0) + (Form.GetValueDecimal("INTERNAL_ID_NUMERIC_FIELD_2").HasValue ? Form.GetValueDecimal("INTERNAL_ID_NUMERIC_FIELD_2") : 0));
Campo calculado. Suma una columna de una tabla
Suma de todos los valores numéricos de una columna de una tabla. Si no hay datos o no son numéricos (están vacíos) el resultado es 0.
return (Form.GetValueTableDecimal("INTERNAL_ID_NUMERIC_FIELD") != null ? Form.GetValueTableDecimal("INTERNAL_ID_NUMERIC_FIELD").Sum(s=> s != null ? s : 0) : 0);
Llamada servicio externo.
Llama a una API externa, en este ejemplo se llama a la propia API de Modela para crear una nueva solicitud. Esta nueva solicitud se creará con datos de la solicitud que ejecutará este script en uno de sus pasos.
string body = "";
try
{
// Como queremos guardar el valor de un campo decimal (nv1) en la nueva solicitud, obtenemos su valor teniendo en cuenta que puede ser null
decimal? decValue = 0;
if (Form.GetValueDecimal("nv1")!=null)
decValue = Form.GetValueDecimal("nv1");
// Montamos el objeto con los valores de los campos, el JSON a enviar se obtiene de la URL:
// HOST/public/external/process/CODIGO_PROCESO/getexternalrequestjson -> Devuelve el JSON de la versión publica del CODIGO_PROCESO
body = @"{
""processCode"": ""COD_PROCESO"", // Código del proceso en el que queremos crear la solicitud
""objectIdOwner"": ""ID_CREADOR"", // ID Azure del usuario creador, este usuario debe existir en Modela
""companyCode"": ""COD_SOCIEDAD"", // Código de la sociedad de la solicitud
""subject"": ""Asunto origen: " + Form.GetValueString("REQUEST_SUBJECT") + @""", // Para el asunto usamos el asunto de la solicitud origen precedido de: "Asunto origen: "
""summary"": ""Resumen origen: " + Form.GetValueString("REQUEST_SUMMARY").Replace("\n","\\n") + @""", // Para el resumen usamos el resumen de la solicitud origen precedido de: "Resumen origen: ", teniendo en cuenta que este campo es multilínea
""formField"": [
{
""idInternal"": ""n1"",
""value"": [{""value"": "+ decValue.Value.ToString(System.Globalization.CultureInfo.InvariantCulture) +@"}] // Al campo numérico de la solicitud generada con ID "n1" le asignamos la variable decimal declarada al principio
},
{
""idInternal"": ""t1"",
""value"": [{""value"": """+ Form.GetValueString("campotexto1")?.Replace("\n","\\n") +@"""}] // Al campo texto de la solicitud generada con ID "t1" le asignamos el valor del campo origen "campotexto1" teniendo en cuenta que es multilinea
},
{
""idInternal"": ""solorigen"",
""value"": [{""value"": """ + Form.GetValueString("REQUEST_CODE") + @"""}] // Al campo "Link a proceso" de la solicitud generada con ID "solorigen" le asignamos el código de la solicitud que lanza el script
}
]
}";
// URL de creación de solicitud, la obtenemos de la variable global URL_MODELA
string url = Global.GetValueString("URL_MODELA") + "public/external/request/create";
// Token de autorización, lo obtenemos de la variable global TOKEN_CREACION
string authorization=Global.GetValueString("TOKEN_CREACION");
// Creamos el objeto para realizar la petición
var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, url);
// Añadimos el token a la cabecera
request.Headers.Add("token", authorization);
// Creamos el content de la llamada con el body anterior
var content = new StringContent(body, null, "application/json");
request.Content = content;
// Realizamos la llamada y gestionamos la respuesta
var response = client.SendAsync(request).Result;
response.EnsureSuccessStatusCode();
return response.Content.ReadAsStringAsync().Result;
}
catch (System.Exception e)
{
throw new System.Exception("Error send next body: " + body,e);
}