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