При разработке сложных отчетов на системе компоновки данных (СКД) программисты часто сталкиваются с ситуацией, когда данные в основном отчете отображаются корректно, но при попытке выполнить расшифровку (например, по регистратору или детальным записям) система выдает пустой результат или игнорирует наложенные фильтры. Особенно остро эта проблема проявляется, когда поле имеет составной тип данных или вычисляется с помощью конструкции ВЫБОР КОГДА. Рассмотрим детально, почему это происходит и как обеспечить идентичность данных в основном отчете и его расшифровке.
Разберем типичную ситуацию. В запросе СКД создается расчетное поле, которое нормализует данные из разных справочников. Например, для разных видов номенклатуры используются разные справочники размеров. Чтобы свести их в одну колонку, мы пишем сложную конструкцию в тексте запроса. Проблема заключается в том, что если мы назовем это поле так же, как называется измерение в регистре (например, Размеры), СКД может автоматически назначить ему роль "Измерение".
Когда пользователь делает расшифровку, СКД пытается наложить отбор на это поле. Если имя поля совпадает с именем измерения регистра накопления, система компоновки часто пытается передать этот отбор напрямую в параметры виртуальной таблицы (например, в параметры таблицы ОстаткиИОбороты). Поскольку в базе данных нет записи с тем "синтетическим" значением, которое мы вычислили в ВЫБОР КОГДА, запрос к виртуальной таблице возвращает пустой результат.
Самый простой и эффективный способ решить проблему "пустой расшифровки" — это разграничить имена физических полей базы данных и псевдонимы расчетных полей. Если ваше расчетное поле называется так же, как измерение регистра, переименуйте его. Например, вместо Размеры используйте РазмерТовара. При этом в колонке "Заголовок" на вкладке "Наборы данных" вы можете оставить привычное пользователю название "Размер".
Рассмотрим пример правильного оформления расчетного поля в тексте запроса:
ВЫБОР
КОГДА ДвижениеТМЦОстаткиИОбороты.Размеры.Ссылка ЕСТЬ NULL
ТОГДА ВЫБОР
КОГДА ДвижениеТМЦОстаткиИОбороты.Номенклатура = ЗНАЧЕНИЕ(Справочник.Номенклатура.Балка)
ТОГДА ЗНАЧЕНИЕ(Справочник.РазмерыБалки.ПустаяСсылка)
КОГДА ДвижениеТМЦОстаткиИОбороты.Номенклатура = ЗНАЧЕНИЕ(Справочник.Номенклатура.Плита)
ТОГДА ЗНАЧЕНИЕ(Справочник.РазмерыПлиты.ПустаяСсылка)
ИНАЧЕ НЕОПРЕДЕЛЕНО
КОНЕЦ
ИНАЧЕ ВЫБОР
КОГДА ДвижениеТМЦОстаткиИОбороты.Номенклатура = ЗНАЧЕНИЕ(Справочник.Номенклатура.БалкаЦветника)
ТОГДА ДвижениеТМЦОстаткиИОбороты.Размеры.РазмерБалкиДляДвиженияПоРегиструТМЦ
ИНАЧЕ ДвижениеТМЦОстаткиИОбороты.Размеры
КОНЕЦ
КОНЕЦ КАК РазмерРасчетный
После переименования поля в РазмерРасчетный, СКД перестанет путать его с измерением Размеры и будет накладывать фильтр в секции ГДЕ итогового запроса расшифровки, что позволит корректно обработать логику ВЫБОР КОГДА.
Проанализируем другой подход. Вместо того чтобы описывать логику трансформации данных в тексте запроса, мы можем вынести ее на вкладку "Вычисляемые поля". Это дает СКД больше контроля над процессом компоновки.
РазмерДляОтчета).ВЫБОР КОГДА.Преимущество этого метода: Вычисляемое поле существует "над" набором данных. Когда пользователь вызывает расшифровку, СКД видит формулу этого поля и может корректно транслировать ее в условия нового запроса, сохраняя связь между агрегированными данными и детальными записями.
Если расшифровка все равно работает не так, как ожидается, нам необходимо увидеть, какой именно запрос генерирует СКД в момент клика по ячейке. Для этого проанализируем ситуацию через модуль объекта отчета. Мы можем перехватить процесс компоновки и вывести итоговый текст запроса в консоль или текстовый файл.
Рассмотрим пример кода в модуле объекта (событие ПриКомпоновкеРезультата):
Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
Настройки = КомпоновщикНастроек.ПолучитьНастройки();
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
// Формируем макет компоновки, который содержит итоговый запрос
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки, ДанныеРасшифровки);
// Здесь мы можем посмотреть текст запроса в отладчике
ТекстЗапроса = МакетКомпоновки.НаборыДанных[0].Запрос;
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, , ДанныеРасшифровки);
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
КонецПроцедуры
Установив точку останова на переменной ТекстЗапроса, мы сможем сравнить запрос основного отчета и запрос, который формируется при расшифровке. Выясним причину: если в запросе расшифровки условие по нашему полю попадает в параметры виртуальной таблицы — значит, у поля некорректно настроена Роль в наборе данных.
При работе с составными типами важно помнить, что ПустаяСсылка одного справочника не равна ПустойСсылке другого. В расшифровке это часто приводит к тому, что отбор по "пустому размеру" не находит данные. Выясним, как это унифицировать.
Для корректной группировки и последующей расшифровки "пустых" значений разных типов, рекомендуется использовать приведение к единому значению в выражении СКД:
ВЫБОР КОГДА Размер ЕСТЬ NULL ИЛИ Размер = ЗНАЧЕНИЕ(Справочник.РазмерыБалки.ПустаяСсылка) ТОГДА "Не задан" ИНАЧЕ Размер КОНЕЦ
Если мы приводим пустые значения к строке или фиксированному предопределенному значению одного типа, механизм расшифровки будет работать значительно стабильнее, так как СКД будет проще сопоставить "пустоту" в итоговой таблице с данными в исходных записях регистра.
Подводя итог, отметим, что ключ к успешной расшифровке в СКД — это уникальность имен расчетных полей и контроль ролей полей в наборе данных. Следите за тем, чтобы расчетные выражения не подменяли собой физические измерения регистров, и тогда расшифровка будет всегда возвращать ожидаемые данные.