Как получить актуальную должность сотрудника в запросе 1С с учетом поля Действует до?

Программист 1С v8.3 (Управляемые формы) 1С:Зарплата и Управление Персоналом Управленческий учет IT и автоматизация бизнеса
← На главную

В практике разработки на платформе 1С, особенно при работе с конфигурациями Зарплата и Управление Персоналом (ЗУП 3.1) или ERP, программисты часто сталкиваются с задачей получения актуальных кадровых данных на определенную дату. Стандартный механизм виртуальной таблицы СрезПоследних кажется логичным решением, однако он таит в себе серьезную проблему, если в регистре сведений используются временные записи с заполненным реквизитом ДействуетДо. Рассмотрим подробнее, почему стандартный подход не всегда работает и как правильно составить запрос для получения корректных данных.

В чем заключается проблема стандартного Среза последних?

Проанализируем ситуацию: у сотрудника есть основная должность (бессрочная) и временное перемещение на другую должность (например, на период отпуска другого сотрудника). В регистре сведений КадроваяИсторияСотрудников это отражается двумя записями. Если срок временного перемещения закончился вчера, а мы делаем СрезПоследних на сегодня, платформа все равно вернет нам запись о временном перемещении. Почему так происходит?

Для платформы 1С СрезПоследних работает только по измерениям и периоду. Реквизит ДействуетДо — это обычный ресурс или реквизит, который не участвует в логике «отрезания» данных самой платформой. Таким образом, последняя по времени запись считается актуальной, даже если логически её срок действия уже истек. Чтобы получить «предыдущую» действующую запись, нам придется изменить подход к построению запроса.

Разберем ошибочный метод фильтрации в виртуальной таблице

Часто разработчики пытаются решить проблему, добавляя условие прямо в параметры виртуальной таблицы. Рассмотрим пример такого запроса:


ВЫБРАТЬ
    КадроваяИстория.Сотрудник,
    КадроваяИстория.Должность
ИЗ
    РегистрСведений.КадроваяИсторияСотрудников.СрезПоследних(&ДатаСреза, 
        Организация = &Организация 
        И ДействуетДо >= &ДатаСреза) КАК КадроваяИстория

Выясним причину, по которой этот код не решит задачу. Виртуальная таблица сначала находит самую последнюю запись в истории сотрудника на указанную дату. И только после этого к этой единственной записи применяется фильтр ДействуетДо >= &ДатаСреза. Если последняя запись просрочена, фильтр просто исключит её из результата, и мы получим пустую строку вместо того, чтобы «откатиться» к предыдущей действующей должности.

Решение №1: Использование физической таблицы и временных таблиц

Для того чтобы система «увидела» предыдущую запись, нам нужно работать не со срезом, а со всей историей записей в физической таблице регистра. Посмотрим на алгоритм, который позволяет это реализовать:

  1. Выбираем все записи по сотруднику, период которых меньше или равен дате среза.
  2. Накладываем фильтр на срок действия: запись должна быть либо бессрочной (пустая дата), либо её окончание должно быть больше или равно нашей дате среза.
  3. Среди отобранных записей находим ту, у которой период является максимальным.

Проанализируем пример кода на языке запросов 1С:


// Шаг 1: Получаем актуальные периоды во временную таблицу
ВЫБРАТЬ
    КадроваяИстория.Сотрудник КАК Сотрудник,
    МАКСИМУМ(КадроваяИстория.Период) КАК Период
ПОМЕСТИТЬ ВТ_АктуальныеДаты
ИЗ
    РегистрСведений.КадроваяИсторияСотрудников КАК КадроваяИстория
ГДЕ
    КадроваяИстория.Период <= &ДатаСреза
    И (КадроваяИстория.ДействуетДо >= &ДатаСреза
        ИЛИ КадроваяИстория.ДействуетДо = ДАТАВРЕМЯ(1, 1, 1))
    И КадроваяИстория.Организация = &Организация
ГРУППИРОВАТЬ ПО
    КадроваяИстория.Сотрудник
;

// Шаг 2: Получаем данные должностей, соединяясь с физической таблицей
ВЫБРАТЬ
    ВТ_АктуальныеДаты.Сотрудник,
    КадроваяИсторияФизическая.Должность,
    КадроваяИсторияФизическая.Период,
    КадроваяИсторияФизическая.Регистратор
ИЗ
    ВТ_АктуальныеДаты КАК ВТ_АктуальныеДаты
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.КадроваяИсторияСотрудников КАК КадроваяИсторияФизическая
        ПО ВТ_АктуальныеДаты.Сотрудник = КадроваяИсторияФизическая.Сотрудник
            И ВТ_АктуальныеДаты.Период = КадроваяИсторияФизическая.Период

В этом решении мы используем ВНУТРЕННЕЕ СОЕДИНЕНИЕ по сотруднику и найденному максимальному периоду. Это гарантирует, что мы получим именно ту запись, которая еще действует на &ДатаСреза, даже если после неё в регистре есть более «свежие», но уже истекшие записи.

Решение №2: Использование типового программного интерфейса (API)

Если вы работаете в современной конфигурации (например, ЗУП 3.1), крайне рекомендуется не изобретать велосипед в виде сложных запросов к регистрам, а использовать встроенные функции кадрового учета. В типовых решениях заложена сложная логика, учитывающая не только ДействуетДо, но и исправления документов, сторнирование и фактические состояния сотрудников.

Рассмотрим использование функции КадровыйУчет.КадровыеДанныеСотрудников. Этот метод позволяет получить таблицу значений с актуальными данными на любую дату.


// Формируем массив сотрудников, по которым нужны данные
МассивСотрудников = Новый Массив;
МассивСотрудников.Добавить(ВыбранныйСотрудник);

// Задаем строку необходимых полей (через запятую)
НужныеПоля = "Должность,Подразделение,Сотрудник";

// Вызываем типовую функцию
ТаблицаДанных = КадровыйУчет.КадровыеДанныеСотрудников(
    Истина, 
    МассивСотрудников, 
    НужныеПоля, 
    ДатаСреза
);

// Читаем результат
Если ТаблицаДанных.Количество() > 0 Тогда
    ТекущаяДолжность = ТаблицаДанных[0].Должность;
КонецЕсли;

Преимущества этого метода:

Важные нюансы при работе с датами

Обратим внимание на важный момент: при проверке поля ДействуетДо в запросах всегда нужно учитывать «пустую дату». В 1С она соответствует значению ДАТАВРЕМЯ(1, 1, 1, 0, 0, 0). Если запись в кадровой истории бессрочная (например, обычный прием на работу), поле ДействуетДо не заполняется. Если вы забудете условие ИЛИ ДействуетДо = ДАТАВРЕМЯ(1, 1, 1), ваш запрос просто отфильтрует всех постоянных сотрудников, оставив только временных.

Также помните про время: если в параметре &ДатаСреза передается дата без времени (00:00:00), то условие ДействуетДо >= &ДатаСреза сработает корректно для документов, закончившихся в этот день. Однако хорошим тоном считается использование конца дня для получения среза данных на отчетную дату: КонецДня(&ДатаСреза).

Заключение

Мы проанализировали проблему получения актуальных кадровых данных и выяснили, что стандартный СрезПоследних не предназначен для обработки логики «срока действия» записи. Правильным решением в рамках запроса является использование физической таблицы регистра с предварительным поиском максимального периода среди действующих записей. Однако наиболее современным и предпочтительным методом остается использование типовых процедур кадрового учета, которые предоставляют разработчику уже готовый и проверенный результат.

← На главную