# Смарт-фильтры — Администрирование

Смарт-выражения (SmartExpressions) — универсальный механизм условий и вычислений в 1Форме: фильтры в гридах и виджетах, условия правил в smart-actions, вычисления параметров. Раздел описывает интерфейсы администрирования (SPA-страницы и Admin API), справочник синтаксиса и типичные ошибки настройки.

## Обзор

Раздел охватывает создание и редактирование смарт-выражений (SmartExpressions) — универсального механизма условий и вычислений в 1Форме. Смарт-выражения используются как:

- **Фильтры** (`IsFilter = 1`) — условия отбора в гридах, виджетах, отчётах
- **Условия правил** (`IsFilter = 0`) — триггерные условия в smart-actions
- **Вычисления** — значения параметров в пакетах действий
- **Смарт-доступ** — динамические права

Администрирование использует:

- **Отдельные SPA-страницы** — 3 формы (SmartExpressions, SmartQueries) — зарегистрированы в дереве автоадминки, но открывают собственные SPA-страницы
- **Admin API** — 6 контроллеров (редактор, конструктор, тест-кейсы)

## Механизмы администрирования

### Отдельные SPA-страницы (в дереве администрирования)

Ключевые операции вынесены на отдельные страницы интерфейса администрирования:

| Alias формы | Название | Url | Таблица БД | Папка |
|-------------|----------|-----|-----------|-------|
| `general-smart` | Общие смарты | `/administration/smart-packs` | dbo.SmartExpressions | Общая бизнес-логика |
| `all-smart` | Все смарты | `/administration/smart-packs?globalEventsOnly=false` | dbo.SmartExpressions | Общая бизнес-логика |
| `rebus-queues` | Очереди шины сообщений | `/administration/rebus-queues` | dbo.SmartQueries | Системные настройки |

Формы `general-smart` и `all-smart` разделяются с доменом smart-actions (обе работают с `dbo.SmartExpressions`). Smart-filters отвечает за создание/редактирование выражений, smart-actions — за привязку к событиям и пакетам.

### Admin API контроллеры

#### Редактор выражений, запросы и конструктор

Методы API редактора выражений:

| Маршрут | Методы | Назначение |
|---------|--------|------------|
| `/api/admin/smart/expressions` | GET, POST, DELETE | CRUD смарт-выражений, список, фильтрация |
| `/api/admin/smart/expression-editor` | GET, POST | Загрузка/сохранение выражения в визуальном редакторе |
| `/api/admin/smart/expression-constructor` | GET, POST | QueryBuilder-конструктор (визуальное дерево условий) |
| `/api/admin/smart/expression-test-cases` | GET, POST, DELETE | CRUD тест-кейсов для отладки выражений |
| `/api/admin/smartexpressions/list` | POST | Список смарт-выражений для дропдауна выбора; принимает `resultTypes` и возвращает по каждому смарту признак совместимости `isCompatible`, не исключая несовместимые из выдачи |
| `/api/admin/smartexpressions/references` | POST | Места использования смарт-выражений, структурированно по типу связи. Отбор выражений — тем же запросом, что и у списка (`expressionIds`, `subcatIds`, `resultTypes`, `isFilter`, `origin`, пейджинг). В ответе: `references` — по каждому выражению его атрибуты (`outputResultType`, `outputResultDescription`, `event`) и список мест использования; `total` — сколько выражений отобрано; `outputResultTypes` — справочник типов результата для фильтра. Собираются связи всех поддерживаемых видов: правила и пакеты действий, шаги маршрутов, кнопки, подписи, ДП и их блоки, источники данных, почта, очереди, телефония, расписания, портал, права доступа, общие настройки приложения. Место использования несёт раздел-владелец (категория либо код `objectKind`: `Category`, `AppSettings`, `AccessRights`, `Workplaces`, `Portal`, `Signatures`, `Lists`, `Mail`, `Queues`, `Telephony`, `Menu`), целевой объект (`target.id`, `target.name`), текст «Используется в» (`usedIn`), имя поля-приёмника (`usageField`) и ожидаемый тип значения (`expectedOutputResultType`), выведенный по типу поля-приёмника. У связи-правила (`EventAction`) есть `ruleId` — по нему из грида открывается вкладка правил с отбором по этому правилу. Ссылки дедуплицируются и ограничиваются 25 записями на одно выражение. Осиротевшая привязка — на удалённый пакет действий — из списка исключается; пакет с удалённой категорией в списке остаётся, подкатегория в записи пустая В сборке 2.268 в ответ добавились ещё четыре вида связи, которые раньше в выдачу не попадали, — они перечислены в разделе «Места использования и ожидаемый результат в гриде смартов»; для них заполнено имя объекта в `target.name`. |

