При разработке внешних печатных форм, отчетов или кастомных механизмов в конфигурации 1С:Зарплата и управление персоналом 3.1 разработчики часто сталкиваются с необходимостью получить кадровые данные сотрудника (например, его должность, подразделение, оклад или график работы) на определенный момент времени. Особенно актуально это при печати кадровых приказов, таких как Т-5 (Кадровый перевод), где требуется вывести как «прежние», так и «новые» реквизиты сотрудника.
Для решения этой задачи в программном интерфейсе ЗУП 3.1 предусмотрен специализированный инструмент — функция КадровыйУчет.КадровыеДанныеСотрудников. Однако ее использование сопряжено с рядом нюансов, несоблюдение которых приводит к трудночитаемым ошибкам среды выполнения вроде Поле объекта не обнаружено (ЛевоеЗначение) или к получению пустых результатов. В этой статье мы подробно разберем, как устроен этот механизм, проанализируем типичные ошибки разработчиков и рассмотрим правильные варианты реализации.
Прежде чем перейти к программному коду, кратко разберем, как система хранит и обрабатывает кадровые сведения. В отличие от старых версий (например, ЗУП 2.5), где многие данные хранились непосредственно в периодических регистрах сведений и выбирались простым запросом среза последних, в ЗУП 3.1 реализована более сложная и производительная схема.
Кадровая история сотрудников распределена по нескольким регистрам:
КадроваяИсторияСотрудников — хранит сведения о приеме, переводах, увольнениях, должностях и подразделениях;ВидыЗанятостиСотрудников — хранит информацию о виде занятости (основное место, совместительство);ФИОФизическихЛиц — периодический регистр со сведениями о персональных данных;ПлановыеНачисления — сведения о тарифных ставках и оплате труда.Для того чтобы разработчику не приходилось вручную соединять множество таблиц и оптимизировать сложные запросы с учетом прав доступа (RLS), фирма «1С» создала единый программный интерфейс — общий модуль КадровыйУчет. Основной его рабочий инструмент — это генерация временных таблиц в базе данных с использованием менеджера временных таблиц (МВТ), что обеспечивает колоссальный прирост производительности на крупных базах данных.
Рассмотрим детально ошибки, с которыми столкнулся автор темы на форуме, и выясним причины их возникновения.
В самом первом сообщении автор пытается вызвать функцию следующим образом:
КадровыеДанныеСотрудников = КадровыйУчет.КадровыеДанныеСотрудников(Истина, СсылкаНаДокумент.Сотрудник, "Должность, Подразделение", ДатаМинусДень);
Функция КадровыеДанныеСотрудников спроектирована для массовой обработки информации. Ее вторым параметром должен выступать строго **Массив** ссылок на справочник Справочники.Сотрудники. Если передать туда одиночную ссылку, внутренний построитель запросов 1С не сможет корректно сформировать условие В (ВТ_СписокСотрудников), что приведет к аварийному завершению работы программы.
Эта ошибка — классическое следствие нарушения структуры параметра ПоляОтбораПериодическихДанных. Когда разработчик передает в функцию этот параметр, система ожидает увидеть строго типизированную структуру определенного формата:
"КадроваяИсторияСотрудников")."ЛевоеЗначение", "ВидСравнения" и "ПравоеЗначение".Если этот параметр передан некорректно (или в него по ошибке попало значение другого типа, как это произошло у автора из-за путаницы в параметрах вызова), внутренний парсер запросов общего модуля ЗарплатаКадрыПериодическиеРегистры падает при попытке обратиться к свойству СтрокаОтбора.ЛевоеЗначение.
Важное правило: Если дополнительные отборы по регистрам не требуются, данный параметр нужно либо вообще опустить (он имеет значение по умолчанию Неопределено), либо передать явное значение Неопределено.
Когда вы запрашиваете кадровые данные на дату «минус один день» от даты перевода, вы рискуете получить пустую таблицу значений. Почему так происходит?
Представим ситуацию: сотрудник принимается на работу 10 октября, и этим же числом оформляется кадровый перевод (например, исправление ошибки или перевод внутри дня). Если вычесть из даты документа ровно одни сутки (86 400 секунд), мы получим дату 9 октября. На этот момент времени сотрудник в организации еще официально не работал, кадровых записей по нему в базе данных нет. Функция вернет пустую таблицу значений. При попытке получить первую строку через КадровыеДанныеСотрудников[0] система выбросит исключение Индекс находится за пределами массива.
Проанализируем оптимальный и безопасный вариант решения этой задачи. Разберем его по шагам.
Для создания массива из одного элемента лучше всего использовать стандартную функцию библиотеки стандартных подсистем (БСП):
МассивСотрудников = ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве(СсылкаНаДокумент.Сотрудник);
Это лаконично и гарантирует, что мы передаем именно коллекцию с нужным типом данных.
Для получения состояния сотрудника непосредственно перед проведением текущего документа «Кадровый перевод» правильнее вычитать не целые сутки (день), а всего одну секунду. Периодичность кадровых регистров в ЗУП 3.1 составляет одну секунду. Вычитание одной секунды позволит получить последнее актуальное состояние прямо перед моментом изменения:
ДатаПолученияДанных = СсылкаНаДокумент.Дата - 1;
Если документ кадрового перевода оформлен началом дня (00:00:00), вычитание секунды вернет 23:59:59 предыдущего дня, что также является абсолютно верным результатом.
Теперь соберем готовый код вызова функции кадрового учета и обязательно добавим проверку на то, что таблица результатов не пуста:
// 1. Формируем массив
МассивСотрудников = ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве(СсылкаНаДокумент.Сотрудник);
// 2. Рассчитываем дату (минус 1 секунда от даты документа)
ДатаПолученияДанных = СсылкаНаДокумент.Дата - 1;
// 3. Запрашиваем данные. Лишние параметры опускаем, они примут значения по умолчанию
КадровыеДанные = "Подразделение, Должность";
ТаблицаДанных = КадровыйУчет.КадровыеДанныеСотрудников(Истина, МассивСотрудников, КадровыеДанные, ДатаПолученияДанных);
// 4. Безопасно извлекаем значения
Если ТаблицаДанных.Количество() > 0 Тогда
СтрокаДанных = ТаблицаДанных[0];
ОбластьРаботник.Параметры.ДолжностьПрежняя = СтрокаДанных.Должность;
ОбластьРаботник.Параметры.ПодразделениеПрежнееНаПечать = СтрокаДанных.Подразделение;
Иначе
// Если на дату «До перевода» данных не найдено (например, перевод в день приема)
ОбластьРаботник.Параметры.ДолжностьПрежняя = "Не найдена";
ОбластьРаботник.Параметры.ПодразделениеПрежнееНаПечать = "Не найдено";
КонецЕсли;
Мало кто из начинающих разработчиков знает, что в программном интерфейсе общего модуля КадровыйУчет существует специальная облегченная функция, предназначенная для работы с **одним** сотрудником. Она называется КадровыеДанныеСотрудника.
Эта функция избавляет нас от необходимости вручную создавать массив, а самое главное — она возвращает не таблицу значений, а **Структуру**. Это значительно упрощает синтаксис кода и снижает вероятность возникновения ошибок индексации.
Рассмотрим пример реализации с использованием этого метода:
// Определяем дату получения прежних сведений
ДатаПолученияДанных = СсылкаНаДокумент.Дата - 1;
// Запрашиваем структуру кадровых данных на одного сотрудника
СтруктураДанных = КадровыйУчет.КадровыеДанныеСотрудника(
Истина,
СсылкаНаДокумент.Сотрудник,
"Должность, Подразделение",
ДатаПолученияДанных
);
// Заполняем параметры печатной формы с проверкой на Неопределено
Если СтруктураДанных <> Неопределено Тогда
ОбластьРаботник.Параметры.ДолжностьПрежняя = СтруктураДанных.Должность;
ОбластьРаботник.Параметры.ПодразделениеПрежнееНаПечать = СтруктураДанных.Подразделение;
КонецЕсли;
Использование функции КадровыеДанныеСотрудника является более предпочтительным стандартом написания кода в среде 1С:ЗУП 3.1, когда речь идет об обработке одиночного документа (например, при формировании печатной формы из формы самого документа кадрового перевода).
Мы успешно разобрали механизмы получения кадровых данных в 1С:ЗУП 3.1. Для предотвращения ошибок всегда соблюдайте следующие правила:
КадровыеДанныеСотрудников всегда передавайте список сотрудников в виде Массива.ПоляОтбораПериодическихДанных, если в этом нет реальной необходимости — оставляйте его в состоянии Неопределено.КадровыйУчет.КадровыеДанныеСотрудника.