Справка

Архитектура

Облачный Pyrus
Безоблачный Pyrus

Скрипт представляет собой исполняемый JavaScript-код, который подписывается на события изменения полей формы и определяет обработчики этих событий. В контексте исполнения скрипта доступен глобальный объект form, предоставляющий методы для обработки событий, загрузки данных и другие вспомогательные методы.

Подписка на изменение полей осуществляется цепочкой вызовов типа form.onChange().action(). Например:

/* Задать значение поля */
form.onChange(/*...*/).setValue(/*...*/);

/* Проверить корректность заполнения */
form.onChange(/*...*/).validate(/*...*/);

Цепочки с обработчиками должны быть объявлены на верхнем уровне скрипта, т.е. не могут находиться внутри других функций. Каждый скрипт должен включать как минимум один обработчик событий.

Скрипт запускается (инициализируется) в момент открытия задачи или заполнения новой формы, а объявленные в нем глобальные переменные будут доступны в процессе редактирования формы между вызовами обработчиков.

Далее рассмотрим кратко все доступные методы глобального объекта form.

Метод onChange и обработка событий

Метод onChange глобального объекта form предназначен для подписки на изменение конкретных полей формы.

interface form {
  onChange(
    fieldNames: string[],
    executeOnLoad: boolean = false
  ): ChangeHandler
}

В метод onChange передается 1-2 параметра:

  • fieldNames — массив строк с названиями или кодами полей, на изменение которых необходимо реагировать. Значения этих полей будут доступны в обработчиках.
  • executeOnLoad — опциональный параметр типа boolean (по умолчанию — false), который определяет нужно ли запускать обработчик при открытии формы, т.е. до того как пользователь отредактировал поле.

Поскольку скрипты работают только в браузере и мобильных приложениях, они не запускаются при заполнении формы через Pyrus API или внешний канал интеграции. Поэтому рекомендуется всегда передавать executeOnLoad равным true, чтобы скрипты выполнились при открытии пользователем задачи с уже заполненными полями.

Метод возвращает объект типа ChangeHandler, в котором доступны различные методы-обработчики, которые будут исполняться при изменении полей.

interface ChangeHandler {
  setValue(
    fieldName: string,
    calc: (state: FormState) => FieldValue
  )

  setValueAsync(
    fieldName: string,
    calcAsync: (state: FormState) => Promise<FieldValue>
  )

  setValues(
    fieldNames: string[],
    calc: (state: FormState) => FieldValue[]
  )

  setValuesAsync(
    fieldNames: string[],
    calcAsync: (state: FormState) => Promise<FieldValue[]>
  )

  setAssignee(
    calc: (state: FormState) => number | null
  )

  setAssigneeAsync(
    calcAsync: (state: FormState) => number | null
  )

  validate(
    fieldName: string,
    validate: (state: FormState) => {
        errorMessage: string,
        canApprove?: boolean,
        canSave?: boolean,
        canReject?: boolean
    } | undefined
  )

  validateAsync(
    fieldName: string,
    validateAsync: (state: FormState) => Promise<{
        errorMessage: string,
        canApprove?: boolean,
        canSave?: boolean,
        canReject?: boolean
    } | undefined>
  )

  setFilter(
    fieldName: string,
    calcFilter: (state: FormState) => CatalogFilter | FormFilter
  )

  setFilterAsync(
    fieldName: string,
    calcFilterAsync: (state: FormState) => Promise<CatalogFilter | FormFilter>
  )
}

Все методы имеют простую и async-реализацию. Async-методы отличаются тем, что переданная в них функция-обработчик должна вернуть не результат, а Promise (обещание) с результатом. Это необходимо в том случае, если для вычислений используются асинхронные методы загрузки данных. Пример использования обоих вариантов реализации:

form
  .onChange(['...'], true)
  .setValue('...', state => {
    return someValue;
  });

form
  .onChange(['...'], true)
  .setValueAsync('...', async state => {
    await dataLoad();
    return someValue;
  });

Более подробно про применение методов setValue, setValues, setValueAsync, setValuesAsync, setAssignee и setAssigneeAsync можно прочитать подробнее в статье Редактирование полей. Методы validate, validateAsync описаны в статье Валидация. Методы фильтрации значений полей типа Справочник: setFilter и setFilterAsync — подробнее описаны в статье Фильтрация значений.

Объект состояния формы

