Серверный вызов в 1С 8.3: директивы, общие модули, отладка и права
Содержание
Чтобы выполнить процедуру на сервере 1С, поставьте перед ней в модуле формы директиву &НаСервере (или &НаСервереБезКонтекста) и вызовите её из клиентской процедуры по имени. Если код нужен в нескольких местах, вынесите его в общий модуль с флагами Сервер и Вызов сервера и вызывайте как ИмяМодуля.ИмяПроцедуры().
Всё ниже относится к платформе 1С:Предприятие 8.3 и управляемым формам. Правила одинаковы для любой конфигурации: БП 3.0, ЗУП 3.1, УТ 11 или своей разработки. Разберём, какой способ вызова выбрать, как отладить серверный код, как ловить ошибки и почему вызов иногда падает с «непонятным» сообщением.
Как работает директива &НаСервере и её варианты
Код управляемой формы исполняется в двух местах. Клиентская часть работает на компьютере пользователя, серверная — в рабочем процессе кластера. В файловом варианте «сервер» живёт внутри того же процесса клиента, но правила разделения кода всё равно действуют.
Директива перед процедурой говорит платформе, где её компилировать:
| Директива | Где выполняется | Доступ к данным формы | Когда использовать |
|---|---|---|---|
&НаКлиенте | клиент | да | обработчики кнопок, диалоги, работа с файлами пользователя |
&НаСервере | сервер | да, форма передаётся на сервер | нужно прочитать или изменить реквизиты формы и базу |
&НаСервереБезКонтекста | сервер | нет | расчёт или запрос по переданным параметрам |
&НаКлиентеНаСервереБезКонтекста | и там, и там | нет | общие вычисления без обращения к базе |
Процедура без директивы в модуле формы считается серверной (&НаСервере). Полагаться на это не стоит: явная директива читается проще.
Минимальный рабочий пример
Кнопка на форме документа пересчитывает сумму по табличной части:
&НаКлиенте
Процедура Пересчитать(Команда)
ПересчитатьНаСервере();
КонецПроцедуры
&НаСервере
Процедура ПересчитатьНаСервере()
Объект.СуммаДокумента = Объект.Товары.Итог("Сумма");
КонецПроцедуры
Клиент вызывает серверную процедуру, платформа отправляет данные формы на сервер, выполняет код и возвращает изменённую форму. Пользователь увидит новую сумму сразу.
Когда нужен вызов без контекста
&НаСервереБезКонтекста не тянет форму на сервер. Это заметно быстрее, если форма большая: табличные части на сотни строк, много реквизитов. Внутри такой процедуры нет Объект, Элементы и реквизитов формы — всё нужное передаётся параметрами.
&НаКлиенте
Процедура КонтрагентПриИзменении(Элемент)
Объект.ИНН = ИННКонтрагента(Объект.Контрагент);
КонецПроцедуры
&НаСервереБезКонтекста
Функция ИННКонтрагента(Контрагент)
Возврат Контрагент.ИНН;
КонецФункции
Обращение к реквизиту ссылки через точку (Контрагент.ИНН) на клиенте не работает. Такие данные всегда читают серверной функцией.
Клиент-серверное взаимодействие: что можно передать и вернуть
Каждый вызов сервера — это сетевой обмен. Параметры и возвращаемое значение сериализуются, поэтому передать можно не всё.
✅ Передаются без проблем:
- примитивные типы:
Строка,Число,Дата,Булево,Неопределено; - ссылки на объекты базы:
СправочникСсылка,ДокументСсылкаи другие; Структура,Массив,Соответствиеи их фиксированные варианты, если внутри — такие же допустимые значения;ХранилищеЗначения,УникальныйИдентификатор.
❌ Не передаются:
СправочникОбъект,ДокументОбъект— сами объекты живут только на сервере;Запрос,РезультатЗапроса,ТаблицаЗначений,ДеревоЗначений— на клиенте этих типов нет;- менеджеры записи регистров и наборы записей.
Если нужно вернуть таблицу, переложите её в массив структур или поместите во временное хранилище через ПоместитьВоВременноеХранилище() и передайте на клиент адрес.
Правило одного вызова
Серверный вызов — самая дорогая операция в клиентском коде. Цикл, в котором на каждой строке зовётся сервер, при 500 строках даст 500 обращений по сети.
Плохо:
Для Каждого Строка Из Объект.Товары Цикл
Строка.Цена = ЦенаНоменклатуры(Строка.Номенклатура);
КонецЦикла;
Хорошо: одна серверная процедура, которая заполняет цены всем строкам сразу одним запросом. Счётчик вызовов сервера удобно смотреть в замере производительности (о нём — в разделе про отладку).
Модуль объекта из формы
Экспортная процедура модуля объекта вызывается из серверной процедуры формы через преобразование данных формы в объект:
&НаСервере
Процедура ЗаполнитьНаСервере()
ДокументОбъект = РеквизитФормыВЗначение("Объект");
ДокументОбъект.ЗаполнитьПоОстаткам(); // процедура с ключевым словом Экспорт
ЗначениеВРеквизитФормы(ДокументОбъект, "Объект");
КонецПроцедуры
Без Экспорт в объявлении процедура модуля объекта снаружи не видна. Тот же приём работает для внешней обработки: её логика лежит в модуле объекта, а форма вызывает её через РеквизитФормыВЗначение.
Серверная логика в общих модулях
Когда процедура нужна в нескольких формах, документах или обработках, её место — в общем модуле. Поведение модуля задают флажки в его свойствах:
| Флаг | Что даёт |
|---|---|
Сервер | модуль компилируется на сервере, его процедуры доступны серверному коду |
Вызов сервера | процедуры можно вызывать прямо с клиента |
Клиент (управляемое приложение) | модуль доступен клиентскому коду |
Привилегированный | код выполняется без проверки прав текущего пользователя |
Внешнее соединение | модуль доступен через COM-соединение |
Пошагово: создать серверный общий модуль
- В Конфигураторе откройте дерево конфигурации, ветка
Общие → Общие модули. - Добавьте модуль, дайте имя, например
РаботаСЦенамиВызовСервера. - В свойствах отметьте
СервериВызов сервера, остальное снимите. - Напишите процедуру с ключевым словом
Экспорт:
Функция ЦеныНоменклатуры(МассивНоменклатуры, ДатаЦен) Экспорт
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Цены.Номенклатура КАК Номенклатура,
| Цены.Цена КАК Цена
|ИЗ
| РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&ДатаЦен, Номенклатура В (&Список)) КАК Цены";
Запрос.УстановитьПараметр("ДатаЦен", ДатаЦен);
Запрос.УстановитьПараметр("Список", МассивНоменклатуры);
Результат = Новый Соответствие;
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Результат.Вставить(Выборка.Номенклатура, Выборка.Цена);
КонецЦикла;
Возврат Результат;
КонецФункции
- Вызовите с клиента:
Цены = РаботаСЦенамиВызовСервера.ЦеныНоменклатуры(Массив, ТекущаяДата());.
Имя регистра в примере условное — подставьте свой. В типовых конфигурациях на БСП принято соглашение по суффиксам: модуль без суффикса — серверный, ВызовСервера — с клиентским доступом, Клиент — клиентский, ПовтИсп — с повторным использованием возвращаемых значений.
Модуль с флагом Вызов сервера открыт для любого клиентского кода. Не кладите туда процедуры, которые удаляют данные или меняют настройки без проверки входных параметров.
Повторное использование значений
В свойствах общего модуля есть Повторное использование возвращаемых значений: На время вызова или На время сеанса. Платформа кеширует результат функции по набору параметров. Подходит для редко меняющихся данных: константы, настройки учёта. Для остатков и цен — нет, пользователь получит устаревшую цифру.
Длительные процедуры: фоновые задания
Если процедура работает минуту и дольше, клиент «висит» всё это время. Такие вещи запускают фоновым заданием — оно выполняется на сервере в отдельном сеансе.
&НаСервереБезКонтекста
Функция ЗапуститьПересчет(Период)
Параметры = Новый Массив;
Параметры.Добавить(Период);
Задание = ФоновыеЗадания.Выполнить("ПересчетСервер.ПересчитатьЗаПериод", Параметры,, "Пересчёт за период");
Возврат Задание.УникальныйИдентификатор;
КонецФункции
Требования к вызываемой процедуре:
- 📌 лежит в общем серверном модуле и экспортная;
- 📌 параметры — только сериализуемые значения;
- 📌 не обращается к форме и не показывает диалогов.
Регламентные задания используют тот же механизм, только запускаются по расписанию. В типовых конфигурациях на БСП для фоновых операций из форм есть готовый механизм длительных операций — посмотрите, как его используют соседние формы вашей конфигурации, прежде чем писать своё.
Отладка серверного кода в режиме 1С:Предприятия
В файловом варианте серверные процедуры отлаживаются сразу: поставили точку останова, запустили отладку — и всё работает.
В клиент-серверном варианте нужен шаг подготовки: сервер 1С должен быть запущен с разрешённой отладкой.
Пошагово для клиент-серверной базы
- На сервере 1С откройте параметры службы агента сервера. В строке запуска
ragent.exeдолжен быть ключ-debug. - Если ключа нет, добавьте его в параметры службы и перезапустите службу.
Перезапуск службы сервера 1С обрывает сеансы всех пользователей. Делайте это вне рабочего времени или на тестовом сервере.
- В Конфигураторе поставьте точку останова (
F9) на нужной строке серверной процедуры. - Запустите отладку:
Отладка → Начать отладку(F5). - Если клиент уже запущен отдельно, подключитесь к нему через
Отладка → Подключениеи включите в списке предметов отладки серверные сеансы. - Выполните действие в режиме 1С:Предприятия — отладчик остановится на серверной строке.
Для отладки фоновых и регламентных заданий в окне подключения включите автоматическое подключение к фоновым заданиям. Иначе точка останова внутри фонового кода не сработает.
Замер производительности
Отладка → Замер производительности показывает время каждой строки и число вызовов сервера. Типичная находка: строка с .Реквизит через точку внутри клиентского цикла съедает 80% времени операции.
Отлаживать на рабочей базе можно, но точка останова в серверном коде держит транзакцию. Если в ней заблокированы данные, другие пользователи будут ждать, пока вы не нажмёте «Продолжить».
Ошибки и исключения в серверном коде
Ошибка на сервере передаётся клиенту и показывается пользователю. Если её не обработать, пользователь увидит системное окно со стеком вызовов. Чтобы этого не было, оборачивайте рискованный код в Попытка … Исключение.
&НаСервереБезКонтекста
Функция ЗагрузитьКурс(Валюта, Дата)
Попытка
// обращение к внешнему сервису или файлу
Возврат Истина;
Исключение
Информация = ИнформацияОбОшибке();
ЗаписьЖурналаРегистрации("Загрузка курса",
УровеньЖурналаРегистрации.Ошибка,,,
ПодробноеПредставлениеОшибки(Информация));
Возврат Ложь;
КонецПопытки;
КонецФункции
Правила, которые экономят время при разборе:
- 🔹 в журнал регистрации пишите
ПодробноеПредставлениеОшибки()— там стек и строка модуля; - 🔹 пользователю показывайте короткий текст через
КраткоеПредставлениеОшибки(); - 🔹 свою ошибку поднимайте через
ВызватьИсключение "Текст";— выполнение остановится, вызывающий код получит исключение.
Исключения и транзакции
Если процедура меняет несколько объектов, их записывают в одной транзакции. При ошибке транзакцию обязательно отменяют:
НачатьТранзакцию();
Попытка
// запись документов, регистров
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ВызватьИсключение;
КонецПопытки;
ВызватьИсключение без параметров пробрасывает исходную ошибку дальше. Пустой блок Исключение без отмены транзакции — частая причина сообщения «В данной транзакции уже происходили ошибки» на следующей записи.
Права пользователя и безопасность серверного вызова
Серверная процедура выполняется с правами текущего пользователя. Если у роли нет права чтения регистра, запрос к нему упадёт с ошибкой «Недостаточно прав». Это нормальное поведение, а не сбой.
Способы управлять правами:
| Способ | Что делает | Риск |
|---|---|---|
Проверка ПравоДоступа("Чтение", Метаданные.РегистрыСведений.Имя) | заранее узнаёт, есть ли право | нет, рекомендуемый вариант |
УстановитьПривилегированныйРежим(Истина) | отключает проверку прав до конца вызова или до Ложь | пользователь получит данные, которые ему не положены |
Флаг Привилегированный у общего модуля | весь код модуля без проверки прав | то же, плюс модуль нельзя вызвать с клиента напрямую |
УстановитьБезопасныйРежим(Истина) | запрещает доступ к файлам, COM, внешним компонентам | ограничивает и полезные операции |
Привилегированный режим включайте точечно: на одно чтение служебного регистра, а не на всю процедуру.
Чего не делать с серверным кодом
- 🚫 Не выполняйте через
Выполнить()строку, пришедшую с клиента. Это прямой путь к выполнению произвольного кода. - 🚫 Не собирайте текст запроса склейкой строк из пользовательского ввода — передавайте значения через
УстановитьПараметр. - 🚫 Не возвращайте на клиент лишние данные «на всякий случай»: всё, что ушло с сервера, видит пользователь.
Почему не вызывается серверная процедура: разбор ошибок
Ещё одна ловушка — клиентская процедура с тем же именем, что и серверная. Модуль формы такой код не скомпилирует: имена процедур уникальны внутри модуля независимо от директивы. Поэтому серверной паре дают суффикс НаСервере.
Частые вопросы
Можно ли вызвать серверную процедуру формы из другой формы?
Чем &НаСервере отличается от общего модуля с флагом «Вызов сервера»?
Как выполнить серверный код без доработки конфигурации?
Файл → Открыть, если у пользователя есть на это право.