# Смарт-действия — задачи, связи, маршруты

> ⚠️ Символом **\*** помечены обязательные параметры. Часть [Справочника смарт-действий](https://help.1forma.ru/domains/smart-actions/actions-reference.md); как вызывать действия из SmartScript и типы параметров — там же ([Типы данных](https://help.1forma.ru/domains/smart-actions/actions-reference.md#типы-данных)).

Действия над задачами и их связями (создание, копирование, перенос, связи, встречи, избранное, аватар, шифрование) и действия маршрутов (переходы, смена статуса).

## Категории, задачи, связи

### Ассоциировать задачу с ЛК (`LinkTaskWithCZ`, ID 59)

Ассоциирует задачу в "Первой Форме" с задачей в Личном кабинете (то же самое, что "Связать задачу с личным кабинетом" ниже — оба используют тот же enum `LinkTaskWithCZ`).

| Параметр | Тип | Описание |
|----------|-----|----------|
| Задача* | `1F.Task` | Задача в "Первой Форме" |
| Личный кабинет* | `1F.CustomerZone` | Личный кабинет |

---

### Действие со встречей (`ActionMeeting`, ID 137)

Выполняет действие с календарным событием

| Параметр | Тип | Описание |
|----------|-----|----------|
| Ключ встречи* | `String` | Уникальный идентификатор встречи. Форматы: для Exchange — `2157:AAMkAGY4...`, для 1Ф — `1forma:{UserID}:{TaskID}` |
| Действие* | — | Принять / Отменить / Под вопросом / Отклонить |
| Доступ к календарю от имени пользователя | `1F.User` | Пользователь для действия в календаре (по умолчанию — Робот) |

> ⚠️ По умолчанию у Робота нет доступа к календарю. Рекомендуется указывать пользователя с правами (организатор встречи и т.п.)

---

### Добавить задачу в избранное (`AddTaskToFavorites`, ID 92)

Добавляет задачу указанным пользователям в блок Избранное. Пользователям из «Кому добавляют», у которых нет доступа к задаче, задача в избранное не добавляется и цвет не назначается; действие при этом завершается без ошибки.

| Параметр | Тип | Описание |
|----------|-----|----------|
| Кто добавляет* | `1F.User` | От чьего имени выполняется действие |
| Кому добавляют* | `Collection<1F.User>` | Пользователи-получатели (для группы — смарт-выражение) |
| Цвет* | `Integer` | 1=Голубой, 2=Зеленый, 3=Изумрудный, 4=Желтый, 5=Фиолетовый, 6=Красный |
| Задача* | `1F.Task` | Задача в "Первой Форме" |

---

### Добавить комментарий в избранное (`AddCommentToFavorites`, ID 94)

Добавляет комментарий указанным пользователям в блок Избранное

| Параметр | Тип | Описание |
|----------|-----|----------|
| Кто добавляет* | `1F.User` | От чьего имени выполняется действие |
| Кому добавляют* | `Collection<1F.User>` | Пользователи-получатели |
| Цвет* | `Integer` | 1=Голубой, 2=Зеленый, 3=Изумрудный, 4=Желтый, 5=Фиолетовый, 6=Красный |
| ID комментария* | `Integer` | ID комментария |

---

### Задать аватар задачи (`UpdateOrInsertTaskAvatar`, ID 193)

Позволяет установить цвет и иконку для аватара задачи

**Права.** Действия над аватаром задачи (установка и удаление) проверяют права владельца пакета: нужно быть заказчиком задачи, администратором категории (или иметь права God) либо обладать правом на изменение задачи. Пакеты, выполняющиеся от системного робота, проверку не проходят — она к ним не применяется.

| Параметр | Тип | Описание |
|----------|-----|----------|
| Задача* | `1F.Task` | Задача |
| Файл | `1F.File` | ID файла с изображением для аватара |
| Цвет | `String` | `none`, `primary`, `red`, `yellow`, `orange`, `green`, `cyan`, `blue`, `purple`, `pink`, `brown`, `grey` |
| Иконка | `String` | Название иконки из набора `/spa/content-icons` |

---

### Запинить задачу (`PinTask`, ID 97)

Задача (одна или несколько) добавляется в список чатов

| Параметр | Тип | Описание |
|----------|-----|----------|
| Номер задачи* | `Collection<1F.Task>` | Добавляемые задачи |
| Кому* | `Collection<1F.User>` | Пользователи, которые будут видеть задачи в списке чатов |

> ⚠️ Ограничивается правами на категорию. Доступно подписчикам или администраторам задачи. Не работает для категории "Общение".

---

### Зашифровать задачу (`EncryptTask`, ID 122)

Задача зашифровывается

| Параметр | Тип | Описание |
|----------|-----|----------|
| Задача* | `1F.Task` | Задача для шифрования |
| Пользователь* | `1F.User` | Кто зашифровывает |

> ⚠️ Работает только если в категории включен режим шифрования

---

### Копировать задачу (`CopyTask`)

Создает копию задачи (возвращает `EntityCollection<Task>`)

| Параметр | Тип | Описание |
|----------|-----|----------|
| Задача* | `1F.Task` | Задача для копирования |
| Категория* | `1F.Subcat` | Категория для копии |
| Копировать подписчиков* | `Boolean` | Копировать подписчиков |
| Копировать вложения* | `Boolean` | Копировать файлы |

> ⚠️ Копируются только ДП, отображаемые в карточке создания задачи

---

### Найти встречи в периоде (`FindMeetingsByPeriod`, ID 139)

Возвращает встречи пользователя в указанном периоде

| Параметр | Тип | Описание |
|----------|-----|----------|
| Владелец календаря* | `1F.User` | Пользователь, в чьём календаре искать |
| С* | `DateTime` | Начало периода |
| По* | `DateTime` | Конец периода |
| Доступ к календарю от имени пользователя | `1F.User` | От чьего лица действие |

---

### Найти встречу по задаче (`FindMeetingByTaskId`, ID 138)

Находит встречу по связанной с ней задаче

| Параметр | Тип | Описание |
|----------|-----|----------|
| Организатор* | `1F.User` | Организатор встречи |
| Привязать задачу* | `1F.Task` | Задача, из которой запланирована встреча |
| Доступ к календарю от имени пользователя | `1F.User` | От чьего лица действие |

---

### Обновить встречу (`UpdateMeeting`, ID 135)

Обновляет параметры в календарном событии

| Параметр | Тип | Описание |
|----------|-----|----------|
| Ключ встречи* | `String` | Уникальный идентификатор встречи |
| Участники | `Collection<1F.User>` | Участники встречи |
| Начало | `DateTime` | Время начала |
| Конец | `DateTime` | Время окончания |
| Место | `String` | Место встречи |
| Тема | `String` | Тема встречи |
| Html | `Boolean` | Описание в HTML-формате |
| Тело | `String` | Описание встречи |
| Привязать задачу | `Boolean` | Задача в поле "Связано с" |
| Статус встречи | `String` | Свободен / Под вопросом / Занят |
| Доп. email адреса участников | `String` | Дополнительные email |
| Доступ к календарю от имени пользователя | `1F.User` | От чьего лица действие |

---

### Отпинить задачу (`UnpinTask`, ID 98)

Задача (одна или несколько) удаляется из списка чатов

| Параметр | Тип | Описание |
|----------|-----|----------|
| Номер задачи* | `Collection<1F.Task>` | Удаляемые задачи |
| Кому* | `Collection<1F.User>` | Пользователи |

> ⚠️ Если пользователь не подписчик — ошибка в журнале. Не работает для категории "Общение".

---

### Перенести задачу (`MoveTask`)

Переносит задачу в указанную категорию

| Параметр | Тип | Описание |
|----------|-----|----------|
| Категория* | `1F.Subcat` | Целевая категория |
| Задача* | `1F.Task` | Задача для переноса (одна) |
| Кто переносит* | `1F.User` | От чьего имени |
| Писать "тихий" комментарий (без адресата) | `Boolean` | Тихий комментарий |

> ⚠️ Пакет выполняется только в исходной категории

---

### Получить встречу (`GetMeeting`, ID 140)

Возвращает все поля встречи по её ключу (для Lua смартов)

| Параметр | Тип | Описание |
|----------|-----|----------|
| Ключ встречи* | `String` | Уникальный идентификатор встречи |
| Доступ к календарю от имени пользователя | `1F.User` | От чьего лица действие |

---

### Связать задачи (`LinkTasks`)

Устанавливает связь между двумя задачами

| Параметр | Тип | Описание |
|----------|-----|----------|
| Пользователь* | `1F.User` | От чьего имени |
| Задача* | `1F.Task` | Первая задача |
| Задача* | `1F.Task` | Вторая задача |
| Сделать задачу не связанной, а дочерней* | `Boolean` | Первая=родительская, вторая=дочерняя |
| Тип связи в проекте | `Integer` | 0=Без зависимости, 1=Начало-начало, 2=Начало-окончание, 3=Окончание-начало, 4=Окончание-окончание |

> ⚠️ Пользователь должен иметь права на обе задачи. Можно использовать `systemrobot`.

---

### Связать задачу с личным кабинетом (`LinkTaskWithCZ`, ID 59)

Связывает задачу в "Первой Форме" с задачей в Личном кабинете

| Параметр | Тип | Описание |
|----------|-----|----------|
| Задача* | `1F.Task` | Задача в "Первой Форме" |
| Идентификатор личного кабинета* | `1F.CustomerZone` | Личный кабинет |

---

### Создать задачу (`CreateTask`)

Создает задачу или подзадачу. **Результат:** `int[]` — ID созданных задач

| Параметр | Тип | Описание |
|----------|-----|----------|
| Заказчик* | `1F.User` | Заказчик |
| Категория* | `1F.Subcat` | Категория |
| Доп. параметры | — | Значения ДП (после выбора категории) |
| Текст | `String` | Текст задачи |
| Сделать связанной* | `Boolean` | Связать с текущей задачей |
| Сделать подзадачей* | `Boolean` | Сделать подзадачей текущей |
| Исполнители | `Collection<1F.User>` | Исполнители |
| Срок | `DateTime` | Срок |
| Дата начала работ | `DateTime` | Дата начала |
| Копировать подписчиков из родительской* | `Boolean` | Копировать подписчиков |
| Копировать вложения* | `Boolean` | Копировать файлы |
| Включать текст исходной* | `Boolean` | Добавить текст исходной |
| Приоритет* | `Integer` | 0=низкий, 1=обычный, 3=высокий |
| Уведомить | `Collection<1F.User>` | Уведомить о создании |
| Подписать | `Collection<1F.User>` | Подписать к задаче |
| Каждому исполнителю отдельную копию* | `Boolean` | Отдельная задача каждому |
| Создать ссылки на вложения | `Boolean` | Ссылки вместо копий файлов |
| Вложить письмо файлом | `Boolean` | Вложить письмо в формате `.eml` в новую задачу (имя файла — `Email.eml`, MIME — `message/rfc822`). С **2.268.356** логика двухуровневая: если у письма сохранён оригинальный MIME-источник (поле `Email.Source` в БД), он отдаётся как есть и `.eml` содержит все оригинальные транспортные заголовки (`Return-Path`, `Received`, `Message-Id`, `DKIM-Signature`, `Received-SPF`) и корректную MIME-структуру тела — файл нормально открывается в Thunderbird/Outlook с оригинальным форматированием. Если источника нет (исходящие письма; старые входящие, у которых исходник не сохраняли) — формируется стандартным перестроением письма, заголовки ограничены envelope (`From`/`To`/`Subject`/`Date`); Content-Type тела корректно определяется как HTML при наличии HTML-версии. До 2.268.356 даже у писем с сохранённым источником шла пересборка, что обрезало transport headers и портило MIME |
| Конфиденциальность | `Boolean` | Признак конфиденциальности |
| Связать от имени | `1F.User` | От чьего имени связь (иначе — заказчик) |
| От чьего имени совершается действие | `1F.User` | Сессионный пользователь |

> ⚠️ Параметр «Копировать подписчиков из родительской» не управляет копированием подписчиков: при создании подзадачи через смарт-действие подписчики родительской задачи копируются всегда. Управление через параметры действия работает только для исполнителей (флаг копирования и режим «только ответственный»). Чтобы убрать лишних подписчиков, добавьте в тот же пакет смарт-действие «Удалить подписчика» или «Удалить всех подписчиков».

> **Как считается срок по умолчанию.** Если параметр «Срок» не задан, срок берётся от длительности по умолчанию категории, и расчёт ведётся от серверного «сейчас»: от одного и того же момента считаются и сам срок, и граница минимального срока категории. Часовой пояс профиля пользователя, запускающего действие, на результат не влияет.
>
> - Срок не может быть раньше момента «сейчас плюс минимальный срок категории»; равенство допустимо, поэтому при длительности по умолчанию, равной минимальному сроку, задача создаётся.
> - При рабочих единицах длительности срок считается по производственному календарю, в рабочих днях и часах.
> - На серверное время переведено только смарт-действие «Создать задачу»: обычное создание задачи, создание чата и перенос категории проверяют минимальный срок от пользовательского времени.

---

### Создать встречу (`CreateMeeting`, ID 134)

Создает календарное событие

| Параметр | Тип | Описание |
|----------|-----|----------|
| Организатор* | `1F.User` | Организатор встречи |
| Участники | `Collection<1F.User>` | Участники |
| Начало* | `DateTime` | Время начала |
| Конец* | `DateTime` | Время окончания |
| Место | `String` | Место |
| Тема* | `String` | Тема |
| Html* | `Boolean` | Описание в HTML |
| Тело* | `String` | Описание |
| Привязать задачу | `1F.Task` | Задача в "Связано с" |
| Статус встречи* | — | Свободен / Под вопросом / Занят / Нет на месте / Работаю в другом месте / Нет данных |
| Проверять привязанную задачу перед созданием* | `Boolean` | Проверка на дубли |
| Доп. email адреса участников | `String` | Дополнительные email |
| Доступ к календарю от имени пользователя | `1F.User` | От чьего лица действие |

---

### Удалить аватар задачи (`DeleteTaskAvatar`, ID 192)

Удаляет аватар задачи

| Параметр | Тип | Описание |
|----------|-----|----------|
| Задача* | `1F.Task` | Задача, в которой удаляется аватар |

---

### Удалить встречу (`DeleteMeeting`, ID 136)

Удаляет встречу из календаря

| Параметр | Тип | Описание |
|----------|-----|----------|
| Ключ встречи* | `String` | Уникальный идентификатор встречи |
| Доступ к календарю от имени пользователя | `1F.User` | От чьего лица действие |

---

### Удалить задачу из избранного (`DeleteTaskFromFavorites`, ID 93)

Удаляет задачу из блока Избранное у указанных пользователей

| Параметр | Тип | Описание |
|----------|-----|----------|
| Кому удаляют* | `Collection<1F.User>` | Пользователи |
| Задача* | `1F.Task` | Задача |

---

### Удалить комментарий из избранного (`DeleteCommentFromFavorites`, ID 95)

Удаляет комментарий из блока Избранное у указанных пользователей

| Параметр | Тип | Описание |
|----------|-----|----------|
| Кому удаляют* | `Collection<1F.User>` | Пользователи |
| ID комментария* | `Integer` | ID комментария |

---

### Удалить связь между задачами (`RemoveTasksLink`, ID 21)

Разрывает связь между задачами

| Параметр | Тип | Описание |
|----------|-----|----------|
| Задача* | `1F.Task` | Первая задача |
| Другая задача* | `1F.Task` | Вторая задача |
| Кто разрывает* | `1F.User` | От чьего имени |
| Писать "тихий" комментарий (без адресата) | `Boolean` | Тихий комментарий |

---

## Маршруты, переходы

### Выполнить переход по маршруту (`MakeStep`)

Выполняет переход по маршруту (если известен ID перехода)

| Параметр | Тип | Описание |
|----------|-----|----------|
| Задача* | `1F.Task` | Задача |
| Инициатор перехода* | `1F.User` | От чьего имени |
| Переход* | `1F.Step` | Переход |
| Причина | `String` | Причина перехода |
| Не писать комментарий* | `Boolean` | Комментарий только автору |

---

### Перейти в заданный статус (`SetState`, ID 9)

> ℹ️ В enum `StandardAction` это действие — `SetState` (ID 9); `MoveToState` — его отображаемое имя (ресурс `ActionMoveToState`), не самостоятельное кодовое имя. Для вызова из SmartScript используйте `SetState`. Это то же действие, что «Установить определённый статус (`SetState`, ID 9)» в § «Системные действия».

Переход к целевому статусу **по маршруту**: действие само ищет переход (step), ведущий из текущего статуса в заданный. Если такого перехода в маршруте нет, поведение определяет параметр **«Кинуть ошибку, если не найден переход»** (`ThrowErrorOnNoStep`): включён — действие завершается ошибкой вида «Не найден шаг для перехода в статус N», статус не меняется; выключен — переход тихо пропускается.

| Параметр | Тип | Описание |
|----------|-----|----------|
| Задача* | `1F.Task` | Задача |
| Инициатор* | `1F.User` | От чьего имени |
| Статус* | `1F.State` | Целевой статус |
| Кинуть ошибку, если не найден переход* | `Boolean` | При отсутствии перехода в маршруте — ошибка (`true`) или тихий пропуск (`false`) |
| Причина | `String` | Причина перехода |
| Не писать комментарий | `Boolean` | Комментарий только автору |

> ⚠️ **Не путать с «Изменить статус задачи» (`ChangeTaskStateForcibly`, ниже).** «Перейти в заданный статус» требует наличия перехода в маршруте; «Изменить статус задачи» меняет статус **принудительно**, без маршрута. Для перевода в статус, до которого маршрута нет, используйте «Изменить статус задачи».

---

### Изменить статус задачи (`ChangeTaskStateForcibly`)

Принудительно меняет статус задачи

| Параметр | Тип | Описание |
|----------|-----|----------|
| Задача* | `1F.Task` | Задача |
| Инициатор* | `1F.User` | От чьего имени |
| Статус* | `1F.State` | Целевой статус |
| Не проверять права* | `Boolean` | Не проверять права пользователя |
| Не писать комментарий | `Boolean` | Комментарий только автору |
| Не записывать ошибку, если уже в этом статусе | `Boolean` | Не считать ошибкой совпадение статусов |

> ⚠️ Для принудительной смены статуса не требуется наличие перехода между статусами в маршруте
