При разработке и кастомизации типовых конфигураций на платформе «1С:Предприятие 8.3» (например, «1С:Бухгалтерия предприятия 3.0») разработчики часто сталкиваются с ограничениями стандартного механизма дополнительных реквизитов и сведений (БСП). Типичная задача — связать с пользователем не одно конкретное значение (например, одно подразделение), а целый список (коллекцию) значений для последующей фильтрации документов или отчетов.
Попытка выбрать тип значения СписокЗначений в плане видов характеристик (ПВХ) ДополнительныеРеквизитыИСведения заканчивается неудачей. Платформа физически не позволяет использовать динамические коллекции в качестве типов данных ПВХ. Разберем по шагам, почему существует это ограничение, и рассмотрим три практических способа его обхода — от классического изменения ПВХ до современных методов с использованием расширений конфигурации и регистров сведений.
Для начала проанализируем ситуацию с точки зрения архитектуры платформы «1С:Предприятие» и СУБД. План видов характеристик предназначен для хранения метаданных о типах, которые могут принимать дополнительные свойства. Сами значения этих свойств хранятся в табличной части справочников (для реквизитов) или в регистре сведений ДополнительныеСведения (для сведений).
СУБД (будь то MS SQL Server, PostgreSQL или любая другая) требует, чтобы в одной ячейке таблицы хранилось атомарное значение: число, строка, дата, булево или ссылка (уникальный идентификатор GUID) на конкретный элемент справочника или документа. Такие объекты, как СписокЗначений или ТаблицаЗначений, являются сложными динамическими структурами в оперативной памяти. Их невозможно записать в одно поле таблицы СУБД стандартными ссылочными методами. Именно поэтому платформа не позволяет выбирать их в качестве типа значения характеристик.
Выход из этой ситуации — изменить структуру хранения данных так, чтобы обойти это ограничение. Рассмотрим самые эффективные методы решения этой задачи.
Этот способ позволяет остаться в рамках стандартного интерфейса БСП. Администратор системы сможет назначать свойства пользователям через стандартную кнопку «Дополнительные реквизиты» в карточке пользователя. Суть метода заключается в создании справочника-контейнера, который будет содержать табличную часть со списком нужных нам значений (подразделений).
Выполним следующие действия по шагам:
_ПрофилиДоступаПоПодразделениям.Подразделения с единственным реквизитом Подразделение (тип ссылки на справочник Подразделения).ДополнительныеРеквизитыИСвойства.Если вы работаете на старых версиях платформы, вам придется зайти в настройки поддержки конфигурации и установить для этого ПВХ режим «Редактируется с сохранением поддержки». Если же вы используете платформу 8.3.20.1549 и выше, снимать конфигурацию с поддержки не нужно! Вы можете заимствовать ПВХ ДополнительныеРеквизитыИСвойства в расширение конфигурации и добавить свой справочник в тип значения характеристик непосредственно внутри расширения.
После этого в режиме «1С:Предприятие» администратор выполняет следующие действия:
_ПрофилиДоступаПоПодразделениям и создает элемент (например, «Профиль согласования заявок Восток»), заполнив его табличную часть списком разрешенных подразделений.Пользователи создает новый реквизит «Доступный профиль подразделений» с типом ссылки на наш новый справочник.При написании кода отбора заявок (например, в модуле менеджера самописного документа при получении данных списка) мы можем легко считать этот профиль и извлечь список подразделений из его табличной части. Посмотрим на пример кода:
Функция ПолучитьДоступныеПодразделенияПользователя(ТекущийПользователь) Экспорт
Результат = Новый Массив;
// Получаем значение дополнительного реквизита "Профиль доступа"
СвойствоПрофиль = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоРеквизиту("Имя", "_ПрофильДоступаПодразделений");
Если ЗначениеЗаполнено(СвойствоПрофиль) Тогда
// Запрос к табличной части дополнительных реквизитов справочника Пользователи
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ПользователиДополнительныеРеквизиты.Значение КАК Профиль
|ИЗ
| Справочник.Пользователи.ДополнительныеРеквизиты КАК ПользователиДополнительныеРеквизиты
|ГДЕ
| ПользователиДополнительныеРеквизиты.Ссылка = &Пользователь
| И ПользователиДополнительныеРеквизиты.Свойство = &СвойствоПрофиль";
Запрос.УстановитьПараметр("Пользователь", ТекущийПользователь);
Запрос.УстановитьПараметр("СвойствоПрофиль", СвойствоПрофиль);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() И ЗначениеЗаполнено(Выборка.Профиль) Тогда
// Извлекаем элементы из табличной части справочника-контейнера
ЗапросТЧ = Новый Запрос;
ЗапросТЧ.Текст =
"ВЫБРАТЬ
| ПрофильПодразделения.Подразделение КАК Подразделение
|ИЗ
| Справочник._ПрофилиДоступаПоПодразделениям.Подразделения КАК ПрофильПодразделения
|ГДЕ
| ПрофильПодразделения.Ссылка = &Профиль";
ЗапросТЧ.УстановитьПараметр("Профиль", Выборка.Профиль);
Результат = ЗапросТЧ.Выполнить().Выгрузить().ВыгрузитьКолонку("Подразделение");
КонецЕсли;
КонецЕсли;
Возврат Результат;
КонецФункции
Предыдущий способ удобен, но заставляет пользователя делать двойную работу: сначала создавать профиль в одном справочнике, а затем привязывать его в карточке пользователя. Рассмотрим более изящное, профессиональное и быстрое с точки зрения производительности решение. Мы создадим регистр сведений в расширении и выведем его прямо на форму пользователя.
Разберем по шагам реализацию этого механизма:
_ДоступныеПодразделенияПользователей.
Пользователь (тип СправочникСсылка.Пользователи).Подразделение (тип СправочникСсылка.Подразделения).Пользователи, его форму элемента ФормаЭлемента и добавим ее в наше расширение.Проанализируем пример программного добавления и настройки интерфейса формы в модуле формы справочника Пользователи через расширение:
&После("ПриСозданииНаСервере")
Процедура Расш1_ПриСозданииНаСервереПосле(Отказ, СтандартнаяОбработка)
// Создаем новую страницу на форме для настройки подразделений
ГруппаСтраницы = Элементы.Найти("ГруппыПанели"); // Находим стандартную панель вкладок
Если ГруппаСтраницы <> Неопределено Тогда
НоваяСтраница = Элементы.Добавить("СтраницаДоступныеПодразделения", Тип("ГруппаФормы"), ГруппаСтраницы);
НоваяСтраница.Вид = ВидГруппыФормы.Страница;
НоваяСтраница.Заголовок = "Доступные подразделения";
// Создаем реквизит формы типа "ДинамическийСписок" для отображения записей регистра
МассивДобавляемыхРеквизитов = Новый Массив;
РеквизитСписок = Новый РеквизитФормы("ДоступныеПодразделенияСписок", Новый ОписаниеТипов("ДинамическийСписок"));
МассивДобавляемыхРеквизитов.Добавить(РеквизитСписок);
ИзменитьРеквизиты(МассивДобавляемыхРеквизитов);
// Настраиваем динамический список
ЗначениеРеквизита = РеквизитыФормыВЗначения("ДоступныеПодразделенияСписок");
ЗначениеРеквизита.ОсновнаяТаблица = "РегистрСведений._ДоступныеПодразделенияПользователей";
ЗначениеРеквизита.ДинамическоеСчитываниеДанных = Истина;
// Устанавливаем параметры и отбор по текущему пользователю
Отбор = ЗначениеРеквизита.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
Отбор.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Пользователь");
Отбор.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
Отбор.ПравоеЗначение = Объект.Ссылка;
Отбор.Использование = Истина;
ЗначениеВРеквизитФормы(ЗначениеРеквизита, "ДоступныеПодразделенияСписок");
// Создаем таблицу на форме для отображения списка
ТаблицаФормы = Элементы.Добавить("ТаблицаДоступныеПодразделения", Тип("ТаблицаФормы"), НоваяСтраница);
ТаблицаФормы.ПутьКДанным = "ДоступныеПодразделенияСписок";
// Добавляем колонку "Подразделение"
КолонкаПодразделение = Элементы.Добавить("КолонкаПодразделение", Тип("ПолеФормы"), ТаблицаФормы);
КолонкаПодразделение.Вид = ВидПоляФормы.ПолеВвода;
КолонкаПодразделение.ПутьКДанным = "ДоступныеПодразделенияСписок.Подразделение";
КонецЕсли;
КонецПроцедуры
Такой подход обеспечивает великолепное быстродействие базы данных. Чтобы ограничить список заявок в динамическом списке, нам достаточно написать простейшее и очень быстрое условие в запросе динамического списка документов:
ГДЕ
Заявка.Подразделение В (
ВЫБРАТЬ
РегистрДоступа.Подразделение
ИЗ
РегистрСведений._ДоступныеПодразделенияПользователей КАК РегистрДоступа
ГДЕ
РегистрДоступа.Пользователь = &ТекущийПользователь
)
Этот запрос будет выполняться мгновенно даже на базах данных с миллионами записей, поскольку СУБД использует индексы по измерениям регистра сведений.
Рассмотрим ситуацию, когда конфигурацию категорически запрещено менять (даже с помощью расширений) и база находится на строгом замке. В этом случае мы можем применить обходные пути самой БСП:
Вариант А: Использование иерархических дополнительных значений.
При создании дополнительного реквизита в БСП можно выбрать тип «Дополнительное значение (иерархия)». Это задействует стандартный справочник БСП ЗначенияСвойствОбъектовИерархия. Мы можем создать дополнительный реквизит «Группа доступа подразделений». В режиме «1С:Предприятие» внутри этого свойства администратор создает иерархические группы (например, «Подразделения дирекции»), а подчиненными элементами делает конкретные подразделения. Пользователю назначается сама папка (группа). В программном коде отборов мы проверяем вхождение подразделения документа в эту группу с помощью оператора запроса В ИЕРАРХИИ.
Вариант Б: Хранение списка в виде строки (сериализация).
Пользователю создается обычный текстовый дополнительный реквизит неограниченной длины. В это поле администратор записывает коды или уникальные идентификаторы (GUID) подразделений через запятую или другой разделитель. Например: 00-000001, 00-000015, 00-000214. При открытии списка документов программа считывает эту строку, преобразует ее в массив подстрок, находит по кодам ссылки на подразделения и накладывает отбор. Данный метод неудобен для ручного заполнения, но отлично подходит, если первоначальное заполнение списка автоматизировано внешней обработкой.
Подведем итог и сравним рассмотренные методы, чтобы вы могли выбрать наиболее подходящий для вашего проекта:
Рекомендуем использовать Способ 2 как наиболее современный стандарт разработки на платформе «1С:Предприятие 8.3».