Как внедрить отбор по структурному подразделению в нетиповой отчет 1С без СКД

Программист 1С v8.3 (Обычные формы) 1C:Бухгалтерия Бухгалтерский учет IT и автоматизация бизнеса
← На главную

При сопровождении и доработке старых или специфических конфигураций на платформе 1С:Предприятие часто приходится сталкиваться с внешними отчетами, которые были разработаны без использования Системы компоновки данных (СКД). Такие отчеты выводятся "по-старинке": с помощью ручного обхода выборки запроса и вывода данных в макет табличного документа.

Одна из классических задач при доработке подобных отчетов — добавление нового необязательного фильтра (например, по измерению СтруктурноеПодразделение). При этом отчет должен работать в двух режимах: если подразделение на форме выбрано — фильтровать данные по нему, если не выбрано — выводить информацию по всему предприятию в целом. В этой статье мы подробно разберем, как правильно реализовать этот механизм, избежав критических ошибок проектирования и проблем с производительностью СУБД.

Почему классический синтаксис фигурных скобок не работает?

Разработчики, привыкшие к созданию отчетов на СКД, часто пытаются использовать расширение языка запросов, заключая условия в фигурные скобки:


| РегистрБухгалтерии.Типовой.Обороты(&НачалоПериода, &КонецПериода, , Счет В (&СчетаДоходов), , {СтруктурноеПодразделение = &СтруктурноеПодразделение}, , ) КАК ТиповойОбороты

Такой подход не сработает в простом отчете. Фигурные скобки {} обрабатываются исключительно процессором компоновки данных (СКД) или построителем отчетов. Если запрос выполняется напрямую через объект Запрос в коде модуля, платформа 1С проигнорирует фигурные скобки или выдаст синтаксическую ошибку. Нам придется использовать другие методы реализации необязательного отбора.

Решение 1. Логическое условие в самом запросе (Простой способ)

Самый очевидный способ реализовать необязательный отбор непосредственно внутри текста запроса — использовать проверку на пустое значение переданного параметра. Сделать это можно с помощью конструкции ВЫБОР КОГДА или через логический оператор ИЛИ.

Проанализируем пример такого условия в параметрах виртуальной таблицы:


ВЫБОР 
    КОГДА &СтруктурноеПодразделение = ЗНАЧЕНИЕ(Справочник.ПодразделенияОрганизаций.ПустаяСсылка) 
        ТОГДА ИСТИНА 
    ИНАЧЕ СтруктурноеПодразделение = &СтруктурноеПодразделение 
КОНЕЦ

Если переписать запрос целиком, то обращение к виртуальной таблице оборотов регистра бухгалтерии будет выглядеть следующим образом:


Запрос.Текст = 
"ВЫБРАТЬ
|	ТиповойОбороты.Счет КАК Счет,
|	ТиповойОбороты.СуммаОборот Дт КАК СуммаОборот
|ИЗ
|	РегистрБухгалтерии.Типовой.Обороты(
|			&НачалоПериода, 
|			&КонецПериода, , 
|			Счет В (&СчетаДоходов), , 
|			ВЫБОР 
|				КОГДА &СтруктурноеПодразделение = ЗНАЧЕНИЕ(Справочник.ПодразделенияОрганизаций.ПустаяСсылка) ТОГДА ИСТИНА 
|				ИНАЧЕ СтруктурноеПодразделение = &СтруктурноеПодразделение 
|			КОНЕЦ, , ) КАК ТиповойОбороты";

Запрос.УстановитьПараметр("НачалоПериода", НачПериода);
Запрос.УстановитьПараметр("КонецПериода", КонПериода);
Запрос.УстановитьПараметр("СчетаДоходов", СчетаДоходов);
Запрос.УстановитьПараметр("СтруктурноеПодразделение", СтруктурноеПодразделение); // Реквизит с формы отчета

Как это работает: Если пользователь оставил поле подразделения на форме пустым, параметр &СтруктурноеПодразделение равен пустой ссылке справочника. Условие ВЫБОР возвращает значение ИСТИНА для всех строк таблицы СУБД, и фильтрация не применяется. Если же конкретное подразделение выбрано, то срабатывает ветка ИНАЧЕ, которая жестко ограничивает выборку только указанным значением.

Важное предупреждение: Проблема производительности СУБД

Хотя первый способ привлекает своей простотой, использовать логические условия (особенно оператор ИЛИ и конструкции ВЫБОР КОГДА) в параметрах виртуальных таблиц крайне не рекомендуется. С точки зрения стандартов разработки фирмы «1С» это считается серьезной ошибкой оптимизации.

При трансляции такого запроса на уровень СУБД (например, MS SQL Server или PostgreSQL) оптимизатор запросов не сможет построить эффективный план выполнения. СУБД не сможет использовать существующие индексы по полю СтруктурноеПодразделение и будет вынуждена прибегнуть к сканированию всей таблицы (Table Scan / Index Scan) вместо быстрого поиска по индексу (Index Seek). На базах данных среднего и крупного объема это гарантированно приведет к существенному замедлению формирования отчета.

Решение 2. Динамическое формирование текста запроса (Рекомендуемый способ)

Чтобы обеспечить максимальную производительность базы данных и соблюсти стандарты разработки, нам необходимо убрать любые логические проверки "пустоты" параметров из самого SQL-запроса. Сделать это можно путем динамической модификации текста запроса в коде модуля перед его запуском на выполнение.

Рассмотрим по шагам, как это реализовать:

  1. В тексте запроса на месте будущего отбора установим текстовый маркер (шаблон), например, &ФильтрПодразделение.
  2. В коде модуля проверим, заполнено ли подразделение на форме.
  3. Если подразделение выбрано, заменим наш маркер на реальное условие фильтрации и передадим параметр в запрос.
  4. Если поле оставлено пустым, заменим маркер на нейтральное условие ИСТИНА (или уберем его вовсе).

