При организации обмена данными между различными конфигурациями «1С:Предприятие» (например, из «1С:Управление торговлей» в «1С:Бухгалтерия предприятия») разработчики часто сталкиваются с проблемой появления дубликатов. Наиболее классический сценарий — наличие в базах-источниках и приемниках контрагентов с одинаковыми реквизитами ИНН и КПП, но имеющих разные уникальные идентификаторы (GUID). Из-за этого стандартные механизмы обмена не могут сопоставить объекты автоматически и создают дублирующие карточки.
В этой статье мы подробно проанализируем ситуацию, выясним причины такого поведения платформы и разберем по шагам все возможные методы решения проблемы несовпадения GUID: от безопасной настройки правил конвертации до программной подмены идентификаторов и низкоуровневых манипуляций.
В платформе «1С:Предприятие» свойство УникальныйИдентификатор у ссылки объекта базы данных доступно исключительно для чтения. Это базовая архитектурная особенность объектной модели 1С. Ссылка формируется один раз при создании элемента справочника или документа и не может быть изменена в процессе его жизненного цикла. Если бы разработчикам было разрешено напрямую менять GUID существующих элементов, это мгновенно разрушило бы ссылочную целостность всей базы данных, так как сотни документов и регистров потеряли бы связь со своими аналитическими разрезами.
Тем не менее, решить задачу сопоставления объектов с разными GUID технически возможно. Рассмотрим подробнее основные методы.
Если вы используете для обмена обработку «Универсальный обмен данными в формате XML» и правила, созданные в конфигурации «Конвертация данных 2.1», лучшим решением будет тонкая настройка полей поиска. Это позволит избежать изменения GUID в базах, но решит проблему дублирования на лету.
Проанализируем стандартную ошибку настройки: часто разработчики включают поиск по уникальному идентификатору, но забывают указать платформе, что делать, если по этому идентификатору объект не найден. В таком случае 1С просто создаст новый элемент с GUID источника, порождая дубликат.
Разберем по шагам, как настроить корректное сопоставление:
Контрагенты.ИНН, КПП, Наименование) установлены флаги «Поиск».
// Обработчик "Поля поиска" в ПКО Контрагенты
// Параметры: СвойстваПоиска, Ссылка, Найдено, ПрекратитьПоиск
Если НомерВариантаПоиска = 1 Тогда
ИНН_Входящий = СвойстваПоиска.Получить("ИНН");
КПП_Входящий = СвойстваПоиска.Получить("КПП");
Если ЗначениеЗаполнено(ИНН_Входящий) Тогда
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Контрагенты.Ссылка КАК Ссылка
|ИЗ
| Справочник.Контрагенты КАК Контрагенты
|ГДЕ
| Контрагенты.ИНН = &ИНН
| И (Контрагенты.КПП = &КПП ИЛИ &КПППустой)";
Запрос.УстановитьПараметр("ИНН", ИНН_Входящий);
Запрос.УстановитьПараметр("КПП", КПП_Входящий);
Запрос.УстановитьПараметр("КПППустой", Не ЗначениеЗаполнено(КПП_Входящий));
РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
Выборка = РезультатЗапроса.Выбрать();
Выборка.Следующий();
// Насильно сопоставляем найденный объект приемника
Ссылка = Выборка.Ссылка;
Найдено = Истина;
ПрекратитьПоиск = Истина;
КонецЕсли;
КонецЕсли;
КонецЕсли;
Использование этого метода гарантирует, что при загрузке данных из источника система найдет уже существующего в приемнике контрагента с тем же ИНН/КПП и обновит его реквизиты, не создавая копий с новыми GUID.
Если вам жизненно необходимо сделать так, чтобы у существующего контрагента в базе-приемнике GUID стал в точности равен GUID из базы-источника, придется использовать алгоритм полного пересоздания элемента с последующей заменой ссылок во всех документах базы данных.
Посмотрим на пример реализации такого алгоритма:
Процедура ЗаменитьИдентификаторКонтрагента(СтарыйКонтрагентСсылка, СтрокаНовогоGUID) Экспорт
// Шаг 1. Преобразуем строковое представление GUID в объектный тип
НовыйGUID = Новый УникальныйИдентификатор(СтрокаНовогоGUID);
// Шаг 2. Генерируем ссылку с нужным нам GUID
НоваяСсылка = Справочники.Контрагенты.ПолучитьСсылку(НовыйGUID);
// Проверим, нет ли уже в базе элемента с таким GUID
Если ЗначениеЗаполнено(НоваяСсылка.ПолучитьОбъект()) Тогда
ВызватьИсключение "Объект с GUID " + СтрокаНовогоGUID + " уже существует в базе!";
КонецЕсли;
// Шаг 3. Создаем новый элемент справочника и сопоставляем ему сгенерированную ссылку
НовыйОбъект = Справочники.Contragents.СоздатьЭлемент();
НовыйОбъект.УстановитьСсылкуНового(НоваяСсылка);
// Шаг 4. Копируем все реквизиты из старого контрагента
СтарыйОбъект = СтарыйКонтрагентСсылка.ПолучитьОбъект();
ЗаполнитьЗначенияСвойств(НовыйОбъект, СтарыйОбъект, , "Ссылка, Предопределенный");
// Копируем табличные части (если они есть)
Для Каждого ТЧ Из СтарыйОбъект.Метаданные().ТабличныеЧасти Цикл
ИмяТЧ = ТЧ.Имя;
НовыйОбъект[ИмяТЧ].Загрузить(СтарыйОбъект[ИмяТЧ].Выгрузить());
КонецЦикла;
// Записываем новый элемент с требуемым GUID
НовыйОбъект.Записать();
// Шаг 5. Поиск и замена старых ссылок на новые по всей базе данных
ТаблицаЗамены = Новый ТаблицаЗначений;
ТаблицаЗамены.Колонки.Добавить("ИсходноеЗначение");
ТаблицаЗамены.Колонки.Добавить("НовоеЗначение");
СтрокаЗамены = ТаблицаЗамены.Добавить();
СтрокаЗамены.ИсходноеЗначение = СтарыйКонтрагентСсылка;
СтрокаЗамены.НовоеЗначение = НоваяСсылка;
// Используем типовой механизм БСП для безопасной замены ссылок во всех метаданных
// Это исключит появление "битых" связей в документах и регистрах
ПараметрыЗамены = ОбщегоНазначенияКлиентСервер.СкопироватьТаблицу(ТаблицаЗамены);
ПоискИЗаменаЗначений.ЗаменитьСсылки(ПараметрыЗамены);
// Шаг 6. Удаляем старый элемент из информационной базы
СтарыйОбъект.Удалить();
ЗаписьЖурналаРегистрации("Синхронизация GUID",
УровеньЖурналаРегистрации.Информация,
Справочники.Контрагенты,
НоваяСсылка,
"Успешно произведена замена GUID для контрагента: " + НовыйОбъект.Наименование);
КонецПроцедуры
Важное предупреждение: Данная операция является ресурсоемкой, так как транзакция замены ссылок обходит абсолютно все таблицы базы данных, где может встречаться тип СправочникСсылка.Контрагенты. Выполнять эту процедуру рекомендуется в периоды минимальной активности пользователей.
Для быстрого исправления единичных объектов без написания масштабного кода замены ссылок можно использовать механизм XML-сериализации платформы 1С. Разберем этот лайфхак по шагам:
СериализаторXDTO.Среди разработчиков периодически обсуждается идея прямого изменения GUID через СУБД путем выполнения SQL-запроса к таблице справочника (например, к полю _IDRRef в таблице _ReferenceXX).
Проанализируем этот сценарий. Действительно, технически выполнить запрос класса UPDATE _ReferenceXX SET _IDRRef = &НовыйБинарныйGUID WHERE _IDRRef = &СтарыйБинарныйGUID не составляет труда. Однако делать это категорически запрещено по следующим причинам:
_DocumentXX), регистры сведений и накопления мгновенно потеряют связь с этим контрагентом. На месте контрагента в интерфейсе появится надпись <Объект не найден> (идентификатор...).Если в результате экспериментов вы все-таки удалили объект, не перенаправив ссылки, и в документах отображается надпись <Объект не найден> (84:bf5600145e3710ab11dda4c605dbe824), вы можете восстановить поврежденную ссылочную связь. Для этого достаточно извлечь из текстового представления битой ссылки шестнадцатеричный GUID и программно воссоздать пустой элемент с этим идентификатором.
Посмотрим на пример функции восстановления:
Процедура ВосстановитьБитуюСсылкуКонтрагента(ИмяСправочника, ТекстБитойСсылки)
// Пример текста: "<Объект не найден> (84:bf5600145e3710ab11dda4c605dbe824)"
ПозицияСкобки = Найти(ТекстБитойСсылки, "(");
Если ПозицияСкобки = 0 Тогда
Возврат;
КонецЕсли;
ОчищеннаяСтрока = Сред(ТекстБитойСсылки, ПозицияСкобки + 1, СтрДлина(ТекстБитойСсылки) - ПозицияСкобки - 1);
// Преобразуем формат 1С в стандартный вид GUID (добавляем дефисы)
// Шаблон: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
Часть1 = Сред(ОчищеннаяСтрока, 25, 8);
Часть2 = Сред(ОчищеннаяСтрока, 21, 4);
Часть3 = Сред(ОчищеннаяСтрока, 17, 4);
Часть4 = Сред(ОчищеннаяСтрока, 1, 4);
Часть5 = Сред(ОчищеннаяСтрока, 5, 12);
СтандартныйGUIDСтрока = Часть1 + "-" + Часть2 + "-" + Часть3 + "-" + Часть4 + "-" + Часть5;
Попытка
ЦелевойGUID = Новый УникальныйИдентификатор(СтандартныйGUIDСтрока);
ВосстановленнаяСсылка = Справочники[ИмяСправочника].ПолучитьСсылку(ЦелевойGUID);
Если Не ЗначениеЗаполнено(ВосстановленнаяСсылка.ПолучитьОбъект()) Тогда
НовыйЭлемент = Справочники[ИмяСправочника].СоздатьЭлемент();
НовыйЭлемент.УстановитьСсылкуНового(ВосстановленнаяСсылка);
НовыйЭлемент.Наименование = "[Восстановлен] " + ТекущаяДата();
НовыйЭлемент.Записать();
Сообщить("Успешно восстановлен элемент с GUID: " + СтандартныйGUIDСтрока);
Иначе
Сообщить("Элемент с таким GUID уже существует в базе данных!");
КонецЕсли;
Исключение
Сообщить("Не удалось разобрать GUID: " + ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
Мы детально рассмотрели проблему несовпадения уникальных идентификаторов при одностороннем обмене данными в «1С:Предприятии». Наиболее безопасным и архитектурно верным решением всегда является настройка правил сопоставления в «Конвертации данных». Это исключает необходимость физического изменения данных в базах и предотвращает появление дублей. К радикальным программным методам замены GUID стоит прибегать лишь в крайних случаях, строго соблюдая ссылочную целостность и используя проверенные транзакционные алгоритмы платформы 1С.