При разработке внешних обработок для массового формирования документов часто возникает задача использовать существующий алгоритм заполнения реквизитов и табличных частей. Однако если разработчик конфигурации поместил этот алгоритм в модуль управляемой формы, при попытке вызвать процедуру через созданный объект возникает ошибка: Метод объекта не обнаружен. Разберем причины такого поведения платформы 1С:Предприятие 8.3 и рассмотрим правильные варианты реализации задачи.
Проанализируем типичный пример кода, в котором возникает ошибка:
&НаСервере
Процедура СформироватьНаСервере()
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| КВП_Здания.Ссылка
|ИЗ
| Справочник.КВП_Здания КАК КВП_Здания
|ГДЕ
| НЕ КВП_Здания.ЭтоГруппа
| И НЕ КВП_Здания.ПометкаУдаления";
РезультатЗапроса = Запрос.Выполнить();
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
ДокОбъект = Документы.УПЖКХ_ЗакрытиеПериодаПоНачислениямЖКХ.СоздатьДокумент();
ДокОбъект.Дата = КонецМесяца(Объект.Дата);
ДокОбъект.Организация = УПЖКХ_ТиповыеМетодыВызовСервера.ПолучитьЗначениеПоУмолчанию("ОсновнаяОрганизация");
ДокОбъект.КорректировкаРасчета = Истина;
// Ошибка: Метод объекта не обнаружен (ЗаполнитьТабличныеЧастиДокумента)
ДокОбъект.ЗаполнитьТабличныеЧастиДокумента(Истина);
ДокОбъект.Записать(РежимЗаписиДокумента.Проведение);
КонецЦикла;
КонецПроцедуры
Выясним причину ошибки: переменная ДокОбъект содержит ссылку на экземпляр ДокументОбъект, чьим контекстом является Модуль объекта. Процедура ЗаполнитьТабличныеЧастиДокумента объявлена с директивой &НаСервере Экспорт в Модуле управляемой формы. При невизуальном создании объекта через СоздатьДокумент() модуль формы вообще не инициализируется и не компилируется, а его экспортные методы недоступны для экземпляра прикладного объекта.
Более того, платформа управляемого приложения не позволяет вызывать экспортные серверные процедуры формы напрямую снаружи (через ПолучитьФорму() на клиенте доступны только клиентские методы формы). Попытка инициализировать управляемые формы в цикле для массовой генерации документов приводит к критическому падению производительности и избыточному расходу оперативной памяти.
Если конфигурация доступна для редактирования, наиболее грамотным архитектурным решением является разделение бизнес-логики и интерфейса. Алгоритм заполнения табличных частей выносится из формы в модуль менеджера документа или модуль объекта:
// В модуле менеджера документа
Процедура ЗаполнитьТабличныеЧастиДокумента(ДокументОбъект, ПолучатьОстаткиСКорректировками) Экспорт
// Алгоритм выборки данных и заполнения табличных частей
// Обращение к реквизитам идет через переданный параметр ДокументОбъект
КонецПроцедуры
&НаСервере
Процедура ЗаполнитьТабличныеЧастиДокументаНаСервере()
ДокОбъект = РеквизитФормыВЗначение("Объект");
Документы.УПЖКХ_ЗакрытиеПериодаПоНачислениямЖКХ.ЗаполнитьТабличныеЧастиДокумента(ДокОбъект, Истина);
ЗначениеВРеквизитФормы(ДокОбъект, "Объект");
КонецПроцедуры
ДокОбъект = Документы.УПЖКХ_ЗакрытиеПериодаПоНачислениямЖКХ.СоздатьДокумент();
ДокОбъект.Дата = КонецМесяца(Объект.Дата);
Документы.УПЖКХ_ЗакрытиеПериодаПоНачислениямЖКХ.ЗаполнитьТабличныеЧастиДокумента(ДокОбъект, Истина);
ДокОбъект.Записать(РежимЗаписиДокумента.Проведение);
Если логика заполнения стандартна при создании нового документа, ее также можно разместить в стандартном обработчике ОбработкаЗаполнения модуля объекта и вызывать через метод ДокОбъект.Заполнить(ПараметрыЗаполнения).
Если конфигурация находится на поддержке и менять исходный код нельзя, перенесем или добавим процедуру в модуль менеджера или модуль объекта с помощью механизма расширений:
&Вместо или &После, перенаправив расчет в общий метод расширения.Когда требуется выполнить задачу исключительно силами внешней обработки без изменений конфигурации и добавления расширений, перенесем код функции непосредственно в модуль обработки. Рассмотрим порядок адаптации кода:
Объект.ИмяРеквизита на обращения к полям локального объекта ДокОбъект.ИмяРеквизита.Элементы.), клиентским переменным и диалогам пользователя, если они присутствовали в исходной процедуре.
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
ДокОбъект = Документы.УПЖКХ_ЗакрытиеПериодаПоНачислениямЖКХ.СоздатьДокумент();
ДокОбъект.Дата = КонецМесяца(Объект.Дата);
ДокОбъект.Организация = УПЖКХ_ТиповыеМетодыВызовСервера.ПолучитьЗначениеПоУмолчанию("ОсновнаяОрганизация");
ДокОбъект.КорректировкаРасчета = Истина;
// Вызов локальной серверной процедуры обработки
ЗаполнитьТабличныеЧастиОбработки(ДокОбъект, Истина);
ДокОбъект.Записать(РежимЗаписиДокумента.Проведение);
КонецЦикла;
Данный подход обеспечивает максимальную скорость выполнения при пакетной генерации документов и полностью исключает зависимость фонового серверного кода от контекста экранных форм.