Какой это документ: определяем вид ссылки и метаданные в коде 1С

· 9 мин

Содержание

Тип документа в коде определяют функцией ТипЗнч(): она возвращает тип значения, а сравнивать его нужно с Тип("ДокументСсылка.ИмяДокумента"). Частая ошибка начинающих разработчиков в 1С Предприятие 8.3 — сравнение с Тип("ДокументОбъект.ИмяДокумента"). Условие тогда никогда не срабатывает, потому что у ссылки и у объекта разные типы.

Второй вариант — спросить метаданные: Ссылка.Метаданные().Имя вернёт имя документа в конфигурации. Он удобнее, когда вид документа заранее неизвестен и нужно обработать любой. Ниже оба способа, запросы и работа с составными типами.

Быстрая проверка типа через ТипЗнч

Базовый приём выглядит так:

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

Что важно помнить:

  • 🔹 ТипЗнч возвращает значение типа Тип, а не строку. Строкой его можно получить только через Строка(...), и такой результат для логики не годится.
  • 🔹 Для ссылки используйте ДокументСсылка., для записанного в память объекта — ДокументОбъект..
  • 🔹 Если в переменной может быть Неопределено или пустая строка, ТипЗнч не упадёт и вернёт Тип("Неопределено") или Тип("Строка"). Это удобно: проверка безопасна.

Когда нужно сверить сразу с несколькими документами, заведите массив типов:

ТипыПродаж = Новый Массив;
ТипыПродаж.Добавить(Тип("ДокументСсылка.РеализацияТоваровУслуг"));
ТипыПродаж.Добавить(Тип("ДокументСсылка.ВозвратТоваровОтПокупателя"));

Если ТипыПродаж.Найти(ТипЗнч(Ссылка)) <> Неопределено Тогда
    // один из этих документов
КонецЕсли;

Если у вас объект, а не ссылка, возьмите Объект.Ссылка — тогда сравнение будет с ДокументСсылка. Прямое ТипЗнч(Объект) сравнивайте только с ДокументОбъект.

Не сравнивайте тип по тексту: Строка(ТипЗнч(Ссылка)) = "...". Представление зависит от настроек и может поменяться при обновлении конфигурации.

Метаданные ссылки: имя, синоним, полное имя

У любой ссылки на объект есть метод Метаданные(). Он возвращает описание документа из конфигурации — именно то, что вы видите в Конфигураторе в ветке «Документы».

ОбъектМД = Ссылка.Метаданные();
Имя        = ОбъектМД.Имя;          // РеализацияТоваровУслуг
Синоним    = ОбъектМД.Синоним;      // как увидит пользователь
ПолноеИмя  = ОбъектМД.ПолноеИмя();  // Документ.РеализацияТоваровУслуг

Для чего каждое свойство:

СвойствоЧто даётГде применять
ИмяВнутреннее имя без пробеловСравнение, обращение к менеджеру
СинонимЧитаемое названиеСообщения пользователю, заголовки
ПолноеИмя()Вид объекта и имя вместеУниверсальный код, журналирование

Метод работает и для пустой ссылки: Документы.РеализацияТоваровУслуг.ПустаяСсылка().Метаданные() вернёт то же описание. Но для Неопределено и значений простых типов (число, строка) он вызовет ошибку, поэтому сначала проверьте, что значение — ссылка:

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

Обратная задача — по типу найти описание, не имея под рукой самой ссылки:

ОбъектМД = Метаданные.НайтиПоТипу(Тип("ДокументСсылка.РеализацияТоваровУслуг"));

А по строке с полным именем помогает Метаданные.НайтиПоПолномуИмени("Документ.РеализацияТоваровУслуг"). Если такого объекта нет, вернётся Неопределено, а не ошибка.

Как искать документ в самой конфигурации

Иногда вопрос не про код, а про то, где у документа вообще находится описание и как называются его реквизиты.

В Конфигураторе откройте Конфигурация → Открыть конфигурацию, разверните ветку Документы и найдите нужный по имени или синониму. Форма объекта покажет реквизиты, табличные части, движения по регистрам и нумератор.

Из кода тот же перечень доступен через коллекции:

Для Каждого Док Из Метаданные.Документы Цикл
    Сообщить(Док.Имя + " — " + Док.Синоним);
КонецЦикла;

Пара полезных вариантов:

  • 🔹 Метаданные.Документы.Найти("РеализацияТоваровУслуг") — поиск по имени, вернёт Неопределено, если документа нет.
  • 🔹 Метаданные.Документы.РеализацияТоваровУслуг.Реквизиты — список реквизитов; у каждого свой Тип.
  • 🔹 Метаданные.Документы.РеализацияТоваровУслуг.Движения — регистры, в которые документ пишет.

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

