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