Вызов сервера из формы 1С: параметры, типы данных и скорость

· 10 мин

Содержание

Чтобы передать значение с клиента на сервер в 1С, вызовите из процедуры &НаКлиенте серверную процедуру или функцию и передайте значение параметром. Для общего модуля нужен флажок «Вызов сервера», для модуля формы — директива &НаСервере или &НаСервереБезКонтекста.

Типичная ситуация: программист дописывает кнопку в форме документа, пишет Запрос прямо в клиентском обработчике и получает ошибку — на клиенте запросы недоступны. Дальше разберём, как устроен обмен между клиентом и сервером, какие типы можно передавать и как не превратить форму в генератор лишних серверных вызовов.

Как устроен обмен между клиентом и сервером в 1С

Тонкий клиент и веб-клиент не имеют доступа к базе данных. Всё, что читает или пишет данные, выполняется на сервере. Клиент отвечает за интерфейс: формы, диалоги, реакцию на кнопки.

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

Где выполнится процедура, определяет директива компиляции перед ней:

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

Общие модули работают по флажкам в свойствах модуля: «Клиент (управляемое приложение)», «Сервер», «Вызов сервера». Функция серверного модуля без флажка «Вызов сервера» из клиентского кода не видна.

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

Синхронный серверный вызов: передаём значение параметром

Самый простой способ — серверная функция с параметрами. Значение уходит на сервер, результат возвращается в переменную.

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

&НаСервереБезКонтекста
Функция РасчетСкидкиНаСервере(Знач Контрагент, Знач СуммаДокумента)
	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ
	|	Контрагенты.ПроцентСкидки КАК ПроцентСкидки
	|ИЗ
	|	Справочник.Контрагенты КАК Контрагенты
	|ГДЕ
	|	Контрагенты.Ссылка = &Контрагент";
	Запрос.УстановитьПараметр("Контрагент", Контрагент);
	Выборка = Запрос.Выполнить().Выбрать();
	Если Выборка.Следующий() Тогда
		Возврат Окр(СуммаДокумента * Выборка.ПроцентСкидки / 100, 2);
	КонецЕсли;
	Возврат 0;
КонецФункции

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

Контекстный и внеконтекстный вызов

Разница между &НаСервере и &НаСервереБезКонтекста — в объёме передаваемых данных:

  • 📦 &НаСервере — вместе с вызовом на сервер уходят данные формы, а после выполнения изменения возвращаются на клиент. Удобно, когда нужно заполнить табличную часть или поменять реквизиты формы.
  • ⚡ &НаСервереБезКонтекста — передаются только параметры. Вызов легче, но к Объект, реквизитам формы и ЭтаФорма обратиться нельзя.

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

Зачем писать Знач перед параметром

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

Ключевое слово Знач говорит платформе, что обратно ничего возвращать не нужно. Ставьте его у всех параметров серверных методов, которые не служат для возврата результата.

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

Какие типы данных можно передать на сервер и обратно

Передавать можно только значения, которые существуют и на клиенте, и на сервере. Это частая причина ошибок.

Можно передаватьНельзя передавать
Строка, Число, Дата, Булево, Неопределено, NullТаблицаЗначений, ДеревоЗначений
Ссылки на справочники, документы, перечисленияДокументОбъект, СправочникОбъект
Структура, Массив, Соответствие и их фиксированные вариантыЗапрос, РезультатЗапроса, Выборка
Данные формы (через реквизиты формы)МенеджерЗаписи, НаборЗаписей
Адрес во временном хранилище (строка)Объекты, привязанные к сеансу сервера

Коллекции передаются только если все их элементы тоже допустимых типов. Структура, внутри которой лежит ТаблицаЗначений, на клиент не уйдёт.

Ссылка на объект — это не сам объект. Передали Объект.Контрагент на сервер — сервер получил ссылку и сам прочитает нужные реквизиты. Тянуть на клиент «весь контрагент» не нужно и невозможно.

Передача таблиц значений и сложных структур

Табличные данные — главная сложность. На клиенте ТаблицаЗначений недоступна, поэтому есть четыре рабочих приёма.

Способ 1. Табличная часть или реквизит формы типа ТаблицаЗначений

Если таблица показывается в форме, создайте реквизит формы с типом ТаблицаЗначений. На клиенте это ДанныеФормыКоллекция, на сервере её превращают в обычную таблицу и обратно:

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

Директива здесь обязательно &НаСервере: метод работает с данными формы.

Способ 2. Массив структур

Небольшую таблицу удобно передать массивом структур. Каждая строка — Структура с нужными колонками:

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

Имя регистра и колонок зависит от конфигурации, пример показывает схему. Параметр Номенклатура — массив ссылок, собранный на клиенте.

Способ 3. Временное хранилище

Когда таблица большая или её нужно передать между несколькими вызовами, кладите её во временное хранилище. Клиент получает только строку-адрес:

&НаСервере
Процедура ПодготовитьДанныеНаСервере()
	Таблица = Новый ТаблицаЗначений;
	// ... заполнение
	АдресДанных = ПоместитьВоВременноеХранилище(Таблица, УникальныйИдентификатор);
КонецПроцедуры

&НаСервереБезКонтекста
Функция ОбработатьДанные(Знач Адрес)
	Таблица = ПолучитьИзВременногоХранилища(Адрес);
	Возврат Таблица.Количество();
