Почему серверная функция в общем модуле не видна на клиенте при использовании инструкций препроцессора

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

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

Анализ исходной ситуации

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


#Если Сервер Тогда
Функция ПолучитьДанные() Экспорт
    // Здесь выполняется запрос к базе данных
    Возврат РезультатЗапроса;
КонецФункции
#КонецЕсли

При вызове этой функции из модуля формы в процедуре с директивой &НаКлиенте выполнение прерывается ошибкой. Разберем по шагам, что происходит в памяти компьютера в этот момент.

Механизм компиляции и «два образа» модуля

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

  1. Клиентский образ: компилируется при запуске клиентского приложения. Платформа берет текст модуля и пропускает его через препроцессор. Поскольку в данный момент контекст — клиентский, инструкция #Если Сервер Тогда срабатывает как «Ложь», и всё содержимое внутри нее просто удаляется из итогового скомпилированного кода.
  2. Серверный образ: компилируется на стороне сервера. Здесь инструкция #Если Сервер Тогда возвращает «Истина», и функция включается в код.

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

Особенности флага «Вызов сервера»

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

Флаг Вызов сервера (Server Call) позволяет клиентскому коду «пробросить» вызов на сторону сервера. Но этот механизм активируется только тогда, когда система понимает, что на клиенте этого метода точно нет и быть не может. Рассмотрим два сценария:

Решение 1: Изменение настроек общего модуля

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

Рассмотрим пример кода в модуле формы после такого изменения:


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

Решение 2: Использование посредника в модуле формы

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


&НаКлиенте
Процедура ВыполнитьВызов(Команда)
    // Вызываем серверную функцию текущей формы
    Результат = ПолучитьДанныеССервера();
КонецПроцедуры

&НаСервере
Функция ПолучитьДанныеССервера()
    // На сервере препроцессор видит функцию в общем модуле
    Возврат ОбщийМодуль.ПолучитьДанные();
КонецФункции

В этой схеме управление сначала переходит на сервер (в контекст формы), а уже оттуда вызывается серверный образ общего модуля. Это обходит проблему клиентской компиляции.

Решение 3: Архитектурное разделение модулей (Стандарт 1С)

Наиболее правильным и современным подходом, который рекомендует фирма «1С» (в частности, в стандартах разработки БСП), является разделение логики на разные модули в зависимости от их назначения. Рассмотрим рекомендуемую структуру:

  1. ИмяМодуляКлиент (Флаг: Только «Клиент»). Содержит логику работы с интерфейсом, открытие форм, вопросы пользователю.
  2. ИмяМодуляСервер (Флаг: Только «Сервер»). Содержит основную бизнес-логику, запросы, работу с базой.
  3. ИмяМодуляВызовСервера (Флаги: «Сервер» и «Вызов сервера»). Тонкая прослойка (API), через которую клиент обращается к серверу.
  4. ИмяМодуляКлиентСервер (Флаги: «Клиент» и «Сервер», без вызова сервера). Вспомогательные функции (расчеты, форматирование строк), которые не используют обращения к БД и могут быть скомпилированы в обоих контекстах без изменений.

Использование инструкций препроцессора #Если Сервер Тогда внутри модуля с флагом Клиент считается «плохим тоном» и признаком непродуманной архитектуры. Это не только путает разработчика, но и заставляет платформу тратить ресурсы на обработку «лишнего» кода при компиляции клиентского приложения.

Выводы и рекомендации

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

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

← На главную