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