Разработка отчетов и запросов по кадровой истории в программе «1С:Зарплата и Управление Персоналом 2.5» (ЗУП 2.5) — классическая задача, с которой сталкивается каждый программист 1С. Казалось бы, что может быть проще, чем вывести список сотрудников, работавших в компании в выбранном интервале дат? Однако именно здесь начинающие разработчики совершают наибольшее количество архитектурных ошибок, приводящих к искажению данных в отчетах.
В этой статье мы подробно разберем, почему стандартные подходы «в лоб» не работают, проанализируем внутреннее устройство кадровых регистров и построим идеальный, быстродействующий запрос, который безошибочно выберет сотрудников, отработавших в организации хотя бы один день за указанный период.
Давайте проанализируем решения, которые часто пытаются применить на практике, и выясним причины их неработоспособности.
Ошибка №1: Срез последних на конец периода
Самая распространенная ошибка — попытка получить данные из виртуальной таблицы СрезПоследних регистра сведений РаботникиОрганизаций на дату окончания периода. Разработчик рассуждает так: «Если я возьму срез на конец периода, то увижу актуальное состояние сотрудников». Это в корне неверно. Если сотрудник уволился в середине выбранного периода (например, 15 числа при периоде с 1 по 30 число), то на конец периода его состояние в регистре будет равно «Увольнение». Нам же нужно, чтобы он попал в отчет, так как первые 15 дней месяца он честно работал в организации. Срез последних на конец периода просто «отрежет» всех уволенных внутри интервала.
Ошибка №2: Получение дат через точку от сотрудника
Попытка обратиться к реквизитам справочника через точку вида Сотрудник.ДатаПриемаНаРаботу или Сотрудник.ДатаУвольнения — это грубейшая ошибка проектирования по двум причинам:
СотрудникиОрганизаций хранятся только самые последние, финальные даты. Если сотрудник сначала работал, потом уволился, а через год его приняли снова (создав новый элемент или используя старый в зависимости от методологии учета), простые реквизиты справочника не покажут вам реальную хронологию за прошлые года. Вся кадровая история пишется исключительно в регистры сведений.Ошибка №3: Игнорирование механизма временных кадровых изменений
В ЗУП 2.5 регистр РаботникиОрганизаций имеет сложную структуру. Для реализации временных перемещений или срочных трудовых договоров в нем используются реквизиты завершения: ПериодЗавершения, ПричинаИзмененияСостоянияЗавершения и другие. Если не учитывать эти поля, запрос вернет неактуальные данные по сотрудникам, у которых временный перевод завершился внутри исследуемого периода.
Чтобы решить задачу корректно и собрать абсолютно всех сотрудников, которые работали хотя бы один день в интервале между &НачалоПериода и &КонецПериода, нам необходимо объединить три независимых подмножества данных:
&НачалоПериода) и отсечь тех, кто к этому моменту уже был уволен.РаботникиОрганизаций, дата которых (поле Период) попадает в наш интервал. Сюда войдут вновь принятые, переведенные и даже уволенные (ведь до момента увольнения внутри периода они работали!).ПериодЗавершения, если оно попадает в границы нашего интервала.Объединение этих трех выборок с помощью оператора ОБЪЕДИНИТЬ (который автоматически удаляет дубликаты записей) даст нам максимально точный и быстрый результат.
Рассмотрим детально, как выглядит реализация данного алгоритма на языке запросов 1С.
Шаг 1. Выбираем работающих на начало периода
Для этого мы обращаемся к виртуальной таблице СрезПоследних на дату начала периода. При этом мы обязательно анализируем, не завершилось ли временное кадровое событие к этой дате, используя конструкцию ВЫБОР.
ВЫБРАТЬ РАЗЛИЧНЫЕ
КадрыНаНачало.Сотрудник КАК Сотрудник
ИЗ
РегистрСведений.РаботникиОрганизаций.СрезПоследних(&НачалоПериода, ) КАК КадрыНаНачало
ГДЕ
ВЫБОР
КОГДА КадрыНаНачало.ПериодЗавершения <> ДАТАВРЕМЯ(1, 1, 1)
И КадрыНаНачало.ПериодЗавершения <= &НачалоПериода
ТОГДА КадрыНаНачало.ПричинаИзмененияСостоянияЗавершения
ИНАЧЕ КадрыНаНачало.ПричинаИзмененияСостояния
КОНЕЦ <> ЗНАЧЕНИЕ(Перечисление.ПричиныИзмененияСостояния.Увольнение)
Шаг 2. Выбираем движения внутри периода
Вторым пакетом мы берем данные из физической таблицы регистра сведений за указанный период. Это позволит нам поймать все изменения статусов сотрудников в процессе работы.
ОБЪЕДИНИТЬ
ВЫБРАТЬ РАЗЛИЧНЫЕ
РаботникиОрганизаций.Сотрудник
ИЗ
РегистрСведений.РаботникиОрганизаций КАК РаботникиОрганизаций
ГДЕ
РаботникиОрганизаций.Период МЕЖДУ &НачалоПериода И &КонецПериода
Шаг 3. Учитываем завершение временных событий в периоде
И, наконец, добавляем выборку по тем сотрудникам, у которых окончание действия временного кадрового решения пришлось на наш расчетный период.
ОБЪЕДИНИТЬ
ВЫБРАТЬ РАЗЛИЧНЫЕ
РаботникиОрганизаций.Сотрудник
ИЗ
РегистрСведений.РаботникиОрганизаций КАК РаботникиОрганизаций
ГДЕ
РаботникиОрганизаций.ПериодЗавершения МЕЖДУ &НачалоПериода И &КонецПериода
Ниже представлен готовый текст запроса, который вы можете скопировать и сразу использовать в своих разработках (в отчетах, обработках или динамических списках обычных форм):
ВЫБРАТЬ РАЗЛИЧНЫЕ
КадрыНаНачало.Сотрудник КАК Сотрудник
ИЗ
РегистрСведений.РаботникиОрганизаций.СрезПоследних(&НачалоПериода, ) КАК КадрыНаНачало
ГДЕ
ВЫБОР
КОГДА КадрыНаНачало.ПериодЗавершения <> ДАТАВРЕМЯ(1, 1, 1)
И КадрыНаНачало.ПериодЗавершения <= &НачалоПериода
ТОГДА КадрыНаНачало.ПричинаИзмененияСостоянияЗавершения
ИНАЧЕ КадрыНаНачало.ПричинаИзмененияСостояния
КОНЕЦ <> ЗНАЧЕНИЕ(Перечисление.ПричиныИзмененияСостояния.Увольнение)
ОБЪЕДИНИТЬ
ВЫБРАТЬ РАЗЛИЧНЫЕ
РаботникиОрганизаций.Сотрудник
ИЗ
РегистрСведений.РаботникиОрганизаций КАК РаботникиОрганизаций
ГДЕ
РаботникиОрганизаций.Период МЕЖДУ &НачалоПериода И &КонецПериода
ОБЪЕДИНИТЬ
ВЫБРАТЬ РАЗЛИЧНЫЕ
РаботникиОрганизаций.Сотрудник
ИЗ
РегистрСведений.РаботникиОрганизаций КАК РаботникиОрганизаций
ГДЕ
РаботникиОрганизаций.ПериодЗавершения МЕЖДУ &НачалоПериода И &КонецПериода
Если вы планируете переходить на современные версии программ 1С (например, 1С:ЗУП 3.1 на управляемых формах), то вам будет полезно узнать, что там архитектура работы с кадрами была кардинально переработана. Разработчикам больше не нужно вручную собирать сложные запросы с объединением таблиц.
В ЗУП 3.1 вся история хранится в периодическом регистре сведений КадроваяИсторияСотрудников. Для получения кадровых срезов и движений за период в системе предусмотрен специальный программный интерфейс (API), доступный через общий модуль кадрового учета. Например, вызов метода КадровыйУчет.СоздатьВТКадроваяИсторияСотрудников в теле вашего запроса автоматически сформирует временную таблицу с учетом всех тонкостей законодательства, исправлений документов «задним числом» и совместительства. Это исключает рутинные ошибки и делает код вашего решения чистым и поддерживаемым.