Как программно заменить дублирующиеся элементы справочника во всей базе 1С?

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

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

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

Использование программного API Библиотеки стандартных подсистем (БСП)

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

Рассмотрим пример программной замены ссылок на сервере:


// Формируем массив замен
ПараметрыЗамены = Новый Массив;

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

// Выполняем замену ссылок средствами БСП
ПоискИУдалениеДублей.ЗаменитьСсылки(ПараметрыЗамены);

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

Собственный алгоритм поиска и перезаписи объектов через платформенный метод

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

  1. Формируем массив с дублирующейся ссылкой.
  2. Получаем таблицу ссылающихся объектов с помощью платформенной функции.
  3. Обходим найденные объекты, заменяя значение реквизитов и строк табличных частей на эталонную ссылку.
  4. Записываем измененные объекты в режиме загрузки данных для ускорения процесса.

Посмотрим на пример кода серверной процедуры:


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

Тонкости работы с регистрами и движениями документов

При ручной замене ссылок важно помнить о поведении платформы при записи объектов:

  1. Движения проведенных документов: если замена выполняется в реквизите проведенного документа с установленным свойством ОбменДанными.Загрузка = Истина, движения документа в регистрах накопления и бухгалтерии автоматически не обновляются. Чтобы актуализировать движения, требуется либо выполнять перепроведение документа (что может изменить закрытые периоды), либо точечно переписывать наборы записей соответствующих регистров.
  2. Коллизии в независимых регистрах сведений: если дублирующаяся ссылка входит в состав ключевых измерений независимого регистра сведений, простая замена значения на целевое может вызвать ошибку неуникальности записи (когда запись с эталонным ключом уже существует). В таких случаях необходимо программно прочитать набор записей, выполнить объединение строк и записать очищенный набор.

Причины ошибки подписок на события и методы их обхода

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

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

Чтобы устранить эту проблему без редактирования конфигурации, применим следующие подходы:

  1. Запуск в управляемом режиме: открываем внешнюю обработку в режиме тонкого клиента или запускаем «1С:Предприятие» с ключом командной строки /RunModeManagedApplication, где все операции с объектами базы происходят строго в серверном контексте &НаСервере.
  2. Фоновое выполнение: вызываем процедуру замены ссылок через фоновое задание с помощью метода ФоновыеЗадания.Выполнить(). Фоновое задание всегда стартует на сервере, что гарантирует доступ ко всем серверным модулям и их подпискам.

Почему нельзя делать замену прямым запросом к СУБД (SQL UPDATE)

Попытка ускорить процесс заменой ссылок напрямую в таблицах SQL-сервера крайне опасна по следующим причинам:

  1. Внутренний формат GUID: платформа 1С хранит ссылки типа УникальныйИдентификатор в виде бинарных данных binary(16) со специфической перестановкой байтов. Прямая вставка стандартного строкового GUID приведет к созданию некорректных битых ссылок.
  2. Поля составных типов: поля с составным типом данных в СУБД разделены на несколько колонок (тип, номер таблицы, идентификатор). Ошибка в обновлении хотя бы одной служебной колонки разрушит структуру данных объекта.
  3. Кэш сервера 1С: кластер серверов 1С кэширует считываемые объекты. Прямое изменение данных в СУБД не сбрасывает кэш платформы, что вызывает рассинхронизацию данных и непредсказуемое поведение системы вплоть до перезапуска рабочих процессов.
← На главную