В практике разработки на платформе 1С:Предприятие 8.2 и 8.3 часто возникает ситуация, когда программист пытается создать «универсальный» общий модуль, который должен работать и на клиенте, и на сервере. Однако при попытке вызвать функцию, ограниченную инструкциями препроцессора, из модуля управляемой формы, система выдает ошибку: «Процедура или функция не обнаружена». Рассмотрим подробно, почему это происходит, разберем внутренние механизмы платформы и выясним, как правильно проектировать такие модули.
Представим общий модуль, у которого в свойствах установлены три флажка: Клиент (управляемое приложение), Сервер и Вызов сервера. Внутри модуля написан следующий код:
#Если Сервер Тогда
Функция ПолучитьДанные() Экспорт
// Здесь выполняется запрос к базе данных
Возврат РезультатЗапроса;
КонецФункции
#КонецЕсли
При вызове этой функции из модуля формы в процедуре с директивой &НаКлиенте выполнение прерывается ошибкой. Разберем по шагам, что происходит в памяти компьютера в этот момент.
Когда мы устанавливаем флаги Клиент и Сервер одновременно, платформа 1С не создает один «общий» экземпляр модуля. На самом деле она компилирует два независимых образа одного и того же модуля:
#Если Сервер Тогда срабатывает как «Ложь», и всё содержимое внутри нее просто удаляется из итогового скомпилированного кода.#Если Сервер Тогда возвращает «Истина», и функция включается в код.Проанализируем ключевой момент: когда вы пишете в модуле формы ОбщийМодуль.ПолучитьДанные() на клиенте, система первым же делом обращается к клиентскому образу модуля. Она видит, что модуль с таким именем существует, но функции ПолучитьДанные() внутри него нет — препроцессор «вырезал» её при компиляции. В результате поиск прекращается, и система выдает ошибку, даже не пытаясь обратиться к серверу.
Многие разработчики полагают, что флаг Вызов сервера автоматически сделает любую серверную функцию доступной везде. Однако это работает только в определенных условиях. Выясним причину такого поведения.
Флаг Вызов сервера (Server Call) позволяет клиентскому коду «пробросить» вызов на сторону сервера. Но этот механизм активируется только тогда, когда система понимает, что на клиенте этого метода точно нет и быть не может. Рассмотрим два сценария:
ОбщийМодуль, понимает, что он серверный, проверяет разрешение на «Вызов сервера» и успешно перенаправляет запрос.Если ваша задача — вызывать серверную функцию напрямую из клиента без лишних посредников, самым простым решением будет снять флаг «Клиент» в свойствах общего модуля. Оставьте только Сервер и Вызов сервера. В этом случае инструкции препроцессора внутри модуля станут избыточными, так как весь модуль будет компилироваться только на сервере.
Рассмотрим пример кода в модуле формы после такого изменения:
&НаКлиенте
Процедура ВыполнитьДействие(Команда)
// Теперь это сработает, так как 1С перенаправит вызов на сервер
Результат = МойСерверныйМодуль.ПолучитьДанные();
КонецПроцедуры
Если по каким-то причинам вы не можете изменить настройки общего модуля (например, это типовая конфигурация), необходимо явно передать контекст выполнения с клиента на сервер внутри самой формы. Проанализируем, как это выглядит:
&НаКлиенте
Процедура ВыполнитьВызов(Команда)
// Вызываем серверную функцию текущей формы
Результат = ПолучитьДанныеССервера();
КонецПроцедуры
&НаСервере
Функция ПолучитьДанныеССервера()
// На сервере препроцессор видит функцию в общем модуле
Возврат ОбщийМодуль.ПолучитьДанные();
КонецФункции
В этой схеме управление сначала переходит на сервер (в контекст формы), а уже оттуда вызывается серверный образ общего модуля. Это обходит проблему клиентской компиляции.
Наиболее правильным и современным подходом, который рекомендует фирма «1С» (в частности, в стандартах разработки БСП), является разделение логики на разные модули в зависимости от их назначения. Рассмотрим рекомендуемую структуру:
Использование инструкций препроцессора #Если Сервер Тогда внутри модуля с флагом Клиент считается «плохим тоном» и признаком непродуманной архитектуры. Это не только путает разработчика, но и заставляет платформу тратить ресурсы на обработку «лишнего» кода при компиляции клиентского приложения.
Подводя итог, можно сказать, что использование препроцессора в общих модулях с установленным флагом Клиент — это путь к неочевидным ошибкам. Выясним основные правила, которых стоит придерживаться:
Запрос, Новый Запрос или обращается к серверным объектам (например, Справочники.Номенклатура.СоздатьЭлемент()), она обязана находиться в серверном контексте.Рациональное разделение кода между клиентскими и серверными модулями не только решает проблему видимости функций, но и существенно упрощает дальнейшую поддержку и отладку конфигурации.