Как в запросе 1С:ЗУП 3.1 определить подразделение сотрудника на каждый день периода без задвоений на дату перевода?

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

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

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

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

В чем истинная причина проблемы (задвоения на дату перевода)?

Выясним причину некорректного поведения запроса. В конфигурации 1С:ЗУП 3.1 кадровая история сотрудников хранится в специализированном регистре сведений КадроваяИсторияСотрудниковИнтервальный. Данный регистр предназначен для оптимизации построения отчетов и содержит готовые периоды в виде полей ДатаНачала и ДатаОкончания.

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

Календарь.Дата >= Интервалы.ДатаНачала И Календарь.Дата <= Интервалы.ДатаОкончания

Из-за специфики записи интервалов в ЗУП 3.1 на стыке кадровых событий происходит следующее:

В результате на дату перевода (15 января) под условие связи ПОДХОДЯТ СРАЗУ ДВЕ ЗАПИСИ: одна попадает по условию «меньше или равно дате окончания», а вторая — по условию «больше или равно дате начала». Из-за этого в выборке для одного дня появляются две строки.


Решение 1. Использование строгого неравенства (Самый простой и быстрый способ)

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

Проанализируем правильное условие связи календаря и интервального регистра:

Календарь.Дата >= Интервалы.ДатаНачала И Календарь.Дата < Интервалы.ДатаОкончания

Обратите внимание на знак строгого меньше (<) вместо привычного меньше или равно (<=) для правой границы.

Посмотрим, как это работает на конкретном примере сотрудника, переведенного 15 января:

  1. Для старого подразделения (интервал действия с 01.01.2020 по 15.01.2020): при проверке даты 15.01.2020 условие 15.01.2020 < 15.01.2020 вернет значение Ложь. Данная запись для 15 января отсекается.
  2. Для нового подразделения (интервал действия с 15.01.2020 по 31.12.3999): условие 15.01.2020 >= 15.01.2020 вернет значение Истина. Сотрудник за этот день будет отнесен только к новому подразделению.
  3. Для предыдущего дня (14 января): условие 14.01.2020 < 15.01.2020 вернет Истина для старого подразделения, и данные запишутся корректно.

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


Решение 2. Использование механизма «Представлений» в СКД (Методологически верный способ)

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

Разберем по шагам, как настроить этот механизм в вашем отчете СКД.

Шаг 1. Описание виртуальных таблиц в запросе СКД

В схеме компоновки данных мы создаем две временные таблицы-шаблоны. При выполнении отчета программный код ЗУП автоматически заменит их на реальные оптимизированные кадровые данные.

Сначала сформируем таблицу периодов (дней):


ВЫБРАТЬ
    ДАТАВРЕМЯ(1, 1, 1) КАК Период
ПОМЕСТИТЬ Представления_Периоды
ГДЕ
    "НачалоИнтервала" = &НачалоПериода
    И "ОкончаниеИнтервала" = &КонецПериода
    И "Периодичность" = "День"
    И "ИспользоватьКонецПериода" = ИСТИНА
;

Затем опишем структуру таблицы кадровых данных сотрудников на каждый день:


ВЫБРАТЬ РАЗРЕШЕННЫЕ
    ДАТАВРЕМЯ(1, 1, 1) КАК Период,
    ЗНАЧЕНИЕ(Справочник.Сотрудники.ПустаяСсылка) КАК Сотрудник,
    ЗНАЧЕНИЕ(Справочник.ПодразделенияОрганизаций.ПустаяСсылка) КАК Подразделение
ПОМЕСТИТЬ Представления_КадровыеДанныеСотрудников
;

В основном запросе отчета вы можете просто выбирать данные из подготовленной таблицы Представления_КадровыеДанныеСотрудников, соединяя ее при необходимости с другими таблицами, и производить подсчет дней.

Шаг 2. Инициализация представлений в модуле объекта отчета

Чтобы система СКД распознала эти временные таблицы и наполнила их данными, нам необходимо запустить предопределенный обработчик. Откроем модуль объекта отчета и в событии ПриКомпоновкеРезультата вызовем процедуру общего модуля ЗУП:


Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
    
    // Инициализируем автозаполнение временных таблиц кадрового учета
    ЗарплатаКадрыОбщиеНаборыДанных.ЗаполнитьОбщиеИсточникиДанныхОтчета(ЭтотОбъект);
    
КонецПроцедуры

Этот способ является стандартом разработки для ЗУП 3.1. Он гарантирует, что ваш отчет не сломается при будущих обновлениях конфигурации и изменении структуры регистров.


Решение 3. «Классический» запрос без интервальных регистров (Эмуляция среза последних на каждый день)

Рассмотрим ситуацию, когда у вас нет возможности использовать программный модуль отчета (например, если вы пишете запрос для сторонней системы интеграции или внешнего источника данных) и нельзя задействовать интервальные регистры. В этом случае мы можем эмулировать срез последних на каждый день периода по классическому регистру сведений КадроваяИсторияСотрудников.

Алгоритм решения состоит из трех последовательных шагов в запросе.

Шаг 1. Получение сетки «Сотрудник — День»

Сначала мы формируем полную сетку дат из производственного календаря за выбранный период и связываем ее со списком сотрудников:


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

Шаг 2. Определение даты последнего кадрового события на каждый день

Теперь для каждого дня нашей сетки найдем дату самого последнего кадрового изменения (приема или перевода), которое произошло не позже этой даты:


ВЫБРАТЬ
    Сетка.Период КАК Период,
    Сетка.Сотрудник КАК Сотрудник,
    МАКСИМУМ(КадроваяИстория.Период) КАК ПериодСобытия
ПОМЕСТИТЬ ВТ_МаксимальныеДаты
ИЗ
    ВТ_СеткаДатыСотрудники КАК Сетка
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КадроваяИсторияСотрудников КАК КадроваяИстория
        ПО Сетка.Сотрудник = КадроваяИстория.Сотрудник
        И Сетка.Период >= КадроваяИстория.Период
ГРУППИРОВАТЬ ПО
    Сетка.Период,
    Сетка.Сотрудник
;

Шаг 3. Получение подразделения на каждый день

На финальном шаге мы соединяем таблицу максимальных дат обратно с регистром кадровой истории по найденной точной дате события. Это позволяет вытащить актуальное подразделение без риска дублирования:


ВЫБРАТЬ
    МаксДаты.Период КАК ДатаКалендаря,
    МаксДаты.Сотрудник КАК Сотрудник,
    КадроваяИстория.Подразделение КАК Подразделение
ИЗ
    ВТ_МаксимальныеДаты КАК МаксДаты
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.КадроваяИсторияСотрудников КАК КадроваяИстория
        ПО МаксДаты.Сотрудник = КадроваяИстория.Сотрудник
        И МаксДаты.ПериодСобытия = КадроваяИстория.Период
;

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

Выберите наиболее подходящий для вашей задачи вариант реализации: для простых доработок подойдет Решение 1, для серьезной разработки новых отчетов на СКД в ЗУП 3.1 настоятельно рекомендуем использовать Решение 2.

← На главную