Серверный вызов из клиентского кода 1С: общий модуль, форма, параметры
Содержание
Чтобы вызвать серверную процедуру общего модуля из клиента 1С, у модуля в палитре свойств должны стоять флаги «Сервер» и «Вызов сервера», а сама процедура объявлена с ключевым словом Экспорт. Тогда в клиентском коде пишут просто ИмяМодуля.ИмяПроцедуры(Параметры) — платформа сама передаст параметры на сервер, выполнит код и вернёт результат.
Внутри модуля формы всё проще: клиентская процедура с директивой &НаКлиенте вызывает процедуру формы с директивой &НаСервере или &НаСервереБезКонтекста напрямую. Механизм общий для платформы 8.3 и не зависит от конфигурации: одинаково работает в БП 3.0, ЗУП 3.1, УТ 11 и собственной разработке на управляемых формах.
Где выполняется код: клиент и сервер
В управляемом приложении код живёт в двух местах. Клиент — это тонкий клиент, веб-клиент или толстый клиент на компьютере пользователя. Сервер — это рабочий процесс кластера 1С, а в файловом варианте серверный контекст поднимается в процессе клиента, но правила видимости остаются теми же.
Разделение строгое:
- 🖥️ На клиенте доступны форма, её элементы, диалоги,
ПоказатьПредупреждение(), работа с локальными файлами. Нет доступа к базе: запрос,Справочники.Номенклатура.НайтиПоКоду(), объект документа здесь не скомпилируются. - 🗄️ На сервере доступны запросы, объекты данных, регистры, транзакции,
ПривилегированныйРежим. Нет интерфейса: открыть форму или показать вопрос нельзя. - 🔁 Вызов сервера — переход из первой зоны во вторую. Каждый такой переход — сетевой обмен с сериализацией параметров туда и обратно.
Код, который пишут в файловой базе, потом попадает в клиент-серверную. Поэтому писать его надо сразу так, будто между клиентом и сервером медленная сеть. В файловом режиме лишний вызов почти незаметен, а на удалённом сервере через интернет каждый вызов стоит десятки миллисекунд.
Свойства общего модуля и директивы компиляции
Флаги общего модуля
Видимость общего модуля задают флажки в палитре свойств (в Конфигураторе — двойной щелчок по модулю в ветке Общие → Общие модули, затем окно свойств):
| Флаги | Где модуль доступен | Можно ли вызвать с клиента |
|---|---|---|
| Клиент (управляемое приложение) | Только клиент | Да, это клиентский код |
| Сервер | Только сервер | Нет — клиент модуль не видит |
| Сервер + Вызов сервера | Сервер, с клиента — серверным вызовом | Да |
| Клиент + Сервер | Оба контекста, код компилируется дважды | Да, но выполнится на клиенте |
| Сервер + Привилегированный | Сервер, без проверки прав | Нет (флаг «Вызов сервера» с ним не сочетается) |
Ключевая строка — третья. Флаг «Вызов сервера» становится доступен только после установки флага «Сервер». Он не переносит код на клиент: процедура всё равно выполняется на сервере, а клиент получает право к ней обращаться.
Модуль «Клиент + Сервер» часто путают с серверным вызовом. Его экспортные процедуры, вызванные с клиента, выполнятся на клиенте, и обращение к базе внутри них не скомпилируется. Чтобы развести код внутри такого модуля, используют инструкции препроцессора:
#Если Сервер Тогда
// код только для серверной части
#КонецЕсли
По стандартам разработки модули с флагом «Вызов сервера» называют с суффиксом ВызовСервера, например ПродажиВызовСервера. По имени сразу видно, что обращение к процедуре — это поход на сервер.
Директивы в модуле формы
В общих модулях директивы компиляции не пишут — контекст задают флаги. В модуле формы и модуле команды директива ставится перед каждой процедурой:
| Директива | Где выполняется | Что уходит на сервер |
|---|---|---|
&НаКлиенте | Клиент | — |
&НаСервере | Сервер | Параметры + данные формы (реквизиты) |
&НаСервереБезКонтекста | Сервер | Только параметры |
&НаКлиентеНаСервереБезКонтекста | Там, откуда вызвали | Только параметры |
Процедура без директивы в модуле формы считается серверной (&НаСервере). Это частый источник лишних серверных вызовов: обработчик без директивы тянет на сервер всю форму.
Как вызвать серверную процедуру: пошагово
Вариант 1. Процедура общего модуля
- В Конфигураторе создайте общий модуль, например
ЗаказыВызовСервера. - В свойствах установите флаги «Сервер» и «Вызов сервера», снимите «Клиент (управляемое приложение)».
- Напишите функцию с ключевым словом
Экспорт:
Функция ОстатокТовара(Номенклатура, Склад) Экспорт
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ЕСТЬNULL(СУММА(Остатки.КоличествоОстаток), 0) КАК Количество
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Остатки(
| ,
| Номенклатура = &Номенклатура
| И Склад = &Склад) КАК Остатки";
Запрос.УстановитьПараметр("Номенклатура", Номенклатура);
Запрос.УстановитьПараметр("Склад", Склад);
Выборка = Запрос.Выполнить().Выбрать();
Выборка.Следующий();
Возврат Выборка.Количество;
КонецФункции
- В клиентском обработчике формы вызовите её через имя модуля:
&НаКлиенте
Процедура НоменклатураПриИзменении(Элемент)
Остаток = ЗаказыВызовСервера.ОстатокТовара(Объект.Номенклатура, Объект.Склад);
Элементы.ДекорацияОстаток.Заголовок = СтрШаблон("На складе: %1", Остаток);
КонецПроцедуры
Имя регистра ТоварыНаСкладах и реквизиты — условные, подставьте свои. Агрегатная функция СУММА с ЕСТЬNULL гарантирует, что выборка вернёт одну строку даже при нулевом остатке.
Вариант 2. Серверная процедура формы
Если нужны данные самой формы — табличная часть, реквизиты объекта, — пишите серверную процедуру в модуле формы:
&НаКлиенте
Процедура Пересчитать(Команда)
ПересчитатьНаСервере();
КонецПроцедуры
&НаСервере
Процедура ПересчитатьНаСервере()
Для Каждого Строка Из Объект.Товары Цикл
Строка.Сумма = Строка.Количество * Строка.Цена;
КонецЦикла;
КонецПроцедуры
Изменения Объект.Товары вернутся на клиент автоматически вместе с данными формы.
Вариант 3. Форма → серверная процедура → чисто серверный модуль
Серверная процедура формы может вызывать любой модуль с флагом «Сервер», даже без «Вызова сервера». Это хорошая схема: из клиента — один вызов &НаСервереБезКонтекста, а внутри уже сколько угодно обращений к серверным модулям, без новых сетевых переходов.
&НаСервере. Данные формы уйдут на сервер и вернутся изменёнными.&НаСервереБезКонтекста или функция общего модуля «Сервер + Вызов сервера».Передача параметров и возврат результата на клиент
При вызове сервера платформа сериализует параметры: на сервер приходят копии значений, а не те же объекты. Отсюда ограничения на типы.
Передать можно:
- ✅ примитивы:
Строка,Число,Дата,Булево,Неопределено; - ✅ ссылки:
СправочникСсылка,ДокументСсылка, перечисления; - ✅ коллекции из таких значений:
Массив,Структура,Соответствиеи их фиксированные варианты; - ✅
ДанныеФормыСтруктура,ДанныеФормыКоллекция,ХранилищеЗначения,ДвоичныеДанные.
Передать нельзя:
- ❌
ТаблицаЗначений,ДеревоЗначений— на клиенте этих типов нет. Конвертируйте в массив структур или используйтеДанныеФормыВЗначение()/ЗначениеВДанныеФормы()на сервере; - ❌
СправочникОбъект,ДокументОбъект,Запрос,МенеджерЗаписи— серверные объекты; - ❌ элементы формы и саму форму.
Если на сервер нужно передать или вернуть большой объём (файл, выгрузку), используйте временное хранилище: ПоместитьВоВременноеХранилище() возвращает строку-адрес, а через сеть идёт только эта строка.
Знач и обратная передача
Параметр без Знач в серверной процедуре, вызванной с клиента, после выполнения передаётся обратно на клиент. Если сервер его изменил, клиент увидит изменения. Если параметр нужен только для чтения, объявляйте его с Знач — платформа не будет гнать данные назад:
Функция СуммаПоЗаказам(Знач МассивЗаказов) Экспорт
Для большого массива ссылок это заметно сокращает трафик.
Возвращать несколько значений удобнее одной структурой, чем несколькими вызовами:
Результат = Новый Структура("Остаток, Резерв, Цена", 0, 0, 0);
// заполнение на сервере
Возврат Результат;
Ошибки при серверном вызове и их обработка
Исключение на сервере не теряется: оно поднимается к клиенту, который сделал вызов. Перехватить его можно обычной конструкцией Попытка:
&НаКлиенте
Процедура Провести(Команда)
Попытка
ЗаказыВызовСервера.ПровестиЗаказ(Объект.Ссылка);
Исключение
Инфо = ИнформацияОбОшибке();
ПоказатьПредупреждение(, КраткоеПредставлениеОшибки(Инфо));
КонецПопытки;
КонецПроцедуры
Подробности для разработчика — стек вызовов, модуль, строку — даёт ПодробноеПредставлениеОшибки(). Пользователю его не показывают, а пишут в журнал регистрации на сервере через ЗаписьЖурналаРегистрации().
Правило для транзакций: если ошибка возникла внутри транзакции на сервере, откатите её там же (ОтменитьТранзакцию() в блоке Исключение) и только потом пробросьте исключение дальше через ВызватьИсключение. Клиент транзакцией управлять не может.
Сообщения и ошибки, которые возникают чаще всего:
Экспорт. Добавьте его и обновите конфигурацию.ТаблицаЗначений, объект справочника или запрос. Верните массив структур или адрес во временном хранилище.#Если Сервер Тогда.Как сократить число серверных вызовов
Вызов сервера — самая дорогая операция клиентского кода. Типичная проблема медленной формы — не тяжёлый запрос, а десяток мелких вызовов на каждое изменение реквизита.
Что помогает:
- Один вызов вместо нескольких. Остаток, цену и резерв получайте одной функцией, возвращающей структуру.
&НаСервереБезКонтекставместо&НаСервере, если данные формы не нужны. С формой на 500 строк табличной части разница видна сразу.- Клиентские проверки до вызова. Пустое значение, ноль, неизменившееся поле — проверяйте на клиенте и не ходите на сервер зря.
- Повторное использование значений. У общего модуля есть свойство «Повторное использование возвращаемых значений» — «На время вызова» или «На время сеанса». Функция с одинаковыми параметрами второй раз вернёт закешированный результат без обращения к базе. Подходит для настроек, констант, редко меняющихся справочных данных. Возвращайте из таких функций фиксированные коллекции, чтобы вызывающий код не испортил кеш.
- Длительные операции — в фоновое задание. Серверный вызов синхронный: пока он идёт, интерфейс ждёт. Расчёт на минуты запускайте через
ФоновыеЗадания.Выполнить(), а клиент пусть опрашивает результат обработчиком ожидания.
Проверить, сколько реально вызовов делает форма, можно встроенным замером: в Конфигураторе запустите отладку и включите Отладка → Замер производительности. В результатах видно, какие строки выполнялись на сервере и сколько раз.
Не вызывайте сервер в цикле на клиенте. Цикл из 1000 итераций с вызовом внутри — это 1000 сетевых обращений. Передайте массив целиком одним вызовом и обработайте его на сервере.
Права доступа и безопасность серверных процедур
Экспортная процедура модуля «Сервер + Вызов сервера» — это фактически открытая точка входа. Её может вызвать любой клиентский код конфигурации, в том числе код внешней обработки, которую пользователь откроет у себя. Отсюда правила:
- 🔒 Проверяйте права внутри процедуры. Серверный код выполняется с правами текущего пользователя, и при чтении данных без прав будет ошибка. Но если процедура меняет данные «в обход» (например, включает привилегированный режим), проверьте право сами через
ПравоДоступа()илиВыполнитьПроверкуПравДоступа(). - 🔒 Не ставьте «Вызов сервера» привилегированным модулям. Платформа это сочетание и не разрешает. Правильная схема — модуль «Вызов сервера» проверяет права и параметры, а потом вызывает функцию привилегированного модуля.
- 🔒 Проверяйте входные параметры. Ожидаете
ДокументСсылка.ЗаказКлиента— проверьте тип черезТипЗнч(). Не подставляйте строки с клиента в текст запроса конкатенацией: только черезЗапрос.УстановитьПараметр(). - 🔒 Держите «Вызов сервера» точечно. Не ставьте флаг на большие служебные модули «на всякий случай». Каждая экспортная функция такого модуля доступна клиенту.
УстановитьПривилегированныйРежим(Истина) внутри обычного серверного модуля работает только в серверном контексте и только на время выполнения кода. Включайте его на минимальном участке и выключайте сразу после.
Частые вопросы
Можно ли из серверной процедуры открыть форму или задать вопрос пользователю?
Можно ли вызвать клиентскую процедуру с сервера?
Сообщить() или объект СообщениеПользователю — текст покажется на клиенте после возврата.