УникальныйИдентификатор(): работа с GUID ссылок в 1С 8.3

· 9 мин

Содержание

Чтобы получить GUID объекта, вызовите у его ссылки метод Ссылка.УникальныйИдентификатор() — он вернёт значение типа УникальныйИдентификатор. Текстовую форму вида 0bf3e4a1-2c5d-11ee-8c3a-00155d010a12 даёт обёртка Строка(...). Обратная задача (есть строка — нужна ссылка) решается связкой Новый УникальныйИдентификатор(строка) и методом менеджера ПолучитьСсылку().

GUID — это внутренний 128-битный идентификатор, который платформа присваивает каждому объекту ссылочного типа (элементу справочника, документу, узлу плана обмена) в момент записи. Он не меняется никогда: ни при переименовании, ни при смене реквизитов, ни при переносе базы. Именно поэтому GUID удобен для внешних систем, обменов и ссылок в веб-сервисах — в отличие от кода или номера, которые можно изменить руками.

Ниже — все рабочие способы получить и преобразовать идентификатор, типовые грабли и ответы на вопросы, которые чаще всего всплывают при обмене данными.

Что такое идентификатор ссылки и чем он не является

Тип значения — УникальныйИдентификатор. Это не строка и не число, а отдельный примитивный тип платформы. Поэтому сравнивать его напрямую со строкой нельзя — сначала приводят к одному типу.

Важно развести два понятия:

  • 🔑 Идентификатор ссылки (GUID) — адрес конкретного объекта в базе. Получаем методом УникальныйИдентификатор().
  • 🔗 Навигационная ссылка (e1cib/...) — это другое. Её формирует ПолучитьНавигационнуюСсылку(), и к GUID она отношения не имеет.

У ссылочного типа идентификатор — это метод со скобками, а не свойство. Частая ошибка новичка — написать Ссылка.УникальныйИдентификатор без скобок и получить не значение, а сам метод.

Хранится идентификатор в СУБД как бинарное поле _IDRRef длиной 16 байт. Это пригодится, если вы читаете базу напрямую через SQL (об этом ниже есть важная оговорка про порядок байтов).

Не у всех объектов конфигурации есть GUID. Он существует только у ссылочных типов:

  • ✅ есть идентификатор: справочники, документы, планы видов характеристик, планы счетов, планы обмена, бизнес-процессы, задачи, перечисления;
  • ❌ нет идентификатора: регистры (сведений, накопления, бухгалтерии), константы, наборы записей.

Запись регистра адресуется не ссылкой, а набором измерений и периодом. Поэтому вопрос «как взять GUID строки регистра сведений» лишён смысла — такого идентификатора у записи регистра просто нет. Если нужна адресация строки регистра во внешней системе, собирайте ключ из значений измерений сами.

Прямой способ через метод ссылки

Это основной и самый быстрый путь. Работает для любого ссылочного значения — справочника, документа, узла плана обмена, счёта плана счетов.

// Есть ссылка на объект
УникИД = Контрагент.УникальныйИдентификатор();

// Если под рукой объект, а не ссылка — сначала берём ссылку
УникИД = ОбъектКонтрагента.Ссылка.УникальныйИдентификатор();

В типе УникальныйИдентификатор нет «пустого» состояния в привычном смысле: у заполненной ссылки идентификатор есть всегда. А вот у пустой ссылки (Справочники.Контрагенты.ПустаяСсылка()) идентификатор состоит из одних нулей: 00000000-0000-0000-0000-000000000000.

Отсюда полезная проверка — не пустая ли ссылка пришла:

Если Не Ссылка.Пустая() Тогда
    УникИД = Ссылка.УникальныйИдентификатор();
КонецЕсли;

Для предопределённых элементов идентификатор тоже доступен, но завязываться на него в коде не стоит: при некоторых сценариях обновления конфигурации GUID предопределённого элемента может пересоздаться. Обращайтесь к ним по имени через ПредопределённоеЗначение("Справочник.ВидыНоменклатуры.Услуга") — это надёжнее, чем хранить их GUID.

Как перевести идентификатор в строку и обратно

Для передачи наружу (в файл, в лог, в параметр запроса) GUID почти всегда нужен в виде строки. И наоборот — приняв строку, надо восстановить значение.

Значение → строка:

СтрокаGUID = Строка(УникИД);
// "0bf3e4a1-2c5d-11ee-8c3a-00155d010a12"

Строковая форма — 36 символов: 32 шестнадцатеричные цифры в нижнем регистре и 4 дефиса. Без фигурных скобок, без пробелов.

