При разработке функционала в конфигурациях на базе обычных форм (например, Управление производственным предприятием 1.2/1.3) разработчики часто сталкиваются с ограничением стандартного механизма настроек пользователей. Стандартный ПланВидовХарактеристик.НастройкиПользователей ориентирован на хранение одиночных (скалярных) значений: одной ссылки, одной строки, одного числа или даты. Попытка выбрать несколько элементов в форме выбора справочника приводит к тому, что кнопка «Выбрать» становится неактивной, так как целевой реквизит не предназначен для коллекции данных.
Рассмотрим подробнее, почему так происходит, и разберем несколько эффективных способов решения этой задачи, от простых архитектурных изменений до программных методов сериализации.
Выясним причину поведения системы. Когда мы открываем форму выбора справочника из поля, связанного с настройкой пользователя, платформа анализирует тип значения этой настройки. Если в Плане видов характеристик (ПВХ) для данной настройки указан тип СправочникСсылка, платформа ожидает получить ровно один элемент. Включение режима МножественныйВыбор = Истина в форме справочника позволяет выделить несколько строк, но штатный механизм возврата значения не понимает, как «упаковать» этот массив в одиночную ячейку. В итоге кнопка выбора блокируется, предотвращая ошибку несоответствия типов.
Для реализации хранения списка значений нам придется выйти за рамки стандартного поведения. Рассмотрим основные пути решения.
Проанализируем этот вариант как наиболее правильный с точки зрения архитектуры 1С. Вместо того чтобы пытаться «впихнуть» массив данных в одну строку настроек, мы создадим структуру, предназначенную для хранения связей «много-ко-многим».
Разберем по шагам создание такого механизма:
РегистрСведений (например, ДополнительныеНастройкиПользователей).Пользователь (тип СправочникСсылка.Пользователи) и Настройка (тип ПланВидовХарактеристикСсылка.НастройкиПользователей).Значение (тип СправочникСсылка нужного вам справочника).Такая структура позволяет хранить для одного пользователя и одной настройки произвольное количество записей. Проанализируем пример кода для получения этих данных в запросе:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ДопНастройки.Значение КАК Значение
|ИЗ
| РегистрСведений.ДополнительныеНастройкиПользователей КАК ДопНастройки
|ГДЕ
| ДопНастройки.Пользователь = &ТекущийПользователь
| И ДопНастройки.Настройка = &НужнаяНастройка";
Запрос.УстановитьПараметр("ТекущийПользователь", ПараметрыСеанса.ТекущийПользователь);
Запрос.УстановитьПараметр("НужнаяНастройка", ПланыВидовХарактеристик.НастройкиПользователей.МояМножественнаяНастройка);
СписокВыбранныхЭлементов = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Значение");
Преимущества: сохраняется ссылочная целостность, данные легко использовать в других запросах и отчетах, нет проблем с производительностью.
Если вы все же хотите использовать стандартную форму «Настройки пользователя», можно изменить состав доступных типов в самом ПВХ. Посмотрим, как это реализовать:
В конфигураторе найдите ПланВидовХарактеристик.НастройкиПользователей. В свойстве «Тип значения характеристик» нужно добавить тип СписокЗначений. После этого для вашей настройки можно будет выбрать этот тип.
Однако здесь есть нюанс. Стандартная форма редактирования настроек в УПП может не иметь встроенного визуального редактора для типа СписокЗначений. Вам может потребоваться доработка формы, чтобы при нажатии на кнопку выбора открывалось окно редактирования списка значений, а не просто форма справочника.
Рассмотрим пример программного вызова подбора элементов в СписокЗначений:
// В обработчике нажатия кнопки выбора в форме настроек
ФормаВыбора = Справочники.Номенклатура.ПолучитьФормуВыбора(, ЭтаФорма);
ФормаВыбора.МножественныйВыбор = Истина;
ВыбранныеДанные = ФормаВыбора.ОткрытьМодально();
Если ТипЗнч(ВыбранныеДанные) = Тип("Массив") Тогда
ТекущийСписокЗначений = Новый СписокЗначений;
ТекущийСписокЗначений.ЗагрузитьЗначения(ВыбранныеДанные);
// Записываем список в настройку
КонецЕсли;
В случаях, когда менять структуру метаданных (добавлять регистры) нельзя, а тип настройки жестко ограничен строкой, можно использовать метод «упаковки» данных. Проанализируем ситуацию: использование кодов через запятую (например, "001,005,010") крайне не рекомендуется, так как это усложняет поиск и может привести к ошибкам при изменении кодов справочника.
Лучше использовать стандартную сериализацию 1С. Разберем пример, как преобразовать СписокЗначений в строку XML и обратно:
// Сериализация (сохранение в строку)
СписокДляСохранения = Новый СписокЗначений;
СписокДляСохранения.Добавить(Ссылка1);
СписокДляСохранения.Добавить(Ссылка2);
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.УстановитьСтроку();
СериализаторXDTO.ЗаписатьXML(ЗаписьXML, СписокДляСохранения);
СтрокаНастройки = ЗаписьXML.Закрыть(); // Эту строку пишем в значение настройки
// Десериализация (чтение из строки)
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.УстановитьСтроку(СтрокаНастройки);
СписокЗагруженный = СериализаторXDTO.ПрочитатьXML(ЧтениеXML);
Важный момент: При таком подходе система не отслеживает ссылки. Если элемент справочника будет удален физически, при десериализации вы получите объект <Объект не найден...>. Это нужно учитывать при написании алгоритмов обработки этих настроек.
Если ваша конфигурация работает на платформе 8.3, мы можем воспользоваться механизмом ХранилищеОбщихНастроек. Это системная таблица, которая позволяет хранить любые сериализуемые данные (включая массивы, структуры и списки значений) без привязки к ПВХ.
Посмотрим на пример работы с этим механизмом:
// Сохранение списка значений под ключом
КлючОбъекта = "ОтборыДляСпецификаций";
КлючНастройки = "СписокИсключенийНоменклатуры";
ХранилищеОбщихНастроек.Сохранить(КлючОбъекта, КлючНастройки, ВашСписокЗначений, , ПользователиИнформационнойБазы.ТекущийПользователь().Имя);
// Загрузка
ВашСписокЗначений = ХранилищеОбщихНастроек.Загрузить(КлючОбъекта, КлючНастройки, , ПользователиИнформационнойБазы.ТекущийПользователь().Имя);
Если ВашСписокЗначений = Неопределено Тогда
ВашСписокЗначений = Новый СписокЗначений;
КонецЕсли;
Подведем итог нашему анализу:
Помните, что при реализации множественного выбора важно всегда проверять права пользователя на чтение выбранных элементов, так как в списках могут оказаться объекты, к которым у пользователя пропал доступ со временем.