Как передать параметры из одного внешнего отчета в другой внешний отчет в 1С?

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

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

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

---

Способ 1. Прямая установка параметров СКД в обычных формах и обход проблемы пользовательских настроек

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

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

Выясним, как решить эту проблему программно. Мы должны не просто установить параметр в основных настройках, но и найти связанный элемент в пользовательских настройках по его уникальному идентификатору. Разберем по шагам рабочий код:


// 1. Получаем двоичные данные внешнего отчета из справочника
ВнешнийОтчетСсылка = Справочники.ВнешниеОбработки.НайтиПоНаименованию("Анализ цен");
Если ВнешнийОтчетСсылка.Пустая() Тогда
    Предупреждение("В справочнике ""Внешние отчеты"" не найден отчет с наименованием ""Анализ цен""");
    Возврат;
КонецЕсли;

ДвоичныеДанные = ВнешнийОтчетСсылка.ХранилищеВнешнейОбработки.Получить();
ИмяФайла = ПолучитьИмяВременногоФайла("epf");
ДвоичныеДанные.Записать(ИмяФайла);

// 2. Создаем объект внешнего отчета в памяти
ОтчетАнализЦен = ВнешниеОтчеты.Создать(ИмяФайла, Ложь);

// 3. Устанавливаем параметр "Номенклатура" в основных настройках
ПараметрНоменклатура = ОтчетАнализЦен.КомпоновщикНастроек.Настройки.ПараметрыДанных.НайтиЗначениеПараметра(Новый ПараметрКомпоновкиДанных("Номенклатура"));
Если ПараметрНоменклатура <> Неопределено Тогда
    ПараметрНоменклатура.Значение = Расшифровка.Номенклатура; // Значение из расшифровки первого отчета
    ПараметрНоменклатура.Использование = Истина;
    
    // СИНХРОНИЗАЦИЯ С ПОЛЬЗОВАТЕЛЬСКИМИ НАСТРОЙКАМИ
    // Если параметр выведен на форму пользователю, меняем его и там
    Если ЗначениеЗаполнено(ПараметрНоменклатура.ИдентификаторПользовательскойНастройки) Тогда
        Для Каждого ЭлементПользовательских Из ОтчетАнализЦен.КомпоновщикНастроек.ПользовательскиеНастройки.Элементы Цикл
            Если ЭлементПользовательских.ИдентификаторПользовательскойНастройки = ПараметрНоменклатура.ИдентификаторПользовательскойНастройки Тогда
                ЭлементПользовательских.Значение = Расшифровка.Номенклатура;
                ЭлементПользовательских.Использование = Истина;
                Прервать;
            КонецЕсли;
        КонецЦикла;
    КонецЕсли;
КонецЕсли;

// 4. Устанавливаем параметр периода "ДатаОтчета"
ПараметрДата = ОтчетАнализЦен.КомпоновщикНастроек.Настройки.ПараметрыДанных.Find("ДатаОтчета");
Если ПараметрДата <> Неопределено Тогда
    ПараметрДата.Значение = ТекущаяДата();
    ПараметрДата.Использование = Истина;
    
    // Аналогично синхронизируем пользовательскую настройку для даты
    Если ЗначениеЗаполнено(ПараметрДата.ИдентификаторПользовательскойНастройки) Тогда
        Для Каждого ЭлементПользовательских Из ОтчетАнализЦен.КомпоновщикНастроек.ПользовательскиеНастройки.Элементы Цикл
            Если ЭлементПользовательских.ИдентификаторПользовательскойНастройки = ПараметрДата.ИдентификаторПользовательскойНастройки Тогда
                ЭлементПользовательских.Значение = ТекущаяДата();
                ЭлементПользовательских.Использование = Истина;
                Прервать;
            КонецЕсли;
        КонецЦикла;
    КонецЕсли;
КонецЕсли;

// 5. Получаем форму и открываем ее
Форма = ОтчетАнализЦен.ПолучитьФорму();
Форма.Открыть();

// Удаляем временный файл (платформа уже загрузила объект в оперативную память)
УдалитьФайлы(ИмяФайла);

---

Способ 2. Использование экспортных методов объекта (Чистый архитектурный подход)

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

Рассмотрим этот элегантный подход по шагам.

Шаг 1. В Модуле объекта открываемого внешнего отчета ("Анализ цен") пишем следующую экспортную процедуру:


