Как в дополнительных реквизитах 1С реализовать хранение списка значений без снятия с поддержки?

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

При разработке и кастомизации типовых конфигураций на платформе «1С:Предприятие 8.3» (например, «1С:Бухгалтерия предприятия 3.0») разработчики часто сталкиваются с ограничениями стандартного механизма дополнительных реквизитов и сведений (БСП). Типичная задача — связать с пользователем не одно конкретное значение (например, одно подразделение), а целый список (коллекцию) значений для последующей фильтрации документов или отчетов.

Попытка выбрать тип значения СписокЗначений в плане видов характеристик (ПВХ) ДополнительныеРеквизитыИСведения заканчивается неудачей. Платформа физически не позволяет использовать динамические коллекции в качестве типов данных ПВХ. Разберем по шагам, почему существует это ограничение, и рассмотрим три практических способа его обхода — от классического изменения ПВХ до современных методов с использованием расширений конфигурации и регистров сведений.

Выясняем причину: почему ПВХ не поддерживает тип «СписокЗначений»?

Для начала проанализируем ситуацию с точки зрения архитектуры платформы «1С:Предприятие» и СУБД. План видов характеристик предназначен для хранения метаданных о типах, которые могут принимать дополнительные свойства. Сами значения этих свойств хранятся в табличной части справочников (для реквизитов) или в регистре сведений ДополнительныеСведения (для сведений).

СУБД (будь то MS SQL Server, PostgreSQL или любая другая) требует, чтобы в одной ячейке таблицы хранилось атомарное значение: число, строка, дата, булево или ссылка (уникальный идентификатор GUID) на конкретный элемент справочника или документа. Такие объекты, как СписокЗначений или ТаблицаЗначений, являются сложными динамическими структурами в оперативной памяти. Их невозможно записать в одно поле таблицы СУБД стандартными ссылочными методами. Именно поэтому платформа не позволяет выбирать их в качестве типа значения характеристик.

Выход из этой ситуации — изменить структуру хранения данных так, чтобы обойти это ограничение. Рассмотрим самые эффективные методы решения этой задачи.

Способ 1. Создание промежуточного справочника-профиля (Решение автора)

Этот способ позволяет остаться в рамках стандартного интерфейса БСП. Администратор системы сможет назначать свойства пользователям через стандартную кнопку «Дополнительные реквизиты» в карточке пользователя. Суть метода заключается в создании справочника-контейнера, который будет содержать табличную часть со списком нужных нам значений (подразделений).

Выполним следующие действия по шагам:

  1. Создадим новый справочник: Назовем его, например, _ПрофилиДоступаПоПодразделениям.
  2. Добавим табличную часть: Создадим табличную часть Подразделения с единственным реквизитом Подразделение (тип ссылки на справочник Подразделения).
  3. Включим возможность изменения ПВХ: Чтобы платформа позволила выбрать наш новый справочник в качестве типа для дополнительных реквизитов, необходимо добавить его в тип значения характеристик ПВХ ДополнительныеРеквизитыИСвойства.

Если вы работаете на старых версиях платформы, вам придется зайти в настройки поддержки конфигурации и установить для этого ПВХ режим «Редактируется с сохранением поддержки». Если же вы используете платформу 8.3.20.1549 и выше, снимать конфигурацию с поддержки не нужно! Вы можете заимствовать ПВХ ДополнительныеРеквизитыИСвойства в расширение конфигурации и добавить свой справочник в тип значения характеристик непосредственно внутри расширения.

После этого в режиме «1С:Предприятие» администратор выполняет следующие действия:

  1. Переходит в справочник _ПрофилиДоступаПоПодразделениям и создает элемент (например, «Профиль согласования заявок Восток»), заполнив его табличную часть списком разрешенных подразделений.
  2. В дополнительных реквизитах справочника Пользователи создает новый реквизит «Доступный профиль подразделений» с типом ссылки на наш новый справочник.
  3. В карточке конкретного пользователя выбирает созданный профиль.

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


