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