В разработке на платформе 1С часто возникает задача отфильтровать данные по значению, которое является константой или предопределенным элементом. Однако, простая на первый взгляд конструкция в запросе может не давать ожидаемого результата. Рассмотрим типичную ситуацию: запрос с условием на предопределенный элемент не отбирает документы, хотя при передаче того же значения через параметр все работает.
Проанализируем, почему так происходит и как правильно составить такой запрос.
Давайте разберем исходный запрос, который вызвал затруднения. Задача — выбрать документы, у которых реквизит СтатьяДвиженияДенежныхСредств равен определенному предопределенному элементу.
ВЫБРАТЬ
РасходДСПлан.Ссылка
ИЗ
Документ.РасходДСПлан КАК РасходДСПлан
ГДЕ
РасходДСПлан.СтатьяДвиженияДенежныхСредств = ЗНАЧЕНИЕ(Справочник.ИМ_ПредопределенныеЭлементыУниверсальный.СтатьяАгентскоеВознаграждение18)
На первый взгляд, все логично. Мы сравниваем поле документа с неким эталонным значением. Но запрос не возвращает данных. Почему?
Ключевая ошибка кроется в сравнении значений разных типов.
РасходДСПлан.СтатьяДвиженияДенежныхСредств имеет тип СправочникСсылка.СтатьиДвиженияДенежныхСредств.ЗНАЧЕНИЕ(Справочник.ИМ_ПредопределенныеЭлементыУниверсальный.СтатьяАгентскоеВознаграждение18) в языке запросов возвращает значение типа СправочникСсылка.ИМ_ПредопределенныеЭлементыУниверсальный.В итоге, система пытается сравнить ссылку на элемент одного справочника со ссылкой на элемент совершенно другого справочника. Такое сравнение никогда не даст истинного результата, и, как следствие, выборка будет пустой.
В данном случае справочник ИМ_ПредопределенныеЭлементыУниверсальный используется как хранилище констант. Сам по себе предопределенный элемент СтатьяАгентскоеВознаграждение18 — это просто запись в этом справочнике. А нужная нам ссылка на статью ДДС хранится внутри этого элемента, в одном из его реквизитов (например, в реквизите с именем ЗначениеЭлемента).
Чтобы запрос заработал корректно, нам нужно сравнивать поле документа не с самим предопределенным элементом-контейнером, а со значением его реквизита. Получить это значение можно прямо в тексте запроса с помощью вложенного подзапроса.
Этот подход делает запрос полностью самодостаточным и не требует передачи параметров извне.
Посмотрим на исправленный код:
ВЫБРАТЬ
РасходДСПлан.Ссылка
ИЗ
Документ.РасходДСПлан КАК РасходДСПлан
ГДЕ
РасходДСПлан.СтатьяДвиженияДенежныхСредств В (
ВЫБРАТЬ
ИМ_ПредопределенныеЭлементыУниверсальный.ЗначениеЭлемента
ИЗ
Справочник.ИМ_ПредопределенныеЭлементыУниверсальный КАК ИМ_ПредопределенныеЭлементыУниверсальный
ГДЕ
ИМ_ПредопределенныеЭлементыУниверсальный.Ссылка = ЗНАЧЕНИЕ(Справочник.ИМ_ПредопределенныеЭлементыУниверсальный.СтатьяАгентскоеВознаграждение18)
)
Разберем эту конструкцию по шагам:
ВЫБРАТЬ ИМ_ПредопределенныеЭлементыУниверсальный.ЗначениеЭлемента — мы выбираем не ссылку на элемент, а значение его реквизита ЗначениеЭлемента. Именно там хранится нужная нам ссылка типа СправочникСсылка.СтатьиДвиженияДенежныхСредств.ИЗ Справочник.ИМ_ПредопределенныеЭлементыУниверсальный — указываем, что данные берем из нашего справочника-хранилища.ГДЕ ИМ_ПредопределенныеЭлементыУниверсальный.Ссылка = ЗНАЧЕНИЕ(...) — здесь мы находим тот самый предопределенный элемент-контейнер, который служит нашей "константой".ГДЕ РасходДСПлан.СтатьяДвиженияДенежныхСредств В (...) — теперь условие внешнего запроса сравнивает поле документа с результатом, который вернул внутренний запрос. А внутренний запрос возвращает как раз значение нужного нам типа.Использование оператора В вместо = является хорошей практикой при работе с подзапросами, которые могут теоретически вернуть несколько значений (хотя в данном случае вернется одно).
Часто возникает путаница, потому что в коде на языке 1С при передаче параметра можно сразу обратиться к реквизиту. Изначально могло показаться, что работает такой код:
// Неверный, но предполагаемый автором код
Запрос.УстановитьПараметр("НужнаяСтатья", Справочники.ИМ_ПредопределенныеЭлементыУниверсальный.СтатьяАгентскоеВознаграждение18);
При такой установке параметра проблема с типами сохранилась бы. На самом деле, чтобы все заработало, в коде 1С необходимо явно обратиться к реквизиту:
// Правильный код для установки параметра
Запрос.УстановитьПараметр("НужнаяСтатья", Справочники.ИМ_ПредопределенныеЭлементыУниверсальный.СтатьяАгентскоеВознаграждение18.ЗначениеЭлемента);
В этом случае в параметр &НужнаяСтатья передается уже не ссылка на элемент справочника-контейнера, а значение его реквизита, которое имеет корректный тип. Поэтому запрос с параметром и отрабатывает успешно.
Однако решение с вложенным запросом является более предпочтительным, так как вся логика отбора данных инкапсулируется внутри текста запроса, делая его более читаемым и независимым от внешнего кода.
ЗНАЧЕНИЕ(Справочник.Имя.ПредопределенныйЭлемент) возвращает ссылку на сам элемент, а не на данные, хранящиеся в его реквизитах.Справочники.Имя.Элемент.ИмяРеквизита.