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