Функция ПолучитьДоступныеПодразделенияПользователя(ТекущийПользователь) Экспорт
    
    Результат = Новый Массив;
    
    // Получаем значение дополнительного реквизита "Профиль доступа"
    СвойствоПрофиль = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоРеквизиту("Имя", "_ПрофильДоступаПодразделений");
    
    Если ЗначениеЗаполнено(СвойствоПрофиль) Тогда
        // Запрос к табличной части дополнительных реквизитов справочника Пользователи
        Запрос = Новый Запрос;
        Запрос.Текст = 
            "ВЫБРАТЬ
            |	ПользователиДополнительныеРеквизиты.Значение КАК Профиль
            |ИЗ
            |	Справочник.Пользователи.ДополнительныеРеквизиты КАК ПользователиДополнительныеРеквизиты
            |ГДЕ
            |	ПользователиДополнительныеРеквизиты.Ссылка = &Пользователь
            |	И ПользователиДополнительныеРеквизиты.Свойство = &СвойствоПрофиль";
        
        Запрос.УстановитьПараметр("Пользователь", ТекущийПользователь);
        Запрос.УстановитьПараметр("СвойствоПрофиль", СвойствоПрофиль);
        
        Выборка = Запрос.Выполнить().Выбрать();
        Если Выборка.Следующий() И ЗначениеЗаполнено(Выборка.Профиль) Тогда
            // Извлекаем элементы из табличной части справочника-контейнера
            ЗапросТЧ = Новый Запрос;
            ЗапросТЧ.Текст = 
                "ВЫБРАТЬ
                |	ПрофильПодразделения.Подразделение КАК Подразделение
                |ИЗ
                |	Справочник._ПрофилиДоступаПоПодразделениям.Подразделения КАК ПрофильПодразделения
                |ГДЕ
                |	ПрофильПодразделения.Ссылка = &Профиль";
            
            ЗапросТЧ.УстановитьПараметр("Профиль", Выборка.Профиль);
            Результат = ЗапросТЧ.Выполнить().Выгрузить().ВыгрузитьКолонку("Подразделение");
        КонецЕсли;
    КонецЕсли;
    
    Возврат Результат;
    
КонецФункции

Способ 2. Использование регистра сведений в расширении и кастомизация формы

Предыдущий способ удобен, но заставляет пользователя делать двойную работу: сначала создавать профиль в одном справочнике, а затем привязывать его в карточке пользователя. Рассмотрим более изящное, профессиональное и быстрое с точки зрения производительности решение. Мы создадим регистр сведений в расширении и выведем его прямо на форму пользователя.

Разберем по шагам реализацию этого механизма:

  1. Создадим расширение конфигурации (если оно еще не создано) и снимем галочку «Активно» на время разработки.
  2. Создадим новый регистр сведений в расширении: Назовем его _ДоступныеПодразделенияПользователей.
    • Установим режим записи: Независимый.
    • Создадим измерение: Пользователь (тип СправочникСсылка.Пользователи).
    • Создадим измерение: Подразделение (тип СправочникСсылка.Подразделения).
  3. Заимствуем форму элемента справочника Пользователи: Найдем в типовой конфигурации справочник Пользователи, его форму элемента ФормаЭлемента и добавим ее в наше расширение.
  4. Добавим интерфейсные элементы: В заимствованной форме программно или визуально добавим новую вкладку «Доступные подразделения» и поместим туда таблицу, связанную с динамическим списком нашего нового регистра сведений с отбором по текущему пользователю.

Проанализируем пример программного добавления и настройки интерфейса формы в модуле формы справочника Пользователи через расширение:


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

Такой подход обеспечивает великолепное быстродействие базы данных. Чтобы ограничить список заявок в динамическом списке, нам достаточно написать простейшее и очень быстрое условие в запросе динамического списка документов:


ГДЕ
    Заявка.Подразделение В (
        ВЫБРАТЬ 
            РегистрДоступа.Подразделение 
        ИЗ 
            РегистрСведений._ДоступныеПодразделенияПользователей КАК РегистрДоступа 
        ГДЕ 
            РегистрДоступа.Пользователь = &ТекущийПользователь
    )

Этот запрос будет выполняться мгновенно даже на базах данных с миллионами записей, поскольку СУБД использует индексы по измерениям регистра сведений.

Способ 3. Работа в рамках стандартной БСП без изменения конфигурации

Рассмотрим ситуацию, когда конфигурацию категорически запрещено менять (даже с помощью расширений) и база находится на строгом замке. В этом случае мы можем применить обходные пути самой БСП:

Вариант А: Использование иерархических дополнительных значений.
При создании дополнительного реквизита в БСП можно выбрать тип «Дополнительное значение (иерархия)». Это задействует стандартный справочник БСП ЗначенияСвойствОбъектовИерархия. Мы можем создать дополнительный реквизит «Группа доступа подразделений». В режиме «1С:Предприятие» внутри этого свойства администратор создает иерархические группы (например, «Подразделения дирекции»), а подчиненными элементами делает конкретные подразделения. Пользователю назначается сама папка (группа). В программном коде отборов мы проверяем вхождение подразделения документа в эту группу с помощью оператора запроса В ИЕРАРХИИ.

Вариант Б: Хранение списка в виде строки (сериализация).
Пользователю создается обычный текстовый дополнительный реквизит неограниченной длины. В это поле администратор записывает коды или уникальные идентификаторы (GUID) подразделений через запятую или другой разделитель. Например: 00-000001, 00-000015, 00-000214. При открытии списка документов программа считывает эту строку, преобразует ее в массив подстрок, находит по кодам ссылки на подразделения и накладывает отбор. Данный метод неудобен для ручного заполнения, но отлично подходит, если первоначальное заполнение списка автоматизировано внешней обработкой.

Сравнение и выбор оптимального решения

Подведем итог и сравним рассмотренные методы, чтобы вы могли выбрать наиболее подходящий для вашего проекта:

Рекомендуем использовать Способ 2 как наиболее современный стандарт разработки на платформе «1С:Предприятие 8.3».

← На главную