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