Вызов сервера из формы 1С: параметры, типы данных и скорость
Содержание
Чтобы передать значение с клиента на сервер в 1С, вызовите из процедуры &НаКлиенте серверную процедуру или функцию и передайте значение параметром. Для общего модуля нужен флажок «Вызов сервера», для модуля формы — директива &НаСервере или &НаСервереБезКонтекста.
Типичная ситуация: программист дописывает кнопку в форме документа, пишет Запрос прямо в клиентском обработчике и получает ошибку — на клиенте запросы недоступны. Дальше разберём, как устроен обмен между клиентом и сервером, какие типы можно передавать и как не превратить форму в генератор лишних серверных вызовов.
Как устроен обмен между клиентом и сервером в 1С
Тонкий клиент и веб-клиент не имеют доступа к базе данных. Всё, что читает или пишет данные, выполняется на сервере. Клиент отвечает за интерфейс: формы, диалоги, реакцию на кнопки.
Каждый переход с клиента на сервер — это сетевой вызов. Платформа сериализует параметры, отправляет их, выполняет код и возвращает результат. В файловом варианте «сервер» живёт в том же процессе, но правила компиляции те же, поэтому код пишут так же, как для клиент-серверной базы.
Где выполнится процедура, определяет директива компиляции перед ней:
| Директива | Где выполняется | Что доступно |
|---|---|---|
&НаКлиенте | на клиенте | данные формы, элементы формы, диалоги |
&НаСервере | на сервере | данные формы, база данных; элементы формы — только свойства |
&НаСервереБезКонтекста | на сервере | только параметры и база, формы нет |
&НаКлиентеНаСервереБезКонтекста | там, откуда вызвали | только общие для клиента и сервера типы |
Общие модули работают по флажкам в свойствах модуля: «Клиент (управляемое приложение)», «Сервер», «Вызов сервера». Функция серверного модуля без флажка «Вызов сервера» из клиентского кода не видна.
Директивы компиляции в модуле формы появились вместе с управляемыми формами. В обычных формах толстого клиента код выполняется на клиенте и может обращаться к базе напрямую, поэтому всё описанное ниже касается управляемых форм.
Синхронный серверный вызов: передаём значение параметром
Самый простой способ — серверная функция с параметрами. Значение уходит на сервер, результат возвращается в переменную.
&НаКлиенте
Процедура РассчитатьСкидку(Команда)
Сумма = РасчетСкидкиНаСервере(Объект.Контрагент, Объект.СуммаДокумента);
Объект.СуммаСкидки = Сумма;
КонецПроцедуры
&НаСервереБезКонтекста
Функция РасчетСкидкиНаСервере(Знач Контрагент, Знач СуммаДокумента)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Контрагенты.ПроцентСкидки КАК ПроцентСкидки
|ИЗ
| Справочник.Контрагенты КАК Контрагенты
|ГДЕ
| Контрагенты.Ссылка = &Контрагент";
Запрос.УстановитьПараметр("Контрагент", Контрагент);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
Возврат Окр(СуммаДокумента * Выборка.ПроцентСкидки / 100, 2);
КонецЕсли;
Возврат 0;
КонецФункции
Реквизит ПроцентСкидки здесь условный — подставьте свой. Важна схема: клиент собрал значения, сервер посчитал и вернул число.
Контекстный и внеконтекстный вызов
Разница между &НаСервере и &НаСервереБезКонтекста — в объёме передаваемых данных:
- 📦
&НаСервере— вместе с вызовом на сервер уходят данные формы, а после выполнения изменения возвращаются на клиент. Удобно, когда нужно заполнить табличную часть или поменять реквизиты формы. - ⚡
&НаСервереБезКонтекста— передаются только параметры. Вызов легче, но кОбъект, реквизитам формы иЭтаФормаобратиться нельзя.
Правило простое: если серверной процедуре не нужна форма, ставьте &НаСервереБезКонтекста. На большой форме документа с табличными частями на сотни строк разница в трафике заметна сразу.
Зачем писать Знач перед параметром
По умолчанию параметр передаётся по ссылке. При серверном вызове это значит: платформа должна вернуть значение параметра обратно на клиент, вдруг сервер его изменил. Для крупной структуры это лишний трафик в обе стороны.
Ключевое слово Знач говорит платформе, что обратно ничего возвращать не нужно. Ставьте его у всех параметров серверных методов, которые не служат для возврата результата.
Если вы намеренно меняете параметр на сервере, чтобы получить результат на клиенте, не ставьте у него Знач, иначе изменения потеряются без всякой ошибки.
Какие типы данных можно передать на сервер и обратно
Передавать можно только значения, которые существуют и на клиенте, и на сервере. Это частая причина ошибок.
| Можно передавать | Нельзя передавать |
|---|---|
| Строка, Число, Дата, Булево, Неопределено, Null | ТаблицаЗначений, ДеревоЗначений |
| Ссылки на справочники, документы, перечисления | ДокументОбъект, СправочникОбъект |
| Структура, Массив, Соответствие и их фиксированные варианты | Запрос, РезультатЗапроса, Выборка |
| Данные формы (через реквизиты формы) | МенеджерЗаписи, НаборЗаписей |
| Адрес во временном хранилище (строка) | Объекты, привязанные к сеансу сервера |
Коллекции передаются только если все их элементы тоже допустимых типов. Структура, внутри которой лежит ТаблицаЗначений, на клиент не уйдёт.
Ссылка на объект — это не сам объект. Передали Объект.Контрагент на сервер — сервер получил ссылку и сам прочитает нужные реквизиты. Тянуть на клиент «весь контрагент» не нужно и невозможно.
Передача таблиц значений и сложных структур
Табличные данные — главная сложность. На клиенте ТаблицаЗначений недоступна, поэтому есть четыре рабочих приёма.
Способ 1. Табличная часть или реквизит формы типа ТаблицаЗначений
Если таблица показывается в форме, создайте реквизит формы с типом ТаблицаЗначений. На клиенте это ДанныеФормыКоллекция, на сервере её превращают в обычную таблицу и обратно:
&НаСервере
Процедура ЗаполнитьТаблицуНаСервере()
Таблица = РеквизитФормыВЗначение("ТаблицаОстатков");
Таблица.Очистить();
// ... заполнение из запроса
ЗначениеВРеквизитФормы(Таблица, "ТаблицаОстатков");
КонецПроцедуры
Директива здесь обязательно &НаСервере: метод работает с данными формы.
Способ 2. Массив структур
Небольшую таблицу удобно передать массивом структур. Каждая строка — Структура с нужными колонками:
&НаСервереБезКонтекста
Функция ТаблицаВМассив(Знач Номенклатура)
Результат = Новый Массив;
Запрос = Новый Запрос(
"ВЫБРАТЬ
| Цены.Номенклатура КАК Номенклатура,
| Цены.Цена КАК Цена
|ИЗ
| РегистрСведений.ЦеныНоменклатуры.СрезПоследних(, Номенклатура В (&Список)) КАК Цены");
Запрос.УстановитьПараметр("Список", Номенклатура);
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Строка = Новый Структура("Номенклатура, Цена", Выборка.Номенклатура, Выборка.Цена);
Результат.Добавить(Строка);
КонецЦикла;
Возврат Результат;
КонецФункции
Имя регистра и колонок зависит от конфигурации, пример показывает схему. Параметр Номенклатура — массив ссылок, собранный на клиенте.
Способ 3. Временное хранилище
Когда таблица большая или её нужно передать между несколькими вызовами, кладите её во временное хранилище. Клиент получает только строку-адрес:
&НаСервере
Процедура ПодготовитьДанныеНаСервере()
Таблица = Новый ТаблицаЗначений;
// ... заполнение
АдресДанных = ПоместитьВоВременноеХранилище(Таблица, УникальныйИдентификатор);
КонецПроцедуры
&НаСервереБезКонтекста
Функция ОбработатьДанные(Знач Адрес)
Таблица = ПолучитьИзВременногоХранилища(Адрес);
Возврат Таблица.Количество();
КонецФункции
АдресДанных — строковый реквизит формы. Второй параметр УникальныйИдентификатор привязывает данные к форме: хранилище очистится после её закрытия.
Если вместо идентификатора формы передать пустое значение, данные живут до конца серверного вызова. На следующем вызове по адресу уже ничего не будет — частая причина «пропадающих» данных.
Способ 4. Строка JSON или XML
Для обмена с внешними системами или хранения в реквизите типа «Строка» структуру сериализуют в JSON (ЗаписьJSON, ЗаписатьJSON) или XML (СериализаторXDTO). Внутри одной базы этот путь обычно лишний: временное хранилище проще и быстрее.
| Приём | Когда подходит | Ограничение |
|---|---|---|
| Реквизит формы | таблица показывается в форме | только &НаСервере, тяжёлый контекст |
| Массив структур | до нескольких сотен строк | ручное преобразование |
| Временное хранилище | большие объёмы, многошаговая обработка | нужен идентификатор формы |
| JSON/XML | обмен, хранение строкой | расходы на сериализацию |
Асинхронность: диалоги, ожидание и длительные операции
Сам вызов сервера из формы синхронный: клиент ждёт, пока сервер не вернёт результат. Асинхронность в управляемых формах касается другого — диалогов с пользователем и долгих операций.
Диалоги: ОписаниеОповещения и Асинх/Ждать
В веб-клиенте модальные окна запрещены, поэтому вопросы пользователю задаются неблокирующими методами. Классический вариант — ПоказатьВопрос с ОписаниеОповещения. Начиная с платформы 8.3.18 появились асинхронные функции с ключевыми словами Асинх и Ждать:
&НаКлиенте
Асинх Процедура Перезаполнить(Команда)
Ответ = Ждать ВопросАсинх("Перезаполнить табличную часть?", РежимДиалогаВопрос.ДаНет);
Если Ответ = КодВозвратаДиалога.Да Тогда
ПерезаполнитьНаСервере();
КонецЕсли;
КонецПроцедуры
Код читается сверху вниз, как обычный, но интерфейс не блокируется. Значение, которое ввёл пользователь, дальше передаётся на сервер обычным вызовом.
Долгие расчёты: фоновое задание
Если серверная обработка идёт минуту, форма «зависнет». Решение — запустить фоновое задание и опрашивать его с клиента:
- На сервере запустите задание через
ФоновыеЗадания.Выполнить(), передав параметры массивом. - Результат задание кладёт во временное хранилище по адресу, который вы передали заранее.
- На клиенте подключите
ПодключитьОбработчикОжиданияи раз в 1–2 секунды проверяйте состояние задания серверным вызовом. - Когда задание завершилось, заберите результат из хранилища.
В типовых конфигурациях на БСП для этого есть общий модуль ДлительныеОперации: он берёт на себя запуск, опрос и вывод индикатора. Если конфигурация на БСП, пользуйтесь им, а не пишите свою обвязку.
Как вернуть данные и сигнал с сервера на клиент
Сервер не может сам вызвать клиентскую процедуру. Инициатива всегда у клиента: он вызвал — сервер ответил. Поэтому «вызов клиента с сервера» решают обходными, но штатными способами:
- 🔁 Возвращаемое значение. Сервер возвращает структуру с результатом и признаком, что делать дальше:
Новый Структура("Успех, ТекстОшибки, ОткрытьФорму", ...). Клиент читает поля и сам открывает форму, задаёт вопрос или вызываетОповестить. - 💬 Сообщения пользователю.
Сообщение = Новый СообщениеПользователюна сервере с методомСообщить()— текст появится у пользователя после возврата вызова. Можно привязать сообщение к полю формы через свойствоПоле. - ⏱ Опрос. Для фоновых процессов клиент сам периодически спрашивает сервер через обработчик ожидания.
- 🧷 Изменённые параметры. Параметр без
Знач, изменённый на сервере, вернётся на клиент — способ вернуть несколько значений, но злоупотреблять им не стоит.
Пример с возвратом инструкций для клиента:
&НаКлиенте
Процедура Проверить(Команда)
Результат = ПроверитьНаСервере(Объект.Ссылка);
Если Не Результат.Успех Тогда
ПоказатьПредупреждение(, Результат.ТекстОшибки);
Возврат;
КонецЕсли;
Оповестить("ДокументПроверен", , Объект.Ссылка);
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПроверитьНаСервере(Знач Ссылка)
Результат = Новый Структура("Успех, ТекстОшибки", Истина, "");
Если Ссылка.Пустая() Тогда
Результат.Успех = Ложь;
Результат.ТекстОшибки = "Сначала запишите документ.";
КонецЕсли;
Возврат Результат;
КонецФункции
Ошибки и оптимизация серверных вызовов
Ниже — то, что чаще всего тормозит формы и ломает код при переходе с клиента на сервер.
Приёмы, которые реально ускоряют форму
- Один вызов вместо десяти. Не вызывайте сервер в цикле по строкам табличной части. Соберите массив ссылок на клиенте, отправьте одним вызовом, верните соответствие «ссылка → значение».
- Внеконтекстный вызов по умолчанию.
&НаСервереБезКонтекстане гоняет данные формы туда и обратно. Значу всех входных параметров. Минус обратная передача.- Повторное использование возвращаемых значений. Для редко меняющихся данных — констант, настроек пользователя — сделайте общий модуль с флагом повторного использования «На время сеанса». Результат функции кешируется, повторный вызов с теми же параметрами не идёт на сервер.
- Клиентский код там, где он справляется. Пересчёт суммы строки
Количество * Ценане требует сервера. Делайте его в&НаКлиентеили в процедуре&НаКлиентеНаСервереБезКонтекста. - Параметры формы при открытии. Нужные данные передавайте в
ОткрытьФорму()через структуру параметров и читайте вПриСозданииНаСервере— это не отдельный вызов, а часть открытия формы.
Число серверных вызовов удобно видеть прямо в работе: в параметрах клиентского приложения включается показ количества вызовов сервера. Для точного разбора используйте замер производительности в режиме отладки — он покажет, какая строка сколько раз ушла на сервер.
Кеш с повторным использованием: на что смотреть
Модуль с повторным использованием возвращает одно и то же значение, пока кеш жив. Если данные изменились в другом сеансе, пользователь увидит старое значение. Поэтому кешируют только то, что меняется редко. Возвращать из такого модуля изменяемые коллекции тоже не стоит: используйте ФиксированнаяСтруктура и ФиксированныйМассив, чтобы вызывающий код не испортил закешированное значение.