В процессе разработки отчетов в системе 1С:Предприятие 8 часто возникает классическая задача: предоставить пользователю возможность отбора по списку значений (например, по конкретным сотрудникам, номенклатуре или организациям). При этом логика должна работать следующим образом: если пользователь выбрал конкретные элементы, отчет формируется только по ним; если же отбор оставлен пустым, система должна проигнорировать это условие и вывести абсолютно все записи.
Начинающие разработчики часто пытаются решить эту задачу «в лоб», используя конструкцию проверки количества элементов в параметре-списке. Однако при этом они сталкиваются с тем, что запрос либо перестает работать, либо возвращает пустой результат, либо игнорирует другие важные условия фильтрации. Давайте подробно разберем, почему так происходит, и рассмотрим несколько вариантов решения — от простого исправления кода до профессионального использования возможностей Системы компоновки данных (СКД).
Выясним причины некорректного поведения исходного запроса и разберем по шагам правильные методики реализации такой логики.
---
В исходном примере разработчик использовал следующее условие в секции ГДЕ:
ГДЕ
&КоличествоСотрудников = 0
ИЛИ ВТ_Итоговая0.Сотрудник В (&СотрудникиОтчета)
И НЕ ВТ_Итоговая0.Сотрудник.ПометкаУдаления
И (ВТ_Итоговая0.Организация = &Организация
ИЛИ &Организация = ЗНАЧЕНИЕ(Справочник.Организации.ПустаяСсылка))
И (ВТ_Итоговая0.ДатаУвольнения = ДАТАВРЕМЯ(1, 1, 1)
И НЕ &УчитыватьУволенных
ИЛИ &УчитыватьУволенных
И ВТ_Итоговая0.ДатаУвольнения >= ДАТАВРЕМЯ(2014, 1, 1))
В этой конструкции скрываются сразу две серьезные ошибки, из-за которых запрос работает не так, как ожидается:
И имеет более высокий приоритет, чем оператор ИЛИ. Без явного указания скобок СУБД сгруппирует условия следующим образом: сначала выполнится вся правая часть (начиная со сравнения сотрудника со списком), а затем к результату применится условие ИЛИ &КоличествоСотрудников = 0. Это приводит к критической ошибке: если список сотрудников пуст, то условие &КоличествоСотрудников = 0 возвращает истину, и СУБД полностью игнорирует все последующие фильтры (пометку удаления, организацию и увольнение!). В отчет попадает абсолютно все, включая помеченные на удаление элементы и чужие организации.&КоличествоСотрудников не заполнялся в коде инициализации. В СКД или обычном запросе, если параметр не передан явно, система не сможет выполнить сопоставление, что приведет к ошибке выполнения запроса.Рассмотрим подробнее несколько способов решения этой задачи.
---
Если мы хотим оставить текущую структуру запроса, нам необходимо сделать две вещи: правильно расставить скобки в секции ГДЕ, чтобы локализовать условие по списку сотрудников, и передать в запрос реальное количество элементов списка.
Проанализируем исправленную секцию ГДЕ в тексте запроса:
ГДЕ
// Обязательно берем проверку списка в отдельные скобки!
(&КоличествоСотрудников = 0
ИЛИ ВТ_Итоговая0.Сотрудник В (&СотрудникиОтчета))
// Все остальные условия накладываются независимо от того, пустой список или нет
И НЕ ВТ_Итоговая0.Сотрудник.ПометкаУдаления
И (ВТ_Итоговая0.Организация = &Организация
ИЛИ &Организация = ЗНАЧЕНИЕ(Справочник.Организации.ПустаяСсылка))
И (ВТ_Итоговая0.ДатаУвольнения = ДАТАВРЕМЯ(1, 1, 1)
И НЕ &УчитыватьУволенных
ИЛИ &УчитыватьУволенных
И ВТ_Итоговая0.ДатаУвольнения >= ДАТАВРЕМЯ(2014, 1, 1))
Теперь в модуле объекта или формы, где происходит вызов отчета и устанавливаются параметры, необходимо передать количество элементов списка в параметр &КоличествоСотрудников:
// Установка параметров в компоновщике настроек СКД
ПараметрыДанных = Отчет.КомпоновщикНастроек.Настройки.ПараметрыДанных;
ПараметрыДанных.УстановитьЗначениеПараметра("Организация", Организация);
ПараметрыДанных.УстановитьЗначениеПараметра("СотрудникиОтчета", СотрудникиОтчета);
ПараметрыДанных.УстановитьЗначениеПараметра("УчитыватьУволенных", УчитыватьУволенных);
ПараметрыДанных.УстановитьЗначениеПараметра("КонецПериода", ТекущаяДата());
// Обязательно передаем количество элементов списка!
ПараметрыДанных.УстановитьЗначениеПараметра("КоличествоСотрудников", СотрудникиОтчета.Количество());
Этот способ рабочий, однако он перегружает код дополнительными параметрами и требует ручного контроля их заполнения.
---
Если ваш отчет строится на базе Системы компоновки данных (СКД), то использовать ручной подсчет элементов через дополнительный параметр — это плохой тон разработки. В СКД заложен мощный механизм динамического изменения текста запроса с помощью фигурных скобок {}.
Давайте разберем по шагам, как переписать условие профессионально. Мы можем полностью убрать параметр количества сотрудников и обернуть условие фильтрации в фигурные скобки. В этом случае СКД сама проанализирует: если параметр &СотрудникиОтчета заполнен пользователем, условие будет добавлено в итоговый SQL-запрос. Если же список пуст или галочка использования параметра снята, СКД просто вырежет это условие из текста запроса перед его отправкой на СУБД.
Посмотрим на пример реализации в тексте запроса СКД:
ГДЕ
НЕ ВТ_Итоговая0.Сотрудник.ПометкаУдаления
И (ВТ_Итоговая0.Организация = &Организация
ИЛИ &Организация = ЗНАЧЕНИЕ(Справочник.Организации.ПустаяСсылка))
И (ВТ_Итоговая0.ДатаУвольнения = ДАТАВРЕМЯ(1, 1, 1)
И НЕ &УчитыватьУволенных
ИЛИ &УчитыватьУволенных
И ВТ_Итоговая0.ДатаУвольнения >= ДАТАВРЕМЯ(2014, 1, 1))
// Необязательное условие компоновки данных:
{ГДЕ ВТ_Итоговая0.Сотрудник В (&СотрудникиОтчета)}
Преимущества этого подхода очевидны:
&КоличествоСотрудников.ИЛИ.---
Бывают ситуации, когда запрос выполняется в обычном коде (например, в обработке проведения документа или автозаполнении табличной части) без использования СКД. В таких случаях динамические фигурные скобки не поддерживаются.
Если мы не хотим передавать второй параметр количества, мы можем проверить список на пустоту прямо внутри запроса, сделав подзапрос к любой реальной таблице базы данных (например, к справочнику сотрудников). Рассмотрим этот изящный прием:
ГДЕ
(ВТ_Итоговая0.Сотрудник В (&СотрудникиОтчета)
ИЛИ НЕ 1 В
(ВЫБРАТЬ ПЕРВЫЕ 1 1
ИЗ Справочник.Сотрудники КАК Спр
ГДЕ Спр.Ссылка В (&СотрудникиОтчета)))
Как работает эта логика:
&СотрудникиОтчета пуст, то внутренний подзапрос ВЫБРАТЬ ПЕРВЫЕ 1... вернет пустой результат (ни одной строки не будет выбрано). Условие НЕ 1 В (ПустойРезультат) превратится в ИСТИНА, и левая часть скобок (фильтрация по сотруднику) будет проигнорирована. Будут выбраны все сотрудники.1. Условие НЕ 1 В (1) вернет ЛОЖЬ, и СУБД будет вынуждена применить фильтрацию по первой части условия: ВТ_Итоговая0.Сотрудник В (&СотрудникиОтчета).---
Еще один профессиональный подход при работе с обычными запросами вне СКД — динамическая сборка текста условия в коде на встроенном языке 1С. Это исключает нагрузку на СУБД по анализу сложных условий.
Рассмотрим пример реализации:
// Базовый текст запроса с маркером для вставки условия
ТекстЗапроса =
"ВЫБРАТЬ
| ВТ.Сотрудник,
| ВТ.Организация
|ИЗ
| ВТ_Итоговая0 КАК ВТ
|ГДЕ
| НЕ ВТ.Сотрудник.ПометкаУдаления
| //_УсловиеПоСотрудникам";
// Проверяем, заполнен ли список отбора
Если ЗначениеЗаполнено(СотрудникиОтчета) Тогда
СтрокаУсловия = "И ВТ.Сотрудник В (&СотрудникиОтчета)";
Запрос.УстановитьПараметр("СотрудникиОтчета", СотрудникиОтчета);
Иначе
СтрокаУсловия = ""; // Если список пуст, условие не накладываем
КонецЕсли;
// Заменяем маркер на сформированное условие
Запрос.Текст = СтрЗаменить(ТекстЗапроса, "//_УсловиеПоСотрудникам", СтрокаУсловия);
Этот метод гарантирует максимальную производительность, так как в СУБД уходит максимально оптимизированный SQL-текст без лишней логики ветвления.
---
При использовании Решения 1 разработчики часто наступают на скрытые «грабли». Вы можете написать абсолютно корректный код установки параметров, но отчет все равно будет возвращать пустой результат, когда отбор по сотрудникам не заполнен.
В чем причина?
Когда в пользовательских настройках СКД добавлен отбор или параметр со списком значений, платформа 1С при инициализации формы отчета может автоматически добавить в этот СписокЗначений одну пустую строку (со значением Неопределено или пустой ссылкой на справочник сотрудников). Пользователь видит на экране пустую таблицу отбора, но физически в объекте списка присутствует один элемент.
В итоге:
СотрудникиОтчета.Количество() возвращает 1 (ведь пустая строка — это тоже строка).&КоличествоСотрудников становится равен 1.&КоличествоСотрудников = 0 возвращает ЛОЖЬ.Как решить эту проблему?
Перед тем как рассчитывать количество элементов списка и передавать его в запрос, обязательно очищайте переданный список значений от пустых элементов. Напишем универсальную процедуру очистки списка:
// Очистка списка от пустых элементов (обход с конца коллекции)
Индекс = СотрудникиОтчета.Количество() - 1;
Пока Индекс >= 0 Цикл
ТекущееЗначение = СотрудникиОтчета[Индекс].Значение;
Если Не ЗначениеЗаполнено(ТекущееЗначение) Тогда
СотрудникиОтчета.Удалить(Индекс);
КонецЕсли;
Индекс = Индекс - 1;
КонецЦикла;
// Теперь количество будет определено абсолютно корректно!
ПараметрыДанных.УстановитьЗначениеПараметра("КоличествоСотрудников", СотрудникиОтчета.Количество());
ПараметрыДанных.УстановитьЗначениеПараметра("СотрудникиОтчета", СотрудникиОтчета);
Используя данный подход, вы гарантируете стабильную работу отчета независимо от того, как именно пользователь взаимодействовал с формой настройки параметров.