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