# Смарт-действия — Известные проблемы

Документ — реестр известных проблем и ограничений смарт-действий (Smart Actions) 1Формы: активные ограничения (локализация ФИО в CreateUser/UpdateUser, EventID 64 в WhenOpenTask, Lua-ориентированная справка редактора при любом языке скрипта) и ранее исправленные проблемы. Для инженеров поддержки и разработчиков, работающих с пакетами смарт-действий.

---

## 1. CreateUser / UpdateUser не поддерживают локализованные ФИО

> Статус: исправлено

**Симптом:** при создании пользователя через смарт-действие «Создать пользователя» нельзя указать ФИО на дополнительном языке (например, английском). ФИО записывается как локализация только для языка, указанного в параметре «Язык».

**Причина:** действие записывает локализованное ФИО только для основного языка пользователя; параметров для второго языка нет.

**Решение:** в действиях «Создать пользователя» и «Обновить пользователя» добавлены параметры «Фамилия (локализованная)», «Имя (локализованное)», «Отчество (локализованное)» и «Язык локализации» — ФИО на дополнительном языке задаётся штатно. См. [actions-reference-people.md](https://help.1forma.ru/domains/smart-actions/actions-reference-people.md). Ограничение касается только одного действия: при обычном сохранении карточки пользователя локализованные ФИО распространяются на языки-автокопии — см. [Локализация — бизнес-правила](https://help.1forma.ru/domains/localization/business.md).

---

## 2. WhenOpenTask (EventID 64) срабатывает в SPA только в представлении «Чаты»

> Статус: ограничение подтверждено, поведение намеренное

**Симптом:** смарт-правило на событие «Во время открытия задачи» не срабатывает, когда пользователь открывает задачу в стандартной карточке (полное представление). Срабатывает только при открытии в представлении «Чаты».

**Обходной путь:** событие можно вызвать напрямую через API (`POST /api/v2/tasks/{taskId}/when-open-task`) — без ограничений по представлению. Актуально для интеграций и мобильного клиента.

**Дополнительно:** контекст открытия передаётся в `@eventParam1` (не `@eventParam0`). `@eventParam0` у данного события не используется — нетипичное поведение по сравнению с другими событиями.

---

## 3–6. Исправленные ранее проблемы (сборки 2.268.63 – 2.268 «Скульптор»)

Четыре исправленные ранее проблемы с указанием сборок исправления; в текущих сборках не воспроизводятся.

**Mustache-шаблоны возвращали пустые строки** (исправлено в 2.268.77): смарт-действие «Сгенерировать файл по шаблону» с mustache-шаблоном возвращало пустое содержимое.

**Смарт-отмена (Cancel) в Lua не работала** (исправлено в 2.268.63): действие «Отменить» (Cancel) не отрабатывало корректно при вызове из Lua-скрипта.

**Ошибка 500 на смарт-кнопках действий** (исправлено в 2.268.74): ошибка при нажатии на смарт-кнопки действий (в частности, при генерации файла по шаблону).

**HTML-таблица с числовыми отступами обрывала формирование файла по шаблону** (исправлено в 2.268 «Скульптор»): значение с `<table cellspacing="0">` или `<table cellpadding="0">` приводило к ошибке `The input string '"0"' was not in a correct format` и отказу действия «Сгенерировать по шаблону». Теперь такие значения формируются так же, как записанные с обычными кавычками.

---

## 6. ESQL: `FROM {0}` без алиаса — 500 на reserved keyword

> Статус: исправлено

**Симптом:** запросы вида `SELECT VALUE COUNT(1) FROM {0} WHERE ...` возвращают 500. Проявлялось при вычислении видимости блоков смарт-фильтра в МТФ.

**Причина:** в бесконтекстной ветке генератора ESQL конструкция `FROM {0}` подставлялась без алиаса коллекции. Парсер ESQL принимал стоящий сразу за `FROM` reserved keyword (`WHERE`, `ORDER`, `GROUP`) за имя коллекции. В контекстной ветке (`FROM Tasks as t`) алиас присутствовал, поэтому ошибка не воспроизводилась.

**Инвариант:** алиас `as t` подставляется во всех ветках генератора ESQL.



## 7. Потеря значения параметра действия через admin-MCP (JsonElement)

> Статус: исправлено

**Симптом:** при сохранении действия пакета через admin-MCP (`/mcp-admin-api-actions-pack-editor` → `actions_pack_editor_save_pack_action`) значение параметра не записывалось в БД. Проявлялось двумя способами:
- `ArgumentException: unknown value type: System.Text.Json.JsonElement` — значение не распознавалось контролом параметра.
- `SqlException 547` — нарушение CHECK-ограничения `CK_PacksActionsParameters_Value_XOR_Expression` при вставке параметра с пустым значением.

**Причина:** admin-MCP десериализует тело запроса через `System.Text.Json`, помещая в object-поле `FixedValue` тип `JsonElement`. Базовый класс контролов `ParameterControlBase` обрабатывал только `JObject` (Newtonsoft) и нативные типы — `JsonElement` попадал в `else`-ветку с исключением. Отдельно — отсутствие фильтрации пустых параметров в `MergeParameters` позволяло довести до БД строку с пустыми значениями всех источников.

**Инвариант:** `ParameterControlBase.SerializeValue` обрабатывает `JsonElement` наравне с `JObject`; `PacksActionsEntityService.MergeParameters` фильтрует параметры без значения через `HasParameterValue` перед записью.

## 8. CreateUser / UpdateUser: поле «Пароль» в редакторе всегда пустое, отдельной очистки нет

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

**Причина:** значение пароля хранится в зашифрованном виде и намеренно не возвращается в редактор, в описание действия и в экспорт модуля — поле работает только на запись. Пустое поле при сохранении трактуется как «не менять сохранённое значение».

**Обходной путь:** чтобы заменить пароль, введите новое значение. Чтобы убрать ранее сохранённый пароль, смените источник значения параметра (выражение, скрипт или предопределённый параметр) либо пересоздайте действие.

## 9. Невалидный JSON в значении параметра действия пакета — 500 вместо ошибки ввода

> Статус: исправлено

**Симптом:** сохранение действия пакета со значением параметра, структура которого не соответствует контролу — например, у элемента выбора пользователей идентификатор пустой, — отвечало HTTP 500, а в журнал исключений писалась ошибка десериализации `Error converting value {null} to type 'System.Int32'. Path 'users[0].id'`.

**Причина:** в `ParameterControlBase.TryParseNaive` десериализация входного значения — `JsonElement.Deserialize<TV>` для запросов от admin-MCP и `JObject.ToObject<TV>()` для запросов от SPA — выполнялась без перехвата: исключение уходило наверх мимо фильтра, который переводит логические ошибки в 400.

**Решение:** блок десериализации обёрнут в `try/catch`, перехватывающий `System.Text.Json.JsonException`, `Newtonsoft.Json.JsonException`, `InvalidCastException` и `ArgumentException`, и вместо них бросается `TCLogicException` с сообщением «некорректное значение параметра действия» — фильтр API транслирует её в HTTP 400. Поведение наследуют все контролы на базе `ParameterControlBase<TV>`; разбор корректных значений не изменился.

## 10. Справка в редакторе смарт-скриптов всегда Lua-ориентированная

**Симптом:** в скрипте на языке, отличном от Lua (Python, JavaScript, OneScript, C#), кнопка «i» («Библиотека подсказок») открывает окно с заголовком «Помощь по lua-скриптам» и содержимым про Lua: `RESULT`, `CONTEXT` как объект задачи/пользователя/письма, `var_dump`, `is_empty`, вкладка «Смарт-действия». В Python и остальных языках эти имена и конструкции не работают.

**Причина:** справка — единый текст из ресурсов интерфейса (ключи `luaDoc.*` словаря `common`). Язык скрипта в компонент справки не передаётся, переключение её содержания по языку не реализовано; кнопка выводится в тулбаре независимо от языка, а заголовок берётся из ресурса `common.helpWithLuaScripts`.

**Обходной путь:** штатного переключателя справки нет. Для Python ориентир — [python-scripting.md](https://help.1forma.ru/domains/smart-actions/python-scripting.md): ключи `ctx`, возврат результата, ограничения песочницы; для остальных языков — документы домена по соответствующему языку.

## 11. Правило «После смены ДП» из другого правила читало значение параметра до изменения

> Статус: исправлено

**Симптом:** правило на событие «После смены ДП», запущенное смарт-действием другого правила, в смарт-выражении получало прежнее значение изменённого параметра — и при чтении из денормализованной таблицы категории, и из значений параметров. Тот же результат давало сохранение пачки параметров одной операцией.

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

**Решение:** признак «читать основной строкой подключения внутри текущей транзакции» удерживается на самой транзакции — при сохранении значения параметра и при сохранении пачки транзакция помечается, и смарт-выражение внутри неё читает записанное значение. Вне таких операций поведение не изменилось. Вошло в сборку 2.268 «Скульптор».

## 12. «Время ожидания выполнения истекло» по смарт-выражению при сохранении факта

> Статус: исправлено в 2.268 «Скульптор»

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

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

**Исправлено.** На MS SQL выражение в режиме XML-формулы считается в транзакции вызвавшего запроса и в его сессии, поэтому видит записанное в этой же транзакции. Выражения с собственным T-SQL под отдельной строкой подключения и поведение на PostgreSQL не менялись.

**Обходной путь (на сборках до исправления):** нет; записи о таймауте по сохранению факта на исправленной сборке не воспроизводятся. Если симптом остался — проверить, что у выражения нет собственного T-SQL, и уточнить сборку площадки.

## 13. Подсказка типа результата действия «Распаковать» расходилась с рантайм-типом при пропущенных записях

> Статус: исправлено (сборка 2.268 «Скульптор»)

**Симптом:** в редакторе смарт-скриптов у действия «Распаковать» подсказка результата показывала `int[]`; если в архиве были повреждённые записи или записи, пропущенные по лимитам, скрипт получал объект с полями `fileIds` и `skippedEntries`, и штатные приёмы работы с массивом (`#`, `ipairs`, `table.insert`) на нём не работали. Смарт-выражение последующего действия, ссылавшееся на результат разархивации, падало с ошибкой Entity SQL.

**Причина:** действие объявляет `ResultType = int[]`, а при пропущенных записях возвращает `UnzipArchiveResult`; в параметр смарт-выражения значение попадало без приведения к массиву.

**Исправлено.** Подсказка в редакторе показывает обе формы — `int[] | { fileIds (int[], 1..n), skippedEntries (string[], 1..n) }`, значение `UnzipArchiveResult` на границе Lua отдаётся таблицей, а в параметр смарт-выражения передаётся массив идентификаторов распакованных файлов. Подробности о форме результата — в [справочнике действий](https://help.1forma.ru/domains/smart-actions/actions-reference-files-data.md), раздел «Файлы (расширение)».

**Обходной путь:** нет. Признак старого поведения — подсказка результата у действия «Распаковать» показывает только `int[]`, а при пропущенных записях скрипт получает объект.

**Связанные документы:**

- [Файлы — справочник действий](https://help.1forma.ru/domains/smart-actions/actions-reference-files-data.md), раздел «Файлы (расширение)»

## 14. «Экспортировать данные в Excel»: выгрузка падает на вычисляемых текстовых колонках

> Статус: в работе

**Симптом:** действие отказывает с текстом `Column '<имя колонки>' is read only`, файл не создаётся. На PostgreSQL так ведёт себя любая функция с текстовой колонкой; на MS SQL — процедура, у которой строковая колонка получена выражением: склейкой строк, `CASE`, `ISNULL`, литералом, параметром процедуры или `CAST`/`CONVERT`.

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

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

**Когда обращаться в поддержку 1Ф:** если процедура отдаёт только колонки из таблиц и отказ всё равно воспроизводится — приложить версию площадки, текст ошибки из журнала ошибок платформы и текст процедуры.

## 15. «Экспортировать данные в Excel»: ошибка после второго набора не останавливает действие

> Статус: в работе

**Симптом:** процедура на MS SQL вернула два и более набора и упала после второго (`RAISERROR` уровня 11 и выше, `THROW`, ошибка выполнения) — действие завершается успешно, в задачу вкладывается файл с данными первого набора, в журнале ошибок записи нет, пакет идёт дальше. При откате транзакции внутри процедуры изменения откатываются, а файл с данными отменённого расчёта остаётся в задаче.

**Причина:** чтение останавливается на первом наборе с колонками; остаток ответа дочитывается при закрытии соединения, а ошибка этой части при закрытии не поднимается. На PostgreSQL проблема не проявляется: функция, завершившаяся ошибкой, строк не отдаёт и вызов падает целиком.

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

**Когда обращаться в поддержку 1Ф:** при подозрении на частичные данные в приложенном файле — приложить версию площадки и текст процедуры: по журналу пакета такой отказ не виден.

## 16. Пользовательские смарт-действия не загрузились — редактор пакета отдаёт ошибку или пустой список

> Статус: исправлено в 2.268 «Скульптор»

**Симптом:** после сбоя загрузки пользовательских действий обращение к смарт-действию по идентификатору пользовательского действия отдавало `NullReferenceException` — в интерфейсе и в фоновых заданиях; каталог доступных типов действий в редакторе пакета отдавал ошибку с пустым списком вместо стандартных групп; предварительная загрузка пользовательских действий не сообщала о сбое.

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

**Исправлено.** Сбой загрузки больше не маскируется: он записывается в журнал ошибок платформы и возвращается как управляемая ошибка «Загрузка пользовательских действий не удалась» с исходной причиной внутри. Обращение по идентификатору пользовательского действия даёт эту управляемую ошибку; каталог имён в редакторе пакета возвращает стандартные действия и не ломается; предварительная загрузка возвращает признак «пользовательские действия не загружены». Стандартные действия и безопасное разрешение имени действия работают как раньше.

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

**Когда обращаться в поддержку 1Ф:** если на исправленной сборке пропадают и стандартные действия либо сбой повторяется после перезапуска пула приложения — приложить версию площадки и текст записи из журнала ошибок платформы.

## 17. «Отправить системный email» без почтового ящика падал разыменованием вместо ошибки конфигурации

> Статус: исправлено в 2.268 «Скульптор»

**Симптом:** пакет с действием «Отправить системный email» (`SendMailBoxEmail`, ID 44) при незаполненном параметре «Email отправителя» завершался технической ошибкой — `NullReferenceException`, а при ящике без идентификатора — `InvalidOperationException` — вместо сообщения о незаполненном параметре. Проявлялось в раздельной рассылке (параметр «Каждому адресату отдельное письмо»).

**Причина:** в ветке раздельной отправки ящик читался без проверки — `mailBox.MailBoxId.Value` в `TCClassLib/Smart/Actions/ActionsInfoProvider.Methods.cs`, при том что отсутствие ящика выше допускалось.

**Исправлено.** Проверка ящика и его идентификатора выполняется один раз до ветвления — `if (mailBox?.MailBoxId == null)` в `SendMailBoxEmail`; при пустом ящике действие отдаёт ошибку конфигурации с указанием «Email отправителя». Отправка с заданным ящиком — и раздельная, и общая — не изменилась; общая отправка без ящика тоже стала возвращать ошибку конфигурации — это принятое расширение поведения, а не прежний сценарий.

**Обходной путь:** нет. На сборках до исправления раздельная отправка без ящика отвечает технической ошибкой.

**Когда обращаться в поддержку 1Ф:** если на исправленной сборке отправка с корректно заданным ящиком завершается ошибкой конфигурации — приложить версию площадки, имя пакета и текст ошибки из журнала пакета.

---

## 18. Тип контекста смарт-скрипта затирался сохранением

> Статус: исправлено в 2.268 «Скульптор»

**Симптом:** после переименования, публикации или сохранения кода скрипт терял контекст — `CONTEXT` оказывался пустым, хотя раньше скрипт с задачей работал. Тип мог «прыгать»: один и тот же скрипт в тесте редактора выполнялся то с контекстом, то без.

**Причина:** тип контекста нигде не задавался явно и доезжал из адреса редактора. При открытии скрипта из действия пакета в адресе стоял тип «задача», и бэкенд подменял им значение из базы — любое сохранение записывало «задача» независимо от того, что было в скрипте, а обновление по API без поля типа записывало «без контекста».

**Решение:** у существующего скрипта тип всегда берётся из базы и меняется только отдельным вызовом смены типа; адрес редактора и обычное сохранение тип не трогают. См. [Редактор смарт-скриптов](https://help.1forma.ru/domains/smart-actions/admin.md).

**Обходной путь на сборках до исправления:** не пересохранять скрипт из действия пакета; тип деплоить по API вместе с полем типа в DTO.

## 19. Пакет по событию откатывал все итерации списочного действия вместе со сбойной

> Статус: исправлено в 2.268 «Скульптор»

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

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

**Решение:** событийный запуск пакета со списочным действием изолирует сбойную итерацию — её записи откатываются, а записи успешных итераций и запись в журнале автоматизации сохраняются после фиксации объемлющей операции. Поведение прямого запуска пакета, запуск вне транзакции и пакеты без сбоев не изменились; сбой вне итераций списка по-прежнему откатывает пакет целиком. См. [Типичные проблемы действий пакета](https://help.1forma.ru/domains/smart-actions/action-package-pitfalls.md), раздел 4.2.

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

