Как корректно установить параметры отчета СКД при программном формировании и выгрузке в ТаблицуЗначений?

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

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

В чем заключается проблема: Почему параметры из ПриКомпоновкеРезультата не подтягиваются?

Основная причина, по которой параметры, заданные в процедуре ПриКомпоновкеРезультата модуля объекта внешнего отчета, остаются пустыми при программном выводе в ТаблицуЗначений, кроется в жизненном цикле формирования отчета. Процедура ПриКомпоновкеРезультата, как правильно было отмечено в обсуждении, запускается только в определенных случаях:

  1. При интерактивном формировании отчета через его форму.
  2. При вызове метода СкомпоноватьРезультат у объекта отчета (например, ОтчетОбъект.СкомпоноватьРезультат(ТабличныйДокумент)).

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


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

обработчик ПриКомпоновкеРезультата не вызывается. Следовательно, весь код, который отвечает за установку параметров внутри этой процедуры, просто не выполняется, и параметры остаются со значениями по умолчанию (или пустыми), что приводит к пустому результату отчета.

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

Решение 1: Программная установка параметров напрямую в общем модуле

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

Шаг 1: Получаем объект настроек СКД

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


ПолучениеОбъекта = ДополнительныеОтчетыИОбработкиВызовСервера.ПолучитьОбъектВнешнейОбработки(СсылкаНаОтчет);
СхемаКД = ПолучениеОбъекта.ПолучитьМакет("ОсновнаяСхемаКомпоновкиДанных");
НастройкиКД = СхемаКД.НастройкиПоУмолчанию;

Шаг 2: Устанавливаем значения параметров

Теперь, когда у нас есть объект НастройкиКД, мы можем найти в нем нужные параметры и установить для них значения. Важно не только присвоить значение, но и установить свойство Использование параметра в Истина, чтобы компоновщик данных учел наше значение.


// Пример установки одного параметра
ИмяПараметра = "НашПараметрДаты"; // Имя параметра, как он задан в схеме СКД
ЗначениеПараметра = НачалоДня(ТекущаяДата()); // Ваше значение для параметра

ПараметрДанных = НастройкиКД.ПараметрыДанных.Найти(ИмяПараметра);
Если ПараметрДанных <> Неопределено Тогда
    ПараметрДанных.Значение = ЗначениеПараметра;
    ПараметрДанных.Использование = Истина; // Обязательно! Иначе параметр не будет применен
КонецЕсли;

// Если параметров несколько, повторяем для каждого
ИмяПараметра2 = "НашПараметрОтбора";
ЗначениеПараметра2 = Справочники.Контрагенты.НайтиПоНаименованию("ООО Ромашка");

ПараметрДанных2 = НастройкиКД.ПараметрыДанных.Найти(ИмяПараметра2);
Если ПараметрДанных2 <> Неопределено Тогда
    ПараметрДанных2.Значение = ЗначениеПараметра2;
    ПараметрДанных2.Использование = Истина;
КонецЕсли;

Фраза КомпоновщикНастроек.Настройки.ПараметрыДанных.УстановитьЗначениеПараметра("П1", Значение), которая обсуждалась в сообщении 6, является более компактным способом установки значения и использования для параметра, если вы работаете непосредственно с КомпоновщикомНастроек. Однако в нашем случае, когда мы уже работаем с объектом НастройкиКомпоновщикаДанных, прямой доступ через Найти() и установка свойств Значение и Использование является эквивалентным и понятным подходом.

Шаг 3: Продолжаем формирование отчета

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


// ... (код установки параметров)

КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКД, НастройкиКД,,,Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений"));
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки);
ТаблицаЗначений = Новый ТаблицаЗначений;
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
ПроцессорВывода.УстановитьОбъект(ТаблицаЗначений);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);

Теперь параметры будут корректно учтены при формировании макета и, соответственно, при выводе результатов.

Решение 2: Перенос логики установки параметров в экспортную функцию модуля объекта отчета

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

Шаг 1: Создаем экспортную функцию в модуле объекта внешнего отчета

В модуле объекта вашего внешнего отчета (например, МодульОбъекта.epf), мы добавим новую экспортную процедуру или функцию. Эта функция будет отвечать за настройку специфических для данного отчета параметров.


// Модуль объекта внешнего отчета
// Пример: ДополнительныеОтчетыИОбработки.<ИмяВашегоОтчета>.МодульОбъекта