В любой из методов-обработчиков необходимо передать функцию, которая принимает первым аргументом состояние формы (аргумент state) и возвращает значение, например:

form
  /*
    При изменении полей «Цена» или «Количество»,
    а также при открытии формы будет запускаться
    обработчик.
  */
  .onChange(['Цена', 'Количество'], true)
  .setValue('Общая сумма', state => {
    const [price, quantity] = state.changes;

    if (!price || !quantity)
        return null;

    return price.value * quantity.value;
  });

Аргумент state — это объект типа FormState, описание которого представлено ниже:

interface FormState {
  changes: FieldValue[]
  prev?: FieldValue[]
  commenter: Person
  assignee: Person | undefined
  currentStep: number
  taskId?: number
}

Доступные поля объекта:

  • changes — массив значений полей, имена которых были переданы в метод onChange. Значения приходят в том же порядке, в котором поля были перечислены при вызове onChange;
  • prev — массив текущих значений вычисляемых полей.
  • commenter — пользователь, который редактирует форму;
  • assignee — ответственный по задаче, есть указан;
  • currentStep — текущий этап задачи. При заполнении новой формы — 0;
  • taskId — номер текущей задачи. При заполнении новой формы не передается.

Методы загрузки данных

Скрипты могут загружать для расчетов дополнительные данные: реестры форм, элементы справочников, списки ролей. Для этого в глобальном объекте form доступны следующие методы:

  • fetchSelfRegister;
  • fetchRegister;
  • getCatalog;
  • fetchRoles.

Уровни доступа к данным при использовании этих методов определяются уровнями доступа пользователя, у которого запускается скрипт.

Методы загрузки реестра: fetchRegister и fetchSelfRegister — описаны в статье Реестр формы.

Важно: В клиентских скриптах запрещены прямые http-запросы как к Pyrus, так и к сторонним сервисам. В случае необходимости использования внешних сервисов или Pyrus API для загрузки данных рассмотрите использование серверных скриптов или обычных ботов.

Загрузка справочников

interface form {
  getCatalog(
    catalogName: string | number
  ): Promise<CatalogItem[]>;
}

interface CatalogItem {
  item_id: number;
  columns: {
    [name: string]: string;
  };
}

Метод getCatalog принимает первым аргументом название (строку) или ID (число) справочника и возвращает Promise с массивом элементов справочника. Каждый элемент справочника представляет собой объект с полями:

  • item_id — уникальный id элемента справочника;
  • columns — объект, в котором ключи соответствуют названиям колонок справочника, а значения — значениям колонок.

Подсказка: в getCatalog необходимо передавать названием именно справочника, а не поля в форме. Например, в форме может использоваться поле типа Справочник под названием Страны и регионы, к которому привязан сам справочник Страны. В этом случает скрипт должен обращаться к объекту под названием Страны.

Загрузка ролей

Метод fetchRoles позволяет получить список ролей пользователя или полный список ролей организации.

interface form {
  fetchRoles(personId?: number): Promise<RoleItem[]>;
}

interface RoleItem {
  role_id: number,
  role_name: string,
  members_ids: number[],
}

Аргументом метода является необязательный параметр personId (число) — если не передан, то вернется список всех ролей. Метод возвращает Promise с массивом объектов-ролей. Для каждой роли доступны поля:

  • role_id — id роли;
  • role_name — название роли;
  • member_ids — id всех пользователей-участников роли.

Вспомогательные методы

Метод getDaysCount

interface form {
  getDaysCount(
    startDate: Date | string,
    endDate: Date | string,
    options?: {
      exclude: "nonWorkingHoliday" | "nonWorking"
      calendar: "ru" | "kz" | "us"
    }
  ): number
}

Метод getDaysCount используется для получения количества дней между датами согласно рабочему календарю. По умолчанию используются настройки организации или пользователя, но при желании можно указать опции через необязательный аргумент options.

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

form.onChange(['Даты отпуска']).setValue('Количество дней', state => {
  const [due] = state.changes;
  return form.getDaysCount(due.start_date, due.end_date, {
    exclude: 'nonWorkingHoliday',
    calendar: 'ru'
  });
});

Метод getDateWithTimezoneOffset

interface FormProxy {
  getDateWithTimezoneOffset(date: string): Date;
}

функция getDateWithTimezoneOffset преобразует переданное значение поля типа Дата в значение, в котором учтено смещение по часовому поясу.

Была ли эта статья полезной?