Скриптовая задача

Задача, запускающая скрипт (т.е. последовательность действий) или программный код, который выполняется автоматически.

Атрибуты и форма

Используется язык JavaScript.

Укажите Имя

../../../../../_images/60.png

Укажите скрипт

../../../../../_images/61.png
Настройки асинхронности:
- Асинхронно «перед»- исполнение доходит до точки перед блоком, транзакция комитится и дальнейшее выполнение ставится в очередь, которую разбирает уже джоба
- Асинхронно «после» - исполнение доходит до точки после блока, транзакция комитится и дальнейшее выполнение ставится в очередь, которую разбирает уже джоба
См. подробнее о асинхронных задачах
../../../../../_images/62.png

Важно

При сохранении, сохранении/публикации процесса проверяется обязательность заполнения следующих полей:

  • «Скрипт»

Иначе в линтере будет выдана ошибка.

Доступные переменные

Переменные процесса

Во время выполнения скриптов доступны все переменные процесса, видимые в текущей области.

// someVar - переменная процесса
print("someVar: " + someVar);

documentRef - строковое представление entityRef документа
documentType - id типа документа
lastTaskCompletor - username пользователя, который завершил последнюю задачу
comment - комментарий из последней завершенной задачи
workflowInitiator - username инициатора БП

Execution

execution - переменная, которая всегда доступна, если скрипт выполняется в области выполнения (например, в Script Task). (DelegateExecution)

// Получение переменной процесса
var sum = execution.getVariable('x');

// Установление переменной процесса
execution.setVariable('y', x + 15);

Document

document - является скриптовым представлением документа AttValueScriptCtx , по которому идет БП.

// Получение атрибута документа
var created = document.load("_created");

// Установление атрибуту документа указанного значения
document.att("firArchiveBoxNumber", 123);
// Сохранение
document.save();

// Сброс состояния документа, если ранее были внесены изменения через att()
document.att("firArchiveBoxNumber", 123);
document.reset();

Records

Records - это сервис, который предоставляет доступ к функциям работы с рекордами RecordsScriptService.

// Получение скриптового представление указанного рекорда
var doc = Records.get("emodel/doc@111");

// Query рекордов
var queryCommentsResult = Records.query({
    sourceId: "emodel/comment",
    language: "predicate",
    query: {
        a: "record",
        t: "eq",
        v: "emodel/doc@123"
    }
}, {
    text: "text",
    created: "_created"
});

var firstComment = queryCommentsResult.records[0];
var text = firstComment.text;
var created = firstComment.created;

print("comment: " + text + " created on " + created);

ECOS Config

Config - предоставляет доступ к Конфигурации Citeck по ключу в формате <область>$<идентификатор>.

  • get(key: String): DataValue - получение значения по ключу

  • getOrDefault(key: String, defaultValue: Any): DataValue - получение значения по ключу, если значение не найдено, то возвращается значение по умолчанию

  • getNotNull(key: String): DataValue - получение значения по ключу, если значение null, то выбрасывается исключение

// Получение значения конфигурации по ключу и приведение к типу String
var serviceDeskEmailFrom = Config.get("app/service-desk$send-sd-email-from").asText()

DataValue

DataValue - объект, позволяющий сконвертировать данные в структуру BpmnDataValue для удобной работы с json представлением, это позволяет безопасно обращаться к полям, получать значения по умолчанию, приводить к нужному типу, сохранять данные в execution и многое другое, подробнее см. методы класса.

  • DataValue.of(content: Any?) - создает объект DataValue из любого объекта, если объект не может быть сконвертирован в DataValue, то возвращается пустой объект DataValue.

  • DataValue.createObj() - создает пустой объект DataValue.

  • DataValue.createArr() - создает пустой массив DataValue.

  • DataValue.createStr(value: Any?) - создает строковое представление переданного значения.

Пример использования:

var event = DataValue.of(someExampleEventStructure);

print("---HELLO FROM SCRIPT---");