// Экспортная функция для установки параметров СКД
Процедура УстановитьПараметрыСКД(НастройкиКомпоновщикаДанных, Параметры) Экспорт

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

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

    // Пример для другого параметра
    ЗначениеПараметраОтбора = Справочники.Контрагенты.НайтиПоНаименованию("ООО Ромашка");
    Если Параметры <> Неопределено И Параметры.Свойство("НашПараметрОтбора") Тогда
        ЗначениеПараметраОтбора = Параметры.НашПараметрОтбора;
    КонецЕсли;

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

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

КонецПроцедуры

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

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

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

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


// Общий модуль
// ...

СсылкаНаОтчет = Справочники.ДополнительныеОтчетыИОбработки.НайтиПоНаименованию("МойВнешнийОтчет"); // Пример: замените на реальную ссылку

ПолучениеОбъекта = ДополнительныеОтчетыИОбработкиВызовСервера.ПолучитьОбъектВнешнейОбработки(СсылкаНаОтчет);
СхемаКД = ПолучениеОбъекта.ПолучитьМакет("ОсновнаяСхемаКомпоновкиДанных");
НастройкиКД = СхемаКД.НастройкиПоУмолчанию;

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

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

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

Этот подход обеспечивает высокую гибкость и позволяет сохранять логику настройки параметров внутри каждого отчета, не усложняя универсальный общий модуль.

Альтернативное решение: Перенос всей логики формирования отчета в модуль объекта

Еще один вариант, предложенный в сообщении 10, заключается в переносе всей 11 строк кода, отвечающих за формирование отчета, непосредственно в модуль объекта внешнего отчета. Это, по сути, инкапсулирует весь процесс формирования и возврата данных в рамках самого отчета.

Шаг 1: Создаем экспортную функцию в модуле объекта отчета, которая формирует и возвращает ТаблицуЗначений

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


// Модуль объекта внешнего отчета

Функция СформироватьОтчетВТаблицуЗначений(ПараметрыДляОтчета = Неопределено) Экспорт

    // 1. Получаем схему компоновки данных
    СхемаКД = ПолучитьМакет("ОсновнаяСхемаКомпоновкиДанных");
    НастройкиКД = СхемаКД.НастройкиПоУмолчанию;

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

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

    // 4. Инициализируем процессор компоновки
    ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
    ПроцессорКомпоновки.Инициализировать(МакетКомпоновки);

    // 5. Выводим результат в ТаблицуЗначений
    ТаблицаЗначенийРезультат = Новый ТаблицаЗначений;
    ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
    ПроцессорВывода.УстановитьОбъект(ТаблицаЗначенийРезультат);
    ПроцессорВывода.Вывести(ПроцессорКомпоновки);

    Возврат ТаблицаЗначенийРезультат;

КонецФункции

// Также убедитесь, что функция СведенияОВнешнейОбработке() возвращает правильные данные.
Функция СведенияОВнешнейОбработке() Экспорт
    // ... ваш код для СведенияОВнешнейОбработке ...
    Возврат Новый Структура(); // Пример возврата пустой структуры
КонецФункции

Шаг 2: Вызов новой функции из общего модуля

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


// Общий модуль
// ...

СсылкаНаОтчет = Справочники.ДополнительныеОтчетыИОбработки.НайтиПоНаименованию("МойВнешнийОтчет"); // Пример: замените на реальную ссылку

ПолучениеОбъекта = ДополнительныеОтчетыИОбработкиВызовСервера.ПолучитьОбъектВнешнейОбработки(СсылкаНаОтчет);

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

// ...

Этот подход упрощает код в универсальном модуле, но делает модуль объекта отчета более "тяжелым" и менее гибким в плане переиспользования отдельных частей логики формирования.

Ключевые выводы и рекомендации

  1. Понимание жизненного цикла: Помните, что ПриКомпоновкеРезультата не вызывается при прямом программном формировании СКД.
  2. Прямая установка параметров: Всегда устанавливайте параметры программно через объект НастройкиКомпоновщикаДанных (НастройкиКД) до вызова КомпоновщикМакета.Выполнить().
  3. Не забывайте про Использование = Истина: Это критически важно, иначе установленное значение параметра не будет учтено.
  4. Инкапсуляция логики: Для универсальных общих модулей и специфичных для отчетов настроек используйте экспортные функции в модуле объекта отчета. Это позволит поддерживать чистоту кода и гибкость системы.
  5. Тестирование: После внесения изменений всегда тщательно тестируйте формирование отчета, чтобы убедиться, что все параметры применяются корректно и результат соответствует ожиданиям.

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

← На главную