Как вызвать экспортную функцию из модуля объекта обработки в модуле управляемой формы в 1С:Предприятие 8.3 (Тонкий клиент)?

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

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

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

Анализируем проблему: почему "Метод объекта не обнаружен"?

Давайте посмотрим на исходный код, который демонстрирует проблему. В модуле объекта обработки у нас есть экспортная процедура Печать():


// В модуле объекта:
#Если Сервер Тогда
Процедура Печать() Экспорт
    // ... логика печати ...
КонецПроцедуры
#КонецЕсли

А в модуле управляемой формы мы пытаемся вызвать эту процедуру следующими способами:


// В модуле формы (УФ):
&НаСервере
Процедура МояСервернаяПроцедура()
    // Вариант 1
    Обработки.ПечатьЦенниковЭтикеток.Создать().Печать(); // Ошибка: "Метод объекта не обнаружен"

    // Вариант 2
    ДокОб = РеквизитФормыВЗначение("Объект");
    ДокОб.Печать(); // Ошибка: "Метод объекта не обнаружен"
КонецПроцедуры

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

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

Выясняем причину: влияние директивы препроцессора

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

  1. Контекст компиляции и выполнения: В управляемом приложении, особенно при работе в тонком клиенте, модуль объекта по умолчанию компилируется и выполняется на стороне сервера. Это означает, что код внутри #Если Сервер Тогда, конечно же, компилируется на сервере.
  2. Видимость методов для клиентского кода формы: Когда код модуля формы (даже серверный код, помеченный &НаСервере) пытается обратиться к методу объекта, он ожидает, что этот метод будет доступен в том контексте, в котором выполняется вызов. Платформа 1С выполняет определенную работу по разрешению имен методов и сопоставлению их с объектами. Если метод находится внутри #Если Сервер Тогда, он может быть не виден для механизма разрешения ссылок, используемого серверным контекстом формы для вызова методов объекта, что приводит к ошибке "Метод объекта не обнаружен". Директива препроцессора, хоть и компилирует код, может влиять на метаданные о методах, доступных для вызова из других модулей.
  3. Обычные формы и толстый клиент: В толстом клиенте в режиме управляемого приложения, а также при файловом варианте работы, модули объектов могут иногда компилироваться и выполняться на клиенте. Исторически, #Если Сервер Тогда использовалась для того, чтобы явно разграничить код, который должен работать только на сервере, от клиентского кода или кода, который не должен быть доступен на клиенте. Однако для современных управляемых форм и тонкого клиента это ограничение может стать избыточным или даже вредным.

Шаги к решению: устраняем проблему видимости метода

Решение этой проблемы оказалось на удивление простым: необходимо убрать директиву препроцессора #Если Сервер Тогда (и соответствующую #КонецЕсли) из модуля объекта, чтобы процедура Печать() была доступна для компиляции без дополнительных условий.

  1. Изменяем модуль объекта: Перейдите в модуль объекта вашей обработки и удалите строки #Если Сервер Тогда и #КонецЕсли вокруг процедуры Печать().

// В модуле объекта (после изменений):
Процедура Печать() Экспорт
    // ... логика печати ...
КонецПроцедуры

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

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

Правильный подход к вызову серверных процедур из управляемой формы

Давайте закрепим общий и рекомендованный подход к вызову экспортных процедур из модуля объекта в управляемой форме. Этот подход обеспечивает корректное взаимодействие между клиентом и сервером:

  1. Объявляем экспортную процедуру в модуле объекта: Функция или процедура в модуле объекта, которую мы хотим вызвать, должна быть экспортной, то есть объявлена с ключевым словом Экспорт. Теперь, когда мы убрали лишние директивы препроцессора, она будет всегда доступна на сервере.
  2. Вызываем серверную процедуру из клиентской: Непосредственный вызов серверного кода из клиентского контекста управляемой формы невозможен. Поэтому из клиентской процедуры формы необходимо вызвать серверную процедуру.

// Пример в модуле формы:
&НаКлиенте
Процедура МояКнопкаНажатие(Команда)
    // Вызываем серверную процедуру формы
    МояСервернаяПроцедура();
КонецПроцедуры
  1. Получаем объект на сервере: На клиенте прикладные объекты 1С (документы, справочники, обработки) не существуют в полном объеме, они доступны только на сервере. В серверной процедуре формы мы получаем текущий объект обработки, связанный с формой, используя функцию РеквизитФормыВЗначение("Объект").
  2. Вызываем экспортную процедуру модуля объекта: После получения объекта на сервере, мы можем вызвать его экспортную процедуру или функцию.