print("event id from base: " + event.get("_meta").get("id"));
print("event id from $: " + event.get("$._meta.id"));
print("event id from JsonPointer: " + event.get("/_meta/id"));

print("event time as instant: " + event.get("/_meta/time").takeAsInstant());
print("event field names list: " + event.fieldNamesList());

print("call undefined prop is safe: " + event.get("/_meta/a/b/c/"));

print("event id is boolean " + event.get("_meta").get("id").isBoolean());


print("-------END--------------");

DataValue может быть сохранен в execution процесса с последующим извлечением и использованием.

Сохраняем в execution:

var arr = ["a", "b"];
var obj = {
  a: "b"
}

var dArr = DataValue.of(arr);
var dObj = DataValue.of(obj);

execution.setVariable("dArr", dArr);
execution.setVariable("dObj", dObj);

Обращаемся к сохраненным в execution переменным в другом скрипте

print("----------");

print("dArr: " + dArr);
print("dArr 0: " + dArr.get("0"));

print("dObj: " + dObj);
print("dObj a: " + dObj.get("a"));

print("----------");

Результат:

----------
dArr: {"0":"a","1":"b"}
dArr 0: "a"
dObj: {"a":"b"}
dObj a: "b"
----------

Time и Duration. Работа с датами и временными интервалами

При работе с датами и временными интервалами рекомендуется использовать объекты Time и Duration.

Time - объект, упрощающий работу с датами, позволяющий сохранять дату в execution процесса, добавлять к дате календарное/рабочее время на основе рабочего расписания, получать разницу между двумя датами и многое другое. Подробнее см. примеры.

Duration - объект, упрощающий работу с временными интервалами. Поддерживается сохранение в execution процесса.

Примеры использования:

// Получение текущего времени
let now = Time.now();

// Получение времени из строки в формате ISO
let timeFromIsoString = Time.of("2023-02-07T15:00:00.0Z");

// Получение времени из миллисекунд с начала 1970-01-01T00:00:00Z.
let timeFromEpochMilli = Time.ofEpochMilli(1644236400000);

// Получение миллисекунд с начала 1970-01-01T00:00:00Z.
let timeToEpochMilli = timeFromIsoString.toEpochMilli();



// Создание объектов Time
let aprilFirst = Time.of("2023-04-01T00:00:00.0Z");
let mayFirst = Time.of("2023-05-01T00:00:00.0Z");
let mayFirst2 = Time.of("2023-05-01T00:00:00.0Z");

// Сравнение времени
let isBefore = aprilFirst.isBefore(mayFirst); // true
let isAfter = aprilFirst.isAfter(mayFirst); // false

let isBeforeOrEqual = aprilFirst.isBeforeOrEqual(mayFirst); // true
let isAfterOrEqual = mayFirst.isAfterOrEqual(mayFirst2); // true



// Создание объектов Time
let timeFirst = Time.of("2023-04-01T00:00:00.0Z");
let timeSecond = Time.of("2023-04-01T00:30:00.0Z");

// Получение разницы между двух точек времени в Duration
let durationBetween = Time.durationBetween(timeFirst, timeSecond); // PT30M
let durationAsString = durationBetween.toString(); // "30m"
let durationAsIsoString = durationBetween.toIsoString(); // "PT30M"

// Получение разницы между двумя точками времени в минутах
let durationBetweenMinutes = Time.durationBetween(timeFirst, timeSecond).inWholeMinutes(); // 30
// etc.



// Объект Time можно сохранять в execution процесса
execution.setVariable("time", now);
// Можно получить объект Time из execution процесса
let timeFromExecution = execution.getVariable("time");

// Объект Duration можно сохранять в execution процесса
execution.setVariable("duration", durationBetween);
// Можно получить объект Duration из execution процесса
let durationFromExecution = execution.getVariable("duration");



let someTime = Time.now();

// Добавление 1 часа к указанному времени
let plus1h = Time.plus(someTime, "PT1H");

// Вычитание 1 часа из указанного времени
let minus1h = Time.minus(someTime, "PT1H");

