Свой уникальный идентификатор для новой ссылки: приёмы для разработчика 1С

· 9 мин

Содержание

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

Типичная ситуация: из внешней системы (сайт, WMS, старая база) приходит 3 000 позиций номенклатуры со своими GUID. Нужно загрузить их так, чтобы ссылки в 1С совпали с внешними ключами, и повторная загрузка не плодила дубли. Ниже — как это сделать во встроенном языке, через COM/OLE и из PowerShell, и как потом проверить результат.

Что такое уникальный идентификатор ссылки в 1С

У каждого объекта ссылочного типа — справочника, документа, плана видов характеристик, плана счетов, бизнес-процесса, задачи — есть ссылка. Внутри ссылки лежит 128-битный UUID. В базе он хранится в поле _IDRRef типа binary(16).

Что из этого следует на практике:

  • 🔑 УИД уникален в пределах одной таблицы объекта. Два справочника могут случайно иметь одинаковый GUID, и это не конфликт.
  • 🔗 Все ссылки на объект в других таблицах хранят этот же UUID. Поменять его «на месте» значит оборвать все связи.
  • 🧩 У регистров сведений и накопления ссылок нет, им GUID не установить.
  • 📌 Значения перечислений получают идентификаторы в конфигураторе, из кода их не задают.

Строковое представление получают так: Строка(Ссылка.УникальныйИдентификатор()). Результат — 36 символов вида a1b2c3d4-0000-1111-2222-333344445555.

Пустая ссылка — это ссылка с нулевым UUID 00000000-0000-0000-0000-000000000000. Установить такой GUID новому объекту нельзя, получится пустое значение.

Как установить GUID новому объекту во встроенном языке

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

  1. Создать объект через менеджер: Справочники.Номенклатура.СоздатьЭлемент().
  2. Построить ссылку из строки GUID: Справочники.Номенклатура.ПолучитьСсылку(Новый УникальныйИдентификатор(СтрокаGUID)).
  3. Вызвать Объект.УстановитьСсылкуНового(Ссылка).
  4. Заполнить реквизиты и записать.

Метод ПолучитьСсылку() не проверяет, есть ли объект в базе. Он просто собирает ссылку. Поэтому перед созданием нужно проверить, не занят ли этот УИД.

Функция ЗагрузитьЭлементСGUID(СтрокаGUID, Наименование) Экспорт

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

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

    Объект.Наименование = Наименование;
    Объект.Записать();

    Возврат Объект.Ссылка;

КонецФункции

Такой код идемпотентен: первый запуск создаст элемент, второй обновит его, а не сделает дубль.

Где можно вызвать метод

  • ✅ На сервере, в серверном общем модуле или модуле объекта.
  • ✅ В обработчике ПередЗаписью нового объекта, если нужно подменить ссылку по правилу.
  • ❌ На клиенте прикладные объекты недоступны, вызывайте серверную процедуру.

Если ссылка нужна раньше записи

Бывает, что ссылку нового объекта нужно подставить в табличную часть другого документа ещё до записи. Есть два пути:

  • задать GUID самому, как выше;
  • вызвать Справочники.Номенклатура.ПолучитьСсылку() без параметра. Платформа выдаст новую уникальную ссылку, её и передают в УстановитьСсылкуНового().

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

Строка, не похожая на GUID (лишний символ, пробел, фигурные скобки), вызовет исключение при Новый УникальныйИдентификатор(...). При массовой загрузке оборачивайте разбор в Попытка ... Исключение и пишите плохие строки в журнал.

Установка УИД через внешнее соединение COM и OLE

Когда загрузку делает внешняя программа, к базе подключаются по COM. Для 8.3 есть два варианта.

СпособProgIDЧто запускаетсяКогда брать
Внешнее соединениеV83.COMConnectorБез интерфейса, код работает как на сервереПакетные загрузки, скрипты, планировщик
OLE AutomationV83.ApplicationПолноценный клиент 1СРедко: когда нужен именно клиентский сеанс

Для установки GUID почти всегда берут V83.COMConnector: он легче и не требует открывать окно программы.

Подготовка

  • 🧱 Разрядность внешней программы должна совпадать с разрядностью установленной платформы. 64-битный скрипт не увидит 32-битный comcntr.dll.
  • 📝 Если ProgID не находится, зарегистрируйте компоненту от имени администратора: regsvr32 "C:\Program Files\1cv8\<версия>\bin\comcntr.dll".
  • 👤 Пользователю 1С, под которым идёт подключение, нужно право «Внешнее соединение» и права на запись в нужный справочник.

Логика вызова

