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

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

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

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

Выясним причину возникновения ошибки

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

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

Решение 1. Правильный вызов экспортного метода через получение объекта на сервере

Для того чтобы вызвать процедуру или функцию модуля объекта из модуля управляемой формы, необходимо перейти в серверный контекст и преобразовать реквизит формы в реальный прикладной объект с помощью платформенного метода РеквизитФормыВЗначение.

Посмотрим на пример организации серверной функции в модуле формы:


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

Если вызываемая процедура объекта модифицирует реквизиты самой обработки (например, заполняет их из сохраненных настроек), изменения, сделанные в объекте ОбработкаОбъект, не отобразятся на форме автоматически. Чтобы обновить реквизиты на форме, требуется выполнить обратное преобразование с помощью метода ЗначениеВРеквизитФормы.

Рассмотрим пример восстановления и сохранения настроек при открытии и закрытии формы:


#Область ОбработчикиСобытийФормы

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

&НаКлиенте
Процедура ПриЗакрытии(ЗавершениеРаботы)
    СохранитьНастройкиНаСервере();
КонецПроцедуры

#КонецОбласти

#Область СлужебныеПроцедурыИФункции

&НаСервере
Процедура СохранитьНастройкиНаСервере()
    ОбработкаОбъект = РеквизитФормыВЗначение("Объект");
    ОбработкаОбъект.СохранитьНастройки();
КонецПроцедуры

#КонецОбласти

Решение 2. Подготовка процедур для запуска обработки по расписанию (фоновые задания)

Если обработка должна выполняться в регламентном задании без участия пользователя, вся бизнес-логика обязана находиться в модуле объекта. В фоновом сеансе формы не создаются, поэтому вызовы любых методов формы невозможны.

Проанализируем ключевые правила написания кода в модуле объекта для регламентного выполнения:

  1. Экспортная точка входа: Для интеграции с Библиотекой Стандартных Подсистем (БСП) в модуле объекта объявляется процедура ВыполнитьКоманду с директивой экспорта:
    
    Процедура ВыполнитьКоманду(ИдентификаторКоманды, ПараметрыВыполнения) Экспорт
        ВосстановитьНастройки();
        ВыполнитьЗакрытиеЗаказов();
    КонецПроцедуры
    
  2. Учет контекста пользователя в хранилище настроек: При использовании метода ОбщегоНазначения.ХранилищеОбщихНастроекЗагрузить или стандартного ХранилищеОбщихНастроек.Загрузить в регламентном задании сеанс выполняется под системным пользователем. Если настройки сохранялись конкретным пользователем, фоновое задание их не увидит. Рекомендуется использовать общий ключ настроек и явно указывать пустую строку в качестве имени пользователя, чтобы настройки были глобальными:
    
    Процедура СохранитьНастройки() Экспорт
        Настройки = Новый Соответствие;
        Настройки.Вставить("Отсрочка", Отсрочка);
        Настройки.Вставить("ВключаяНеСогласованныеЗаказы", ВключаяНеСогласованныеЗаказы);
        
        // Сохраняем настройки с явным ключом, доступным для всех пользователей
        ХранилищеОбщихНастроек.Сохранить("Обработка_ЗакрытиеЗаказов", "ОбщиеНастройки", Настройки, , "");
    КонецПроцедуры
    
    Процедура ВосстановитьНастройки() Экспорт
        ЗначениеНастроек = ХранилищеОбщихНастроек.Загрузить("Обработка_ЗакрытиеЗаказов", "ОбщиеНастройки", , "");
        Если ТипЗнч(ЗначениеНастроек) = Тип("Соответствие") Тогда
            Отсрочка = ЗначениеНастроек.Получить("Отсрочка");
            ВключаяНеСогласованныеЗаказы = ЗначениеНастроек.Получить("ВключаяНеСогласованныеЗаказы");
        КонецЕсли;
    КонецПроцедуры
    
  3. Использование серверного времени: В коде фоновых заданий вместо ТекущаяДата() рекомендуется применять ТекущаяДатаСеанса(), что обеспечивает корректную работу с часовыми поясами информационной базы.

Рекомендации по оптимизации запросов при заполнении табличных частей

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

  1. Передача условий в параметры виртуальной таблицы: Никогда не накладывайте фильтры по измерениям (Склад, Организация) в блоке ГДЕ. Условие должно передаваться напрямую в параметры виртуальной таблицы Остатки(&Период, Склад = &Склад), иначе СУБД выполнит расчет итогов по всей базе и лишь затем отфильтрует строки.
  2. Исключение лишней группировки: Конструкция СГРУППИРОВАТЬ ПО в запросе к виртуальной таблице остатков по ее же измерениям является избыточной и снижает производительность.

Посмотрим на оптимизированный пример запроса к остаткам:


Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ
|   ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура,
|   ТоварыНаСкладахОстатки.Характеристика КАК Характеристика,
|   ТоварыНаСкладахОстатки.ВНаличииОстаток КАК Количество
|ИЗ
|   РегистрНакопления.ТоварыНаСкладах.Остатки(&Период, Склад = &Склад И ВНаличииОстаток > 0) КАК ТоварыНаСкладахОстатки";

Запрос.УстановитьПараметр("Период", ОбъектДокумент.Дата);
Запрос.УстановитьПараметр("Склад", ОбъектДокумент.СкладОтправитель);

Таким образом, разделение логики на серверные методы объекта и интерфейсные обработчики формы с использованием РеквизитФормыВЗначение позволяет избавиться от дублирования кода и обеспечивает корректную работу обработки как вручную, так и в автоматическом режиме.

← На главную