Как в запросе 1С 7.7 отфильтровать документы по выбранным флагам на форме?

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

Работа с запросами в платформе 1С:Предприятие 7.7 имеет свои особенности, отличные от современной версии 8.3. Одной из частых задач, с которой сталкиваются начинающие разработчики при поддержке старых конфигураций («клюшек»), является динамическая фильтрация данных. Рассмотрим ситуацию, когда на форме отчета или обработки есть несколько чекбоксов (флагов), и нам необходимо выбрать документы только тех видов, которые отмечены пользователем.

В исходной задаче у нас есть три вида документов: КомандировкиРБ, КомандировкиЗагран и КомандировкиТрансп. Соответственно, на форме есть три флага: ДокументыРБ, ДокументыЗагран и ДокументыТрансп. Разберем несколько способов решения этой задачи, от самых простых до более оптимизированных.

Способ 1. Использование оператора Условие в тексте запроса

Самый понятный и надежный способ для платформы 7.7 — это использование конструкции |Условие(...) внутри текста запроса. Этот метод позволяет не менять структуру текста запроса, а лишь наложить логическое ограничение на выборку.

Мы можем проверять вид текущего документа и состояние соответствующего флага прямо в запросе. Для определения типа документа в 7.7 используется метод Вид().

Давайте посмотрим, как можно модифицировать код запроса. Нам нужно составить логическое выражение: «выбираем строку, если (документ — это РБ И флаг РБ включен) ИЛИ (документ — это Загран И флаг Загран включен)» и так далее.

Код процедуры формирования будет выглядеть следующим образом:


Процедура Сформировать()
    Запрос = СоздатьОбъект("Запрос");
    
    // Подготовим текст запроса
    ТекстЗапроса = 
    "//{{ЗАПРОС(Сформировать)
    |Период с ДатаС по ДатаПо;
    |ОбрабатыватьДокументы все;
    |Док = Документ.КомандировкиРБ.ТекущийДокумент,
    |      Документ.КомандировкиЗагран.ТекущийДокумент,
    |      Документ.КомандировкиТрансп.ТекущийДокумент;
    // ... здесь перечисление остальных полей запроса (ФИО, ДатаРег и т.д.) ...
    |Группировка Док;
    |Условие(
    |    ((Док.Вид() = ""КомандировкиРБ"") И (ДокументыРБ = 1)) ИЛИ 
    |    ((Док.Вид() = ""КомандировкиЗагран"") И (ДокументыЗагран = 1)) ИЛИ 
    |    ((Док.Вид() = ""КомандировкиТрансп"") И (ДокументыТрансп = 1))
    |);
    |"//}}ЗАПРОС
    ;

    Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда
        Возврат;
    КонецЕсли;

    // Далее код вывода в таблицу...
КонецПроцедуры

В данном случае переменные формы ДокументыРБ, ДокументыЗагран и ДокументыТрансп доступны внутри контекста запроса. Если флаг равен 1 (Истина), условие для данного вида документа выполняется.

Способ 2. Использование СпискаЗначений (Рекомендуемый)

Если видов документов много, писать длинные условия через «ИЛИ» становится неудобно и чревато ошибками. Более элегантный подход — сформировать список нужных видов документов перед запросом и использовать оператор вхождения В.

Давайте реализуем этот алгоритм:

  1. Создадим объект СписокЗначений.
  2. Проверим каждый флаг: если он установлен, добавим строковое название вида документа в список.
  3. В запросе проверим вхождение вида текущего документа в этот список.

Процедура Сформировать()
    // 1. Формируем список нужных видов
    СписокВидов = СоздатьОбъект("СписокЗначений");
    Если ДокументыРБ = 1 Тогда
        СписокВидов.ДобавитьЗначение("КомандировкиРБ");
    КонецЕсли;
    Если ДокументыЗагран = 1 Тогда
        СписокВидов.ДобавитьЗначение("КомандировкиЗагран");
    КонецЕсли;
    Если ДокументыТрансп = 1 Тогда
        СписокВидов.ДобавитьЗначение("КомандировкиТрансп");
    КонецЕсли;

    // Если ничего не выбрано, можно сразу выйти или не выполнять запрос
    Если СписокВидов.РазмерСписка() = 0 Тогда
        Предупреждение("Выберите хотя бы один вид документа!");
        Возврат;
    КонецЕсли;

    Запрос = СоздатьОбъект("Запрос");
    ТекстЗапроса = 
    "//{{ЗАПРОС(Сформировать)
    |Период с ДатаС по ДатаПо;
    |ОбрабатыватьДокументы все;
    |Док = Документ.КомандировкиРБ.ТекущийДокумент,
    |      Документ.КомандировкиЗагран.ТекущийДокумент,
    |      Документ.КомандировкиТрансп.ТекущийДокумент;
    // ... остальные поля ...
    |Группировка Док;
    |Условие(Док.Вид() В СписокВидов);
    |"//}}ЗАПРОС
    ;
    
    // Выполнение запроса и вывод...