КонецФункции

АдресДанных — строковый реквизит формы. Второй параметр УникальныйИдентификатор привязывает данные к форме: хранилище очистится после её закрытия.

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

Способ 4. Строка JSON или XML

Для обмена с внешними системами или хранения в реквизите типа «Строка» структуру сериализуют в JSON (ЗаписьJSON, ЗаписатьJSON) или XML (СериализаторXDTO). Внутри одной базы этот путь обычно лишний: временное хранилище проще и быстрее.

ПриёмКогда подходитОграничение
Реквизит формытаблица показывается в форметолько &НаСервере, тяжёлый контекст
Массив структурдо нескольких сотен строкручное преобразование
Временное хранилищебольшие объёмы, многошаговая обработканужен идентификатор формы
JSON/XMLобмен, хранение строкойрасходы на сериализацию

Асинхронность: диалоги, ожидание и длительные операции

Сам вызов сервера из формы синхронный: клиент ждёт, пока сервер не вернёт результат. Асинхронность в управляемых формах касается другого — диалогов с пользователем и долгих операций.

Диалоги: ОписаниеОповещения и Асинх/Ждать

В веб-клиенте модальные окна запрещены, поэтому вопросы пользователю задаются неблокирующими методами. Классический вариант — ПоказатьВопрос с ОписаниеОповещения. Начиная с платформы 8.3.18 появились асинхронные функции с ключевыми словами Асинх и Ждать:

&НаКлиенте
Асинх Процедура Перезаполнить(Команда)
	Ответ = Ждать ВопросАсинх("Перезаполнить табличную часть?", РежимДиалогаВопрос.ДаНет);
	Если Ответ = КодВозвратаДиалога.Да Тогда
		ПерезаполнитьНаСервере();
	КонецЕсли;
КонецПроцедуры

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

Долгие расчёты: фоновое задание

Если серверная обработка идёт минуту, форма «зависнет». Решение — запустить фоновое задание и опрашивать его с клиента:

  1. На сервере запустите задание через ФоновыеЗадания.Выполнить(), передав параметры массивом.
  2. Результат задание кладёт во временное хранилище по адресу, который вы передали заранее.
  3. На клиенте подключите ПодключитьОбработчикОжидания и раз в 1–2 секунды проверяйте состояние задания серверным вызовом.
  4. Когда задание завершилось, заберите результат из хранилища.

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

Как вернуть данные и сигнал с сервера на клиент

Сервер не может сам вызвать клиентскую процедуру. Инициатива всегда у клиента: он вызвал — сервер ответил. Поэтому «вызов клиента с сервера» решают обходными, но штатными способами:

  • 🔁 Возвращаемое значение. Сервер возвращает структуру с результатом и признаком, что делать дальше: Новый Структура("Успех, ТекстОшибки, ОткрытьФорму", ...). Клиент читает поля и сам открывает форму, задаёт вопрос или вызывает Оповестить.
  • 💬 Сообщения пользователю. Сообщение = Новый СообщениеПользователю на сервере с методом Сообщить() — текст появится у пользователя после возврата вызова. Можно привязать сообщение к полю формы через свойство Поле.
  • ⏱ Опрос. Для фоновых процессов клиент сам периодически спрашивает сервер через обработчик ожидания.
  • 🧷 Изменённые параметры. Параметр без Знач, изменённый на сервере, вернётся на клиент — способ вернуть несколько значений, но злоупотреблять им не стоит.

Пример с возвратом инструкций для клиента:

&НаКлиенте
Процедура Проверить(Команда)
	Результат = ПроверитьНаСервере(Объект.Ссылка);
	Если Не Результат.Успех Тогда
		ПоказатьПредупреждение(, Результат.ТекстОшибки);
		Возврат;
	КонецЕсли;
	Оповестить("ДокументПроверен", , Объект.Ссылка);
КонецПроцедуры

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

Ошибки и оптимизация серверных вызовов

Ниже — то, что чаще всего тормозит формы и ломает код при переходе с клиента на сервер.

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

Приёмы, которые реально ускоряют форму

  1. Один вызов вместо десяти. Не вызывайте сервер в цикле по строкам табличной части. Соберите массив ссылок на клиенте, отправьте одним вызовом, верните соответствие «ссылка → значение».
  2. Внеконтекстный вызов по умолчанию. &НаСервереБезКонтекста не гоняет данные формы туда и обратно.
  3. Знач у всех входных параметров. Минус обратная передача.
  4. Повторное использование возвращаемых значений. Для редко меняющихся данных — констант, настроек пользователя — сделайте общий модуль с флагом повторного использования «На время сеанса». Результат функции кешируется, повторный вызов с теми же параметрами не идёт на сервер.
  5. Клиентский код там, где он справляется. Пересчёт суммы строки Количество * Цена не требует сервера. Делайте его в &НаКлиенте или в процедуре &НаКлиентеНаСервереБезКонтекста.
  6. Параметры формы при открытии. Нужные данные передавайте в ОткрытьФорму() через структуру параметров и читайте в ПриСозданииНаСервере — это не отдельный вызов, а часть открытия формы.

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

Кеш с повторным использованием: на что смотреть

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

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

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