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