Ссылочный тип в коде 1С:Предприятия 8.3: проверки через ТипЗнч, ТипВсеСсылки, метаданные и БСП

· 9 мин

Содержание

Проверить, что значение в 1С является ссылкой на конкретный справочник, проще всего так: ТипЗнч(Значение) = Тип("СправочникСсылка.Номенклатура"). Если нужно понять, ссылка ли это вообще (любой справочник, документ, перечисление), используйте Справочники.ТипВсеСсылки().СодержитТип(ТипЗнч(Значение)) и аналоги для других видов объектов, а в конфигурациях на БСП — готовую функцию ОбщегоНазначения.ЗначениеСсылочногоТипа(Значение).

Всё это — механизмы платформы 1С:Предприятие 8.3 и Библиотеки стандартных подсистем, от конкретной конфигурации (БП, ЗУП, УТ) они не зависят. Разница только в том, есть ли в конфигурации БСП и где выполняется код — на сервере или на клиенте.

Проверка ссылки через ТипЗнч и Тип

ТипЗнч() возвращает тип значения, Тип() строит тип по имени. Сравнение двух типов — самая быстрая и понятная проверка. Она не обращается к базе данных и работает и на клиенте, и на сервере.

Если ТипЗнч(Значение) = Тип("СправочникСсылка.Контрагенты") Тогда
    // это ссылка на справочник «Контрагенты» (в том числе пустая)
КонецЕсли;

Если ТипЗнч(Значение) = Тип("ДокументСсылка.РеализацияТоваровУслуг") Тогда
    // ссылка на документ реализации
КонецЕсли;

Имя типа собирается по шаблону <ВидОбъекта>Ссылка.<ИмяОбъектаМетаданных>. Основные виды:

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

Несколько допустимых типов сразу

Если параметр может быть ссылкой на один из нескольких объектов, не стройте цепочку из пяти Или. Соберите ОписаниеТипов и спросите у него:

ДопустимыеТипы = Новый ОписаниеТипов("СправочникСсылка.Контрагенты, СправочникСсылка.ФизическиеЛица");

Если ДопустимыеТипы.СодержитТип(ТипЗнч(Значение)) Тогда
    // контрагент или физлицо
КонецЕсли;

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

Любая ссылка: метод ТипВсеСсылки

У каждого менеджера вида объектов (Справочники, Документы, Перечисления и т. д.) есть метод ТипВсеСсылки(). Он возвращает ОписаниеТипов со всеми ссылочными типами этого вида. Универсальная функция без БСП выглядит так:

&НаСервереБезКонтекста
Функция ЭтоЗначениеСсылочногоТипа(Значение)
    
    Если Значение = Неопределено Или Значение = Null Тогда
        Возврат Ложь;
    КонецЕсли;
    
    ТипЗначения = ТипЗнч(Значение);
    
    Возврат Справочники.ТипВсеСсылки().СодержитТип(ТипЗначения)
        Или Документы.ТипВсеСсылки().СодержитТип(ТипЗначения)
        Или Перечисления.ТипВсеСсылки().СодержитТип(ТипЗначения)
        Или ПланыВидовХарактеристик.ТипВсеСсылки().СодержитТип(ТипЗначения)
        Или ПланыСчетов.ТипВсеСсылки().СодержитТип(ТипЗначения)
        Или ПланыВидовРасчета.ТипВсеСсылки().СодержитТип(ТипЗначения)
        Или ПланыОбмена.ТипВсеСсылки().СодержитТип(ТипЗначения)
        Или БизнесПроцессы.ТипВсеСсылки().СодержитТип(ТипЗначения)
        Или Задачи.ТипВсеСсылки().СодержитТип(ТипЗначения);
    
КонецФункции

Нужен только один вид? Оставьте одну строку: Документы.ТипВсеСсылки().СодержитТип(ТипЗнч(Значение)) отвечает на вопрос «это ссылка на какой-нибудь документ?».

Точки маршрута бизнес-процессов (ТочкаМаршрутаБизнесПроцессаСсылка) тоже ссылочный тип, но в список выше они не попадают. Если в конфигурации есть бизнес-процессы и такие значения могут прийти в функцию, учтите их отдельно.

Как убедиться, что это ссылка, через метаданные

Второй путь — спросить у метаданных, какому объекту конфигурации соответствует тип. Метаданные.НайтиПоТипу() возвращает объект метаданных или Неопределено, если тип не связан с объектом конфигурации (число, строка, дата, таблица значений).

ОбъектМетаданных = Метаданные.НайтиПоТипу(ТипЗнч(Значение));

Если ОбъектМетаданных <> Неопределено
    И Метаданные.Справочники.Содержит(ОбъектМетаданных) Тогда
    
    ПолноеИмя = ОбъектМетаданных.ПолноеИмя(); // "Справочник.Номенклатура"
    
КонецЕсли;

