Как получить предыдущую запись (должность) в регистре сведений "Кадровая история сотрудников" в 1С

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

Работа с периодическими регистрами сведений — одна из самых частых задач при разработке отчетов и обработок в 1С — есть готовый отчет по анализу кадровых изменений для 1С. Стандартная виртуальная таблица СрезПоследних позволяет легко получить текущее состояние, но что делать, если нам нужно узнать, какая запись была до текущей? Отчет "Кадровые изменения" в формате до / после наглядно показывает такие трансформации. Например, какую должность занимал сотрудник до перевода?

В этой статье мы подробно разберем несколько способов решения этой задачи, ориентируясь на конфигурацию "Зарплата и Управление Персоналом" (ЗУП 3.1), а также рассмотрим универсальные методы и современные возможности платформы, включая печать приказа о переводе (Т-5а) из групповых документов — для этого подойдёт автоматическое формирование трудовых договоров в ЗУП.

Особенности хранения истории в ЗУП 3.1 и проверка отклонений тарифных ставок

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

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

Для решения этой проблемы существует вспомогательный регистр КадроваяИсторияСотрудниковИнтервальный — есть готовая массовая корректировка данных регистров ЗУП. Его особенность в том, что в каждой записи хранятся сразу два поля:

  1. ДатаНачала — когда запись вступила в силу.
  2. ДатаОкончания — когда запись перестала действовать (или 31.12.3999, если она действует по сей день).

Использование этого регистра — самый эффективный способ решения нашей задачи.

Способ 1: Использование интервального регистра (Рекомендуемый для ЗУП)

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

Логика следующая:

  1. Выбираем текущую запись (например, действующую на дату отчета).
  2. Присоединяем к ней таблицу того же регистра (назовем ее "Предыдущая").
  3. Условие связи: Сотрудник совпадает, а момент окончания предыдущей записи + 1 секунда равен моменту начала текущей записи.

Давайте напишем запрос. Предположим, мы ищем изменения должностей.


ВЫБРАТЬ
    КадроваяИсторияТекущая.Сотрудник КАК Сотрудник,
    КадроваяИсторияТекущая.ПериодЗаписи КАК ТекущийПериод,
    КадроваяИсторияТекущая.Должность КАК ТекущаяДолжность,
    ЕСТЬNULL(КадроваяИсторияПредыдущая.Должность, ЗНАЧЕНИЕ(Справочник.Должности.ПустаяСсылка)) КАК ПредыдущаяДолжность,
    КадроваяИсторияПредыдущая.ПериодЗаписи КАК ПредыдущийПериод
ИЗ
    РегистрСведений.КадроваяИсторияСотрудниковИнтервальный КАК КадроваяИсторияТекущая
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КадроваяИсторияСотрудниковИнтервальный КАК КадроваяИсторияПредыдущая
        ПО КадроваяИсторияТекущая.Сотрудник = КадроваяИсторияПредыдущая.Сотрудник
        И (ДОБАВИТЬКДАТЕ(КадроваяИсторияПредыдущая.ДатаОкончания, СЕКУНДА, 1) = КадроваяИсторияТекущая.ДатаНачала)
ГДЕ
    КадроваяИсторияТекущая.ДатаНачала = &НаДату
    И КадроваяИсторияТекущая.ГоловнаяОрганизация = &Организация

Важные моменты в этом запросе:

Способ 2: Классический подход (Для любых периодических регистров)

Если вы работаете в конфигурации, где нет интервального регистра (например, самописная конфигурация или старая версия УПП), метод выше не сработает. Нам придется использовать стандартный регистр сведений.

Алгоритм действий будет таким:

  1. Получаем срез последних на нужную дату (текущие данные). Помещаем во временную таблицу (ВТ).
  2. Соединяем ВТ с физической таблицей регистра.
  3. Ищем записи, где период строго меньше текущего периода.
  4. Из найденных записей выбираем ту, у которой период максимальный.

Давайте реализуем это пошагово:


// Шаг 1: Получаем текущее состояние
ВЫБРАТЬ
    Срез.Сотрудник,
    Срез.Период,
    Срез.Должность
ПОМЕСТИТЬ ВТ_ТекущиеДанные
ИЗ
    РегистрСведений.КадроваяИсторияСотрудников.СрезПоследних(&НаДату, ) КАК Срез;