Посмотрим на пример практической реализации данного алгоритма:


// Шаг 1: Задаем базовый текст запроса с маркером замены
ТекстЗапроса = 
"ВЫБРАТЬ
|	ТиповойОбороты.Счет КАК Счет,
|	ТиповойОбороты.СуммаОборотДт КАК СуммаОборот
|ИЗ
|	РегистрБухгалтерии.Типовой.Обороты(
|			&НачалоПериода, 
|			&КонецПериода, , 
|			Счет В (&СчетаДоходов), , 
|			&ФильтрПодразделение, , ) КАК ТиповойОбороты";

Запрос = Новый Запрос;

// Шаг 2: Проверяем заполнение реквизита на форме и динамически меняем текст
Если ЗначениеЗаполнено(СтруктурноеПодразделение) Тогда
    // Подразделение заполнено — внедряем строгое условие и устанавливаем параметр
    ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ФильтрПодразделение", "СтруктурноеПодразделение = &СтруктурноеПодразделение");
    Запрос.УстановитьПараметр("СтруктурноеПодразделение", СтруктурноеПодразделение);
Иначе
    // Подразделение не выбрано — заменяем маркер на ИСТИНА, отбор отключается
    ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ФильтрПодразделение", "ИСТИНА");
КонецЕсли;

// Шаг 3: Передаем готовый оптимизированный текст в объект запроса
Запрос.Текст = ТекстЗапроса;

// Установка остальных параметров
Запрос.УстановитьПараметр("НачалоПериода", НачПериода);
Запрос.УстановитьПараметр("КонецПериода", КонПериода);
Запрос.УстановитьПараметр("СчетаДоходов", СчетаДоходов);

РезультатЗапроса = Запрос.Выполнить();

Этот способ гарантирует, что СУБД получит максимально простой, чистый и предсказуемый SQL-код. Индексы таблицы будут задействованы оптимальным образом, и отчет выполнится мгновенно даже на базах данных объемом в терабайты.

Решение 3. Использование оператора «В» для множественного отбора

Иногда на форме нетипового отчета требуется предоставить пользователю возможность выбирать не одно конкретное подразделение, а сразу несколько (например, через СписокЗначений). В этом случае разработчики часто пытаются применить оператор В:

СтруктурноеПодразделение В (&СписокПодразделений)

Здесь важно помнить о критическом нюансе работы платформы 1С:Предприятие. Если передать пустой список значений или пустой массив в оператор В в запросе, то это условие вернет значение ЛОЖЬ. В результате запрос не вернет вообще ни одной строки (а не "все строки", как ошибочно предполагают начинающие программисты).

Поэтому при работе со списками значений динамическая модификация текста запроса также остается лучшим решением:


Если СписокПодразделений.Количество() > 0 Тогда
    ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ФильтрПодразделение", "СтруктурноеПодразделение В (&СписокПодразделений)");
    Запрос.УстановитьПараметр("СписокПодразделений", СписокПодразделений);
Иначе
    ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ФильтрПодразделение", "ИСТИНА");
КонецЕсли;

Специфика учета по подразделениям в «1С:Бухгалтерии» (БП)

Поскольку доработка выполняется в конфигурации «1С:Бухгалтерия предприятия», необходимо учитывать ее внутренние архитектурные особенности:

Проанализируем пример проверки использования учета по подразделениям в БП 3.0:


// Проверяем доступность ведения учета по подразделениям
УчетПоПодразделениямВключен = ПолучитьФункциональнуюОпцию("ИспользоватьУчетПоПодразделениям");

Если Не УчетПоПодразделениямВключен Тогда
    // Скрываем элемент на форме, чтобы пользователь не мог его выбрать
    Элементы.СтруктурноеПодразделение.Видимость = Ложь;
    
    // В тексте запроса полностью избавляемся от фильтрации по этому измерению
    ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ФильтрПодразделение", "ИСТИНА");
Иначе
    // Если учет включен, работаем по стандартной схеме динамической сборки отбора
    Если ЗначениеЗаполнено(СтруктурноеПодразделение) Тогда
        ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ФильтрПодразделение", "СтруктурноеПодразделение = &СтруктурноеПодразделение");
        Запрос.УстановитьПараметр("СтруктурноеПодразделение", СтруктурноеПодразделение);
    Else
        ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ФильтрПодразделение", "ИСТИНА");
    КонецЕсли;
КонецЕсли;

Альтернативный подход: Программный запуск СКД

Если отчет изначально написан без использования СКД, но логика его отборов и связей усложняется, отличным архитектурным решением станет компромиссный "гибридный" вариант. Вы можете создать Схему компоновки данных (СКД), в которой настроите гибкий запрос со всеми необходимыми необязательными параметрами в фигурных скобках {}.

При этом вам не нужно полностью переделывать форму отчета и интерфейс вывода. Достаточно использовать СКД исключительно как программный инструмент для получения "чистых" отфильтрованных данных:

  1. Создайте макет СКД внутри вашего нетипового внешнего отчета.
  2. Пропишите в нем полноценный запрос с удобными отборами через фигурные скобки.
  3. В модуле объекта отчета программно инициализируйте процессор компоновки данных.
  4. Передайте параметры, выполните компоновку и выгрузите результат в обычную ТаблицуЗначений.
  5. Передайте эту таблицу значений в старый проверенный алгоритм вывода данных в ручной табличный макет.

Такой подход избавит вас от необходимости писать громоздкие функции для строковой замены параметров (СтрЗаменить) и позволит использовать мощный, безопасный и оптимизированный механизм построения запросов СКД на уровне платформы 1С.

← На главную