Этот способ хорош, когда нужно не только узнать «ссылка или нет», но и получить имя таблицы, набор реквизитов или вид объекта — например, для универсальной обработки, которая работает с любым справочником.

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

Короткий вариант через XML-тип

Встречается и компактный приём через XMLТипЗнч(). У ссылочных типов XML-имя содержит Ref.: CatalogRef.Номенклатура, DocumentRef.ПоступлениеТоваровУслуг, EnumRef.ВидыОпераций.

ТипXML = XMLТипЗнч(Значение);
ЭтоСсылка = ТипXML <> Неопределено И СтрНайти(ТипXML.ИмяТипа, "Ref.") > 0;

Код короткий, но читается хуже и опирается на строковое имя. В новых разработках лучше функция на ТипВсеСсылки или БСП.

Сравнение способов

СпособКогда применятьОграничения
ТипЗнч = Тип(...)Известен один конкретный типНужно перечислить все типы вручную
ОписаниеТипов.СодержитТипНесколько допустимых типов, в том числе на клиентеСписок типов задаётся строкой
ТипВсеСсылки()«Любая ссылка» или «любой документ»Только сервер и толстый клиент
Метаданные.НайтиПоТипуНужны имя, реквизиты, вид объектаНе отличает ссылку от объекта; недоступно в тонком клиенте
Функции БСПТиповые конфигурации на БСПТолько там, где БСП внедрена

Готовые функции БСП для проверки ссылочного типа

В типовых конфигурациях (БП 3.0, ЗУП 3.1, УТ 11 и других на БСП) велосипед писать не нужно. В общем модуле ОбщегоНазначения есть:

  • 🔹 ОбщегоНазначения.ЗначениеСсылочногоТипа(Значение) — Истина, если передана ссылка любого вида;
  • 🔹 ОбщегоНазначения.ЭтоСсылка(Тип) — то же, но принимает не значение, а тип. Удобно, когда тип уже получен, например из колонки таблицы значений;
  • 🔹 ОбщегоНазначения.СсылкаСуществует(Ссылка) — проверяет, есть ли объект с такой ссылкой в базе.
Если Не ОбщегоНазначения.ЗначениеСсылочногоТипа(Параметр) Тогда
    Возврат;
КонецЕсли;

Перед использованием откройте модуль ОбщегоНазначения в конфигураторе и найдите функцию поиском по модулю: состав и параметры функций БСП меняются между версиями библиотеки. Если функции нет, возьмите вариант с ТипВсеСсылки из раздела выше.

Пустая ссылка, Неопределено и Null: в чём разница

Это три разных значения, и путают их постоянно.

  • 📌 Пустая ссылка — значение ссылочного типа, которое ни на что не указывает. ТипЗнч(Справочники.Контрагенты.ПустаяСсылка()) вернёт СправочникСсылка.Контрагенты. То есть проверка типа скажет «это ссылка» — и будет права.
  • 📌 Неопределено — значения нет вообще. Так выглядит незаполненный реквизит составного типа или необязательный параметр без значения.
  • 📌 Null — приходит из запроса, когда левое соединение не нашло пары. В объектах конфигурации Null не хранится.

Поэтому проверка «это ссылка, и она заполнена» делается в два шага:

Если ТипЗнч(Значение) = Тип("СправочникСсылка.Контрагенты")
    И Не Значение.Пустая() Тогда
    // настоящая ссылка на существующий элемент (с точки зрения значения)
КонецЕсли;

Порядок условий важен: метод Пустая() есть только у ссылок. Если вызвать его у Неопределено, получите ошибку Метод объекта не обнаружен (Пустая). В языке 1С выражение И вычисляется сокращённо: второе условие не выполняется, если первое ложно.

Как ведёт себя ЗначениеЗаполнено() на разных значениях:

ЗначениеЗначениеЗаполнено()Ссылочный тип?
Пустая ссылка справочникаЛожьДа
Ссылка на элементИстинаДа
НеопределеноЛожьНет
NullЛожьНет
0, "", пустая датаЛожьНет

Вывод простой: ЗначениеЗаполнено() отвечает «есть ли значение», но не «ссылка ли это». Для типа нужна отдельная проверка.

Битая ссылка

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

Предопределённые элементы и значения перечислений

Ссылки на предопределённые элементы справочников, планов счетов и планов видов характеристик — обычные ссылки. Их тип такой же, как у любого другого элемента этого справочника. Отличить их по типу нельзя — только по самому значению.

Сравнение с конкретным предопределённым элементом:

// на сервере
Если Значение = Справочники.ВидыЦен.Закупочная Тогда ...

// на клиенте и в общих клиент-серверных модулях
Если Значение = ПредопределенноеЗначение("Справочник.ВидыЦен.Закупочная") Тогда ...

