Серверный вызов в 1С 8.3: директивы, общие модули, отладка и права

· 9 мин

Содержание

Чтобы выполнить процедуру на сервере 1С, поставьте перед ней в модуле формы директиву &НаСервере (или &НаСервереБезКонтекста) и вызовите её из клиентской процедуры по имени. Если код нужен в нескольких местах, вынесите его в общий модуль с флагами Сервер и Вызов сервера и вызывайте как ИмяМодуля.ИмяПроцедуры().

Всё ниже относится к платформе 1С:Предприятие 8.3 и управляемым формам. Правила одинаковы для любой конфигурации: БП 3.0, ЗУП 3.1, УТ 11 или своей разработки. Разберём, какой способ вызова выбрать, как отладить серверный код, как ловить ошибки и почему вызов иногда падает с «непонятным» сообщением.

Как работает директива &НаСервере и её варианты

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

Директива перед процедурой говорит платформе, где её компилировать:

ДирективаГде выполняетсяДоступ к данным формыКогда использовать
&НаКлиентеклиентдаобработчики кнопок, диалоги, работа с файлами пользователя
&НаСервересерверда, форма передаётся на сервернужно прочитать или изменить реквизиты формы и базу
&НаСервереБезКонтекстасервернетрасчёт или запрос по переданным параметрам
&НаКлиентеНаСервереБезКонтекстаи там, и тамнетобщие вычисления без обращения к базе

Процедура без директивы в модуле формы считается серверной (&НаСервере). Полагаться на это не стоит: явная директива читается проще.

Минимальный рабочий пример

Кнопка на форме документа пересчитывает сумму по табличной части:

&НаКлиенте
Процедура Пересчитать(Команда)
	ПересчитатьНаСервере();
КонецПроцедуры

&НаСервере
Процедура ПересчитатьНаСервере()
	Объект.СуммаДокумента = Объект.Товары.Итог("Сумма");
КонецПроцедуры

Клиент вызывает серверную процедуру, платформа отправляет данные формы на сервер, выполняет код и возвращает изменённую форму. Пользователь увидит новую сумму сразу.

Когда нужен вызов без контекста

&НаСервереБезКонтекста не тянет форму на сервер. Это заметно быстрее, если форма большая: табличные части на сотни строк, много реквизитов. Внутри такой процедуры нет Объект, Элементы и реквизитов формы — всё нужное передаётся параметрами.

&НаКлиенте
Процедура КонтрагентПриИзменении(Элемент)
	Объект.ИНН = ИННКонтрагента(Объект.Контрагент);
КонецПроцедуры

&НаСервереБезКонтекста
Функция ИННКонтрагента(Контрагент)
	Возврат Контрагент.ИНН;
КонецФункции

Обращение к реквизиту ссылки через точку (Контрагент.ИНН) на клиенте не работает. Такие данные всегда читают серверной функцией.

Клиент-серверное взаимодействие: что можно передать и вернуть

Каждый вызов сервера — это сетевой обмен. Параметры и возвращаемое значение сериализуются, поэтому передать можно не всё.

✅ Передаются без проблем:

  • примитивные типы: Строка, Число, Дата, Булево, Неопределено;
  • ссылки на объекты базы: СправочникСсылка, ДокументСсылка и другие;
  • Структура, Массив, Соответствие и их фиксированные варианты, если внутри — такие же допустимые значения;
  • ХранилищеЗначения, УникальныйИдентификатор.

❌ Не передаются:

  • СправочникОбъект, ДокументОбъект — сами объекты живут только на сервере;
  • Запрос, РезультатЗапроса, ТаблицаЗначений, ДеревоЗначений — на клиенте этих типов нет;
  • менеджеры записи регистров и наборы записей.

Если нужно вернуть таблицу, переложите её в массив структур или поместите во временное хранилище через ПоместитьВоВременноеХранилище() и передайте на клиент адрес.

Правило одного вызова

Серверный вызов — самая дорогая операция в клиентском коде. Цикл, в котором на каждой строке зовётся сервер, при 500 строках даст 500 обращений по сети.

Плохо:

Для Каждого Строка Из Объект.Товары Цикл
	Строка.Цена = ЦенаНоменклатуры(Строка.Номенклатура);
КонецЦикла;

Хорошо: одна серверная процедура, которая заполняет цены всем строкам сразу одним запросом. Счётчик вызовов сервера удобно смотреть в замере производительности (о нём — в разделе про отладку).

Модуль объекта из формы

Экспортная процедура модуля объекта вызывается из серверной процедуры формы через преобразование данных формы в объект:

&НаСервере
Процедура ЗаполнитьНаСервере()
	ДокументОбъект = РеквизитФормыВЗначение("Объект");
	ДокументОбъект.ЗаполнитьПоОстаткам(); // процедура с ключевым словом Экспорт
	ЗначениеВРеквизитФормы(ДокументОбъект, "Объект");
