При разработке отчетов на платформе 1С с использованием Системы компоновки данных (СКД) программисты часто сталкиваются со специфической проблемой, которую в сообществе называют «лишней иерархией» или «дублированием строк на детальном уровне». Это происходит при выводе данных по иерархическому справочнику (например, «Статьи затрат», «Показатели», «Номенклатура» или «Подразделения»), когда у элементов-родителей есть собственные обороты или плановые показатели в базе данных.
В этой ситуации СКД по умолчанию выводит элемент дважды:
Давайте подробно разберем, почему стандартные фильтры запроса здесь не работают, и рассмотрим наиболее эффективные практические способы решения этой задачи, включая использование вычисляемых полей, функций ВычислитьВыражение и отборов после группировки.
Первое интуитивное решение, которое приходит в голову многим разработчикам — наложить условие непосредственно в тексте запроса, отсекая элементы, которые сами являются родителями для других элементов. Например, использовать конструкцию:
ГДЕ НЕ Показатель.Ссылка В (ВЫБРАТЬ Показатели.Родитель ИЗ Справочник.Показатели КАК Показатели)
Проанализируем, к чему это приведет на практике. Если мы исключим эти записи на уровне СУБД, то СКД просто не получит эти данные физически. Как результат, суммы по этим скрытым элементам не попадут в расчет ресурсов. Итоговые суммы по иерархическим группам станут неверными, так как СКД не сможет просуммировать то, чего нет в выборке. Наша задача состоит в том, чтобы СКД «видела» данные для расчета итогов, но визуально скрывала дублирующую детальную строку на нижнем уровне.
Это наиболее изящный способ, который позволяет решить проблему исключительно средствами настроек схемы компоновки данных, не прибегая к изменению запроса.
Разберем этот метод по шагам:
Перейдем на вкладку «Настройки» в конструкторе СКД, далее откроем вкладку «Пользовательские поля» и добавим новое Поле-выражение. Назовем его, например, ЛишняяИерархия.
В окне редактирования выражения для детальных и итоговых записей пропишем следующий код:
Выбор
Когда Уровень() = 0 Тогда Неопределено
Иначе ВычислитьВыражение("ИмяПоляГруппировки", , "Иерархия")
Конец
Обратите внимание: вместо "ИмяПоляГруппировки" вам нужно вписать реальное имя вашего поля иерархии из набора данных (например, "Показатель" или "Подразделение").
Как работает это выражение? Системная функция ВычислитьВыражение с третьим параметром "Иерархия" вычисляет значение родительского элемента для текущей строки в дереве компоновки. Если детальная запись совпадает со своим родителем, это поле вернет значение этого родителя.
Перенесем созданное поле ЛишняяИерархия в Выбранные поля нашей иерархической группировки. Для того чтобы это служебное поле не выводилось в финальный отчет и не путало пользователей, просто снимите с него флаг видимости (галочку) в списке выбранных полей.
Это самый важный шаг. Нам нужно установить отбор не на весь отчет, а конкретно на нашу иерархическую группировку. Выделим группировку в структуре отчета, перейдем на вкладку «Отбор» и добавим условие:
Показатель Не равно ЛишняяИерархия
В колонке «Применение» для этого элемента отбора обязательно выберем значение «После группировки или для иерархии».
Почему это решает проблему? Применение фильтра в режиме «После группировки» указывает процессору компоновки данных, что сначала нужно выбрать все записи из базы данных, рассчитать все итоги по ресурсам для каждого уровня дерева, построить саму иерархическую структуру и только на финальном этапе вывода применить наше условие фильтрации. Повторяющаяся нижняя строка будет скрыта, а итоговые суммы в вышестоящих группах останутся абсолютно верными.
Если структура вашего отчета сложная и первый метод требует адаптации, можно воспользоваться решением, которое нашел автор темы для точечного определения того, является ли строка группировочной.
Для этого в вычисляемых полях или условном оформлении используется выражение:
ВычислитьВыражение("Ссылка", , "Группировка", "Последняя")
Рассмотрим логику работы этой функции подробнее. Данное выражение позволяет получить контекст группировки. Если мы рассчитываем его на уровне детальной записи, оно вернет значение детальной записи. Если на уровне группировки — значение группировочного поля. Сравнивая значение системного поля Ссылка со значением, вычисленным по контексту группировки, мы можем однозначно определить, имеем ли мы дело с дубликатом родительской записи на нижнем уровне детальных записей, и гибко управлять ее выводом.
Если вы хотите полностью избежать появления дублей еще на этапе компоновки данных и при этом получить максимально прозрачную архитектуру отчета, рассмотрим альтернативный подход — разделение логики получения данных на два независимых набора данных.
Давайте проанализируем этот метод:
Родитель -> Ссылка).При такой архитектуре СКД самостоятельно сопоставит родительские записи с подчиненными. Поскольку в детальном наборе данных родительские элементы физически отсутствуют как подчиненные строки, дублирование не возникнет физически, а механизм итогов СКД рассчитает все ресурсы корректно для каждого уровня связи.
Иногда по требованиям бизнес-заказчиков структуру строк нельзя менять (например, если отчет выгружается в сторонние системы или Excel-шаблоны, где важен физический индекс строк), но визуально дубли все же требуется скрыть. В таких случаях мы можем прибегнуть к условному оформлению.
Выясним, как настроить такое оформление:
План, Факт).Показатель Равно ЛишняяИерархия (используя пользовательское поле из Способа 1).Таким образом, строка физически останется в дереве отчета, что предотвратит сдвиги ячеек, но для пользователя она станет абсолютно невидимой, не перегружая визуальное восприятие отчета.