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