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

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

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

Задача 1: Получение кадровых данных на определенную дату

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

Способ 1: Использование виртуальных таблиц «СрезПоследних»

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


ВЫБРАТЬ
    &Сотрудник КАК Сотрудник,
    &Период КАК Период
ПОМЕСТИТЬ ВТ_Отбор;

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

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

Важный нюанс: для получения состояния сотрудника (отпуск, болезнь) мы используем ДанныеСостоянийСотрудников. Если записи на указанную дату нет, мы через ЕСТЬNULL устанавливаем состояние Работа.

Способ 2: Интервальные регистры

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

&Период МЕЖДУ ДатаНачала И ДатаОкончания

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

Способ 3: Использование программных представлений (профессиональный уровень)

Разработчики ЗУП предусмотрели механизм «сублимированных запросов». Это позволяет писать запросы к виртуальным таблицам-представлениям, которые система сама разворачивает в оптимальный код в зависимости от версии конфигурации. Рассмотрим, как это работает через процедуру ЗарплатаКадрыОбщиеНаборыДанных.ЗаменитьЗапросыКПредставлениямВиртуальныхТаблиц.


Текст = "ВЫБРАТЬ
|   &Период КАК Период,
|   Сотрудники.Ссылка КАК Сотрудник
|ПОМЕСТИТЬ ВТСотрудникиПериоды
|ИЗ
|   Справочник.Сотрудники КАК Сотрудники;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
|   * 
|ПОМЕСТИТЬ Представления_КадровыеДанныеСотрудников
|ИЗ
|   ВТСотрудникиПериоды КАК СотрудникиПериоды";

ЗарплатаКадрыОбщиеНаборыДанных.ЗаменитьЗапросыКПредставлениямВиртуальныхТаблиц(Текст);

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

Задача 2: Анализ начислений и удержаний с итогом по периодам

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

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

Проанализируем структуру регистров расчета. В регистре Начисления основным измерением является Сотрудник. Однако в регистре Удержания часто используется только ФизическоеЛицо. Это связано с тем, что удержания (например, алименты) привязаны к человеку, а не к его должности. Для корректной связи в запросе необходимо:

  1. Сгруппировать начисления по Сотруднику и ПериодуРегистрации.
  2. Сгруппировать удержания по ФизическомуЛицу (связанному с сотрудником) и ПериодуРегистрации.
  3. Соединить эти таблицы левым соединением по паре Сотрудник.ФизическоеЛицо = Удержания.ФизическоеЛицо.

Сравнение с предыдущим периодом

Поскольку в языке запросов 1С нет функций смещения (типа LAG в SQL), мы применим прием «соединение таблицы с самой собой». Разберем алгоритм по шагам:

  1. Формируем временную таблицу ВТ_ИтогиПоМесяцам, где для каждого сотрудника считаем общую сумму за месяц.
  2. Присоединяем эту же таблицу к самой себе по условию: ВТ1.Сотрудник = ВТ2.Сотрудник И ВТ1.Месяц = ДОБАВИТЬКДАТЕ(ВТ2.Месяц, МЕСЯЦ, 1).
  3. Теперь в каждой строке текущего месяца у нас есть данные за предыдущий. С помощью конструкции ВЫБОР КОГДА мы легко вычисляем флаг «Сумма увеличилась».

ВЫБРАТЬ
    ТекущийМесяц.Сотрудник КАК Сотрудник,
    ТекущийМесяц.Месяц КАК Месяц,
    ТекущийМесяц.Сумма КАК СуммаТекущая,
    ЕСТЬNULL(ПрошлыйМесяц.Сумма, 0) КАК СуммаПрошлая,
    ВЫБОР
        КОГДА ТекущийМесяц.Сумма > ЕСТЬNULL(ПрошлыйМесяц.Сумма, 0) ТОГДА "Да"
        ИНАЧЕ "Нет"
    КОНЕЦ КАК СуммаУвеличилась
ИЗ
    ВТ_ИтогиПоМесяцам КАК ТекущийМесяц
    ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ИтогиПоМесяцам КАК ПрошлыйМесяц
        ПО ТекущийМесяц.Сотрудник = ПрошлыйМесяц.Сотрудник
        И ТекущийМесяц.Месяц = ДОБАВИТЬКДАТЕ(ПрошлыйМесяц.Месяц, МЕСЯЦ, 1)
УПОРЯДОЧИТЬ ПО
    ТекущийМесяц.Месяц УБЫВ
ИТОГИ
    СУММА(СуммаТекущая)
ПО
    Сотрудник

Важные рекомендации по работе с итогами

Для вывода результатов в нужном виде используйте вкладку «Итоги» в конструкторе запросов:

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

← На главную