Ссылки на объекты 1С:Предприятия 8.3 — от УникальногоИдентификатора до запросов и форм

· 9 мин

Содержание

Ссылка в 1С — это значение, которое однозначно указывает на запись ссылочного объекта в базе: элемент справочника, документ, счёт, вид характеристики, задачу. Внутри неё лежат тип объекта и 16-байтный УникальныйИдентификатор. Само содержимое объекта (наименование, реквизиты, табличные части) в ссылке не хранится.

Частое заблуждение новичков: «ссылка — это сам элемент, раз я пишу Ссылка.Наименование». Нет. Каждое обращение через точку заставляет платформу прочитать объект из базы. Отсюда и большинство проблем с производительностью, и ошибки на клиенте управляемой формы. Поэтому ссылку стоит воспринимать как адрес: по нему можно сходить за данными, но каждый такой поход стоит запроса к СУБД.

Как устроена ссылка и какие объекты её имеют

Ссылку имеют только ссылочные типы метаданных. У регистров, констант и обработок её нет: запись регистра определяется набором измерений и периодом, а не идентификатором.

Объект метаданныхТип ссылки в кодеПример
СправочникСправочникСсылка.<Имя>СправочникСсылка.Номенклатура
ДокументДокументСсылка.<Имя>ДокументСсылка.РеализацияТоваровУслуг
План счетовПланСчетовСсылка.<Имя>ПланСчетовСсылка.Хозрасчетный
План видов характеристикПланВидовХарактеристикСсылка.<Имя>—
План видов расчётаПланВидовРасчетаСсылка.<Имя>—
План обменаПланОбменаСсылка.<Имя>—
Бизнес-процесс, задачаБизнесПроцессСсылка.<Имя>, ЗадачаСсылка.<Имя>—
ПеречислениеПеречислениеСсылка.<Имя>—

Перечисление стоит особняком. Его значения задаются в конфигураторе, пользователь их не создаёт, но технически это тоже ссылки: их можно хранить в реквизитах и сравнивать.

Что внутри:

  • 🔑 тип — какая таблица базы (в СУБД это отдельная таблица, например _Reference45);
  • 🆔 идентификатор — GUID, в таблице поле _IDRRef типа binary(16);
  • 📭 пустая ссылка — тип есть, идентификатор нулевой. Это не Неопределено.

Получить GUID и восстановить ссылку из строки:

Идентификатор = Ссылка.УникальныйИдентификатор();   // тип УникальныйИдентификатор
Строка36      = Строка(Идентификатор);             // "a1b2c3d4-...."

// Сериализация с типом не нужна, если тип известен заранее
СтрокаXML = XMLСтрока(Ссылка);
Ссылка2   = XMLЗначение(Тип("СправочникСсылка.Номенклатура"), СтрокаXML);

Строка(Ссылка) возвращает представление («Стол письменный»), а не идентификатор. Для обмена и хранения во внешних системах берите XMLСтрока() или УникальныйИдентификатор().

Способы получить и создать ссылку в коде

Ссылку редко «создают» в буквальном смысле. Обычно её получают одним из шести способов.

1. Из объекта после записи

НовыйЭлемент = Справочники.Номенклатура.СоздатьЭлемент();
НовыйЭлемент.Наименование = "Стол письменный";
НовыйЭлемент.Записать();
Ссылка = НовыйЭлемент.Ссылка;

До вызова Записать() у нового объекта Ссылка пустая.

2. Поиском через менеджер

Товар    = Справочники.Номенклатура.НайтиПоКоду("00-00000015");
Товар2   = Справочники.Номенклатура.НайтиПоНаименованию("Стол письменный", Истина);
Документ = Документы.РеализацияТоваровУслуг.НайтиПоНомеру("0000-000123", ТекущаяДата());

Если ничего не найдено, вернётся пустая ссылка, а не Неопределено. Проверяйте через ЗначениеЗаполнено() или .Пустая(). Второй параметр НайтиПоНаименованию (Истина) включает точное совпадение; без него поиск идёт по началу строки.

