Почему возникает ошибка «Процедура или функция не определена» в модуле менеджера при использовании управляемых форм в толстом клиенте?

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

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

Суть проблемы и анализ контекста

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

Проанализируем пример кода, который вызывает затруднение:


#Если Сервер И НЕ Клиент И НЕ ВнешнееСоединение Тогда
Функция глЗначениеПеременной(Имя) Экспорт
    Кэш = ПараметрыСеанса.ОбщиеЗначения.Получить();
    КэшИзменен = Ложь;
    ПолученноеЗначение = ОбщегоНазначения.ПолучитьЗначениеПеременной(Имя, Кэш, КэшИзменен);
    
    Если КэшИзменен Тогда
        ПараметрыСеанса.ОбщиеЗначения = Новый ХранилищеЗначения(Кэш);
    КонецЕсли;
    
    Возврат ПолученноеЗначение;
КонецФункции
#КонецЕсли

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

Где на самом деле выполняется модуль менеджера?

Многие начинающие (и даже опытные) разработчики полагают, что МодульМенеджера — это всегда серверный контекст. Это заблуждение. Выясним реальное положение дел:

  1. В тонком клиенте и веб-клиенте: Модуль менеджера действительно всегда исполняется на стороне сервера. Это происходит потому, что тонкий клиент в принципе не предназначен для выполнения тяжелой логики работы с данными объекта.
  2. В толстом клиенте (обычное приложение): Модуль менеджера компилируется на стороне клиента. Это ключевой момент. В этом режиме толстый клиент берет на себя функции по обработке данных, которые в управляемом режиме делегируются серверу.

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

Разбор директив препроцессора

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

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

Роль флага «Вызов сервера» (Server Call)

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

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

Пошаговое решение проблемы

Чтобы распутать этот клубок контекстов, рекомендуем выполнить следующие действия:

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

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


// В настройках модуля: Сервер = Истина, Вызов сервера = Истина
Функция глЗначениеПеременной(Имя) Экспорт
    #Если Сервер Или ТолстыйКлиентОбычноеПриложение Или ВнешнееСоединение Тогда
        // Здесь логика получения значения, например, через ПараметрыСеанса
        Возврат ПолучитьЗначениеИзКэша(Имя);
    #Иначе
        // Для тонкого клиента, если вызов идет не через сервер
        Возврат Неопределено;
    #КонецЕсли
КонецФункции

Особенности работы с глобальными модулями

Часто функция глЗначениеПеременной располагается в глобальном общем модуле. Рассмотрим, как это влияет на ситуацию. Глобальные модули подгружаются в контекст при старте системы. Если при компиляции глобального контекста для толстого клиента (клиентская часть) функция была исключена препроцессором, она не попадет в глобальное пространство имен.

При обращении к ней из модуля менеджера (который в толстом клиенте тоже клиентский) платформа не найдет ее в глобальном контексте и выдаст ту самую ошибку. Важно помнить, что в управляемом приложении (тонкий клиент) использование глобальных модулей крайне не рекомендуется из-за их влияния на производительность, однако в старых конфигурациях (УПП, КА 1.1) это стандартная практика.

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

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

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

← На главную