// Добавление 10 минут к текущему времени
let nowPlus10m = Time.nowPlus("PT10M");

// Вычитание 10 минут из текущего времени
let nowMinus10m = Time.nowMinus("PT10M");



// Добавление 1 часа 30 минут рабочего времени к указанному. Используется рабочее расписание по умолчанию
let plusWorkingTime1h30m = Time.plusWorkingTime(someTime, "PT1H30M");

// Добавление 1 часа 30 минут рабочего времени к указанному. Можно указать id рабочего расписания
let plusWorkingTime1h30mScheduleId = Time.plusWorkingTime(someTime, "PT1H30M", "DEFAULT");

// Добавление 8 часов рабочего времени к текущему времени. Используется рабочее расписание по умолчанию
let nowPlusWorkingTime = Time.nowPlusWorkingTime("PT8H");

// Добавление 8 часов рабочего времени к текущему времени. Можно указать id рабочего расписания
let nowPlusWorkingTimeWithScheduleId = Time.nowPlusWorkingTime("PT8H", "DEFAULT");

Предупреждение

Сохранение объекта javascript Date в execution процесса не поддерживается. Для работы с датами и временными интервалами используйте объекты Time и Duration.

WebUrl

webUrl - переменная возвращает настроенный веб url сервера

Tasks

tasks - сервис для манипуляций над задачами.

  • completeActiveTasks(execution: DelegateExecution) - завершает все активные задачи по инстансу процесса из [DelegateExecution.getProcessInstanceId]. Задачи завершаются с результатом defaultDone: Выполнено.

Templated content

templatedContent - сервис для работы с шаблонизированным контентом.

  • write(record: String, template: String) - генеририрует контент по указанному шаблону [template], на основе данных рекорда [record] и записывает сгенерированный контент в [record] в атрибут контент по умолчанию content.

  • write(record: String, template: String, attribute: String) - генеририрует контент по указанному шаблону [template], на основе данных рекорда [record] и записывает сгенерированный контент в [record] в переданный атрибут [attribute].

В качестве [template] можно передать строковое представление EntityRef шаблона или его id.

Events

events - сервис для работы с событиями Citeck.

  • send(type: String, data: BpmnDataValue) - отправляет событие с указанным типом (именем) и данными.

Например, можно отправить Citeck событие через скрипт:

var data = DataValue.of({
  foo: "bar",
  number: 123
});

events.send("test-topic", data);

И подписаться на него в bpmn event через ручную настройку с «test-topic» или программно через слушателя:

eventsService.addListener<ObjectData> {
  withEventType("test-topic")
  withDataClass(ObjectData::class.java)
  withTransactional(true)
  withAttributes(
    mapOf("foo" to "foo", "itsNum" to "number")
  )
  withAction { event ->
    log.("event received: $event")
  }
}

Logger

log - логгер, пишет в микросервис ecos-process, дополнительно выводится информация о execution. Для настройки уровня логирования используется класс ru.citeck.ecos.process.domain.bpmn.engine.camunda.services.beans.ScriptLogger.
Поддерживаемые методы:

  • log.info(message: String)

  • log.warn(message: String)

  • log.error(message: String)

  • log.debug(message: String)

  • log.trace(message: String)

Примечание

Читай подробнее о scripting в Camunda

Примеры

Скрипт, как в атрибут записать человека, который выполнил предыдущую задачу

document.att("manager", "emodel/person@" + lastTaskCompletor);
document.save();
../../../../../_images/sample_011.png

Получить локальную часть глобальной ссылки на сущность

document.load("requestCategory?localId") == "community"
  • requestCategory - ассоциация

  • ?localId - скаляр из Records API, который возвращает локальную часть глобальной ссылки на сущность. Например для emodel/person@someuser локальная часть - это «someuser»

Скрипт для проверки наличия комментариев после завершения задачи

if (!comment) {
    throw new Error("Комментарий не заполнен");
}

В эту переменную информация не пишется, если строка пустая.

Пример вычисления id рабочего расписания с использованием DMN

[TBD]