**Состав колонок грида.** На странице смарт-выражений две вкладки. Вкладка «Smart выражения» показывает колонки по порядку: идентификатор, тип выражения, название (ссылка в редактор), объект-владелец, «Используется в», «Зависит от события» и принимаемый результат. Вкладка «Smart фильтры»: идентификатор, тип фильтра, название (ссылка в редактор), объект, «Используется в», «Зависит от события» и признак доступа; над гридом остаётся переключатель «Все / Доступен в смарт поиске», который отбирает строки по признаку доступности в смарт-поиске. Колонки «Используется в» и «Принимаемый результат» — самые широкие: в них выводится перечень мест подключения и ожидаемый тип результата.

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

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

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

**Виджет «Профиль пользователя».** Для блока `GridBlockType.UserProfile` раздел-владелец `Portal` фиксирует использование выражения в двух ролях: как источник пользователя (`SourceType = Smart`) и как подстрочник карточки (`SubtitleType = Smart`). Если выражение назначено одновременно источником и подстрочником, связь дедуплицируется — показывается одна запись блока с порталом. Удаление выражения, привязанного к блоку, также блокируется: иначе виджет перешёл бы в нерабочее состояние «недоступно».

Методы API запросов и конструктора условий (в составе раздела редактора):

| Маршрут | Методы | Назначение |
|---------|--------|------------|
| `/api/admin/smart/query` | GET, POST | SmartQueries — конфигурации выборок для виджетов |
| `/api/admin/smart/query-constructor` | GET, POST | Конструктор TSQL-запросов |

## Ключевые настройки

### Создание и редактирование выражений

**Где настраивается:** Admin API (`/api/admin/smart/expression-editor`, `/api/admin/smart/expressions`)
**Таблица БД:** `dbo.SmartExpressions`

Два способа создания:

1. **Визуальный редактор** — через дерево свойств сущностей и панель операторов и функций
2. **TSQL-режим** — прямой ввод SQL-кода в `TSqlContent`

