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