Определение вида документа по ссылке в универсальных обработках

Допустим, вы пишете обработку, которая принимает произвольную ссылку и должна ответить: «это документ?» и «какой именно?». Универсальный порядок такой:

  1. Проверить, что значение заполнено и является ссылкой.
  2. Получить Метаданные().
  3. Убедиться, что это именно документ, а не справочник.
  4. Взять ПолноеИмя() или Имя.
Функция ИмяДокументаПоСсылке(Ссылка)
    Если Не ЗначениеЗаполнено(Ссылка) Тогда
        Возврат Неопределено;
    КонецЕсли;
    Если Не Документы.ТипВсеСсылки().СодержитТип(ТипЗнч(Ссылка)) Тогда
        Возврат Неопределено;
    КонецЕсли;
    Возврат Ссылка.Метаданные().Имя;
КонецФункции

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

Пустая ссылка в ЗначениеЗаполнено даёт Ложь. Если вам нужно определить тип именно пустого значения (например, чтобы создать новый документ того же вида), проверяйте ТипЗнч без ЗначениеЗаполнено.

Тип документа в запросах 1С

В тексте запроса тип сравнивают не через Тип(), а через ТИП() и ТИПЗНАЧЕНИЯ():

ВЫБРАТЬ
    Движения.Регистратор КАК Регистратор
ИЗ
    РегистрНакопления.ТоварыНаСкладах КАК Движения
ГДЕ
    ТИПЗНАЧЕНИЯ(Движения.Регистратор) = ТИП(Документ.РеализацияТоваровУслуг)

Есть более короткая запись через оператор ССЫЛКА:

ГДЕ Движения.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг

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

Как получить сведения из конкретного документа, если тип поля составной? Используйте ВЫРАЗИТЬ:

ВЫБРАТЬ
    ВЫРАЗИТЬ(Движения.Регистратор КАК Документ.РеализацияТоваровУслуг).Контрагент КАК Контрагент
ИЗ
    РегистрНакопления.ТоварыНаСкладах КАК Движения
ГДЕ
    Движения.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг

Без ГДЕ по типу для строк другого вида получится NULL. Это не ошибка, но результат придётся фильтровать.

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

СпособКогда использоватьНюанс
ТИПЗНАЧЕНИЯ(...) = ТИП(...)Нужно явное сравнение, в том числе в условиях соединенияЧуть длиннее записи
... ССЫЛКА Документ.ИмяПростая фильтрация по составному полюЧитается легче всего
ВЫРАЗИТЬ(... КАК Документ.Имя)Нужны реквизиты конкретного документаДля чужих типов вернёт NULL

Обращение через точку к составному полю без ВЫРАЗИТЬ заставляет платформу соединять много таблиц сразу. На больших базах такой запрос работает заметно медленнее.

Составные типы реквизитов и динамическая работа с типами

Бывает, что вам нужно не определить тип одного значения, а понять, документы каких видов вообще допустимы в реквизите. Ответ даёт описание типов:

ОписаниеТипа = Метаданные.Документы.ПоступлениеТоваровУслуг.Реквизиты.ДокументОснование.Тип;
Для Каждого Тип Из ОписаниеТипа.Типы() Цикл
    Сообщить(Строка(Тип));
КонецЦикла;

Проверка вхождения конкретного типа в реквизит — через СодержитТип:

Если ОписаниеТипа.СодержитТип(Тип("ДокументСсылка.ЗаказКлиента")) Тогда
    // документ-основание может быть заказом
КонецЕсли;

Поскольку имя реквизита в этом примере условное, подставьте реальный реквизит вашего документа.

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

Менеджер = Документы[ИмяДокумента];
ПустаяСсылка = Менеджер.ПустаяСсылка();
ТипДокумента = ТипЗнч(ПустаяСсылка);

Зная имя, можно создать документ , выбрать записи или получить тип без жёсткого перечисления. Если имени не существует, платформа бросит исключение. Поэтому сначала проверьте его через Метаданные.Документы.Найти(ИмяДокумента).

Выбирать между способами проще по схеме:

Что у вас на входе?

Мини-кейс и другие места, где нужен вид документа

Задача из практики: в подписке на событие ПередЗаписью нужно проверять заполнение поля «Склад», но только для трёх видов документов из десятков. Писать три подписки неудобно, одну общую — разумно.

Рабочая схема:

  1. В подписке на событие укажите нужные документы в списке источников. Тогда обработчик вызывается только для них.
  2. Внутри получите вид через Источник.Метаданные().Имя.
  3. Для каждого вида задайте свой реквизит проверки в структуре соответствия.
  4. Если вид не найден в соответствии, выйдите из процедуры, не трогая документ.
