При разработке в среде 1С, особенно при использовании расширений, можно столкнуться с неочевидным поведением платформы. Одна из таких ситуаций — когда в запросе, написанном в модуле расширения, не удается получить доступ к реквизитам документа через точку. Например, мы пытаемся получить дату документа Заказ из реквизита справочника Назначение, но платформа «не видит» поле Дата.
Давайте подробно разберем, почему так происходит и какие существуют надежные способы решения этой проблемы. Проанализируем типичный сценарий: у нас есть справочник Назначения с реквизитом Заказ, который ссылается на документ. В запросе мы пишем конструкцию вида Назначение.Заказ.Дата, но ни автоподстановка в конструкторе запросов не работает, ни сам запрос не возвращает нужные данные.
Корень проблемы кроется в том, как платформа 1С обрабатывает метаданные объектов с составными типами данных в контексте расширения. Реквизит, который может ссылаться на объекты разных типов (например, на Документ.ЗаказПокупателя и Документ.ЗаказПоставщику), имеет составной тип.
Когда мы добавляем в расширение объект с таким реквизитом, платформа не всегда корректно передает полную информацию о структуре всех возможных типов в движок запросов. В результате, для запроса поле Назначение.Заказ выглядит не как ссылка на конкретный документ (ДокументСсылка.ЗаказПокупателя), а как абстрактная ЛюбаяСсылка (AnyRef).
У типа ЛюбаяСсылка нет своих реквизитов, таких как Дата, Номер или Контрагент. Поэтому, когда мы пытаемся обратиться к ним через точку, система не знает, из какой таблицы базы данных их нужно извлекать. Интересно, что в основной конфигурации или во внешней обработке тот же самый запрос может работать корректно, так как там контекст метаданных полный. В расширении же он оказывается ограниченным.
Теперь, когда мы понимаем причину, давайте рассмотрим практические способы решения этой задачи.
Это самый надежный и часто используемый метод для решения подобных проблем. Функция ВЫРАЗИТЬ() в языке запросов 1С позволяет явно указать системе, какого типа данные содержатся в поле. Таким образом, мы помогаем движку запросов правильно интерпретировать ссылку и получить доступ к ее реквизитам.
Как это работает? Мы "оборачиваем" наше поле в функцию ВЫРАЗИТЬ и указываем нужный тип документа.
Посмотрим на пример. Исходный запрос, который не работает в расширении:
ВЫБРАТЬ
Назначение.Заказ.Дата КАК ДатаЗаказа,
Назначение.Заказ.Номер КАК НомерЗаказа
ИЗ
Справочник.Назначения КАК Назначение
Чтобы его исправить, применим функцию ВЫРАЗИТЬ. Мы знаем, что в поле Заказ находится ссылка на документ ЗаказПокупателя.
ВЫБРАТЬ
ВЫРАЗИТЬ(Назначение.Заказ КАК Документ.ЗаказПокупателя).Дата КАК ДатаЗаказа,
ВЫРАЗИТЬ(Назначение.Заказ КАК Документ.ЗаказПокупателя).Номер КАК НомерЗаказа
ИЗ
Справочник.Назначения КАК Назначение
В этом варианте мы прямо говорим системе: «считай, что в поле Назначение.Заказ находится ссылка на Документ.ЗаказПокупателя, и возьми из него реквизиты Дата и Номер». После этого запрос будет выполнен корректно.
Оптимизация с помощью вложенного запроса. Если нужно получить много реквизитов из связанного документа, многократное использование ВЫРАЗИТЬ может сделать код громоздким. В этом случае удобно использовать вложенный запрос, чтобы выполнить приведение типа один раз:
ВЫБРАТЬ
ДанныеЗаказов.ЗаказСсылка.Дата КАК ДатаЗаказа,
ДанныеЗаказов.ЗаказСсылка.Номер КАК НомерЗаказа,
ДанныеЗаказов.ЗаказСсылка.Контрагент КАК Контрагент
ИЗ
(ВЫБРАТЬ
ВЫРАЗИТЬ(Назначение.Заказ КАК Документ.ЗаказПокупателя) КАК ЗаказСсылка
ИЗ
Справочник.Назначения КАК Назначение) КАК ДанныеЗаказов
Этот подход делает основной запрос более чистым и читаемым.
Обращение к реквизитам через точку — это, по сути, неявное (виртуальное) соединение таблиц, которое выполняет сама платформа. Когда этот механизм дает сбой, мы можем выполнить соединение таблиц явно с помощью конструкции ЛЕВОЕ СОЕДИНЕНИЕ (LEFT JOIN).
Этот способ часто оказывается не только более надежным, но и более производительным, так как план выполнения запроса становится более очевидным для СУБД.
Как это работает? Мы соединяем нашу исходную таблицу (Справочник.Назначения) с таблицей нужного нам документа (Документ.ЗаказПокупателя) по условию равенства ссылок.
Давайте перепишем наш запрос с использованием соединения:
ВЫБРАТЬ
ЗаказПокупателя.Дата КАК ДатаЗаказа,
ЗаказПокупателя.Номер КАК НомерЗаказа,
ЗаказПокупателя.Контрагент КАК Контрагент
ИЗ
Справочник.Назначения КАК Назначение
ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказПокупателя КАК ЗаказПокупателя
ПО Назначение.Заказ = ЗаказПокупателя.Ссылка
В этом запросе мы явно связываем таблицы по полю Назначение.Заказ и полю Ссылка из таблицы документа. После выполнения соединения все реквизиты документа ЗаказПокупателя становятся доступны напрямую через псевдоним таблицы ЗаказПокупателя. Этот метод полностью исключает неоднозначность с типами и является отличной альтернативой функции ВЫРАЗИТЬ.
Хотя два описанных выше способа являются основными для решения проблемы, стоит упомянуть еще несколько шагов, которые могут быть полезны при работе с подобными ошибками.
%APPDATA%\1C\1cv8.Подводя итог, можно сказать, что проблема невидимости реквизитов в запросах расширений — это известная особенность платформы, связанная с обработкой составных типов. Наиболее эффективными и правильными решениями являются явное приведение типов с помощью ВЫРАЗИТЬ или замена неявного соединения через точку на явное ЛЕВОЕ СОЕДИНЕНИЕ. Эти подходы позволяют создать надежный и предсказуемый код, который будет корректно работать в контексте любого расширения.