# Смарт-действия — файлы, отчёты, данные, сроки, ДП

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

Действия над файлами и отчётами (генерация по шаблону, Excel, файловые папки), данными (реестр, импорт), сроками и трудозатратами, модификацией значений ДП.

## Файлы и отчёты

### Создать файл отчёта (`CreateReportFile`)

Генерирует файл отчёта FastReport и загружает его в задачу. Enum ID = 75.

| Параметр | Кодовое имя | Тип | Описание |
|----------|-------------|-----|----------|
| ID отчёта* | `ReportIdActionParam` | `Report` | Отчёт из справочника (объект `{ Id: reportId }`) |
| Параметры отчёта | `ParametersOfReportActionParam` | `Collection<ReportParameter>` | Параметры для передачи в FastReport (необязательно) |
| Имя файла* | `ReportFileNameActionParam` | `String` | Имя выходного файла (без расширения). Прямой и обратный слеш и управляющие символы в имени заменяются на `_`: «Договор 91/2026» сохранится как «Договор 91_2026» |
| Формат файла | `ReportFileExtActionParam` | `ReportFileExt` | Формат: `Excel2007` (xlsx), `Word2007` (docx), `Pdf`, `Csv`, `Html`, `Rtf`, `Json`, `Png` и др. По умолчанию Pdf |
| Пользователь доступа | `ActionParameterCreateReportFileAccessUser` | `User` | Необязательно. Пользователь, от имени которого созданный файл отчёта загружается в задачу. Пусто — пользователь сессии, запустивший действие; если сессии нет (фоновый запуск пакета), действие завершается отказом в доступе ещё до формирования отчёта. Согласно подсказке параметра, системного робота указывают здесь только для доверенных фоновых сценариев — тогда файл создаётся без проверки прав. При переносе конфигурации значение параметра сопоставляется с пользователями площадки-приёмника по идентификатору: если такого пользователя там нет, вместо него подставляется системный робот — подмена видна отдельным предупреждением в журнале переноса |

**Lua:**
```lua
SMART:execute_action('CreateReportFile', CONTEXT.Id, 'task', {
    ReportIdActionParam = { Id = reportId },
    ReportFileNameActionParam = 'Отчёт_' .. os.date('%Y%m%d'),
    ReportFileExtActionParam = 'Excel2007'
})
```

**JavaScript:**
```javascript
// Без параметров — отчёт берёт данные из контекста задачи
SMART.execute_action_in_task_context("CreateReportFile", taskId, {
    ReportIdActionParam: { Id: reportId },
    ReportFileNameActionParam: "Отчёт_" + new Date().toISOString().slice(0,10),
    ReportFileExtActionParam: "Excel2007"
});

// С параметрами — передача значений в фильтры отчёта
SMART.execute_action_in_task_context("CreateReportFile", taskId, {
    ReportIdActionParam: { Id: reportId },
    ParametersOfReportActionParam: [
        { ParameterName: "TaskID", FixedValue: String(taskId) },
        { ParameterName: "DateFrom", FixedValue: "2026-01-01" }
    ],
    ReportFileNameActionParam: "Выгрузка",
    ReportFileExtActionParam: "Pdf"
});
```

**JavaScript — с пользователем доступа:**
```javascript
SMART.execute_action_in_task_context("CreateReportFile", taskId, {
    ReportIdActionParam: { Id: reportId },
    ReportFileNameActionParam: "Выгрузка",
    ReportFileExtActionParam: "Pdf",
    AccessUser: 181 // необязательно: UserID, от чьего имени файл загружается в задачу; пусто — пользователь сессии
});
```

**Lua — с пользователем доступа:**
```lua
SMART:execute_action('CreateReportFile', CONTEXT.Id, 'task', {
    ReportIdActionParam = { Id = reportId },
    ReportFileNameActionParam = 'Выгрузка',
    ReportFileExtActionParam = 'Pdf',
    AccessUser = 181 -- необязательно: UserID, от чьего имени файл загружается в задачу; пусто — пользователь сессии
})
```

> **Особенность — имя параметра в скрипте:** в таблице выше параметр называется «Пользователь доступа» (кодовое имя `ActionParameterCreateReportFileAccessUser`), а в Lua/JS ему соответствует короткое имя `AccessUser` (UserID числом). Другие имена скрипт не примет.

> **Особенность — фоновый запуск:** без пользовательской сессии и с пустым `AccessUser` действие завершается отказом в доступе до формирования отчёта. Для доверенных фоновых сценариев указывайте системного робота явно.

> **Особенность (`ParametersOfReportActionParam`):** каждый элемент массива — объект с `ParameterName` (имя параметра в FR) и **одним** из: `FixedValue` (строка) или `SmartExpressionId` (ID смарт-выражения, значение вычисляется). Если SmartExpression возвращает несколько строк — берётся только первая.

> **Особенность — результат:** действие возвращает `FileFromLibrary` (JSON с `Id` файла). Для прикрепления к задаче используйте действие «Вложить файл» (`UploadFile`) в том же пакете. Объект `FileFromLibrary` можно передавать напрямую во входные параметры действий, ожидающих `IFileFromLibrary` (например, ЭДО-действие `FillEpFile`) — конвертер параметров принимает значение, если оно реализует требуемый интерфейс, без ручного извлечения `Id`.

**Форматы (enum `ReportFileExt`):** `Pdf`, `Excel2007` (xlsx), `Word2007` (docx), `Csv`, `Html`, `Rtf`, `Json`, `Png`, `Jpg`, `Xml`, `Odf`, `Ods`, `Odt`, `Point2007` (pptx), `Text` (txt), `Svg`.

### Вложить файл (`UploadFile`)

Загружает файл в задачу (комментарий или ДП типа Файл).

| Параметр | Тип | Описание |
|----------|-----|----------|
| Задача* | `1F.Task` | Куда загрузить |
| Загружающий пользователь* | `1F.User` | От чьего имени |
| Файл | `1F.File` | Файл (из результата CreateReportFile или др.); необязателен, если задан Base64 |
| Описание | `String` | Описание файла |
| Создать новый файл | `Boolean` | Всегда создавать новую версию |
| Не писать комментарий | `Boolean` | Без комментария о вложении |
| Base64-данные файла | `String` | Содержимое файла в Base64 (альтернатива «Файл») |
| Имя файла | `String` | Имя при загрузке из Base64 |
| ID предзагруженного файла | `Integer` | ID заранее загруженного файла |

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

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

