# Смарт-пакеты: типичные ошибки и как их избежать

Документ собирает повторяющиеся ошибки настройки пакетов действий: порядок действий, контекст выполнения (переход vs расписание), привязка к расписаниям, тихие сбои, асинхронные пакеты, отладка и чеклист. Каждая ошибка описана по схеме **Симптом — Причина — Решение**. Для администраторов платформы, настраивающих смарт-пакеты. Связанные документы: [Смарт-действия — администрирование](https://help.1forma.ru/domains/smart-actions/admin.md), [Справочник смарт-действий](https://help.1forma.ru/domains/smart-actions/actions-reference.md), [Решение проблем](https://help.1forma.ru/domains/smart-actions/support-guide.md).

---

## 1. Порядок действий в пакете — критически важен

Действия в пакете выполняются **последовательно**, в порядке `ActionOrder` (таблица `PacksActions`). Если действие N убирает условие, необходимое для действия N+1, пакет прерывается или даёт неожиданный результат.

Порядок можно менять перетаскиванием в редакторе пакета. Перед сохранением пакета проверяйте: каждое следующее действие должно иметь всё, что ему нужно, **после** выполнения предыдущих.

### 1.1 «Удалить исполнителей» до перехода

**Симптом:** выполняются смарт-действия на переходе, но сам переход не происходит. Задача остаётся в прежнем статусе. Лог автоматизации показывает успешное выполнение действий, но перехода нет.

**Причина:** в пакете действие «Удалить исполнителей» стоит **перед** действием «Выполнить переход» (MakeStep). Маршрут требует наличия исполнителя для выполнения перехода. После удаления исполнителей переход невозможен — система отклоняет его молча или с ошибкой, которая не всегда видна в интерфейсе.

**Решение:**

1. Переместить «Удалить исполнителей» **после** «Выполнить переход».
2. Если бизнес-логика требует удаления до перехода — использовать два пакета на двух событиях: пакет «Перед переходом» (удаление) и пакет «После перехода» (назначение новых). Но помнить: действие «Удалить исполнителей» в пакете «Перед переходом» может заблокировать сам переход.
3. Альтернатива: заменить «Удалить исполнителей» + «Выполнить переход» на «Выполнить переход» + «Удалить исполнителей» + «Добавить исполнителя» (нового). Порядок: переход — удаление старых — назначение новых.

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

### 1.2 «Создать задачу» без обязательных ДП

**Симптом:** действие «Создать задачу» (CreateTask) в пакете завершается с ошибкой или молча не создаёт задачу. В логе автоматизации — запись без результата.

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

**Решение:**

1. Проверить обязательные ДП и поля целевой категории.
2. Добавить в пакет действия «Изменить значение ДП» **после** CreateTask для заполнения обязательных полей. Учесть: CreateTask возвращает ID созданной задачи (переменная), его можно использовать в последующих действиях через пункт «Переменные» в дереве сущностей.
3. Если обязательные ДП нужно заполнить **при создании** — использовать смарт-скрипт (JS/Lua) вместо стандартного действия CreateTask.

### 1.3 «Изменить ДП» после «Заблокировать ДП»

**Симптом:** значение ДП не меняется, хотя в логе автоматизации действие «Изменить значение ДП» отмечено как выполненное.

**Причина:** ранее в том же пакете стоит действие «Заблокировать ДП» (SetExtParamReadOnly) для того же ДП. Блокировка применяется мгновенно, и последующее изменение игнорируется.

**Решение:**

1. Поменять порядок: сначала «Изменить значение ДП», затем «Заблокировать ДП».
2. Если нужно разблокировать, изменить, заблокировать обратно — три действия в правильном порядке: разблокировать — изменить — заблокировать.

**Несколько действий меняют один ДП:**

**Симптом:** ДП содержит значение из первого действия, хотя ожидалось значение из второго.

**Причина:** два действия «Изменить значение ДП» для одного ExtParamID. Оба выполняются, но итоговое значение — из последнего. Если между ними стоит действие, зависящее от промежуточного значения, оно получит неожиданные данные.

**Решение:** убрать дублирование. Если нужна цепочка вычислений — использовать смарт-скрипт.

---

## 2. Пакет на переходе vs пакет на таймере (расписании)

Контекст выполнения пакета зависит от того, **откуда** он вызван. Одинаковый пакет может работать на переходе и падать на расписании.

### 2.1 Контекст задачи

Контекст и данные, доступные пакету в зависимости от источника запуска:

| Источник запуска | Контекст задачи | Параметры события | Пользователь |
|---|---|---|---|
| Событие перехода (до/после) | Текущая задача | Переход, пользователь-инициатор | Тот, кто инициировал переход |
| Событие ДП | Текущая задача | Старое/новое значение ДП | Тот, кто изменил ДП |
| Расписание «Вне контекста задачи» | **Нет** | **Нет** | Системный робот |
| Расписание «Для каждой задачи по фильтру» | Текущая задача из итерации | **Нет** параметров события | Системный робот |
| Произвольное событие | Зависит от вызова | Параметры пользовательского события | Зависит от вызова |
| Очередь (QueueEventsJob) | Задача, связанная с событием очереди | Зависит от типа события | Инициатор события; при отсутствии валидного пользователя — системный робот |

**Файловые действия — исключение.** «Выгрузить файл в сетевую папку» (`UploadFileToDisk`) и «Создать файл отчёта» (`CreateReportFile`) системного робота сами не подставляют: без пользователя сессии и без явно заданного пользователя доступа они завершаются отказом. Для таких действий в фоновом пакете субъект указывают в параметре действия.

**Ключевое отличие:** при выполнении из расписания **нет параметров события**. Если смарт-выражение в действии ссылается на параметр события (например, «Инициатор перехода»), оно вернёт null.


### Тип контекста скрипта в схеме действия «Выполнить смарт-скрипт»

Тип контекста, который получает скрипт, добавленный в пакет, вычисляется так же, как тип для параметров пакета:

| Условие | Тип контекста скрипта |
|---|---|
| Режим без контекста (`NoContextMode`) | `None` |
| Пакет без `origin` (создан на странице пакетов действий) | `Task` — как для параметров пакета |
| `origin = Subcat` | `Task` |
| `origin = Mail` | `Mail` |
| `origin = EdocumentLink` | `EdocumentLink` |
| `origin = MessageQueue` | `MessageQueue` |
| `origin = EdocumentSbisLink` | `EdocumentSbisLink` |

Сам скрипт своё значение при этом не меняет: контекст он получает от пакета в момент исполнения. Правило «без `origin` — `Task`» и введено, чтобы скрипт, добавленный в пакет без явного источника, не оставался без контекста — как параметры пакета.
### 2.2 Ошибка: пакет работает на переходе, падает на расписании

**Симптом:** пакет привязан и к событию перехода, и к расписанию. На переходе работает, на расписании — ошибка или пустой результат.

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

**Решение:**

1. Не переиспользовать один пакет для разных типов источников. Создать отдельный пакет для расписания.
2. Если переиспользование необходимо — заменить ссылки на параметры события на смарт-выражения, которые вычисляют значение из контекста задачи (например, текущий исполнитель вместо «Инициатор перехода»).

**Ошибка «пакет из расписания не видит задачи»:**

**Симптом:** расписание срабатывает (видно в логе), но пакет не обрабатывает ни одной задачи.

**Причины (проверять по порядку):**

1. Режим «Вне контекста задачи» — пакет не итерирует задачи. Нужен режим «Для каждой задачи по фильтру».
2. Смарт-фильтр расписания содержит `WHERE TaskID = @ContextID` — остаток от конвертации смарт-выражения в TSQL. Убрать эту проверку.
3. В категории нет ни одной задачи — расписание не запускается.

---

## 3. Привязка пакетов к расписаниям — потеря видимости

### 3.1 «Пакеты действий затерлись в Расписаниях»

**Симптом:** после обновления названия пакета расписание перестаёт показывать пакет в интерфейсе. Или: расписание ссылается на пакет, но в списке расписаний название пакета пустое.

**Причина:** расписание хранит `ActionPackID` (ссылка на `ActionsPacks`). Если пакет был удалён и пересоздан с тем же названием — ID изменился, старая привязка сломана. Имя пакета — отображаемое свойство, не ключ.

**Решение:**

1. Не удалять и пересоздавать пакеты. Редактировать существующие.
2. Если пакет уже удалён — открыть расписание на редактирование и выбрать новый пакет из списка.
3. Диагностика: проверить `ActionPackID` в привязке и убедиться, что он существует в `ActionsPacks`.

```sql
-- Расписания с несуществующими пакетами
SELECT sr.ID, sr.SubcatID, sr.ActionsPackID, sr.Enabled
FROM SmartRecurrence sr
LEFT JOIN ActionsPacks ap ON sr.ActionsPackID = ap.ID
WHERE ap.ID IS NULL;
```

**Название пакета с апострофами:**

**Симптом:** пакет не открывается, не редактируется, интерфейс показывает ошибку.

**Причина:** апострофы (`'`) в названии пакета ломают интерфейс. Кавычки (`"`) допустимы.

**Решение:** убрать апострофы из названия. Через API:
```
PUT /api/admin/smart/packs/{packId}
```
Или напрямую в БД (только если интерфейс недоступен):
```sql
UPDATE ActionsPacks SET Name = REPLACE(Name, '''', '') WHERE ActionPackID = @id;
```

---

## 4. Тихие сбои — пакет «молча» не работает

### 4.1 Действие выполняется от имени пользователя без прав

**Симптом:** действие в логе отмечено как выполненное, но эффекта нет. Или: ошибка 403 в ExceptionsLog.

**Причина:** действие выполняется от имени пользователя, у которого нет прав на целевую категорию/задачу. Типичный случай: действие «Создать задачу» в другой категории от имени инициатора перехода, у которого нет доступа к целевой категории.

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

**Смарт-выражение возвращает неожиданный тип:**

**Симптом:** действие пропускается без ошибки.

**Причина:** смарт-выражение в параметре действия возвращает тип, не совпадающий с ожидаемым. Например, строку вместо числа в поле «Задача», или пустой список вместо ID пользователя.

**Решение:** протестировать смарт-выражение отдельно через редактор выражений (кнопка «Тест»). Убедиться, что тип возвращаемого значения совпадает с типом параметра.

**Пакет на событии «Перед...» отменяет событие:**

**Симптом:** событие (переход, создание комментария, смена ДП) не происходит. Пользователь видит ошибку или ничего не видит.

**Причина:** в пакете на событии «Перед...» стоит действие «Отменить» без условия, или условие всегда истинно.

**Решение:** проверить смарт-фильтр на событии. Действие «Отменить» должно быть обёрнуто в условие (смарт-фильтр на привязке к событию), которое ограничивает отмену конкретным сценарием.

---
### 4.2 Сбой действия или итерации списочного параметра в транзакции

**Отдельный случай — нумераторы.** Если на шаге установлено несколько нумераторов, каждый обрабатывается независимо: сбой при установке одного не прерывает остальные, а у сбойного значение в задачу не попадает и счётчик не сдвигается — счётчик растёт только вместе с записанным значением. Сбой пишется в журнал. Случай, когда значения всех нумераторов уже сформированы, а их запись завершается сбоем, этим правилом не покрывается.

**Симптом:** действие пакета выполняется по списочному параметру, часть элементов обработана, затем одна итерация завершается ошибкой или отклоняется из-за пустого обязательного параметра.

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

**Поведение:** при прямом выполнении пакета успешные итерации сохраняют свои изменения, а изменения сбойной итерации откатываются. Если действие возвращает результат, результат успешной итерации доступен следующим действиям только в согласованном с базой виде. Пустое значение обязательного параметра отклоняет только соответствующую итерацию, остальные выполняются. Запись о выполнении действия сохраняется в журнале автоматизации; при отсутствии сбоя поведение прежнее.

**Запуск по событию.** Поведение такое же: если пакет запускает событийное правило, сбой или отклонение одной итерации откатывает только её — записи успешных итераций и запись о выполнении действия в журнале автоматизации сохраняются после того, как зафиксируется операция, в ходе которой сработало событие. Это касается и событий, которые система выполняет внутри своей же операции, — например, события «После изменения значения параметра», вызванного другим пакетом: сбой итерации не отменяет ни само изменение параметра, ни записи уже выполненных итераций.

**Что по-прежнему откатывает пакет целиком:** сбой, не связанный с итерациями списка, — ошибка вычисления параметров или действие, прерванное логической ошибкой. Пакеты, привязанные к событию асинхронно, выполняются вне этого механизма: сбой итерации в них на записи соседних итераций не влияет, но и изоляции итерации там нет.

**Решение:**

1. Проверить журнал автоматизации и журнал ошибок, чтобы определить сбойную итерацию и причину.
2. Проверить обязательные параметры и вычисляемые значения для каждого элемента списка.
3. Сверить изменения успешных итераций и результат, использованный последующими действиями.
4. Если часть изменений не сохранилась, проверить два случая: событие было привязано асинхронно или пакет прерван логической ошибкой вне списка — и то и другое откатывает пакет целиком.

> Успешные итерации не означают, что всё действие или пакет имеет общий признак «успешно»: при наличии ошибки пакет может вернуть ошибку вызывающему. Повторный запуск следует оценивать с учётом уже сохранённых итераций.
### 4.3 Действие с параметром-пользователем работает не от того пользователя

**Симптом:** после переноса конфигурации на другую площадку действие с параметром «Пользователь» (например, «Пользователь доступа» у файловых действий) выполняется от системного робота: права не проверяются, файл создаётся или подкладывается там, где от имени человека действие отказало бы.

**Причина:** при переносе значение параметра-пользователя сопоставляется с пользователями приёмника по идентификатору. Пользователя с таким идентификатором на приёмнике нет — вместо него подставляется системный робот. В списке пользователей ненайденные элементы убираются, робот вместо элемента не подставляется.

**Решение:** открыть журнал переноса и посмотреть предупреждения по параметрам-пользователям — там перечислены пакет, действие, параметр и результат. Для заменённых значений указать на приёмнике нужного пользователя вручную. Предупреждение о подмене пишется по каждому такому параметру, это единственный способ её заметить.

### 5.1 Два пакета на одном событии, один зависит от другого

**Симптом:** иногда пакет B видит изменения пакета A, иногда — нет.

**Причина:** флаг «Асинхронно» на привязке к событию. При включённой асинхронности пакеты выполняются параллельно, порядок не детерминирован.

**Решение:**

1. Убрать флаг «Асинхронно», если пакеты зависят друг от друга.
2. Объединить зависимые действия в один пакет — внутри пакета порядок всегда последовательный.

*«Настройку [Асинхронно] следует включать только в том случае, если все пакеты действий в текущем событии не зависят друг от друга, т.е. порядок выполнения не важен, и одно действие не использует результат другого.»*

### 5.2 Асинхронный пакет ушёл в очередь и «завис»

**Симптом:** действие не выполнилось. В журнале очередей запись со статусом Error или NotHandled.

**Причина:** ошибка в пакете при асинхронном выполнении уходит в `MessageQueue`. Если поток (`MessageFlows`) настроен с `ActionOnError = Stop`, одна ошибка блокирует весь поток.

**Решение:**

1. Проверить `MessageFlows` — поле `HasErrors` и `ActionOnError`.
2. Исправить причину ошибки в пакете.
3. Перезапустить обработку потока (сбросить `HasErrors`).

```sql
-- Найти застрявшие события
-- QueueState: 0=NotHandled, 1=Success, 2=Error, 3=InProgress
SELECT TOP 50 Id, Created, State, ErrorDescr, ContextId, Flow
FROM MessageQueue
WHERE State IN (0, 2)  -- NotHandled или Error
ORDER BY Created DESC;

-- Проверить потоки с ошибками
SELECT Id, FlowName, HasErrors, FailedAttemptsCounter, ActionOnError
FROM MessageFlows
WHERE HasErrors = 1;
```

---

## 6. Отладка: как понять что пошло не так

### 6.1 Журнал автоматизации

**Путь:** Администрирование — Журналы — Автоматизация (лог Smart).

Журнал фиксирует выполнение смарт-пакетов и смарт-действий. Записи создаются только при включённом логировании: Общие настройки приложения — «Логировать выполнение smart запросов».

Что смотреть:

- **Тип = 4** (пакет) — факт запуска пакета, длительность
- **Тип = 2** (действие) — каждое действие внутри пакета
- **Тип = 9** (recurrence) — запуск расписания

Таблица БД: `dbo.AutomationScriptsLog`.

**ExceptionsLog:** исключения, возникшие при выполнении действий. Не отображаются в интерфейсе пользователю — только в журнале и в БД.

```sql
-- Ошибки автоматизации за последние сутки
SELECT TOP 100 ID, Date, Exception, SourceID, AppPool
FROM ExceptionsLog
WHERE Date > DATEADD(day, -1, GETDATE())
  AND Exception LIKE '%ActionPack%'
ORDER BY Date DESC;
```

**Журнал очередей:** для асинхронных пакетов. Таблицы: `MessageQueue` (текущие), `MessageQueueLog` (история). Путь в интерфейсе: Журналы — Очереди.

### 6.4 Алгоритм локализации проблемы

Как пошагово найти причину сбоя:

1. **Воспроизвести** — выполнить действие, запускающее пакет.
2. **Лог автоматизации** — есть ли запись о запуске пакета? Если нет — проблема в привязке (событие, фильтр, активность).
3. **Лог автоматизации** — есть ли записи о каждом действии? Если нет — действие упало. Искать в ExceptionsLog.
4. **ExceptionsLog** — текст исключения покажет причину (обращение к пустому значению, нехватка прав, невалидные параметры).
5. **Очереди** — если пакет асинхронный, проверить `MessageQueue` на статус Error.

---

## 7. Чеклист перед запуском пакета

Что проверить перед сохранением пакета:

| # | Проверка | Как проверить |
|---|----------|---------------|
| 1 | Порядок действий логически корректен | Каждое действие N+1 получает всё необходимое после N. Действия удаления — в конце. |
| 2 | Обязательные параметры заполнены | В редакторе пакета все поля со `*` имеют значения. Смарт-выражения возвращают правильный тип. |
| 3 | Действия выполняются от имени пользователя с правами | Если есть сомнения — системный робот. |
| 4 | Пакет не использует параметры события, которых нет | Если пакет на расписании — нет параметров события. Если на «Перед/После ДП» — есть параметры ДП. |
| 5 | Асинхронные пакеты не зависят друг от друга | Если два пакета на одном событии и один зависит от другого — убрать «Асинхронно». |
| 6 | Логирование включено | Общие настройки — «Логировать выполнение smart запросов». Без этого диагностика невозможна. |
| 7 | Название пакета без апострофов | Апострофы ломают интерфейс. |

---
- После переноса конфигурации — проверить журнал переноса: у пакетов из переноса параметры-пользователи могли быть заменены системным роботом или убраны из списка.
