Для лучшего понимания материала статьи рекомендую сначала ознакомиться с предыдущей статьёй «Консоль запросов ЗУП с поддержкой обращения к методам программного интерфейса в запросах».
Недостатки универсального отчёта в контексте ЗУП
Универсальный отчёт удобен для простой аналитики, но в 1С:Зарплата и управление персоналом он быстро упирается в ограничения. Причина в том, что ключевые данные ЗУП — кадровые данные сотрудников, данные учета времени, расчётная база — не хранятся в регистрах в готовом виде, а формируются бизнес-логикой через программный интерфейс.
Универсальный отчёт работает с таблицами и не учитывает эту логику. В результате даже типовые задачи — например, план-фактный анализ рабочего времени — невозможно корректно решить без СКД или программного кода с вызовами ПИ и созданием временных таблиц.
Дополнительное ограничение — один источник данных. Универсальный отчёт плохо подходит для задач, где требуется объединять несколько источников с расчётной логикой, поэтому в ЗУП он чаще используется для вспомогательных выборок, а не для полноценной аналитики.
Именно этот разрыв между удобным инструментом и реальной сложностью предметной области ЗУП и стал отправной точкой для дальнейшего развития подхода.
От консоли запросов к универсальному отчёту: развитие идеи
Ранее эта проблема уже была решена на уровне запросов. В прошлой статье я показал консоль запросов и конструктор запросов, которые позволяют обращаться к методам программного интерфейса ЗУП прямо из текста запроса с помощью механизма исполняемых представлений. Консоль сама вызывает нужные методы, формирует временные таблицы и возвращает результат в виде обычного набора данных, с которым можно работать как с виртуальной таблицей.
Следующий шаг — перенести этот же подход в универсальный отчёт. Если отчёт изначально предназначен для быстрой аналитики, то его источник данных должен уметь работать не только с таблицами, но и с программным интерфейсом конфигурации. Именно поэтому в доработанном универсальном отчёте источником данных становится полноценный запрос с поддержкой исполняемых представлений.
В результате универсальный отчёт сохраняет привычный интерфейс и сценарии использования, но при этом получает доступ к программному интерфейсу ЗУП. Это позволяет строить сложные аналитические отчёты без дублирования бизнес-логики, без написания обработок и без глубокого погружения во внутреннее устройство конфигурации.
Новый универсальный отчёт: привычный интерфейс и возможность задать запрос
Доработанный универсальный отчёт для ЗУП сохраняет привычный пользовательский интерфейс. Все основные элементы — формирование отчёта, настройки, компоновка — остаются на своих местах.
Ключевое отличие заключается в появлении новой возможности — установить собственный запрос в качестве источника данных отчёта.

В открывшемся редакторе мы можем как установить уже готовый текст запроса (например написанный и отлаженный в консоли запросов), так и воспользоваться конструктором запросов

