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

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

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

В чем суть проблемы и почему она возникает?

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


ВЫБРАТЬ
    ВТ_Итог.Счет КАК Счет,
    ВТ_Итог.Организация КАК Организация,
    ВТ_Итог.Подразделение КАК Подразделение,
    ВТ_Итог.Субконто1 КАК Субконто1,
    ВТ_Итог.Субконто2 КАК Субконто2,
    ВТ_Итог.Субконто3 КАК Субконто3,
    ВТ_Итог.СуммаОборотДт КАК СуммаОборотДт,
    ВТ_Итог.СуммаРаздельногоУчетаНДС КАК СуммаРаздельногоУчетаНДС
ИЗ
    ВТ_Итог КАК ВТ_Итог
ГДЕ
    ВТ_Итог.СуммаОборотДт <> ВТ_Итог.СуммаРаздельногоУчетаНДС

Или использует секцию ИМЕЮЩИЕ непосредственно при группировке детальных записей:


ИМЕЮЩИЕ
    СУММА(ВТ_Суммы.СуммаРаздельногоУчетаНДС) <> СУММА(ВТ_Суммы.СуммаОборотДт)

Когда этот запрос помещается в СКД с включенным флагом «Автозаполнение», система начинает управлять текстом запроса динамически. СКД пытается оптимизировать запрос под те поля, которые пользователь выбрал в настройках компоновщика на форме. Если пользователь убирает из выбранных полей, например, Субконто1 или Подразделение, СКД делает следующее:

В этот момент и происходит сбой. Если условие фильтрации сумм жестко зашито в секцию ГДЕ или ИМЕЮЩИЕ внутри SQL-запроса, СУБД выполняет расчет разницы на уровне тех группировок, которые прописаны в базе данных (до субконто). Когда пользователь отключает эти поля на форме, СКД выводит сгруппированный результат без учета отключенных полей, но математический отбор уже отсек строки на более детальном уровне! В итоге мы получаем искаженные данные или полное отсутствие фильтрации.

Также негативную роль играют фигурные скобки {} в запросе. Если вы помещаете условие в фигурные скобки, например: {ГДЕ (ВТ_Итог.СуммаРаздельногоУчетаНДС <> ВТ_Итог.СуммаОборотДт) КАК ОтборПоСумме}, то при невыбранном хотя бы одном поле, участвующем в условии, СКД считает это условие необязательным и просто вырезает его из итогового SQL-запроса.

Рассмотрим три надежных способа решения этой проблемы, которые соответствуют лучшим практикам разработки в среде 1С.

Способ 1. Перенос группировки и фильтрации на уровень ресурсов СКД (Рекомендуемый)

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

Разберем этот способ по шагам:

  1. Упростите исходный запрос. Уберите из итогового запроса пакетного запроса секцию СГРУППИРОВАТЬ ПО и условия ИМЕЮЩИЕ или ГДЕ, сравнивающие суммы. Запрос должен возвращать «плоскую» детальную таблицу со всеми необходимыми аналитиками и детальными суммами.
  2. Настройте ресурсы. Перейдите на вкладку «Ресурсы» в конструкторе схемы компоновки данных. Добавьте туда поля СуммаОборотДт и СуммаРаздельногоУчетаНДС. В качестве выражения для обоих полей укажите функцию Сумма(СуммаОборотДт) и Сумма(СуммаРаздельногоУчетаНДС) соответственно.
  3. Наложите отбор на уровне группировок. Это ключевой момент. Если наложить отбор СуммаОборотДт <> СуммаРаздельногоУчетаНДС в общих настройках отчета, СКД применит его к детальным записям (в секции ГДЕ), что приведет к неверному результату. Чтобы фильтрация происходила после группировки данных:
    • Перейдите на вкладку «Настройки» в СКД.
    • Выберите в структуре отчета конкретную группировку (например, группировку верхнего уровня или строки таблицы).
    • Внизу перейдите на вкладку «Отбор» (это локальный отбор для конкретной группировки).
    • Добавьте условие: СуммаОборотДт Не равно СуммаРаздельногоУчетаНДС.

При таком подходе СКД сначала динамически сгруппирует данные по тем полям, которые выбрал пользователь, затем рассчитает итоги по ресурсам для этой группировки, и только после этого отсечет строки, где суммы сошлись. Данный метод работает безотказно при любых манипуляциях пользователя со структурой отчета.

Способ 2. Использование вычисляемого поля для расчета разницы

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

Разберем данный алгоритм:

  1. В конструкторе СКД перейдите на вкладку «Вычисляемые поля».
  2. Добавьте новое поле с именем РазницаСумм. В качестве выражения укажите: ЕСТЬNULL(СуммаОборотДт, 0) - ЕСТЬNULL(СуммаРаздельногоУчетаНДС, 0).
  3. Перейдите на вкладку «Ресурсы» и сделайте поле РазницаСумм ресурсом. Выражение для расчета: Сумма(РазницаСумм).
  4. В настройках отчета (на уровне нужной группировки или в общих настройках, если вы хотите фильтровать итоговые строки) добавьте отбор: РазницаСумм Не равно 0. Для удобства пользователя этот отбор можно включить в состав пользовательских настроек с быстрым доступом.

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

Способ 3. Отключение автозаполнения и явное управление компоновкой

Иногда возникают ситуации, когда логика запроса настолько сложна (например, используются специфические соединения или виртуальные таблицы с жесткими параметрами), что «сгладить» запрос до детальных записей невозможно. В таком случае нам придется отключить стандартный оптимизатор СКД и настроить схему вручную.

Выполните следующие действия:

  1. На вкладке «Набор данных - запрос» снимите флаг «Автозаполнение». Это запретит СКД произвольно модифицировать ваш текст запроса и исключать из него поля группировок.
  2. Перейдите на вкладку «Поля» набора данных и вручную опишите все поля, определив их роли (измерения, периоды) и ограничения (запрет группировки, запрет отбора, если это необходимо).
  3. В тексте запроса используйте необязательные поля и условия, заключенные в фигурные скобки {}, но делайте это осознанно. Например, секцию описания полей компоновки в запросе можно оформить так:

ВЫБРАТЬ
    ВТ_Итог.Счет КАК Счет,
    ВТ_Итог.Организация КАК Организация,
    ВТ_Итог.СуммаОборотДт КАК СуммаОборотДт,
    ВТ_Итог.СуммаРаздельногоУчетаНДС КАК СуммаРаздельногоУчетаНДС
{ВЫБРАТЬ
    Счет.*,
    Организация.*,
    СуммаОборотДт,
    СуммаРаздельногоУчетаНДС}
ИЗ
    ВТ_Итог КАК ВТ_Итог
{ГДЕ
    ВТ_Итог.Счет.*,
    ВТ_Итог.Организация.*}

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

Резюме

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

← На главную