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