Как предотвратить регистрацию битых ссылок на узлах плана обмена в 1С при удалении объектов

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

При работе с распределенными информационными базами (РИБ) и обменивающимися узлами в платформе «1С:Предприятие 8» разработчики часто сталкиваются с неприятным явлением — появлением так называемых «битых ссылок» (когда в базе-приемнике вместо объекта отображается надпись <Объект не найден>...). Эта проблема напрямую связана с тем, как платформа и прикладной код регистрируют и передают информацию об изменении и удалении элементов данных.

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

Выясняем причину: почему на узлах регистрируются битые ссылки?

Давайте разберем классический сценарий, который часто приводит к нарушению ссылочной целостности в распределенной системе:

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

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


Объект = СсылкаНаОбъект.ПолучитьОбъект();
Объект.УстановитьПометкуУдаления(Истина); // Вызывает запись объекта!
Объект.Удалить();

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

Способ 1. Проверка существования ссылки перед регистрацией

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

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


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

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

Способ 2. Управление регистрацией через обработку событий удаления

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

Если в вашем плане обмена свойство Авторегистрация для объектов установлено в значение Запретить, платформа сама по себе ничего регистрировать при удалении не будет. В этом случае, чтобы зарегистрировать (или, наоборот, проигнорировать) удаление, вы должны явно прописать логику в подписке на событие ПередУдалением.

Если вы хотите полностью исключить отправку информации об удалении конкретных объектов на периферийные узлы, просто оставьте обработчик события ПередУдалением для этих объектов пустым (не регистрируйте в нем ничего), а в обработчике ПриЗаписи проверяйте пометку удаления:


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

Способ 3. Блокировка отправки удаления на этапе формирования сообщения

Что делать, если удаление объекта все-таки зарегистрировалось на узле плана обмена (например, из-за включенной авторегистрации или работы других механизмов конфигурации)? Мы можем перехватить и заблокировать выгрузку этого удаления непосредственно в момент формирования файла обмена (XML-сообщения).

Для этого используются стандартные обработчики событий, расположенные в модуле вашего плана обмена: ПриОтправкеДанныхПодчиненному или ПриОтправкеДанныхГлавному. При физическом удалении объекта платформа регистрирует на узле специальный служебный объект типа УдалениеОбъекта. Мы можем проанализировать тип отправляемых данных и принудительно отменить их отправку.

Рассмотрим пример кода в модуле плана обмена:


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

Этот способ считается одним из самых надежных и архитектурно правильных в платформе 1С. Его ключевое преимущество заключается в том, что вам не нужно отслеживать все транзакции удаления по всей конфигурации. Даже если регистрация удаления произошла, система просто "молча" снимет ее с регистрации при очередной отправке пакета данных, не отправляя само удаление в базу-приемник. Объект останется в базе-получателе в своем последнем актуальном состоянии.

Способ 4. Программное отключение авторегистрации "на лету"

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

Для этого используется свойство ОбменДанными, которое есть у любого регистрируемого объекта информационной базы. Рассмотрим пример:


ОбъектКонтрагент = СсылкаНаКонтрагента.ПолучитьОбъект();

// Отключаем автоматическое заполнение получателей для этого объекта
ОбъектКонтрагент.ОбменДанными.Получатели.АвтоЗаполнение = Ложь;

// Полностью очищаем список получателей, чтобы изменения никуда не записались
ОбъектКонтрагент.ОбменДанными.Получатели.Очистить();

// Теперь можно безопасно помечать на удаление или записывать объект
ОбъектКонтрагент.УстановитьПометкуУдаления(Истина);

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

Архитектурный подход к ссылочной целостности

Решая техническую проблему битых ссылок, важно помнить об общей архитектуре распределенной системы. В Библиотеке стандартных подсистем (БСП) и крупных типовых конфигурациях 1С (таких как ERP, КА, УТ) применяется строгое правило:

Физическое удаление объектов непосредственно в периферийных узлах категорически запрещено.

Пользователям в подчиненных узлах разрешается только устанавливать пометку удаления (ПометкаУдаления = Истина). Само же физическое удаление должно происходить централизованно в главной базе с помощью стандартной обработки «Удаление помеченных объектов». Эта обработка в обязательном порядке проверяет ссылочную целостность базы данных и не позволит удалить объект, если на него есть ссылки в других документах. Только после этого санкционированное удаление (сигнал УдалениеОбъекта) отправляется в периферийные базы, гарантируя, что там не образуется битых связей.

← На главную