Имена ВидыЦен.Закупочная здесь условные — подставьте объект и предопределённый элемент из своей конфигурации. Посмотреть имена можно в конфигураторе: откройте объект и нажмите кнопку Предопределенные в его окне.

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

Если ЗначениеЗаполнено(Ссылка) И Ссылка.Предопределенный Тогда
    ИмяЭлемента = Ссылка.ИмяПредопределенныхДанных;
КонецЕсли;

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

Значения перечислений тоже ссылки: ТипЗнч(Перечисления.СтавкиНДС.НДС20) — это ПеречислениеСсылка.СтавкиНДС. Поэтому Перечисления.ТипВсеСсылки() включён в универсальную функцию, а пустое значение перечисления проверяется так же, через Пустая() или сравнение с Перечисления.СтавкиНДС.ПустаяСсылка().

Проверка ссылочного параметра в процедурах и функциях

Язык 1С не проверяет типы параметров при вызове. В экспортную функцию общего модуля может прийти что угодно: строка из внешней системы, Неопределено из незаполненного поля, объект вместо ссылки. Ошибка тогда вылетает в глубине кода, а не на входе.

Защитный шаблон — проверка в начале функции и понятное исключение:

Функция ДолгКонтрагента(Контрагент, ДатаОстатка) Экспорт
    
    Если ТипЗнч(Контрагент) <> Тип("СправочникСсылка.Контрагенты") Тогда
        ВызватьИсключение СтрШаблон(
            "Недопустимое значение параметра Контрагент в функции ДолгКонтрагента: ожидается ссылка на справочник «Контрагенты», передано значение типа %1",
            Строка(ТипЗнч(Контрагент)));
    КонецЕсли;
    
    Если Не ЗначениеЗаполнено(Контрагент) Тогда
        Возврат 0;
    КонецЕсли;
    
    // дальше расчёт долга по регистру
    Возврат РассчитатьДолг(Контрагент, ДатаОстатка);
    
КонецФункции

В конфигурациях на БСП то же самое делает одна строка:

ОбщегоНазначенияКлиентСервер.ПроверитьПараметр("ДолгКонтрагента", "Контрагент",
    Контрагент, Тип("СправочникСсылка.Контрагенты"));

Вместо одного типа можно передать ОписаниеТипов с несколькими допустимыми типами.

Что учесть при выборе места проверки:

  1. Тонкий клиент. В модуле формы на клиенте Метаданные недоступны, а менеджеры вроде Справочники в тонком клиенте использовать нельзя. На клиенте проверяйте через ТипЗнч и ОписаниеТипов, остальное выносите в серверную функцию с директивой &НаСервереБезКонтекста.
  2. Составной реквизит. Если реквизит формы или объекта имеет составной тип, в нём может лежать Неопределено, примитив или ссылка. Проверяйте тип перед любым обращением к реквизитам через точку.
  3. Запросы. Внутри запроса проверку типа делайте средствами языка запросов — это дешевле, чем выбирать всё и фильтровать в цикле:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|    Хозрасчетный.Регистратор КАК Регистратор
|ИЗ
|    РегистрБухгалтерии.Хозрасчетный КАК Хозрасчетный
|ГДЕ
|    Хозрасчетный.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг";

Вместо ССЫЛКА можно написать ТИПЗНАЧЕНИЯ(Хозрасчетный.Регистратор) = ТИП(Документ.РеализацияТоваровУслуг) — результат тот же. Регистр Хозрасчетный взят для примера из БП 3.0; конструкция работает с любым полем составного типа.

Частые ошибки при проверке на ссылку

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

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

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

Обращается ли ТипЗнч к базе данных?
Нет. Тип известен из самого значения, поэтому ТипЗнч можно вызывать в циклах по тысячам строк без потери скорости. К базе обращается только чтение реквизитов через точку и проверка существования ссылки.
Как получить имя объекта по ссылке строкой, например «Справочник.Номенклатура»?
На сервере: Метаданные.НайтиПоТипу(ТипЗнч(Ссылка)).ПолноеИмя(). Перед вызовом убедитесь, что значение ссылочного типа, иначе НайтиПоТипу вернёт Неопределено.
Помеченный на удаление элемент — это всё ещё ссылка?
Да. Пометка удаления — свойство объекта, а не типа. Ссылка остаётся ссылкой до физического удаления объекта; после него она становится битой.
Работают ли эти проверки во внешней обработке?
Да, если обработка подключена к базе с нужными объектами. Тип(“СправочникСсылка.Имя”) требует, чтобы такой справочник был в конфигурации базы, иначе получите ошибку «Тип не определен».
Чем отличается Тип("СправочникСсылка.Х") от Тип("СправочникОбъект.Х")?
Первый — ссылка, лёгкое значение-указатель. Второй — объект целиком, с реквизитами и табличными частями, его получают через ПолучитьОбъект(). В реквизитах и параметрах почти всегда хранят и передают ссылку.