Строка → значение:

УникИД = Новый УникальныйИдентификатор("0bf3e4a1-2c5d-11ee-8c3a-00155d010a12");

Конструктор принимает строку как с дефисами, так и без них. Если формат битый — платформа выбросит исключение, поэтому входные данные из внешней системы стоит оборачивать в Попытка.

Есть и второй, эквивалентный способ сериализации — через XML-представление. Он удобен, когда вы и так работаете с ЗаписьXML/ЧтениеXML:

Текст    = XMLСтрока(УникИД);                              // в строку
УникИД2  = XMLЗначение(Тип("УникальныйИдентификатор"), Текст); // из строки

Сравнительная таблица, когда что применять:

СпособИз чего → во чтоКогда удобнее
Строка() / Новый УникальныйИдентификатор()значение ↔ строкаобычный код, параметры, логи
XMLСтрока() / XMLЗначение()значение ↔ строкавнутри XML-сериализации, обмены

А вот про «конвертацию в число» важно сказать прямо: привести GUID к типу Число нельзя. Идентификатор — это 128 бит, он не помещается в числовой тип платформы и в принципе не задумывался как число. Если во внешней системе идентификатор хранится как большое целое — договаривайтесь о передаче строкой, иначе потеряете точность.

Можно ли достать GUID запросом к базе

Короткий ответ: напрямую в языке запросов функции «дай мне GUID ссылки» нет. В запросе вы получаете саму ссылку, а её идентификатор вычисляете уже в коде:

Запрос = Новый Запрос;
Запрос.Текст =
    "ВЫБРАТЬ Ссылка ИЗ Справочник.Контрагенты
    |ГДЕ ИНН = &ИНН";
Запрос.УстановитьПараметр("ИНН", ИНН);
Выборка = Запрос.Выполнить().Выбрать();

Пока Выборка.Следующий() Цикл
    УникИД = Выборка.Ссылка.УникальныйИдентификатор();
КонецЦикла;

Обратная задача — найти объект по известному GUID — решается без перебора всей таблицы. Не стройте запрос с перебором и сравнением строк: сначала соберите ссылку, потом фильтруйте по ней.

УникИД  = Новый УникальныйИдентификатор(ВходящаяСтрока);
Ссылка  = Справочники.Контрагенты.ПолучитьСсылку(УникИД);

ПолучитьСсылку() не проверяет, что объект существует. Он просто собирает ссылку из идентификатора. Если объекта с таким GUID в базе нет, вы получите «битую» ссылку, которая выглядит рабочей, но указывает в пустоту.

Проверять существование нужно явно:

Если Ссылка.ПолучитьОбъект() = Неопределено Тогда
    Сообщить("Объекта с таким идентификатором в базе нет");
КонецЕсли;

Никогда не считайте, что ссылка из ПолучитьСсылку() ведёт на реальный объект, пока не проверили это явно. Пропуск этой проверки — причина многих ошибок при приёме данных извне.

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

Запрос = Новый Запрос("ВЫБРАТЬ Ссылка ИЗ Справочник.Контрагенты ГДЕ Ссылка В (&Ссылки)");
Запрос.УстановитьПараметр("Ссылки", МассивСсылок);
НайденныеСсылки = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка");

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

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

Передача идентификатора в HTTP-сервисах и JSON

При интеграции GUID — естественный ключ, по которому две системы договариваются об одном и том же объекте. Схема всегда одна: наружу отдаём строку, внутрь принимаем строку и восстанавливаем ссылку.

Отдаём объект в ответе HTTP-сервиса:

Ответ = Новый Структура;
Ответ.Вставить("id",           Строка(Ссылка.УникальныйИдентификатор()));
Ответ.Вставить("наименование", Ссылка.Наименование);

ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, Ответ);
ТелоОтвета = ЗаписьJSON.Закрыть();

Обратите внимание: идентификатор мы приводим к строке вручную. ЗаписатьJSON не сериализует тип УникальныйИдентификатор сам — если передать его как есть, получите либо ошибку, либо неожиданный формат. Приводите к Строка() заранее.

Принимаем запрос и находим объект:

Данные = ПрочитатьJSON(ЧтениеJSON); // {"id": "0bf3e4a1-..."}
Попытка
    УникИД = Новый УникальныйИдентификатор(Данные.id);
Исключение
    // некорректный формат идентификатора во входных данных
    ВызватьИсключение "Неверный формат id";
КонецПопытки;

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

