Как удалить взаимосвязанные и неудаляемые объекты в 1С 8.3?

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

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

Почему объекты блокируются при удалении?

Проанализируем архитектурные причины, по которым стандартный механизм контроля ссылочной целостности запрещает физическое удаление:

  1. Циклические и взаимные связи: когда элемент справочника «А» хранит ссылку на элемент «Б», а «Б» указывает на «А», стандартная проверка видит, что на каждый из объектов есть внешняя ссылка, и блокирует операцию для обоих.
  2. Подчиненные справочники: попытка удалить элемент-владелец без предварительного удаления всех подчиненных ему объектов (например, договоров, контактных лиц, банковских счетов) завершится ошибкой.
  3. Скрытые ссылки в независимых регистрах сведений: в типовых решениях при записи справочника данные часто дублируются в независимые регистры (контактная информация, дополнительные реквизиты, история версий, соответствия объектов информационных баз). У таких регистров нет документа-регистратора, поэтому записи остаются в базе даже после пометки элемента на удаление.
  4. Регистрация изменений на узлах планов обмена: если в базе настроена синхронизация данных, ссылки на измененные объекты попадают в таблицы регистрации изменений, что также расценивается платформой как активное использование ссылки.
  5. Предопределенные элементы: платформа 1С на уровне ядра блокирует удаление предопределенных элементов, даже если они помечены на удаление.

Опасность прямого удаления через метод Удалить()

Посмотрим на пример кода, к которому часто прибегают начинающие разработчики:


Запрос1 = Новый Запрос;
Запрос1.Текст = 
    "ВЫБРАТЬ
    |    Контрагенты.Ссылка КАК Ссылка
    |ИЗ
    |    Справочник.Контрагенты КАК Контрагенты
    |ГДЕ
    |    Контрагенты.ПометкаУдаления = ИСТИНА";

ИтогЗапр = Запрос1.Выполнить().Выгрузить();
Для Каждого Стр Из ИтогЗапр Цикл
    УдалОб = Стр.Ссылка.ПолучитьОбъект();
    УдалОб.Удалить();
КонецЦикла;

Разберем, почему такой подход создает серьезные проблемы:

  1. Появление битых ссылок: если удаленный контрагент был указан в договоре или в независимом регистре, там останется «висящая» ссылка вида <Объект не найден>. База потеряет целостность.
  2. Ошибки времени выполнения: метод Удалить() инициирует события ПередУдалением и ПриУдалении в модуле объекта. В типовых конфигурациях в этих процедурах часто установлены проверки, вызывающие исключение при попытке удаления. В итоге половина элементов выдаст ошибку и останется в базе.

Способ 1. Использование функции НайтиПоСсылкам и каскадная пометка

Чтобы удалить объект без нарушения ссылочной целостности, выясним, кто именно на него ссылается, и пометим зависимые объекты на удаление вместе с ним.

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


МассивПомеченных = Новый Массив;
Выборка = Справочники.Контрагенты.Выбрать();
Пока Выборка.Следующий() Цикл
    Если Выборка.ПометкаУдаления Тогда
        МассивПомеченных.Добавить(Выборка.Ссылка);
    КонецЕсли;
КонецЦикла;

ТаблицаСсылок = НайтиПоСсылкам(МассивПомеченных);

Для Каждого Стр Из ТаблицаСсылок Цикл
    // В Стр.Данные находится ссылка на объект или запись регистра, ссылающаяся на удаляемый элемент
    Если ОбщегоНазначенияКлиентСервер.ЭтоСсылка(ТипЗнч(Стр.Данные)) Тогда
        ОбъектСсылка = Стр.Данные.ПолучитьОбъект();
        Если Не ОбъектСсылка.ПометкаУдаления Тогда
            ОбъектСсылка.УстановитьПометкуУдаления(Истина);
        КонецЕсли;
    КонецЕсли;
КонецЦикла;

После каскадной пометки зависимых объектов типовая обработка удаления помеченных объектов сможет удалить всю цепочку связанных элементов целиком.

Способ 2. Очистка блокирующих записей в независимых регистрах сведений

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


// Очистка записей независимого регистра сведений
Набор = РегистрыСведений.КонтактнаяИнформация.СоздатьНаборЗаписей();
Набор.Отбор.Объект.Установить(УдаляемаяСсылка);
Набор.Записать(); // Запись пустого набора удаляет существующие строки

Очистив зависимые регистры, мы убираем внешние ссылки, после чего элемент справочника успешно удаляется штатным механизмом.

Способ 3. Пакетное физическое удаление методом УдалитьОбъекты

Если база абсолютно новая, документов в ней еще нет, а требуется быстро зачистить группу взаимно ссылающихся справочников без пошагового разрыва связей, используем процедуру глобального контекста УдалитьОбъекты() с отключенным контролем ссылочной целостности.

Посмотрим на синтаксис вызова:


МассивДляУдаления = Новый Массив;

// Собираем в массив ссылки на взаимосвязанные справочники
ВыборкаКонтрагенты = Справочники.Контрагенты.Выбрать();
Пока ВыборкаКонтрагенты.Следующий() Цикл
    Если ВыборкаКонтрагенты.ПометкаУдаления Тогда
        МассивДляУдаления.Добавить(ВыборкаКонтрагенты.Ссылка);
    КонецЕсли;
КонецЦикла;

ВыборкаДоговоры = Справочники.ДоговорыКонтрагентов.Выбрать();
Пока ВыборкаДоговоры.Следующий() Цикл
    Если ВыборкаДоговоры.ПометкаУдаления Тогда
        МассивДляУдаления.Добавить(ВыборкаДоговоры.Ссылка);
    КонецЕсли;
КонецЦикла;

НайденныеСсылки = Новый ТаблицаЗначений;

// Второй параметр "Ложь" отключает проверку целостности
УдалитьОбъекты(МассивДляУдаления, Ложь, НайденныеСсылки);

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

Способ 4. Снятие признака предопределенности

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


Объект = Ссылка.ПолучитьОбъект();
Объект.ИмяПредопределенныхДанных = "";
Объект.Записать();

После очистки имени предопределенного элемента он превратится в обычный элемент справочника и сможет быть помечен и удален на общих основаниях.

Способ 5. Снятие регистрации на узлах планов обмена

Если удалению препятствуют служебные таблицы регистрации изменений, очистим их вызовом специализированного метода:


Для Каждого План Из Метаданные.ПланыОбмена Цикл
    ПланыОбмена.УдалитьРегистрациюИзменений(ПланыОбмена[План.Имя].Выбрать(), УдаляемаяСсылка);
КонецЦикла;

Интерактивные методы: обработка «Групповое изменение реквизитов»

Если требуется решить задачу без написания собственного программного кода, воспользуемся типовой обработкой «Групповое изменение реквизитов» (раздел «Администрирование» — «Обслуживание» — «Корректировка данных»):

  1. Выбираем справочник, который ссылается на удаляемые объекты.
  2. Настраиваем отбор по элементам, которые нужно скорректировать.
  3. В списке реквизитов находим поле связи (например, «Основной договор» или «Родитель») и устанавливаем флажок для изменения, оставляя значение реквизита пустым.
  4. Нажимаем кнопку «Изменить реквизиты».

После разрыва циклической связи типовое удаление помеченных объектов отработает штатно в стандартном режиме.

← На главную