При разработке отчетов и кастомных обработок в конфигурации «1С:Зарплата и управление персоналом 3.1» программисты часто сталкиваются с необходимостью получить «нарезанную» по периодам историю кадровых перемещений сотрудников внутри одного или нескольких подразделений. Стандартные методы получения сотрудников на определенную дату не подходят, если один и тот же сотрудник в течение одного месяца несколько раз переводился с одной должности на другую в рамках одного подразделения. Нам важно получить хронологическую последовательность со всеми интервалами работы.
Рассмотрим классический пример. Сотрудник Иванов устроен в подразделение «Отдел продаж». С 2 по 5 октября он работал на должности «Младший менеджер», с 6 по 25 октября его перевели на должность «Ведущий менеджер» внутри того же подразделения, а с 26 октября он был переведен в «Отдел маркетинга» (другое подразделение). Наша задача — получить следующую таблицу:
Если мы попробуем использовать стандартную функцию программного интерфейса КадровыйУчет.СотрудникиОрганизации, то столкнемся с проблемой: этот метод возвращает список сотрудников без разбиения по периодам изменения должностей внутри интервала. Метод возвращает сотрудника и его финальные или начальные реквизиты, не разделяя строку сотрудника на несколько строк в зависимости от внутренних перемещений.
Самым быстрым и простым способом решить эту задачу является написание прямого запроса к регистру сведений КадроваяИсторияСотрудниковИнтервальный. В конфигурации 1С:ЗУП 3.1 этот регистр спроектирован специальным образом: разработчики фирмы «1С» уже хранят в нем предварительно рассчитанные и «нарезанные» временные интервалы по каждому сотруднику. Это избавляет нас от необходимости писать тяжелые запросы со сложными соединениями для вычисления даты окончания предыдущего периода.
Проанализируем готовый запрос, который позволяет получить нужные данные:
ВЫБРАТЬ
КадроваяИсторияСотрудниковИнтервальный.Подразделение КАК Подразделение,
ВЫБОР
КОГДА КадроваяИсторияСотрудниковИнтервальный.ДатаНачала < НАЧАЛОПЕРИОДА(&Период, МЕСЯЦ)
ТОГДА НАЧАЛОПЕРИОДА(&Период, МЕСЯЦ)
ИНАЧЕ КадроваяИсторияСотрудниковИнтервальный.ДатаНачала
КОНЕЦ КАК ДатаНачала,
ВЫБОР
КОГДА КадроваяИсторияСотрудниковИнтервальный.ДатаОкончания > КОНЕЦПЕРИОДА(&Период, МЕСЯЦ)
ТОГДА КОНЕЦПЕРИОДА(&Период, МЕСЯЦ)
ИНАЧЕ ДОБАВИТЬКДАТЕ(КадроваяИсторияСотрудниковИнтервальный.ДатаОкончания, ДЕНЬ, -1)
КОНЕЦ КАК ДатаОкончания,
КадроваяИсторияСотрудниковИнтервальный.Сотрудник КАК Сотрудник,
КадроваяИсторияСотрудниковИнтервальный.ДолжностьПоШтатномуРасписанию КАК ДолжностьПоШтатномуРасписанию,
КадроваяИсторияСотрудниковИнтервальный.КоличествоСтавок КАК КоличествоСтавок,
КадроваяИсторияСотрудниковИнтервальный.ВидСобытия КАК ВидСобытия
ИЗ
РегистрСведений.КадроваяИсторияСотрудниковИнтервальный КАК КадроваяИсторияСотрудниковИнтервальный
ГДЕ
КадроваяИсторияСотрудниковИнтервальный.Подразделение = &Подразделение
И КадроваяИсторияСотрудниковИнтервальный.ДатаОкончания > НАЧАЛОПЕРИОДА(&Период, МЕСЯЦ)
И КадроваяИсторияСотрудниковИнтервальный.ДатаНачала < КОНЕЦПЕРИОДА(&Период, МЕСЯЦ)
И НЕ КадроваяИсторияСотрудниковИнтервальный.ВидСобытия = ЗНАЧЕНИЕ(Перечисление.ВидыКадровыхСобытий.Увольнение)
УПОРЯДОЧИТЬ ПО
КадроваяИсторияСотрудниковИнтервальный.Сотрудник.Наименование
Важные нюансы данного запроса:
ВЫБОР мы принудительно ограничиваем даты начала и окончания интервалов рамками выбранного месяца (параметр &Период). Если кадровый интервал начался раньше начала месяца или закончился позже окончания месяца, система «обрежет» даты под границы анализируемого периода.ДатаОкончания на 1 день с помощью функции ДОБАВИТЬКДАТЕ, так как в интервальных регистрах дата окончания часто пишется как начало следующего дня (например, «06.10.2023 00:00:00»).Несмотря на то, что этот запрос прост и отлично решает локальную задачу, прямое обращение к интервальным регистрам имеет существенные недостатки:
Чтобы сделать код устойчивым к обновлениям и полностью соответствующим стандартам разработки фирмы «1С», мы можем применить программные функции общего модуля ЗарплатаКадрыПериодическиеРегистры. Данный подход гарантирует корректность получаемых данных за счет использования платформенных механизмов формирования интервалов.
Разберем по шагам, как реализовать этот алгоритм в своем коде:
Посмотрим на пример кода, реализующего этот подход:
// 1. Создаем таблицу нужных сотрудников и их периодов анализа
ТаблицаСотрудников = Новый ТаблицаЗначений;
ТаблицаСотрудников.Колонки.Добавить("Сотрудник", Новый ОписаниеТипов("СправочникСсылка.Сотрудники"));
ТаблицаСотрудников.Колонки.Добавить("ДатаНачала", Новый ОписаниеТипов("Дата"));
ТаблицаСотрудников.Колонки.Добавить("ДатаОкончания", Новый ОписаниеТипов("Дата"));
// Заполняем таблицу сотрудниками подразделения за выбранный период
ПараметрыПолучения = КадровыйУчет.ПараметрыПолученияСотрудниковОрганизацийПоСпискуФизическихЛиц();
ПараметрыПолучения.Организация = Справочники.Организации.ОрганизацияПоУмолчанию();
ПараметрыПолучения.НачалоПериода = НачалоМесяца(Объект.ПериодРегистрации);
ПараметрыПолучения.ОкончаниеПериода = КонецМесяца(Объект.ПериодРегистрации);
ПараметрыПолучения.Подразделение = Объект.Подразделение;
ТЗСотрудникиПодразделения = КадровыйУчет.СотрудникиОрганизации(Истина, ПараметрыПолучения);
Для Каждого СтрокаСотрудник Из ТЗСотрудникиПодразделения Цикл
НоваяСтрока = ТаблицаСотрудников.Добавить();
НоваяСтрока.Сотрудник = СтрокаСотрудник.Сотрудник;
НоваяСтрока.ДатаНачала = НачалоМесяца(Объект.ПериодРегистрации);
НоваяСтрока.ДатаОкончания = КонецМесяца(Объект.ПериодРегистрации);
КонецЦикла;
// Помещаем таблицу во временную таблицу в запросе
МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;
Запрос.Текст = "ВЫБРАТЬ * ПОМЕСТИТЬ ВТСотрудникиПериоды ИЗ &ТаблицаСотрудников КАК ТаблицаСотрудников";
Запрос.УстановитьПараметр("ТаблицаСотрудников", ТаблицаСотрудников);
Запрос.Выполнить();
// 2. Вызываем программный интерфейс для построения интервальной таблицы
ОписаниеФильтра = ЗарплатаКадрыПериодическиеРегистры.ОписаниеФильтраДляСоздатьВТИмяРегистра("ВТСотрудникиПериоды", "Сотрудник");
ПараметрыПостроения = ЗарплатаКадрыПериодическиеРегистры.ПараметрыПостроенияДляСоздатьВТИмяРегистра();
ЗарплатаКадрыПериодическиеРегистры.СоздатьВТИмяРегистраПериоды(
"КадроваяИсторияСотрудников",
Запрос.МенеджерВременныхТаблиц,
Истина,
ОписаниеФильтра,
ПараметрыПостроения,
"ВТСотрудникиИнтервалы"
);
// Теперь у нас есть временная таблица ВТСотрудникиИнтервалы со всеми периодами и кадровыми данными!
Запрос.Текст =
"ВЫБРАТЬ
Интервалы.Сотрудник,
Интервалы.ДатаНачала,
Интервалы.ДатаОкончания,
Интервалы.Подразделение,
Интервалы.Должность
ИЗ
ВТСотрудникиИнтервалы КАК Интервалы
УПОРЯДОЧИТЬ ПО
Интервалы.Сотрудник.Наименование,
Интервалы.ДатаНачала";
КадроваяИстория = Запрос.Выполнить().Выгрузить();
Этот метод гарантирует получение абсолютно корректных кадровых интервалов по должностям и подразделениям. На выходе мы получаем временную таблицу ВТСотрудникиИнтервалы, структура которой совпадает со стандартными отчетами ЗУП. Метод автоматически обрабатывает ситуации с исправлениями кадровых документов и разграничением прав доступа.
Если вы разрабатываете отчет на базе Системы компоновки данных (СКД), то лучшим решением будет использование виртуальных таблиц-представлений. В ЗУП 3.1 разработчики внедрили концепцию динамической подстановки текстов запросов. Вместо реальных таблиц баз данных вы можете использовать конструкцию:
Представления_ТаблицаРегистра_КадроваяИсторияСотрудников
Для того чтобы система развернула эту виртуальную таблицу в оптимизированный запрос при выводе отчета, достаточно в модуле объекта отчета прописать вызов предопределенного метода:
ЗарплатаКадрыОбщиеНаборыДанных.ЗаполнитьОбщиеИсточникиДанныхОтчета(ЭтотОбъект)
Этот подход избавляет от написания сложного программного кода создания временных таблиц в самом модуле, перенося всю логику построения кадровых интервалов на уровень платформы.
Часто в обсуждениях подобных задач на форумах всплывает важный методологический аспект. Если у вас на предприятии сотрудники перемещаются между должностями или бригадами каждые 2–3 дня, то оформление каждого такого шага полноценным документом «Кадровый перевод» создаст колоссальную бумажную нагрузку на кадровую службу. Трудовой кодекс обязывает подписывать дополнительное соглашение к трудовому договору при каждом изменении должности или подразделения.
Чтобы избежать бумажной рутины и не засорять официальную кадровую историю техническими перемещениями, проанализируйте возможность использования альтернативных механизмов ЗУП 3.1:
Использование правильных методологических инструментов не только упростит работу кадровой службы, но и сделает ваши программные запросы к кадровой истории более прозрачными и легкими в поддержке.