КонецПроцедуры

Без Экспорт в объявлении процедура модуля объекта снаружи не видна. Тот же приём работает для внешней обработки: её логика лежит в модуле объекта, а форма вызывает её через РеквизитФормыВЗначение.

Серверная логика в общих модулях

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

ФлагЧто даёт
Сервермодуль компилируется на сервере, его процедуры доступны серверному коду
Вызов серверапроцедуры можно вызывать прямо с клиента
Клиент (управляемое приложение)модуль доступен клиентскому коду
Привилегированныйкод выполняется без проверки прав текущего пользователя
Внешнее соединениемодуль доступен через COM-соединение

Пошагово: создать серверный общий модуль

  1. В Конфигураторе откройте дерево конфигурации, ветка Общие → Общие модули.
  2. Добавьте модуль, дайте имя, например РаботаСЦенамиВызовСервера.
  3. В свойствах отметьте Сервер и Вызов сервера, остальное снимите.
  4. Напишите процедуру с ключевым словом Экспорт:
Функция ЦеныНоменклатуры(МассивНоменклатуры, ДатаЦен) Экспорт
	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ
	|	Цены.Номенклатура КАК Номенклатура,
	|	Цены.Цена КАК Цена
	|ИЗ
	|	РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&ДатаЦен, Номенклатура В (&Список)) КАК Цены";
	Запрос.УстановитьПараметр("ДатаЦен", ДатаЦен);
	Запрос.УстановитьПараметр("Список", МассивНоменклатуры);

	Результат = Новый Соответствие;
	Выборка = Запрос.Выполнить().Выбрать();
	Пока Выборка.Следующий() Цикл
		Результат.Вставить(Выборка.Номенклатура, Выборка.Цена);
	КонецЦикла;
	Возврат Результат;
КонецФункции
  1. Вызовите с клиента: Цены = РаботаСЦенамиВызовСервера.ЦеныНоменклатуры(Массив, ТекущаяДата());.

Имя регистра в примере условное — подставьте свой. В типовых конфигурациях на БСП принято соглашение по суффиксам: модуль без суффикса — серверный, ВызовСервера — с клиентским доступом, Клиент — клиентский, ПовтИсп — с повторным использованием возвращаемых значений.

Модуль с флагом Вызов сервера открыт для любого клиентского кода. Не кладите туда процедуры, которые удаляют данные или меняют настройки без проверки входных параметров.

Повторное использование значений

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

Длительные процедуры: фоновые задания

Если процедура работает минуту и дольше, клиент «висит» всё это время. Такие вещи запускают фоновым заданием — оно выполняется на сервере в отдельном сеансе.

&НаСервереБезКонтекста
Функция ЗапуститьПересчет(Период)
	Параметры = Новый Массив;
	Параметры.Добавить(Период);
	Задание = ФоновыеЗадания.Выполнить("ПересчетСервер.ПересчитатьЗаПериод", Параметры,, "Пересчёт за период");
	Возврат Задание.УникальныйИдентификатор;
КонецФункции

Требования к вызываемой процедуре:

  • 📌 лежит в общем серверном модуле и экспортная;
  • 📌 параметры — только сериализуемые значения;
  • 📌 не обращается к форме и не показывает диалогов.

Регламентные задания используют тот же механизм, только запускаются по расписанию. В типовых конфигурациях на БСП для фоновых операций из форм есть готовый механизм длительных операций — посмотрите, как его используют соседние формы вашей конфигурации, прежде чем писать своё.

Отладка серверного кода в режиме 1С:Предприятия

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

В клиент-серверном варианте нужен шаг подготовки: сервер 1С должен быть запущен с разрешённой отладкой.

Пошагово для клиент-серверной базы

  1. На сервере 1С откройте параметры службы агента сервера. В строке запуска ragent.exe должен быть ключ -debug.
  2. Если ключа нет, добавьте его в параметры службы и перезапустите службу.

Перезапуск службы сервера 1С обрывает сеансы всех пользователей. Делайте это вне рабочего времени или на тестовом сервере.

  1. В Конфигураторе поставьте точку останова (F9) на нужной строке серверной процедуры.
  2. Запустите отладку: Отладка → Начать отладку (F5).
  3. Если клиент уже запущен отдельно, подключитесь к нему через Отладка → Подключение и включите в списке предметов отладки серверные сеансы.
  4. Выполните действие в режиме 1С:Предприятия — отладчик остановится на серверной строке.

Для отладки фоновых и регламентных заданий в окне подключения включите автоматическое подключение к фоновым заданиям. Иначе точка останова внутри фонового кода не сработает.

Замер производительности

Отладка → Замер производительности показывает время каждой строки и число вызовов сервера. Типичная находка: строка с .Реквизит через точку внутри клиентского цикла съедает 80% времени операции.

