Ссылки на объекты 1С:Предприятия 8.3 — от УникальногоИдентификатора до запросов и форм
Содержание
Ссылка в 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 или резервной копии СУБД.
Чаще всего правильный выбор — «создавать объекты»: заглушки потом можно заполнить или аккуратно перебить. Очистка ссылок теряет связь с документом безвозвратно.
Поиск, где используется объект
Перед удалением или объединением дублей узнайте, кто ссылается на элемент:
Ссылки = Новый Массив;
Ссылки.Добавить(Товар);
Таблица = НайтиПоСсылкам(Ссылки); // колонки: Ссылка, Данные, Метаданные
На большой базе метод работает долго: он обходит все таблицы, где тип может встретиться. Запускайте его на сервере, не в клиентском обработчике. Интерактивно то же самое делает команда «Найти ссылки на объект» в меню «Ещё» формы списка.