**Свойства вложенных сущностей под оператором коллекции.** В визуальном редакторе под оператором коллекции раскрываются не только свойства элемента первого уровня, но и свойства вложенных связанных сущностей — на любую глубину: при выборе такого свойства в формулу вставляется полный путь к нему. Синтаксис обращения к вложенным свойствам в готовом выражении — в [read-data.md](https://help.1forma.ru/domains/smart-filters/read-data.md), раздел про ДП Lookup и Multilookup.

**Лукап-колонки ДП «Таблица» в дереве свойств.** Если ДП «Таблица» содержит колонку типа «Лукап» (ссылка на задачи другой категории), при выборе такой колонки в дереве свойств визуального редактора раскрываются сущности связанной категории — параметры задач (номер, текст, срок, исполнитель, ДП целевой категории), а не технические поля строки таблицы (`ColumnID`, `RowID`, `Value`). Так условия строятся по свойствам выбранных задач, а не по идентификаторам строк.

**Поля SmartExpressions** (16 в форме `general-smart`):

| Поле | Тип | Что контролирует |
|------|-----|------------------|
| `Content` | nvarchar(max) | XML-формула визуального конструктора |
| `TSqlContent` | nvarchar(max) | TSQL-код (ручной ввод, приоритет над Content) |
| `Name` | nvarchar | Название выражения |
| `SubcatID` | int | Привязка к категории (NULL = глобальное) |
| `IsFilter` | bit | Тип: фильтр (true) или условие правила (false) |
| `EventID` | int | Событие-триггер (только для условий правил) |
| `ParameterValue` | nvarchar | Уточнение параметра события |
| `NoContextMode` | int | Режим без контекста задачи |
| `AvailAsSmartSearch` | bit | Доступно как смарт-поиск в гридах |
| `Public` | bit | Публичное (видимо другим администраторам) |
| `OwnerUserId` | int | Владелец выражения |
| `QueryBuilderState` | nvarchar(max) | Состояние QueryBuilder (JSON) |
| `OriginID` | int | Контекст использования (Origin enum) |
| `IsDeleted` | bit | Мягкое удаление |
| `GUID` | uniqueidentifier | Уникальный ID для миграции |
| `ID` | int | Primary key |

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

**Колонка «Доступ» в гриде смартов.** На странице `/administration/smart-packs` на вкладке «Фильтры» колонка «Доступ» между «Название» и «Доступен в смарт поиске» показывает признак `Public`: публичное выражение отмечено иконкой-глобусом с подсказкой «Публичный — доступен всем», остальные — замком с подсказкой «Доступ ограничен — доступен только владельцу». На экране почтовых серверов и в смартах шага маршрута эта колонка скрыта.

**Колонка «Доступен в смарт поиске».** На той же вкладке «Фильтры» выражение, доступное как смарт-поиск в гридах (`AvailAsSmartSearch`), отмечено галочкой, недоступное — крестиком.

#### Отборы категории

**Признак и отборы категории.** Тот же признак «Доступен в смарт поиске» (`AvailAsSmartSearch`) делает смарт-фильтр отбором категории: выражение с признаком, принадлежащее категории, показывается в списке отборов категории — на странице «Отборы» в настройках категории и в меню «Отбор» на панели инструментов таблицы задач. Со страницы «Отборы» чип на карточке открывает редактор этого выражения, а кнопка «Создать отбор» открывает то же окно создания смарт-выражения с признаком, включённым и заблокированным, — снять его там нельзя. Снятие признака в гриде смартов убирает отбор из списка. Страница «Отборы» описана в [настройках категории](https://help.1forma.ru/domains/categories/admin.md).

**Места использования и ожидаемый результат в гриде смартов.** В гриде смартов на странице `/administration/smart-packs` применение выражения показывается его местами использования:

- **Объект** — раздел-владелец: категория либо раздел интерфейса (настройки приложения, права доступа, рабочие места, портал, подписи, списки, почта, очереди, телефония, меню).
- **Используется в** — конкретная точка подключения выражения: правило, шаг маршрута, кнопка, подпись, ДП или его блок, параметр действия пакета, расписание, источник данных. У правила рядом с точкой стоит идентификатор правила: клик по нему открывает вкладку правил с отбором по этому правилу.
- **Принимаемый результат** — тип значения, который ожидает поле-приёмник в этой точке (признак, строка, набор строк, пользователь и др.). Пустая ячейка — приёмник принимает любой тип. Колонка показывает расхождение между объявленным типом выражения и контрактом поля-приёмника.
- **Зависит от события** — событие-триггер, закреплённое за выражением.

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

**Четыре вида связи, добавленные в выдачу в сборке 2.268.** Вместе с остальными видами в ответе приходят строки четырёх видов — тех, что привязывают смарт к объекту настройки, а не к действию:

| Вид связи | Где живёт объект | Что приходит в `target.name` |
|---|---|---|
| `LookupParamSettings` | настройка источника данных лукапа | имя настройки |
| `ExtParamFileSetting` | настройка дополнительного параметра типа «Файл» | имя настройки |
| `TaskUniversalTemplate` | универсальный шаблон задачи | имя шаблона |
| `WorkplacesFilter` | фильтр рабочего места | имя фильтра |

Что важно при чтении такой выдачи:

- дедупликация и лимит 25 записей считаются на всё выражение; строки этих четырёх видов входят в тот же счёт, а не добавляются сверху;
- имя целевого объекта заполнено у всех четырёх видов: у `ExtParamFileSetting`, `TaskUniversalTemplate` и `WorkplacesFilter` — имя связанного объекта, у `LookupParamSettings` — имя связанного дополнительного параметра с наименьшим идентификатором. Если у настройки лукапа связанного дополнительного параметра нет, имя остаётся пустым, и такая строка в гриде не отображается.

**Состав одной записи места использования.** Место приходит из ответа `POST /api/admin/smartexpressions/references` и состоит из подписи объекта и строк места: подпись объекта — из `owner.kindLabel` («Пакет действий», «Настройка лукапа», «ДП-файл», «Универсальный шаблон», «Блок»), строки места — из `rows` (`label` — что это за строка, `value` — значение, `link` — переход, если у вида он есть), в устойчивом порядке отображения. Если имя объекта не задано, в строке показывается ID настройки; строки, для которых переход не предусмотрен, выводятся текстом без ссылки. Ожидаемый тип результата у места виден отдельно — полем `expectedOutputResultType`; для видов, где он пока не определён, поле пустое.


**Приоритет выполнения:**

1. `TSqlContent` (если задан) — прямой TSQL
2. `TSqlCompiled` (скомпилированное выражение)
3. `Content` (формула визуального конструктора)

**Глобальные смарт-фильтры в меню «Преднастроенный отбор».** Смарт-выражение без привязки к категории (`SubcatID` = NULL) с типом «фильтр» (`IsFilter`) и признаком «Доступен как смарт-поиск» (`AvailAsSmartSearch`) попадает в меню «Преднастроенный отбор» расширенного поиска, если оно публичное (`Public`), принадлежит текущему пользователю (`OwnerUserId`) либо открыто одной из его групп. Открытие группам задаётся при создании и редактировании выражения третьим вариантом доступа — «Группам»; связь выражения с группами хранится отдельной таблицей. Владелец проставляется только личному выражению («Только мне»); у публичного и у группового выражения владельца нет, поэтому в выдачу они попадают по признаку публичности или по группе, а не по владельцу. Такой отбор применяется без ввода текстового запроса, наряду с личными пользовательскими отборами. Смарт-фильтры с привязкой к категории работают иначе — как отбор в табличном представлении своей категории.

#### Вкладка «Код смарта»

В редакторе смарт-выражения есть вкладка **«Код смарта»** — редактируемый код выражения на языке смартов (`smart1f`). Раньше вкладка была доступна только для чтения, теперь её можно править, и правка сохраняется вместе с самим смарт-выражением.

- текст вкладки и визуальный конструктор — две проекции одного выражения: правка кода попадает в сохраняемое выражение, а собранное в конструкторе — в код вкладки;
- словарь языка обновляется по данным редактора: после загрузки выражения во вкладку передаются панель операторов и дерево объектов, поэтому подсказки соответствуют текущему контексту выражения;
- регистр ключевых слов (`если`, `то`, `иначе`, `конец`, `В случае`) при вводе приводится к каноническому виду автоматически;
- редакторы TSQL и PGSQL на этой же странице работают на своём языке и на язык смартов не переключаются; информационная панель и подсказки комментариев к вкладке кода не подключены.

#### Окно «Информация» в редакторе смарт-выражения

В редакторе сохранённого смарт-выражения кнопка **«Информация»** открывает окно со сведениями о местах, где выражение используется, и о принимаемом результате для каждого места. Кнопка доступна после сохранения выражения, когда у него появился идентификатор.

Состав сведений зависит от вида привязки: например, для параметра действия показываются категория (либо «Общие SMART»), пакет действий, действие, параметр и принимаемый результат; для правила на событии — категория, событие, правило и принимаемый результат; для настройки категории — категория, название объекта или настройки и принимаемый результат. Окно строит строки по данным самого места, поэтому набор строк не обязан быть одинаковым для всех видов связей.

Принимаемый результат сопровождается подсказкой о типе. Если ожидаемые типы в местах использования различаются, окно отмечает несовпадение; тип `null` или `Undefined` сам по себе конфликтом не считается. Ссылки на категорию и настройку открывают целевую страницу в новой вкладке.

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

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

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

### Origin — контекст использования

**Где настраивается:** атрибут `originId` при создании выражения через API

| Значение | Имя | Контекст |
|----------|-----|----------|
| 0 | Mail | Почтовые ящики |
| 1 | CustomPages | Кастомные страницы, виджеты, подписи, отчёты |
| 2 | Subcat | Подкатегории задач (дефолт) |
| 3 | Queue | Очереди |
| 4 | EdocumentLink | Э-документы |
| 5 | EdocumentSbisLink | Э-документы СБИС |

**Правило валидации:** если задан `SubcatId` — origin обязан быть `Subcat (2)`. Исключение: старые контролы (прежняя админка) могли создавать выражения с `SubcatId` и `origin=1 (CustomPages)` — такие записи существуют в БД, но при пересохранении через SPA origin будет приведён к `2`.

### SmartQueries (выборки для виджетов/отчётов)

**Где настраивается:** SPA-страница `/administration/rebus-queues` (пункт `rebus-queues` в дереве автоадминки) / Admin API (`/api/admin/smart/query`)
**Таблица БД:** `dbo.SmartQueries`

| Поле | Что контролирует |
|------|------------------|
| `Name` | Название выборки |
| `SmartFilterID` | Смарт-выражение для фильтрации |
| `RecordsLimit` | Лимит записей |
| `SubcatID` | Привязка к категории |
| `XAxisExpressionID` | Выражение для оси X (графики) |
| `YAxisExpressionID` | Выражение для оси Y |
| `AggregationTypeID` | Тип агрегации |
| `SortByXAxis` | Сортировка по X |
| `SortAsc` | Направление сортировки |
| `EventID` | Событие (для контекста) |

SmartQueries — связующее звено между смарт-выражениями и UI-виджетами. Каждый SmartQuery ссылается на один или несколько SmartExpressions через `SmartFilterID`, `XAxisExpressionID`, `YAxisExpressionID`.

### Редактор выражений

**Где настраивается:** Admin API (`/api/admin/smart/expression-editor`)

Визуальный редактор — ключевой инструмент администратора. Состоит из:

1. **Дерево свойств** — свойства сущностей (Задача, Пользователь, Комментарий…), из которых строится выражение
2. **Панель операторов** — операторы и функции
3. **Визуальное представление** формулы

**Основные методы редактора:**

| HTTP | Маршрут | Назначение |
|------|---------|------------|
| GET | `{id}/editor` | Загрузить выражение в редактор |
| POST | `editor` | Сохранить выражение |
| POST | `editor/execute` | Выполнить и проверить выражение |
| POST | `editor/tree/reload` | Перестроить дерево свойств |
| POST | `editor/tree/expand` | Развернуть узел дерева |
| GET | `editor/toolbar/objects` | Список объектов для вставки по GUID (диалог кнопки «ID объекта») |

**Вставка ссылки на объект («ID объекта»).** Кнопка **«ID объекта»** в панели инструментов редактора открывает диалог **«Выбрать»** со списком групп объектов (категории, орг. единицы, порталы, разделы, статусы, группы, дополнительные параметры — набор зависит от конфигурации). Группа раскрывается в список объектов; выбор объекта вставляет в выражение его **GUID**. Группы без объектов не выбираются. В диалоге доступен поиск по списку объектов: отбор идёт по названию объекта и по его идентификатору, найденные объекты остаются внутри своих групп (группы с совпадениями раскрываются автоматически), поэтому разделение результатов по типам объектов сохраняется.

**Адресация параметра события.** Ссылка на параметр события хранится в разметке выражения узлом `<eventparameter>` с атрибутом `index`. Условие адресует параметр именно по этому номеру: при вычислении номер разворачивается в параметр запроса `@EventParamN`. В разметке визуального редактора параметр представлен блоком с тем же атрибутом `index`, поэтому номер сохраняется при сохранении и повторном открытии выражения в редакторе.

**Контекст вложенных конструкций.** Сложное свойство и коллекционный оператор в разметке выражения (`<complexproperty>`, `<collectionoperator>`) несут признак `isrootcontext`. При значении `false` конструкция вычисляется в контексте ближайшей охватывающей конструкции — алиас источника берётся от неё; при `true` — в контексте корневой сущности выражения. Поэтому условие, построенное внутри вложенной конструкции, адресует элемент этой конструкции, а не корневой объект выражения.

**Редактирование условия во вложенной коллекции.** Команда «Редактировать условие» для условия коллекционного оператора, вложенного в другой коллекционный оператор, строит дерево полей по элементу самой внутренней коллекции: путь условия разбирается от внешней коллекции к внутренней. Если путь разобрать не удаётся, редактор выводит сообщение, которое начинается словами «Не удалось редактировать условие:», — например, «свойство в пути условия не найдено» или «не удалось определить тип поля в пути условия».

**Параметры функции внутри условия.** Аргументы функции открываются в редакторе защищёнными от случайной правки: чтобы изменить ссылку на свойство или значение, нужна команда «Редактировать параметры» из меню аргумента. Команда работает и внутри условия оператора коллекции — например, в выражении `в Все пользователи нет элемента, такого что СтрокаСодержит(Сменивший.Ник, 'Иванов')` после «Редактировать условие» меню аргумента функции в этом условии содержит только «Редактировать параметры». Удалить внешнюю конструкцию или открыть её условие из этого меню нельзя.

> После команды «Редактировать параметры» аргумент функции становится редактируемым. Ввод выполняется в том аргументе, куда установлен курсор.

**Границы режима редактирования условия.** Пока условие открыто, ввод и выделение остаются внутри него: стрелки влево и вправо, `Home` и `End` на границе условия не выводят курсор за его пределы, а внешнее выражение с клавиатуры не правится. Клавиши вставки (`Backspace`, `Delete`) на границе условия действуют внутри него. Пока фокус в условии, на экране один курсор — в месте ввода; при переходе фокуса в дерево или на панель место вставки показывается одним индикатором и исчезает при возврате фокуса. Вставка элемента из дерева и с панели работает как раньше: элемент появляется в позиции курсора, дальнейший ввод идёт сразу после него.

### Тестирование выражений через execute и тест-кейсы

**Сравнение результатов тестирования двух версий.** В интерфейсе сравнения версий редактора смарт-выражений (при открытой истории версий и выбранной паре ревизий) команда тестирования вызывает выполнение выражения параллельно дважды — отдельно для левой и правой версий. Это позволяет администратору на одном экране сопоставить не только разницу исходного кода, но и результирующие наборы данных, сформированный запрос и планы выполнения обеих версий.

**Endpoint:** `POST /api/admin/smart/expressions/editor/execute`

Выполняет SE и возвращает результат + execution plan. Работает из Admin UI (кнопка «Тест» в редакторе выражений) и через curl.

**URL Admin UI:** `/spa/noframe/administration/smart-expressions?SubcatId={subcatId}&ID={seId}&EventID={eventId}&ActionsPackID={packId}`

**Ключевые параметры payload:**

| Параметр | Тип | Назначение |
|----------|-----|-----------|
| `expression` | object | Полный объект SE (id, tSql, pgSql, subcatId, isFilter и т.д.) |
| `context.eventId` | string | Событийный контекст: `"AfterTaskCreate"`, `"BeforeTaskChange"` и т.д. Позволяет тестировать событийно-зависимые SE |
| `context.subcatId` | int | Категория контекста |
| `context.isFilter` | bool | Тип: фильтр или условие |
| `params.contextId` | int/null | ID задачи-контекста. `null` → используется фиктивный ID (`-1`) |
| `generateDummyParams` | bool | Автоподстановка фиктивных параметров (`@ContextId = -1` и т.д.) |
| `getExecutionPlan` | bool | Вернуть план выполнения (EXPLAIN ANALYZE для PG) |
| **`runOnSmartPgConnection`** | **bool** | **Ключевой флаг.** `true` → выполняет `pgSql` на PG через `SmartPgConnectionString`. `false`/отсутствует → выполняет `tSql` на MSSQL |

**Ответ (`data`):**

| Поле | Описание |
|------|----------|
| `filterResult` | Результат фильтра (bool) |
| `objectResult` | Результат для non-filter SE |
| `tSql` | Итоговый SQL (tSql или pgSql в зависимости от `runOnSmartPgConnection`) |
| `executionPlan.executionPlanMsSql` | MSSQL plan (если запрашивался, требует SHOWPLAN permission) |
| `executionPlan.executionPlanPostgreSql` | PG EXPLAIN ANALYZE JSON (если `runOnSmartPgConnection: true`) |
| `matchExpectedResult` | Совпадение с ожидаемым типом |
| `actualResultType` | Фактический тип результата |

**Пример curl (PG):**
```bash
curl -s -H "1F-Pat: $PAT" -H "Content-Type: application/json" \
  -X POST "https://{host}/api/admin/smart/expressions/editor/execute" \
  -d '{"expression":{...},"context":{"eventId":"AfterTaskCreate","subcatId":5574,"isFilter":true,...},"params":{"contextId":null,"eventParameters":[]},"getExecutionPlan":true,"generateDummyParams":true,"runOnSmartPgConnection":true}'
```

**Известные ограничения:**

- `generateDummyParams: true` подставляет `@ContextId = -1` — выражения с JOIN на контекстную задачу вернут пустой результат, но синтаксическая проверка пройдёт
- `generateDummyParams: true` требует `expression.id`: для несохранённого выражения запрос отклоняется сообщением «Не удалось сформировать тестовый сценарий: у смарт-выражения не задан идентификатор. Сохраните выражение перед запуском.» Редактор включает этот флаг, когда не заданы ни номер задачи, ни параметры события; с `params.contextId` или параметрами события выполняется формула из `expression` без обращения к идентификатору
- Execution plan для MSSQL требует SHOWPLAN permission на БД (у PAT-пользователя может не быть)

Позволяет создавать тестовые наборы данных для отладки смарт-выражений. Тест-кейс = набор входных параметров + ожидаемый результат.

Контрол выбора смарт-выражения (dropdown) принимает параметры:

| Параметр | Дефолт | Влияние |
|----------|--------|---------|
| `originId` | 2 (Subcat) | Контекст: какие свойства доступны в дереве |
| `subcatId` | null | Привязка к категории (фильтрует выборку) |
| `isFilter` | true | Тип: фильтр или условие |
| `eventId` | null | Событие (для условий правил) |
| `smartExpressionId` | null | Текущее выбранное выражение |


**Совместимость по типу результата (`resultTypes` / `isCompatible`).** Запрос `POST /api/admin/smartexpressions/list` принимает необязательный список допустимых типов результата `resultTypes`. Он работает как пометка совместимости, а не как фильтр: состав и порядок выдачи не меняются, несовместимые смарты остаются в ответе. По каждому смарту возвращается флаг `isCompatible`:

- `resultTypes` пуст или не передан — `isCompatible = true` у всех;
- список задан — `isCompatible = true`, когда `outputResultType` смарта входит в список;
- смарты без типа (`outputResultType = null`) всегда помечаются совместимыми, чтобы не заблокировать уже выбранное в настройках значение.

Флаг предназначен для клиентской пометки совместимости — несовместимые смарты не исключаются из выдачи. Клиент посылает ключ `resultTypes` всегда: при незаданном наборе допустимых типов он уходит со значением `null`. Для сервера отсутствие ключа и `null` равнозначны — список не ограничивается, все элементы помечаются совместимыми. Права проверяются как и прежде (`CheckGodOrSubcatAdmin`).

Значения `resultTypes` и `outputResultType` передаются **строковыми именами** типа результата — это общая сериализация перечислений в API, а не особенность метода. Перечисление `SmartExpressionOutputResultType` содержит 50 значений:

| Группа | Значения |
|--------|----------|
| Базовые типы | `Boolean`, `Number`, `String`, `DateTime`, `StringCollection`, `Enum` |
| Сущности и их коллекции | `User`, `UserCollection`, `Group`, `GroupType`, `GroupCollection`, `Task`, `TaskCollection`, `File`, `FileCollection`, `OrgStructureType`, `OrgStructureUnit`, `OrgStructureUnitCollection`, `Subcategory`, `ExtParam`, `ExtParamValues`, `CustomParamValues`, `State`, `RouteStep`, `SmartAccess`, `TaskSignature`, `SignatureResolution`, `Mail`, `MailBox`, `MailBoxFolder` |
| Служебные перечисления | `CommentType`, `TaskLinkType`, `TimeZone`, `Language1F`, `FavoritesColor`, `QueueType`, `QueueFlow`, `NotificationType`, `StandartActionCancelType`, `ConvertionFormats`, `DocumentsOnWhichInsert`, `PagesOnWhichInsert`, `PlaceForPicture`, `MeetingAction`, `CalendarEventFreeBusyStateEnum`, `HttpRequestMethod`, `RMSmartActionType`, `CatalogResource`, `PushSounds` |
| Не определён | `Undefined` — у смарта тип результата не определён |

Соответствие «тип параметра действия → тип результата» считает бэкенд (`SmartExpressionOutputResultTypeHelper.MapToResultType`): целочисленные и вещественные типы, включая `byte` и `decimal`, сводятся к `Number`, `string[]` — к `StringCollection`, незнакомый перечислимый тип — к `Enum`. Справочник типов для фильтра отдаёт `POST /api/admin/smartexpressions/references` (поле `outputResultTypes`), подписи значений берутся из словаря локализации.

**Признак публичности в списке выражений.** Ответ списка смарт-выражений (`POST /api/admin/smartexpressions/list`) содержит по каждому элементу флаг `isPublic` — значение поля `Public`. Флаг используется в гриде смартов для колонки «Доступ».


**Как связи попадают в грид.** Ответ справочника мест использования содержит плоские записи; каждая относится к смарту по идентификатору выражения, а строки грида собираются группировкой по этому значению, поэтому места использования разных смартов не смешиваются. Колонка «Объект» показывает категорию или объект-владелец, колонка «Используется в» — конкретное место подключения: для каждой связи выводятся её строки в исходном порядке, где подпись и значение берутся из самой строки, а значение становится ссылкой, только если у строки задана ссылка. Колонка «Принимаемый результат» показывает ожидаемый тип результата по справочнику типов.
## Справочник синтаксиса — дополнения к read-data.md

Базовый справочник операторов и функций для чтения данных — в [`read-data.md`](https://help.1forma.ru/domains/smart-filters/read-data.md): числовые/строковые/датовые/логические/агрегатные функции, операторы сравнения, работа со всеми типами ДП (включая Lookup, Multilookup, Таблицу, Файл, Выбор пользователей), реквизиты задачи, ресурсы, FileStorage. Ниже — то, что в нём явно не описано.

### Математические функции

Математические функции выражений:

| Функция | Параметры | Возвращает | Пример / описание |
|---------|-----------|-----------|------|
| `ПоМодулю(N)` | Число | Число | Абсолютное значение (модуль) |
| `Степень(N, M)` | Число (основание N), Число (показатель M) | Число | N в степени M |
| `УрезатьДоЗнаков(N, Знаков)` | Число, Число (макс. знаков после запятой) | Число | `УрезатьДоЗнаков(123.456, 2) = 123.45`; `УрезатьДоЗнаков(123, 2) = 123` |
| `СлучЗнач(N)` | Число (seed) | Число | Случайное десятичное от 0 до 1, рассчитанное на основе указанного числа |
| `ЗнакЧисла(N)` | Число | Целое | `1` для положительных, `-1` для отрицательных, `0` для нуля |
| `Корень(N)` | Число | Число | Квадратный корень |

> Базовая арифметика (`+`, `-`, `*`, `/`, сравнения), `Округлить`, `МинЦелоеНеМеньше`, `МаксЦелоеНеБольше`, `IsNullDecimal`, `ВСтроку`, `ВЦелоеЧисло`, `ВДробноеЧисло` — см. [`read-data.md` § Числовые ДП](https://help.1forma.ru/domains/smart-filters/read-data.md#числовые-и-строковые-дп).

### Строковые функции — дополнение

Дополнительные строковые функции:

| Функция | Параметры | Возвращает | Назначение |
|---------|-----------|-----------|------------|
| `DateConvert(Дата, СтильЧисло)` | Дата, Целое (стиль формата MS SQL `CAST/CONVERT`) | Строка | Преобразование даты в строку по указанному стилю (см. справочник стилей MS SQL `date-and-time-styles`) |
| `ПовторитьСтроку(Строка, N)` | Строка, Целое | Строка | Повторяет строку N раз |
| `Конкатенация(Строка1, Строка2)` | Строка, Строка | Строка | Соединяет две строки (функциональная альтернатива оператору `+`) |

> Остальные строковые функции (`СтрокаСодержит`, `НачинаетсяС`, `ЗаканчиваетсяНа`, `Подстрока`, `ВернутьСлева/Справа`, `ВВерхРегистр/ВНижРегистр`, `ЗаменитьВСтроке`, `БезПробелов`, `ДлинаСтроки`, `IsNullString`, `ПоискВСтроке`, `ПоискПоШаблону`), а также оператор `+` для конкатенации — см. [`read-data.md` § Строковые ДП](https://help.1forma.ru/domains/smart-filters/read-data.md#числовые-и-строковые-дп).

### Объект «Текущий пользователь» (раздел дерева «Прочее»)

В дереве сущностей редактора раздел **«Прочее»** содержит объект **«Текущий пользователь»** — пользователя, под которым выполняется смарт-выражение. Через него доступен полный набор параметров `1F.User` (ID, логин, ФИО, email, группы, орг.единица — см. [`read-data.md` § Пользователь (User)](https://help.1forma.ru/domains/smart-filters/read-data.md#пользователь-категория-и-файл)).

Применяется для проверки принадлежности к группе/орг.единице, подстановки имени в выражение, отбора задач по исполнителю/заказчику = текущий пользователь. При редактировании **глобальных смартов** (форма `general-smart`, без контекста категории/события) обращение к объектам системы возможно **только через раздел «Прочее»** — это единственный путь к данным пользователя в таком контексте.

### Блок «Используется» — параметры события «Во время открытия задачи»

Для отбора задач в блоке «Используется» категории (вкладка, отображающая задачи, ссылающиеся на текущую через Lookup/Multilookup) применяются смарт-фильтры. Особенности дерева сущностей такого фильтра:

- **Параметры основной задачи** (в карточке которой открыт блок) — через папку **«Параметры события "Во время открытия задачи"»** в дереве.
- **Параметры задач, отображаемых в блоке** (ссылающихся) — через объекты вне контекста события, ниже в дереве сущностей.
- **ДП основной и зависимых задач** — через объекты «Значения ДП».

### Ограничения языка смарт-выражений

Что нужно учитывать при написании выражений:

- **`ORDER BY` запрещён** в скрипте смарт-фильтра, доступного в смарт-поиске (отбор в табличном представлении категории).
- **Тип возвращаемого результата** должен соответствовать контексту использования: для записи в поле «Дата» — `DateTime`; для поля единичного объекта — единичный объект, не список; для смарт-фильтра — `Boolean` (тип выставляется системой автоматически и не редактируется).
- **Конвертация TSQL → SMART не предусмотрена.** Из режима SMART можно перейти в TSQL (`Конвертировать в режим TSQL`), обратный путь отсутствует — кнопка не отображается, если выражение в TSQL.
- **Список значений** в смарт-выражении записывается через фигурные скобки: `{ААА, ВВВ, ССС}`. Чтобы единичный объект воспринимался как коллекция, он тоже заключается в `{}`.
- **Запись (модификация) ДП** через смарт-выражения использует отдельный синтаксис (JSON для SelectUsers/Email, разделители `+`/`-`/`=`/`#`/`|` и `]||[` для Таблицы и Выбора нескольких задач) — описан в [`../smart-actions/admin.md`](https://help.1forma.ru/domains/smart-actions/admin.md). Smart-filters отвечают только за чтение и фильтрацию.

## Типичные ошибки настройки

Частые проблемы и где их искать:

| Симптом | Причина | Где проверить | SQL-диагностика |
|---------|---------|---------------|-----------------|
| Выражение возвращает пусто | Неверный SubcatID / OriginID | Форма `general-smart` | `select ID, Name, SubcatID, OriginID, IsFilter from dbo.SmartExpressions where ID = {id}` |
| Редактор не показывает свойства | Неверный Origin (не тот контекст) | Origin-audit.md → таблица | `select ID, Name, OriginID from dbo.SmartExpressions where Name like '%{name}%'` |
| «Выражение удалено» но используется | IsDeleted = 1, но ссылки остались | Поиск по FK | `select * from dbo.SmartExpressions where IsDeleted = 1 and ID in (select SmartFilterID from dbo.CustomQueueActions)` |
| TSql-выражение не работает, визуальное — да | TSqlContent имеет приоритет, но содержит ошибку | Поле TSqlContent | `select ID, Name, left(TSqlContent, 200) from dbo.SmartExpressions where TSqlContent is not null and ID = {id}` |
| SmartQuery виджет пустой | SmartFilterID ссылается на несуществующее выражение | Форма `rebus-queues` | `select sq.*, se.Name as FilterName, se.IsDeleted from dbo.SmartQueries sq left join dbo.SmartExpressions se on sq.SmartFilterID = se.ID where sq.ID = {id}` |
| Дерево свойств в редакторе неполное | Внутренняя ошибка построения дерева свойств | — | Обновить дерево («Перестроить»); если не помогает — обратиться в поддержку 1Ф |
| Выпадающий фильтр не открывается, ошибка «В результате смарт-выражения {ID} отсутствует колонка '{имя}'» | Выражение-источник не возвращает колонку, запрошенную по имени: значение, заголовок либо `available`/`parent`. Обычно колонку переименовали или выражение стало возвращать другой набор полей | Форма `general-smart`, поля `Content` и `TSqlContent` выражения | `select ID, Name, left(TSqlContent, 200) as TSql, left(Content, 200) as Content from dbo.SmartExpressions where ID = {id}` |
| В дереве свойств клик по свойству ничего не вставляет | Узел оператора коллекции не найден в дереве либо дерево свойств неполное | Редактор выражений, кнопка «Перестроить»; если не помогает — поддержка 1Ф | — |

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

- [Справочник чтения данных](https://help.1forma.ru/domains/smart-filters/read-data.md) — операторы и функции выражений
- [Смарт-действия — администрирование](https://help.1forma.ru/domains/smart-actions/admin.md) — привязка правил к событиям, смарт-доступ
