Почему элемент справочника не перемещается в группу программно и как это исправить

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

При разработке конфигураций на платформе 1С:Предприятие 8.3 нередко возникает задача автоматического распределения элементов справочника по папкам. Типичный сценарий: в зависимости от значения определенного реквизита элемент должен автоматически попадать в соответствующую группу-родителя при записи. Однако разработчики часто сталкиваются с ситуацией, когда стандартный реквизит Родитель в объекте заполняется, но визуально в списке элемент остается на прежнем месте или вовсе отображается в корне справочника. Рассмотрим причины такого поведения и разберем по шагам правильную реализацию этой логики.

Анализ проблемы: почему элемент визуально не перемещается

Проанализируем исходную ситуацию. Разработчик написал код, который при записи элемента находит существующую группу или создает новую, а затем присваивает ее ссылку в реквизит Объект.Родитель:


&НаСервере
Процедура ВыбратьРодителя()
    Класс = Объект.Класс.Наименование;
    Если НЕ Объект.Родитель.Наименование = Класс Тогда
        Группа = Справочники.Ученики.НайтиПоНаименованию(Класс);
        Если Группа = Справочники.Ученики.ПустаяСсылка() Тогда
            Группа = Справочники.Ученики.СоздатьГруппу();
            Группа.Наименование = Класс;
            Группа.Записать();
        КонецЕсли;
        Объект.Родитель = Группа.Ссылка;
    КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура ПередЗаписью(Отказ, ПараметрыЗаписи)
    ВыбратьРодителя();
КонецПроцедуры

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

В платформе 1С для таблиц форм, отображающих иерархические справочники, предусмотрено системное свойство Отображение (перечисление ОтображениеТаблицы), которое может принимать значения:

  1. ИерархическийСписок — классическое отображение с возможностью заходить внутрь групп.
  2. Список — плоский список, где все элементы и группы отображаются на одном уровне без вложенности.
  3. Дерево — разворачивающееся дерево узлов непосредственно в строках таблицы.

Если в пользовательских или системных настройках формы списка было установлено отображение Список, все элементы отображаются в общей массе, независимо от заполненности реквизита Родитель. Переключение режима в Иерархический список немедленно решает проблему видимости перемещенного объекта.

Программная фиксация режима отображения списка

Чтобы исключить зависимость интерфейса от случайных настроек пользователя, зафиксируем правильный режим таблицы формы программно при ее создании. Для этого в модуле формы списка используем обработчик ПриСозданииНаСервере:


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    Элементы.Список.Отображение = ОтображениеТаблицы.ИерархическийСписок;
КонецПроцедуры

Здесь Список — это имя элемента формы (таблицы), связанного с динамическим списком справочника.

Архитектурные ошибки и перенос логики в модуль объекта

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

  1. Отсутствие универсальности: если элемент создается или обновляется программно (при обмене данными, через внешнюю обработку или фоновое задание), события формы не сработают. Элемент останется без родителя.
  2. Нарушение транзакционности: метод Группа.Записать() запускает отдельную транзакцию до записи основного элемента. Если пользователь прервет сохранение или возникнет ошибка заполнения других реквизитов, пустая группа останется в базе.
  3. Состояние гонки: при одновременной записи элементов несколькими сеансами два пользователя могут одновременно не найти группу и создать дублирующие папки с одинаковым наименованием.

Перенесем бизнес-логику в предопределенное событие ПередЗаписью в модуль объекта справочника.

Поиск группы запросом и корректное назначение родителя

Обратим внимание на метод НайтиПоНаименованию. Если в справочнике существует обычный элемент с тем же наименованием, что и требуемая группа, метод может вернуть ссылку на этот элемент, а не на группу. При попытке назначить обычный элемент в качестве родителя в справочнике с иерархией групп и элементов платформа выдаст ошибку. Безопаснее и быстрее находить группу с помощью запроса, явно проверяя признак ЭтоГруппа.

Посмотрим на реализацию в модуле объекта справочника Ученики:


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

КонецПроцедуры

Рассмотрим, что изменилось в этом подходе:

  1. Код размещен в модуле объекта, поэтому распределение по группам сработает при любом способе записи объекта — как из интерфейса, так и программно.
  2. Запрос отбирает только записи, у которых ЭтоГруппа = ИСТИНА, исключая ошибочное присвоение элемента вместо папки.
  3. Обращение к свойствам текущего объекта (Класс, Родитель) происходит напрямую без промежуточной контекстной переменной Объект.

Проверка настроек метаданных справочника

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

  1. Свойство Иерархия должно быть включено.
  2. В свойстве Вид иерархии должно быть установлено значение Иерархия групп и элементов. Если выбрано значение Иерархия элементов, использование метода СоздатьГруппу() приведет к критической ошибке выполнения, так как группы в такой структуре отсутствуют.
  3. Проверим свойство Ограничивать количество уровней иерархии. Если ограничение включено, создание подгрупп глубже заданного числа уровней вызовет ошибку платформы при записи.
← На главную