В этом конструкторе доступны исполняемые представления, сгруппированные по предметным областям ЗУП: кадровый учёт, учёт времени, расчёт заработной платы и другие.
С точки зрения конструктора они выглядят как обычные источники данных, однако по своей сути являются аналогом виртуальных таблиц, позволяющими получить доступ к программному интерфейсу ЗУП.
Практический пример: отчёт по отклонениям рабочего времени
В качестве практического примера рассмотрим задачу построения отчёта по отклонениям рабочего времени.
Цель отчёта — показать разницу между плановым и фактическим рабочим временем и предоставить удобный инструмент для анализа этих отклонений.
Отчёт должен решать сразу несколько задач:
-
показывать отклонения по рабочему времени за выбранный период;
-
поддерживать вывод данных в разных разрезах:
-
по сотрудникам,
-
по подразделениям,
-
по позициям штатного расписания;
-
-
предоставлять гибкие возможности отбора:
-
по периоду,
-
по подразделениям,
-
по конкретным сотрудникам или должностям.
-
Реализация отчёта
В качестве источника данных для универсального отчёта используется следующий запрос:
ВЫБРАТЬ
КадроваяИсторияСотрудниковПериоды.Сотрудник КАК Сотрудник,
КадроваяИсторияСотрудниковПериоды.НачалоПериода КАК НачалоПериода,
КадроваяИсторияСотрудниковПериоды.КонецПериода КАК КонецПериода,
КадроваяИсторияСотрудниковПериоды.ГоловнаяОрганизация КАК ГоловнаяОрганизация,
КадроваяИсторияСотрудниковПериоды.Организация КАК Организация,
КадроваяИсторияСотрудниковПериоды.Подразделение КАК Подразделение,
КадроваяИсторияСотрудниковПериоды.Должность КАК Должность,
КадроваяИсторияСотрудниковПериоды.ДолжностьПоШтатномуРасписанию КАК ДолжностьПоШтатномуРасписанию,
КадроваяИсторияСотрудниковПериоды.ВидДоговора КАК ВидДоговора
ПОМЕСТИТЬ ВТПериодыКадровойИстории
ИЗ
ИсполняемоеПредставление.РегистрСведений.КадроваяИсторияСотрудников.Периоды(
(ДатаНачала, ДатаОкончания) В
(ВЫБРАТЬ
&ДатаНачала КАК ДатаНачала,
&ДатаОкончания КАК ДатаОкончания),
ВключатьЗаписиНаНачалоПериода = ИСТИНА) КАК КадроваяИсторияСотрудниковПериоды
ГДЕ
КадроваяИсторияСотрудниковПериоды.ВидСобытия <> ЗНАЧЕНИЕ(Перечисление.ВидыКадровыхСобытий.Увольнение)
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ДанныеОВремени.Сотрудник КАК Сотрудник,
ДанныеОВремени.Дата КАК Дата,
НачалоПериода(ДанныеОВремени.Дата, Месяц) КАК Месяц,
ДанныеОВремени.ВидУчетаВремени КАК ВидУчетаВремени,
КадроваяИсторияСотрудниковПериоды.Организация КАК Организация,
КадроваяИсторияСотрудниковПериоды.Подразделение КАК Подразделение,
КадроваяИсторияСотрудниковПериоды.Должность КАК Должность,
КадроваяИсторияСотрудниковПериоды.ДолжностьПоШтатномуРасписанию КАК ДолжностьПоШтатномуРасписанию,
КадроваяИсторияСотрудниковПериоды.ВидДоговора КАК ВидДоговора,
СУММА(ДанныеОВремени.ДниФакт) КАК ДниФакт,
СУММА(ДанныеОВремени.ЧасыФакт) КАК ЧасыФакт,
СУММА(ДанныеОВремени.ДниПлан) КАК ДниПлан,
СУММА(ДанныеОВремени.ЧасыПлан) КАК ЧасыПлан,
СУММА(ДанныеОВремени.ДниПлан - ДанныеОВремени.ДниФакт) КАК ДниОтклонение,
СУММА(ДанныеОВремени.ЧасыПлан - ДанныеОВремени.ЧасыФакт) КАК ЧасыОтклонение
ИЗ
(ВЫБРАТЬ
ДанныеУчетаВремениСотрудников.Сотрудник КАК Сотрудник,
ДанныеУчетаВремениСотрудников.Дата КАК Дата,
ДанныеУчетаВремениСотрудников.ВидУчетаВремени КАК ВидУчетаВремени,
ДанныеУчетаВремениСотрудников.Дни КАК ДниФакт,
ДанныеУчетаВремениСотрудников.Часы КАК ЧасыФакт,
0 КАК ДниПлан,
0 КАК ЧасыПлан
ИЗ
ИсполняемоеПредставление.ДанныеУчетаВремениСотрудников(
(Сотрудник, ДатаНачала, ДатаОкончания) В
(ВЫБРАТЬ РАЗЛИЧНЫЕ
ВТОтборСотрудников.Сотрудник КАК Сотрудник,
&ДатаНачала КАК ДатаНачала,
&ДатаОкончания КАК ДатаОкончания
ИЗ
ВТПериодыКадровойИстории КАК ВТОтборСотрудников),
ДатаНачала = &ДатаНачала,
ДатаОкончания = &ДатаОкончания,
ДатаАктуальности = &ДатаОкончания) КАК ДанныеУчетаВремениСотрудников
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
ПлановоеВремяСотрудников.Сотрудник,
ПлановоеВремяСотрудников.Дата,
ПлановоеВремяСотрудников.ВидУчетаВремени,
0,
0,
ПлановоеВремяСотрудников.ДниПлан,
ПлановоеВремяСотрудников.ЧасыПлан
ИЗ
ИсполняемоеПредставление.ПлановоеВремяСотрудников(
(Сотрудник, ДатаНачала, ДатаОкончания, ДатаАктуальности) В
(ВЫБРАТЬ РАЗЛИЧНЫЕ
ВТОтборСотрудников.Сотрудник КАК Сотрудник,
&ДатаНачала КАК ДатаНачала,
&ДатаОкончания КАК ДатаОкончания,
&ДатаОкончания КАК ДатаАктуальности
ИЗ
ВТПериодыКадровойИстории КАК ВТОтборСотрудников
ЛЕВОЕ СОЕДИНЕНИЕ ИсполняемоеПредставление.Периоды(
НачалоИнтервала = &ДатаНачала,
ОкончаниеИнтервала = &ДатаОкончания,
Периодичность = "МЕСЯЦ") КАК Периоды
ПО Периоды.Период МЕЖДУ &ДатаНачала И &ДатаОкончания)
) КАК ПлановоеВремяСотрудников) КАК ДанныеОВремени
{ЛЕВОЕ СОЕДИНЕНИЕ ВТПериодыКадровойИстории КАК КадроваяИсторияСотрудниковПериоды
ПО ДанныеОВремени.Сотрудник = КадроваяИсторияСотрудниковПериоды.Сотрудник
И ДанныеОВремени.Дата МЕЖДУ КадроваяИсторияСотрудниковПериоды.НачалоПериода И КадроваяИсторияСотрудниковПериоды.КонецПериода}
ГДЕ
ДанныеОВремени.ВидУчетаВремени <> ЗНАЧЕНИЕ(Справочник.ВидыИспользованияРабочегоВремени.РабочееВремя)
СГРУППИРОВАТЬ ПО
ДанныеОВремени.Сотрудник,
ДанныеОВремени.Дата,
ДанныеОВремени.ВидУчетаВремени,
КадроваяИсторияСотрудниковПериоды.ГоловнаяОрганизация,
КадроваяИсторияСотрудниковПериоды.Организация,
КадроваяИсторияСотрудниковПериоды.Подразделение,
КадроваяИсторияСотрудниковПериоды.Должность,
КадроваяИсторияСотрудниковПериоды.ДолжностьПоШтатномуРасписанию,
КадроваяИсторияСотрудниковПериоды.ВидДоговора
Пояснение структуры запроса
1. Формирование кадровых периодов сотрудников
Первая часть запроса формирует временную таблицу ВТПериодыКадровойИстории.
Она содержит периоды действия кадровых данных сотрудников за выбранный интервал времени.
Ключевые моменты:
-
используется исполняемое представление регистра КадроваяИсторияСотрудников.Периоды;
-
возвращаются именно периоды, а не отдельные записи;
-
исключаются события увольнения;
-
таблица используется далее для отбора сотрудников и привязки аналитик.
2. Получение фактического рабочего времени
Фактическое рабочее время получается через исполняемое представление
ДанныеУчетаВремениСотрудников.
Здесь важно, что:
-
данные рассчитываются программным интерфейсом ЗУП;
-
учитываются все отклонения и виды учёта времени;
-
отбор сотрудников формируется на основе кадровых периодов.
3. Получение планового рабочего времени
Плановое время формируется через исполняемое представление
ПлановоеВремяСотрудников.
Этот блок:
-
рассчитывает план по графикам работы и индивидуальным графикам;
-
учитывает дату актуальности;
-
возвращает данные в том же формате, что и факт.
4. Привязка к кадровым данным
После объединения плановых и фактических данных выполняется привязка к кадровым периодам сотрудников. Это позволяет корректно анализировать рабочее время в разрезе подразделений, должностей и позиций штатного расписания с учётом кадровых изменений.
Ключевой момент здесь в том, что кадровая история уже представлена в виде готовой таблицы интервалов. Благодаря этому нет необходимости выполнять срез кадровых данных на каждую дату из учёта времени, что упрощает запрос и положительно сказывается на его производительности.
Настройки СКД
После того как запрос установлен и используется в качестве источника данных, дальнейшая работа с отчётом выполняется стандартными средствами системы компоновки данных. Это важный момент — несмотря на нетипичный источник данных, отчёт настраивается так же, как обычный универсальный отчёт.
Отбор данных
В рамках рассматриваемого примера нас интересуют отклонения только по рабочему времени, поэтому первым шагом зададим соответствующий фильтр.

