Как программно заменить ссылки на объект во всей базе 1С?

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

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

Поиск ссылок: Метод НайтиПоСсылкам

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


МассивДляПоиска = Новый Массив;
МассивДляПоиска.Добавить(СкладДляПоиска);

ТаблицаСсылок = НайтиПоСсылкам(МассивДляПоиска);
// В ТаблицаСсылок теперь содержатся колонки: 
// Ссылка — объект, который содержит ссылку на искомый элемент
// Данные — само значение ссылки (дублирует Ссылку в данном контексте)
// Метаданные — объект метаданных, где найдена ссылка

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

Разбор ошибки: Почему поле объекта недоступно для записи?

Проанализируем ситуацию: автор темы столкнулся с ошибкой "Поле объекта недоступно для записи (Значение)". Причина кроется в архитектуре 1С. Ссылка — это неизменяемый идентификатор записи в базе данных. Чтобы изменить данные внутри объекта (справочника или документа), на который указывает ссылка, необходимо получить сам Объект в оперативную память.

Для исправления этой ошибки мы должны использовать метод ПолучитьОбъект(). Только после этого поля станут доступны для модификации.


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

Правильная работа с типами данных

Второй важный момент — проверка типов реквизитов. В метаданных поле Тип представляет собой объект ОписаниеТипов. Если мы попытаемся сравнить его напрямую, например, Реквизит.Тип = Тип("СправочникСсылка.Склады"), условие может не сработать, особенно если тип реквизита составной (например, "Склад" или "Подразделение").

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


НужныйТип = Тип("СправочникСсылка.Склады");

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

Обработка табличных частей

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


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

Использование режима ОбменДанными.Загрузка

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

Это позволит системе "понять", что данные просто обновляются технически, и большинство прикладных проверок будет пропущено.

Особенности работы с регистрами

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

  1. Регистры сведений (независимые): Необходимо создать набор записей, установить отбор по измерениям (если это возможно) или прочитать весь набор, заменить значение в колонках и записать набор.
  2. Подчиненные регистры: Если ссылка найдена в движениях документа, то лучшим способом будет изменение самого документа-регистратора и его перезапись. Если установлена Загрузка = Истина, движения могут не обновиться автоматически (в зависимости от логики конфигурации), тогда придется программно править набор записей этого регистратора.

Альтернативные способы решения

Вместо написания собственного кода "с нуля", рассмотрим стандартные инструменты, которые сэкономят время и снизят риск ошибок:

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

← На главную