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

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

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

Если применить стандартный отбор СКД «в лоб», платформа наложит условие ГДЕ непосредственно на строки итоговой выборки в SQL-запросе. В результате из отчета исчезнут все «лишние» строки документов, не содержащие искомую номенклатуру, что полностью исказит картину документа. Выясним причины такого поведения и разберем по шагам несколько эффективных способов решения этой проблемы.

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

---

Метод 1. Использование временных таблиц и фигурных скобок в запросе СКД (Без автозаполнения)

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

Рассмотрим подробнее схему построения такого запроса:

  1. Откройте схему компоновки данных и перейдите на вкладку Наборы данных.
  2. Снимите флажок Автозаполнение у нашего набора данных (запроса).
  3. Напишите следующий текст запроса, где первый пакет собирает ссылки на документы по фильтру, а второй выводит полные данные:

ВЫБРАТЬ РАЗЛИЧНЫЕ
    РеализацияТоваровУслугТовары.Ссылка КАК Ссылка
ПОМЕСТИТЬ ВременнаяТаблицаДокументов
ИЗ
    Документ.RealizacijaTovarovUslug.Tovary КАК РеализацияТоваровУслугТовары
{ГДЕ
    РеализацияТоваровУслугТовары.Номенклатура.* КАК СпецНоменклатура}
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    РеализацияТоваровУслугТовары.Ссылка КАК Ссылка,
    РеализацияТоваровУслугТовары.Номенклатура КАК Номенклатура,
    РеализацияТоваровУслугТовары.Количество КАК Количество,
    РеализацияТоваровУслугТовары.Сумма КАК Сумма
ИЗ
    Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
ГДЕ
    РеализацияТоваровУслугТовары.Ссылка В
        (ВЫБРАТЬ
            Т.Ссылка
        ИЗ
            ВременнаяТаблицаДокументов КАК Т)

Обратите внимание на конструкцию в фигурных скобках {ГДЕ РеализацияТоваровУслугТовары.Номенклатура.* КАК СпецНоменклатура}. Это описание необязательного отбора. СКД применит этот фильтр только на этапе формирования первой временной таблицы и только если пользователь заполнит поле СпецНоменклатура на вкладке отборов в пользовательском режиме. При этом основная выборка во втором запросе пакета останется нетронутой автоматическими фильтрами компоновщика.

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

---

Метод 2. Связь двух наборов данных в СКД (Связи наборов)

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

Разберем данный подход по шагам:

  1. Создаем НаборДанных1 (Фильтрующий): В запросе выбираем только уникальные ссылки на документы, на которые СКД сможет накладывать стандартные отборы по табличной части.
    
    ВЫБРАТЬ РАЗЛИЧНЫЕ
        РеализацияТоваровУслугТовары.Ссылка КАК Ссылка
    ИЗ
        Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
    {ГДЕ
        РеализацияТоваровУслугТовары.Номенклатура.* КАК Номенклатура}
    
  2. Создаем НаборДанных2 (Основной): Этот запрос возвращает абсолютно все строки документов без каких-либо условий.
    
    ВЫБРАТЬ
        РеализацияТоваровУслугТовары.Ссылка КАК Ссылка,
        РеализацияТоваровУслугТовары.Номенклатура КАК Номенклатура,
        РеализацияТоваровУслугТовары.Количество КАК Количество,
        РеализацияТоваровУслугТовары.Сумма КАК Сумма
    ИЗ
        Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
    
  3. Настраиваем связи: Переходим на вкладку Связи наборов данных и добавляем новую строку:
    • Источник связи: НаборДанных1
    • Приемник связи: НаборДанных2
    • Условие связи: Ссылка = Ссылка

Когда пользователь применит отбор по номенклатуре, СКД сначала выполнит первый запрос, получив ограниченный массив ссылок на документы. Затем эти ссылки будут переданы в качестве параметров связи во второй запрос. В итоге мы получим документы, где есть выбранная номенклатура, но выведенные в отчет полностью.

---

Метод 3. Использование вычисляемых полей и функций агрегации

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

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

  1. Перейдите на вкладку Вычисляемые поля и добавьте новое поле с именем ВсеНоменклатурыДокумента.
  2. На вкладке Ресурсы укажите для этого поля следующее выражение расчета итогов: ВычислитьВыражениеСГруппировкойМассив("Номенклатура", "Ссылка")
  3. В настройках отчета (на вкладке Отбор) пользователю нужно будет накладывать условие не на стандартное поле Номенклатура, а на наше новое поле ВсеНоменклатурыДокумента с видом сравнения ВСписке или Содержит.

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

---

Метод 4. Программная трансформация настроек в модуле отчета

Бывают ситуации, когда отчет должен работать универсально и пользователю не хочется объяснять тонкости работы с техническими полями. Мы можем перехватить установленный пользователем стандартный отбор по детальному полю в модуле отчета и программно перестроить структуру запроса или компоновщика.

Рассмотрим пример кода в процедуре ПриКомпоновкеРезультата модуля отчета:


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

Этот способ гарантирует, что пользователь может работать со стандартным и интуитивно понятным полем отбора, но под капотом система автоматически трансформирует его в условие по списку документов, сохраняя целостность выгружаемых табличных частей.

← На главную