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