Как в СКД программно обработать данные, сохранив пользовательские отборы?

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

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

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

Основной подход: выгрузка в таблицу, обработка и загрузка через "Набор данных - объект"

Этот метод является прямым ответом на поставленную задачу. Мы полностью берем управление процессом вывода отчета на себя: сначала получаем данные от СКД в промежуточную таблицу, обрабатываем ее, а затем "скармливаем" эту таблицу СКД для красивого вывода в макет.

Разберем по шагам:

  1. Подготовка схемы компоновки.

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

  2. Получение данных с учетом настроек СКД.

    Чтобы получить данные так, как их "видит" СКД с учетом всех пользовательских фильтров, мы выполним компоновку не в табличный документ, а в коллекцию значений (ТаблицаЗначений). Этот код размещается в модуле отчета в процедуре-обработчике события ПриКомпоновкеРезультата.

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

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

  3. Программная обработка таблицы.

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

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

    Финальный шаг. Мы должны отменить стандартную обработку и запустить процесс компоновки заново, но уже с нашим "Набором данных - объектом", в который мы передадим обработанную таблицу.

    Этот код выполняется в той же процедуре ПриКомпоновкеРезультата.

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

Альтернативный подход: два связанных набора данных (Запрос и Объект)

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

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

Этот метод предпочтительнее, когда расчеты касаются лишь нескольких колонок, а основной объем данных остается неизменным. Он позволяет сохранить оптимизацию выполнения основного запроса СКД.

Продвинутый подход: использование Менеджера временных таблиц (МВТ)

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

Схема работы:

  1. Получение и обработка данных. Первые шаги аналогичны основному подходу: получаем данные в ТаблицаЗначений с учетом настроек пользователя и обрабатываем ее.
  2. Помещение ТЗ в Менеджер временных таблиц. Создаем МВТ и с помощью простого запроса помещаем нашу таблицу в него.
  3. 
    // ... у нас есть обработанная таблица РезультатТЗ ...
    
    МВТ = Новый МенеджерВременныхТаблиц;
    ЗапросДляМВТ = Новый Запрос;
    ЗапросДляМВТ.МенеджерВременныхТаблиц = МВТ;
    ЗапросДляМВТ.Текст = 
        "ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_ОбработанныеДанные ИЗ &ТЗ";
    ЗапросДляМВТ.УстановитьПараметр("ТЗ", РезультатТЗ);
    ЗапросДляМВТ.Выполнить();
    
  4. Модификация основного запроса СКД. В тексте основного запроса в схеме компоновки данных теперь можно обращаться к временной таблице ВТ_ОбработанныеДанные как к обычной таблице базы данных, например, соединяя ее с другими таблицами.
  5. Передача МВТ в компоновщик. Перед формированием отчета необходимо передать созданный и заполненный МВТ в компоновщик. Это можно сделать, например, через параметры данных в настройках.
  6. 
    // В процедуре ПриКомпоновкеРезультата, перед стандартным выполнением
    ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, , , , , МВТ);
    // ... далее стандартный вывод
    

В заключение, выбор метода зависит от сложности задачи. Для простого добавления одной-двух колонок идеально подходит подход с двумя связанными наборами. Если требуется полный контроль над выводом или структура данных сильно меняется, используйте первый метод с полной заменой вывода. А для сложных сценариев, где обработанные данные должны участвовать в дальнейших соединениях и агрегации на уровне запроса, Менеджер временных таблиц будет самым мощным инструментом.

← На главную