Почему возникает ошибка "Переменная не определена УправлениеПечатью" в модуле менеджера расширения в Толстом клиенте?

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

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

Рассмотрим типовую ситуацию: в модуле менеджера расширения мы пытаемся вызвать функцию из общего модуля основной конфигурации, например, УправлениеПечатью.МакетПечатнойФормы(). В тонком клиенте все работает корректно, а в толстом клиенте возникает ошибка: "Переменная не определена УправлениеПечатью". Давайте проанализируем, почему это происходит, и как мы можем решить эту проблему.

Выясняем причину ошибки "Переменная не определена УправлениеПечатью"

Ошибка "Переменная не определена" для общего модуля УправлениеПечатью (или любого другого общего модуля, содержащего серверный код) в толстом клиенте часто возникает из-за различий в контексте выполнения кода между тонким и толстым клиентами, а также между различными режимами работы толстого клиента (обычное приложение и управляемое приложение).

  1. Контекст выполнения общих модулей:

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

  2. Различия между тонким и толстым клиентом:
    • Тонкий клиент: В тонком клиенте любой вызов серверной функции (в том числе из общих модулей) автоматически направляется на сервер. То есть, даже если код написан в клиентском модуле, при обращении к серверной функции платформа самостоятельно организует серверный вызов, и модуль УправлениеПечатью будет доступен в серверном контексте. Поэтому в тонком клиенте такая ошибка не возникает.
    • Толстый клиент: Толстый клиент в режиме обычного приложения пытается максимально компилировать и выполнять код на клиенте. Если мы пытаемся вызвать серверную функцию из модуля УправлениеПечатью в клиентском коде модуля менеджера расширения без явного указания, что этот вызов должен происходить на сервере, платформа попытается найти и скомпилировать модуль УправлениеПечатью в клиентском контексте. Поскольку этот модуль (или его экспортные функции) не предназначен для клиентской компиляции и выполнения, возникает ошибка "Переменная не определена". В режиме управляемого приложения толстый клиент ведет себя подобно тонкому, автоматически перенаправляя вызовы серверных функций, но в контексте модуля менеджера расширения это все равно может быть критично.
  3. Роль расширений:

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

Методы решения проблемы

Для решения проблемы, когда код в модуле менеджера расширения в толстом клиенте не видит общий модуль УправлениеПечатью, мы можем применить два основных подхода. Оба подхода направлены на то, чтобы принудительно выполнять соответствующий участок кода в серверном контексте.

1. Использование директив препроцессора для управления контекстом компиляции

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

Рассмотрим ситуацию: в нашем модуле менеджера расширения есть код, вызывающий функцию УправлениеПечатью.МакетПечатнойФормы(). Подобные вызовы часто требуются, когда необходимо выполнить заполнение Word-макетов при помощи БСП.

Исходный код (вызывающий ошибку), который часто встречается, когда используется шаблон ВПФ для вывода в Word:


// Модуль менеджера расширения
Процедура ПриНачалеРаботыСистемы()
    // Какой-то код...
    Макет = УправлениеПечатью.МакетПечатнойФормы("ОбщийМакет.ПФ_MXL_ПФ"); // Здесь возникает ошибка
    // Какой-то еще код...
КонецПроцедуры

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


#Если Сервер Или ТолстыйКлиентОбычноеПриложение Или ВнешнееСоединение Тогда
    // Код, который должен выполняться на сервере или в специфических клиентских режимах
#КонецЕсли

Эта конструкция означает, что заключенный в нее код будет компилироваться и выполняться, если контекст текущего приложения является серверным, или если это толстый клиент в режиме обычного приложения, или если это внешнее соединение. Именно режим "ТолстыйКлиентОбычноеПриложение" является ключевым в нашей ситуации, поскольку он позволяет толстому клиенту понять, что этот участок кода нужно обрабатывать как серверный или доступный для выполнения в этом режиме.

Решение с использованием директив препроцессора:


// Модуль менеджера расширения
Процедура ПриНачалеРаботыСистемы()
    // Какой-то код...
    #Если Сервер Или ТолстыйКлиентОбычноеПриложение Или ВнешнееСоединение Тогда
        Макет = УправлениеПечатью.МакетПечатнойФормы("ОбщийМакет.ПФ_MXL_ПФ"); // Ошибка ушла!
    #КонецЕсли
    // Какой-то еще код...
КонецПроцедуры

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

2. Использование атрибута &НаСервере для конкретных процедур и функций

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

Если код с вызовом УправлениеПечатью.МакетПечатнойФормы() находится внутри отдельной экспортной или внутренней процедуры/функции в модуле менеджера расширения, мы можем просто добавить к ней атрибут &НаСервере.

Пример использования атрибута &НаСервере:


// Модуль менеджера расширения

// ... какой-то клиентский код ...

Процедура ПриНачалеРаботыСистемы()
    // Какой-то клиентский код...
    ВыполнитьПечатьНаСервере();
    // Какой-то еще клиентский код...
КонецПроцедуры

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

В этом случае, когда процедура ВыполнитьПечатьНаСервере() вызывается из клиентского контекста (например, из ПриНачалеРаботыСистемы()), платформа 1С автоматически организует вызов этой процедуры на сервере. В серверном контексте модуль УправлениеПечатью гарантированно доступен, и ошибка "Переменная не определена" не возникнет.

Этот подход особенно удобен, когда только часть логики в модуле менеджера требует серверного выполнения, а остальной код может работать на клиенте. Мы можем выделить серверную логику в отдельные процедуры или функции и пометить их атрибутом &НаСервере.

Важные замечания

  1. Общие модули и заимствование: Нам не требуется заимствовать общий модуль УправлениеПечатью в расширение. Проблема заключается не в отсутствии самого модуля, а в недоступности его в конкретном контексте компиляции и выполнения кода из расширения. Заимствование общих модулей в расширение имеет смысл только в том случае, если мы хотим изменить их поведение, добавить свои экспортные функции или процедуры, или переопределить существующие.
  2. Свойства модуля УправлениеПечатью: Как правило, сам общий модуль УправлениеПечатью в основной конфигурации уже имеет правильные свойства и директивы препроцессора внутри себя, обеспечивающие его доступность на сервере. Поэтому проблема кроется именно в модуле менеджера *расширения*, который по умолчанию может быть интерпретирован как клиентский.
  3. Почему тонкий клиент "не ошибается": Тонкий клиент по своей архитектуре всегда работает через сервер, поэтому любой вызов функции, которая может быть серверной, автоматически передается на сервер для выполнения. Толстый клиент в режиме обычного приложения более "самодостаточен" и пытается выполнить как можно больше кода на своей стороне, что и приводит к таким ошибкам, если не указан явный серверный контекст.

Заключение

Таким образом, если мы столкнулись с ошибкой "Переменная не определена УправлениеПечатью" (или другим серверным общим модулем) в модуле менеджера расширения при работе в толстом клиенте, нам необходимо обеспечить выполнение кода, обращающегося к этому модулю, в серверном контексте. Мы можем сделать это одним из двух способов:

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

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

← На главную