// Шаг 2 и 3: Ищем дату предыдущей записи
ВЫБРАТЬ
    Текущие.Сотрудник,
    МАКСИМУМ(История.Период) КАК ПериодПредыдущейЗаписи
ПОМЕСТИТЬ ВТ_ДатыПредыдущих
ИЗ
    ВТ_ТекущиеДанные КАК Текущие
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КадроваяИсторияСотрудников КАК История
        ПО Текущие.Сотрудник = История.Сотрудник
        И История.Период < Текущие.Период
СГРУППИРОВАТЬ ПО
    Текущие.Сотрудник;

// Шаг 4: Получаем данные по найденной дате
ВЫБРАТЬ
    Текущие.Сотрудник,
    Текущие.Должность КАК ТекущаяДолжность,
    ЕСТЬNULL(История.Должность, ЗНАЧЕНИЕ(Справочник.Должности.ПустаяСсылка)) КАК ПредыдущаяДолжность
ИЗ
    ВТ_ТекущиеДанные КАК Текущие
        ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ДатыПредыдущих КАК ДатыПредыдущих
        ПО Текущие.Сотрудник = ДатыПредыдущих.Сотрудник
        
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КадроваяИсторияСотрудников КАК История
        ПО ДатыПредыдущих.Сотрудник = История.Сотрудник
        И ДатыПредыдущих.ПериодПредыдущейЗаписи = История.Период

Этот метод более громоздкий, но универсальный. Он сработает для любого периодического регистра — поможет универсальный редактор записей регистров сведений.

Способ 3: Современный подход (Оконные функции)

Если ваша платформа 1С версии 8.3.14 и выше, вы можете использовать оконные функции языка запросов. Это самый элегантный и производительный способ, который не требует сложных соединений таблиц.

Функция LAG позволяет получить значение поля из предыдущей строки выборки в рамках заданного окна.

Пример запроса с оконной функцией:


ВЫБРАТЬ
    История.Сотрудник,
    История.Период,
    История.Должность КАК ТекущаяДолжность,
    // Получаем предыдущую должность для этого же сотрудника, упорядочив записи по периоду
    LAG(История.Должность, 1, ЗНАЧЕНИЕ(Справочник.Должности.ПустаяСсылка)) 
        ПОВЕРХ (
            РАЗДЕЛ ПО История.Сотрудник 
            УПОРЯДОЧИТЬ ПО История.Период
        ) КАК ПредыдущаяДолжность
ИЗ
    РегистрСведений.КадроваяИсторияСотрудников КАК История
ГДЕ
    История.Период <= &НаДату

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

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

При работе с кадровыми регистрами часто возникает ошибка "потери" данных за последний день. Это связано с тем, что кадровые документы могут создавать движения не на начало дня (00:00:00), а со сдвигом.

Если вы передаете в параметр &НаДату просто дату (например, 01.03.2024), то в запрос попадает время 00:00:00. Если перевод сотрудника был оформлен тем же днем, но проведен в базе чуть позже (или системой автоматически проставлен порядок документов), то запись может иметь время отличное от полуночи.

Рекомендация: Всегда приводите параметр даты к концу дня, если хотите захватить события, произошедшие в этот день.


// В коде модуля:
Запрос.УстановитьПараметр("НаДату", КонецДня(ТекущаяДата));
// Или для параметра &Период виртуальной таблицы
Запрос.УстановитьПараметр("Период", Новый Граница(КонецДня(ТекущаяДата), ВидГраницы.Включая));

Проблема "Технических" записей

Обратите внимание, что в кадровых регистрах могут быть записи, которые не меняют должность. Например, изменение графика работы или индексация заработка создают новую запись в регистре КадроваяИсторияСотрудников, но должность при этом остается старой.

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

Заключение

Мы рассмотрели три способа получения предыдущей записи:

  1. Через интервальный регистр — лучший выбор для типовых конфигураций ЗУП 3.1, ERP, КА.
  2. Через соединение и группировку (MAX) — универсальный "классический" метод для старых платформ.
  3. Через оконные функции (LAG) — современный, быстрый и читаемый код для новых платформ (8.3.14+).

Выбирайте метод исходя из версии вашей платформы и архитектуры конфигурации.

← На главную