Через COM объект УникальныйИдентификатор создают не оператором Новый, а методом соединения NewObject():

  1. Соединение = Новый COMОбъект("V83.COMConnector").Connect(СтрокаПодключения).
  2. УИД = Соединение.NewObject("УникальныйИдентификатор", СтрокаGUID).
  3. Ссылка = Соединение.Справочники.Номенклатура.ПолучитьСсылку(УИД).
  4. Дальше — тот же СоздатьЭлемент(), УстановитьСсылкуНового(), Записать().

Строка подключения для файловой базы: File="C:\Bases\Trade";Usr="Загрузка";Pwd="***";. Для клиент-серверной: Srvr="srv1c";Ref="trade";Usr="Загрузка";Pwd="***";.

Скрипт PowerShell для установки GUID

PowerShell работает с тем же V83.COMConnector. Ниже — загрузка из CSV с колонками guid;name.

# Сохраните файл в UTF-8 с BOM, иначе кириллические имена объектов не распознаются
$connector = New-Object -ComObject V83.COMConnector
$conn = $connector.Connect('File="C:\Bases\Trade";Usr="Загрузка";Pwd="secret";')

$rows = Import-Csv -Path 'C:\Exchange\items.csv' -Delimiter ';'

foreach ($row in $rows) {
    try {
        $uuid = $conn.NewObject('УникальныйИдентификатор', $row.guid)
        $ref  = $conn.Справочники.Номенклатура.ПолучитьСсылку($uuid)
        $obj  = $ref.ПолучитьОбъект()

        if ($obj -eq $null) {
            $obj = $conn.Справочники.Номенклатура.СоздатьЭлемент()
            $obj.УстановитьСсылкуНового($ref)
        }

        $obj.Наименование = $row.name
        $obj.Записать()
        Write-Host "OK   $($row.guid)"
    }
    catch {
        Write-Host "FAIL $($row.guid): $($_.Exception.Message)"
    }
}

[System.Runtime.InteropServices.Marshal]::ReleaseComObject($conn) | Out-Null

Что учесть:

  • 🐢 Каждое обращение через COM — межпроцессный вызов. 10 000 элементов идут минуты, а не секунды. Для больших объёмов выгоднее передать файл целиком и разобрать его серверной процедурой общего модуля, вызвав её одним COM-вызовом.
  • 🔒 Пароль в скрипте храните вне репозитория, например в переменной окружения.
  • 🧹 Освобождайте COM-объект в конце, иначе сеанс 1С повиснет до закрытия процесса PowerShell.

Если кириллица в именах свойств мешает, используйте английские синонимы встроенного языка: Catalogs, GetRef, CreateItem, SetNewObjectRef, Write. Имя самого справочника (Номенклатура) остаётся как в конфигурации.

Пакетный вариант: один вызов вместо тысяч

Быстрее всего вынести цикл в 1С. Создайте общий модуль с флагами «Сервер» и «Внешнее соединение» и экспортной функцией, которая принимает текст файла целиком. Внутри — та же функция ЗагрузитьЭлементСGUID() для каждой строки.

Тогда скрипт делает один вызов:

$text = Get-Content -Path 'C:\Exchange\items.csv' -Raw -Encoding UTF8
$result = $conn.ЗагрузкаGUID.ЗагрузитьИзТекста($text)
Write-Host $result

Функция может вернуть строку-отчёт: сколько создано, сколько обновлено, какие GUID не прошли разбор. На тысячах строк такой вариант работает заметно быстрее поштучного цикла, потому что межпроцессный вызов один.

GUID при обмене данными и передаче через временное хранилище

Обмен через XML

При выгрузке ссылочного значения стандартными средствами (ЗаписатьXML, СериализаторXDTO) ссылка сериализуется строкой GUID. При чтении ПрочитатьXML платформа восстанавливает ссылку с тем же УИД. Если объекта в базе-приёмнике нет, получится «битая» ссылка — до тех пор, пока объект не загрузят.

Поэтому при загрузке через ПрочитатьXML объект создаётся сразу с исходным идентификатором. Перед записью ставят Объект.ОбменДанными.Загрузка = Истина, чтобы не срабатывали проверки и подписки на события, которые в загрузке не нужны.

Режим Загрузка отключает проверки заполнения и часть бизнес-логики. Включайте его только в коде обмена, а не в обычной записи.

Временное хранилище

Временное хранилище само по себе GUID не устанавливает, но пригодится для двух задач:

  • 📦 передать с клиента на сервер таблицу «GUID → данные» одним вызовом: ПоместитьВоВременноеХранилище(Таблица, УникальныйИдентификатор), а на сервере — ПолучитьИзВременногоХранилища(Адрес);
  • ⏳ держать загрузку в фоновом задании и отдавать форме результат по адресу.

