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