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