Строки GUID в таблице передавайте как строки, а не как ссылки: ссылки из чужой базы в хранилище не перенести.

Сопоставление по внешнему ключу без смены УИД

Не всегда разумно делать ссылку в 1С равной внешнему ключу. Альтернатива — хранить внешний GUID в отдельном реквизите (тип «Строка» длиной 36) и искать по нему. Сравним:

ПодходПлюсыМинусы
УстановитьСсылкуНовогоПоиск мгновенный, по ссылке; совпадение ключей в обеих системахРаботает только при создании; конфликт, если у разных внешних систем GUID совпадут
Реквизит «Внешний ID»Можно добавить к существующим данным; несколько систем — несколько реквизитовНужен индекс; ещё один реквизит в форме

Предопределённые элементы и их идентификаторы

С предопределёнными элементами справочников и планов счетов нужно быть осторожнее.

  • 🏷️ Предопределённый элемент определяется не GUID, а значением свойства ИмяПредопределенныхДанных. Код обращается к нему по имени: Справочники.ВидыКонтактнойИнформации.EmailКонтрагента.
  • 🗂️ Платформа создаёт такие элементы в каждой базе сама, поэтому в двух базах одной конфигурации у «одного и того же» предопределённого элемента обычно разные УИД.
  • 🔄 При обмене предопределённые элементы сопоставляют по имени, а не по ссылке. Если загрузить элемент с чужим GUID и тем же ИмяПредопределенныхДанных, в базе окажутся два элемента с одним именем. Платформа сообщит об ошибке неуникальности предопределённых данных при обращении к ним.

Как поступать:

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

Если дубль предопределённого уже появился, снимите ИмяПредопределенныхДанных у лишнего элемента (присвойте пустую строку) и перенесите ссылки на основной. Удалять лишний элемент сразу нельзя, пока на него ссылаются документы.

Как проверить, что GUID установлен правильно

После загрузки сверьте результат по трём признакам.

  1. Ссылка совпадает с исходной строкой. Строка(Ссылка.УникальныйИдентификатор()) = СтрокаGUID — регистр букв в 1С нижний, приводите внешнюю строку через НРег().
  2. Объект существует. Ссылка.ПолучитьОбъект() <> Неопределено. Если вернулось Неопределено, ссылка «битая», в формах она видна как <Объект не найден> (...) с идентификатором в скобках.
  3. Нет дублей по наименованию или коду. Запросом сгруппируйте элементы по наименованию с отбором КОЛИЧЕСТВО(*) > 1.

Быстрая проверка из конфигуратора или консоли запросов:

Ссылка = Справочники.Номенклатура.ПолучитьСсылку(
    Новый УникальныйИдентификатор("a1b2c3d4-0000-1111-2222-333344445555"));
Сообщить(?(Ссылка.ПолучитьОбъект() = Неопределено, "Нет в базе", Ссылка.Наименование));

Как GUID выглядит в навигационной ссылке и в SQL

В навигационной ссылке (e1cib/data/Справочник.Номенклатура?ref=...) и в поле _IDRRef идентификатор записан 32 hex-символами без дефисов, а группы переставлены. GUID aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee хранится как dddd + eeeeeeeeeeee + cccc + bbbb + aaaaaaaa. Это удобно для поиска объекта по ссылке из журнала регистрации или SQL-трассы.

Не меняйте _IDRRef прямым SQL-запросом. Ссылки в табличных частях, регистрах и данных других объектов останутся старыми, база будет нарушена, а такие изменения — повод для отказа в поддержке.

Диагностика: выберите симптом

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

Можно ли поменять GUID у уже существующего элемента?
Средствами платформы нет. Путь один: создать новый элемент с нужным УИД, перенести на него ссылки (например, обработкой поиска и замены ссылок) и пометить старый на удаление.
Сгенерирует ли 1С GUID, совпадающий с моим?
Практически нет: платформа генерирует случайные UUID, вероятность совпадения ничтожна. Конфликт реален только если вы сами загрузили одинаковые GUID из разных систем.
Подходит ли УстановитьСсылкуНового для документов?
Да, порядок тот же: Документы.<Имя>.СоздатьДокумент(), затем ПолучитьСсылку(УИД) и УстановитьСсылкуНового() до первой записи.
Как получить новый GUID в 1С, не создавая объект?
Строка(Новый УникальныйИдентификатор) вернёт случайный идентификатор. Его можно сохранить и позже использовать для ссылки.
Работает ли установка GUID в расширении конфигурации?
Да, для объектов, собственных для расширения, и для заимствованных справочников и документов: код расширения вызывает те же методы менеджера и объекта.