Разработка и Конфигурация · 10 мин
Как программно определить тип документа в 1С
Способы программно определить тип документа в 1С: ТипЗнч, Метаданные(), Тип(). Примеры кода для тонкого клиента, управляемых форм и запросов.
При разработке на платформе 1С:Предприятие часто возникает задача: в переменной лежит ссылка на документ, но заранее неизвестно, какой именно это документ — «Реализация товаров», «Поступление товаров» или «Приходный ордер». Такое происходит при обходе результатов запроса по составному типу, при обработке подписок на события, при работе с критериями отбора и универсальными обработчиками.
Платформа предлагает несколько способов определить тип документа: функции ТипЗнч() и Тип(), метод Метаданные() у объекта или ссылки, а также конструкции языка запросов ТИПЗНАЧЕНИЯ, ССЫЛКА и ВЫРАЗИТЬ. У каждого подхода есть область применения, ограничения и типичные ошибки.
Ниже разобраны рабочие варианты с примерами кода, различиями между ссылкой и объектом, а также приёмы для управляемых форм и запросов.
Определение типа через ТипЗнч и Тип
Самый распространённый приём — функция ТипЗнч(), которая возвращает тип переданного значения, и функция Тип(), которая возвращает тип по его строковому имени. Сравнение двух типов даёт булево и удобно в условиях:
Если ТипЗнч(ДокументСсылка) = Тип("ДокументСсылка.РеализацияТоваровУслуг") Тогда
// обработка реализации
ИначеЕсли ТипЗнч(ДокументСсылка) = Тип("ДокументСсылка.ПоступлениеТоваровУслуг") Тогда
// обработка поступления
КонецЕсли;
Имя типа строится по шаблону ДокументСсылка.<ИмяДокумента> для ссылок и ДокументОбъект.<ИмяДокумента> для объектов. Это принципиально: ссылка и объект — разные типы, и сравнение ТипЗнч(Ссылка) = Тип("ДокументОбъект.РеализацияТоваровУслуг") всегда даст «Ложь».
Если проверяется принадлежность к группе документов, удобнее собрать соответствие или массив допустимых типов один раз и проверять вхождение, а не строить длинную цепочку ИначеЕсли.
Получение имени документа через Метаданные()
Когда нужно не сравнить тип, а узнать имя вида документа строкой (для журнала, сообщения пользователю, построения запроса), используйте метод Метаданные(). Он есть и у ссылки, и у объекта:
МетаданныеДокумента = ДокументСсылка.Метаданные();
ИмяДокумента = МетаданныеДокумента.Имя; // "РеализацияТоваровУслуг"
Представление = МетаданныеДокумента.Представление(); // синоним для пользователя
ПолноеИмя = МетаданныеДокумента.ПолноеИмя(); // "Документ.РеализацияТоваровУслуг"
Обратный приём — получение пустой ссылки по имени: Документы[ИмяДокумента].ПустаяСсылка(), а затем ТипЗнч(ПустаяСсылка). Это безопасная альтернатива функции Тип(), когда имя документа приходит извне (например, из настроек обмена) и может отсутствовать в конфигурации.
Заметим, что обращение Ссылка.Метаданные() не выполняет чтение объекта из базы — метод работает с метаданными конфигурации, поэтому он быстрый и безопасный даже для «битых» ссылок.
- 1Определите, что лежит в переменной: ссылка или объект (Ссылки и объекты — разные типы)
- 2Для сравнения используйте ТипЗнч(Значение) = Тип("ДокументСсылка.Имя")
- 3Для получения имени строкой вызовите Значение.Метаданные().Имя
- 4Если имя приходит извне, проверьте Метаданные.Документы.Найти(Имя) перед использованием
- 5Проверьте результат на тестовых данных с несколькими видами документов
Определение типа документа в запросе
В языке запросов работают другие механизмы. Оператор ТИПЗНАЧЕНИЯ() возвращает тип значения поля, а сравнение с конкретным типом выполняется через конструкцию ЗНАЧЕНИЕ(Документ.Имя.ПустаяСсылка) или через проверку ССЫЛКА:
ВЫБРАТЬ
Журнал.Ссылка КАК Ссылка,
ВЫБОР
КОГДА Журнал.Ссылка ССЫЛКА Документ.РеализацияТоваровУслуг
ТОГДА "Реализация"
КОГДА Журнал.Ссылка ССЫЛКА Документ.ПоступлениеТоваровУслуг
ТОГДА "Поступление"
ИНАЧЕ "Прочее"
КОНЕЦ КАК ВидДокумента
ИЗ
ЖурналДокументов.ЖурналОпераций КАК Журнал
Конструкция ССЫЛКА предпочтительнее сравнения через ТИПЗНАЧЕНИЯ с параметром-типом, потому что она лучше оптимизируется СУБД и не требует передачи типа параметром. Если нужно отобрать только документы конкретного вида, условие ГДЕ Ссылка ССЫЛКА Документ.ИмяДокумента работает быстрее, чем фильтрация уже после выборки.
Для обращения к реквизитам составного поля, существующим только у части документов, применяйте оператор ВЫРАЗИТЬ(Поле КАК Документ.Имя) — он приводит значение к нужному типу, а для несовпадающих типов даёт NULL.
Сравнение способов
Подходит для проверки в коде, когда имя документа известно на этапе разработки. Ограничение: исключение при несуществующем имени.
Подходит для получения имени строкой, логирования, динамических алгоритмов. Не требует чтения объекта из базы.
Оптимально для отбора и классификации на уровне СУБД. Не применимо вне запроса.
Нужен для доступа к специфичным реквизитам составного поля. Для несовпадающих типов возвращает NULL.
Отличие ссылки от объекта и типичные проверки
Частая ошибка новичков — путать ДокументСсылка и ДокументОбъект. Ссылка — лёгкий идентификатор записи, объект — загруженные в память данные с возможностью изменения и записи. Типы у них разные, поэтому одна и та же проверка должна учитывать, с чем вы работаете.
Если код универсален и может получить и то и другое, безопаснее приводить к ссылке: у объекта есть свойство Ссылка. Либо проверять оба варианта:
ТипЗначения = ТипЗнч(Значение);
Если ТипЗначения = Тип("ДокументСсылка.РеализацияТоваровУслуг")
Или ТипЗначения = Тип("ДокументОбъект.РеализацияТоваровУслуг") Тогда
// общий код
КонецЕсли;
Для проверки, является ли значение вообще документом (любым), используйте определение через метаданные: получить Метаданные.НайтиПоТипу(ТипЗнч(Значение)) и проверить, что результат принадлежит ветке документов, например сравнив Метаданные.Документы.Содержит(НайденныеМетаданные).
Определение типа в управляемых формах и на клиенте
В управляемых формах действуют ограничения контекста. На клиенте недоступны многие серверные методы, но ТипЗнч() и Тип() работают на тонком клиенте, поэтому базовое сравнение типов доступно и в клиентских процедурах.
Метод Метаданные() у ссылки на клиенте недоступен — попытка вызова приведёт к ошибке. Если в клиентском коде нужно имя вида документа, получите его заранее в серверной процедуре (например, при создании формы) и передайте в реквизит формы или параметр.
Для динамических списков с произвольным запросом классификацию вида документа выполняйте прямо в тексте запроса через ССЫЛКА — это работает на сервере и не требует дополнительных вызовов из формы.
Динамическое определение без жёсткой привязки к конфигурации
Во внешних обработках и универсальных механизмах обмена имена документов часто неизвестны заранее. В этом случае строят таблицу соответствия: перебирают Метаданные.Документы, сохраняют пары «имя — тип» в соответствие и дальше работают через него.
ТаблицаТипов = Новый Соответствие;
Для Каждого МетаДокумент Из Метаданные.Документы Цикл
ПустаяСсылка = Документы[МетаДокумент.Имя].ПустаяСсылка();
ТаблицаТипов.Вставить(ТипЗнч(ПустаяСсылка), МетаДокумент.Имя);
КонецЦикла;
// позже:
ИмяВида = ТаблицаТипов[ТипЗнч(ПроизвольнаяСсылка)];
Такой кэш строится один раз за сеанс и позволяет быстро получать имя вида без обращения к метаданным в цикле. Если обработка должна работать в конфигурациях, где часть документов отсутствует, каждое обращение к Документы[Имя] защищайте проверкой Метаданные.Документы.Найти(Имя) <> Неопределено.
Как проверить результат
ТипЗнч(Ссылка) показывает ожидаемый тип, условие срабатывает
Условие всегда Ложь — вероятно, перепутаны Ссылка и Объект
Возвращает имя вида документа из конфигуратора
Ошибка на клиенте управляемой формы — метод вызван в недоступном контексте
В выборку попадают только документы нужного вида
Пустой результат при наличии данных — проверьте имя документа в запросе
Частые вопросы
Как определить тип документа, если в переменной может быть Неопределено или NULL?
Сначала проверьте значение на Неопределено и NULL, иначе ТипЗнч вернёт соответствующие типы, а сравнение с типом документа даст Ложь. Типичный шаблон: Если ЗначениеЗаполнено(Ссылка) И ТипЗнч(Ссылка) = Тип("ДокументСсылка.Имя") Тогда.
Можно ли получить тип документа по его навигационной ссылке?
Да. Функция ПолучитьСсылкуИзНавигационной() (в тонком клиенте) или разбор строки навигационной ссылки позволяют получить ссылку, после чего тип определяется стандартно через ТипЗнч. Но сама строка вида «e1cib/data/Документ.РеализацияТоваровУслуг?ref=...» уже содержит имя вида — его можно извлечь и напрямую, хотя надёжнее работать со ссылкой.
Чем отличается ТипЗнч от Метаданные().Имя?
ТипЗнч возвращает объект «Тип» — его сравнивают с другими типами. Метаданные().Имя возвращает строку с именем вида из конфигурации. Для сравнений используйте типы, для вывода пользователю и динамических алгоритмов — имя строкой.
Как в запросе отобрать документы нескольких видов сразу?
Используйте условие с ИЛИ: ГДЕ Ссылка ССЫЛКА Документ.Вид1 ИЛИ Ссылка ССЫЛКА Документ.Вид2. Альтернатива — объединение через ОБЪЕДИНИТЬ ВСЕ запросов к конкретным таблицам документов, что иногда быстрее при больших объёмах.
Работает ли ТипЗнч на мобильном клиенте и в веб-клиенте?
Да, ТипЗнч и Тип доступны во всех клиентах. Ограничение касается метода Метаданные() у прикладных объектов — он доступен только на сервере, поэтому в клиентском коде имя вида получайте заранее через серверный вызов.
Как определить, что значение является документом вообще, без привязки к виду?
Вызовите Метаданные.НайтиПоТипу(ТипЗнч(Значение)) и проверьте, что найденный объект метаданных принадлежит коллекции документов: Метаданные.Документы.Содержит(Найденное). Метод вернёт Истина только для ссылок и объектов документов.
Что важно запомнить
Для сравнения типов в коде используйте ТипЗнч и Тип, для получения имени строкой — Метаданные().Имя, в запросах — ССЫЛКА и ВЫРАЗИТЬ. Учитывайте разницу между ссылкой и объектом, а динамические имена проверяйте через Метаданные.Документы.Найти.