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