Обратим внимание на важный момент: отбор задаётся через точку, так же как и в типовом универсальном отчёте. Это означает, что все поля, полученные запросом, доступны для фильтрации стандартным способом, без каких-либо дополнительных настроек или ограничений.
Пользовательские поля
Так как отчёт должен рассчитывать итоговые значения по группировкам, добавим пользовательские поля через механизм формул.

Структура отчёта
На завершающем этапе настраивается структура отчёта — группировки и порядок вывода данных.

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

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


Итог
В результате мы получили полноценный отчёт для план-фактного анализа рабочего времени с отклонениями по часам (и при необходимости — по дням), с гибкими разрезами (сотрудник, подразделение, должность/позиция штатного расписания) и стандартными возможностями СКД: отборами, группировками, пользовательскими формулами и оформлением.
Самое важное — благодаря тому, что источником данных стал полноценный запрос с поддержкой исполняемых представлений, отчёт собирается без переписывания бизнес-логики ЗУП и без разработки отдельной обработки.
На практике настройка такого отчёта «с нуля» заняла 15–20 минут: установить запрос, задать отборы, настроить формулы и структуру, сохранить вариант и разместить его в нужном разделе.
Заглянем под капот: какой запрос уходит в движок исполняемых представлений
Теперь посмотрим, что происходит при выполнении отчёта «под капотом». В первую очередь нас интересует запрос формирования интервалов кадровой истории — именно он создаёт таблицу ВТПериодыКадровойИстории, от которой дальше зависит корректная привязка аналитик и производительность.
Ниже приведён запрос, который фактически уходит в движок исполняемых представлений:
ВЫБРАТЬ
КадроваяИсторияСотрудниковПериоды1.Сотрудник КАК Сотрудник,
КадроваяИсторияСотрудниковПериоды1.НачалоПериода КАК НачалоПериода,
КадроваяИсторияСотрудниковПериоды1.КонецПериода КАК КонецПериода,
КадроваяИсторияСотрудниковПериоды1.Организация КАК Организация,
КадроваяИсторияСотрудниковПериоды1.Подразделение КАК Подразделение
ПОМЕСТИТЬ ВТПериодыКадровойИстории
ИЗ
ИсполняемоеПредставление.РегистрСведений.КадроваяИсторияСотрудников.Периоды(
(ДатаНачала, ДатаОкончания) В
(ВЫБРАТЬ
&ДатаНачала КАК ДатаНачала,
&ДатаОкончания КАК ДатаОкончания),
ВключатьЗаписиНаНачалоПериода = ИСТИНА) КАК КадроваяИсторияСотрудниковПериоды1
ГДЕ
КадроваяИсторияСотрудниковПериоды1.ВидСобытия <> ЗНАЧЕНИЕ(Перечисление.ВидыКадровыхСобытий.Увольнение)
И КадроваяИсторияСотрудниковПериоды1.Организация = &П2
Здесь хорошо видно два важных момента.
1) Отбор из СКД “проталкивается” в самый первый запрос
Хотя СКД напрямую не работает с исполняемыми представлениями как с типовыми таблицами, оптимизатор СКД всё равно применяется. В результате отбор по организации не остаётся на уровне отчёта “после факта”, а попадает в ГДЕ уже на этапе формирования кадровых периодов:
-
в отчёте настроили отбор по
Организация; -
оптимизатор встроил это условие в первый запрос (
...Организация = &П2).
2) Движок исполняемых представлений делегирует отбор дальше
Дальше движок исполняемых представлений принимает этот отбор и делегирует его вниз — на получение данных кадровой истории. За счёт этого программный интерфейс ЗУП выполнит оптимальный запрос: система отбирает только нужную организацию и не делает лишней работы.
ВЫБРАТЬ
...
ПОМЕСТИТЬ ВТКадроваяИсторияСотрудниковПериоды1
ИЗ
ВТФильтрКадроваяИсторияСотрудниковПериоды1 КАК ИзмеренияДаты
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.КадроваяИсторияСотрудниковИнтервальный КАК РегистрСведений
ПО (РегистрСведений.ДатаНачала >= ДОБАВИТЬКДАТЕ(НАЧАЛОПЕРИОДА(ИзмеренияДаты.ДатаНачала, ДЕНЬ), ДЕНЬ, 1)
И РегистрСведений.ДатаНачала <= ВЫБОР
КОГДА ИзмеренияДаты.ДатаОкончания = ДАТАВРЕМЯ(1, 1, 1)
ТОГДА ДАТАВРЕМЯ(3999, 12, 31, 23, 59, 59)
ИНАЧЕ КОНЕЦПЕРИОДА(ИзмеренияДаты.ДатаОкончания, ДЕНЬ)
КОНЕЦ
ИЛИ НАЧАЛОПЕРИОДА(РегистрСведений.ДатаНачала, ДЕНЬ) <= КОНЕЦПЕРИОДА(ИзмеренияДаты.ДатаНачала, ДЕНЬ)
И РегистрСведений.ДатаОкончания >= КОНЕЦПЕРИОДА(ИзмеренияДаты.ДатаНачала, ДЕНЬ))
И (РегистрСведений.ДатаОкончания >= КОНЕЦПЕРИОДА(РегистрСведений.ДатаНачала, ДЕНЬ))
ГДЕ
РегистрСведений.Организация = &ВТКадроваяИсторияСотрудниковПериоды1_Параметр1
Дополнительно видно, что в запросе выбираются только поля, которые реально используются в отчёте. Это снижает объём передаваемых данных и ускоряет выполнение (особенно на больших базах).
Открытый исходный код и продолжение темы
Используемый в статье подход реализован в виде open source-проекта: исходный код доступен, реализацию исполняемых представлений при необходимости адаптировать под свои задачи и свою конфигурацию.
Если тема интересна глубже — у меня есть отдельный доклад о следующем шаге развития: Как я заставил ИИ понимать ЗУП: современный RAG-pipeline для генерации корректных запросов (в том числе с учётом программного интерфейса и исполняемых представлений). В докладе расскажу про архитектуру, практические сценарии и то, как превратить «генерацию текста» в реально рабочий инструмент, который ускоряет разработку запросов и отчётов.
Проверено на следующих конфигурациях и релизах:
- Зарплата и кадры государственного учреждения КОРП, редакция 3, релизы 3.1.36.41
- Зарплата и кадры государственного учреждения, редакция 3, релизы 3.1.36.41
- Зарплата и управление персоналом, редакция 3.1, релизы 3.1.36.41
- Зарплата и управление персоналом КОРП, редакция 3.1, релизы 3.1.36.41
- 1С:ERP Управление предприятием 2, релизы 2.5.25.83
Вступайте в нашу телеграмм-группу Инфостарт
