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