При разработке отчетов в 1С на Системе Компоновки Данных (СКД) часто возникает необходимость программно устанавливать определенные отборы или параметры перед тем, как отчет будет показан пользователю. Это позволяет предоставлять преднастроенные варианты отчетов, сокращая время на их подготовку и уменьшая количество возможных ошибок. Однако, мы сталкиваемся с определенной сложностью: как изменить именно пользовательские настройки отбора таким образом, чтобы они не только применялись при формировании отчета, но и корректно отображались на его форме, как будто пользователь установил их сам? Основная проблема заключается в том, что элементы пользовательских настроек, которые мы видим в коллекции КомпоновщикНастроек.ПользовательскиеНастройки.Элементы, идентифицируются по ИдентификаторПользовательскойНастройки, который может быть уникальным для каждого пользователя и не предоставляет прямого доступа к ЛевоеЗначение (т.е., к имени поля отбора).
В этой статье мы подробно рассмотрим, как преодолеть это ограничение и программно управлять пользовательскими настройками отбора СКД.
Для эффективной работы с Системой Компоновки Данных (СКД) программным способом, нам необходимо четко представлять, как организованы и взаимодействуют различные виды настроек. СКД оперирует тремя ключевыми видами настроек:
КомпоновщикНастроек.Настройки): Это фундамент отчета, заданный разработчиком в конфигураторе или представляющий собой базовый вариант отчета. Здесь определяются все детали структуры: отборы, группировки, поля, параметры и т.д. Эти настройки являются статичными для конкретного варианта отчета.КомпоновщикНастроек.ПользовательскиеНастройки): Эти настройки являются динамическими и персонализированными. Они позволяют каждому пользователю интерактивно изменять или дополнять основные настройки под свои нужды. Важно понимать, что они хранятся отдельно для каждого пользователя и по сути "перекрывают" или "дополняют" основные настройки, предоставляя возможность сохранять индивидуальные предпочтения.Объект КомпоновщикНастроек служит центральным элементом, который агрегирует и управляет всеми этими видами настроек, формируя на их основе итоговый отчет. Наша задача — научиться манипулировать именно ПользовательскиеНастройки, чтобы изменения были видны и сохранялись как пользовательские. Для удобного связывания интерфейса с настройками компоновщика подойдёт решение для стыковки элементов управляемой формы и пользовательских настроек СКД.
Основная сложность, с которой мы сталкиваемся при попытке изменить пользовательские настройки отбора программно, заключается в уникальной структуре хранения этих настроек. У каждого элемента отбора в пользовательских настройках есть свой ИдентификаторПользовательскойНастройки. Этот идентификатор, как правило, уникален для каждого пользователя и для каждого элемента, даже если речь идет об одном и том же поле отбора (например, "Организация").
При этом, напрямую получить ЛевоеЗначение (т.е., имя поля отбора, например, "Ссылка.Организация") из элемента коллекции КомпоновщикНастроек.ПользовательскиеНастройки.Элементы невозможно. Эта коллекция в основном оперирует уже готовыми элементами пользовательского интерфейса, связанными с соответствующими идентификаторами, но не с самими полями данных отчета.
Нам нужен способ связать имя поля, которое мы хотим отфильтровать (например, "Номенклатура"), с конкретным элементом в пользовательских настройках, чтобы установить его значение. Проще говоря, мы знаем, что хотим отфильтровать по "Номенклатуре", но не знаем, какой ИдентификаторПользовательскойНастройки соответствует этой "Номенклатуре" у текущего пользователя.
Для решения этой задачи мы будем использовать подход, основанный на сопоставлении двух коллекций настроек: КомпоновщикНастроек.Настройки.Отбор.Элементы (основные настройки отбора) и КомпоновщикНастроек.ПользовательскиеНастройки.Элементы (пользовательские настройки).
КомпоновщикНастроек.Настройки.Отбор.Элементы. Эта коллекция содержит элементы отбора, которые были заданы разработчиком или являются частью базового варианта отчета. У этих элементов мы можем напрямую получить ЛевоеЗначение – то есть имя поля, по которому производится отбор.ЛевоеЗначение (имени поля, например, "Номенклатура"), мы можем получить его ИдентификаторПользовательскойНастройки. Это ключевой момент, поскольку именно этот идентификатор связывает основной элемент отбора с его представлением в пользовательских настройках.ИдентификаторПользовательскойНастройки, мы должны найти соответствующий элемент в коллекции КомпоновщикНастроек.ПользовательскиеНастройки.Элементы.ПравоеЗначение (устанавливаемое значение отбора), ВидСравнения (например, "Равно", "В списке"), Использование (включен ли отбор) и РежимОтображения (как он будет виден пользователю).Таким образом, мы используем основные настройки как "словарь", чтобы найти нужный идентификатор, а затем применяем этот идентификатор для работы с пользовательскими настройками.
Давайте рассмотрим конкретный пример процедуры, которая позволяет найти и изменить существующий элемент пользовательского отбора. Эта процедура будет принимать в качестве аргументов форму отчета (содержащую объект компоновщика настроек), имя поля отбора, значение для установки, вид сравнения и флаг использования.
Мы будем работать с клиентской процедурой, так как она взаимодействует непосредственно с формой отчета и ее компоновщиком настроек.
&НаКлиенте
Процедура ИзменитьЭлементПользовательскогоОтбора(ФормаОтчета, ИмяПоля, Значение, ВидСравнения, Использование = Истина)
// Создаем объект ПолеКомпоновкиДанных из имени поля для удобства сравнения
// Это позволяет нам сравнивать поля по их логическому представлению, а не по строке
ПолеКомпоновки = Новый ПолеКомпоновкиДанных(ИмяПоля);
// Перебираем элементы пользовательских настроек.
// Это коллекция содержит элементы, которые могут быть изменены пользователем.
Для Каждого ТекЭлементПользовательскихНастроек Из ФормаОтчета.Отчет.КомпоновщикНастроек.ПользовательскиеНастройки.Элементы Цикл
// Для каждого элемента пользовательских настроек ищем соответствующий в основных настройках отбора.
// Основные настройки содержат метаданные и ЛевоеЗначение.
Для Каждого ТекЭлементОсновныхНастроекОтбора Из ФормаОтчета.Отчет.КомпоновщикНастроек.Настройки.Отбор.Элементы Цикл
// Сопоставляем элементы по ИдентификаторПользовательскойНастройки и по имени поля (ЛевоеЗначение).
// Это ключевой шаг для нахождения нужного элемента.
Если ТекЭлементПользовательскихНастроек.ИдентификаторПользовательскойНастройки = ТекЭлементОсновныхНастроекОтбора.ИдентификаторПользовательскойНастройки
И ТекЭлементОсновныхНастроекОтбора.ЛевоеЗначение = ПолеКомпоновки Тогда
// Если соответствующий элемент найден, устанавливаем необходимые свойства.
// Эти изменения будут отражены в быстрых настройках отчета и при его формировании.
ТекЭлементПользовательскихНастроек.ПравоеЗначение = Значение;
ТекЭлементПользовательскихНастроек.Использование = Использование;
ТекЭлементПользовательскихНастроек.ВидСравнения = ВидСравнения;
// Устанавливаем режим отображения для быстрого доступа, чтобы отбор был виден на форме.
ТекЭлементПользовательскихНастроек.РежимОтображения = РежимОтображенияЭлементаНастройкиКомпоновкиДанных.БыстрыйДоступ;
Возврат; // Нашли и изменили, выходим из процедуры, так как задача выполнена
КонецЕсли;
КонецЦикла;
КонецЦикла;
// Если элемент не был найден среди существующих пользовательских настроек (например, он был скрыт,
// удален пользователем или еще не добавлен в быстрый доступ),
// то мы можем его добавить или создать.
// Вызываем дополнительную процедуру для установки или добавления нового элемента.
УстановитьЭлементПользовательскогоОтбора(ФормаОтчета, ИмяПоля, Значение, ВидСравнения, Использование);
КонецПроцедуры
Эта процедура сначала пытается найти элемент отбора, который уже присутствует в пользовательских настройках и сопоставлен с элементом в основных настройках по имени поля. Если такой элемент найден, его свойства обновляются. Если же элемент не найден (возможно, он был удален пользователем или еще не добавлен в быстрый доступ), то вызывается следующая процедура для его создания или модификации.
Теперь рассмотрим процедуру, которая может как обновить существующий пользовательский отбор, так и добавить новый, если он еще не присутствует. Это особенно полезно, когда мы хотим обеспечить наличие определенного отбора, даже если пользователь его никогда не использовал или удалил из быстрых настроек.
&НаКлиенте
Процедура УстановитьЭлементПользовательскогоОтбора(ФормаОтчета, ИмяЭлемента, ЗначениеЭлемента, ВидСравнения, Использование = Истина)
// Создаем ПолеКомпоновкиДанных для поиска по имени элемента
ПолеКомпоновки = Новый ПолеКомпоновкиДанных(ИмяЭлемента);
// Перебираем элементы пользовательских настроек.
// Здесь мы ищем группу "Отбор", внутри которой находятся отдельные элементы отбора.
Для Каждого ЭлементНастройки Из ФормаОтчета.Отчет.КомпоновщикНастроек.ПользовательскиеНастройки.Элементы Цикл
// Проверяем, является ли текущий элемент группой отборов
Если ТипЗнч(ЭлементНастройки) = Тип("ОтборКомпоновкиДанных") Тогда
ЭлементОтбораПользовательский = Неопределено;
// Ищем существующий элемент отбора по ЛевоеЗначение внутри этой группы
Для Каждого ТекЭлементНастройки Из ЭлементНастройки.Элементы Цикл
Если ТекЭлементНастройки.ЛевоеЗначение = ПолеКомпоновки Тогда
ЭлементОтбораПользовательский = ТекЭлементНастройки;
Прервать; // Нашли существующий элемент, выходим из внутреннего цикла
КонецЕсли;
КонецЦикла;
// Если элемент не найден в пользовательских настройках, создаем новый
Если ЭлементОтбораПользовательский = Неопределено Тогда
ЭлементОтбораПользовательский = ЭлементНастройки.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
// Важно: для нового элемента создаем новый УникальныйИдентификатор.
// Это обеспечит его уникальность и корректное взаимодействие с СКД.
ЭлементОтбораПользовательский.ИдентификаторПользовательскойНастройки = Новый УникальныйИдентификатор();
ЭлементОтбораПользовательский.ЛевоеЗначение = ПолеКомпоновки;
КонецЕсли;
// Устанавливаем или обновляем свойства элемента отбора
ЭлементОтбораПользовательский.ВидСравнения = ВидСравнения;
ЭлементОтбораПользовательский.ПравоеЗначение = ЗначениеЭлемента;
ЭлементОтбораПользовательский.Использование = Использование;
ЭлементОтбораПользовательский.РежимОтображения = РежимОтображенияЭлементаНастройкиКомпоновкиДанных.БыстрыйДоступ;
Возврат; // Задача выполнена, выходим из процедуры
КонецЕсли;
КонецЦикла;
// Дополнительный шаг: если в пользовательских настройках еще нет группы "Отбор"
// (что маловероятно для типовых отчетов, но теоретически возможно),
// то можно добавить и ее. Этот сценарий более сложен и обычно не требуется,
// так как группа "Отбор" почти всегда присутствует по умолчанию.
// Для простоты, данный код предполагает ее наличие.
// В случае, если группа "Отбор" отсутствует, можно добавить:
// ГруппаОтборов = ФормаОтчета.Отчет.КомпоновщикНастроек.ПользовательскиеНастройки.Элементы.Добавить(Тип("ОтборКомпоновкиДанных"));
// И далее уже работать с ГруппаОтборов.Элементы.Добавить(...)
КонецПроцедуры
Эта процедура проходит по пользовательским настройкам, находит группу "Отбор" (тип ОтборКомпоновкиДанных), затем ищет внутри нее элемент по ЛевоеЗначение. Если элемент найден, его свойства обновляются. Если нет — создается новый элемент отбора с новым ИдентификаторПользовательскойНастройки и заданными свойствами.
Важно отметить: в приведенном коде для добавления нового отбора мы создаем новый УникальныйИдентификатор(). Это гарантирует, что даже если ранее у этого поля не было пользовательского отбора, теперь он будет создан и связан с уникальным ID. Установка РежимОтображения = РежимОтображенияЭлементаНастройкиКомпоновкиДанных.БыстрыйДоступ обеспечивает видимость этого отбора в быстрых настройках отчета.
В сообщениях форума справедливо отмечалось, что для коллекции элементов пользовательских настроек существует метод Найти, который позволяет искать элемент по ИдентификаторПользовательскойНастройки. Это может показаться более эффективным, чем полный перебор в цикле.
Однако, как было указано в обсуждении, ИдентификаторПользовательскойНастройки в отладчике может выглядеть как строка. Метод Найти для коллекции Элементы объекта НастройкиКомпоновкиДанных ожидает объект типа УникальныйИдентификатор, а не строковое представление. Если вы получаете ИдентификаторПользовательскойНастройки как строку, вам потребуется преобразовать ее обратно в УникальныйИдентификатор, используя конструктор Новый УникальныйИдентификатор(<СтрокаИдентификатора>), прежде чем передавать его в метод Найти.
Пример использования метода Найти (при условии, что у нас есть объект УникальныйИдентификатор из основных настроек):
// Предположим, ТекЭлементОсновныхНастроекОтбора - это найденный нами элемент из КомпоновщикНастроек.Настройки.Отбор.Элементы
// ИдентификаторОтбора уже является объектом УникальныйИдентификатор
ИдентификаторОтбора = ТекЭлементОсновныхНастроекОтбора.ИдентификаторПользовательскойНастройки;
ПользовательскийЭлемент = ФормаОтчета.Отчет.КомпоновщикНастроек.ПользовательскиеНастройки.Элементы.Найти(ИдентификаторОтбора);
Если ПользовательскийЭлемент <> Неопределено Тогда
// ... работаем с ПользовательскийЭлемент: устанавливаем ПравоеЗначение, Использование и т.д.
ПользовательскийЭлемент.ПравоеЗначение = Значение;
ПользовательскийЭлемент.Использование = Истина;
ПользовательскийЭлемент.ВидСравнения = ВидСравнения;
ПользовательскийЭлемент.РежимОтображения = РежимОтображенияЭлементаНастройкиКомпоновкиДанных.БыстрыйДоступ;
КонецЕсли;
В случае, если ИдентификаторПользовательскойНастройки был получен или хранится как строка, необходимо выполнить преобразование:
// Предположим, СтроковыйИдентификатор - это строка, например, "12345678-ABCD-EFGH-IJKL-0123456789AB"
СтроковыйИдентификатор = ТекЭлементОсновныхНастроекОтбора.ИдентификаторПользовательскойНастройки.Строка();
УникальныйИдентификаторОбъект = Новый УникальныйИдентификатор(СтроковыйИдентификатор);
ПользовательскийЭлемент = ФормаОтчета.Отчет.КомпоновщикНастроек.ПользовательскиеНастройки.Элементы.Найти(УникальныйИдентификаторОбъект);
Если ПользовательскийЭлемент <> Неопределено Тогда
// ... работаем с ПользовательскийЭлемент
ПользовательскийЭлемент.ПравоеЗначение = Значение;
ПользовательскийЭлемент.Использование = Истина;
ПользовательскийЭлемент.ВидСравнения = ВидСравнения;
ПользовательскийЭлемент.РежимОтображения = РежимОтображенияЭлементаНастройкиКомпоновкиДанных.БыстрыйДоступ;
КонецЕсли;
Таким образом, использование цикла, как в приведенных выше процедурах, является надежным и универсальным способом, особенно если мы одновременно сопоставляем элементы по идентификатору и по ЛевоеЗначение из основных настроек. Метод Найти может быть более производительным на больших коллекциях, но требует внимательной работы с типами данных ИдентификаторПользовательскойНастройки.
Помимо отборов, часто требуется программно устанавливать и параметры отчета. Принцип работы схож: необходимо установить значение как в основных настройках, так и в пользовательских, если для параметра определен ИдентификаторПользовательскойНастройки.
&НаКлиенте
Процедура УстановитьПараметрОтчета(ФормаОтчета, ИмяПараметра, ЗначениеПараметра)
// Предположим, у нас есть имя параметра "ДатаНачала" и значение
ПолеПараметра = Новый ПараметрКомпоновкиДанных(ИмяПараметра);
// Находим параметр в основных настройках.
// Используем НайтиПараметр, т.к. ПараметрыДанных - это коллекция ЗначенийПараметровНастроекКомпоновкиДанных.
ПараметрСКД = ФормаОтчета.Отчет.КомпоновщикНастроек.Настройки.ПараметрыДанных.НайтиПараметр(ПолеПараметра);
Если ПараметрСКД <> Неопределено Тогда
// Устанавливаем значение и использование в основных настройках.
// Это необходимо для корректного формирования отчета.
ПараметрСКД.Значение = ЗначениеПараметра;
ПараметрСКД.Использование = Истина;
// Если параметр имеет идентификатор пользовательской настройки,
// то необходимо обновить и соответствующий элемент в пользовательских настройках,
// чтобы значение отобразилось на форме отчета в быстрых настройках.
Если ЗначениеЗаполнено(ПараметрСКД.ИдентификаторПользовательскойНастройки) Тогда
// Ищем пользовательский параметр по его идентификатору
ПользовательскийПараметр = ФормаОтчета.Отчет.КомпоновщикНастроек.ПользовательскиеНастройки.Элементы.Найти(ПараметрСКД.ИдентификаторПользовательскойНастройки);
Если ТипЗнч(ПользовательскийПараметр) = Тип("ЗначениеПараметраНастроекКомпоновкиДанных") Тогда
ПользовательскийПараметр.Значение = ПараметрСКД.Значение;
ПользовательскийПараметр.Использование = Истина;
// Также можно установить режим отображения, если необходимо
ПользовательскийПараметр.РежимОтображения = РежимОтображенияЭлементаНастройкиКомпоновкиДанных.БыстрыйДоступ;
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Этот код гарантирует, что значение параметра будет не только учтено при формировании отчета, но и корректно отображено на форме отчета в разделе быстрых настроек или на вкладке "Параметры".
После того как мы программно модифицировали настройки компоновщика (как основные, так и пользовательские), мы можем открыть форму отчета, передав ей эти настройки. Это позволит отчету открыться уже с установленными значениями и при необходимости сразу же сформироваться.
&НаКлиенте
Процедура ОткрытьОтчетСНастроеннымиПараметрами(ФормаОтчета)
// Предполагаем, что ФормаОтчета.Отчет.КомпоновщикНастроек уже содержит нужные нам настройки,
// которые были изменены процедурами, рассмотренными выше.
ПараметрыОткрытия = Новый Структура();
// Параметр "СформироватьПриОткрытии" указывает форме отчета сформироваться сразу после открытия
ПараметрыОткрытия.Вставить("СформироватьПриОткрытии", Истина);
// Передаем основные настройки, которые содержат полную структуру отчета
ПараметрыОткрытия.Вставить("Вариант", ФормаОтчета.Отчет.КомпоновщикНастроек.Настройки);
// Передаем пользовательские настройки, которые будут применены поверх основных
// и отобразятся в интерфейсе пользователя
ПараметрыОткрытия.Вставить("ПользовательскиеНастройки", ФормаОтчета.Отчет.КомпоновщикНастроек.ПользовательскиеНастройки);
// Открываем форму отчета, передавая ей модифицированные настройки.
// Замените "<ИмяВашегоОтчета>" на фактическое имя вашего отчета в конфигурации.
ОткрытьФорму("Отчет.<ИмяВашегоОтчета>.Форма.ФормаОтчета", ПараметрыОткрытия, ЭтаФорма, Истина);
КонецПроцедуры
Замените <ИмяВашегоОтчета> на фактическое имя вашего отчета в конфигурации. Параметр СформироватьПриОткрытии позволит отчету автоматически сформироваться, если это необходимо.
При работе с программным изменением настроек СКД полезно учитывать следующие моменты:
Управление видимостью и доступностью: Если вы хотите сделать определенный отбор или параметр недоступным для изменения пользователем, вы можете установить РежимОтображенияЭлементаНастройкиКомпоновкиДанных.Недоступный как для элемента в Настройки.Отбор (или Настройки.ПараметрыДанных), так и для соответствующего элемента в ПользовательскиеНастройки.Элементы. Это предотвратит интерактивное изменение настройки.
// Пример установки недоступности отбора "Организация"
Для Каждого ЭлементОтбора Из КомпоновщикНастроек.Настройки.Отбор.Элементы Цикл
Если ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Организация") Тогда
ЭлементОтбора.РежимОтображения = РежимОтображенияЭлементаНастройкиКомпоновкиДанных.Недоступный;
Если ЗначениеЗаполнено(ЭлементОтбора.ИдентификаторПользовательскойНастройки) Тогда
ПользовательскийЭлемент = КомпоновщикНастроек.ПользовательскиеНастройки.Элементы.Найти(ЭлементОтбора.ИдентификаторПользовательскойНастройки);
Если ПользовательскийЭлемент <> Неопределено Тогда
ПользовательскийЭлемент.РежимОтображения = РежимОтображенияЭлементаНастройкиКомпоновкиДанных.Недоступный;
КонецЕсли;
КонецЕсли;
Прервать;
КонецЕсли;
КонецЦикла;
"Затирание" идентификатора: Иногда, чтобы полностью исключить элемент из пользовательских настроек или запретить его изменение через быстрые настройки, можно "затереть" ИдентификаторПользовательскойНастройки, установив его в Неопределено или пустой уникальный идентификатор. Однако это следует делать с осторожностью, поскольку может нарушить стандартное поведение СКД или привести к непредсказуемым результатам при работе с сохраненными пользовательскими вариантами.
Динамические списки: Аналогичные подходы к работе с пользовательскими настройками применяются и для динамических списков, когда необходимо программно установить или изменить отбор, который пользователь может затем скорректировать. Принцип поиска элемента по имени поля и последующей модификации через идентификатор остается тем же.
Проверка наличия: Перед тем как пытаться изменить элемент, всегда полезно проверить его наличие с помощью условий Если ... <> Неопределено Тогда ... КонецЕсли;, чтобы избежать ошибок "Поле объекта не обнаружено". Это особенно актуально, когда вы работаете с отчетами, которые могут иметь различные варианты или быть изменены пользователями.
Работа с группировками и другими настройками: Описанный принцип сопоставления полей с идентификаторами пользовательских настроек применим не только к отборам и параметрам, но и к другим видам настроек СКД, таким как группировки, поля и сортировки, если для них предусмотрена возможность пользовательской настройки.
Обработка ошибки: Если вы ожидаете, что определенный отбор или параметр должен существовать, но его не оказалось ни в основных, ни в пользовательских настройках, рассмотрите возможность вывода сообщения об ошибке или добавления элемента с использованием УстановитьЭлементПользовательскогоОтбора.
Программное изменение пользовательских настроек отбора СКД — это мощный инструмент для создания гибких и адаптируемых отчетов в 1С. Понимая взаимосвязь между основными и пользовательскими настройками, а также используя ИдентификаторПользовательскойНастройки для их сопоставления, мы можем эффективно управлять поведением отчетов, устанавливая нужные значения отборов и параметров перед их открытием.
Представленные процедуры и подходы позволят вам контролировать процесс формирования отчетов и предоставлять пользователям преднастроенные, но при этом изменяемые варианты отчетов, повышая удобство и функциональность системы. Всегда помните о необходимости тщательного тестирования ваших программных изменений, чтобы обеспечить корректную работу отчетов во всех возможных сценариях. Для упрощения работы с объектной моделью компоновки данных можно использовать модуль-обёртку для программного создания и настройки СКД.