3. По реквизиту

Контрагент = Справочники.Контрагенты.НайтиПоРеквизиту("ИНН", "7700000000");

Метод возвращает первый попавшийся элемент. Если ИНН повторяется (головная организация и обособка), результат непредсказуем — здесь лучше запрос.

4. По известному GUID

UUID   = Новый УникальныйИдентификатор("8f1c2b7e-4a10-11ee-9f3a-0050569b1c22");
Ссылка = Справочники.Номенклатура.ПолучитьСсылку(UUID);

ПолучитьСсылку() не проверяет, есть ли такой объект в базе. Вы получите корректную по форме ссылку, которая может вести в никуда. Проверка:

Если Ссылка.ПолучитьОбъект() = Неопределено Тогда
    // объекта нет — ссылка «битая»
КонецЕсли;

ПолучитьОбъект() читает объект целиком. Для массовой проверки быстрее запрос ВЫБРАТЬ Ссылка ИЗ Справочник.Номенклатура ГДЕ Ссылка В (&Ссылки). В типовых конфигурациях на БСП есть готовая функция ОбщегоНазначения.СсылкаСуществует().

5. Ссылка нового объекта заранее

Приём практика для обменов и загрузок: объект создаётся с идентификатором из внешней системы, чтобы при повторной загрузке он находился, а не задваивался.

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

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

6. Пустая ссылка

Пусто = Справочники.Номенклатура.ПустаяСсылка();
Если Товар = Пусто Тогда ...   // то же самое, что Товар.Пустая()

Предопределённые элементы и как к ним обращаться

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

Обращение зависит от контекста:

ГдеКак написать
Серверный модульПланыСчетов.Хозрасчетный.РасчетыСПоставщиками
Клиентский модуль формыПредопределенноеЗначение("ПланСчетов.Хозрасчетный.РасчетыСПоставщиками")
Текст запросаЗНАЧЕНИЕ(ПланСчетов.Хозрасчетный.РасчетыСПоставщиками)
Перечисление в запросеЗНАЧЕНИЕ(Перечисление.ВидыОперацийПоступлениеТоваровУслуг.Товары)
Пустая ссылка в запросеЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)

Полезные свойства и методы:

  • ✅ Ссылка.Предопределенный — Истина, если элемент предопределённый;
  • 🏷️ ИмяПредопределенныхДанных — имя, под которым элемент описан в конфигурации;
  • 🔍 ОбщегоНазначения.ПредопределенныйЭлемент("Справочник.Х.Имя") из БСП — возвращает Неопределено вместо исключения, если элемента в базе нет.

Две характерные ошибки предопределённых данных:

  • ❌ Предопределенный элемент не существует — имя есть в конфигурации, а записи в таблице нет (удалили, не создали при обновлении, база создана загрузкой данных);
  • ❌ Предопределенный элемент не уникален — две записи с одним ИмяПредопределенныхДанных, частый итог обменов между базами.

Вторую лечат так: в одной из записей очищают ИмяПредопределенныхДанных, ссылки перебивают на оставшуюся запись, лишнюю помечают на удаление. Делают это в копии базы и после резервного копирования: на предопределённые счета завязаны проводки всех документов.

Не ищите предопределённый счёт через НайтиПоКоду("60.01"). Код пользователь может поменять, имя — нет.

Ссылки в запросах и системе компоновки данных

Разыменование через точку

Номенклатура.ЕдиницаИзмерения.Наименование в запросе выглядит безобидно, но каждая точка — это неявное левое соединение с таблицей объекта. Если поле составного типа (например, Регистратор или Субконто1), платформа соединяет со всеми таблицами, входящими в тип. Для регистратора на 40 видов документов это 40 соединений.

Выход — явно привести тип:

ВЫБРАТЬ
    ВЫРАЗИТЬ(Продажи.Регистратор КАК Документ.РеализацияТоваровУслуг).Контрагент КАК Контрагент