> **Особенность — предзагруженный файл:** параметр «ID предзагруженного файла» работает только с файлом, который загрузил сам инициатор действия (пользователь из параметра «Загружающий пользователь»); файл, предзагруженный другим пользователем, недоступен. Запуск без действительного инициатора (в том числе от системного робота) отклоняется. То же ограничение — у действия «Заполнить файл расширенного параметра» (`FillEpFile`).

---

## Данные

### Записать в регистр (`WriteRegistryValue`)

Добавляет запись в регистр или перезаписывает существующую. Enum ID = 109.

**Что такое регистр.** Регистр — именованное многомерное хранилище данных, которое накапливает историю изменений. Состоит из **измерений** (разрезы: номенклатура, склад и т.п.) и **значений** (данные: количество, сумма). Опциональная **дискретность** добавляет временное измерение (Секунда / День / Месяц).

**Где настраивается:** Администрирование → Бизнес-логика → Регистры (`dbo.Registers`, `dbo.RegisterDimensions`, `dbo.RegisterValues`).

| Параметр | Кодовое имя | Тип | Описание |
|----------|-------------|-----|----------|
| Регистр\* | `RegistryActionParam` | `1F.Registry` | Регистр из справочника |
| Измерения и значения\* | _(динамические)_ | — | Колонки соответствуют выбранному регистру |
| Задача | `TaskActionParam` | `1F.Task` | Задача, к которой привязывается запись (опционально) |
| Перезаписать | `OverwriteActionParam` | `Boolean` | `true` — перезаписать существующую запись; `false` (по умолчанию) — добавить новую |
| Дата | `DateActionParam` | `DateTime` | Дата фиксации (для регистров с дискретностью) |

**Lua:**
```lua
SMART:execute_action('WriteRegistryValue', CONTEXT.Id, 'task', {
    RegistryActionParam  = { Id = registryId },
    Nomenclature         = nomenclatureId,  -- имена колонок из настроек регистра
    Warehouse            = warehouseId,
    Quantity             = 42,
    OverwriteActionParam = true,
    DateActionParam      = os.date('%Y-%m-%dT%H:%M:%S')
})
```

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

### Удалить из регистра (`DeleteRegistryValue`, ID 110)

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

| Параметр | Кодовое имя | Тип | Описание |
|----------|-------------|-----|----------|
| Регистр\* | `RegistryActionParam` | `1F.Registry` | Регистр |
| Измерения | _(динамические)_ | — | Колонки соответствуют выбранному регистру |
| Дата | `DateActionParam` | `DateTime` | Для регистров с дискретностью |

### Очистить регистр по владельцу (`ClearRegistryByOwner`, ID 111)

Удаляет **все** записи регистра, привязанные к конкретной задаче. Используется при удалении/архивации задачи или при переинициализации.

| Параметр | Тип | Описание |
|----------|-----|----------|
| Регистр* | `1F.Registry` | Регистр |
| Задача* | `1F.Task` | Владелец записей |

---

## Сроки и трудозатраты

### Заблокировать срок (`LockDue`, ID 33) / Разблокировать срок (`UnLockDue`, ID 52)

Управление флагом блокировки срока (`Tasks.IsLocked`-семантика). После блокировки автоматические сдвиги срока (смарты, иерархия ПУ) игнорируются — изменить срок можно только разблокировав его.

### Обновить плановое время задачи (`UpdateTaskOrderedTime`, ID 8)

Смена `Tasks.OrderedTime`. Если срок заблокирован — действие пропускается. Параметры: задача, новый срок, причина (опц.).

### Изменить плановое начало (`ChangeTaskPlannedStart`, ID 35) / окончание (`ChangeTaskPlannedEnd`, ID 36) / фактическое окончание (`ChangeTaskFactEnd`, ID 37)

Меняют соответствующие даты задачи. Используются в проектном управлении и при автогенерации. Также есть отдельное действие **«Изменить плановое время начала задачи»** (`ChangePlannedTaskStartTime`, ID 11).

### Установить плановый объём (`SetPlanAmount`, ID 61) / Установить фактический объём (`SetFactAmount`, ID 32)

Скалярное обновление плановых / фактических трудозатрат. ⚠️ По умолчанию плановые «фиксируются» при превышении факта; разрешить изменение — `CustomSetting NoValidateFixLengthPlan=true`.

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

**Ресурс по умолчанию.** Параметр «Ресурс» необязателен, но при незаполненном значении трудозатраты не записываются «без ресурса» — ресурс подбирается автоматически. Для исполнителя, уже присутствующего в ресурсном плане задачи, берётся его ресурс из плана; явно указанный ресурс в этом случае влияет только на запись факта и между ресурсами исполнителя не переносит. Для исполнителя, которого в плане нет, подставляется «Ресурс по умолчанию» из настроек ресурсного планирования категории — при условии, что он доступен для планирования и допустим для этого исполнителя; иначе берётся первый ресурс, привязанный к пользователю-исполнителю. Если ресурс не определился ни одним из способов, внесение фактических трудозатрат завершается ошибкой «Невозможно внести трудозатраты. Требуется заполнить справочник ресурсов». Заполненный параметр со ссылкой на ресурс, отсутствующий в каталоге или недоступный для планирования, к подстановке ресурса по умолчанию не приводит — возвращается ошибка «Ресурс не найден в каталоге или недоступен для планирования».

Параметры одинаковы для обоих действий (плановый / фактический объём определяется самим действием):

| Параметр | Тип | Описание |
|----------|-----|----------|
| Задача* | `1F.Task` | Задача |
| Кто вносит* | `1F.User` | Пользователь, вносящий трудозатраты |
| Действие* | `enum RMSmartActionType` | Тип операции с трудозатратами |
| Ресурс | `1F.CatalogResource` | Ресурс (справочник) |
| Исполнитель (сотрудник) | `Collection<1F.User>` | Сотрудники-исполнители |
| Время (мин.) | `Integer` | Объём в минутах |
| Игнорировать возможные ошибки назначения трудозатрат | `Boolean` | Не прерываться на ошибках назначения |
| Исполнитель (задача) | `1F.Task` | Задача-исполнитель |
| Дата | `DateTime` | Дата трудозатрат |

### Обновить процент выполнения (`UpdatePercentDone`, ID 24)

