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

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

Создание

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

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

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

Если в контексте процесса, приложения или виджета есть поле типа Приложение, то в его значении будет находиться ссылка на этот объект. Для получения самого объекта необходимо его запросить с помощью метода [[Item.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 — набор операндов с помощью которых можно получить значение из контекста.

Фильтр применяется на уровне всего поля, и не для текущей формы или бизнес-процесса. Если фильтр больше не требуется, его нужно очистить с помощью метода [[StaticApplicationField.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 status = Application.fields.__status.variants.progress
const item = Application.create();
await item.setStatus(status);
await item.save();

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

У объекта 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;
       ...
}

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

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;
}