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