Меняет `Tasks.CompletePercent`. Применяется в проектных задачах.

### Установить приоритет (`SetPriority`, ID 22)

Смена `Tasks.Priority` (нумерация приоритетов — в `dbo.Priorities`).

### Изменить текст задачи (`ChangeTaskText`, ID 12)

Смена `Tasks.TaskText`. Поддерживает шаблонизацию (значения ДП через `##ExtParam{ID}##`, см. [Паттерны и примеры](https://help.1forma.ru/domains/smart-actions/academy-patterns.md)).

### Изменить конфиденциальность (`ChangeConfidentiality`, ID 62)

Смена режима конфиденциальности задачи.

| Параметр | Тип | Описание |
|----------|-----|----------|
| Задача* | `1F.Task` | Задача |
| Заказчик* | `1F.User` | Заказчик задачи |
| Конфиденциальность | `Boolean` | Включить / выключить режим |

### Обновить ресурсный план (`ResourcePlanUpdate`, ID 130)

Пересчёт ресурсного плана задачи — корректирует распределение трудозатрат по ресурсам.

---

## ДП — модификация значений

### Строки ДП «Таблица» из автоматизации

Для табличного ДП в смарт-автоматизации есть два пути. Первый — действие «Изменить значение ДП» со строкой-командой и префиксом операции (`+` — добавить, `=` — изменить, `-` — удалить); формат описан в разделе [«JSON-синтаксис модификации ДП»](https://help.1forma.ru/domains/smart-actions/admin.md#json-синтаксис-модификации-дп). Второй — три отдельных действия, которые берут строки из SQL-выборки и собирать строку-команду не требуют:

| Действие | Что делает | Как читается результат SQL | Enum ID |
|---|---|---|---|
| Добавить строки ДП Таблица | Добавляет новые строки | каждая колонка — `c{Id колонки}` со значением | 216 |
| Изменить строки ДП Таблица | Обновляет существующие строки | первая колонка — идентификатор строки, остальные — `c{Id колонки}` | 217 |
| Удалить строки ДП Таблица | Удаляет строки | только первая колонка — идентификатор строки | 218 |

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

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

### Изменить значение ДП (`ChangeExtParamValue`, ID 5)

Самое частое смарт-действие. Меняет одно значение ДП в одной задаче.

| Параметр | Тип | Описание |
|----------|-----|----------|
| Задача* | `1F.Task` | — |
| Пользователь* | `1F.User` | От чьего имени вносится изменение |
| ДП* | `1F.ExtParam` | Какой параметр менять |
| Значение | _(зависит от типа ДП)_ | Необязательный (пусто = очистка). Для составных типов (Lookup, Multilookup, Table, Файл, Выбор пользователей, Email) — JSON-синтаксис, см. [раздел «JSON-синтаксис модификации ДП»](https://help.1forma.ru/domains/smart-actions/admin.md#json-синтаксис-модификации-дп) |
| Писать комментарий при изменении* | `Boolean` | Фиксировать изменение комментарием |
| Причина | `String` | Причина изменения |
| Язык локализации | `1F.Language` | Локаль для локализованных значений ДП |

> Для **табличного ДП** формат строки и форматы значений по типам колонок (включая отличие колонки `SelectUsers` от самостоятельного ДП `SelectUsers`) — детальный референс с примерами в [`docs/domains/ext-params/types-reference.md`](https://help.1forma.ru/domains/ext-params/types-reference.md), раздел «ДП «Таблица» → Операции через API/SmartScript».

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

**ДП «Адрес» без ссылки на справочник.** Если адрес сохранён текстом, но не связан с записью справочника адресов, в нативной колонке `AddressId` может быть пусто. При сравнении в действии «Изменить значение ДП» такое пустое нативное значение не считается достаточным признаком того, что сам ДП пуст: действие дополнительно читает сохранённый текст — но только у тех типов параметров, у которых нативная колонка вообще есть. Поэтому передача пустого значения очищает текстовый адрес. Если ДП действительно пуст, повторная передача пустого значения по-прежнему пропускает запись; повторная передача того же адреса также не записывает значение заново. Это уточнение относится к действию «Изменить значение ДП» и не меняет общий путь записи ДП, другие стандартные действия или сохранение ДП из карточки задачи.

> **Очистка значения («Оставить пустым»):** в редакторе пакета можно выбрать «Оставить пустым» вместо конкретного значения — при срабатывании действия ДП будет сброшен в `null`. **v2.268+:** такая конфигурация корректно сохраняется и выполняется. **До v2.268:** ДП с пустым значением молча выпадали из конфигурации при сохранении пакета — используйте `ClearExtParamValue` (ID 26) для надёжной очистки на более ранних версиях.

### Добавить строки ДП «Таблица» (`AddTableRows`, ID 216)

Добавляет новые строки в табличный ДП значениями из SQL-выборки. Типизированная замена строки-команды добавления в действии «Изменить значение ДП»: собирать JSON не нужно.

Каждая колонка выборки должна называться `c{Id колонки}` и содержать значение этого столбца. Каждая строка выборки становится новой строкой таблицы. Все строки сохраняются одним вызовом.

| Параметр | Тип | Описание |
|----------|-----|----------|
| Задача* | 1F.Task | Задача, чей ДП пополняется |
| Пользователь* | 1F.User | От чьего имени вносится изменение |
| ДП* | 1F.ExtParam | Табличный параметр, в который добавляются строки |
| SQL-скрипт* | String | SELECT, колонки — `c{Id колонки}` со значениями |
| Писать комментарий при изменении* | Boolean | Фиксировать изменение комментарием. По умолчанию включено |

Пример: задача — текущая, ДП — «Состав работ», SQL-скрипт — `SELECT 'Иванов И.И.' AS [c1234], '2026-01-15' AS [c1235]`. После срабатывания в таблице появляются новые строки с этими значениями.

Гейты безопасности: добавить действие в пакет может только пользователь с правами god; исполнение блокируется флагом запрета SQL в смартах; конструкции INSERT, UPDATE, DELETE, DROP, TRUNCATE, GRANT, ALTER отклоняются до выполнения — шире, чем у действий «Изменить строки» и «Удалить строки», где запрещены только DROP, TRUNCATE, GRANT и ALTER; попытка пишется в журнал действий с префиксом `[SmartAction AddTableRows]`. Таймаут запроса — из общей настройки `SmartAction_ExecuteSql_CommandTimeout` (по умолчанию 30 секунд).

Когда завершается ошибкой: в выборке нет ни одной колонки вида `c{номер}`; такой колонки нет в настройках ДП; колонка нередактируемая — вычисляемая, шаблон или кнопки; повтор имени колонки или схлопывание разных имён в один номер, например `c5` и `c05`. Пустая выборка ошибкой не считается — действие ничего не добавляет.

Значения приводятся к формату колонки: значение типа «дата и время» передаётся в формате ISO — `2026-01-15T10:00:00`, дата без времени — `2026-01-15T00:00:00`; флажок — `1` или `0`; число и деньги — с точкой как разделителем дробной части; `NULL` — пустое значение. Строковые значения передаются как есть.

Недоступно в 1F Certificate Edition: действие исключено из сборки.

### Изменить строки ДП «Таблица» (`UpdateTableRows`, ID 217)

Обновляет существующие строки табличного ДП значениями из SQL-выборки — типизированная замена строки-команды «Обновить» в действии «Изменить значение ДП».

Первая колонка выборки — идентификатор строки таблицы, каждая следующая должна называться `c{Id колонки}` и содержать новое значение этого столбца. Все строки уходят в сохранение одним вызовом: применяются либо все, либо ни одна.

| Параметр | Тип | Описание |
|----------|-----|----------|
| Задача* | `1F.Task` | Задача, чей ДП обновляется |
| Пользователь* | `1F.User` | От чьего имени вносится изменение |
| ДП* | `1F.ExtParam` | Табличный параметр, строки которого обновляются |
| SQL-скрипт* | `String` | Первая колонка результата — идентификатор строки, остальные — `c{Id колонки}` со значениями |
| Писать комментарий при изменении* | `Boolean` | Фиксировать изменение комментарием. По умолчанию включено |

Пример: ДП «Состав работ», колонка 1237 — «Статус строки». SQL-скрипт — `SELECT RowID, 'Обработано' AS [c1237] FROM ExtParamTableValues WHERE ExtParamID = 1234 AND ColumnID = 1237 AND Value = 'Новый'`. Первая колонка отдаёт идентификатор строки таблицы (не задачу), вторая записывает в колонку 1237 значение «Обработано». Список строк для обновления удобно строить по самой таблице `ExtParamTableValues`: в ней хранятся значения всех колонок табличного ДП, а `RowID` — идентификатор строки.

**Гейты безопасности** — те же, что у действия «Выполнить SQL-скрипт» (см. [`actions-reference-system.md`](https://help.1forma.ru/domains/smart-actions/actions-reference-system.md)): добавить действие в пакет может только пользователь с правами god; исполнение блокируется флагом `DisableSqlShellInSmartExpressions`; конструкции `DROP`/`TRUNCATE`/`GRANT`/`ALTER` отклоняются, а попытка пишется в журнал действий с префиксом `[SmartAction UpdateTableRows]`. Таймаут запроса берётся из общей с «Выполнить SQL-скрипт» настройки `SmartAction_ExecuteSql_CommandTimeout` (по умолчанию 30 секунд).

**Когда действие завершается ошибкой:** в строке выборки нет ни одной колонки значений; идентификатор строки пустой, не целое число или не больше нуля; имя колонки не соответствует шаблону `c{номер}`; такой колонки нет в настройках ДП; колонка нередактируемая — вычисляемая, шаблон или кнопки. Эти проверки выполняются до сохранения. Несуществующий идентификатор строки выявляется уже при сохранении, и в тексте ошибки указывается сам идентификатор; частичной записи при этом не остаётся. Пустая выборка ошибкой не считается — действие просто ничего не меняет.

Значения приводятся к формату колонки: дата и время — `дд.мм.гггг чч:мм:сс`, дата — `дд.мм.гггг`, флажок — `1` или `0`, число и деньги — с точкой как разделителем дробной части, `NULL` — пустое значение.

### Удалить строки ДП «Таблица» (`DeleteTableRows`, ID 218)

Удаляет строки табличного ДП, найденные SQL-запросом — типизированная замена строки-команды «Удалить» в действии «Изменить значение ДП»: собирать JSON с префиксом `-` по каждому идентификатору не нужно.

Первая колонка каждой строки выборки — идентификатор удаляемой строки, остальные колонки не используются. Все строки уходят в сохранение одним вызовом.

| Параметр | Тип | Описание |
|----------|-----|----------|
| Задача* | `1F.Task` | Задача, чей ДП изменяется |
| Пользователь* | `1F.User` | От чьего имени вносится изменение |
| ДП* | `1F.ExtParam` | Табличный параметр, строки которого удаляются |
| SQL-скрипт* | `String` | Первая колонка результата — идентификатор удаляемой строки |
| Писать комментарий при изменении* | `Boolean` | Фиксировать изменение комментарием. По умолчанию включено |

Пример: задача — текущая, пользователь — робот, ДП «Состав работ», SQL-скрипт — `SELECT RowID FROM ExtParamTableValues WHERE Value = 'Отменено'`, комментарий — да.

Пустая выборка ошибкой не считается — действие не удаляет ничего. Значение первой колонки приводится к целому числу; нечисловое значение приводит к ошибке выполнения.

**Гейты безопасности** — те же, что у действия «Выполнить SQL-скрипт» (см. [`actions-reference-system.md`](https://help.1forma.ru/domains/smart-actions/actions-reference-system.md)): добавить действие в пакет может только пользователь с правами god; исполнение блокируется флагом `DisableSqlShellInSmartExpressions`; конструкции `DROP`/`TRUNCATE`/`GRANT`/`ALTER` отклоняются, а попытка пишется в журнал действий с префиксом `[SmartAction DeleteTableRows]`. Таймаут запроса берётся из общей с «Выполнить SQL-скрипт» настройки `SmartAction_ExecuteSql_CommandTimeout` (по умолчанию 30 секунд).

> **Недоступно в 1F Certificate Edition** (ФСТЭК-сборка): действие исключено из сборки; пакеты действий, где оно было настроено ранее, в этой редакции не выполняются.

### Изменить массово значение ДП (`MassChangeExtParamValue`, ID 50)

Тот же эффект, но для пачки задач (отбор через смарт-фильтр или явный список). Использует SQL-обновление в одну транзакцию — в разы быстрее цикла одиночных вызовов.

### Скопировать значение ДП (`CopyEpValue`, ID 27)

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

### Очистить значение ДП (`ClearExtParamValue`, ID 26)

Сброс значения ДП в задаче (`null`). Для Multilookup/Table — очищает все строки.

### Обновить параметры с помощью TSQL-вычислений

Выполняет хранимую процедуру (на PostgreSQL — функцию) на стороне базы и записывает полученный набор в параметры задач. Применяется для интеграции 1Формы с внешними системами через общую базу: расчёт значений целиком выполняется в СУБД, действие только передаёт параметры вызова и раскладывает результат.

| Параметр | Тип | Описание |
|----------|-----|----------|
| Номер задачи | `1F.Task` | Необязательный: подставляется в процедуру как `@TaskID`. Достаточно указать его или категорию |
| ID категории | Число | Необязательный: передаётся как `@SubcatID`. Без задачи и категории действие прерывается |
| ID параметра | Число | Передаётся как `@ExtParamID` |
| Имя TSQL процедуры | Строка | Обязательный: имя вызываемой процедуры (на PostgreSQL — функции) |
| Режим выполнения | Строка | Передаётся как `@Mode` |
| ID пользователя | Число | Передаётся как `@UserID` |

Процедура должна принимать параметры `@TaskID`, `@SubcatID`, `@ExtParamID`, `@Mode`, `@UserID` и возвращать первым набором строки с колонками `TaskID`, `ExtParamID`, `Value`, `UserID`: по каждой строке набора действие записывает значение в указанный параметр указанной задачи от имени указанного пользователя. На MS SQL у процедуры есть также выходной параметр `@ErrorMessage` — его текст поднимается как ошибка действия, если процедура его заполнила.

**Работа на разных СУБД.** Действие одинаково выполняется на MS SQL и PostgreSQL: способ вызова подбирается по типу базы автоматически. На MS SQL процедура вызывается как хранимая, с выходным параметром ошибки; на PostgreSQL — как таблично-возвращающая функция текстовым запросом, с именами аргументов через префикс `p_` (аргумента `p_errormessage` у перенесённых функций нет — ошибки возвращаются исключением). Таймаут выполнения — 600 секунд.

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

---

## Файлы (расширение)

### Скачать файл (`DownloadFile`, ID 49) / Загрузить файл в файловое хранилище (`UploadFileToFileStorage`, ID 185) / Выгрузить файл в сетевую папку (`UploadFileToDisk`, ID 87)

Получение файла из ДП «Файл» / общих вложений в переменную смарт-действия и обратная загрузка в файловое хранилище / сетевую папку (для передачи во внешний сервис, конвертации и т. п.). Адрес, по которому действие скачивает файл, ограничен списком разрешённых хостов: обращения к произвольному адресу с учётными данными сервера не выполняются, а в сертифицируемой сборке такое скачивание и вовсе отклоняется. Поэтому действие нельзя использовать, чтобы подставить чужой адрес и получить ответ от имени сервера. Базовая `UploadFile` (ID 28) — простая загрузка из переменной в задачу.

**Пользователь доступа.** У «Выгрузить файл в сетевую папку» (`UploadFileToDisk`, 87) есть необязательный параметр «Пользователь доступа»: от его имени проверяется доступ к файлу библиотеки. Пусто — берётся пользователь сессии, запустивший действие. Если сессии нет, например при запуске пакета по расписанию, и параметр пуст, действие завершается отказом в доступе: системный робот сам не подставляется. Системного робота указывают в этом параметре явно и только для доверенных фоновых сценариев.

### Заполнить файл расширенного параметра (`FillEpFile`, ID 43)

Загрузка файла (из переменной, URL или результата `CreateReportFile`) в ДП «Файл» задачи.

### Прикрепить email к задаче (`AttachEmailToTask`, ID 153) / по Mail ID (`AttachEmailToTaskByMailId`, ID 186) / Связать письмо с задачей (`LinkEmailToTask`, ID 23) / Связать письмо с задачей — общее (`LinkLetterToTask`, ID 82)

Прикрепляет email из почтового ящика по различным критериям (по контексту события джоба, по явному `EmailID`, по результату обработки).

Параметры «Связать письмо с задачей — общее» (`LinkLetterToTask`, 82):

| Параметр | Тип | Описание |
|----------|-----|----------|
| Почтовое сообщение* | `1F.Email` | Письмо |
| Задача* | `1F.Task` | Задача |
| Кто привязывает* | `1F.User` | Инициатор |

`LinkLetterToTask` (82) вкладывает письмо в задачу файлом `.eml` (имя файла — тема письма и `MessageId`) от имени пользователя из параметра «Кто привязывает»; у этого пользователя должен быть доступ к задаче, иначе действие отклоняется. Запись связи письма с задачей в `EmailTaskLinks` при этом не создаётся.

Параметры «Привязать письмо к задаче» (`LinkEmailToTask`, 23):

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

`LinkEmailToTask` (23) вызывается в контексте письма и вкладывает его в задачу файлом `.eml`, дожидаясь завершения операции. Запись связи письма с задачей в `EmailTaskLinks` не создаётся. От действия 82 отличается тем, что письмо берётся из контекста, а не из параметра.

### Архивировать файлы (`ArchiveFiles`, ID 89) / Распаковать архив (`UnzipArchive`, ID 179) / Объединить файлы (`MergeFiles`, ID 83) / Объединить docx-файлы (`MergeDocxFiles`, ID 79)

Четыре действия для архивации и объединения файлов:

- **Архивировать** — упаковка нескольких файлов в ZIP. Каждый файл, попавший в архив, записывается в журнал действий с файлами как скачивание архивом от имени пользователя из параметра «Автор» (по умолчанию — системный робот), без привязки к задаче и ДП. Запись создаётся независимо от настройки «Логировать действия с файлами».
- **Распаковать** — извлечение содержимого архива. Распаковка **плоская**: файлы из вложенных папок извлекаются без структуры каталогов, а файлы с одинаковыми именами перезаписываются. Записи, которые извлечь не удалось, пропускаются: небезопасный путь, некорректное имя записи или превышение порога по объёму; их имена возвращаются в результате действия и записываются в журнал.
Результат действия в пакете — список идентификаторов распакованных файлов.

**Результат действия «Распаковать».** Форма результата зависит от того, остались ли в архиве пропущенные записи:

| Ситуация | Тип результата | Что в нём |
|---|---|---|
| Пропущенных записей нет | `int[]` | идентификаторы распакованных файлов, индексация с нуля |
| Есть пропущенные записи | `{ fileIds (int[], 1..n), skippedEntries (string[], 1..n) }` | `fileIds` — идентификаторы распакованных файлов; `skippedEntries` — имена пропущенных записей архива |

В смарт-выражениях последующего действия результат всегда приходит массивом идентификаторов распакованных файлов — и при пропусках, и без них. В Lua форму определяют по наличию поля `fileIds`: без пропусков это .NET-массив с нумерацией с нуля (`res[0]`), при пропусках — таблица с нумерацией с единицы, и тогда идентификаторы лежат в `fileIds`.

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

- не более 200 МБ распакованных данных на одну запись;
- не более 1 ГБ распакованных данных на весь архив;
- не более 10 000 записей, включая каталоги и пропущенные имена.

Если превышен любой из этих пределов, действие завершается ошибкой «Распаковка архива прервана» с указанием причины — объём записи, суммарный объём или число записей. Уже извлечённые из этого архива файлы при таком срыве убираются в корзину. При любой другой ошибке распаковки ранее загруженные файлы остаются, как и раньше.

Запись, у которой распакованный объём больше сжатого в 500 раз, действие не прерывает: она пропускается, в журнал пишется предупреждение, остальные записи обрабатываются. Её имя при этом есть в результате действия. Обычный архив, в том числе созданный действием «Архивировать файлы», извлекается как прежде: состав и имена файлов не меняются, вложенные папки по-прежнему не сохраняются.

Эти пределы относятся только к действию «Распаковать». Распаковка ZIP-контейнера при конвертации в PDF и при обработке правок Word ограничена иначе — 250 МБ на элемент, 500 МБ суммарно и 10 000 элементов; см. разделы этих действий ниже.

**Подсказка типа в редакторе.** В редакторе смарт-скриптов рядом с действием «Распаковать» результат показывается в двух формах: `int[] | { fileIds (int[], 1..n), skippedEntries (string[], 1..n) }`. Это не украшение подсказки, а договор между редактором и исполнением: правую или левую форму скрипт получит в зависимости от того, остались ли в архиве пропущенные записи. Форма, которую вернул конкретный вызов, — та, на которую рассчитан скрипт; определить её можно по наличию полей: если `fileIds` есть, результат — таблица, иначе это обычный массив идентификаторов. Штатные идиомы (`if res.fileIds`, `#`, `ipairs`) рассчитаны именно на эту развилку, отдельная обработка «а если придёт другая форма» не нужна.

**Результат «Распаковать» в смарт-выражениях.** В смарт-выражениях последующих действий пакета значение результата — массив идентификаторов распакованных файлов (`int[]`), одинаково и когда часть записей архива пропущена, и когда пропусков нет: в параметр выражения попадают идентификаторы успешно извлечённых файлов, и условия по результату распаковки работают в обоих случаях одинаково.

**Результат «Распаковать» в Lua.** Если архив распакован без пропущенных записей, возвращается массив идентификаторов распакованных файлов как обычный массив .NET: первый идентификатор — `res[0]`. Если есть пропущенные записи, результат — таблица с полями `fileIds` и `skippedEntries`: `fileIds` содержит идентификаторы распакованных файлов, `skippedEntries` — имена пропущенных записей. Оба массива внутри этой таблицы индексируются с единицы — `res.fileIds[1]`, `res.skippedEntries[1]`, — и для них работают оператор длины `#` и обход `ipairs`. Нулевая индексация `res[0]` к полям этой таблицы не применяется: ориентируйтесь на форму, указанную в подсказке результата редактора.

```lua
local res = SMART:execute_action('UnzipArchive', CONTEXT.Id, 'task', params)
if res.fileIds then
    for i, fileId in ipairs(res.fileIds) do
        print(i, fileId)
    end
    for i, entryName in ipairs(res.skippedEntries) do
        print(i, entryName)
    end
else
    local firstFileId = res[0] -- обычный .NET-массив: индексация с нуля
end
```

**Результат «Распаковать» на пути MCP.** Когда действие вызывается инструментом MCP, результат приходит объектом с теми же двумя полями: `fileIds` — идентификаторы распакованных файлов, `skippedEntries` — имена записей архива, которые извлечь не удалось. Если пропусков не было, результат остаётся массивом идентификаторов, как и раньше. Имена пропущенных записей видны в ответе инструмента, а не только в серверном журнале.

- **MergeFiles** — слияние PDF/изображений в один документ.
- **MergeDocxFiles** — слияние **двух** .docx-файлов (File1 + File2) в один с сохранением форматирования.

> **Особенности выполнения.** Слияние с сохранением истории правок и авторства исправлений выполняет серверный компонент, которому нужен доступный Microsoft Word на сервере приложений; если Word недоступен, операция завершается ошибкой, а не отдаёт документ без правок. Сбой самого Word и ошибки определения его версии больше не гасятся: они пишутся в журнал и возвращаются вызывающему, поэтому «слияние прошло, но правок в документе нет» больше не воспроизводится как успешный результат. Сравнение версий документа, между которыми добавили или удалили рисунок, больше не падает: библиотеки обработки документов собраны под ту же версию формата, что и остальное приложение, тогда как раньше они компилировались под прежнюю версию и на рисунке вызывали отсутствующий метод. Это касается и сравнения версий файла в карточке задачи, и режима сравнения смарт-действия. Базовое объединение содержимого документов без отслеживания правок работает средствами OpenXml и установленного офиса не требует.

### Сгенерировать QR-код (`GenerateQRCode`, ID 194)

Генерация QR-картинки из текста / URL и привязка к задаче.

### Конвертировать файл в другой формат (`ConvertFileTo`, ID 86) / Конвертировать в PDF (`ConvertFileToPdf`, ID 70) / Вставить картинку в pdf/doc (`InsertPictureIntoDocument`, ID 112)

**Пользователь доступа.** У действия «Наложить изображение на документ» (`InsertPictureIntoDocument`, 112) есть необязательный параметр «Пользователь доступа» (в конце списка): доступ к исходному документу и к картинке проверяется от его имени. Пусто — пользователь сессии. Фоновый запуск без сессии и без заданного пользователя завершается отказом. Нюанс: при явно заданном пользователе, отличном от инициатора, новый файл создаётся от имени этого пользователя — он становится владельцем созданного файла.

Использует конвертеры из `appsettings.json` (`UseDevexpressDocToPdfConverter`) или CustomSetting `DefaultConverterPdf`. Вставка картинки — водяные знаки, печати, подписи.

**Предел распаковки при конвертации в другой формат.** Действие «Конвертировать файл в другой формат» проверяет исходный файл до выбора целевого формата, поэтому предел действует одинаково для всех целей этого действия. Если файл — ZIP-контейнер (`.docx`, `.odt`, `.epub`), его распаковка ограничена 250 МБ на один элемент, 500 МБ суммарно и 10 000 элементов; документ, выходящий за эти пределы, действие отклоняет с ошибкой. Повреждённый контейнер также отклоняется. Файлы, не являющиеся ZIP-контейнером (`.doc`, `.rtf`), передаются в конвертер без этой проверки. Пределы заданы в коде и настройками не меняются.

Параметры «Конвертировать в PDF» (`ConvertFileToPdf`, 70):

| Параметр | Тип | Описание |
|----------|-----|----------|
| Автор* | `1F.User` | Автор операции |
| ID задачи* | `1F.Task` | Задача-контекст |
| Файл* | `1F.File` | Исходный файл |
| Защита файла* | `Integer` | Уровень защиты итогового PDF |
| Пароль* | `String` | Пароль защиты (пусто — без пароля) |
| Целевой ДП | `1F.ExtParam` | ДП для результата |
| PDF-A | `Boolean` | Формировать PDF/A |

**Предел распаковки.** Если исходный файл — ZIP-контейнер (`.docx`, `.xlsx` и другие форматы Office), перед конвертацией проверяется объём его распаковки: не более 250 МБ на один элемент, 500 МБ суммарно и 10 000 элементов. Документ, выходящий за эти пределы, действие отклоняет с ошибкой. Повреждённый контейнер также отклоняется — в конвертер он не передаётся. Файлы, не являющиеся ZIP-контейнером (`.doc`, `.rtf`), передаются в конвертер без этой проверки. Пределы заданы в коде и настройками не меняются.

Параметры «Вставить картинку в pdf/doc» (`InsertPictureIntoDocument`, 112) — все обязательные:

| Параметр | Тип | Описание |
|----------|-----|----------|
| Тип документа* | `enum DocumentsOnWhichInsert` | Формат документа: `PDF` или `DOCX` |
| Исходный документ* | `1F.File` | Документ, в который вставляется картинка |
| Изображение* | `1F.File` | Картинка для вставки |
| Страницы* | `enum PagesOnWhichInsert` | Куда вставлять: «На каждой странице», «На титульной странице», «На последней странице» |
| Расположение* | `enum PlaceForPicture` | Угол страницы: «Левый верхний угол», «Правый верхний угол», «Левый нижний угол», «Правый нижний угол» |

### Получить список редакторов Word-файла (`GetRevisionersList`, ID 76) / Принять правки авторов (`RemoveRevisionsByAuthors`, ID 77) / Отклонить правки авторов (`DeclineRevisionsByAuthors`, ID 78)

Применяет / отклоняет track-changes в Word-документе. Используется в маршрутах согласования документов. `GetRevisionersList` (76) возвращает `String[]` — логины авторов правок.

Параметры «Принять правки авторов» (`RemoveRevisionsByAuthors`, 77):

| Параметр | Тип | Описание |
|----------|-----|----------|
| Файл* | `1F.File` | Word-файл |
| Авторы правок | `Collection<String>` | Логины авторов (пусто — все) |
| Не создавать новую версию файла | `Boolean` | Изменить текущую версию |
| От чьего имени совершается действие | `1F.User` | Инициатор |

Параметры «Отклонить правки авторов» (`DeclineRevisionsByAuthors`, 78) — те же, без «От чьего имени»:

| Параметр | Тип | Описание |
|----------|-----|----------|
| Файл* | `1F.File` | Word-файл |
| Авторы правок | `Collection<String>` | Логины авторов (пусто — все) |
| Не создавать новую версию файла | `Boolean` | Изменить текущую версию |

**Предел распаковки.** Word-файл — это ZIP-контейнер, и перед обработкой все три действия проверяют объём его распаковки: не более 250 МБ на один элемент, 500 МБ суммарно и 10 000 элементов. Документ, выходящий за эти пределы, отклоняется с ошибкой до передачи в движок, поэтому результат одинаков независимо от того, какая библиотека обработки выбрана настройками. Пределы заданы в коде и настройками не меняются.

### Импортировать лист Excel в таблицу БД (`ImportSheetToDb`, ID 141)

Парсит загруженный XLSX и записывает строки в указанную таблицу или ДП «Таблица».

**Права.** Запись в таблицу рабочей базы (не в ДП «Таблица») через смарт-действие: в таблицы схемы `custom` — любому пользователю, запустившему пакет; в таблицы схемы `dbo` и любых других схем, кроме `custom`, — только администратору системы. На PostgreSQL имя без схемы тоже считается схемой `custom`, на MS SQL такое имя идёт в `dbo` и обычному пользователю запрещено. Проверка выполняется в сервисе импорта, поэтому обход через прямой вызов действия невозможен. Добавить действие в пакет может только администратор. HTTP-импорт `POST api/files/import/exceltodb` для обычных пользователей по-прежнему закрыт — только администратор.

Имя таблицы и имена колонок валидируются у всех пользователей, включая администратора: пустые, слишком длинные (для MS SQL — больше 128 символов, для PostgreSQL — больше 63 байт) и содержащие служебные символы SQL-разметки отклоняются. Подробнее — [files/admin.md](https://help.1forma.ru/domains/files/admin.md) § «Импорт Excel в базу данных».

Типовой пример: загрузка этапов проекта из файла в `custom.projectstages` кнопкой в карточке задачи — работает у обычных сотрудников без прав администратора.

### Разобрать по шаблону (`ParseByTemplate`, ID 42)

OCR + распознавание полей по шаблону (для счёт-фактур, актов и т. п.). Зависит от `FileIdToStartOcr` и `TikaJob`.

### Сгенерировать по шаблону (`GenerateByTemplate`, ID 39)

Генерация документа из Word-шаблона (`.docx`) с подстановкой тегов вида `##ExtParam{ID}##`.

Имя результирующего файла берётся из параметра «Имя целевого файла»; если он не заполнен, имя формируется автоматически по задаче. Расширение определяется по файлу шаблона (если у шаблона расширения нет — `.docx` для документа и `.txt` для текстовой генерации) и добавляется к имени, если оно ещё не оканчивается этим расширением. Точка в середине имени расширением не считается: из `тес.т` получится `тес.т.docx`, а `тес.docx` останется без изменений.

> **Конфиденциальные задачи.** Генерация не останавливается: без доступа к содержимому в файл подставляется заглушка, при наличии доступа значения подставляются полностью. Сортировка цикла по значению дополнительного параметра не искажается. Подробнее — [шаблонизатор печатных форм](https://help.1forma.ru/domains/reports/academy-patterns.md).

> **Значения с HTML-разметкой.** Если в значение попал HTML — из дополнительного параметра «Большой текст с форматированием», из результата смарт-выражения или смарт-скрипта, — разметка переносится в документ как разметка Word. Числовые отступы HTML-таблиц `cellspacing` и `cellpadding` принимаются как целое число, любое другое значение завершает генерацию ошибкой. Правила и ограничения — [Значения с HTML-разметкой](https://help.1forma.ru/domains/reports/academy-patterns.md#значения-с-html-разметкой). Одиночный UTF-16 суррогат в значении не роняет текстовую генерацию: символ кодируется отдельной десятичной HTML-сущностью, а корректная суррогатная пара — например эмодзи — кодируется как единая точка Unicode. Правило относится к генерации текста; вывод через компилятор портальных шаблонов не менялся.

### Связать файловую папку с задачей (`LinkFileFolderToTask`, ID 66) / Отвязать (`UnLinkFileFolderFromTask`, ID 67)

Привязка / отвязка папки на Диске к задаче.

Параметры «Связать файловую папку с задачей» (`LinkFileFolderToTask`, 66):

| Параметр | Тип | Описание |
|----------|-----|----------|
| Задача* | `1F.Task` | Задача |
| Путь к папке* | `String` | Путь к папке файлового хранилища |
| Связать вложенные файлы в задаче с папкой файлового хранилища | `Boolean` | Синхронизировать вложения |
| Выдать права на родительские папки в файловом хранилище | `Boolean` | Права на родительские папки |

### Защитить файл (`ProtectFile`, ID 40) / Удалить файл перманентно (`DeleteFilePermanently`, ID 126) / Создать копию версии файла (`CreateFileVersionCopy`, ID 80) / Добавить метаданные к файлу (`AddFileInfoExtValue`, ID 81)

Базовые операции с файловым хранилищем.

Параметры «Добавить метаданные к файлу» (`AddFileInfoExtValue`, 81):

| Параметр | Тип | Описание |
|----------|-----|----------|
| Файл* | `1F.File` | Файл файлового хранилища |
| Ключ* | `String` | Имя метаданного поля |
| Значение* | `String` | Значение |

**Пользователь доступа.** У действий «Создать копию для версии файла» (`CreateFileVersionCopy`, 80) и «Добавить метаданные к файлу» (`AddFileInfoExtValue`, 81) есть необязательный параметр «Пользователь доступа» (в конце списка): от его имени проверяется доступ к файлу библиотеки. Пусто — пользователь сессии, запустивший действие. Фоновый запуск без сессии и без заданного пользователя завершается отказом в доступе. Системного робота указывают явно и только для доверенных фоновых сценариев.

### Экспортировать данные в Excel (`ExportDataToExcel`, ID 91)

Экспорт результатов TSQL-процедуры в файл Excel.

| Параметр | Тип | Описание |
|----------|-----|----------|
| Имя целевого файла* | `String` | Имя формируемого файла Excel |
| Имя TSQL-процедуры* | `String` | Хранимая процедура, формирующая данные |
| Задача* | `1F.Task` | Задача-контекст |
| Целевой ДП | `1F.ExtParam` | ДП, куда вкладывается файл |
| От чьего имени файл вкладывается* | `1F.User` | Автор вложения |
| ID задачи | `1F.Task` | Задача для вложения (если отличается от контекста) |
| ID пользователя | `Integer` | Пользователь-контекст |
| Режим выполнения | `String` | Значение передаётся в вызов базы как `@Mode` (на PostgreSQL — `p_mode`). Передаётся, только если параметр заполнен |

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

**Параметры вызова.** Действие передаёт в базу три параметра — `TaskID`, `UserID` и `Mode`, каждый только тогда, когда соответствующий параметр действия заполнен. На MS SQL процедура принимает их как `@TaskID`, `@UserID`, `@Mode`; на PostgreSQL у перенесённой функции те же аргументы называются `p_taskid`, `p_userid`, `p_mode`. Процедура, которой эти параметры не нужны, вызывается без них.

**Работа на разных СУБД.** Действие одинаково выполняется на MS SQL и PostgreSQL: способ вызова подбирается по типу базы автоматически. На MS SQL процедура вызывается как хранимая, на PostgreSQL — как таблично-возвращающая функция текстовым запросом с именами аргументов через префикс `p_`. Таймаут выполнения — 600 секунд. Соединение берётся общее для действий пакета; отдельную строку подключения для этого действия настраивать не нужно.

**Схема по умолчанию.** Имя, заданное без схемы, вызывается как `dbo.<имя>` — на обеих СУБД: объекты платформы на PostgreSQL тоже лежат в схеме `dbo`. Явно заданная в имени схема сохраняется как есть. Проверка по списку разрешённых процедур выполняется до открытия соединения, поэтому имя вне списка отклоняется одинаково на обеих СУБД.

**Какой набор попадает в файл.** В файл попадает **первый набор результата с колонками**. Наборы без колонок — значения `ROWCOUNT`, сообщения `PRINT` и `RAISERROR` — пропускаются, берётся следующий. Если ни один набор колонок не отдал, действие отказывает с сообщением о том, что процедура не вернула данные, и файл не создаётся. В текстовых ячейках срезается HTML-разметка и обрезаются краевые пробелы, числа и даты пишутся типизированными ячейками.

**Отказы.** Отказ с текстом в журнале пакета возникает, если имя процедуры не входит в список `ExportDataToExcelAllowedProcedures`, имя не является допустимым именем объекта базы, процедура завершилась ошибкой до конца первого набора или не вернула ни одного набора с колонками. Ошибку, возникшую **после** второго и следующих наборов, действие не поднимает — см. «Известные проблемы» домена.