ИЗ
    РегистрНакопления.Продажи КАК Продажи
ГДЕ
    Продажи.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг

Оператор ССЫЛКА проверяет, что значение относится к нужной таблице. Для проверки типа вообще есть ТИПЗНАЧЕНИЯ(Поле) = ТИП(Документ.РеализацияТоваровУслуг).

Представление вместо разыменования

Если нужно только вывести имя объекта, используйте ПРЕДСТАВЛЕНИЕ(Поле) или ПРЕДСТАВЛЕНИЕССЫЛКИ(Поле). Платформа берёт готовое представление, не таща в результат лишние поля.

Параметры-ссылки

Запрос.УстановитьПараметр("Организация", Организация);
Запрос.УстановитьПараметр("Склады", МассивСкладов);   // ГДЕ Склад В (&Склады)

Ссылки передаются параметром как есть. Склеивать GUID в текст запроса не нужно и опасно.

В СКД

Поле ссылочного типа в наборе данных СКД автоматически получает представление и становится доступным для расшифровки: пользователь кликает по ячейке и открывает документ. Реквизиты этого поля (Контрагент.ИНН) пользователь выбирает в настройках сам, и СКД добавляет соединения в запрос только для выбранных полей.

Отсюда практический совет: в тексте запроса набора данных выбирайте саму ссылку, а не пачку её реквизитов. Нужные реквизиты пользователь добавит в варианте отчёта, лишних соединений не будет.

Ссылки в управляемых формах: клиент и сервер

На клиенте ссылка — это лёгкое значение: тип плюс идентификатор и закэшированное представление. Данных объекта там нет. Поэтому такой код в клиентской процедуре падает:

&НаКлиенте
Процедура ТоварПриИзменении(Элемент)
    Ед = Объект.Товар.ЕдиницаИзмерения;   // Ошибка: Поле объекта не обнаружено
КонецПроцедуры

Правильно — серверный вызов без контекста формы:

&НаКлиенте
Процедура ТоварПриИзменении(Элемент)
    Объект.ЕдиницаИзмерения = ЕдиницаТовара(Объект.Товар);
КонецПроцедуры

&НаСервереБезКонтекста
Функция ЕдиницаТовара(Товар)
    Возврат Товар.ЕдиницаИзмерения;
КонецФункции

&НаСервереБезКонтекста не гоняет на сервер всю форму, поэтому быстрее обычного &НаСервере. Несколько реквизитов за один вызов возвращайте структурой: в БСП это ОбщегоНазначения.ЗначенияРеквизитовОбъекта(Ссылка, "Код,Наименование").

Что на клиенте со ссылкой делать можно:

  • 🟰 сравнивать (=, <>) и проверять ЗначениеЗаполнено();
  • 📂 открывать форму: ПоказатьЗначение(, Ссылка) или ОткрытьФорму("Справочник.Номенклатура.ФормаОбъекта", Новый Структура("Ключ", Ссылка));
  • 🔗 получать навигационную ссылку ПолучитьНавигационнуюСсылку(Ссылка) вида e1cib/data/Справочник.Номенклатура?ref=... — её можно скопировать коллеге, и через Shift+F11 объект откроется у него.

Типичные ошибки и оптимизация кода со ссылками

Разыменование в цикле

// Медленно: на каждой строке чтение объекта
Для Каждого Строка Из Объект.Товары Цикл
    Если Строка.Номенклатура.ЭтоУслуга Тогда ...
КонецЦикла;

Каждое Строка.Номенклатура.ЭтоУслуга читает из базы основную запись элемента, со всеми реквизитами. На документе в 500 строк — 500 обращений. Решение: один запрос по всем товарам табличной части, результат — в соответствие или таблицу значений, дальше поиск в памяти.

Ссылка как ключ соответствия

Ссылки сравниваются по типу и идентификатору, поэтому годятся в ключи Соответствия. Так выглядит исправленный цикл из примера выше:

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

