При разработке отчетов по персоналу в таких конфигурациях, как УПП 1.3 или ЗиУП 2.5, программисты часто сталкиваются с задачей вывода данных в виде календарной сетки. Основная сложность заключается в том, что в регистрах сведений (например, ГрафикОтпусковОрганизаций) данные хранятся в виде периодов (дата начала и дата окончания) или даже просто как записи о смене состояния. Чтобы превратить эти две даты в непрерывную последовательность дней в колонках отчета СКД, необходимо применить специфические приемы построения запроса.
В этой статье мы подробно разберем, как правильно спроектировать запрос, чтобы "развернуть" отпускные периоды по календарным дням, и как настроить систему компоновки данных для получения наглядного результата.
Рассмотрим типовую ситуацию: у нас есть периодический регистр сведений, где фиксируется состояние сотрудника. В одной записи указано, что с 01.06.2024 сотрудник уходит в ежегодный отпуск, а в другой записи — что с 15.06.2024 он снова "Свободен". Наша цель — получить в отчете 14 строк (или колонок) для этого сотрудника, по одной на каждый день отпуска.
Проблема простого запроса к регистру заключается в том, что он вернет только две строки (точки изменения состояния). Попытка соединить это с календарем напрямую по равенству дат также даст лишь две точки. Для решения нам нужно создать интервальную таблицу и соединить её с таблицей всех дат.
Прежде всего, нам нужен источник всех дней за выбранный период. В типовых конфигурациях 1С для этого идеально подходит регистр сведений РегламентированныйПроизводственныйКалендарь. Проанализируем, как получить список дат в запросе:
ВЫБРАТЬ
Календарь.ДатаКалендаря КАК Дата
ПОМЕСТИТЬ ВТ_ВсеДаты
ИЗ
РегистрСведений.РегламентированныйПроизводственныйКалендарь КАК Календарь
ГДЕ
Календарь.ДатаКалендаря МЕЖДУ &НачалоПериода И &КонецПериода
;
Если в вашей базе календарь не заполнен, можно использовать альтернативные методы генерации последовательности чисел, но использование готового регистра — наиболее надежный путь.
Выясним причину, по которой обычный выбор из регистра не подходит. Нам нужно, чтобы в каждой строке была и дата начала, и дата окончания. Рассмотрим ситуацию для УПП 1.3, где в регистре ГрафикОтпусковОрганизаций есть ресурсы Период и ДатаОкончания. Если же ДатаОкончания отсутствует (как в регистрах состояний), её нужно вычислить через срез последних или соединение с "самим собой" для поиска следующей даты изменения состояния.
Рассмотрим вариант, когда даты начала и конца известны:
ВЫБРАТЬ
График.Сотрудник КАК Сотрудник,
График.Организация КАК Организация,
График.Период КАК ДатаНачала,
График.ДатаОкончания КАК ДатаОкончания
ПОМЕСТИТЬ ВТ_Отпуска
ИЗ
РегистрСведений.ГрафикОтпусковОрганизаций КАК График
ГДЕ
График.Состояние = ЗНАЧЕНИЕ(Перечисление.ТипыПериодическихЗадачРаботника.ОтпускЕжегодный)
И (График.Период <= &КонецПериода И График.ДатаОкончания >= &НачалоПериода)
;
Обратите внимание на условие в ГДЕ. Мы используем перекрестную проверку периодов, чтобы захватить отпуска, которые начались раньше выбранного периода или закончились позже него.
Теперь разберем самый важный этап — левое соединение. Мы берем таблицу всех дат (ВТ_ВсеДаты) и присоединяем к ней наши отпуска. Условие связи должно быть не по равенству, а по вхождению даты в интервал (оператор МЕЖДУ или сравнения >= и <=).
ВЫБРАТЬ
Календарь.Дата КАК ДатаКалендаря,
Данные.Сотрудник КАК Сотрудник,
ВЫБОР
КОГДА Данные.Сотрудник ЕСТЬ NULL
ТОГДА 0
ИНАЧЕ 1
КОНЕЦ КАК ВОтпуске
ИЗ
ВТ_ВсеДаты КАК Календарь
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Отпуска КАК Данные
ПО Календарь.Дата МЕЖДУ Данные.ДатаНачала И Данные.ДатаОкончания
;
Проанализируем результат: для каждого дня из календаря система проверит, попадает ли этот день в какой-либо интервал отпуска. Если попадает — в поле ВОтпуске будет 1, если нет — 0. Если один и тот же сотрудник имеет два разных отпуска в один день (ошибка учета), в результате появятся дубли, которые легко отсечь с помощью ключевого слова РАЗЛИЧНЫЕ или последующей группировки.
Существует еще один продвинутый способ, который часто оказывается эффективнее в сложных отчетах. Можно использовать два набора данных в СКД:
СрезПоследних по регистру сведений на конкретную дату.В настройках Связи наборов данных мы связываем их по сотруднику, а в качестве параметра Период для второго набора передаем поле Дата из первого набора. Таким образом, СКД сама выполнит "срез" на каждый день для каждого сотрудника. Это позволяет корректно обрабатывать ситуации, когда отпуск прерывается больничным или отзывом, так как СрезПоследних всегда вернет актуальное состояние на конкретные сутки.
Чтобы отчет выглядел как календарь (как в примере автора), выполним следующие действия в конструкторе настроек СКД:
Сотрудник.ДатаКалендаря.ВОтпуске (Сумма).Важный нюанс: Для поля ДатаКалендаря в настройках группировки полезно установить Тип дополнения — "День", чтобы даже если в какой-то день никто не был в отпуске, колонка с этой датой все равно отобразилась в отчете.
Мы рассмотрели основной принцип построения запроса для отчета "График отпусков". Ключевым моментом является отказ от попыток вытащить всё одним плоским запросом из регистра. Вместо этого мы используем производственный календарь как опорную сетку и накладываем на неё периоды отпусков через условие МЕЖДУ. Такой подход гарантирует, что вы получите данные за каждый день, что и требуется для построения качественной визуализации в СКД.