КонецПроцедуры

Этот код намного чище и легче читается. Механизм запросов 1С 7.7 отлично работает с проверкой вхождения в список значений.

Способ 3. Динамическое формирование текста запроса

Этот метод часто используется профессионалами для оптимизации производительности. Идея заключается в том, чтобы не заставлять систему сканировать таблицы документов, которые нам не нужны. Мы будем «склеивать» текст запроса из кусочков в зависимости от галочек.

Сложность этого метода в 1С 7.7 заключается в синтаксисе: перечисление документов в секции |Док = ... и других полях должно разделяться запятыми. Если мы формируем текст программно, нам нужно следить за расстановкой запятых.

Разберем пример реализации:


Процедура Сформировать()
    Перем ТекстДок, ТекстПолей;
    
    ТекстДок = "";
    // Формируем часть запроса с определением переменной "Док"
    Если ДокументыРБ = 1 Тогда
        ТекстДок = ТекстДок + "Документ.КомандировкиРБ.ТекущийДокумент,";
    КонецЕсли;
    Если ДокументыЗагран = 1 Тогда
        ТекстДок = ТекстДок + "Документ.КомандировкиЗагран.ТекущийДокумент,";
    КонецЕсли;
    // ... аналогично для третьего ...

    // Удаляем последнюю лишнюю запятую
    Если СтрДлина(ТекстДок) > 0 Тогда
        ТекстДок = Лев(ТекстДок, СтрДлина(ТекстДок) - 1);
    Иначе
        Возврат; // Ничего не выбрано
    КонецЕсли;

    // Собираем итоговый текст
    ТекстЗапроса = 
    "//{{ЗАПРОС(Сформировать)
    |Период с ДатаС по ДатаПо;
    |ОбрабатыватьДокументы все;
    |Док = " + ТекстДок + ";
    // Обратите внимание: для остальных полей (ФИО, ДатаРег) 
    // придется писать аналогичную логику склейки, 
    // так как они берутся из конкретных документов.
    |Группировка Док;
    |"//}}ЗАПРОС
    ;
    
    // ...
КонецПроцедуры

Важное замечание: В вашем случае, когда в запросе выбирается множество реквизитов (ФИО, Табельный, Должность) из разных документов, динамическое формирование текста станет очень громоздким. Вам придется динамически формировать строку не только для переменной Док, но и для каждой переменной (ДатаРег, ФИО и т.д.), удаляя лишние запятые везде. Поэтому для данной конкретной задачи этот способ не рекомендуется из-за высокой сложности поддержки кода.

Способ 4. Фильтрация при выводе (Пост-обработка)

Если база данных небольшая, можно оставить запрос как есть (выбирать все документы), а фильтрацию осуществлять в момент обхода результатов запроса циклом. Это самый простой способ с точки зрения написания кода, но он создает лишнюю нагрузку на сервер, так как выбирает данные, которые потом отбрасываются.


// Запрос остается без изменений, выбирает всё
Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда 
    Возврат; 
КонецЕсли;
    
Пока Запрос.Группировка() = 1 Цикл
        
    ВидДок = Запрос.Док.Вид();
        
    // Проверяем: если документ РБ, а галочка снята - пропускаем
    Если (ВидДок = "КомандировкиРБ") И (ДокументыРБ = 0) Тогда
        Продолжить;
    КонецЕсли;
        
    Если (ВидДок = "КомандировкиЗагран") И (ДокументыЗагран = 0) Тогда
        Продолжить;
    КонецЕсли;
        
    Если (ВидДок = "КомандировкиТрансп") И (ДокументыТрансп = 0) Тогда
        Продолжить;
    КонецЕсли;
        
    // Если дошли сюда, значит документ подходит - выводим
    Таб.ВывестиСекцию("Строчка");
        
КонецЦикла;

Рекомендация по интерфейсу

Стоит также проанализировать удобство использования (UX). Если по логике работы пользователю нужно выбрать только один тип документов за раз, то вместо трех независимых флагов лучше использовать Переключатель (Radio Button) или Поле со списком на форме. Это исключит ситуацию, когда пользователь выбрал два несовместимых типа или не выбрал ничего.

Однако, если пользователю необходимо строить сводные отчеты (например, по РБ и Загран одновременно, но без Транспорта), то использование флагов и метода со СпискомЗначений (Способ 2) будет идеальным решением.

Резюме

Для решения вашей задачи в 1С 7.7 наиболее эффективным балансом между простотой кода и скоростью работы является использование Способа 2:

  1. Создайте СписокЗначений.
  2. Заполните его видами документов ("КомандировкиРБ" и т.д.) на основе состояния флагов.
  3. Добавьте в запрос строку |Условие(Док.Вид() В СписокВидов);.

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

← На главную