Ссылочный тип в коде 1С:Предприятия 8.3: проверки через ТипЗнч, ТипВсеСсылки, метаданные и БСП
Содержание
Проверить, что значение в 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;
КонецЕсли;
// дальше расчёт долга по регистру
Возврат РассчитатьДолг(Контрагент, ДатаОстатка);
КонецФункции
В конфигурациях на БСП то же самое делает одна строка:
ОбщегоНазначенияКлиентСервер.ПроверитьПараметр("ДолгКонтрагента", "Контрагент",
Контрагент, Тип("СправочникСсылка.Контрагенты"));
Вместо одного типа можно передать ОписаниеТипов с несколькими допустимыми типами.
Что учесть при выборе места проверки:
- Тонкий клиент. В модуле формы на клиенте
Метаданныенедоступны, а менеджеры вродеСправочникив тонком клиенте использовать нельзя. На клиенте проверяйте черезТипЗнчиОписаниеТипов, остальное выносите в серверную функцию с директивой&НаСервереБезКонтекста. - Составной реквизит. Если реквизит формы или объекта имеет составной тип, в нём может лежать
Неопределено, примитив или ссылка. Проверяйте тип перед любым обращением к реквизитам через точку. - Запросы. Внутри запроса проверку типа делайте средствами языка запросов — это дешевле, чем выбирать всё и фильтровать в цикле:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Хозрасчетный.Регистратор КАК Регистратор
|ИЗ
| РегистрБухгалтерии.Хозрасчетный КАК Хозрасчетный
|ГДЕ
| Хозрасчетный.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг";
Вместо ССЫЛКА можно написать ТИПЗНАЧЕНИЯ(Хозрасчетный.Регистратор) = ТИП(Документ.РеализацияТоваровУслуг) — результат тот же. Регистр Хозрасчетный взят для примера из БП 3.0; конструкция работает с любым полем составного типа.
Частые ошибки при проверке на ссылку
ТипЗнч(Х) = "СправочникСсылка.Контрагенты". Тип и строка не равны никогда. Оберните имя в Тип("…").Тип("Справочник.Контрагенты") такого типа нет. Правильно — Тип("СправочникСсылка.Контрагенты"). Также проверьте, что объект с таким именем есть в конфигурации.Пустая() вызвали у Неопределено, Null или строки. Сначала проверьте тип, потом вызывайте метод — в одном условии через И.Метаданные.НайтиПоТипу находит метаданные и для СправочникОбъект. Добавьте проверку типа через ТипВсеСсылки или функцию БСП.Ещё одна частая ошибка — определять ссылку по строковому представлению: СтрНайти(Строка(Значение), ...). Представление зависит от наименования, номера и даты документа. Оно меняется при переименовании элемента и ничего не говорит о типе.
Частые вопросы
Обращается ли ТипЗнч к базе данных?
Как получить имя объекта по ссылке строкой, например «Справочник.Номенклатура»?
Метаданные.НайтиПоТипу(ТипЗнч(Ссылка)).ПолноеИмя(). Перед вызовом убедитесь, что значение ссылочного типа, иначе НайтиПоТипу вернёт Неопределено.