Случаи, когда уникальные идентификаторы (UUID/GUID) разных объектов в независимых базах 1С совпадают, кажутся математически маловероятными, однако на практике это реальная проблема. Особенно часто это происходит при клонировании баз или некорректном использовании механизмов генерации ссылок. Когда данные из нескольких таких баз-источников (например, нескольких ЗУП) стекаются в одну центральную базу (например, Документооборот или самописную конфигурацию), стандартный поиск по внутреннему идентификатору в Конвертации данных 2.0 (КД 2.0) дает сбой: система находит объект по GUID, считает его «тем самым» и перезаписывает данные, фактически уничтожая информацию об объекте из первой базы данными из второй.
В этой статье мы подробно разберем, почему возникает эта проблема и какие стратегии позволяют гарантированно разделить объекты с одинаковыми идентификаторами, используя механизмы КД 2.0 и Библиотеки стандартных подсистем (БСП).
Проанализируем ситуацию: в КД 2.0 в правилах конвертации объектов (ПКО) обычно включен флаг «Искать объект по внутреннему идентификатору». Алгоритм работы загрузчика в этом случае следующий:
Справочники.ФизическиеЛица.ПолучитьСсылку(Новый УникальныйИдентификатор(GUIDИзФайла)).Основная проблема заключается в том, что поиск по UUID имеет наивысший приоритет и «не знает» о существовании разных узлов обмена. Если в ЗУП №1 и ЗУП №2 созданы разные люди с одинаковым GUID, база-приемник их не отличит.
Если ваш обмен построен на базе БСП, то самым правильным решением будет использование стандартного регистра сведений СоответствиеОбъектовИнформационныхБаз. Рассмотрим подробнее, почему это работает. Данный регистр хранит связку не просто «GUID — Ссылка», а «УзелОбмена + GUIDИсточник — СсылкаПриемник».
Чтобы задействовать этот механизм и обойти прямое сопоставление по UUID, необходимо выполнить следующие действия:
Пример кода для обработчика «Поля поиска»:
// Проверим, не сопоставлен ли уже объект через стандартный регистр БСП
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Соответствие.СобственнаяСсылка
|ИЗ
| РегистрСведений.СоответствиеОбъектовИнформационныхБаз КАК Соответствие
|ГДЕ
| Соответствие.УзелИнформационнойБазы = &УзелОбмена
| И Соответствие.УникальныйИдентификаторИсточника = &ИдентификаторИсточника";
Запрос.УстановитьПараметр("УзелОбмена", ПараметрыОбъекта["УзелОбмена"]); // Узел должен передаваться в параметрах
Запрос.УстановитьПараметр("ИдентификаторИсточника", Строка(УникальныйИдентификатор));
РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
Выборка = РезультатЗапроса.Выбрать();
Выборка.Следующий();
СсылкаНаОбъект = Выборка.СобственнаяСсылка;
ПрекратитьПоиск = Истина;
КонецЕсли;
Если вы хотите избежать сложных запросов в обработчиках, можно пойти по пути расширения структуры данных. Разберем этот вариант по шагам:
ФизическиеЛица новый реквизит ВнешнийИдентификатор (тип: Строка, длина 36 или больше).ВнешнийИдентификатор.Важный нюанс: Чтобы гарантировать уникальность между разными базами, в ВнешнийИдентификатор лучше записывать связку ПрефиксБазы + "_" + GUID. Рассмотрим пример настройки в обработчике «Перед выгрузкой» в КД 2.0:
// Добавим префикс базы к идентификатору, чтобы исключить коллизии
ИмяУзла = Строка(ОбменДанными.УзелИнформационнойБазы);
Значение = ИмяУзла + "_" + Строка(Объект.Ссылка.УникальныйИдентификатор());
Теперь, даже если GUID совпадут, строки поиска будут отличаться за счет префикса базы-источника.
Проанализируем ситуацию, когда невозможно менять структуру базы-приемника. В этом случае мы можем полагаться на комбинацию полей, которая с высокой вероятностью уникальна для человека (СНИЛС, ИНН, ФИО + Дата рождения).
Однако, как заметил автор темы, поиск по нескольким полям может привести к «склейке» полных тезок. Чтобы этого избежать, мы можем программно формировать сложный ключ поиска. В обработчике «Поля поиска» мы можем управлять приоритетами. Рассмотрим алгоритм:
Пример реализации в КД 2.0 (событие «Поля поиска»):
Если НомерВариантаПоиска = 1 Тогда
Если ЗначениеЗаполнено(СвойстваПоиска["СтраховойНомерПФР"]) Тогда
СтрокаИменСвойствПоиска = "СтраховойНомерПФР";
Иначе
// Пропускаем этот вариант, если поле не заполнено
НомерВариантаПоиска = 2;
КонецЕсли;
КонецЕсли;
Если НомерВариантаПоиска = 2 Тогда
Если ЗначениеЗаполнено(СвойстваПоиска["ИНН"]) Тогда
СтрокаИменСвойствПоиска = "ИНН";
Иначе
НомерВариантаПоиска = 3;
КонецЕсли;
КонецЕсли;
Если НомерВариантаПоиска = 3 Тогда
СтрокаИменСвойствПоиска = "Наименование, ДатаРождения";
КонецЕсли;
Чтобы в будущем предотвратить подобные инциденты без постоянного ручного контроля, рекомендуется внедрить механизм сигнализации о дублях. Проанализируем, как это реализовать:
В момент загрузки, если система нашла объект по GUID, но при этом другие ключевые поля (например, ИНН или Фамилия) существенно отличаются от тех, что пришли в файле, мы должны не перезаписывать объект, а записывать информацию о конфликте в ЖурналРегистрации или создавать специальную задачу для администратора.
Для реализации такой проверки используйте обработчик ПКО «При загрузке»:
Если Не Объект.ЭтоНовый() Тогда
// Сравним ключевые реквизиты существующего объекта и пришедших данных
Если Объект.ИНН <> ПараметрыОбъекта["ИНН"] И ЗначениеЗаполнено(Объект.ИНН) Тогда
ОбщегоНазначения.СообщитьПользователю("Внимание! Обнаружено несовпадение ИНН при совпадении UUID. Возможно дублирование GUID в базах-источниках.");
// Здесь можно отменить загрузку или пометить объект для проверки
Отказ = Истина;
КонецЕсли;
КонецЕсли;
Подводя итог, отметим: полное доверие к UUID в распределенных системах — это архитектурный риск. Наиболее надежным методом решения является использование регистра соответствий с привязкой к узлу обмена или префиксация идентификаторов на этапе выгрузки. Это гарантирует, что каждый объект из каждой информационной базы сохранит свою уникальность в центральном узле.