Как программно изменить родителя номенклатуры в 1С без потери иерархии?

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

При работе с базами 1С часто возникает задача массового переноса элементов номенклатуры из одних папок в другие. Казалось бы, задача простая: нужно найти новую папку и присвоить ее в качестве родителя всем необходимым товарам и подпапкам. Однако, если попытаться написать код "в лоб", можно столкнуться с крайне неприятной проблемой — разрушением древовидной структуры. Разберем по шагам, почему это происходит и как правильно реализовать смену родителя.

Проанализируем классическую ошибку: почему ломается иерархия

Представим ситуацию: в корне справочника есть структура вида Папка / ПодПапка / ПодПодПапка / Товар. Мы хотим перенести всю эту ветку в условную папку "hello". Рассмотрим код, который часто пытаются использовать для этой задачи:


НовыйРодитель = Справочники.Товары.НайтиПоНаименованию("hello");
Выборка = Справочники.Товары.ВыбратьИерархически();
Пока Выборка.Следующий() Цикл
    ОбъектТовар = Выборка.ПолучитьОбъект();
    Если ОбъектТовар.Родитель <> НовыйРодитель Тогда
        ОбъектТовар.Родитель = НовыйРодитель;
        ОбъектТовар.Записать();
    КонецЕсли;
КонецЦикла;

После выполнения этого кода пользователи с ужасом обнаруживают, что иерархия стала плоской. Все подпапки и сами товары вырваны из своих исходных мест и теперь лежат сплошным списком прямо внутри папки "hello", на одном уровне. Товары визуально "теряют" свои категории.

Выясним причину: Метод ВыбратьИерархически обходит абсолютно все элементы ветки — сначала саму родительскую папку, затем все ее вложенные подпапки, затем товары. В цикле мы берем каждый из этих элементов и жестко назначаем ему реквизит Родитель равным папке "hello". Естественно, структура ломается, так как мы лишаем вложенные папки и товары их законных связей друг с другом.

Как правильно перенести ветку иерархии кодом?

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

Все подчиненные элементы автоматически "переедут" в новое место вместе со своей корневой папкой, полностью сохранив внутреннюю структуру. Если же нам необходимо выбрать только папки самого верхнего уровня программно, лучше всего использовать запрос.

Использование запроса для безопасного переноса

Рассмотрим пример кода, где мы отбираем только те папки (или элементы), которые лежат в корне справочника (то есть их родитель пуст), и переносим их в новую категорию:


НовыйРодитель = Справочники.Номенклатура.НайтиПоНаименованию("hello");
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |    Номенклатура.Ссылка КАК Ссылка
    |ИЗ
    |    Справочник.Номенклатура КАК Номенклатура
    |ГДЕ
    |    Номенклатура.Родитель = ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)";

РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();

Пока Выборка.Следующий() Цикл
    Если Выборка.Ссылка <> НовыйРодитель Тогда
        ОбъектТовар = Выборка.Ссылка.ПолучитьОбъект();
        ОбъектТовар.Родитель = НовыйРодитель;
        ОбъектТовар.Записать();
    КонецЕсли;
КонецЦикла;

Если же нужно отобрать элементы конкретной ветки для фильтрации, мы можем добавить в запрос конструкцию В ИЕРАРХИИ(&ИскомаяПапка). Главное — четко контролировать, у каких именно ссылок мы вызываем метод ПолучитьОбъект.

Лучшие практики при массовом изменении объектов

При написании подобных обработок настоятельно рекомендуем придерживаться следующих правил:

  1. Отключение бизнес-логики: Если элементов много, при вызове метода Записать будут срабатывать многочисленные обработчики модулей объекта. Это может вызвать ошибки (если старые товары не проходят новые проверки заполнения). Чтобы записать объект "как есть", используйте: ОбъектТовар.ОбменДанными.Загрузка = Истина; перед записью.
  2. Защита от циклических ссылок: Всегда проверяйте в коде, чтобы НовыйРодитель не оказался равен самому изменяемому объекту или одному из его подчиненных узлов. Иначе база выдаст ошибку зацикливания иерархии.
  3. Использование транзакций: При переносе тысяч элементов оборачивайте запись в транзакции. Использование конструкций НачатьТранзакцию и ЗафиксироватьТранзакцию для порций по 100-500 объектов ускорит работу в разы.

Нужно ли вообще программировать?

В завершение разберем еще один важный момент. Зачастую разработчики сразу бросаются писать код для одноразовых задач, забывая о мощных возможностях пользовательского режима.

Если вам нужно просто перенести несколько веток справочника из одного места в другое, воспользуйтесь штатным интерфейсом 1С:Предприятия. Вы можете просто выделить нужные папки в динамическом списке (зажав клавиши Shift или Ctrl), нажать правую кнопку мыши и выбрать команду "Переместить в группу". Также в современных формах отлично работает обычное перетаскивание мышью (Drag-and-Drop). Платформа сама аккуратно поменяет родителей, не нарушая связей.

← На главную