Почему метод УстановитьСсылкуНового() не работает и как правильно сохранить свой GUID при записи объекта в 1С?

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

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

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

Как должен работать метод УстановитьСсылкуНового() в идеальных условиях

Для начала вспомним базовую теорию. Когда мы создаем новый объект в памяти (например, документ), у него еще нет физической ссылки в базе данных. Метод УстановитьСсылкуНового() позволяет заранее зарезервировать конкретную ссылку для этого объекта. Ссылка связывается с объектом в памяти, и в момент транзакции записи платформа использует именно её.

Типичный классический шаблон кода выглядит следующим образом:


// Шаг 1. Получаем GUID из строки источника
ДокументГуид = Новый УникальныйИдентификатор(ДокументГуидСтрока);

// Шаг 2. Формируем ссылку нужного типа по этому GUID
ДокументПоГуиду = Документы[Тип].ПолучитьСсылку(ДокументГуид);        

// Шаг 3. Создаем новый объект документа в памяти
ТекущийДокумент = Документы[Тип].СоздатьДокумент();

// Шаг 4. Устанавливаем объекту зарезервированную ссылку
ТекущийДокумент.УстановитьСсылкуНового(ДокументПоГуиду);

// Шаг 5. Заполняем реквизиты и записываем объект
ТекущийДокумент.Записать();

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


ЗарезервированныйГуид = ТекущийДокумент.ПолучитьСсылкуНового().УникальныйИдентификатор();
Сообщить(ЗарезервированныйГуид); // Здесь мы увидим наш исходный GUID

Но почему же после вызова метода ТекущийДокумент.Записать() в базе данных всё равно оказывается объект с другим, сгенерированным платформой GUID? Давайте разбираться.

Причина 1: Прикладной код в обработчиках событий (Ограничения БСП и типовых конфигураций)

Основной виновник того, что метод УстановитьСсылкуНового() «не работает», — это прикладной код самих конфигураций (особенно разработанных на базе Библиотеки стандартных подсистем — БСП). Очень часто в модулях объектов (например, справочника Сотрудники или различных документов в БП 3.0, УТ 11 и других решениях) в предопределенном обработчике события ПередЗаписью прописана логика генерации или синхронизации ссылок.

Проанализируем пример реального кода из обработчика ПередЗаписью справочника сотрудников в типовой «Бухгалтерии предприятия»:


Если НЕ ЗначениеЗаполнено(ГоловнойСотрудник) Тогда
    СсылкаОбъекта = Неопределено;
    Если ДополнительныеСвойства.Свойство("СсылкаНового") Тогда
        СсылкаОбъекта = ДополнительныеСвойства.СсылкаНового;
        УстановитьСсылкуНового(СсылкаОбъекта);
    ИначеЕсли ЗначениеЗаполнено(Ссылка) Тогда
        СсылкаОбъекта = Ссылка;
    Иначе
        СсылкаОбъекта = Справочники.Сотрудники.ПолучитьСсылку();
        УстановитьСсылкуНового(СсылкаОбъекта);
    КонецЕсли; 
    
    ГоловнойСотрудник = СсылкаОбъекта;
КонецЕсли;

Что здесь происходит? Разработчики типового решения жестко переопределяют ссылку нового элемента прямо в процессе записи. Они проверяют наличие специального реквизита в структуре ДополнительныеСвойства. Если этого свойства нет, платформа принудительно генерирует абсолютно новую пустую ссылку через метод ПолучитьСсылку() без параметров и переустанавливает её вызовом УстановитьСсылкуНового(СсылкаОбъекта). В результате ваш ранее установленный GUID просто затирается системным кодом конфигурации!

Решение проблемы подмены GUID в типовых конфигурациях

Для того чтобы обойти это поведение прикладного кода, у нас есть два надежных пути.

Способ 1. Использование флага загрузки обмена данными

Если мы выполняем интеграцию или перенос данных, самым правильным и безопасным решением будет установка свойства ОбменДанными.Загрузка в значение Истина. Это отключает выполнение большинства тяжелых прикладных проверок в обработчиках событий конфигурации, включая автоматическую перегенерацию ссылок:


ТекущийДокумент = Документы[Тип].СоздатьДокумент();
ТекущийДокумент.УстановитьСсылкуНового(ДокументПоГуиду);

