Почему НайтиПоСсылкам() находит документы, в которых нет ссылок на объект? Как найти скрытые связи

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

Каждый разработчик на платформе «1С:Предприятие 8» рано или поздно сталкивается с классической задачей — удалением или заменой ссылок на определенный объект (например, на элемент справочника Подразделения или Контрагенты). Мы пишем обработку, используем стандартную функцию НайтиПоСсылкам(), заменяем битые ссылки во всех найденных документах, проводим их и... при повторном поиске платформа снова выдает нам тот же самый список документов. Мы открываем документ в режиме предприятия, заглядываем в отладчике в каждый реквизит шапки, перебираем все табличные части — ссылки на удаляемый объект нигде нет! Почему так происходит?

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

Где могут скрываться «невидимые» ссылки

Когда встроенная функция НайтиПоСсылкам() анализирует базу данных, она сканирует не только реквизиты самого документа и его табличные части. Платформа проверяет абсолютно все таблицы СУБД, которые логически или физически связаны с данным документом. Рассмотрим подробнее основные места обитания таких «фантомных» связей.

  1. Записи в регистрах (движения документа)

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

    В чем подвох: Если движения по какому-то регистру были записаны программно (например, через прямой набор записей с установкой регистратора, но без привязки к коллекции Движения документа, либо при отключенном флаге автоматической записи движений), то при обычном интерактивном перепроведении или перезаписи документа эти записи в регистре не очищаются автоматически. Они остаются «висеть» в СУБД, привязаны к документу-регистратору и продолжают удерживать ссылку на удаляемый объект.

  2. Таблицы регистрации изменений (Планы обмена)

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

  3. Последовательности документов

    Границы последовательностей физически хранятся в СУБД в виде отдельных таблиц. Если документ когда-то зафиксировал границу по определенному разрезу (куда входило удаляемое подразделение), эта связь запишется в таблицу границ последовательности. Простое изменение или даже перепроведение документа не очистит эту запись. Требуется сдвинуть границу последовательности назад или программно ее очистить.

    Обратите внимание, что даже если в конфигураторе дерево последовательностей кажется пустым, в сложных типовых конфигурациях (таких как ЗУП 3.1) могут существовать системные или предопределенные механизмы контроля последовательностей на уровне платформы.

  4. Таблицы перерасчетов (актуально для ЗУП)

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

  5. Платформенный механизм «История данных» (Data History)

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

  6. Бизнес-процессы и задачи

    Если документ выступает основанием для бизнес-процесса или задачи, в которых фигурирует удаляемое подразделение (например, как исполнитель или аналитика адресации), ссылка останется в этих задачах. Визуально в самом документе никакой связи уже может не быть, но цепочка «Документ -> Задача -> Подразделение» не позволит очистить связи.

Как детально локализовать скрытую ссылку

Для того чтобы не гадать, в каком именно регистре или системной таблице «застряла» ссылка, лучше всего воспользоваться специализированными инструментами. Отличным решением является инструмент «Редактор объекта БД» из подсистемы «Инструменты разработчика» (ИР).

Разберем по шагам, как локализовать скрытую ссылку с его помощью:

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

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

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


// Пример очистки зависших движений по регистру накопления
ДокументОбъект = СсылкаНаДокумент.ПолучитьОбъект();

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

Если НаборЗаписей.Количество() > 0 Тогда
    // Обходим записи и заменяем старое подразделение на новое
    Для Каждого Запись Из НаборЗаписей Цикл
        Если Запись.Подразделение = УдаляемоеПодразделение Тогда
            Запись.Подразделение = НовоеПодразделение;
        КонецЕсли;
    КонецЦикла;
    
    // Записываем измененный набор
    НаборЗаписей.Записать(Истина);
КонецЕсли;

Если же записи регистра нужно просто полностью удалить (например, при программном удалении документа или его полной очистке), мы можем записать пустой набор записей:


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

Особенности работы с функцией НайтиПоСсылкам() в коде

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

НайтиПоСсылкам(<СписокСсылок>, <ОбластьПоиска>, <ВключитьОбъекты>, <ИсключитьОбъекты>, <РезультатПоиска>)

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

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


// Безопасный вызов НайтиПоСсылкам с использованием исключений
МассивПоиска = Новый Массив;
МассивПоиска.Добавить(УдаляемоеПодразделение);

ОбластьПоиска = Новый Массив; // Пустой массив - искать везде
ВключитьОбъекты = Новый Массив; // Пустой массив - включать все типы

МассивИсключений = Новый Массив;
МассивИсключений.Добавить(Метаданные.РегистрыСведений.ВерсииОбъектов); // Исключаем историю версий

ТаблицаРезультата = Новый ТаблицаЗначений;

// Вызываем функцию, обязательно заполняя все параметры массивами
НайтиПоСсылкам(МассивПоиска, ОбластьПоиска, ВключитьОбъекты, МассивИсключений, ТаблицаРезультата);

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

Резюме

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

← На главную