Как исправить ошибку получения макета и вывести внешний отчет на управляемую форму в 1С

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

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

Причина ошибки «Поле объекта не обнаружено» при обращении к макету

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

Макет = ВнешниеОтчеты.ВидыОплатККМ.ПолучитьМакет("Макет");

Выясним причину такого поведения. Для встроенных в конфигурацию отчетов разработчики привыкли использовать менеджер коллекции — Отчеты.ИмяОтчета.ПолучитьМакет(...). Однако объект ВнешниеОтчеты представляет собой ВнешниеОтчетыМенеджер. В отличие от менеджера стандартных метаданных, он не содержит списка внешних файлов конфигурации и не предоставляет обращение через точку к конкретному отчету. У него присутствуют только служебные методы для работы с файлами, такие как Создать() или Подключить().

Поскольку внешний отчет уже открыт, в контексте его управляемой формы существует основной реквизит формы — Отчет (тип ДанныеФормыСтруктураСDynamicRef). Чтобы добраться до свойств и методов самого прикладного объекта (включая метод получения макета), необходимо преобразовать данные формы в реальный объект информационной базы.

Способ 1: Получение макета внешнего отчета через РеквизитФормыВЗначение

Рассмотрим стандартный и наиболее корректный способ извлечения макета в серверном контексте модуля управляемой формы. Воспользуемся встроенной функцией РеквизитФормыВЗначение():


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

Разберем, как это работает: метод РеквизитФормыВЗначение("Отчет") создает экземпляр объекта ВнешнийОтчетОбъект.<ИмяОтчета> со всеми его реквизитами, табличными частями и метаданными макетов. После этого вызов метода ПолучитьМакет() успешно возвращает нужный табличный документ.

Способ 2: Вызов экспортной функции из модуля объекта

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


// В модуле объекта внешнего отчета:
Функция ПолучитьМакетОтчета(ИмяМакета) Экспорт
    Возврат ПолучитьМакет(ИмяМакета);
КонецФункции

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


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

Ошибка преобразования значения к типу Дата

Следующая распространенная ошибка возникает при попытке рассчитать границы периода отчета:


КонецПериода = КонецДня(КонецПериода);
НачалоПериода = НачалоДня(НачалоПериода);

Сообщение «Преобразование значения к типу Дата не может быть выполнено» свидетельствует о том, что в функции НачалоДня() или КонецДня() передан параметр, тип которого не является датой. В среде управляемых форм это происходит по следующим причинам:

  1. Реквизиты НачалоПериода и КонецПериода добавлены на форму, но для них не задан явный тип Дата (в свойствах реквизита оставлен составной или неопределенный тип). В таком случае значение по умолчанию равно Неопределено.
  2. Пользователь не заполнил поля ввода на форме перед нажатием кнопки формирования.

Чтобы избежать ошибки, необходимо задать жесткую типизацию реквизитов формы и добавить проверку заполнения:


&НаСервере
Процедура УстановитьПараметрыПериода(Запрос)
    Если Не ЗначениеЗаполнено(НачалоПериода) Или Не ЗначениеЗаполнено(КонецПериода) Тогда
        ВызватьИсключение "Необходимо заполнить период отчета!";
    КонецЕсли;
    
    Запрос.УстановитьПараметр("НачалоПериода", НачалоДня(НачалоПериода));
    Запрос.УстановитьПараметр("КонецПериода", КонецДня(КонецПериода));
КонецПроцедуры

Обратите внимание: конструкция НачалоДня(ТекущаяДата()) + 1 не прибавляет к дате сутки. В платформе 1С к переменным типа Дата числа прибавляются в секундах. Выражение + 1 сдвинет время всего на одну секунду вперед. Для смещения на целые сутки необходимо прибавлять 86400 секунд (24 * 60 * 60) либо использовать специальные функции добавления периода.

Почему отчет формируется, но не выводится на форму

Рассмотрим фрагмент кода, где макет заполняется данными, но после выполнения процедуры на форме ничего не появляется:


&НаСервере
Процедура СформироватьНаСервере()
    ТабДок = Новый ТабличныйДокумент;
    // ... заполнение ТабДок областями макета ...
КонецПроцедуры

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

Для корректного отображения результата на управляемой форме выполним следующие шаги:

  1. В редакторе формы перейдем на вкладку Реквизиты и добавим новый реквизит (например, с именем РезультатОтчета) с типом ТабличныйДокумент.
  2. Перетащим созданный реквизит в окно элементов формы (в левую верхнюю панель). Платформа автоматически создаст элемент с видом Поле табличного документа.
  3. В программном коде на сервере будем выводить данные непосредственно в этот реквизит формы, исключив создание локального объекта через Новый ТабличныйДокумент.

Посмотрим на итоговый рабочий вариант процедуры формирования отчета:


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

Важное замечание по тексту запроса: в исходном коде использовалось соединение табличной части документа с его шапкой через ЛЕВОЕ СОЕДИНЕНИЕ, после чего в секции ГДЕ проверялась дата чека. В 1С обращение к реквизитам документа-владельца из табличной части выполняется напрямую через точку: ЧекККМОплата.Ссылка.Дата, что избавляет от громоздких и неоптимальных соединений таблиц базы данных.

Альтернативный вариант: вывод отчета в отдельное окно

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


&НаСервере
Функция СформироватьДокументНаСервере()
    ТабДок = Новый ТабличныйДокумент;
    // ... заполнение ТабДок ...
    Возврат ТабДок;
КонецФункции

&НаКлиенте
Процедура Сформировать(Команда)
    ТабДок = СформироватьДокументНаСервере();
    ТабДок.Показать("Отчет по видам оплат ККМ");
КонецПроцедуры

Табличный документ поддерживает сериализацию, поэтому его передача из контекста &НаСервере в контекст &НаКлиенте выполняется платформой без ошибок.

← На главную