// Продолжение примера в модуле формы:
&НаСервере
Процедура МояСервернаяПроцедура()
    // Получаем объект обработки, связанный с формой
    ОбъектОбработки = РеквизитФормыВЗначение("Объект");
    
    // Вызываем экспортную процедуру из модуля объекта
    ОбъектОбработки.Печать(); 
    
    // Если в серверной процедуре были внесены изменения в ОбъектОбработки,
    // их необходимо вернуть в реквизит формы
    ЗначениеВРеквизитФормы(ОбъектОбработки, "Объект");
КонецПроцедуры

В этом примере Печать() — это наша экспортная процедура в модуле объекта обработки.

Почему вариант Обработки.ПечатьЦенниковЭтикеток.Создать().Печать(); не подходил?

Первый вариант вызова, который был опробован, Обработки.ПечатьЦенниковЭтикеток.Создать().Печать();, не является корректным для работы с текущим объектом формы, даже если бы проблема с директивой #Если Сервер Тогда отсутствовала. Давайте разберем почему:

  1. Создание нового объекта: Метод Создать() создает новый экземпляр объекта метаданных (в данном случае, обработки). Это означает, что мы работаем с чистым, только что созданным объектом, который не имеет отношения к данным, загруженным или изменяемым в текущей форме.
  2. Контекст формы: Если форма редактирует или оперирует конкретным экземпляром обработки (например, при заполнении реквизитов обработки на форме), то нам необходимо работать именно с этим экземпляром, а не с новым. Функция РеквизитФормыВЗначение("Объект") как раз позволяет получить объект, который является источником данных для текущей формы.

Таким образом, даже без проблемы с видимостью метода, использование Создать() было бы методологически неверным для сценария, когда требуется воздействовать на объект, связанный с текущей формой.

Дополнительные аспекты и директивы компиляции

Для полного понимания контекстов выполнения и их взаимодействия, давайте кратко рассмотрим основные элементы и директивы в 1С:

  1. Модуль формы: Предназначен для обработки действий пользователя и работы с интерфейсом. Может содержать как клиентский код (&НаКлиенте) для интерактивных действий, так и серверный код (&НаСервере, &НаСервереБезКонтекста) для работы с данными на сервере.
  2. Модуль объекта: Содержит процедуры и функции для обработки событий, возникающих при работе с самим объектом (запись, удаление, проведение), а также экспортные процедуры, которые можно вызывать из других модулей (как в нашем случае).
  3. Общие модули: Используются для размещения кода, который может быть использован в разных частях системы. Они могут быть чисто серверными, чисто клиентскими или комбинированными (клиент-серверными). Для вызова серверных процедур из клиентского кода в общих модулях необходимо установить флаг "Вызов сервера" в свойствах модуля. Это явно указывает платформе, какие методы серверного модуля разрешено вызывать с клиента.

Основные директивы компиляции:

Совместимость с толстым клиентом и обычными формами

Автор исходного вопроса правильно задался вопросом: "Надеюсь в Обычном приложении ничего не сломается?". При удалении #Если Сервер Тогда из модуля объекта, код процедуры Печать() становится безусловным для компиляции. Если в процедуре нет клиентских вызовов (например, методов работы с интерфейсом), то для обычных форм и толстого клиента, где модуль объекта также может исполняться на сервере, это не вызовет проблем. Однако, если модуль объекта предполагал выполнение некоторых частей кода на клиенте в толстом клиенте или во внешнем соединении, а процедура Печать() должна быть только серверной, то удаление директивы может потребовать более аккуратного подхода.

В таких сложных случаях, когда требуется обеспечить совместимость с несколькими режимами работы клиента и сервера, можно использовать более универсальные директивы препроцессора:


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

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

Мы выяснили, что причиной ошибки "Метод объекта не обнаружен" при вызове экспортной процедуры из модуля объекта обработки в модуле управляемой формы в 1С:Предприятие 8.3 (Тонкий клиент) стала излишняя директива препроцессора #Если Сервер Тогда в модуле объекта. Удалив эту директиву и следуя стандартному подходу вызова серверных процедур через РеквизитФормыВЗначение("Объект"), мы успешно решили проблему.

← На главную