Работа с приложениями

Доступ к приложению можно получить несколькими способами:

Создание

Приложения позволяют создавать новые элементы. Например, если в контексте процесса есть поле order типа Приложение, то создать новый элемент этого приложения можно следующим образом:

const order = Context.fields.order.app.create();
order.data.client = Context.data.client;
await order.save();
Context.data.order = order;

Получение по ссылке

Если в контексте процесса, приложения или виджета есть поле типа Приложение, то в его значении будет находиться ссылка на этот объект. Для получения самого объекта необходимо его запросить с помощью метода ApplicationItemRef.fetch:

const order = await Context.data.order.fetch();

Если поле множественное, то можно запросить сразу все связанные объекты с помощью метода ApplicationField.fetchAll:

const orders = await Context.fields.orders.fetchAll();

Аналогичным образом, если каким-то способом получен элемент приложения, ссылающийся на другое приложение, можно получить элементы, на которые он ссылается:

const order = await Context.data.order.fetch();
const client = await order.data.client.fetch();

Получение текущего статуса элемента

Получить текущий статус элемента приложения можно следующим образом:

const currentStatus = item.data.__status;
Context.data.__comment = `Текущий статус: ${ currentStatus.name }`;

Изменить статус приложения можно с помощью метода ApplicationItem.setStatus.

Поиск

Для выборки элементов коллекций используется объект Search, который можно получить из описания приложения:

const pinnedClients = await Context.fields.client.app.search()
    .where(f => f.agent.eq(Context.data.__createdBy))
    .all();

С помощью объекта Search можно запросить:

  • количество результатов по фильтру Search.count;
  • первый результат по фильтру Search.first;
  • страницу результатов по фильтру Search.all.

Для постраничного запроса можно использовать следующие методы:

  • Search.from для задания количества элементов, которое необходимо пропустить от начала;
  • Search.size для задания количества элементов на странице (максимально допустимый размер страницы — 10000, по умолчанию — 10);
  • Search.sort для задания сортировки выборки.

Фильтр можно задать через метод Search.where. Фильтр представляет из себя функцию-замыкание, в которую передаются два аргумента:

  • f — набор фильтров, с помощью которых можно задать условия по отдельным полям объектов;
  • g — глобальные функции фильтров для полнотекстового поиска и создания сложных фильтров GlobalFilters.

Например, если мы хотим выбрать заказы от определённого клиента, название которых начинается на «аб» и которые созданы меньше недели назад, то получится следующий запрос:

const weekAgo = (new Datetime()).addDate(0, 0, -7);
const orders = await Application.search()
    .where((f, g) => g.and(
        f.client.eq(Context.data.client),
        f.__name.like('аб%'),
        f.__createdAt.gt(weekAgo),
    ))
    .all();

Если необходимо выбрать заказы по определенному статусу (например, «выполненные»), то получится следующий запрос:

const doneStatus = Application.fields.__status.variants.done;
const orders = await Application.search()
     .where(f => f.__status.eq(doneStatus))
     .all();

Для получения элементов приложения, ссылающихся на элемент другого приложения, можно использовать следующий запрос:

const clients = await Application.search()
     .where(f => f.client.link(Context.data.order)
     .all();

Объект f формируется по описанию полей приложения. Для множественных полей формируется FieldArrayOperand; для полей, расширяющих строку (TString, TPhone, TEmail, TFullName, TLink и TEnum), — StringFieldOperand; для остальных — FieldOperand.

Фильтрация для поля типа Приложение

Если необходимо ограничить выбор элементов в поле типа Приложение, можно использовать метод [[StaticApplicationField.setFilter]]:

 Context.fields.cars.data.setFilter((f, c, g) => g.and(
     f.active.eq(true),
     f.year_of_issue.gte(c.year)
 ));;

Фильтр представляет из себя функцию-замыкание, в которую передаются три аргумента. Первый f и третий g соответствуют тому, что передается в функцию-замыкание метода Search.where (описан выше), а второй аргумент c — набор операндов, с помощью которых можно получить значение из контекста.

Фильтр применяется на уровне всего поля, а не для текущей формы или бизнес-процесса. Если фильтр больше не требуется, его нужно очистить с помощью метода [[ApplicationField.clearFilter]].

Context.fields.cars.data.clearFilter();

Изменение

Получив элемент приложения, можно изменять значения его полей. После этого необходимо вызвать метод ApplicationItem.save, чтобы сохранить изменения:

const order = await Context.data.order.fetch();
// Применим скидку
order.data.total = order.data.total.multiply(0.75);
await order.save();

Удаление

У элементов приложений есть метод ApplicationItem.delete, с помощью которого элемент помечается как удалённый (заполняется его дата удаления BaseItemData.__deletedAt). При этом к нему можно получить доступ по ссылке и при поиске. Также для удаления элемента не обязательно получать весь объект — достаточно ссылки.

await Context.data.order.delete();
Context.data.order = undefined;

Изменение статуса

У элементов приложений есть метод ApplicationItem.setStatus, с помощью которого можно изменить статус:

const request = await Context.data.request.fetch();
const finalStatus = request.fields.__status.variants.completed;
await request.setStatus(finalStatus, "Закрыт автоматически");

Запуск процесса

У объекта Application есть доступ к списку процессов Application.processes. Через него можно запустить процесс. Процессы запускаются асинхронно. Это значит, что процесс ещё не будет запущен на момент выполнения функции run.

Например, можем в цикле запустить процесс обработки заявки на обслуживание для всех заявок в контексте:

for(let request of Context.data.service_requests) {
    await Application.processes.process_code.run({
        service_request: request
    });
}

Подробнее о работе с процессами можно прочитать в описании Process.

Получение элементов одной коллекции/приложения одним запросом

Допустим, мы получили элементы какого-либо приложения, в котором есть поля типа Пользователь, и нам нужно получить данные этих пользователей. До оптимизации в сценарии будет последовательно выполнено N+1 запросов:

const teams = await Namespace.app.komandy.search(...).all();
for (const team of teams) {
       const lead = await team.data.lead.fetch();
       const name = lead.data.__name;
       ...
}

После оптимизации будет выполнено последовательно два запроса:

const teams = await Namespace.app.komandy.search(...).all();

// Получаем идентификаторы пользователей
let teamLeadIds = teams.map(t => t.data.lead.id);

 // Удаление дубликатов идентификаторов пользователей
teamLeadIds = teamLeadIds.filter(function(elem, index, self) {
    return index === self.indexOf(elem);
});

 // Получаем всех пользователей одним запросом
const users = await System.users.search().where(f => f.__id.in(teamLeadIds)).size(10000).all();

// Формируем объект вида {ID_пользователя: ФИО_пользователя}
const namesById: { [key: string]: string } = {};
for (const user of users) {
       namesById[user.id] = user.data.__name;
}