Процедура ИнициализироватьОтчетИзВнешнегоИсточника(СтруктураПараметров) Экспорт
    
    // Установка параметров СКД
    ПараметрыДанных = ЭтотОбъект.КомпоновщикНастроек.Настройки.ПараметрыДанных;
    
    Если СтруктураПараметров.Свойство("Номенклатура") Тогда
        ПараметрНоменклатура = ПараметрыДанных.НайтиЗначениеПараметра(Новый ПараметрКомпоновкиДанных("Номенклатура"));
        Если ПараметрНоменклатура <> Неопределено Тогда
            ПараметрНоменклатура.Значение = СтруктураПараметров.Номенклатура;
            ПараметрНоменклатура.Использование = Истина;
        КонецЕсли;
    КонецЕсли;
    
    Если СтруктураПараметров.Свойство("НаДату") Тогда
        ПараметрДата = ПараметрыДанных.НайтиЗначениеПараметра(Новый ПараметрКомпоновкиДанных("ДатаОтчета"));
        Если ПараметрДата <> Неопределено Тогда
            ПараметрДата.Значение = СтруктураПараметров.НаДату;
            ПараметрДата.Использование = Истина;
        КонецЕсли;
    КонецЕсли;
    
    // Здесь также можно программно настроить группировки или выбранные поля, если необходимо
    
КонецПроцедуры

Шаг 2. В первом (вызывающем) отчете код вызова становится лаконичным и простым. Нам не нужно знать внутреннее устройство параметров второго отчета:


ОтчетАнализЦен = ВнешниеОтчеты.Создать(ИмяФайлаВременного, Ложь);

// Формируем пакет параметров для передачи
ПараметрыДляПередачи = Новый Структура;
ПараметрыДляПередачи.Вставить("Номенклатура", Расшифровка.Номенклатура);
ПараметрыДляПередачи.Вставить("НаДату", ТекущаяДата());

// Вызываем экспортный метод инициализации
ОтчетАнализЦен.ИнициализироватьОтчетИзВнешнегоИсточника(ПараметрыДляПередачи);

// Открываем форму
Форма = ОтчетАнализЦен.ПолучитьФорму();
Форма.Открыть();

---

Способ 3. Передача параметров в Управляемых формах (v8.3)

Если ваша конфигурация работает на управляемых формах, классический подход с получением формы через метод объекта работать не будет. Платформа 1С v8.3 требует четкого разделения контекста на клиентский и серверный. Также платформа не позволяет использовать путь к файлу напрямую в методе ОткрытьФорму.

Проанализируем правильный алгоритм для управляемых форм:

  1. На сервере получаем двоичные данные отчета и регистрируем его во временном хранилище.
  2. Подключаем внешний отчет в сеансе при помощи метода ВнешниеОтчеты.Подключить(), который возвращает уникальное системное имя отчета.
  3. Передаем параметры отбора и указание сформировать отчет в специальной структуре параметров формы.
  4. Вызываем стандартный метод ОткрытьФорму() на клиенте.

Посмотрим на пример программной реализации:


// Серверная функция для регистрации отчета в системе
&НаСервере
Функция ПодключитьВнешнийОтчетНаСервере(ДвоичныеДанныеОтчета)
    
    // Помещаем двоичные данные во временное хранилище
    АдресХранилища = ПоместитьВоВременноеХранилище(ДвоичныеДанныеОтчета, Новый УникальныйИдентификатор);
    
    // Подключаем внешний отчет и получаем его системное имя
    ИмяВнешнегоОтчета = ВнешниеОтчеты.Подключить(АдресХранилища, , Ложь);
    Возврат ИмяВнешнегоОтчета;
    
КонецФункции

// Клиентская процедура открытия
&НаКлиенте
Процедура ОткрытьОтчетНаУправляемыхФормах(ВыбраннаяНоменклатура)
    
    // Получаем двоичные данные (в реальной задаче они берутся с сервера)
    ДвоичныеДанныеОтчета = ПолучитьДвоичныеДанныеОтчетаНаСервере(); 
    
    // Подключаем отчет
    ИмяОтчетаВСистеме = ПодключитьВнешнийОтчетНаСервере(ДвоичныеДанныеОтчета);
    
    // Формируем структуру параметров формы для СКД
    ПараметрыФормы = Новый Структура;
    
    // Автоматическое выполнение компоновки при открытии
    ПараметрыФормы.Вставить("СформироватьПриОткрытии", Истина); 
    
    // Задаем структуру отборов, которую СКД распознает автоматически
    СтруктураОтбора = Новый Структура;
    СтруктураОтбора.Вставить("Номенклатура", ВыбраннаяНоменклатура);
    
    ПараметрыФормы.Вставить("Отбор", СтруктураОтбора);
    
    // Открываем форму отчета по зарегистрированному в системе имени
    ОткрытьФорму("ВнешнийОтчет." + ИмяОтчетаВСистеме + ".ФормаОтчета", ПараметрыФормы);
    
КонецПроцедуры

---

Способ 4. Передача параметров через элементы формы напрямую (для не-СКД отчетов)

Если открываемый внешний отчет построен по старой технологии (без СКД, например, на базе простого запроса или построителя отчета) и параметры в нем выводятся через обычные реквизиты формы, мы можем передать значения напрямую в элементы этой формы. Этот способ актуален для обычных форм.

Рассмотрим пример реализации:


Попытка
    // Находим внешний отчет в справочнике
    ВнешняяФормаСсылка = Справочники.ВнешниеОбработки.НайтиПоНаименованию("Отчет по номенклатуре");
    ИмяФайла = ПолучитьИмяВременногоФайла("epf");
    
    ДвоичныеДанные = ВнешняяФормаСсылка.ХранилищеВнешнейОбработки.Получить();
    ДвоичныеДанные.Записать(ИмяФайла);
    
    // Получаем форму отчета без его предварительного создания как объекта базы данных
    Форма = ВнешниеОбработки.ПолучитьФорму(ИмяФайла);
    
    // Напрямую заполняем значения на форме открываемого отчета
    Форма.ЭлементыФормы.Номенклатура.Значение = ЗначениеРасшифровки;
    Форма.ЭлементыФормы.ДатаНачала.Значение   = НачалоМесяца(ТекущаяДата());
    Форма.ЭлементыФормы.ДатаОкончания.Значение = КонецМесяца(ТекущаяДата());
    
    // Открываем заполненную форму пользователю
    Форма.Открыть();
    
Исключение
    Сообщить("Не удалось корректно открыть внешний отчет по номенклатуре! Ошибка: " + ОписаниеОшибки());
КонецПопытки;

---

Способ 5. Передача параметров через Хранилище общих настроек

Иногда возникают ситуации, когда прямая передача параметров затруднена (например, из-за сложных ограничений прав доступа или особенностей архитектуры конфигурации). В таких случаях можно использовать системный механизм 1С — ХранилищеОбщихНастроек. Это своего рода глобальный буфер обмена настройками в рамках сеанса пользователя.

Выясним, как это работает на практике.

В первом (вызывающем) отчете перед открытием второго отчета мы сохраняем параметры под уникальным ключом:


// Формируем структуру с параметрами
НастройкиДляПередачи = Новый Структура;
НастройкиДляПередачи.Вставить("Номенклатура", Расшифровка.Номенклатура);
НастройкиДляПередачи.Вставить("ДатаОтчета", ТекущаяДата());

// Сохраняем в системное хранилище настроек
ХранилищеОбщихНастроек.Сохранить("ВнешниеОтчеты_ПередачаПараметров", "ПараметрыАнализаЦен", НастройкиДляПередачи);

Во втором (открываемом) отчете в событии формы ПриОткрытии (или в модуле объекта при инициализации) мы считываем данные настройки и применяем их:


// Считываем сохраненные ранее настройки
НастройкиДляПередачи = ХранилищеОбщихНастроек.Загрузить("ВнешниеОтчеты_ПередачаПараметров", "ПараметрыАнализаЦен");

Если НастройкиДляПередачи <> Неопределено Тогда
    
    // Устанавливаем параметры в компоновщик
    ПараметрНоменклатура = КомпоновщикНастроек.Настройки.ПараметрыДанных.НайтиЗначениеПараметра(Новый ПараметрКомпоновкиДанных("Номенклатура"));
    Если ПараметрНоменклатура <> Неопределено Тогда
        ПараметрНоменклатура.Значение = НастройкиДляПередачи.Номенклатура;
        ПараметрНоменклатура.Использование = Истина;
    КонецЕсли;
    
    // Обязательно очищаем за собой хранилище, чтобы при обычном открытии отчета параметры не подтягивались снова
    ХранилищеОбщихНастроек.Удалить("ВнешниеОтчеты_ПередачаПараметров", "ПараметрыАнализаЦен");
    
КонецЕсли;

---

Дополнительные рекомендации и автоматическое формирование отчета

Для того чтобы сделать работу пользователя максимально комфортной, хорошей практикой является автоматический запуск формирования отчета сразу после его открытия. Пользователю не придется делать лишний клик по кнопке "Сформировать".

В обычных формах для гарантированного вывода данных СКД-отчета в табличное поле формы используйте метод СкомпоноватьРезультат():


Форма = ОтчетАнализЦен.ПолучитьФорму();

// Программная компоновка результата в табличный документ на форме (обычно он называется "Результат")
ОтчетАнализЦен.СкомпоноватьРезультат(Форма.ЭлементыФормы.Результат); 

Форма.Открыть();

В управляемых формах, как мы уже рассматривали ранее, достаточно просто передать системный параметр СформироватьПриОткрытии со значением Истина в структуру параметров метода ОткрытьФорму().

Также помните о безопасности работы с временными файлами. При использовании метода ВнешниеОтчеты.Создать(ИмяФайла) платформа полностью загружает двоичный исполняемый код отчета в оперативную память сеанса, поэтому физический файл .epf с диска можно удалять сразу после вызова конструктора создания. Однако, если ваш отчет динамически обращается к своим файловым макетам во время работы, безопаснее производить очистку временных файлов в событии ПриЗакрытии формы самого открываемого отчета.

← На главную