При обмене данными между несколькими информационными базами или внешними сервисами иногда возникает критическая ситуация: уникальный идентификатор (GUID / UUID) нового документа совпадает с идентификатором ранее созданного или уже выгруженного объекта. Разберем, почему в платформе 1С нельзя просто отредактировать свойство Ссылка у существующего объекта, какими способами можно безопасно сменить идентификатор и как предотвратить подобные коллизии в будущем.
Проанализируем архитектурные ограничения платформы 1С:Предприятие:
ЭтоНовый() = Ложь) свойство Ссылка доступно строго только для чтения.УстановитьСсылкуНового() работает исключительно до первого сохранения объекта в информационную базу._IDRRef (соответствующее ссылке) входит в уникальный кластерный индекс, а также выступает внешним ключом для всех подчиненных табличных частей и движений по регистрам.Рассмотрим основной и самый надежный способ смены идентификатора средствами платформы 1С. Идея заключается в создании точной копии документа с новым GUID, копировании его движений, замене ссылок по всей базе данных и последующем удалении исходного документа.
Разберем пошаговый алгоритм выполнения этой операции:
УстановитьСсылкуНового().ОбменДанными.Загрузка = Истина, чтобы избежать повторного выполнения логики проведения и проверок заполнения.Посмотрим на пример программной реализации такого переноса:
// Получаем исходный документ для замены
СтарыйДокументОбъект = СтараяСсылка.ПолучитьОбъект();
// Генерируем новый GUID и получаем ссылку
НовыйГУИД = Новый УникальныйИдентификатор();
НоваяСсылка = Документы[СтараяСсылка.Метаданные().Имя].ПолучитьСсылку(НовыйГУИД);
// Создаем документ-клон и устанавливаем ему новую ссылку
НовыйДокументОбъект = Документы[СтараяСсылка.Метаданные().Имя].СоздатьДокумент();
НовыйДокументОбъект.УстановитьСсылкуНового(НоваяСсылка);
// Заполняем реквизиты и табличные части
НовыйДокументОбъект.Заполнить(СтарыйДокументОбъект);
НовыйДокументОбъект.Дата = СтарыйДокументОбъект.Дата;
НовыйДокументОбъект.Номер = СтарыйДокументОбъект.Номер;
НовыйДокументОбъект.Проведен = СтарыйДокументОбъект.Проведен;
НовыйДокументОбъект.ПометкаУдаления = СтарыйДокументОбъект.ПометкаУдаления;
Для Каждого ТЧ Из СтарыйДокументОбъект.Метаданные().ТабличныеЧасти Цикл
НовыйДокументОбъект[ТЧ.Имя].Загрузить(СтарыйДокументОбъект[ТЧ.Имя].Выгрузить());
КонецЦикла;
// Записываем новый документ без проверок
НовыйДокументОбъект.ОбменДанными.Загрузка = Истина;
НовыйДокументОбъект.Записать(?(НовыйДокументОбъект.Проведен, РежимЗаписиДокумента.Проведение, РежимЗаписиДокумента.Запись));
// Переносим движения по всем регистрам
Для Каждого Движение Из СтарыйДокументОбъект.Движения Цикл
Движение.Прочитать();
Если Движение.Количество() > 0 Тогда
НаборНового = НовыйДокументОбъект.Движения[Движение.Метаданные().Имя];
НаборНового.Очистить();
Для Каждого Запись Из Движение Цикл
ЗаполнитьЗначенияСвойств(НаборНового.Добавить(), Запись);
КонецЦикла;
НаборНового.ОбменДанными.Загрузка = Истина;
НаборНового.Записать();
КонецЕсли;
КонецЦикла;
// Выполняем замену ссылок в базе данных
МассивСтарых = Новый Массив;
МассивСтарых.Добавить(СтараяСсылка);
ТаблицаПоиска = НайтиПоСсылкам(МассивСтарых);
Для Каждого Стр Из ТаблицаПоиска Цикл
ОбъектДляЗамены = Стр.Данные.ПолучитьОбъект();
Если ОбъектДляЗамены <> Неопределено И ОбъектДляЗамены.Ссылка <> НоваяСсылка Тогда
ОбъектДляЗамены.ОбменДанными.Загрузка = Истина;
// Заменяем значение ссылки в реквизите или строке ТЧ
// и перезаписываем зависимый объект
ОбъектДляЗамены.Записать();
КонецЕсли;
КонецЦикла;
// Удаляем старый документ
СтарыйДокументОбъект.ОбменДанными.Загрузка = Истина;
СтарыйДокументОбъект.Удалить();
Для интерактивной замены ссылок также можно воспользоваться стандартными инструментами, такими как типовая обработка «Поиск и замена значений» или функционал подсистемы управления ссылками в специализированных консолях разработчика.
Прямое обновление поля _IDRRef в таблицах Microsoft SQL Server или PostgreSQL технически возможно, но сопряжено с максимальным уровнем риска для целостности базы данных. Разберем, с какими сложностями придется столкнуться:
binary(16) со специфической перестановкой байтов (Endianness). Простое преобразование стандартной строки UUID в бинарный вид не совпадет с форматом 1С, что приведет к повреждению ссылочной целостности._IDRRef / _DocumentX_IDRRef не только в таблице _DocumentX, но и во всех подчиненных табличных частях _DocumentX_VT..., во всех таблицах регистров накопления, бухгалтерии, сведений и расчета, где данный документ выступает регистратором или реквизитом._NodeX_....ragent) и полное выполнение процедуры «Тестирование и исправление» с пересчетом итогов.Важно: Прямая модификация таблиц СУБД снимает информационную базу с официальной поддержки и допустима только квалифицированными администраторами на предварительно снятой резервной копии.
Выясним причину, почему вообще происходит дублирование GUID. Как правило, коллизии возникают в базах-клонах (развернутых из одной резервной копии), при выгрузке/загрузке через XML без очистки ссылок либо при детерминированном вычислении GUID по простым алгоритмам. Если внутренний GUID документа в базе менять нежелательно, правильнее изменить подход к формированию идентификатора для внешних систем.
Рассмотрим альтернативные архитектурные решения:
СоответствиеВнешнихИдентификаторов) с измерениями Объект (тип ДокументСсылка) и ресурсом ВнешнийИдентификатор (тип Строка или УникальныйИдентификатор). При первичной выгрузке генерируем новый уникальный GUID, записываем связку в регистр и в дальнейшем берем идентификатор только из него.ПрефиксБазы + "_" + GUID, что гарантирует уникальность в разрезе источников данных.