Услуги = Новый Соответствие;
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
    Услуги.Вставить(Выборка.Ссылка, Выборка.ЭтоУслуга);
КонецЦикла;

Для Каждого Строка Из Объект.Товары Цикл
    Если Услуги[Строка.Номенклатура] = Истина Тогда
        // обработка услуги
    КонецЕсли;
КонецЦикла;

Один запрос вместо пятисот. Повторы товаров в табличной части не мешают: условие В (&Товары) вернёт каждый элемент один раз.

Сравнение с Неопределено

Если Товар = Неопределено не поймает пустую ссылку. Для реквизита конкретного типа значение по умолчанию — пустая ссылка этого типа. Универсальная проверка — ЗначениеЗаполнено(Товар): она ложна и для Неопределено, и для пустой ссылки.

Проверка «это вообще ссылка?»

ЭтоСсылкаНаСправочник = Справочники.ТипВсеСсылки().СодержитТип(ТипЗнч(Значение));

Аналогичные ТипВсеСсылки() есть у менеджеров документов, планов счетов и прочих коллекций. В БСП — ОбщегоНазначения.ЭтоСсылка(ТипЗнч(Значение)).

Битые ссылки

В списке или отчёте вместо названия виден текст <Объект не найден> (45:9f3a0050569b1c2211ee4a108f1c2b7e). Число до двоеточия — внутренний номер таблицы, дальше идентификатор. Причины:

  • 🗑️ объект удалили напрямую, минуя контроль ссылочной целостности;
  • 🔄 при обмене пришёл документ, а элемент справочника, на который он ссылается, нет;
  • 🧩 данные загружены внешней обработкой через ПолучитьСсылку() без проверки.

Найти и исправить такие ссылки помогает Конфигуратор → Администрирование → Тестирование и исправление с флажком проверки ссылочной целостности. Для ссылок на несуществующие объекты там есть варианты: создать объекты-заглушки, очистить ссылки или не изменять.

Тестирование и исправление требует монопольного доступа: работающих пользователей придётся отключить. Запускайте вне рабочего времени и только после выгрузки .dt или резервной копии СУБД.

Чаще всего правильный выбор — «создавать объекты»: заглушки потом можно заполнить или аккуратно перебить. Очистка ссылок теряет связь с документом безвозвратно.

Поиск, где используется объект

Перед удалением или объединением дублей узнайте, кто ссылается на элемент:

Ссылки = Новый Массив;
Ссылки.Добавить(Товар);
Таблица = НайтиПоСсылкам(Ссылки);   // колонки: Ссылка, Данные, Метаданные

На большой базе метод работает долго: он обходит все таблицы, где тип может встретиться. Запускайте его на сервере, не в клиентском обработчике. Интерактивно то же самое делает команда «Найти ссылки на объект» в меню «Ещё» формы списка.

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

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

Можно ли поменять ссылку у существующего объекта?
Нет. Идентификатор задаётся один раз при первой записи. Для «замены» создают новый объект с нужной ссылкой и переносят на него все упоминания старого.
Совпадёт ли ссылка одного элемента в двух копиях базы?
Да, если копия сделана выгрузкой .dt или копированием СУБД. При ручном вводе в обе базы идентификаторы будут разными, и обмен будет сопоставлять объекты по другим полям.
Чем ссылка отличается от навигационной ссылки?
Ссылка — значение для кода и реквизитов. Навигационная ссылка — текстовая строка e1cib/data/…, по которой пользователь открывает объект в интерфейсе.
Почему Ссылка.ПолучитьОбъект() вернул Неопределено?
Объекта с таким идентификатором в таблице нет: он удалён или ссылка получена через ПолучитьСсылку() по чужому GUID.
Можно ли хранить ссылку во внешней системе?
Да, храните строку УникальныйИдентификатор() вместе с именем типа. Восстановить ссылку можно через ПолучитьСсылку() у менеджера нужного объекта.