Рассмотрим распространенную задачу рефакторинга в 1С: перенос объекта метаданных (например, справочника) из расширения конфигурации в основную конфигурацию. Для предварительной оценки зависимостей и объема работ полезно использовать Анализ конфигураций и состава расширений. Часто возникает соблазн использовать "хитрый" способ с сохранением уникальных идентификаторов (GUID), чтобы избежать долгой процедуры замены ссылок. Разберем подробно, почему "быстрый" метод не сработает, как устроена ссылка в базе данных 1С и какой алгоритм действий является единственно верным и надежным.
В исходной постановке задачи предлагается следующий алгоритм, который на первый взгляд кажется логичным:
УстановитьСсылкуНового, присвоив им те же самые GUID, которые были у удаленных элементов.Предполагается, что раз GUID (Уникальный Идентификатор) остался прежним, то все документы, регистры и справочники, которые ссылались на этот объект, "подхватят" его автоматически. Однако на практике после таких действий мы получим в базе данных огромное количество значений <Объект не найден>.
Давайте разберемся, почему это происходит. Для этого нам необходимо заглянуть "под капот" платформы 1С:Предприятие и понять, как физически хранится ссылка в СУБД (SQL).
Поле типа "Ссылка" в базе данных — это не просто GUID. Это составной тип данных, который фактически состоит из двух частей (на уровне физических таблиц SQL это часто реализуется как поля _TRef и _RRef, либо неявно обрабатывается платформой):
Когда справочник находится в расширении, у него есть свой уникальный Идентификатор типа. Когда мы создаем точно такой же справочник (даже с тем же именем) в основной конфигурации — для платформы и СУБД это совершенно другая таблица с новым Идентификатором типа.
Следовательно, в регистрах и документах записано: "Ищи объект с GUID = X в таблице типа Y (Расширение)". Мы создаем новый объект: "Вот объект с GUID = X, но он лежит в таблице типа Z (Основная)".
Платформа обращается по старой ссылке, видит старый Тип, идет в таблицу расширения (которая либо пуста, либо удалена), не находит там записи и честно сообщает: "Объект не найден". То, что в соседней таблице (основной конфигурации) лежит запись с таким же GUID, платформу не интересует, так как тип не совпадает.
Чтобы корректно перенести метаданные и сохранить целостность данных, нам придется выполнить процедуру замены ссылок. Это неизбежный этап. Разберем пошаговый план действий, который гарантирует результат.
Создадим в основной конфигурации справочник-приемник. Рекомендуется на этапе переноса дать ему имя, отличное от имени в расширении, или использовать то же имя, но четко понимать, что это разные объекты метаданных (платформа позволяет иметь одноименные объекты, если один в расширении, а другой в основной, но во избежание путаницы лучше временно использовать префикс).
Пусть в расширении справочник называется Расш_Справочник, а в основной мы создадим СправочникОсн. Структура реквизитов должна быть идентичной.
Нам необходимо физически создать новые элементы в справочнике основной конфигурации. Напишем простую обработку для переноса данных, либо, если требуется быстрое решение без кодирования, применим Простой менеджер данных (simple data manager).
В этом случае нам не обязательно сохранять старые GUID (хотя это возможно для синхронизации, но не решит проблему ссылок). Проще создать новые элементы и запомнить соответствие "Старая Ссылка -> Новая Ссылка".
Примерный алгоритм переноса данных:
// Создадим соответствие для хранения пар ссылок
СоответствиеСсылок = Новый Соответствие;
Выборка = Справочники.Расш_Справочник.Выбрать();
Пока Выборка.Следующий() Цикл
// Создаем новый элемент в основном справочнике
НовыйЭлемент = Справочники.СправочникОсн.СоздатьЭлемент();
// Копируем стандартные реквизиты
НовыйЭлемент.Наименование = Выборка.Наименование;
НовыйЭлемент.Родитель = ?; // С иерархией придется работать отдельно (сначала родители, потом дети)
// Копируем прочие реквизиты
ЗаполнитьЗначенияСвойств(НовыйЭлемент, Выборка);
НовыйЭлемент.Записать();
// Запоминаем пару: что на что менять
СоответствиеСсылок.Вставить(Выборка.Ссылка, НовыйЭлемент.Ссылка);
КонецЦикла;
Важно: Если справочник иерархический, перенос нужно делать в два прохода или использовать рекурсию, чтобы сначала создавались родители, а потом подчиненные элементы. Либо сначала перенести все элементы "плоским" списком без заполнения родителя, а вторым проходом проставить родителей, используя наше СоответствиеСсылок.
Теперь, когда у нас есть дубликаты данных в основной конфигурации, нам нужно "переключить" всю базу со старых ссылок (из расширения) на новые (из основной).
Для этого используем стандартную обработку «Поиск и замена значений» (входит в состав большинства типовых конфигураций или доступна на ИТС как внешняя обработка).
Либо, если мы пишем код сами, используем метод глобального контекста НайтиПоСсылкам и затем заменяем значения.
Почему это работает? Процедура замены ссылок физически перезаписывает данные в регистрах сведений, документах, справочниках. Она меняет пару {СтарыйТип + СтарыйGUID} на {НовыйТип + НовыйGUID}.
Пример программной замены (если мы делаем это своим кодом):
Для Каждого ЭлементСоответствия Из СоответствиеСсылок Цикл
СтараяСсылка = ЭлементСоответствия.Ключ;
НоваяСсылка = ЭлементСоответствия.Значение;
// Находим все места использования старой ссылки
МассивСсылок = Новый Массив;
МассивСсылок.Добавить(СтараяСсылка);
ТаблицаСсылок = НайтиПоСсылкам(МассивСсылок);
Для Каждого СтрокаТЗ Из ТаблицаСсылок Цикл
ОбъектДанных = СтрокаТЗ.Данные;
// Внимание: здесь нужен универсальный код, который умеет менять ссылки
// в разных типах объектов (справочники, документы, наборы записей)
// Обычно для этого используется готовая библиотека или БСП (ОбщегоНазначения.ЗаменитьСсылки)
КонецЦикла;
КонецЦикла;
Настоятельно рекомендуем использовать готовую обработку «Поиск и замена значений», так как она корректно обрабатывает транзакции, наборы записей регистров и исключительные ситуации.
После того как мы убедились, что ссылок на объекты расширения не осталось (можно проверить тем же методом НайтиПоСсылкам или удалением с контролем ссылочной целостности), выполним завершающие действия:
СправочникОсн в нужное нам имя (если использовали временное).Существует "полуавтоматический" способ, который часто используют для больших объемов данных, чтобы избежать написания кода переноса реквизитов вручную.
Этот метод позволяет перенести данные с сохранением GUID, но не отменяет необходимости замены ссылок в связанных объектах.
CatalogRef.Расширение1_Номенклатура на CatalogRef.Номенклатура (или как называется ваш новый справочник).
Также меняем теги описания типов в начале файла, если они есть.Что мы получили? Мы получили элементы в основном справочнике с теми же самыми GUID. Решена ли проблема ссылок? Нет. Как мы выяснили ранее, регистры хранят идентификатор типа. Они все еще ссылаются на "Тип Расширения".
Теперь нам снова нужно запустить обработку «Поиск и замена значений». Настраиваем её так:
Преимущество этого метода в том, что нам не нужно писать код сопоставления реквизитов — XML загрузит всё один-в-один.
При выполнении данной операции учтем несколько важных нюансов:
1. Предопределенные элементы
Если в справочнике были предопределенные элементы, их необходимо создать в конфигураторе основной базы до начала переноса данных. Имена предопределенных элементов должны совпадать. При загрузке или переносе нужно следить, чтобы ссылки синхронизировались корректно.
2. Настройки пользователей и Варианты отчетов
Это самая болезненная часть. Настройки вариантов отчетов (СКД), сохраненные настройки форм, "Избранное" пользователей часто хранят ссылки в сериализованном виде или во внутренних хранилищах настроек.
При удалении старого справочника и создании нового, эти настройки "слетят". Пользователи увидят пустые поля в отборах или сообщение "Объект не найден".
Восстановить их программно крайне сложно, так как формат хранилищ настроек бинарный или XML-специфичный. Обычно пользователей предупреждают о необходимости перенастроить свои варианты отчетов.
3. Журнал регистрации
Массовая замена ссылок приведет к огромному количеству записей в Журнале Регистрации, так как каждый документ и набор записей будет перезаписан. Учитывайте это при планировании свободного места на диске и времени выполнения операции.
4. Тестирование и исправление
Никогда не запускайте "Тестирование и исправление" с галочкой "Очищать ссылки на несуществующие объекты" до того, как выполните замену ссылок. Если вы удалите расширение, и ссылки станут "битыми", ТиИ просто очистит данные в регистрах (например, удалит номенклатуру из приходных накладных), что приведет к потере данных учета. Для точечного контроля и ручной правки реквизитов или движений можно использовать Универсальный редактор данных (УРД).
Попытка обмануть платформу простым копированием GUID не сработает из-за архитектуры составного типа "Ссылка" (Тип + GUID). Единственный надежный путь:
Этот путь может показаться трудоемким, но он гарантирует целостность вашей базы данных.