Два правила, которые экономят время на отладке интеграций:

  • 📥 На приёме всегда оборачивайте разбор строки в Попытка — внешняя система пришлёт мусор рано или поздно.
  • 📤 На выдаче решите один раз, в каком регистре и с дефисами ли отдаёте GUID, и зафиксируйте это в описании API. Строковая форма 1С — нижний регистр с дефисами.

Один и тот же GUID на всех узлах РИБ

Это ключевая особенность распределённых информационных баз (РИБ), которую важно понимать.

В РИБ идентификатор объекта одинаков на всех узлах. Создали элемент в центральной базе — он приедет в периферийную с тем же самым GUID. Поэтому в РИБ синхронизация идёт именно по ссылкам: платформа узнаёт «тот же самый объект» по совпадению идентификаторов, без поиска по реквизитам.

Из этого следуют практические выводы:

  • GUID можно безопасно использовать как сквозной ключ между узлами РИБ — он гарантированно совпадает.
  • Ссылку, полученную в одном узле, корректно разобрать в другом через ПолучитьСсылку().

А вот при обычном обмене (не РИБ: через Конвертацию данных, EnterpriseData, свой формат) всё иначе. Там у каждой базы свои идентификаторы, и один объект в разных базах имеет разные GUID. Сопоставление ведётся по правилам обмена и полям поиска, а соответствие идентификаторов хранит механизм регистрации обмена. Поэтому:

Не передавайте «голый» GUID между базами, которые связаны обычным обменом, а не РИБ. В базе-получателе он будет указывать не на тот объект или в пустоту.

Если нужен единый идентификатор между произвольными базами — заводите собственный реквизит-ключ (например, код из учётной системы) и сопоставляйте по нему.

Типичные ошибки при работе с идентификатором

Самые частые грабли, которые всплывают на код-ревью и в поддержке:

  1. Метод без скобок. Ссылка.УникальныйИдентификатор вернёт не значение, а объект-метод. Правильно — со скобками.
  2. Сравнение GUID со строкой напрямую. Если УникИД = "0bf3..." Тогда всегда ложно: разные типы. Приводите обе стороны к Строка() или оба к УникальныйИдентификатор.
  3. Доверие ПолучитьСсылку() без проверки. Метод не проверяет существование объекта — проверяйте через ПолучитьОбъект() = Неопределено.
  4. Разный регистр и фигурные скобки. Внешние системы иногда шлют GUID в верхнем регистре или в фигурных скобках {...}. Конструктор 1С скобки не любит — очищайте строку перед разбором.
  5. Ставка на GUID предопределённых элементов. Он может пересоздаться при обновлении — используйте ПредопределённоеЗначение().
  6. Попытка «перевести в число». 128 бит в числовой тип не влезут — работайте только со строкой.

Небольшой тест, чтобы проверить, что всё улеглось:

Проверьте себя
Что вернёт `Ссылка.УникальныйИдентификатор` без скобок?
Можно ли корректно передать GUID объекта между двумя базами, связанными обычным обменом (не РИБ)?
Проверяет ли `ПолучитьСсылку()` существование объекта в базе?

Частые вопросы

Чем GUID отличается от кода или номера объекта?
Код и номер — реквизиты, их можно изменить вручную, и они уникальны только в пределах своего справочника или нумератора. GUID платформа присваивает автоматически, он неизменен и уникален в пределах базы (а в РИБ — и между узлами).
Как получить GUID по ссылке в консоли запросов?
Прямой функции в языке запросов нет. Выберите в запросе поле Ссылка, а идентификатор вычислите в коде обработки результата: Выборка.Ссылка.УникальныйИдентификатор().
Почему строка GUID из 1С не совпадает с тем, что я вижу в таблице SQL?
Платформа при показе меняет местами две 8-байтовые половины бинарного поля _IDRRef. Чтобы собрать строку из бинарника SQL, половины нужно переставить обратно. Прямое чтение таблиц вендором не поддерживается.
Можно ли по GUID узнать, какому типу объекта он принадлежит?
Нет. Сам по себе идентификатор типа не несёт — ПолучитьСсылку() вызывается у конкретного менеджера (Справочники.Контрагенты, Документы.РеализацияТоваровУслуг). Тип нужно знать или передавать отдельным полем.
GUID объекта меняется при переносе базы или обновлении конфигурации?
Нет. Идентификатор объекта данных сохраняется при выгрузке/загрузке .dt, резервном копировании и обновлении. Исключение — GUID предопределённых элементов, на который завязываться не стоит.