Процедура ПроверкаСклада(Источник, Отказ) Экспорт
    ИмяПоля = Новый Соответствие;
    ИмяПоля.Вставить("РеализацияТоваровУслуг", "Склад");
    ИмяПоля.Вставить("ПоступлениеТоваровУслуг", "Склад");

    Поле = ИмяПоля.Получить(Источник.Метаданные().Имя);
    Если Поле = Неопределено Тогда
        Возврат;
    КонецЕсли;
    Если Не ЗначениеЗаполнено(Источник[Поле]) Тогда
        Отказ = Истина;
        Сообщить("Не заполнен склад в документе " + Источник.Метаданные().Синоним);
    КонецЕсли;
КонецПроцедуры

Здесь в Источник лежит объект, а не ссылка, но метод Метаданные() есть и у него. Сравнивать тип через ТипЗнч в такой схеме не нужно — достаточно имени.

Что изменится, когда в конфигурации появится четвёртый документ с таким же полем? Добавится одна строка в соответствии и ещё один источник в подписке. Тело процедуры править не придётся.

Где ещё встречается задача определения вида

Помимо обработчиков и запросов, вид документа приходится определять в нескольких типичных местах:

  • 🔹 Отчёты на СКД. В наборе данных поле регистратора имеет составной тип. Чтобы вывести только продажи, добавьте отбор по виду, а не по тексту представления.
  • 🔹 Обмен данными. Правила конвертации выбирают обработчик по имени вида документа: для каждого вида свой набор свойств.
  • 🔹 Журнал регистрации и логирование. Полное имя из ПолноеИмя() удобно писать в строку события: по нему потом легко отбирать записи.
  • 🔹 Печатные формы. Универсальная команда печати получает массив ссылок разных видов и должна понимать, какой макет к какой ссылке применить.

Во всех этих случаях рабочее правило одно: тип проверяют по метаданным или по Тип(), но не по тексту, который видит пользователь. Текст можно переименовать, а имя в коде — нет.

Частые промахи и как их избежать

Большинство проблем с определением вида документа повторяется от конфигурации к конфигурации.

  • ❌ ДокументОбъект вместо ДокументСсылка. Условие молча остаётся ложным. Проверьте, что вы сравниваете именно ссылку.
  • ❌ Вызов Метаданные() на Неопределено. Падает с исключением времени выполнения. Сначала проверяйте, что значение — ссылка.
  • ❌ Сравнение через строку. Название зависит от языка и версии. Используйте Тип() или ПолноеИмя().
  • ❌ Точка к составному полю в запросе. Работает, но плохо масштабируется. Ограничивайте выборку по типу и применяйте ВЫРАЗИТЬ.
  • ❌ Хардкод десятка видов в цикле. При добавлении нового документа код придётся править. Вынесите перечень в массив типов или в справочник настроек.
  • ❌ Проверка типа после записи. В обработчике ПередЗаписью объект ещё не сохранён, а ссылка может быть новой. Для нового документа Ссылка уже есть, но в базе записи ещё нет — запросом её не найти.

В отладчике удобно ставить точку останова и смотреть значение выражения ТипЗнч(Ссылка) в окне вычисления. Если там ДокументОбъект.…, а ожидали ссылку, причина найдена за секунду.

Чтобы код не развалился при обновлении конфигурации, держите проверки типов в одном месте — в общем модуле. Тогда замена одного условия не потребует правки сорока процедур.

Перед тем как зафиксировать проверку типа в коде
Выполнено: 0 / 5

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

Чем ТипЗнч отличается от Метаданные()?
ТипЗнч возвращает тип значения и подходит для сравнения с конкретным видом. Метаданные() возвращает объект конфигурации с именем, синонимом и составом реквизитов — его берут, когда нужны сведения о самом документе.
Как узнать, что значение — ссылка на документ, а не на справочник?
Проверьте вхождение типа в Документы.ТипВсеСсылки() или сравните Ссылка.Метаданные() с коллекцией Метаданные.Документы. Справочник в эту проверку не попадёт.
Можно ли определить тип документа по пустой ссылке?
Да. У пустой ссылки сохраняется тип, поэтому ТипЗнч и Метаданные() работают. Не работает только ЗначениеЗаполнено, она вернёт Ложь.
Как получить все документы определённого вида в запросе?
Выберите их из таблицы документа напрямую: ИЗ Документ.РеализацияТоваровУслуг КАК Док. Условие по типу нужно, когда данные приходят из составного поля, например из регистратора.
Что вернёт Метаданные() для значения простого типа?
Исключение: у числа, строки и даты такого метода нет. Поэтому перед вызовом убедитесь, что значение — ссылка на объект информационной базы.