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

· 9 мин

Содержание

Чтобы вызвать серверную процедуру общего модуля из клиента 1С, у модуля в палитре свойств должны стоять флаги «Сервер» и «Вызов сервера», а сама процедура объявлена с ключевым словом Экспорт. Тогда в клиентском коде пишут просто ИмяМодуля.ИмяПроцедуры(Параметры) — платформа сама передаст параметры на сервер, выполнит код и вернёт результат.

Внутри модуля формы всё проще: клиентская процедура с директивой &НаКлиенте вызывает процедуру формы с директивой &НаСервере или &НаСервереБезКонтекста напрямую. Механизм общий для платформы 8.3 и не зависит от конфигурации: одинаково работает в БП 3.0, ЗУП 3.1, УТ 11 и собственной разработке на управляемых формах.

Где выполняется код: клиент и сервер

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

Разделение строгое:

  • 🖥️ На клиенте доступны форма, её элементы, диалоги, ПоказатьПредупреждение(), работа с локальными файлами. Нет доступа к базе: запрос, Справочники.Номенклатура.НайтиПоКоду(), объект документа здесь не скомпилируются.
  • 🗄️ На сервере доступны запросы, объекты данных, регистры, транзакции, ПривилегированныйРежим. Нет интерфейса: открыть форму или показать вопрос нельзя.
  • 🔁 Вызов сервера — переход из первой зоны во вторую. Каждый такой переход — сетевой обмен с сериализацией параметров туда и обратно.

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

Свойства общего модуля и директивы компиляции

Флаги общего модуля

Видимость общего модуля задают флажки в палитре свойств (в Конфигураторе — двойной щелчок по модулю в ветке Общие → Общие модули, затем окно свойств):

ФлагиГде модуль доступенМожно ли вызвать с клиента
Клиент (управляемое приложение)Только клиентДа, это клиентский код
СерверТолько серверНет — клиент модуль не видит
Сервер + Вызов сервераСервер, с клиента — серверным вызовомДа
Клиент + СерверОба контекста, код компилируется дваждыДа, но выполнится на клиенте
Сервер + ПривилегированныйСервер, без проверки правНет (флаг «Вызов сервера» с ним не сочетается)

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

Модуль «Клиент + Сервер» часто путают с серверным вызовом. Его экспортные процедуры, вызванные с клиента, выполнятся на клиенте, и обращение к базе внутри них не скомпилируется. Чтобы развести код внутри такого модуля, используют инструкции препроцессора:

#Если Сервер Тогда
    // код только для серверной части
#КонецЕсли

По стандартам разработки модули с флагом «Вызов сервера» называют с суффиксом ВызовСервера, например ПродажиВызовСервера. По имени сразу видно, что обращение к процедуре — это поход на сервер.

Директивы в модуле формы

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

ДирективаГде выполняетсяЧто уходит на сервер
&НаКлиентеКлиент—
&НаСервереСерверПараметры + данные формы (реквизиты)
&НаСервереБезКонтекстаСерверТолько параметры
&НаКлиентеНаСервереБезКонтекстаТам, откуда вызвалиТолько параметры

Процедура без директивы в модуле формы считается серверной (&НаСервере). Это частый источник лишних серверных вызовов: обработчик без директивы тянет на сервер всю форму.

Как вызвать серверную процедуру: пошагово

Вариант 1. Процедура общего модуля

  1. В Конфигураторе создайте общий модуль, например ЗаказыВызовСервера.
  2. В свойствах установите флаги «Сервер» и «Вызов сервера», снимите «Клиент (управляемое приложение)».
  3. Напишите функцию с ключевым словом Экспорт:
Функция ОстатокТовара(Номенклатура, Склад) Экспорт

    Запрос = Новый Запрос;
    Запрос.Текст =
    "ВЫБРАТЬ
    |    ЕСТЬNULL(СУММА(Остатки.КоличествоОстаток), 0) КАК Количество
    |ИЗ
    |    РегистрНакопления.ТоварыНаСкладах.Остатки(
    |        ,
    |        Номенклатура = &Номенклатура
    |            И Склад = &Склад) КАК Остатки";
    Запрос.УстановитьПараметр("Номенклатура", Номенклатура);
    Запрос.УстановитьПараметр("Склад", Склад);

    Выборка = Запрос.Выполнить().Выбрать();
    Выборка.Следующий();
    Возврат Выборка.Количество;

КонецФункции
  1. В клиентском обработчике формы вызовите её через имя модуля:
&НаКлиенте
Процедура НоменклатураПриИзменении(Элемент)
    Остаток = ЗаказыВызовСервера.ОстатокТовара(Объект.Номенклатура, Объект.Склад);
    Элементы.ДекорацияОстаток.Заголовок = СтрШаблон("На складе: %1", Остаток);
КонецПроцедуры

Имя регистра ТоварыНаСкладах и реквизиты — условные, подставьте свои. Агрегатная функция СУММА с ЕСТЬNULL гарантирует, что выборка вернёт одну строку даже при нулевом остатке.