Отлаживать на рабочей базе можно, но точка останова в серверном коде держит транзакцию. Если в ней заблокированы данные, другие пользователи будут ждать, пока вы не нажмёте «Продолжить».

Ошибки и исключения в серверном коде

Ошибка на сервере передаётся клиенту и показывается пользователю. Если её не обработать, пользователь увидит системное окно со стеком вызовов. Чтобы этого не было, оборачивайте рискованный код в Попытка … Исключение.

&НаСервереБезКонтекста
Функция ЗагрузитьКурс(Валюта, Дата)
	Попытка
		// обращение к внешнему сервису или файлу
		Возврат Истина;
	Исключение
		Информация = ИнформацияОбОшибке();
		ЗаписьЖурналаРегистрации("Загрузка курса",
			УровеньЖурналаРегистрации.Ошибка,,,
			ПодробноеПредставлениеОшибки(Информация));
		Возврат Ложь;
	КонецПопытки;
КонецФункции

Правила, которые экономят время при разборе:

  • 🔹 в журнал регистрации пишите ПодробноеПредставлениеОшибки() — там стек и строка модуля;
  • 🔹 пользователю показывайте короткий текст через КраткоеПредставлениеОшибки();
  • 🔹 свою ошибку поднимайте через ВызватьИсключение "Текст"; — выполнение остановится, вызывающий код получит исключение.

Исключения и транзакции

Если процедура меняет несколько объектов, их записывают в одной транзакции. При ошибке транзакцию обязательно отменяют:

НачатьТранзакцию();
Попытка
	// запись документов, регистров
	ЗафиксироватьТранзакцию();
Исключение
	ОтменитьТранзакцию();
	ВызватьИсключение;
КонецПопытки;

ВызватьИсключение без параметров пробрасывает исходную ошибку дальше. Пустой блок Исключение без отмены транзакции — частая причина сообщения «В данной транзакции уже происходили ошибки» на следующей записи.

Права пользователя и безопасность серверного вызова

Серверная процедура выполняется с правами текущего пользователя. Если у роли нет права чтения регистра, запрос к нему упадёт с ошибкой «Недостаточно прав». Это нормальное поведение, а не сбой.

Способы управлять правами:

СпособЧто делаетРиск
Проверка ПравоДоступа("Чтение", Метаданные.РегистрыСведений.Имя)заранее узнаёт, есть ли правонет, рекомендуемый вариант
УстановитьПривилегированныйРежим(Истина)отключает проверку прав до конца вызова или до Ложьпользователь получит данные, которые ему не положены
Флаг Привилегированный у общего модулявесь код модуля без проверки правто же, плюс модуль нельзя вызвать с клиента напрямую
УстановитьБезопасныйРежим(Истина)запрещает доступ к файлам, COM, внешним компонентамограничивает и полезные операции

Привилегированный режим включайте точечно: на одно чтение служебного регистра, а не на всю процедуру.

Чего не делать с серверным кодом

  • 🚫 Не выполняйте через Выполнить() строку, пришедшую с клиента. Это прямой путь к выполнению произвольного кода.
  • 🚫 Не собирайте текст запроса склейкой строк из пользовательского ввода — передавайте значения через УстановитьПараметр.
  • 🚫 Не возвращайте на клиент лишние данные «на всякий случай»: всё, что ушло с сервера, видит пользователь.

Почему не вызывается серверная процедура: разбор ошибок

Диагностика: выберите симптом

Ещё одна ловушка — клиентская процедура с тем же именем, что и серверная. Модуль формы такой код не скомпилирует: имена процедур уникальны внутри модуля независимо от директивы. Поэтому серверной паре дают суффикс НаСервере.

Частые вопросы

Можно ли вызвать серверную процедуру формы из другой формы?
Напрямую нет. Процедуры модуля формы видны только внутри этой формы. Общий код выносите в общий модуль с флагом «Вызов сервера».
Чем &НаСервере отличается от общего модуля с флагом «Вызов сервера»?
&НаСервере работает в контексте конкретной формы и видит её данные. Процедура общего модуля доступна из любого места конфигурации, но данных формы не видит — всё передаётся параметрами.
Как выполнить серверный код без доработки конфигурации?
Внешней обработкой: логику пишут в модуле объекта обработки, форма вызывает её через серверную процедуру. Обработку открывают через Файл → Открыть, если у пользователя есть на это право.
Работает ли &НаСервере в файловой базе?
Да. Код выполняется в том же процессе, но разделение клиент/сервер платформа соблюдает. Ошибки с типами и контекстом проявятся так же, как на кластере.
Почему Сообщить() из серверной процедуры появляется не сразу?
Сообщения с сервера доставляются клиенту после завершения вызова. В долгом фоновом задании используйте механизм сообщений фонового задания или прогресс через временное хранилище.