Почему не вызываются серверные процедуры из клиентских в одном общем модуле и как это исправить

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В практике разработки на платформе 1С:Предприятие 8 часто возникает ситуация, когда программист пытается вызвать процедуру с директивой &НаСервере из клиентского контекста, находясь при этом в одном и том же общем модуле. Несмотря на установленные флаги «Сервер» и «Вызов сервера», система выдает ошибку, сообщая, что метод не обнаружен. Проанализируем, почему так происходит, и разберем архитектурные принципы, которые помогут избежать подобных ошибок.

Суть проблемы: механизм «двойной» компиляции

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

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

  1. Клиентский экземпляр: компилируется для исполнения на стороне клиентского приложения.
  2. Серверный экземпляр: компилируется для работы на стороне сервера.

Выясним причину ошибки: когда программный код исполняется на клиенте и обращается к процедуре этого же модуля, 1С ищет её в «клиентском» экземпляре. Если процедура помечена как серверная или обернута в инструкции препроцессора #Если Сервер Тогда, она просто не попадает в клиентскую версию скомпилированного кода. Платформа видит, что модуль доступен локально (на клиенте), и не инициирует удаленный вызов (RPC) к серверу, считая, что процедура должна быть здесь. Не найдя её, система выдает ошибку.

Почему флаг «Вызов сервера» не спасает ситуацию

Разберем подробнее роль флага Вызов сервера. Этот флаг предназначен исключительно для того, чтобы разрешить клиентскому коду «перешагнуть» границу контекста и обратиться к процедурам модуля, который компилируется только на сервере.

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

Посмотрим на пример кода, который часто приводит к ошибке:


// В модуле установлены флаги: Клиент, Сервер, Вызов сервера

&НаКлиенте
Процедура КомандаВыполнить(Команда) Экспорт
    // Платформа ищет этот метод в клиентской копии этого же модуля
    Результат = МойСерверныйМетод(); 
КонецПроцедуры

&НаСервере
Функция МойСерверныйМетод() Экспорт
    // Этот код существует только в серверной копии модуля
    Возврат "Данные с сервера";
КонецФункции

В данном примере вызов МойСерверныйМетод() приведет к ошибке «Процедура или функция с указанным именем не определена», так как компилятор на клиенте «отрезал» все, что помечено &НаСервере. Для автоматического поиска подобных проблем в коде можно использовать инструмент для анализа конфигураций на ошибки вызова процедур общих модулей.

Правильная архитектура: разделение контекстов

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

  1. ИмяМодуляКлиент:
    • Флаги: только Клиент.
    • Назначение: логика интерфейса, открытие форм, сообщения пользователю.
  2. ИмяМодуляСервер:
    • Флаги: только Сервер.
    • Назначение: тяжелые вычисления, запросы к базе данных, обработка документов.
    • Примечание: из клиента методы этого модуля напрямую недоступны.
  3. ИмяМодуляВызовСервера:
    • Флаги: Сервер и Вызов сервера.
    • Назначение: «шлюз» или «мост» между клиентом и сервером. Именно сюда клиент обращается, чтобы получить данные с сервера.

Выясним, как это выглядит в коде. Предположим, нам нужно получить остаток товара из общего модуля:

Шаг 1. Создаем модуль СкладСервер (Флаг: Сервер)


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

Шаг 2. Создаем модуль СкладВызовСервера (Флаги: Сервер, Вызов сервера)


Функция ОстатокТовара(Товар) Экспорт
    // Вызываем логику из чисто серверного модуля
    Возврат СкладСервер.ПолучитьОстатокНаСкладе(Товар);
КонецФункции

Шаг 3. Вызываем из клиента (из любого места)


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

Оптимизация производительности и памяти

Важно понимать, что объединение флагов «Клиент» и «Сервер» в одном огромном модуле негативно сказывается на производительности. Проанализируем ситуацию: если ваш модуль содержит 10 000 строк кода, большая часть из которых — это работа с запросами и транзакциями (серверная часть), то при включенном флаге «Клиент» весь этот объем текста будет передаваться на клиентское место при инициализации системы.

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

Инструкции препроцессора в общих модулях

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

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

← На главную