Вариант 2. Серверная процедура формы

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

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

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

Изменения Объект.Товары вернутся на клиент автоматически вместе с данными формы.

Вариант 3. Форма → серверная процедура → чисто серверный модуль

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

Какой способ вызова выбрать?

Передача параметров и возврат результата на клиент

При вызове сервера платформа сериализует параметры: на сервер приходят копии значений, а не те же объекты. Отсюда ограничения на типы.

Передать можно:

  • ✅ примитивы: Строка, Число, Дата, Булево, Неопределено;
  • ✅ ссылки: СправочникСсылка, ДокументСсылка, перечисления;
  • ✅ коллекции из таких значений: Массив, Структура, Соответствие и их фиксированные варианты;
  • ✅ ДанныеФормыСтруктура, ДанныеФормыКоллекция, ХранилищеЗначения, ДвоичныеДанные.

Передать нельзя:

  • ❌ ТаблицаЗначений, ДеревоЗначений — на клиенте этих типов нет. Конвертируйте в массив структур или используйте ДанныеФормыВЗначение()/ЗначениеВДанныеФормы() на сервере;
  • ❌ СправочникОбъект, ДокументОбъект, Запрос, МенеджерЗаписи — серверные объекты;
  • ❌ элементы формы и саму форму.

Если на сервер нужно передать или вернуть большой объём (файл, выгрузку), используйте временное хранилище: ПоместитьВоВременноеХранилище() возвращает строку-адрес, а через сеть идёт только эта строка.

Знач и обратная передача

Параметр без Знач в серверной процедуре, вызванной с клиента, после выполнения передаётся обратно на клиент. Если сервер его изменил, клиент увидит изменения. Если параметр нужен только для чтения, объявляйте его с Знач — платформа не будет гнать данные назад:

Функция СуммаПоЗаказам(Знач МассивЗаказов) Экспорт

Для большого массива ссылок это заметно сокращает трафик.

Возвращать несколько значений удобнее одной структурой, чем несколькими вызовами:

Результат = Новый Структура("Остаток, Резерв, Цена", 0, 0, 0);
// заполнение на сервере
Возврат Результат;

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

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

&НаКлиенте
Процедура Провести(Команда)
    Попытка
        ЗаказыВызовСервера.ПровестиЗаказ(Объект.Ссылка);
    Исключение
        Инфо = ИнформацияОбОшибке();
        ПоказатьПредупреждение(, КраткоеПредставлениеОшибки(Инфо));
    КонецПопытки;
КонецПроцедуры

Подробности для разработчика — стек вызовов, модуль, строку — даёт ПодробноеПредставлениеОшибки(). Пользователю его не показывают, а пишут в журнал регистрации на сервере через ЗаписьЖурналаРегистрации().

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

Сообщения и ошибки, которые возникают чаще всего:

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

Как сократить число серверных вызовов

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

Что помогает:

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

Проверить, сколько реально вызовов делает форма, можно встроенным замером: в Конфигураторе запустите отладку и включите Отладка → Замер производительности. В результатах видно, какие строки выполнялись на сервере и сколько раз.

Не вызывайте сервер в цикле на клиенте. Цикл из 1000 итераций с вызовом внутри — это 1000 сетевых обращений. Передайте массив целиком одним вызовом и обработайте его на сервере.

Права доступа и безопасность серверных процедур

Экспортная процедура модуля «Сервер + Вызов сервера» — это фактически открытая точка входа. Её может вызвать любой клиентский код конфигурации, в том числе код внешней обработки, которую пользователь откроет у себя. Отсюда правила:

  • 🔒 Проверяйте права внутри процедуры. Серверный код выполняется с правами текущего пользователя, и при чтении данных без прав будет ошибка. Но если процедура меняет данные «в обход» (например, включает привилегированный режим), проверьте право сами через ПравоДоступа() или ВыполнитьПроверкуПравДоступа().
  • 🔒 Не ставьте «Вызов сервера» привилегированным модулям. Платформа это сочетание и не разрешает. Правильная схема — модуль «Вызов сервера» проверяет права и параметры, а потом вызывает функцию привилегированного модуля.
  • 🔒 Проверяйте входные параметры. Ожидаете ДокументСсылка.ЗаказКлиента — проверьте тип через ТипЗнч(). Не подставляйте строки с клиента в текст запроса конкатенацией: только через Запрос.УстановитьПараметр().
  • 🔒 Держите «Вызов сервера» точечно. Не ставьте флаг на большие служебные модули «на всякий случай». Каждая экспортная функция такого модуля доступна клиенту.

УстановитьПривилегированныйРежим(Истина) внутри обычного серверного модуля работает только в серверном контексте и только на время выполнения кода. Включайте его на минимальном участке и выключайте сразу после.

Проверка серверной процедуры перед публикацией
Выполнено: 0 / 6

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

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