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