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