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