Почему условие по предопределенному элементу в запросе 1С не работает и как это исправить?

Программист 1С v8.3 (Управляемые формы)
← На главную

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

Проанализируем, почему так происходит и как правильно составить такой запрос.

Анализ проблемы: несоответствие типов

Давайте разберем исходный запрос, который вызвал затруднения. Задача — выбрать документы, у которых реквизит СтатьяДвиженияДенежныхСредств равен определенному предопределенному элементу.


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

На первый взгляд, все логично. Мы сравниваем поле документа с неким эталонным значением. Но запрос не возвращает данных. Почему?

Ключевая ошибка кроется в сравнении значений разных типов.

  1. Реквизит документа РасходДСПлан.СтатьяДвиженияДенежныхСредств имеет тип СправочникСсылка.СтатьиДвиженияДенежныхСредств.
  2. Конструкция ЗНАЧЕНИЕ(Справочник.ИМ_ПредопределенныеЭлементыУниверсальный.СтатьяАгентскоеВознаграждение18) в языке запросов возвращает значение типа СправочникСсылка.ИМ_ПредопределенныеЭлементыУниверсальный.

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

В данном случае справочник ИМ_ПредопределенныеЭлементыУниверсальный используется как хранилище констант. Сам по себе предопределенный элемент СтатьяАгентскоеВознаграждение18 — это просто запись в этом справочнике. А нужная нам ссылка на статью ДДС хранится внутри этого элемента, в одном из его реквизитов (например, в реквизите с именем ЗначениеЭлемента).

Правильное решение: используем вложенный запрос

Чтобы запрос заработал корректно, нам нужно сравнивать поле документа не с самим предопределенным элементом-контейнером, а со значением его реквизита. Получить это значение можно прямо в тексте запроса с помощью вложенного подзапроса.

Этот подход делает запрос полностью самодостаточным и не требует передачи параметров извне.

Посмотрим на исправленный код:


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

Разберем эту конструкцию по шагам:

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

Использование оператора В вместо = является хорошей практикой при работе с подзапросами, которые могут теоретически вернуть несколько значений (хотя в данном случае вернется одно).

Почему же работал вариант с параметром?

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


// Неверный, но предполагаемый автором код
Запрос.УстановитьПараметр("НужнаяСтатья", Справочники.ИМ_ПредопределенныеЭлементыУниверсальный.СтатьяАгентскоеВознаграждение18);

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


// Правильный код для установки параметра
Запрос.УстановитьПараметр("НужнаяСтатья", Справочники.ИМ_ПредопределенныеЭлементыУниверсальный.СтатьяАгентскоеВознаграждение18.ЗначениеЭлемента);

В этом случае в параметр &НужнаяСтатья передается уже не ссылка на элемент справочника-контейнера, а значение его реквизита, которое имеет корректный тип. Поэтому запрос с параметром и отрабатывает успешно.

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

Ключевые выводы

← На главную