При разработке отчетов или обработок в системах на базе 1С:Предприятие (таких как ЗУП 3.1, ERP или Комплексная автоматизация) часто возникает задача получения среза кадровых данных на конкретную дату. Основная сложность заключается в том, что информация распределена по нескольким регистрам сведений, а данные в них являются периодическими. Нам необходимо собрать воедино сотрудника, его подразделение, должность, количество ставок, вид занятости, дату приема и текущее состояние (работает, в отпуске, на больничном).
Рассмотрим подробнее, как построить эффективный запрос, избежать дублирования записей и правильно отсечь сотрудников, которые еще не приняты на работу или уже уволены.
Для работы с периодическими регистрами сведений в 1С предусмотрены виртуальные таблицы СрезПоследних. Это наиболее оптимальный механизм, так как платформа берет на себя задачу поиска последней записи на указанный момент времени. Проанализируем типичную ошибку начинающих разработчиков: попытку найти максимум по полю Период через вложенные запросы. Это существенно замедляет работу системы. Вместо этого мы будем использовать параметры виртуальных таблиц.
Разберем первый шаг — получение основных данных из регистра КадроваяИсторияСотрудников. Нам нужно получить состояние сотрудника на дату среза:
ВЫБРАТЬ
КадроваяИстория.Сотрудник КАК Сотрудник,
КадроваяИстория.Подразделение КАК Подразделение,
КадроваяИстория.Должность КАК Должность,
КадроваяИстория.КоличествоСтавок КАК КоличествоСтавок,
КадроваяИстория.ВидСобытия КАК ВидСобытия,
КадроваяИстория.Период КАК ПериодПоследнегоСобытия
ПОМЕСТИТЬ ВтПоследниеДанные
ИЗ
РегистрСведений.КадроваяИсторияСотрудников.СрезПоследних(&ДатаСреза, ) КАК КадроваяИстория
ГДЕ
НЕ КадроваяИстория.ВидСобытия = ЗНАЧЕНИЕ(Перечисление.ВидыКадровыхСобытий.Увольнение)
ИНДЕКСИРОВАТЬ ПО
Сотрудник;
Обратите внимание на использование условия в секции ГДЕ. Мы исключаем уволенных сотрудников сразу в первой временной таблице. Также крайне важно использовать ИНДЕКСИРОВАТЬ ПО для временных таблиц, если в дальнейшем планируется их соединение. Это значительно повышает производительность запроса на больших базах данных.
Одной из главных проблем, описанных в теме, является получение даты приема. Если мы просто возьмем поле Период из среза последних, мы получим дату последнего кадрового изменения (например, перемещения), а не дату фактического приема. Чтобы получить именно дату приема, нам нужно обратиться к тому же регистру, но с фильтром по виду события.
Рассмотрим, как это реализовать:
ВЫБРАТЬ
Приемы.Сотрудник КАК Сотрудник,
Приемы.Период КАК ДатаПриема
ПОМЕСТИТЬ ВтДатыПриема
ИЗ
РегистрСведений.КадроваяИсторияСотрудников.СрезПоследних(&ДатаСреза, ВидСобытия = ЗНАЧЕНИЕ(Перечисление.ВидыКадровыхСобытий.Прием)) КАК Приемы
ИНДЕКСИРОВАТЬ ПО
Сотрудник;
Здесь мы используем второй параметр виртуальной таблицы для фильтрации. Выясним причину, по которой у автора вопроса появлялась некорректная дата "25.10.2010". Это часто происходит из-за использования ПОЛНОЕ СОЕДИНЕНИЕ или неверного сопоставления периодов. Используя СрезПоследних с конкретным отбором по событию Прием, мы гарантируем получение правильной даты начала работы.
Теперь проанализируем ситуацию с остальными регистрами: ВидыЗанятостиСотрудников и СостоянияСотрудников. Нам необходимо соединить все полученные данные в одну выборку. Посмотрим на пример итогового запроса:
ВЫБРАТЬ
Данные.Сотрудник КАК Сотрудник,
Данные.Подразделение КАК Подразделение,
Данные.Должность КАК Должность,
Данные.КоличествоСтавок КАК КоличествоСтавок,
ДатыПриема.ДатаПриема КАК ДатаПриема,
ВидыЗанятости.ВидЗанятости КАК ВидЗанятости,
Состояния.Состояние КАК Состояние
ИЗ
ВтПоследниеДанные КАК Данные
ЛЕВОЕ СОЕДИНЕНИЕ ВтДатыПриема КАК ДатыПриема
ПО Данные.Сотрудник = ДатыПриема.Сотрудник
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ВидыЗанятостиСотрудников.СрезПоследних(&ДатаСреза, ) КАК ВидыЗанятости
ПО Данные.Сотрудник = ВидыЗанятости.Сотрудник
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.СостоянияСотрудников.SрезПоследних(&ДатаСреза, ) КАК Состояния
ПО Данные.Сотрудник = Состояния.Сотрудник
ГДЕ
НЕ ДатыПриема.ДатаПриема ЕСТЬ NULL
Разберем по шагам, почему выбрана такая структура:
ВтПоследниеДанные, где уже отсечены уволенные сотрудники.ДатыПриема.ДатаПриема возвращает NULL, это означает, что на указанную дату сотрудник еще не был принят (или нет записи о приеме). Условие НЕ ... ЕСТЬ NULL позволяет нам вывести только тех, кто уже фактически числится в штате.СостоянияСотрудников покажет нам, находится ли человек в отпуске или на больничном. Это периодический регистр, и СрезПоследних здесь работает идеально.Если ваша задача выполняется в рамках типовой конфигурации (ЗУП 3.1 или ERP), мы крайне рекомендуем использовать встроенный программный интерфейс (API). В этих конфигурациях существует мощный механизм — программный модуль КадровыйУчет. Функция КадровыеДанныеСотрудников позволяет получить все необходимые сведения одной строкой кода, при этом система сама оптимизирует запросы и учитывает все нюансы (например, работу по внутреннему совместительству).
Посмотрим на пример использования:
СписокПолей = "Сотрудник,Подразделение,Должность,ВидЗанятости,КоличествоСтавок,ДатаПриема";
ТаблицаДанных = КадровыйУчет.КадровыеДанныеСотрудников(Истина, МассивСотрудников, СписокПолей, ДатаСреза);
Этот метод предпочтителен по нескольким причинам:
Подводя итог нашему анализу, выделим ключевые моменты для успешного решения задачи:
Во-первых, всегда используйте виртуальные таблицы СрезПоследних с параметром даты. Это исключает необходимость вручную фильтровать периоды и искать максимальные даты.
Во-вторых, для получения даты приема делайте отдельное соединение с тем же регистром кадровой истории, но с фильтром по значению Перечисление.ВидыКадровыхСобытий.Прием.
В-третьих, если вы работаете в современной типовой конфигурации, рассмотрите возможность использования функции КадровыйУчет.КадровыеДанныеСотрудников. Это сэкономит ваше время и обеспечит высокую стабильность работы системы.
Правильно построенный запрос не только решает текущую проблему с выводом дат, но и обеспечивает высокую производительность отчета, что критически важно при работе с большим штатом сотрудников (от нескольких тысяч человек и выше).