Примеры скриптов
- Скопировать значение одного поля в другое
- Вычислить сумму (разницу, процент) нескольких полей
- Вывести значение колонки справочника
- Выбор значения из справочника по полю с типом Контакт
- Перенос крайнего срока в зависимости от приоритета
- Установка срока за N дней до указанной даты
- Автоматическая смена статусов задач при редактировании полей
- Автоматическое заполнение срока SLA
- Фильтрация справочника
- Установка даты в рабочих днях
- Фильтрация поля типа Форма
- Валидация: дата окончания не раньше даты начала
- Валидация: заполнено хотя бы одно поле
- Сделать поле невидимым
- Сделать поле невидимым, начиная с определённого этапа
- Скрываем поле, если письмо пришло не от заданного адреса
- Подсчёт дней между датами
- Подсчёт суммы в строках таблицы и скидки
- Фильтрация полей в зависимости от поля типа Галочка
- Фильтрация полей по типу Форма
- Проверяем правильность заполнения текстового поля
- Запрет удаления строк в таблице
- Ограничение на добавление файлов определённого типа
- Отображение полей в зависимости от условий
- Проверка оставшихся дней отпуска
Скопировать значение одного поля в другое
Копируем значение из поля Поле-источник в поле Текстовое поле.
form.onChange(['Поле-источник'], true) .setValue('Текстовое поле', state => { const [value] = state.changes; if (!value) return null; return value.text; });
Вычислить сумму (разницу, процент) нескольких полей
В этом примере скрипт прибавляет премию сотрудника к его окладу и рассчитывает НДФЛ от полученной суммы. Затем вычитает НДФЛ из этой суммы и выводит итоговую выплату за месяц.
form.onChange(['Оклад', 'Премия']) .setValues(['НДФЛ (13%)', 'Итого к выплате за месяц'], state => { const [salary, bonus] = state.changes; if (!salary || !bonus) return null; const ndflTax = (salary.value + bonus.value) * 0.13; const result = (salary.value + bonus.value) - ndflTax; return [ndflTax, result]; });
Вывести значение колонки справочника
Здесь Справочник — название поля с вашим справочником, Колонка — название колонки справочника, а Текст — поле типа Текст, в котором будет сохранено значение.
form.onChange(['Справочник']) .setValue('Текст', state => { const [item] = state.changes; if (!item || !item.columns) return null; return item.columns['Колонка']; });
Выбор значения из справочника по полю с типом Контакт
Иногда требуется подставлять в заявку ФИО сотрудника в родительном падеже. Простой пример — заявление на отпуск. Чтобы Pyrus делал это автоматически, создаём справочник со списком сотрудников и колонкой с их ФИО в родительном падеже. И подключаем к форме такой скрипт:
const catalogPromise = form.getCatalog(ID_справочника); form.onChange(['Отпускник']) .setValueAsync('От (ФИО):', async state => { const [contactField] = state.changes; const catalogItems = await catalogPromise; if (!catalogItems || !contactField || !contactField.person_id) return null; const filteredRow = catalogItems .find(item => item.columns['Сотрудник'] == `${contactField.first_name} ${contactField.last_name}`); return filteredRow ? filteredRow.columns['ФИО Род'] : null; });
В этом примере:
- Отпускник — название поля типа Контакт;
- Сотрудник и ФИО Род — название колонок справочника, в котором мы ищем значение по выбранному полю;
- От (ФИО): - поле типа Текст, в котором сохраняем найденное значение из справочника.
Валидация: дата окончания не раньше даты начала
form.onChange(['Дата начала', 'Дата окончания']) .validate('Дата окончания', state => { const [start, end] = state.changes; if (!start || !end) return null; if (start.date && end.date && start.date >= end.date) return { errorMessage: 'Не может быть раньше даты начала' }; return null; });
Валидация: заполнено хотя бы одно поле
form.onChange(['Эл. почта', 'Телефон'], true) .validate('Эл. почта', state => { const [email, phone] = state.changes; const emailIsEmpty = !email || !email.text; const phoneIsEmpty = !phone || !phone.text; if (emailIsEmpty && phoneIsEmpty) return { errorMessage: 'Заполните электронную почту или телефон' }; return null; });
Перенос крайнего срока в зависимости от приоритета
C возможностью установки произвольного срока.
form.onChange(['Высокий приоритет', 'Перенос даты']) .setValue('Срок', state => { const [checkmark, customDue] = state.changes; if (customDue && customDue.date) return {date: customDue.date}; if (!checkmark) return null; if (checkmark.checked) return {days_from_create: 1}; return {days_from_create: 3}; });
Установка срока за N дней до указанной даты
В этом примере в зависимости от указанной при заполнении даты срок будет автоматически устанавливаться за семь дней до этой даты.
form.onChange(['Дата']) .setValues(['Срок'], state => { const [dateValue] = state.changes; if (!dateValue) return null; const dueDate = new Date(dateValue.date + 'T10:00:00Z'); const daysBeforeDate = 7; dueDate.setDate(dueDate.getDate() - daysBeforeDate); return [{date: dueDate.toDateString()}]; });
Автоматическая смена статусов задачи при редактировании полей
Скрипт подойдёт тем, кто в рамках задачи работает с партнёрами. В нашем примере статус задачи будет меняться на «В работе», если партнёр назначил ответственного в поле Ответственный за решение.
// Индивидуальный номер организации, которая редактирует форму. // Чтобы узнать ID, напишите на support@pyrus.com. const orgId = 111111; form.onChange([], true) .setStatus(state => { if (state.commenter && state.commenter.organization_id !== orgId) return {choice_name: 'В работе'}; }); form.onChange(['Ответственный за решение']) .setStatus(state => { const [currVal] = state.changes; const [prevVal] = state.prev; if (!prevVal && currVal) return {choice_name: 'В работе'}; });
Автоматическое заполнение срока SLA
Укажите в скрипте своё время решения и названия полей. Вместо числовых констант вы можете использовать переменные или формулы для расчёта времени решения. При изменении в заявке полей Приоритет и Клиент скрипт будет выполняться и устанавливать один из указанных в нём сроков.
form.onChange(['Приоритет', 'Клиент']) .setValue('Срок', state => { const [priority, client] = state.changes; if (!priority || !priority.columns || !client || !client.columns) return null; const tariff = client.columns['Тариф']; const priorityValue = priority.columns['Приоритет']; const hours = getSlaHoursByTariffAndPriority(tariff, priorityValue); if (hours === undefined) return null; return {hours_from_create: hours}; }); function getSlaHoursByTariffAndPriority(tariff, priority) { switch (tariff) { case 'Базовый': switch (priority) { case 'Низкий' : return 64; case 'Средний': return 32; case 'Высокий': return 16; } case 'Стандарт': switch (priority) { case 'Низкий' : return 32; case 'Средний': return 16; case 'Высокий': return 8; } case 'Премиум': switch (priority) { case 'Низкий' : return 16; case 'Средний': return 8; case 'Высокий': return 4; } default: return undefined; } }
Фильтрация справочника
Этот скрипт позволяет фильтровать список городов по выбранному региону. Другой пример — при выборе отдела показывать только статьи расходов, относящиеся к этому отделу.
const catalogPromise = form.getCatalog("Города по регионам"); form.onChange(["Регион"]).setFilterAsync("Город", async state => { const [region] = state.changes; const catalogItems = await catalogPromise; if (!catalogItems || !region || !region.columns) return null; const regionCol = region.columns["Регион"]; const filtered = catalogItems .filter(item => item.columns["Регион"] === regionCol) .map(item => item.columns["Город"]); return filtered.length > 0 ? { values: filtered } : null });
Установка даты в рабочих днях
Этот скрипт позволяет задавать дату с учётом выходных дней. Даты выходных и рабочих дней берутся из справочника.
// Получаем массив дат из справочника "Даты перенос" со столбцами "Даты перенос", "Тип" const catalogPromise = form.getCatalog("Даты перенос").then(items => { if (!items) return null; return { holidayItems: items .filter(item => item.columns["Тип"] == 'Выходной') .map(item => item.columns["Даты перенос"]) .map(date => new Date(date + 'T00:00:00Z')), workingItems: items .filter(item => item.columns["Тип"] == 'Рабочий') .map(item => item.columns["Даты перенос"]) .map(date => new Date(date + 'T00:00:00Z')) } }); // Поиск даты в массиве function containsDate(date, array) { for (let i = 0; i < array.length; i++) { if (date.getTime() == array[i].getTime()) { return true; } } return false; } // Проверить является ли дата выходным днем function isWeekend(date) { return date.getDay() == 0 || date.getDay() == 6; } // Количество и список выходных и праздничных дней form.onChange(['Дата','Дата окончания']) .setValuesAsync(['Список выходных дней', 'Число выходных между датами'], async state => { const [startDateField, endDateField] = state.changes; if (!startDateField || !endDateField) return null; const startDate = new Date(startDateField.date); const endDate = new Date(endDateField.date); const {holidayItems, workingItems} = await catalogPromise; const textArray = []; const monthNames = 'января,февраля,марта,апреля,мая,июня,июля,августа,сентября,октября,ноября,декабря'.split(','); while (startDate <= endDate) { if ((isWeekend(startDate) && !containsDate(startDate, workingItems)) || containsDate(startDate, holidayItems)) { const dateText = startDate.getDate().toString() + ' ' + monthNames[startDate.getMonth()] + ' ' + startDate.getFullYear().toString() + ' г.'; textArray.push(dateText); } startDate.setDate(startDate.getDate() + 1); } let text = ''; if (textArray.length == 1) { text = textArray[0]; } else if (textArray.length > 1) { text = textArray.slice(0, textArray.length - 1).join(', ') + ' и ' + textArray[textArray.length - 1]; } return [text, textArray.length]; }); // Установка даты в рабочих днях form.onChange(['Дата','Кол-во дней']) .setValuesAsync(['Дата + Кол-во рабочих дней'], async state => { const [startDateField, daysNum] = state.changes; if (!startDateField || !daysNum) return null; const startDate = new Date(startDateField.date); const endDate = new Date(startDateField.date); endDate.setDate(endDate.getDate() + daysNum.value); const {holidayItems, workingItems} = await catalogPromise; do { if ((isWeekend(startDate) && !containsDate(startDate, workingItems)) || containsDate(startDate, holidayItems)) { endDate.setDate(endDate.getDate() + 1); } startDate.setDate(startDate.getDate() + 1); } while (startDate <= endDate); return [{date: endDate.toDateString()}]; });
Фильтрация поля типа Форма
В этом примере скрипт позволяет выполнить фильтрацию полей типа Форма, чтобы отсортировать заявку, соответствующую запросу.
form.onChange(["Текст"]).setFilter("Bug 143331863", state => { const [region] = state.changes; if (!region || !region.value) return null; return { filters: [ {fieldCode: "code_field", value: 3}, {fieldName: "name_field", value: "12"} ] } });
Сделать поле невидимым
В этом примере поле Оптовая скидка скрыто, если значение в поле Количество не превышает 99.
form.onChange(['Количество'], true) .setVisibility(['Оптовая скидка'], state => { const [quantity] = state.changes; if (!quantity) return null; const num = quantity.value; return num > 99; });
Сделать поле невидимым, начиная с определённого этапа
В этом примере поле Клиент будет скрыто, начиная со второго этапа.
form.onChange([''], true) .setVisibility(['Клиент'], state => { return state.currentStep < 2; });
Скрываем поле, если письмо пришло не от заданного адреса
Если письмо пришло не от mail@example.com — скрываем поле «Тип заявки».
form.onChange(['Email'], true) .setVisibility(['Тип заявки'], state => { const [email] = state.changes; return email && email.text == 'mail@example.com'; });
Подсчёт дней между датами
Скрипт считает количество дней между указанными датами. Также он показывает, сколько в указанном интервале праздничных дней — для этого нужно использовать отдельный справочник таких дней.
let catalog = null; form.getCatalog("Праздничные дни").then(items => { catalog = items }); form.onChange(['start_date', 'end_date'], true) .setValuesAsync(['days', "holidays"], async state => { const [start, end] = state.changes; if (!start.date || !end.date) return [null, null]; let days = daysBetween( new Date(start.date), new Date(end.date) ) + 1; let hol = 0; const startdate = new Date(start.date); const enddate = new Date(end.date); let catalog_element = null; let catalog_Date = null; for (let i = 0; i < catalog.length; i++) { catalog_element = catalog[i].columns["День"] if (!catalog_element) continue; const day = catalog_element.slice(3,5); const month = catalog_element.slice(0,2); const year = catalog_element.slice(6,10); catalog_Date = new Date(`${day}/${month}/${year}`); console.log(catalog_Date, startdate <= catalog_Date && catalog_Date <= enddate); if (startdate <= catalog_Date && catalog_Date <= enddate) hol += 1; } return [days, hol]; }); function daysBetween(d1, d2) { const msInDay = 1000 * 60 * 60 * 24; return Math.floor((d2.getTime() - d1.getTime()) / msInDay); }
Подсчёт суммы в строках таблицы и скидки
Скрипт считает сумму значений в заполненной таблице и применяет указанную скидку.
form.onChange(["product", "quantity", "discount"]) .setValues(["price_for_one", "total_price"], state => { const [product, quantity, discount] = state.changes; if (!product?.columns) return [null, null]; const discount_rate = discount?.value ? discount.value / 100 : 0; const item_price = product.columns["Цена"] ? Number(product.columns["Цена"]) * (1 - discount_rate) : 0; return quantity?.value ? [item_price, item_price * quantity.value] : [item_price, 0]; });
Фильтрация полей в зависимости от поля типа Галочка
Скрипт показывает скрытые поля типа Справочник», если поставить галочку.
let table_catalog = null; const catalog_id = 200743; form.onChange(["VIP"], true) .setFilterAsync("table", async state => { const [vip] = state.changes; if (!table_catalog) await form.getCatalog(catalog_id ).then(items => table_catalog = items); let filtered = table_catalog.filter(x => x.columns["VIP"] == 0 || vip.checked).map(x => x.columns["id"]); return {values: filtered}; });
Фильтрация полей по типу Форма
Скрипт показывает скрытые поля типа Форма в зависимости от указанного сотрудника.
form.onChange(["manager"], true) .setFilter("contragent", state => { const [manager] = state.changes; if (!manager || !manager.person_id) return null; return { filters: [{ fieldName: "Менеджер", value: manager.person_id }] } });
Проверяем правильность заполнения текстового поля
Скрипт проверяет, чтобы текстовое поле было заполнено только заглавными латинскими буквами.
form.onChange(["fio_text"], true) .validate("fio_text", state => { const [text] = state.changes; if (!text?.text) return null; const reg = new RegExp(/^[A-Z'\s]+$/); if (!reg.test(text.text)) return { errorMessage: "Обнаружены недопустимые символы" }; });
Запрет удаления строк в таблице
Скрипт не даёт удалить заполненные строки таблицы в форме.
let exist_table = null; form.onChange(["no_delete_table"], true) .validate("no_delete_table", state => { const [table] = state.changes; if (state.currentStep == 0) return null; if (!exist_table) { exist_table = []; if (table?.value) { for (let i in table.value) { exist_table.push(table.value[i].Id); } } return null; } if (exist_table.length == 0) return null; if (!table?.value || table.value.length == 0) return { errorMessage: "Нельзя удалять строки таблицы. Перезагрузите страницу" }; for (let i in exist_table) { if (!table.value.find(x => x.Id == exist_table[i])) return { errorMessage: "Нельзя удалять строки таблицы. Перезагрузите страницу" }; } return null; });
Ограничение на добавление файлов определённого типа
Скрипт позволяет добавить в поле только PDF-файлы и никакие другие.
form.onChange(["files"]) .validate("files", state => { const [files] = state.changes; const correct_extensions = ["pdf"]; if (!files?.files) return null; for (let i in files.files) { let extension = files.files[i].name.split(".").slice(-1); if (correct_extensions.indexOf(extension[0]) < 0) return { errorMessage: "Можно приложить только pdf" }; } return null; });
Отображение полей в зависимости от условий
Скрипт отображает выбранные поля формы в зависимости от выбранного значения справочника.
form.onChange(["problem_type"], true) .setVisibility(["ip_address"], state => { const [problem] = state.changes; return (problem?.columns?.["IP пользователя"] != ""); });
Проверка оставшихся дней отпуска
Скрипт рассчитывает, сколько у сотрудника ещё осталось дней оплачиваемого отпуска. Данные для расчёта берутся из реестра формы.
form.onChange(["vacation_start", "vacation_end", "employee", "vacation_type"]) .validateAsync("vacation_type", async state => { const [start, end, employee, vacation_type] = state.changes; if (!start?.date || !end?.date || !employee?.person_id || !vacation_type?.choice_name) return null; if (vacation_type.choice_name != "Оплачиваемый") return null; const max = 28; let this_time = daysBetween(new Date(start.date), new Date(end.date)) + 1; if (this_time > max) return { errorMessage: `Выбран промежуток в ${this_time} дней. Максимально ${max}` }; let today = new Date(); let already_taken = await form.fetchRegister(1027552, f => f.fieldGreaterThen("Дата начала", {date: `01-01-${today.getFullYear()}`}) .fieldLessThen("Дата окончания", {date: `01-01-${today.getFullYear()+1}`}), ["Дата начала", "Дата окончания"], {activeOnly: false} ); if (!already_taken.tasks || already_taken.tasks.length == 0) return null; let taken_days = 0; for (let i in already_taken.tasks) { if (!already_taken.tasks[i].fields[0] || !already_taken.tasks[i].fields[1]) continue; taken_days += daysBetween( new Date(already_taken.tasks[i].fields[0].date), new Date(already_taken.tasks[i].fields[1].date) ) + 1; } if (taken_days + this_time > max) return { errorMessage: `Уже использовано ${taken_days} дней. Осталось ${max - taken_days}. Превышение на ${this_time + taken_days - max}` }; return null; });