// Устанавливаем режим загрузки данных
ТекущийДокумент.ОбменДанными.Загрузка = Истина;

ТекущийДокумент.Записать();

Этот способ является стандартом при написании правил обмена в КД (Конвертация данных) и внешних обработках загрузки. Он гарантирует, что установленный GUID не будет заменен логикой конфигурации.

Способ 2. Передача ссылки через ДополнительныеСвойства

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


ТекущийОбъект = Справочники.Сотрудники.СоздатьЭлемент();
СсылкаПоУиду = Справочники.Сотрудники.ПолучитьСсылку(УИД);

// Резервируем ссылку на уровне платформы
ТекущийОбъект.УстановитьСсылкуНового(СсылкаПоУиду);

// Передаем ссылку прикладной логике БСП
ТекущийОбъект.ДополнительныеСвойства.Вставить("СсылкаНового", СсылкаПоУиду);

ТекущийОбъект.Записать();

В этом случае обработчик ПередЗаписью успешно обнаружит свойство СсылкаНового в коллекции дополнительных свойств, не станет затирать ваш идентификатор и запишет объект с нужным GUID.

Как правильно проверять существование ссылки перед созданием нового объекта

При проектировании загрузки данных важно корректно разделять логику: если объект с таким GUID уже существует в базе, мы должны его обновить (модифицировать), а если не существует — создать новый с этим GUID. Рассмотрим, как делать это оптимально.

Важно: Никогда не используйте конструкцию проверки существования через попытку получения объекта, например:

Если ПромДок.ПолучитьОбъект() = Неопределено Тогда...

Метод ПолучитьОбъект() считывает из базы данных всю информацию об объекте, включая огромные табличные части. Если у вас тысячи документов с сотнями строк в табличных частях, этот метод приведет к колоссальной нагрузке на оперативную память сервера 1С и СУБД. Также не стоит использовать конструкцию преобразования ссылки в строку для проверки на "<Объект не найден>".

Рассмотрим два правильных варианта проверки существования объекта в базе.

Вариант А. Использование функций БСП (если конфигурация стандартная)

В современных конфигурациях есть готовая и высокооптимизированная функция общего назначения:


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

Вариант Б. Быстрый запрос (универсальный способ)

Если вы пишете код в самописной конфигурации без БСП, выполните легковесный запрос. Он проверит наличие записи в таблице БД без считывания самих данных:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ Первые 1
    |   Ссылка
    |ИЗ
    |   Документ.РеализацияТоваровУслуг КАК Реализация
    |ГДЕ
    |   Реализация.Ссылка = &Ссылка";

Запрос.УстановитьПараметр("Ссылка", ДокументПоГуиду);
РезультатЗапроса = Запрос.Выполнить();

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

Полезные советы по работе с GUID при обменах данными

  1. Контролируйте формат GUID из внешних систем. Обратите внимание, что СУБД (например, MS SQL Server) и платформа «1С:Предприятие» по-разному хранят порядок байтов в уникальных идентификаторах. Если вы напрямую считываете GUID из сторонней базы данных SQL, перед передачей его в 1С необходимо программно привести его к стандарту 1С, иначе вы запишете искаженную ссылку.
  2. Помните о фрагментации индексов. Массовая генерация абсолютно случайных GUID во внешней системе и их последующая запись в 1С через УстановитьСсылкуНового() нарушает последовательность физического расположения записей на диске СУБД. Это вызывает сильную фрагментацию кластерного индекса таблицы. Если вы загружаете миллионы записей со случайными GUID, обязательно запланируйте регламентную процедуру реиндексации базы данных на SQL-сервере.
  3. Используйте возможности новых платформ. Начиная с версии платформы 1С 8.3.22, вы можете получать уникальный идентификатор прямо в тексте запроса с помощью функции УникальныйИдентификатор(Ссылка). Это позволяет эффективно анализировать GUID и сопоставлять таблицы без перебора объектов в цикле.

Резюмируя наш разбор: если вы столкнулись с тем, что метод УстановитьСсылкуНового() сбрасывает GUID после записи объекта — обязательно проверьте, не переопределяется ли ссылка прикладным кодом в модуле объекта. Используйте режим загрузки данных ОбменДанными.Загрузка = Истина или передавайте ссылку через ДополнительныеСвойства, и ваши обмены данными будут работать стабильно и предсказуемо!

← На главную