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